天天看點

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

Hello,大家好,這裡是從不拖更的糖葫蘆喵喵~!

ようこそ実力至上主義のCS231n教室へ

天気がいいから、いっしょに散歩しましょう。(劃掉煩人的聽力!)

考N1什麼的才不會耽誤更新!大家一定要保佑喵喵一次通過啊~

那麼,最後一篇CS231n 2017 Spring Assignment3 就要開始了~!

喵喵的代碼實作:Observerspy/CS231n,歡迎watch和star!

視訊講解CS231n Assignment3:

Assignment 3-AI慕課學院​www.mooc.ai

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

Part 1 Image Captioning with Vanilla RNNs

下面讓我們來和普通RNN一起玩耍,并完成一個簡單的看圖說話(圖像語義分析)的任務吧!

1.1 Vanilla RNN
圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...
1.1.1 RNN單元

首先我們來看對于一個RNN單元:

前向:

向右的箭頭:也就是

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

從左到右的傳播過程:

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

向上的箭頭:輸出層,是一個softmax:

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...
反向:

反向我們需要計算dWx,dWh,db,dx和dh,不管計算哪一個,都要先把

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

這層先去掉,其導數是

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

。是以上遊傳下來的dout和導數先相乘,我們把這個值先記為dtheta。

剩下的就是一個線性的式子,分别對我們要的5個參數求導:

db = sum(dtheta)

dWx = dtheta

dot

x (是

矩陣乘

哦,然後還有

注意形狀

!這裡喵喵給出的不是實際式子,應該誰和誰相乘請按照形狀自己想一想~)

dWh = dtheta

dot

h

dx = dtheta

dot

Wx

dh = dtheta

dot

Wh

1.1.2 完整RNN

每個單元都是一樣的,那麼,對于完整的RNN:

前向:

也就是要對RNN單元循環T次,T是序列的長度。

傳回來看RNN單元前向公式,每次輸入5個參數,輸出一個

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...
又是下一個循環的輸入

。是以在循環内部我們要不斷更新

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

,并記錄它。這裡就不贅述了。

反向:

RNN反向和之前我們計算的反向都不太一樣,因為之前我們計算反向時上遊傳回來的值隻有dout一個。在RNN示例圖中,反向時所有箭頭都要翻轉,也就是說,不僅有從右邊來的dprev_h,還有從上面來的dh。

既然有兩個那麼加起來就好了。

首先我們逆序循環:

for t in range(T-1, -1, -1):
           

對于每一個時間片t,上面來的導數是dh[:, t, :](形狀是(N, T, H)),右邊來的暫時記作dprev_h。

對于最後一個單元,它的右邊沒有傳來導數,是以初始化dprev_h是0。

于是對于每一個RNN單元,需要傳進去的導數就是

dh[:, t, :]+dprev_h

。顯然dprev_h是需要更新的。

注意到我們在實作RNN單元的反向傳播時還需要有一個參數cache,是以還需要構造cache,這時候需要注意其中有些元素的

下标

參數計算完畢,利用剛才實作的反向傳播就可以計算出5個導數了。

但是還沒有完!

對于dx,我們可以計算它每一個時刻的導數,那麼剩下dWx,dWh,db和dh呢?注意到

其實RNN中,

這四個參數在不同時刻是共享的,

是以需要在

每一個時刻把它們都加起來做更新

RNN我們做完啦!下面讓我們來看看這個看圖說話(圖像語義分析)任務是什麼樣!

1.2 Image Captioning

簡單說就是看圖說話,給你一幅圖,給出關于這幅圖的說明文字。網絡結構如下圖所示:

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...
1. 隐藏層

輸入一幅圖檔,從訓練好的vgg16的fc7層中取出

特征

,當做

h0

輸入到RNN中。這樣剩下的任務就是訓練網絡,使之能夠輸出圖檔說明句子(單詞序列)。然後h還要經過一次線性變換。

如何表示單詞呢?做過自然語言處理想必一定知道必不可少的操作就是Word embedding。其實就是把onehot編碼的

單詞映射為一個向量

,這個

映射

你可以自己從頭訓練,也可以用現成的比如word2vec,glove等繼續訓練,這裡不再贅述。那麼我們來看Word embedding的具體操作:

1.2.1 Word embedding 前向:

就是從

映射矩陣

裡取出你要的單詞對應的vec,直接取就好了。

反向:

反向隻是選出dout中對應x的值就行了,注意各部分的形狀,np.add.at()可以實作這一操作:

dW = np.zeros(W.shape) #dW.shape: (V, D)
np.add.at(dW, x, dout) #x,shape(N, T) dout.shape: (N, T, D)
           

這樣,對于RNN而言通過上面的兩個步驟,兩個輸入(隐藏層的

h0

embedding

的單詞)都準備好了,就可以進行訓練了!

1.2.2 網絡結構:

圖檔特征 -> FC -> h0

單詞 -> embedding -> input

(h0, input) -> RNN -> Output

Output ->

時序FC

->

時序softmax

-> Loss

注意最後的loss是時序FC和時序softmax,具體可以看給出的實作(其實沒有太大的差别,就是形狀不一樣:原來是輸入是(N, D)現在是(N, T, D),要來回reshape(N*T, D))。

前向和反向過程按照上述結構組合子產品就可以了!

注意訓練時候輸入是captions[:, :-1],label是captions[:, 1:],也就是用

下一個詞當做目前詞的label

captions的第一個詞是start标記,最後一個詞是end标記

測試的時候稍有不同,我們需要構造一個輸入,它的第一個單詞是start标記。和訓練時不同的是,

測試時我們不知道T的大小

,是以

限定了最大輸出長度

,需要在循環裡用RNN前向單元來進行預測:

for t in range(1,max_length):
    word_embedding_forward()
    rnn_step_forward()
    affine_forward()
    captions[:,t] = np.argmax()
           

這樣所有的結構都完成了,快去進行愉快的訓練和測試吧!看看結果是不是很有趣?

Part 2 Image Captioning with LSTMs

RNN對長一點的序列有難以訓練的缺點,那麼我們就用LSTM替換它!

整個Image Captioning結構就不在重複說了,這裡着重說明一下LSTM。

2.1 LSTM單元
圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...
前向:

解釋一下這個圖

從左向右傳遞的兩個:

上面是
圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...
下面是
圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

從下向上計算的分别是:

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

,計算公式如下:(空心圈是元素乘)

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

注意到我們有三個輸入c、h和x,同時Wx、Wh和b都分别有四個,實際上代碼中将四個Wx、Wh和b連結在了一起,是以

f、i、g、o的線性部分可以通過以一次計算完成

z = x.dot(Wx) + prev_h.dot(Wh) + b
           

z就變成了(N, 4H)的形狀,取出對應的部分計算c、h即可。

反向:

反向要求dWx,dWh,db,dx,dprev_h和dprev_c。

和RNN不同,

這次反向傳回來的值有兩個,分别是dnext_h和dnext_c

回到圖中分析,将

所有箭頭反過

來發現

o隻受到dnext_h影響,而i,f,g都是收到

dnext_c與dnext_h通過tanh傳回上面的值(記作dh2c)的和

影響。

式子比較多,我們先不管那四個一樣的線性部分(把它們記作i,f,o,g)

針對

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

首先求do(o是sigmoid求出來的,複習一下sigmoid的導數:sigmoid * (1 - sigmoid))

do = dnext_h * tanh(c) * o * (1 - o)

然後求

dnext_h通過tanh傳回上面的值(dh2c):

dh2c = dnext_h * o * (1 - tanh(c)**2)

這個值和

dnext_c

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

的計算中彙合(就是把兩個值加起來),共同影響了i,f,g

針對

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

:(傳回這個式子的導數就是

dnext_h + dh2c

di = (dnext_h + dh2c) * g * i * (1 - i)

df = (dnext_h + dh2c) * prev_c * f * (1 - f)

dg = (dnext_h + dh2c) * i * (1 - g**2)

dprev_c = (dnext_h + dh2c) * f

然後把di,df,do和dg用np.hstack連結在一起(記作d)就得到了線性部分的導數。

剩下的線性部分導數就很簡單了

dx = d

dot

Wx

dWx = d

dot

x

dWh = d

dot

prev_h

dprev_h = d

dot

Wh

db = sum(d)

注意對應形狀,大功告成!

2.2 完整LSTM

LSTM單元完成了,剩下的LSTM子產品其實和RNN差不多啦

前向:

比RNN多了一個c,初始為0,記得循環裡要更新它。

反向:

也是多了一個c,初始為0,循環裡要更新它。

注意代碼中cache的構造方式和RNN不同

好了,現在你可以把LSTM加入到Image Captioning中的網絡結構裡去了!看看效果吧!

Part 3 Network Visualization: Saliency maps, Class Visualization, and Fooling Images

複雜的公式和求導再也沒有了,從這裡開始都是有趣的例子,讓我們一起看看吧!

3.1 Saliency maps

顯著圖,也就是要看看我們訓練好的網絡在進行圖檔分類時

關注的是圖檔的那些地方(像素點)

和之前不同的是,以前我們的梯度都是針對參數進行計算的,現在我們要利用

正确分類的score

去計算

輸入圖檔的梯度。

然後取

梯度的絕對值

,在

3個通道上選擇最大

的那個值。

這裡注意求梯度的函數tf.gradients()傳回的是一個

list

,我們隻要取

tf.gradients()[0]

的值就行了。

然後運作可視化出來,是不是很有趣的結果?

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...
3.2 Fooling Images

調整我們的圖檔,使它

騙過我們訓練好的網絡,

也就是用

目标分類

輸入圖檔

梯度來

疊代

更新輸入圖檔。

首先計算目标分類對于目前輸入圖檔的梯度,注意這裡label要用tf.one_hot轉換成onehot編碼形式:

loss = tf.nn.softmax_cross_entropy_with_logits(labels=tf.one_hot(target_y, 1000), logits=model.classifier)
g = tf.gradients(loss, model.image)[0]
           

然後按照要求計算normalize的梯度,并梯度上升更新輸入:

X_fooling = model.image - dX
           

一般在100輪疊代内即可完成,是以要寫一個循環,循環裡面計算:

X = sess.run(X_fooling, feed_dict={model.image: X})
           

注意這行代碼實際上是在不斷更新X(也就是一開始我們的輸入)。

當然你可以不用等100輪疊代完成,用下面的子產品做一個判斷然後break就行啦。

scores = sess.run(model.classifier, {model.image: X})
if scores[0].argmax() == target_y:
    print(i," ",class_names[scores[0].argmax()])
    break
           

然後跑起來吧,得到的結果如下圖,看起來一樣,但是你的網絡已經認為它不一樣了!

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...
3.3 Class Visualization

類别可視化,和顯著圖類似,不過這次我們要看的是訓練好的網絡

關注的每個類别圖檔是什麼樣子

。基本和上一個任務一樣,隻不過這次我們在

随機噪聲圖檔

上更新目标分類的梯度。

注意根據要求這次loss要減去一個l2正則項。

循環裡依舊更新X,最終跑出來的結果:

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

竟然是蜘蛛!(逃...

Part 4 Style Transfer

風格遷移,顧名思義,就是把一幅圖檔的風格轉移到另一幅圖檔上,效果如下:

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

網絡結構是這樣子的:

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

簡單說就是定義一個新的loss,然後對随機噪聲圖檔進行梯度更新。注意三個圖檔是經過

同樣的訓練好的網絡的

4.1 loss

為了達到這個效果,首先我們要考慮loss。新的loss由三項構成:content loss、style loss和total variation loss。

4.1.1 content loss

内容loss反映了

生成的圖檔内容

源内容圖檔

的差異,很簡單,用feature map度量:

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

其中

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

生成圖檔在網絡中第
圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

的feature map,

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

源内容圖檔在網絡中第
圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

的feature map,

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

是權重。可以用2 * tf.nn.l2_loss()實作(l2_loss是有系數0.5的)。

4.1.2 style loss

風格loss目的就是為了衡量

生成的圖檔風格

源風格圖檔

的差異。而風

格是用feature map的協方差矩陣度量

的,是以新定義了一個Gram matrix:

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...
圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

實際上協方差矩陣反應了feature map值之間的關聯性,通過相乘,使得

原來大的值更大

原來小的值更小,

也就是要突出自己的feature map的特點。

和内容loss一樣,風格差異定義如下:

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

其中

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

生成圖檔在網絡中第
圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

的feature map,

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

源風格圖檔在網絡中第
圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

的feature map,

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

是權重。實際計算的時候往往用

多層的feature map來計算風格loss

,是以最後要對不同層的風格loss

進行求和

4.1.3 total variation loss

這一項正則可以使生成圖檔更平滑:

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

其中

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

是生成圖像,

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

是權重。這個式子乍一看很複雜,其實這個式子就是在H和W次元上計算

相鄰像素的內插補點

的平方和,隻要計算好

相鄰像素的內插補點:
pixel_dif1 = img[:, 1:, :W-1, :] - img[:, :H-1, :W-1, :]
pixel_dif2 = img[:, :H-1, 1:, :] - img[:, :H-1, :W-1, :]
           

實際上tf裡提供了絕對值版本的

total variation loss:

tf.image.total_variation()。也就是不算平方,隻用絕對值。

至此,三個loss計算完畢,加在一起就好了。

代碼跑起來就可以看到上面的效果了,是不是很有趣?

這裡有很多可以調的參數,loss的三個權重,内容loss和風格loss的層選擇等等,這些參數對于結果有很大影響。

特别的,當風格loss權重為0時,是對源内容圖像的重建,當然你也可以試試内容loss權重為0的情況。

另外,和這個需要不斷疊代的風格遷移不同,有更快的fast style transfer方法去進行遷移,詳見部落格:談談圖像的Style Transfer(一),談談圖像的style transfer(二)。

Part 5 Generative Adversarial Networks(GAN)

終于到了我們作業的最後階段啦!

生成對抗網絡可以說是近年最火的一個研究方向,它生成的圖檔品質之高讓人驚歎。那麼就讓我們來看看它的結構吧!

GAN其實就是

生成器

判别器

之間的

博弈

,一方面

判别器

從真實資料和生成資料中不斷學習

提高自己的判别能力

,另一方面

生成器

不斷疊代以

提高自己的欺騙能力

。這樣,我們就可以用loss來表達我們的需求了:

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

這個公式有兩層意思:

  1. 更新生成器 使得判别器做出 正确判斷 的機率 下降
  2. 更新判别器 使得判别器做出 正确判斷 的機率 上升

所謂正确判斷是指判别器把

真實資料判别為真,生成樣本資料判别為假

。這個過程也就是我們說的博弈的過程。但是一邊上升一邊下降是很難計算的,是以我們把1.反過來說:

  1. 更新生成器 使得判别器做出 錯誤判斷 的機率 上升

1.對應公式:

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

2.對應公式:

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

接下來,我們分别要實作vanilla gan、ls-gan、dc-gan和wgan-gp。至于哪一種gan好,谷歌說,都差不多2333。詳見那麼多GAN哪個好?谷歌大腦潑了盆冷水:都沒能超越原版|論文。不過從效果上看,我覺得dc-gan在手寫數字生成上更好一些。

下面就是實作不同的gan了,都是調用tf的函數,建議使用

tf.layers裡面的函數

,省的自己定義參數了,友善快捷,效果也好。

5.1 vanilla gan 判别器:

Fully connected layer from size 784 to 256

LeakyReLU with alpha 0.01

Fully connected layer from 256 to 256

LeakyReLU with alpha 0.01

Fully connected layer from 256 to 1

生成器:

Fully connected layer from tf.shape(z)[1] (the number of noise dimensions) to 1024

ReLU

Fully connected layer from 1024 to 1024

ReLU

Fully connected layer from 1024 to 784

TanH (To restrict the output to be [-1,1])

loss:有三個部分,

一定注意label應該對應誰。
D_loss1 = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits = logits_real, labels = D_label))
D_loss2 = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits = logits_fake, labels = G_fake_label))
G_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits = logits_fake, labels = G_real_label))
           
5.2 ls-gan

loss有所變動,不在計算交叉熵了:

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...
圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...
5.3 dc-gan 判别器:

32 Filters, 5x5, Stride 1, Leaky ReLU(alpha=0.01)

Max Pool 2x2, Stride 2

64 Filters, 5x5, Stride 1, Leaky ReLU(alpha=0.01)

Max Pool 2x2, Stride 2

Flatten

Fully Connected size 4 x 4 x 64, Leaky ReLU(alpha=0.01)

Fully Connected size 1

生成器:注意conv2d^T (transpose)操作是反卷積tf.nn.conv2d_transpose

Fully connected of size 1024, ReLU

BatchNorm

Fully connected of size 7 x 7 x 128, ReLU

BatchNorm

Resize into Image Tensor

64 conv2d^T (transpose) filters of 4x4, stride 2, ReLU

BatchNorm

1 conv2d^T (transpose) filter of 4x4, stride 2, TanH

5.4 wgan-gp 判别器:

64 Filters of 4x4, stride 2, LeakyReLU

128 Filters of 4x4, stride 2, LeakyReLU

BatchNorm

Flatten

Fully connected 1024, LeakyReLU

Fully connected size 1

生成器同上一個網絡。

loss:這裡多了

gradient penalty

的操作。優點詳見鄭華濱:生成式對抗網絡GAN有哪些最新的發展,可以實際應用到哪些場景中?

由于涉及内容較多,具體的

gradient penalty

實作請看代碼。

5.5 效果

最後的最後,讓我們比較一下四種gan的輸出結果吧!

左上vanilla gan右上ls-gan左下dc-gan右下wgan-gp。看起來還是dc-gan好呢!

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

恭喜看到這裡的大家,你們已經完成了所有的Assignment!相信你們一定學到了很多東西,請在接下來的旅程中繼續努力吧!

じゃ、おやすみ~!

圖像風格遷移_歡迎來到實力至上主義的CS231n教室(三) 圖像标注、風格遷移和GAN...

下期預告: CS224n Assignment1作業詳解

對你沒看錯!CS224n!記得來看哦!