Python深度學習(8):DeepDream

算法簡介

DeepDream是Google在2015年發出的一個比較好玩的算法。它的本意是去可視化神經網絡。其思想簡介就是,對於深度學習,以圖片分類爲例,我們最終的網絡可以識別圖像,是因爲在訓練過程中不斷改變網絡的參數值。那麼我們能不能用已經訓練好的網絡去改變圖片,使得網絡相信該圖片屬於哪一類。聽上去好像是可行的。
最終的效果就是,輸入圖片有點像某類的特徵的地方會越來越明顯。

Tips

1.需要將多層的過濾器激活同時最大化
具體做法:對一組靠近頂部的層激活的L2範數進行加權求和
2.輸入圖像在不同的尺度上進行處理
具體做法:每次在當前尺寸運行梯度上升後,將得到的圖像放大40%

代碼

from keras.applications import inception_v3
from keras import backend as K
import numpy as np
import scipy
from keras.preprocessing import image

K.set_learning_phase(0) #禁止所有與訓練有關的操作
model = inception_v3.InceptionV3(weights = 'imagenet', include_top = False)
#構建不包含全連接層的Inception V3網絡,使用在ImageNet權重上預訓練的模型

#選擇哪些層將其激活最大化,將他們的影響設一個權重值,靠近底部的層生成內容抽象,靠近頂部的層生成內容具體
layer_contributions = {'mixed2' : 0.2, 'mixed3' : 3., 'mixed4' : 2., 'mixed5' : 1.5}

layer_dict = dict([(layer.name, layer) for layer in model.layers]) #將層的名字映射爲層的實例

loss = K.variable(0.) #定義損失函數的Variable

for layer_name in layer_contributions:
    coeff = layer_contributions[layer_name] #該層對應的影響係數
    activation = layer_dict[layer_name].output #該層的輸出
    scaling = K.prod(K.cast(K.shape(activation), 'float32'))
    #將該層輸出的L2範數添加到Loss去
    loss += coeff * K.sum(K.square(activation[:, 2 : -2, 2 : -2, :])) / scaling

dream = model.input #用於保存夢境圖像
grads = K.gradients(loss, dream)[0] #計算損失相對於夢境圖像的梯度
grads /= K.maximum(K.mean(K.abs(grads)), 1e-7) #梯度標準化,除以梯度絕對值的平均值

#設置函數,輸入圖片可以獲取損失值和梯度值
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 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 as', i, ':', loss_value)
        x += step * grad_values
    return x

#修改圖片大小
def resize_img(img, size):
    img = np.copy(img)
    factors = (1, float(size[0]) / img.shape[1], float(size[1]) / img.shape[2], 1)
    return scipy.ndimage.zoom(img, factors, order=1)
#保存圖片
def save_img(img, fname):
    pil_img = deprocess_image(np.copy(img))
    scipy.misc.imsave(fname, pil_img)
#轉換爲Inception V3能夠處理的張量
def preprocess_image(image_path):
    img = image.load_img(image_path)
    img = image.img_to_array(img)
    img = np.expand_dims(img, axis=0)
    img = inception_v3.preprocess_input(img)
    return img
#將張量轉爲圖像
def deprocess_image(x):
    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

step = 0.1 #梯度上升的步長
num_octave = 3 #運行梯度上升的尺度個數
octave_scale = 1.4 #兩個尺度之間大小比例
iterations = 20 #在每個尺度上運行梯度上升的步數

max_loss = 10 #損失最大範圍,避免醜陋僞影

base_image_path = 'original.png'

img = preprocess_image(base_image_path) #圖像轉爲numpy格式
original_shapes = img.shape[1:3] #存放原始圖片的大小
successive_shapes = [original_shapes]#形狀列表存放運行梯度上升時不同的圖片尺度(每次是上一次的octave_scale倍)
for i in range(1, num_octave):
    shape = tuple([int(dim / (octave_scale ** i)) for dim in original_shapes])
    successive_shapes.append(shape)

successive_shapes = successive_shapes[::-1] #形狀列表反轉,變爲升序
original_img = np.copy(img)
shrunk_original_img = resize_img(img, successive_shapes[0]) #將圖像numpy數組大小縮放到最小尺寸

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(img, fname = 'drean_at_scale_' + str(shape) + '.png') #保存圖片

save_img(img, fname='final_dream.png')

最終效果:
在這裏插入圖片描述
在這裏插入圖片描述
改了一下層,使用更偏頂部的層後,大型車禍現場。
有興趣的可以多試幾個。

layer_contributions = {'conv2d_85' : 0.2, 'mixed9_0' : 3., 'mixed9' : 2., 'mixed9_1' : 1.5}

在這裏插入圖片描述

參考

Tensorflow實現

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章