本文主要實現了DeepDream算法。python
另外,在原文中每次迭代都打印出輸入圖像以及添加梯度後的圖像。爲了閱讀體驗,本文每次迭代只打印一張圖像。另外github上傳文件的大小限制在25M,所以python notebook中也沒有顯示出全部的輸出結果,完整的輸出要運行notebook才能看到。git
01 - 簡單線性模型 | 02 - 卷積神經網絡 | 03 - PrettyTensor | 04 - 保存& 恢復
05 - 集成學習 | 06 - CIFAR 10 | 07 - Inception 模型 | 08 - 遷移學習
09 - 視頻數據 | 11 - 對抗樣本 | 12 - MNIST的對抗噪聲 | 13 - 可視化分析github
by Magnus Erik Hvass Pedersen / GitHub / Videos on YouTube
中文翻譯 thrillerist / Github算法
若有轉載,請附上本文連接。express
在上一篇教程中,咱們看到了如何用神經網絡的梯度來生成圖像。教程#11和#12展現瞭如何用梯度來生成對抗噪聲。教程#13展現了怎麼用梯度來生成神經網絡內部特徵所響應的圖像。數組
本文會使用一個與以前相似的方法。如今咱們會用神經網絡的梯度來放大輸入圖像中的圖案(patterns)。這個一般稱爲DeepDream算法,但這個技術實際上有許多不一樣的變體。bash
本文基於以前的教程。你須要大概地熟悉神經網絡(詳見教程 #01和 #02)。網絡
下面的流程圖粗略展現了DeepDream算法的想法。咱們使用的是Inception模型,它的層次要比這邊顯示的更多。咱們使用TensorFlow自動導出網絡中一個給定層相對於輸入圖像的梯度。而後用梯度來更新輸入圖像。這個過程重複屢次,直到出現圖案而且咱們對所獲得的圖像滿意爲止。session
這裏的原理就是,神經網絡在圖像中看到一些圖案的痕跡,而後咱們只是用梯度把它放大了。app
這裏沒有顯示DeepDream算法的一些細節,例如梯度被平滑了,後面會討論它的一些優勢。梯度也是分塊計算的,所以它能夠在高分辨率的圖像上工做,而不會耗盡計算機內存。
from IPython.display import Image, display
Image('images/14_deepdream_flowchart.png')複製代碼
Inception模型是在至關低分辨率的圖像上進行訓練的,大概200-300像素。因此,當咱們使用更大分辨率的圖像時,DeepDream算法會在圖像中建立許多小的圖案。
一個解決方案是將輸入圖像縮小到200-300像素。可是這麼低的分辨率(的結果)是像素化並且醜陋的。
另外一個解決方案是屢次縮小原始圖像,在每一個較小的圖像上運行DeepDream算法。這樣會在圖像中建立更大的圖案,而後以更高的分辨率進行改善。
這個流程圖粗略顯示了這個想法。算法遞歸地實現而且支持任何數量的縮小級別。算法有些細節並未在這裏展現,好比,圖像在縮小以前會作一些模糊處理,而且原始圖像只是與DeepDream圖像混合在一塊兒,來增長一些原始的細節。
Image('images/14_deepdream_recursive_flowchart.png')複製代碼
%matplotlib inline
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
import random
import math
# Image manipulation.
import PIL.Image
from scipy.ndimage.filters import gaussian_filter複製代碼
使用Python3.5.2(Anaconda)開發,TensorFlow版本是:
tf.__version__複製代碼
'1.1.0'
前面的一些教程都使用了Inception v3模型。本文將會使用Inception模型的另外一個變體。因爲Google開發者並無很好的爲其撰寫文檔(跟一般同樣),不太清楚模型是哪一個版本。咱們在這裏用「Inception 5h」來指代它,由於zip包的文件名就是這樣,儘管看起來這是Inception模型的一個早期的、更簡單的版本。
這裏使用Inception 5h模型是由於它更容易使用:它接受任何尺寸的輸入圖像,而後建立比Inception v3模型(見教程 #13)更漂亮的圖像。
import inception5h複製代碼
從網上下載Inception 5h模型。這是你保存數據文件的默認文件夾。若是文件夾不存在就自動建立。
# inception.data_dir = 'inception/5h/'複製代碼
若是文件夾中不存在Inception模型,就自動下載。
它有50MB。
inception5h.maybe_download()複製代碼
Downloading Inception 5h Model ...
Data has apparently already been downloaded and unpacked.
載入模型,以便使用。
model = inception5h.Inception5h()複製代碼
Inception 5h模型有許多層可用來作DeepDreaming。咱們列出了12個最經常使用的層,以供參考。
len(model.layer_tensors)複製代碼
12
這個函數載入一張圖像,並返回一個浮點型numpy數組。
def load_image(filename):
image = PIL.Image.open(filename)
return np.float32(image)複製代碼
將圖像保存成jpeg文件。圖像是保存着0-255像素的numpy數組。
def save_image(image, filename):
# Ensure the pixel-values are between 0 and 255.
image = np.clip(image, 0.0, 255.0)
# Convert to bytes.
image = image.astype(np.uint8)
# Write the image-file in jpeg-format.
with open(filename, 'wb') as file:
PIL.Image.fromarray(image).save(file, 'jpeg')複製代碼
這是繪製圖像的函數。使用matplotlib將獲得低分辨率的圖像。使用PIL效果比較好。
def plot_image(image):
# Assume the pixel-values are scaled between 0 and 255.
if False:
# Convert the pixel-values to the range between 0.0 and 1.0
image = np.clip(image/255.0, 0.0, 1.0)
# Plot using matplotlib.
plt.imshow(image, interpolation='lanczos')
plt.show()
else:
# Ensure the pixel-values are between 0 and 255.
image = np.clip(image, 0.0, 255.0)
# Convert pixels to bytes.
image = image.astype(np.uint8)
# Convert to a PIL-image and display it.
display(PIL.Image.fromarray(image))複製代碼
歸一化圖像,則像素值在0.0到1.0之間。這個在繪製梯度時頗有用。
def normalize_image(x):
# Get the min and max values for all pixels in the input.
x_min = x.min()
x_max = x.max()
# Normalize so all values are between 0.0 and 1.0
x_norm = (x - x_min) / (x_max - x_min)
return x_norm複製代碼
對梯度作歸一化以後,用這個函數繪製。
def plot_gradient(gradient):
# Normalize the gradient so it is between 0.0 and 1.0
gradient_normalized = normalize_image(gradient)
# Plot the normalized gradient.
plt.imshow(gradient_normalized, interpolation='bilinear')
plt.show()複製代碼
這個函數調整圖像的大小。函數的參數是你指定的具體的圖像分辨率,好比(100,200),它也能夠接受一個縮放因子,好比,參數是0.5時,圖像每一個維度縮小一半。
這個函數用PIL來實現,代碼有點長,由於咱們用numpy數組來處理圖像,其中像素值是浮點值。PIL不支持這個,所以須要將圖像轉換成8位字節,來確保像素值在合適的範圍內。而後,圖像被調整大小並轉換回浮點值。
def resize_image(image, size=None, factor=None):
# If a rescaling-factor is provided then use it.
if factor is not None:
# Scale the numpy array's shape for height and width.
size = np.array(image.shape[0:2]) * factor
# The size is floating-point because it was scaled.
# PIL requires the size to be integers.
size = size.astype(int)
else:
# Ensure the size has length 2.
size = size[0:2]
# The height and width is reversed in numpy vs. PIL.
size = tuple(reversed(size))
# Ensure the pixel-values are between 0 and 255.
img = np.clip(image, 0.0, 255.0)
# Convert the pixels to 8-bit bytes.
img = img.astype(np.uint8)
# Create PIL-object from numpy array.
img = PIL.Image.fromarray(img)
# Resize the image.
img_resized = img.resize(size, PIL.Image.LANCZOS)
# Convert 8-bit pixel values back to floating-point.
img_resized = np.float32(img_resized)
return img_resized複製代碼
下面的幫助函數計算了在DeepDream中使用的輸入圖像的梯度。Inception 5h模型能夠接受任意尺寸的圖像,但太大的圖像可能會佔用千兆字節的內存。爲了使內存佔用最低,咱們將輸入圖像分割成小的圖塊,而後計算每小塊的梯度。
然而,這可能會在DeepDream算法最終生成的圖像中產生肉眼可見的線條。所以咱們隨機地挑選小塊,這樣它們的位置就是不一樣的。這使得在最終的DeepDream圖像裏,小塊之間的縫隙不可見。
這個幫助函數用來肯定合適的圖塊尺寸。好比,指望的圖塊尺寸爲400x400像素,但實際大小取決於圖像尺寸。
def get_tile_size(num_pixels, tile_size=400):
""" num_pixels is the number of pixels in a dimension of the image. tile_size is the desired tile-size. """
# How many times can we repeat a tile of the desired size.
num_tiles = int(round(num_pixels / tile_size))
# Ensure that there is at least 1 tile.
num_tiles = max(1, num_tiles)
# The actual tile-size.
actual_tile_size = math.ceil(num_pixels / num_tiles)
return actual_tile_size複製代碼
這個幫助函數計算了輸入圖像的梯度。圖像被分割成小塊,而後分別計算各個圖塊的梯度。圖塊是隨機選擇的,避免在最終的DeepDream圖像內產生可見的縫隙。
def tiled_gradient(gradient, image, tile_size=400):
# Allocate an array for the gradient of the entire image.
grad = np.zeros_like(image)
# Number of pixels for the x- and y-axes.
x_max, y_max, _ = image.shape
# Tile-size for the x-axis.
x_tile_size = get_tile_size(num_pixels=x_max, tile_size=tile_size)
# 1/4 of the tile-size.
x_tile_size4 = x_tile_size // 4
# Tile-size for the y-axis.
y_tile_size = get_tile_size(num_pixels=y_max, tile_size=tile_size)
# 1/4 of the tile-size
y_tile_size4 = y_tile_size // 4
# Random start-position for the tiles on the x-axis.
# The random value is between -3/4 and -1/4 of the tile-size.
# This is so the border-tiles are at least 1/4 of the tile-size,
# otherwise the tiles may be too small which creates noisy gradients.
x_start = random.randint(-3*x_tile_size4, -x_tile_size4)
while x_start < x_max:
# End-position for the current tile.
x_end = x_start + x_tile_size
# Ensure the tile's start- and end-positions are valid.
x_start_lim = max(x_start, 0)
x_end_lim = min(x_end, x_max)
# Random start-position for the tiles on the y-axis.
# The random value is between -3/4 and -1/4 of the tile-size.
y_start = random.randint(-3*y_tile_size4, -y_tile_size4)
while y_start < y_max:
# End-position for the current tile.
y_end = y_start + y_tile_size
# Ensure the tile's start- and end-positions are valid.
y_start_lim = max(y_start, 0)
y_end_lim = min(y_end, y_max)
# Get the image-tile.
img_tile = image[x_start_lim:x_end_lim,
y_start_lim:y_end_lim, :]
# Create a feed-dict with the image-tile.
feed_dict = model.create_feed_dict(image=img_tile)
# Use TensorFlow to calculate the gradient-value.
g = session.run(gradient, feed_dict=feed_dict)
# Normalize the gradient for the tile. This is
# necessary because the tiles may have very different
# values. Normalizing gives a more coherent gradient.
g /= (np.std(g) + 1e-8)
# Store the tile's gradient at the appropriate location.
grad[x_start_lim:x_end_lim,
y_start_lim:y_end_lim, :] = g
# Advance the start-position for the y-axis.
y_start = y_end
# Advance the start-position for the x-axis.
x_start = x_end
return grad複製代碼
這個函數是DeepDream算法的主要優化循環。它根據輸入圖像計算Inception模型中給定層的梯度。而後將梯度添加到輸入圖像,從而增長層張量(layer-tensor)的平均值。屢次重複這個過程,並放大Inception模型在輸入圖像中看到的任何圖案。
def optimize_image(layer_tensor, image, num_iterations=10, step_size=3.0, tile_size=400, show_gradient=False):
""" Use gradient ascent to optimize an image so it maximizes the mean value of the given layer_tensor. Parameters: layer_tensor: Reference to a tensor that will be maximized. image: Input image used as the starting point. num_iterations: Number of optimization iterations to perform. step_size: Scale for each step of the gradient ascent. tile_size: Size of the tiles when calculating the gradient. show_gradient: Plot the gradient in each iteration. """
# Copy the image so we don't overwrite the original image.
img = image.copy()
print("Image before:")
plot_image(img)
print("Processing image: ", end="")
# Use TensorFlow to get the mathematical function for the
# gradient of the given layer-tensor with regard to the
# input image. This may cause TensorFlow to add the same
# math-expressions to the graph each time this function is called.
# It may use a lot of RAM and could be moved outside the function.
gradient = model.get_gradient(layer_tensor)
for i in range(num_iterations):
# Calculate the value of the gradient.
# This tells us how to change the image so as to
# maximize the mean of the given layer-tensor.
grad = tiled_gradient(gradient=gradient, image=img)
# Blur the gradient with different amounts and add
# them together. The blur amount is also increased
# during the optimization. This was found to give
# nice, smooth images. You can try and change the formulas.
# The blur-amount is called sigma (0=no blur, 1=low blur, etc.)
# We could call gaussian_filter(grad, sigma=(sigma, sigma, 0.0))
# which would not blur the colour-channel. This tends to
# give psychadelic / pastel colours in the resulting images.
# When the colour-channel is also blurred the colours of the
# input image are mostly retained in the output image.
sigma = (i * 4.0) / num_iterations + 0.5
grad_smooth1 = gaussian_filter(grad, sigma=sigma)
grad_smooth2 = gaussian_filter(grad, sigma=sigma*2)
grad_smooth3 = gaussian_filter(grad, sigma=sigma*0.5)
grad = (grad_smooth1 + grad_smooth2 + grad_smooth3)
# Scale the step-size according to the gradient-values.
# This may not be necessary because the tiled-gradient
# is already normalized.
step_size_scaled = step_size / (np.std(grad) + 1e-8)
# Update the image by following the gradient.
img += grad * step_size_scaled
if show_gradient:
# Print statistics for the gradient.
msg = "Gradient min: {0:>9.6f}, max: {1:>9.6f}, stepsize: {2:>9.2f}"
print(msg.format(grad.min(), grad.max(), step_size_scaled))
# Plot the gradient.
plot_gradient(grad)
else:
# Otherwise show a little progress-indicator.
print(". ", end="")
print()
print("Image after:")
plot_image(img)
return img複製代碼
Inception模型在至關小的圖像上進行訓練。不清楚圖像的確切大小,但可能每一個維度200-300像素。若是咱們使用較大的圖像,好比1920x1080像素,那麼上面的optimize_image()
函數會在圖像上添加不少小的圖案。
這個幫助函數將輸入圖像屢次縮放,而後用每一個縮放圖像來執行上面的optimize_image()
函數。這在最終的圖像中生成較大的圖案。它也能加快計算速度。
def recursive_optimize(layer_tensor, image, num_repeats=4, rescale_factor=0.7, blend=0.2, num_iterations=10, step_size=3.0, tile_size=400):
""" Recursively blur and downscale the input image. Each downscaled image is run through the optimize_image() function to amplify the patterns that the Inception model sees. Parameters: image: Input image used as the starting point. rescale_factor: Downscaling factor for the image. num_repeats: Number of times to downscale the image. blend: Factor for blending the original and processed images. Parameters passed to optimize_image(): layer_tensor: Reference to a tensor that will be maximized. num_iterations: Number of optimization iterations to perform. step_size: Scale for each step of the gradient ascent. tile_size: Size of the tiles when calculating the gradient. """
# Do a recursive step?
if num_repeats>0:
# Blur the input image to prevent artifacts when downscaling.
# The blur amount is controlled by sigma. Note that the
# colour-channel is not blurred as it would make the image gray.
sigma = 0.5
img_blur = gaussian_filter(image, sigma=(sigma, sigma, 0.0))
# Downscale the image.
img_downscaled = resize_image(image=img_blur,
factor=rescale_factor)
# Recursive call to this function.
# Subtract one from num_repeats and use the downscaled image.
img_result = recursive_optimize(layer_tensor=layer_tensor,
image=img_downscaled,
num_repeats=num_repeats-1,
rescale_factor=rescale_factor,
blend=blend,
num_iterations=num_iterations,
step_size=step_size,
tile_size=tile_size)
# Upscale the resulting image back to its original size.
img_upscaled = resize_image(image=img_result, size=image.shape)
# Blend the original and processed images.
image = blend * image + (1.0 - blend) * img_upscaled
print("Recursive level:", num_repeats)
# Process the image using the DeepDream algorithm.
img_result = optimize_image(layer_tensor=layer_tensor,
image=image,
num_iterations=num_iterations,
step_size=step_size,
tile_size=tile_size)
return img_result複製代碼
咱們須要一個TensorFlow會話來運行圖。這是一個交互式的會話,所以咱們能夠繼續往計算圖中添加梯度方程。
session = tf.InteractiveSession(graph=model.graph)複製代碼
在第一個例子中,咱們有一張綠巨人的圖像。注意看看DeepDream圖像是如何保留絕大部分原始圖像顏色的。這是因爲梯度在其顏色通道中被平滑處理了,所以變得有點像灰階的,主要改變圖像的形狀,而不改變其顏色。
image = load_image(filename='images/hulk.jpg')
plot_image(image)複製代碼
首先,咱們須要Inception模型中的張量的引用,它將在DeepDream優化算法中被最大化。在這個例子中,咱們選擇Inception模型的第3層(層索引2)。它有192個通道,咱們將嘗試最大化這些通道的平均值。
layer_tensor = model.layer_tensors[2]
layer_tensor複製代碼
如今運行DeepDream優化算法,總共10次迭代,步長爲6.0,這是下面遞歸優化的兩倍。每次迭代咱們都展現它的梯度,你能夠看到圖像方塊之間的痕跡。
img_result = optimize_image(layer_tensor, image,
num_iterations=10, step_size=6.0, tile_size=400,
show_gradient=True)複製代碼
Image before:
Processing image: Gradient min: -26.993517, max: 25.577057, stepsize: 3.35
>Gradient min: -15.383774, max: 12.962121, stepsize: 5.97複製代碼
Gradient min: -5.993865, max: 6.191866, stepsize: 10.42
Gradient min: -3.638639, max: 3.307561, stepsize: 15.68
Gradient min: -2.407669, max: 2.166253, stepsize: 22.57
Gradient min: -1.716694, max: 1.467488, stepsize: 29.86
Gradient min: -1.153857, max: 1.025310, stepsize: 38.37
Gradient min: -1.026255, max: 0.869002, stepsize: 48.34
Gradient min: -0.634610, max: 0.765562, stepsize: 63.08
Gradient min: -0.585900, max: 0.485299, stepsize: 83.16
Image after:
若是你願意的話,能夠保存DeepDream圖像。
# save_image(img_result, filename='deepdream_hulk.jpg')複製代碼
如今,遞歸調用DeepDream算法。咱們執行5個遞歸(num_repeats + 1
),每一個步驟中圖像都被模糊並縮小,而後在縮小圖像上運行DeepDream算法。接着,在每一個步驟中,將產生的DeepDream圖像與原始圖像混合,從原始圖像獲取一點細節。這個過程重複了屢次。
注意,如今DeepDream的圖案更大了。這是由於咱們先在低分辨率圖像上建立圖案,而後在較高分辨率圖像上進行細化。
img_result = recursive_optimize(layer_tensor=layer_tensor, image=image,
num_iterations=10, step_size=3.0, rescale_factor=0.7,
num_repeats=4, blend=0.2)複製代碼
Recursive level: 0
Image before:
Processing image: . . . . . . . . . .
Image after:
Recursive level: 1
Image before:
Processing image: . . . . . . . . . .
Image after:
Recursive level: 2
Image before:
Processing image: . . . . . . . . . .
Image after:
Recursive level: 3
Image before:
Processing image: . . . . . . . . . .
Image after:
Recursive level: 4
Image before:
Processing image: . . . . . . . . . .
Image after:
如今咱們將最大化Inception模型中的較高層。使用7號層(索引6)爲例。該層識別輸入圖像中更復雜的形狀,因此DeepDream算法也將產生更復雜的圖像。這一層彷佛識別了狗的臉和毛髮,所以DeepDream算法往圖像中添加了這些東西。
再次注意,與DeepDream算法其餘變體不一樣的是,這裏輸入圖像的大部分顏色被保留了下來,建立了更多柔和的顏色。這是由於咱們在顏色通道中平滑了梯度,使其變得有點像灰階,所以不會太多地改變輸入圖像的顏色。
layer_tensor = model.layer_tensors[6]
img_result = recursive_optimize(layer_tensor=layer_tensor, image=image,
num_iterations=10, step_size=3.0, rescale_factor=0.7,
num_repeats=4, blend=0.2)複製代碼
下面這個例子用DeepDream算法來最大化層的特徵通道的子集。此時層的索引爲7,而且只有前3個特徵通道被最大化。
layer_tensor = model.layer_tensors[7][:,:,:,0:3]
img_result = recursive_optimize(layer_tensor=layer_tensor, image=image,
num_iterations=10, step_size=3.0, rescale_factor=0.7,
num_repeats=4, blend=0.2)複製代碼
這個例子展現了最大化Inception模型最後一層的第一個特徵通道的結果。不太清楚這一層及這個特徵可能會在輸入圖像中識別出什麼來。
(譯者注:原文的num_repeates
參數設爲4,我在配有NVIDIA GT 650M的筆記本上運行程序時,會出現內存不足的狀況。所以,下面將num_repeates
設爲3,須要的話能夠本身改回來。)
layer_tensor = model.layer_tensors[11][:,:,:,0]
img_result = recursive_optimize(layer_tensor=layer_tensor, image=image,
num_iterations=10, step_size=3.0, rescale_factor=0.7,
num_repeats=3, blend=0.2)複製代碼
image = load_image(filename='images/giger.jpg')
plot_image(image)複製代碼
layer_tensor = model.layer_tensors[3]
img_result = recursive_optimize(layer_tensor=layer_tensor, image=image,
num_iterations=10, step_size=3.0, rescale_factor=0.7,
num_repeats=3, blend=0.2)複製代碼
Recursive level: 0
Processing image: . . . . . . . . . .Recursive level: 1
Processing image: . . . . . . . . . .Recursive level: 2
Processing image: . . . . . . . . . .Recursive level: 3
Processing image: . . . . . . . . . .
layer_tensor = model.layer_tensors[5]
img_result = recursive_optimize(layer_tensor=layer_tensor, image=image,
num_iterations=10, step_size=3.0, rescale_factor=0.7,
num_repeats=3, blend=0.2)
Recursive level: 0
Processing image: . . . . . . . . . .Recursive level: 1
Processing image: . . . . . . . . . .Recursive level: 2
Processing image: . . . . . . . . . .Recursive level: 3
Processing image: . . . . . . . . . .
layer_tensor = model.layer_tensors[5]
img_result = recursive_optimize(layer_tensor=layer_tensor, image=image,
num_iterations=10, step_size=3.0, rescale_factor=0.7,
num_repeats=3, blend=0.2)複製代碼
image = load_image(filename='images/escher_planefilling2.jpg')
plot_image(image)複製代碼
layer_tensor = model.layer_tensors[6]
img_result = recursive_optimize(layer_tensor=layer_tensor, image=image,
num_iterations=10, step_size=3.0, rescale_factor=0.7,
num_repeats=3, blend=0.2)複製代碼
Recursive level: 0
Processing image: . . . . . . . . . .Recursive level: 1
Processing image: . . . . . . . . . .Recursive level: 2
Processing image: . . . . . . . . . .Recursive level: 3
Processing image: . . . . . . . . . .
如今咱們已經用TensorFlow完成了任務,關閉session,釋放資源。
# This has been commented out in case you want to modify and experiment
# with the Notebook without having to restart it.
# session.close()複製代碼
這篇教程展現瞭如何使用神經網絡的梯度來放大圖像中的圖案。輸出圖像彷佛已經用抽象的或相似動物的圖案來從新繪製了。
還有許多這種技術的變體,來生成不一樣的輸出圖像。咱們鼓勵你修改上述參數和算法進行實驗。
下面使一些可能會讓你提高TensorFlow技能的一些建議練習。爲了學習如何更合適地使用TensorFlow,實踐經驗是很重要的。
在你對這個Notebook進行修改以前,可能須要先備份一下。
optimize_image()
和recursive_optimize()
的不一樣參數,看看它如何影響結果。optimize_image()
中的梯度。會發生什麼?optimize_image()
時繪製梯度。會看到一些失真嗎?你認爲是什麼緣由?這重要嗎?你能找到一種方法來去掉它們嗎?inception5h.py
這個文件的Inception5h.get_gradient()
裏,刪除tf.square()
。 DeepDream圖像會發生什麼變化?爲何?optimize_image()
外面以節省內存嗎?optimize_image()
並在圖像上放大一點,製做一個DeepDream電影。