-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
2,113 additions
and
5 deletions.
There are no files selected for viewing
37 changes: 37 additions & 0 deletions
37
25_Keras_examples_3_Generative_models_examples/25_keras_examples_3.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
## 케라스 예제 3(Keras examples) | ||
[원문 링크](https://github.com/keras-team/keras/tree/master/examples) | ||
> 현 디렉토리에는 케라스를 활용한 예제들 중 Generative 모델을 다루는 코드들이 있습니다. style transfer, auto encoder 등 generator 이론을 알고있는 전제하에 실제 구현에 초점을 맞춰져 있으므로 코드에 첨가된 주석 외 정보는 직접 찾아보셔야 합니다. | ||
* keras | ||
* neural style transfer | ||
* auto encoder | ||
* lstm | ||
* visualization | ||
|
||
### Generative models examples | ||
|
||
[lstm_text_generation.py](lstm_text_generation.py) | ||
니체풍의 문장을 생성하기 | ||
|
||
[conv_filter_visualization.py](conv_filter_visualization.py) | ||
입력공간의 기울기를 통해 VGG16 필터들을 시각화 | ||
|
||
[deep_dream.py](deep_dream.py) | ||
케라스로 Deep Dream. | ||
|
||
[neural_doodle.py](neural_doodle.py) | ||
Keras를 이용해 신경망으로 낙서하기 | ||
|
||
[neural_style_transfer.py](neural_style_transfer.py) | ||
Neural style transfer. | ||
|
||
[variational_autoencoder.py](variational_autoencoder.py) | ||
variational autoencoder를 만드는 방법을 보여줍니다. | ||
|
||
[variational_autoencoder_deconv.py](variational_autoencoder_deconv.py) | ||
Deconvolution 레이어와 케라스를 사용해 variational autoencoder를 만드는 방법을 보여줍니다. | ||
|
||
|
||
> 이 글은 2018 컨트리뷰톤에서 [`Contributue to Keras`](https://github.com/KerasKorea/KEKOxTutorial) 프로젝트로 진행했습니다. | ||
> Translator : [mike2ox](https://github.com/mike2ox) (Moonhyeok Song) | ||
> Translator Email : <[email protected]> |
137 changes: 137 additions & 0 deletions
137
25_Keras_examples_3_Generative_models_examples/conv_filter_visualization.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
'''입력공간의 기울기를 통해 VGG16 필터들을 시각화 | ||
이 글은 CPU환경에서 몇분이면 실행할 수 있습니다. | ||
결과 예시 : http://i.imgur.com/4nj4KjN.jpg | ||
''' | ||
from __future__ import print_function | ||
|
||
import numpy as np | ||
import time | ||
from keras.preprocessing.image import save_img | ||
from keras.applications import vgg16 | ||
from keras import backend as K | ||
|
||
# 각 필터들을 위한 생성 이미지의 차원 설정합니다 | ||
img_width = 128 | ||
img_height = 128 | ||
|
||
# 시각화하고 싶은 레이어의 이름을 설정합니다 | ||
# (모델에 대한 정의는 keras/applications/vgg16.py에서 볼 수 있습니다.) | ||
layer_name = 'block5_conv1' | ||
|
||
# 텐서(tensor)를 확인된 이미지로 변환해주는 함수입니다. | ||
def deprocess_image(x): | ||
|
||
# 텐서를 정규화합니다 : 중심은 0, 편차는 0.1 | ||
x -= x.mean() | ||
x /= (x.std() + K.epsilon()) | ||
x *= 0.1 | ||
|
||
# clip to [0, 1] | ||
x += 0.5 | ||
x = np.clip(x, 0, 1) | ||
|
||
# RGB 배열로 변환합니다 | ||
x *= 255 | ||
if K.image_data_format() == 'channels_first': | ||
x = x.transpose((1, 2, 0)) | ||
x = np.clip(x, 0, 255).astype('uint8') | ||
return x | ||
|
||
|
||
# ImageNet의 가중치를 VGG16에 적용, 설계합니다 | ||
model = vgg16.VGG16(weights='imagenet', include_top=False) | ||
print('Model loaded.') | ||
|
||
model.summary() | ||
|
||
# 이미지를 입력받기 위한 placeholder를 설정합니다 | ||
input_img = model.input | ||
|
||
# (앞서 이름을 지정한)각 핵심 레이어의 출력들을 가져옵니다. | ||
layer_dict = dict([(layer.name, layer) for layer in model.layers[1:]]) | ||
|
||
|
||
def normalize(x): | ||
# L2 norm으로 텐서를 정규화 해주는 함수 | ||
return x / (K.sqrt(K.mean(K.square(x))) + K.epsilon()) | ||
|
||
|
||
kept_filters = [] | ||
for filter_index in range(200): | ||
|
||
# 실제론 512개의 필터가 있지만 처음 200개의 필터만 스캔합니다. | ||
print('Processing filter %d' % filter_index) | ||
start_time = time.time() | ||
|
||
# 관심을 두고 있는 레이어의 n번째 필터의 활성화를 최대치로 하는 손실 함수를 설계합니다. | ||
layer_output = layer_dict[layer_name].output | ||
if K.image_data_format() == 'channels_first': | ||
loss = K.mean(layer_output[:, filter_index, :, :]) | ||
else: | ||
loss = K.mean(layer_output[:, :, :, filter_index]) | ||
|
||
# 손실 함수를 통해 입력 이미지의 기울기를 계산합니다 | ||
grads = K.gradients(loss, input_img)[0] | ||
|
||
# 정규화 기법 : 기울기를 정규화 합니다. | ||
grads = normalize(grads) | ||
|
||
# 입력 이미지의 손실과 기울기를 반환합니다. | ||
iterate = K.function([input_img], [loss, grads]) | ||
|
||
# 기울기 상승을 위해 스탭 크기 지정합니다. | ||
step = 1. | ||
|
||
# 몇 개의 임의의 노이즈와 같이 회색 이미지부터 시작합니다. | ||
if K.image_data_format() == 'channels_first': | ||
input_img_data = np.random.random((1, 3, img_width, img_height)) | ||
else: | ||
input_img_data = np.random.random((1, img_width, img_height, 3)) | ||
input_img_data = (input_img_data - 0.5) * 20 + 128 | ||
|
||
# 20 스텝동안 기울기 상승을 시도합니다. | ||
for i in range(20): | ||
loss_value, grads_value = iterate([input_img_data]) | ||
input_img_data += grads_value * step | ||
|
||
print('Current loss value:', loss_value) | ||
if loss_value <= 0.: | ||
# 몇가지 필터가 0을 가질 때는 넘어갑니다. | ||
break | ||
|
||
# 입력 이미지의 결과물을 디코드(decode)합니다. | ||
if loss_value > 0: | ||
img = deprocess_image(input_img_data[0]) | ||
kept_filters.append((img, loss_value)) | ||
end_time = time.time() | ||
print('Filter %d processed in %ds' % (filter_index, end_time - start_time)) | ||
|
||
# 8 X 8 격자인 64개의 필터들을 사용할 겁니다. | ||
n = 8 | ||
|
||
# 가장 큰 손실값을 가진 필터는 더 잘보일 것입니다. | ||
# 상위 64개의 필터는 유지시킬 겁니다. | ||
kept_filters.sort(key=lambda x: x[1], reverse=True) | ||
kept_filters = kept_filters[:n * n] | ||
|
||
# 128 x 128 크기의 8 x 8 필터를 저장할 수 있는 충분한 공간이 있는 검정 이미지를 만듭니다. | ||
# 5px의 여유공간도 둬야합니다. | ||
margin = 5 | ||
width = n * img_width + (n - 1) * margin | ||
height = n * img_height + (n - 1) * margin | ||
stitched_filters = np.zeros((width, height, 3)) | ||
|
||
# 필터와 이미지를 저장합니다. | ||
for i in range(n): | ||
for j in range(n): | ||
img, loss = kept_filters[i * n + j] | ||
width_margin = (img_width + margin) * i | ||
height_margin = (img_height + margin) * j | ||
stitched_filters[ | ||
width_margin: width_margin + img_width, | ||
height_margin: height_margin + img_height, :] = img | ||
|
||
# 결과를 디스크에 저장합니다. | ||
save_img('stitched_filters_%dx%d.png' % (n, n), stitched_filters) |
195 changes: 195 additions & 0 deletions
195
25_Keras_examples_3_Generative_models_examples/deep_dream.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
'''케라스로 Deep Dreaming 하기 | ||
원문 : https://github.com/keras-team/keras/tree/master/examples/deep_dream.py | ||
> 현 스크립트는 케라스를 이용해 입력 이미지의 특징들을 pareidolia 알고리즘으로 분석, 강화시켜 | ||
> 마치 꿈, 환각같은 이미지 형태로 출력해주는 튜토리얼입니다. | ||
* deepdream | ||
* keras | ||
* CNN | ||
현 스크립트를 실행하기 위해선: | ||
``` | ||
python deep_dream.py path_to_your_base_image.jpg prefix_for_results | ||
``` | ||
예 : | ||
``` | ||
python deep_dream.py img/mypic.jpg results/dream | ||
``` | ||
''' | ||
from __future__ import print_function | ||
|
||
from keras.preprocessing.image import load_img, save_img, img_to_array | ||
import numpy as np | ||
import scipy | ||
import argparse | ||
|
||
from keras.applications import inception_v3 | ||
from keras import backend as K | ||
|
||
# 입출력 이미지 경로 설정합니다. | ||
parser = argparse.ArgumentParser(description='Deep Dreams with Keras.') | ||
parser.add_argument('base_image_path', metavar='base', type=str, | ||
help='Path to the image to transform.') | ||
parser.add_argument('result_prefix', metavar='res_prefix', type=str, | ||
help='Prefix for the saved results.') | ||
|
||
args = parser.parse_args() | ||
base_image_path = args.base_image_path | ||
result_prefix = args.result_prefix | ||
|
||
# 아래 코드는 마지막 손실에서 가중치와 활성화를 최대로 하는 계층들의 이름입니다 | ||
# 이제 최대화를 시도해봅시다. | ||
# 밑의 설정들을 수정해서 새로운 시각효과를 얻어봅시다. | ||
settings = { | ||
'features': { | ||
'mixed2': 0.2, | ||
'mixed3': 0.5, | ||
'mixed4': 2., | ||
'mixed5': 1.5, | ||
}, | ||
} | ||
|
||
|
||
def preprocess_image(image_path): | ||
# 이미지들을 열어서 적절한 tensor에 resize, format 해주는 함수 | ||
img = load_img(image_path) | ||
img = img_to_array(img) | ||
img = np.expand_dims(img, axis=0) | ||
img = inception_v3.preprocess_input(img) | ||
return img | ||
|
||
|
||
def deprocess_image(x): | ||
# 하나의 tensor를 검증 이미지로 변환해주는 함수 | ||
if K.image_data_format() == 'channels_first': | ||
x = x.reshape((3, x.shape[2], x.shape[3])) | ||
x = x.transpose((1, 2, 0)) | ||
else: | ||
x = x.reshape((x.shape[1], x.shape[2], 3)) | ||
x /= 2. | ||
x += 0.5 | ||
x *= 255. | ||
x = np.clip(x, 0, 255).astype('uint8') | ||
return x | ||
|
||
K.set_learning_phase(0) | ||
|
||
# 실험하실 placehorder(입력 데이터)기반으로 InceptionV3 network를 설계합니다. | ||
# 해당 모델은 ImageNet으로 선행학습된 가중치를 가져올 겁니다. | ||
model = inception_v3.InceptionV3(weights='imagenet', | ||
include_top=False) | ||
dream = model.input | ||
print('Model loaded.') | ||
|
||
# 각 핵심 계층의 상징적인 결과를 가져옵니다(고유한 이름을 부여해야 합니다.). | ||
layer_dict = dict([(layer.name, layer) for layer in model.layers]) | ||
|
||
# 손실을 정의합니다. | ||
loss = K.variable(0.) | ||
for layer_name in settings['features']: | ||
# 계층의 특징들에 대한 L2 norm을 손실에 추가합니다. | ||
assert (layer_name in layer_dict.keys(), | ||
'Layer ' + layer_name + ' not found in model.') | ||
coeff = settings['features'][layer_name] | ||
x = layer_dict[layer_name].output | ||
# 손실에서 경계부분을 제외한 픽셀만 포함시키도록 artifacts(예술작품?) 경계를 피해줍니다. | ||
scaling = K.prod(K.cast(K.shape(x), 'float32')) | ||
if K.image_data_format() == 'channels_first': | ||
loss += coeff * K.sum(K.square(x[:, :, 2: -2, 2: -2])) / scaling | ||
else: | ||
loss += coeff * K.sum(K.square(x[:, 2: -2, 2: -2, :])) / scaling | ||
|
||
# 손실에 대해 실제 'dream' 모델의 기울기를 계산합니다. | ||
grads = K.gradients(loss, dream)[0] | ||
# 기울기들을 표준화합니다 | ||
grads /= K.maximum(K.mean(K.abs(grads)), K.epsilon()) | ||
|
||
# 주어진 입력이미지의 기울기들과 손실 값을 검색하는 함수를 설정합니다. | ||
outputs = [loss, grads] | ||
fetch_loss_and_grads = K.function([dream], outputs) | ||
|
||
|
||
def eval_loss_and_grads(x): | ||
outs = fetch_loss_and_grads([x]) | ||
loss_value = outs[0] | ||
grad_values = outs[1] | ||
return loss_value, grad_values | ||
|
||
|
||
def resize_img(img, size): | ||
img = np.copy(img) | ||
if K.image_data_format() == 'channels_first': | ||
factors = (1, 1, | ||
float(size[0]) / img.shape[2], | ||
float(size[1]) / img.shape[3]) | ||
else: | ||
factors = (1, | ||
float(size[0]) / img.shape[1], | ||
float(size[1]) / img.shape[2], | ||
1) | ||
return scipy.ndimage.zoom(img, factors, order=1) | ||
|
||
|
||
def gradient_ascent(x, iterations, step, max_loss=None): | ||
for i in range(iterations): | ||
loss_value, grad_values = eval_loss_and_grads(x) | ||
if max_loss is not None and loss_value > max_loss: | ||
break | ||
print('..Loss value at', i, ':', loss_value) | ||
x += step * grad_values | ||
return x | ||
|
||
|
||
"""진행과정 | ||
- 원본 이미지를 불러옵니다. | ||
- 아주 작은것부터 가장 큰 것까지, | ||
- 여러가지의 처리 구조를 정의합니다(예: 이미지 형태) | ||
- 원본 이미지를 가장작은 규모로 크기 변경합니다. | ||
- 모든 계층 구조를 위해, 가장 작은 단위에서 시작합니다.(예, 현재 척도): | ||
- 기울기 상승 진행 | ||
- 이미지를 다음 규모로 업그레이드 | ||
- 업그레이드시 손실된 세부정보를 재입력 | ||
- 원래 크기로 돌아갔을 때, 정지합니다. | ||
업그레이드 동안 손실된 세부정보를 얻기 위해, 그저 원본 이미지를 가져와, | ||
축소, 확장하고, 그 결과를 원래 (크기 변경된) 이미지와 비교합니다 | ||
""" | ||
|
||
|
||
# 아래 하이퍼파라미터들을 사용하면 새로운 효과들을 얻을 수 있습니다. | ||
step = 0.01 # 기울기 상승 step의 크기 | ||
num_octave = 3 # 기울기 상승을 실행할 때, 계층 구조의 수(?) | ||
octave_scale = 1.4 # 계층들 간 비율 | ||
iterations = 20 # 계층마다 (기울기) 상승 step의 횟수 | ||
max_loss = 10. | ||
|
||
img = preprocess_image(base_image_path) | ||
if K.image_data_format() == 'channels_first': | ||
original_shape = img.shape[2:] | ||
else: | ||
original_shape = img.shape[1:3] | ||
successive_shapes = [original_shape] | ||
for i in range(1, num_octave): | ||
shape = tuple([int(dim / (octave_scale ** i)) for dim in original_shape]) | ||
successive_shapes.append(shape) | ||
successive_shapes = successive_shapes[::-1] | ||
original_img = np.copy(img) | ||
shrunk_original_img = resize_img(img, successive_shapes[0]) | ||
|
||
for shape in successive_shapes: | ||
print('Processing image shape', shape) | ||
img = resize_img(img, shape) | ||
img = gradient_ascent(img, | ||
iterations=iterations, | ||
step=step, | ||
max_loss=max_loss) | ||
upscaled_shrunk_original_img = resize_img(shrunk_original_img, shape) | ||
same_size_original = resize_img(original_img, shape) | ||
lost_detail = same_size_original - upscaled_shrunk_original_img | ||
|
||
img += lost_detail | ||
shrunk_original_img = resize_img(original_img, shape) | ||
|
||
save_img(result_prefix + '.png', deprocess_image(np.copy(img))) |
Oops, something went wrong.