CS231n (winter 2016) : Assignment3

前言:

以斯坦福cs231n課程的python編程任務為主線,展開對該課程主要內容的理解和部分數學推導。建議PC端閱讀,該課程的學習資料和代碼如下:
視頻和PPT
筆記
assignment3初始代碼


Part 1: 空間定位和檢測(Spatial Localization and Detection)

以ILSVRC競賽為例,該競賽包含了三個計算機視覺任務:分類、定位和檢測。

分類:圖像上是啥? 預測top-5分類
定位:目標在哪里、是啥? 預測top-5分類+每個類別的bounding box(覆蓋率50%以上)
檢測:在哪里、都有啥?
---->> 定位是介于分類和檢測的中間任務,分類和定位使用相同的數據集,檢測的數據集有額外的數據集(物體比較小)。

這里貼張圖,方便直觀理解下各個任務的區別:

Computer Vision Tasks

其中,分類+定位我們可以一起完成。方便感受,上張圖:

Classification + Localization

那么改如何一起完成呢?我們可以將定位看成回歸問題,具體請看下圖:

Localization as Regression

對于目標檢測問題,我們是否也可以看成回歸問題來解決呢?由于每個圖像中目標個數不一樣,要定位的坐標數量也不一樣,所以這并不是一個很好的思路;另一個思路是將其看成分類問題,不過我們需要在不同位置進行很多次分類,這會很耗時。

對于目標檢測,R-CNN無疑是深度學習下目標檢測的經典作品,其思想引領了最近兩年目標檢測的潮流。這里簡單介紹下R-CNN的算法思路:

輸入一張圖片,我們先定位出2K個物體候選框,然后采用CNN提取每個候選框中圖片的特征向量,特征向量的維度為4096維,接著采用SVM算法對各個候選框中的物體進行分類識別。

R-CNN

這里列一下近兩年來目標檢測的風向標:
R-CNN ---> SPPNET ---> Fast-RCNN ---> Faster-RCNN


Part 2: 循環神經網絡(Recurrent Neural Networks)

RNNs主要用來處理序列數據。在傳統的神經網絡模型中,是從輸入層到隱含層再到輸出層,層與層之間是全連接的,每層之間的節點是無連接的。但是這種普通的神經網絡對于很多問題卻無能無力。例如,你要預測句子中的下一個單詞是什么,一般需要用到前面的單詞,因為一個句子中前后單詞并不是獨立的。RNNs之所以稱為循環神經網路,即一個序列當前的輸出與前面的輸出也有關。具體的表現形式為網絡會對前面的信息進行記憶并應用于當前輸出的計算中,即隱藏層之間的節點不再無連接而是有連接的,并且隱藏層的輸入不僅包括輸入層的輸出還包括上一時刻隱藏層的輸出。下圖是一個典型的RNNs(右側是左側的簡化結構):

RNNs

為了更好地說明RNN,我們可以將網絡按照時間進行展開:

A RNN and the unfolding in time of the computation involved in its forward computation.

在RNN中每一個時間步驟用到的參數(U, W, V)都是一樣的。一般來說,每一時間的輸入和輸出是不一樣的,比如對于序列數據就是將序列項依次傳入,每個序列項再對應不同的輸出(比如下一個序列項)。

1. 反向傳播算法(Back Propagation Through Time, BPTT)

RNNs的前向傳播依次按照時間的順序計算,反向傳播就是從最后一個時間點將累積的殘差傳遞回來即可。下面給出前向傳播和后向傳播的計算公式:

BPTT

2. 圖像描述(Image Captioning)

顧名思義,對于給定的一張圖片,自動生成一段文字描述。就像這樣:

Image Captioning

在Assignment3中,我們將用CNN+RNN來實現圖像自動描述,將會用到Microsoft COCO數據庫。那么如何搭建結構框架呢?這里先給張圖直觀感受下:

CNN + RNN

上圖中,我們用CNN來對輸入圖像進行特征提取,然后將提取到的特征作為RNN隱藏層的初始態(相當于t = -1時,隱藏層的輸出值)輸入到第一個時間點(t = 0)的隱藏層。RNN每個時間點的輸出是當前輸入序列項的下一項(比如,輸入"straw",輸出"hat")。

下面我給出一張詳細的diagram,方便大家完成Assignment3第一部分的編程任務,即RNN_Captioning.ipynb里的任務:

Image Captioning by CNN+RNN

3. Python編程任務(RNN)

這部分我們需要完成以下編程任務(此外,需要理解下captioning_solver.py):
--> rnn_layers.py,除了LSTM部分
--> rnn.py

具體代碼如下:

--> rnn_layers.py

__coauthor__ = 'Deeplayer'
# 8.13.2016 #

import numpy as np

def rnn_step_forward(x, prev_h, Wx, Wh, b):
    next_h = np.tanh(x.dot(Wx) + prev_h.dot(Wh) + b)
    cache = (x, Wx, Wh, prev_h, next_h)
    
    return next_h, cache

def rnn_step_backward(dnext_h, cache):
    x, Wx, Wh, prev_h, next_h = cache
    dtanh = 1 - next_h ** 2                 # (N, H)
    dx = (dnext_h * dtanh).dot(Wx.T)        # (N, D)
    dprev_h = (dnext_h * dtanh).dot(Wh.T)   # (N, H)
    dWx = x.T.dot(dnext_h * dtanh)          # (D, H)
    dWh = prev_h.T.dot(dnext_h * dtanh)     # (H, H)
    db = np.sum((dnext_h * dtanh), axis=0)

    return dx, dprev_h, dWx, dWh, db

def rnn_forward(x, h0, Wx, Wh, b):
    N, T, D = x.shape
    _, H = h0.shape
    h = np.zeros((N, T, H))
    h_interm = h0
    cache = []
    for i in xrange(T):    
        h[:, i, :], cache_sub = rnn_step_forward(x[:, i, :], h_interm, Wx, Wh, b) 
        h_interm = h[:, i, :]    
        cache.append(cache_sub)
   
    return h, cache

def rnn_backward(dh, cache):
    x, Wx, Wh, prev_h, next_h = cache[-1]
    _, D = x.shape
    N, T, H = dh.shape
    dx = np.zeros((N, T, D))
    dh0 = np.zeros((N, H))
    dWx = np.zeros((D, H))
    dWh = np.zeros((H, H))
    db = np.zeros(H)
    dprev_h_=np.zeros((N, H))
    for i in xrange(T-1, -1, -1):    
        dx_, dprev_h_, dWx_, dWh_, db_ = rnn_step_backward(dh[:, i, :] + dprev_h_, cache.pop())    
        dx[:, i, :] = dx_    
        dh0 = dprev_h_    
        dWx += dWx_    
        dWh += dWh_    
        db += db_

    return dx, dh0, dWx, dWh, db

def word_embedding_forward(x, W):
    N, T = x.shape
    V, D = W.shape
    out = np.zeros((N, T, D))
    for n in xrange(N):    
        for t in xrange(T):        
            out[n, t, :] = W[x[n, t]]

    cache = (x, W)

    return out, cache  

def word_embedding_backward(dout, cache):
    x, W = cache
    N, T, D = dout.shape
    dW = np.zeros(W.shape)
    for n in xrange(N):    
        for t in xrange(T):        
            dW[x[n, t]] += dout[n, t, :]

    return dW

--> rnn.py

__coauthor__ = 'Deeplayer'
# 8.13.2016 #

def loss(self, features, captions):
    captions_in = captions[:, :-1]
    captions_out = captions[:, 1:]
    mask = (captions_out != self._null)
    W_proj, b_proj = self.params['W_proj'], self.params['b_proj']
    W_embed = self.params['W_embed']
    Wx, Wh, b = self.params['Wx'], self.params['Wh'], self.params['b']
    W_vocab, b_vocab = self.params['W_vocab'], self.params['b_vocab']
    loss, grads = 0.0, {}

    # forward pass
    imf2hid = features.dot(W_proj) + b_proj         # initial hidden state: [N, H]
    word_vectors, word_cache = word_embedding_forward(captions_in, W_embed)   # [N, T, W]
    if self.cell_type == 'rnn':    
        hidden, rnn_cache = rnn_forward(word_vectors, imf2hid, Wx, Wh, b)    # [N, T, H]
    else:    
        hidden, lstm_cache = lstm_forward(word_vectors, imf2hid, Wx, Wh, b)
    scores, h2v_cache = temporal_affine_forward(hidden, W_vocab, b_vocab)    # [N, T, V]
    loss, dscores = temporal_softmax_loss(scores, captions_out, mask)

    # backward pass
    dhidden, grads['W_vocab'], grads['b_vocab'] = temporal_affine_backward(dscores, h2v_cache)
    if self.cell_type == 'rnn':    
        dword_vectors, dimf2hid, grads['Wx'], grads['Wh'], grads['b'] = rnn_backward(dhidden, rnn_cache)
    else:    
        dword_vectors, dimf2hid, grads['Wx'], grads['Wh'], grads['b'] = lstm_backward(dhidden, lstm_cache)
    grads['W_embed'] = word_embedding_backward(dword_vectors, word_cache)
    grads['W_proj'] = features.T.dot(dimf2hid)
    grads['b_proj'] = np.sum(dimf2hid, axis=0)

    return loss, grads

def sample(self, features, max_length=30):
    N = features.shape[0]
    captions = self._null * np.ones((N, max_length), dtype=np.int32)    # [N, max_length]
    # Unpack parameters
    W_proj, b_proj = self.params['W_proj'], self.params['b_proj']
    W_embed = self.params['W_embed']             # [V, W]
    V, W = W_embed.shape
    Wx, Wh, b = self.params['Wx'], self.params['Wh'], self.params['b']
    W_vocab, b_vocab = self.params['W_vocab'], self.params['b_vocab']       # [H, V]

    h = features.dot(W_proj) + b_proj            # [N, H]
    c = np.zeros(h.shape)
    init_word = np.repeat(self._start, N)
    captions[:, 0] = init_word
    for i in xrange(1, max_length):    
        onehots = np.eye(V)[captions[:, i-1]]    # [N, V]    
        word_vectors = onehots.dot(W_embed)      # [N, W]    
        if self.cell_type == 'rnn':        
            h, cache = rnn_step_forward(word_vectors, h, Wx, Wh, b)    
        else:        
            h, c, cache = lstm_step_forward(word_vectors, h, c, Wx, Wh, b)
        scores = h.dot(W_vocab) + b_vocab                                   # [N, V]
        captions[:, i] = np.argmax(scores, axis=1)

    return captions

Part 3: Long Short-Term Memory Networks (LSTM Networks)

對于上面提到的RNN,存在一個問題,就是無法解決長期依賴問題(long-term dependencies)。當時間序列變得很長的時候,前后信息的關聯度會越來越小,直至消失,即所謂的梯度消失現象。而LSTM這種特殊的RNN結構,可以解決長期依賴問題。LSTM由Hochreiter & Schmidhuber于1997年提出,之后有很多改進版本。
下面介紹下一般的LSTM,也將是我們在assignment中用到的結構,內容借鑒自Colah 的博文。
和RNN一樣,LSTM也是隨著時間序列重復著一樣的模塊,只是LSTM的每個某塊比RNN更加復雜,擁有四個層(3個門+1個記憶單元)。下圖方框內上方的那條水平線,被稱為胞元狀態(cell state),LSTM通過門結構對記憶單元上的信息進行線性修改,保證了當時間序列變得很長的時候,前后信息的關聯度不會衰減。

The repeating module in an LSTM

下面介紹下3個門:

遺忘門(Forget gate): 通過sigmoid來控制,它會根據上一時刻的輸出ht-1和當前輸入xt來產生一個0到1的值ft,來決定讓上一時刻學到的信息Ct-1通過的程度(即對上一時刻的信息Ct-1進行遺忘)。

Forget gate

輸入門(Input gate): 通過sigmoid來決定哪些值用來更新進cell state,這里的值是由一個tanh層生成的,稱為候選值Ct(上方少個 ~)。

Input gate

現在,我們對cell state進行更新(丟棄不需要的信息,添加新信息),如下所示:

Updating cell state

輸出門(Output gate): 通過sigmoid層來決定cell state的哪個部分將被輸出。接著,我們把當前的cell state通過tanh層進行處理,并將它和sigmoid層的輸出相乘,最終輸出我們確定要輸出的那部分信息 ht

Output gate

目前為止,我們所講的是標準的LSTM。LSTM 還有許多變體,這里我們介紹幾種變體。

Gers & Schmidhuber于2000年提出的,增加了 “peephole connection” 的LSTM。主要變化是:3個門層接受了cell state的輸入。

Peephole Connection

另一個變體是通過使用 coupled 遺忘門和輸入門,遺忘和輸入是同時進行的。從下圖的公式可以看出,新的信息僅僅是輸入到那些已經被遺忘的部分。

Coupled forget & input gate

另一個變體是 Gated Recurrent Unit (GRU),由 Cho, et al. 于2014年提出。它將忘記門和輸入門合成了一個單一的更新門。同樣還混合了胞元狀態和隱藏狀態,和其他一些改動。最終的模型比標準的 LSTM 模型要簡單。

GRU

在給出LSTM代碼前,我先給出一下使用標準LSTM進行Image captioning的模型結構圖:

Image Captioning by CNN+LSTM

代碼如下:

def lstm_step_forward(x, prev_h, prev_c, Wx, Wh, b):
    _, H = prev_h.shape
    a = x.dot(Wx) + prev_h.dot(Wh) + b       # (N, 4H)
    ai, af, ao, ag = a[:, 0:H], a[:, H:2*H], a[:, 2*H:3*H], a[:, 3*H:]
    i, f, o, g = sigmoid(ai), sigmoid(af), sigmoid(ao), np.tanh(ag)
    next_c = f * prev_c + i * g
    next_h = o * np.tanh(next_c)
    cache = (x, prev_h, prev_c, Wx, Wh, a, i, f, o, g, next_c, next_h)
  
    return next_h, next_c, cache


def lstm_step_backward(dnext_h, dnext_c, cache):
    _, H = dnext_h.shape
    x, prev_h, prev_c, Wx, Wh, a, i, f, o, g, next_c, next_h = cache
    ai, af, ao, ag = a[:, 0:H], a[:, H:2*H], a[:, 2*H:3*H], a[:, 3*H:]
    dnext_c += dnext_h * o * (1 - (np.tanh(next_c))**2)
    do = dnext_h * np.tanh(next_c)
    df = dnext_c * prev_c
    dprev_c = dnext_c * f
    di = dnext_c * g
    dg = dnext_c * i
    dai = di * (sigmoid(ai) * (1-sigmoid(ai)))
    daf = df * (sigmoid(af) * (1-sigmoid(af)))
    dao = do * (sigmoid(ao) * (1-sigmoid(ao)))
    dag = dg * (1 - np.tanh(ag)**2)
    da = np.hstack((dai, daf, dao, dag))               # (N, 4H)
    dx = da.dot(Wx.T)                                  # (N, D)
    dWx = x.T.dot(da)
    dprev_h = da.dot(Wh.T)
    dWh = prev_h.T.dot(da)
    db = np.sum(da, axis=0)

    return dx, dprev_h, dprev_c, dWx, dWh, db


def lstm_forward(x, h0, Wx, Wh, b):
    N, T, D = x.shape
    _, H = h0.shape
    cache = []
    hidden = h0
    h = np.zeros((N, T, H))
    cell = np.zeros((N, H))
    for i in xrange(T):
        hidden, cell, sub_cache = lstm_step_forward(x[:, i, :], hidden, cell, Wx, Wh, b)
        cache.append(sub_cache)
        h[:, i, :] = hidden

    return h, cache


def lstm_backward(dh, cache):
    x, prev_h, prev_c, Wx, Wh, a, i, f, o, g, next_c, next_h = cache[0]
    N, T, H = dh.shape
    N, D = x.shape
    dh0 = np.zeros((N, H))
    db = np.zeros(4*H)
    dWx = np.zeros((D, 4*H))
    dWh = np.zeros((H, 4*H))
    dx = np.zeros((N, T, D))
    dprev_c_ = np.zeros((N, H))
    dprev_h = np.zeros((N, H))
    for i in xrange(T-1, -1, -1):
        dx_, dprev_h, dprev_c_, dWx_, dWh_, db_ = lstm_step_backward(dh[:, i, :]+dprev_h, dprev_c_, cache.pop())
        dWx += dWx_
        dWh += dWh_
        db += db_
        dx[:, i, :] += dx_
        dh0 = dprev_h
  
    return dx, dh0, dWx, dWh, db

這里給出一些在驗證集上的結果:

Some examples on validation set

Part 4:圖像梯度(Image Gradients)

這部分我們將用預訓練好的CNN模型來計算圖像的梯度,并用圖像梯度來產生class saliency maps 和 fooling images。這部分我們會用到TinyImageNet數據集,它是ILSVRC-2012分類數據集的一個子集,包含了200個類,每一個類擁有500張訓練圖片,50張驗證圖片和50張測試圖片,每張圖片大小為64x64。TinyImageNet數據集被分成了兩部分:TinyImageNet-100-A和TinyImageNet-100-B,每部分包含100個類,這里我們使用的是TinyImageNet-100-A。

Examples of the TinyImageNet-100-A
1. Saliency Maps

給定一張圖片X,我們想要知道到底是圖片中的哪些部分決定了該圖片的最終分類結果。給定一個類,我們可以通過反向傳播求出X關于loss function的偏導矩陣,這個偏導矩陣就是該圖片的圖像梯度,然后計算出類顯著度圖(class saliency map, csm)。Karen Simonyan論文的3.1節給出了計算方法:如果圖片是灰度圖,那么csm就取圖像梯度的絕對值;如果是RGB圖,csm就取圖像梯度3個通道中絕對值最大的那個通道。csm中元素值的大小表示對應位置的圖片像素對最終分類結果的影響程度。

代碼如下:

def compute_saliency_maps(X, y, model):
    N,C,H,W = X.shape
    saliency = np.zeros((N,H,W))

    # Compute the score by a single forward pass
    scores, cache = model.forward(X, mode='test')    # Score size (N,100)

    # The loss function we want to optimize(maximize)
    # loss = (scores[np.arange(N), y] - lambda*np.sqrt(np.sum(X**2)))   # Size (N,)

    # The gradient of this loss wih respect to the input image
    dscores = np.zeros_like(scores)
    dscores[np.arange(N), y] = 1.0
    dX, grads = model.backward(dscores, cache)
    saliency += np.max(np.abs(dX), axis=1)

    return saliency

下圖是一些saliency maps的可視化結果:

Random images
Cherry-picked images

2. Fooling Images

我們可以用圖像梯度來生成 虛假圖像(fooling images)。給定一張圖片和一個目標類,我們可以對該圖片執行梯度上升(將圖像梯度不斷地疊加到原圖片上),以產生一個fooling image。該fooling image和原圖片在視覺上非常接近,但是CNN會把它識別成我們預先設定的目標類。

代碼如下:

def make_fooling_image(X, target_y, model):
    X_fooling = X.copy()
    N,C,H,W = X_fooling.shape      # N=1
    i = 0
    y_pred = -1
    lr = 200.0
    while (y_pred != target_y) & (i<200):
        scores, cache = model.forward(X_fooling, mode='test') # Score size (N,100)
        # The loss function we want to optimize(maximize)
        # loss = scores[np.arange(N), target_y]                 # Size (N,)
        # print loss
        # The gradient of this loss wih respect to the input image
        dscores = np.zeros_like(scores)
        dscores[np.arange(N), target_y] = 1.0
        dX, grads = model.backward(dscores, cache)
        X_fooling += lr*dX
        y_pred = model.loss(X_fooling).argmax(axis=1)
        i+=1
        print 'Iteration %d: current class: %d; target class: %d ' % (i, y_pred, target_y)

    return X_fooling
Left: original image, Middle: fooling image, Right: difference

從上圖結果我們可以看出:CNN依舊無法擺脫維度的詛咒這一難題,因為存在對抗樣本使它無法正確辨識。


Part 5:圖像生成(Image Generation)

這一部分我們繼續探索圖像梯度,我們將使用不同的方法通過圖像梯度生成圖像。

1. Class visualization

給定一個目標類(比如蜘蛛),我們可以在一個隨機噪聲圖像上,利用梯度上升來生成一個(蜘蛛)圖像,并且CNN會把它識別成目標類。具體實現方法可以參見論文:Deep Inside Convolutional Networks: Visualising Image Classification Models and Saliency Maps。

代碼如下:

def create_class_visualization(target_y, model, **kwargs):

    learning_rate = kwargs.pop('learning_rate', 10000)
    blur_every = kwargs.pop('blur_every', 1)
    l2_reg = kwargs.pop('l2_reg', 1e-6)
    max_jitter = kwargs.pop('max_jitter', 4)
    num_iterations = kwargs.pop('num_iterations', 200)
    show_every = kwargs.pop('show_every', 25)

    X = np.random.randn(1, 3, 64, 64)
    for t in xrange(num_iterations):
        # As a regularizer, add random jitter to the image
        ox, oy = np.random.randint(-max_jitter, max_jitter+1, 2)
        X = np.roll(np.roll(X, ox, -1), oy, -2)

        # Compute the score and gradient
        scores, cache = model.forward(X, mode='test')
        # loss = scores[0, target_y] - l2_reg*np.sum(X**2)
        dscores = np.zeros_like(scores)
        dscores[0, target_y] = 1.0
        dX, grads = model.backward(dscores, cache)
        dX -= 2*l2_reg*X

        X += learning_rate*dX

        # Undo the jitter
        X = np.roll(np.roll(X, -ox, -1), -oy, -2)

        # As a regularizer, clip the image
        X = np.clip(X, -data['mean_image'], 255.0 - data['mean_image'])

        # As a regularizer, periodically blur the image
        if t % blur_every == 0:
            X = blur_image(X)

        # Periodically show the image
        if t % show_every == 0:
            print 'The loss is %f' % loss
            plt.imshow(deprocess_image(X, data['mean_image']))
            plt.gcf().set_size_inches(3, 3)
            plt.axis('off')
            plt.title('Iteration: %d' % t)
            plt.show()

    return X

下圖是迭代過程中生成的(蜘蛛)圖像:

Generated images
2. Feature Inversion

這部分我們將完成一個很有意思的工作:在一張隨機噪聲圖像上重建出指定層CNN學習到的圖像特征表達。詳細的實現方法參見論文: Understanding Deep Image Representations by Inverting themUnderstanding Neural Networks Through Deep Visualization。

代碼如下:

def invert_features(target_feats, layer, model, **kwargs):
    learning_rate = kwargs.pop('learning_rate', 10000)
    num_iterations = kwargs.pop('num_iterations', 500)
    l2_reg = kwargs.pop('l2_reg', 1e-7)
    blur_every = kwargs.pop('blur_every', 1)
    show_every = kwargs.pop('show_every', 50)

    X = np.random.randn(1, 3, 64, 64)
    for t in xrange(num_iterations):

        # Forward until target layer
        feats, cache = model.forward(X, end=layer, mode='test')

        # Compute the loss
        loss = np.sum((feats-target_feats)**2) + l2_reg*np.sum(X**2)

        # Compute the gradient of the loss with respect to the activation
        dfeats = 2*(feats-target_feats)
        dX, grads = model.backward(dfeats, cache)
        dX += 2*l2_reg*X

        X -= learning_rate*dX

        # As a regularizer, clip the image
        X = np.clip(X, -data['mean_image'], 255.0 - data['mean_image'])

        # As a regularizer, periodically blur the image
        if (blur_every > 0) and t % blur_every == 0:
            X = blur_image(X)

        if (show_every > 0) and (t % show_every == 0 or t + 1 == num_iterations):
            print loss
            plt.imshow(deprocess_image(X, data['mean_image']))
            plt.gcf().set_size_inches(3, 3)
            plt.axis('off')
            plt.title('Iteration: %d' % t)
            plt.show()

下圖是迭代過程中生成的圖像特征(淺層和深層):

Shallow feature reconstruction
Deep feature reconstruction
3. DeepDream

這部分我們體驗一下簡化版的DeepDream。實現思想很簡單,我們先選定CNN的某一層(我們將在該層dream),然后將需要dream的圖像輸入進預訓練好的CNN。前向傳播至目標層,令該層的梯度等于該層的激活值(特征)。然后反向傳播至輸入層,求出圖像梯度,同過梯度下降法將圖像梯度不斷疊加到輸入圖像上。

代碼如下:

def deepdream(X, layer, model, **kwargs):
    X = X.copy()
    learning_rate = kwargs.pop('learning_rate', 5.0)
    max_jitter = kwargs.pop('max_jitter', 16)
    num_iterations = kwargs.pop('num_iterations', 200)
    show_every = kwargs.pop('show_every', 50)

    for t in tqdm(xrange(num_iterations)):
        # As a regularizer, add random jitter to the image
        ox, oy = np.random.randint(-max_jitter, max_jitter+1, 2)
        X = np.roll(np.roll(X, ox, -1), oy, -2)

        # Forward until dreaming layer
        fea, cache = model.forward(X, end=layer ,mode='test')

        # Set the gradient equal to the feature
        dfea = fea
        dX, grads = model.backward(dfea, cache)
        X += learning_rate*dX

        # Undo the jitter
        X = np.roll(np.roll(X, -ox, -1), -oy, -2)

        # As a regularizer, clip the image
        mean_pixel = data['mean_image'].mean(axis=(1, 2), keepdims=True)
        X = np.clip(X, -mean_pixel, 255.0 - mean_pixel)

        # Periodically show the image
        if t == 0 or (t + 1) % show_every == 0:
            img = deprocess_image(X, data['mean_image'], mean='pixel')
            plt.imshow(img)
            plt.title('Iteration: %d' % (t + 1))
            plt.gcf().set_size_inches(8, 8)
            plt.axis('off')
            plt.show()

    return X

接下來我們就可以生成DeepDream圖像啦!

Tibidabo
Deeplayer
Leaning Tower

---> CS231n : Assignment 1
---> CS231n : Assignment 2

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯系作者。

推薦閱讀更多精彩內容