在講Sequence Generation之前,再復習下RNN和有門的RNN(LSTM,GRU)
之前告訴你說,RNN是一個有記憶的神經網絡,但今天從另外一個角度來講RNN。我們說RNN特別的地方是它里面有一個basic函數,用\(f\)來表示,在RNN里面會被反復使用。這個basic函數的input是兩個向量(\(h,x\)),output是另外兩個向量,寫作\(h',y\),我們知道說要使用RNN時,你的輸入是一個向量序列,現在向量序列用\(x^1,x^2,x^3\)表示。
現在第一個向量\(x^1\)進來,是basic函數\(f\)的input,\(f\)還需要另外一個input \(h^0\)(在用RNN的時候,有一個初始化的向量),然后\(f\)產生兩個向量\(h^1,y^1\)。
接下來新的\(x^2\)進來,同樣的\(f\)會被反復使用。這邊是用反復使用某一個函數來說明RNN,在之前告訴你說,神經元會把\(h^1\)存下來在下一個時間點被使用。\(f\)把\(x^2,h^1\)作為input,output \(y^2,h^2\)。你可以想象說,如果你要反復使用函數\(f\),那么\(h^0,h^1,h^2\)的維度一定是要一樣的。
RNN比較強的地方在,不管input的sequence有多么長,它都是以不變應萬變,都只有一個basic函數\(f\),所以RNN的參數量不會隨着input sequence的長度而改變,特別適合用來處理input sequence特別長的狀況
RNN可以是deep的,比如本來已經有一個basic函數\(f_1\),現在再加另一個basic函數\(f_2\)。\(f_2\)一樣是接受兩個向量作為input,輸出另外兩個向量。
如上圖所示,\(f_2\)接受\(f_1\)的output \(y^1\),再接受另一個初始化向量\(b^0\),輸出\(b^1,c^1\)......
我們也講過雙向RNN,一樣有兩個函數,\(f_1\)就是之前看到的,\(f_2\)接受input的方向和\(f_1\)相反,\(f_2\)先input \(x^3\),再input \(x^2\),再input \(x^1\) 。\(f_1\)會產生一排向量(\(y^1,y^2,y^3\)),\(f_2\)也會產生另外一排向量(\(c^1,c^2,c^3\))。
接下來要有另外一個函數\(f_3\),接受兩個向量\(a^1,c^1\),得到最后的output。
這個RNN我們說是由一個basic函數組成的,basic函數\(f\)長什么樣子是自己自由設計的,只要能接受兩個向量\(x,h\),output另外兩個向量\(y,h'\),唯一的限制是\(h,h'\)的維度要一樣,\(x,y\)的維度不需要一樣。
\(f\)最簡單的設計方式如上圖右邊所示,\(x\)乘上一個矩陣\(W^i\)(結果還是一個向量),\(h\)乘上另一個矩陣\(W^h\)(結果也是一個向量),把這兩個結果向量加起來,通過一個激活函數得到\(h'\)。這個激活函數通常使用Hyperbolic tangent(雙曲正切函數,tanh),我們知道在一般的深度網絡里面通常會用Relu,但在RNN里面用Relu表現不會太好。
有了\(h'\)之后,乘上\(W^0\)得到一個向量,在通過一個激活函數得到\(y\),這個是最簡單的basic函數的設計,但不是最常見的設計。
最常見的設計是LSTM。
之前RNN是input \(h^{t-1},x^t\),output \(h^t\)。LSTM不一樣的地方是把本來的\(h\)拆成兩塊,如上圖所示,紅色的\(c^{t-1}\)和藍色的\(h^{t-1}\)串在一起就是綠色的\(h^{t-1}\),紅色的向量和藍色的向量串在一起就是綠色的向量。在LSTM里面我們特別說,input和output都分兩部分,兩種不同的向量有不同的特性。
在LSTM里面,\(c\)是一個變換比較慢的memory,\(c^t\)和\(c^{t-1}\)會非常像,\(c^t\)就是\(c^{t-1}\)加上某個東西,而\(h^t\)和\(h^{t-1}\)是非常不一樣的。像一般的RNN里\(h^t\)和\(h^{t-1}\)就非常不像,\(h^{t-1}\)會乘上一個transformation以后再加上某個東西變成\(h^t\)(\(h^{t-1}\)會乘以一個矩陣\(W\))。
LSTM強的地方在於,相較於一般的RNN,不太會忘記很久以前的信息,就是因為有\(c^{t-1}\)跟\(c^t\)的關系,變化是比較緩慢的,如果有要長時間記憶的東西,就放在\(c\)里面。
實際看一下LSTM的計算過程。
input 三個向量\(c^{t-1},h^{t-1},x^t\),如上圖右上方所示,把\(x^t,h^{t-1}\)接在一起變成一個比較長的向量,再乘上一個矩陣\(W\)再通過tanh()得到一個向量\(z\) 。
\(x^t,h^{t-1}\)接在一起的向量會乘上不同的transform矩陣,可以制造出不同的向量。如上圖乘上了一個矩陣\(W^i\) ,和\(W\)是不一樣的,但都可以從訓練數據中學習出來。之后通過一個激活函數得到\(z^i\),以此類推得到\(z^f、z^o\)。
\(z^i\)就是輸入門的input,\(z^f\)是遺忘門的input,\(z^o\)是輸出門的input。
除了把\(x\)跟\(h\)串在一起乘上一個transform以外,你要做的更好還有一招叫做peephole,把\(c\)跟\(h、x\)也串在一起,再乘上一個特別長的矩陣\(W\),再通過tanh()得到\(z\)。你可以想象說,加了\(c\)后向量變長了,所以\(W\)的參數就變多了,實際上這樣做表現不一定會好,因為參數變多了就容易過擬合。實際你要做peephole的話,你會強迫矩陣的右邊部分是diagonal的(對角化的),也就是說現在\(x^t\)會乘上藍色矩陣\(W\)的前三分之一,\(h^{t-1}\)會乘上藍色矩陣\(W\)中間的三分之一,\(c^{t-1}\)會乘上藍色矩陣\(W\)的對角化部分(最后三分之一),乘對角化矩陣的好處是可以減少參數使用量。所以\(C^{t-1}\)不同維度不會有相關性,不同維度就是各自乘上一個數值而已。
\(z^o、z^f、z^i\)也是做同樣的事情。
有了四個向量(\(z^f、z^i、z、z^o\))之后,接下來做的事情就是把\(z、z^i\)做元素乘積,\(c^{t-1}、z^f\)也做元素乘積,把兩個元素乘積加起來得到\(c^t\)。從這個結果可以看到,原來的\(c^{t-1}\)跟\(z^f\)做元素乘積再加上一個東西。所以發現說,\(c^t\)跟\(c^{t-1}\)可能沒有太大差別,變化比較少,那么一些不太需要的信息就可以放在\(c\)里面。你會意外發現說,\(c^{t-1}\)跟\(c^t\)之間甚至連非線性激活函數都沒有。
\(h^t\)是從\(c^t\)算出來的,\(c^t\)先做tanh(),然后和\(z^0\)做元素乘積,所以發現說\(h^{t-1}\)跟\(h^t\)非常不一樣。\(h^{t-1}\)做了transform之后才得到\(z^0、z^i、z^f\),間接影響\(c^t\)(\(c^t\)做計算得到\(h^t\))。\(h^t\)和\(h^{t-1}\)有關系,但是是非常不一樣的。
最后要得到\(y^t\)的話,就是把\(h^t\)乘上一個矩陣\(W'\)再通過一個激活函數。
我們知道說同樣的block(上圖右上方所示)會反復使用,我們知道RNN的特性就是同樣的basic函數被反復使用,那在LSTM里面block被反復使用,所以\(c^t、h^t\)又接入下一個block,產生新的\(c^{t+1}、h^{t+1}\)。
除了LSTM,GRU也是非常常用的RNN的basic函數。GRU input一個\(h^{t-1}\),output一個\(h^t\),input一個\(x^t\),output一個\(y^t\),表面上看起來跟原來的RNN一樣。然而GRU更像是LSTM,GRU里\(h\)的角色比較像LSTM里\(c\)的角色(沒有LSTM里\(h\)的角色)。
GRU的運作方式是:
- input \(x^t\)跟\(h^{t-1}\),首先把\(x^t\)跟\(h^{t-1}\)接起來,再乘上一個transform(藍色箭頭)后得到\(r\)(叫做reset 門),乘上另一個transform后得到\(z\)(叫做update門)。
- 有了reset門和update門之后,把\(r\)跟\(h\)先做元素乘積得到一個向量,這個向量跟\(x^t\)並在一起后再通過一個transform(黃色箭頭)得到\(h'\)。
- 把\(z\)跟\(h^{t-1}\)做元素乘積后得到一個向量,\(1-z\)跟\(h'\)做元素乘積后得到另一個向量,這兩個向量加起來得到\(h^t\)
- \(h^t\)乘上一個transform之后得到\(y^t\)
把式子列出來就是\(h^t=z \bigodot h^{t-1}+(1-z)\bigodot h'\)。
那可以看到GRU的參數量是比LSTM少的,可以減少過擬合的可能性。看上圖所示,LSTM有4個transform(不同顏色粗箭頭,不算到\(y^t\)的transform),GRU只有三個transform(不同顏色粗箭頭,兩個黃色算一個transform)。
那么GRU跟LSTM有什么關系呢?
GRU可以再看做是LSTM里面遺忘門跟輸入門是聯動的,從上面的圖很難看出來。現在把GRU里面的\(h\)想象成是LSTM的\(c\),因為GRU里面\(h^t\)跟\(h^{t-1}\)是非常相近的,看式子里,只是差了一個東西而已。
那GRU的\(h\)在哪里呢?
在LSTM里面,\(c\)乘上輸出門得到\(h^t\)。在GRU中把\(h^{t-1}\)想成\(c\),reset門想成是輸出門,\(h^{t-1}\)乘上reset門得到的就是\(h\)。LSTM里面的\(h\)變化很快,那在GRU里面\(h\)不會被拿到輸出的地方。 \(h^{t-1}\)既然是memory,那跟memory相乘的東西就是遺忘門(上圖右邊2處),上圖3處就是輸入門,輸入門和遺忘門是聯動的(一個是\(z\),一個是\(1-z\))。
\(h^t\)跟\(y^t\)有什么差別?
不管是在LSTM還是GRU里面,都是把\(h^t\)乘上一個transform之后(灰色箭頭),才會得到\(y^t\)。
Sequence Generation
要講的是怎么讓讓機器產生出東西,過去機器學習通常是做分類。假設要讓機器output一個sentences,怎么做呢?我們知道說一個句子,是由words(詞)或者characters(字)組成的。舉例來說,如果是英文的話,英文的詞和詞之間是有空白隔開的,比如apple是一個word,那a到e這5個字母就是characters(因為里面的字)。那對中文來說的話,每一個詞匯是由數個字所組成的,比如說葡萄(詞)是由葡(字)萄(字)組成的。
今天要讓機器學習做Sequence Generation(寫一個句子)的話,我們可以用characters(字)為單位,也可以用詞為單位,我們只要讓RNN每次都可以產生出一個字或是一個詞就可以了。
我們說RNN是一個basic函數,input \(h\)和\(x\),output \(h'\)和\(y\)。在做Generation的時候,這個\(x\)是前一個時間點產生出來的token(不管是字符還是詞),\(y\)是現在機器要產生出來的token的distribution,但我們要的是某一個字或詞,而不是token的distribution,所以最后要從\(y\)這個distribution里面sample出一個token。如上圖最上方,以字符為單位,假如在某個時間點,機器寫了一個“我”,那么此時\(x\)就是“我”位置為1的向量\([0 \ 1\ 0 \ 0 \cdots 0]\),而\(y\)這個向量是在每個字符上的distribution(概率分布),然后根據概率分布sample出字符,比如0.7概率為“我是”,0.3概率為“我很”。
說說機器是怎么完整的產生Sequence的。
要產生第一個字符的時候,會給機器一個非常特殊的字源,只有在句子的開頭才會發生,通常叫做begin of sentence(
- 如上圖所示,把\(y^1\)看作是給定句首(
)這個token的時候某一個character出現的概率分布,然后根據這個分布做sample,可能sample出“床”這個字(也可以argmax,但這樣每次取出來的都是同一個字)。 - 接下來把“床”作為\(x^2\)向量(對應到“床”的維度為1,其他維度為0)丟進去,經過同樣的函數\(f\),input是\(x^2\)和\(h^1\),output是\(h^2\)和\(y^2\),同樣的對\(y^2\)做sample,可能就得到下一個字(“前”)。\(y^2\)就是一個概率分布,在床之后每個字出現的概率。
- 然后把“前”作為\(x^3\)向量丟進去,得到\(h^3\)和\(y^3\),\(y^3\)可以看作是已經有“床、前”后,下一個字出現的概率分布。\(y^3\)不是只看到了“前”,還有“床”,因為還input了\(h^2\),然后可能“明”出現的概率最大,這樣就能寫出一首詩。
- 那什么時候停止呢?你會設計一個特殊的符號
,代表句子的結束,即當機器產生 時停止。
這個是生成的過程,我們還沒有講怎么得到這個\(f\)函數。
要找出這個\(f\),首先要給機器訓練數據,比如你要給機器讀唐詩三百首,例如“春眠不覺曉”。給
剛才說的是產生一個句子,也可以產生其他東西,比如讓機器畫一個圖。
我們知道一張圖片是由一大堆像素組成的,如果把每個像素看做是一個字的話,一張圖片就可以看做一個句子。比如上圖一個3x3的圖片,把每個像素想成是一個詞,就可以寫為“blue red yellow gray ......”。接下來就訓練一個RNN,然后把RNN訓練好之后就可以用來畫圖。
如果你要產生更好的圖,就要有更進階的方法。在剛才的例子里,產生圖的順序是blue-red-yellow-gray.....,但實際上我們知道在一個圖片里面,“yellow”和“gray”的距離可能是很遠的,它們之間可能沒有什么太大的關系,真正跟“gray”有關系的可能是“blue”。所以你要做一個先進的圖片生成的話,會用一個特別的技術叫做pixelRNN。
pixelRNN改變了生成的方式,如上圖右下方所示,在產生灰色pixel的時候,看得不是黃色的pixel,而是藍色的pixel。在產生黑色pixel的時候,要同時看紅色和灰色的pixel,以此類推,這個技術叫做pixelRNN。它的結構比較復雜,之后有時間再會講到。
Conditional Generation
接下來要講的是conditional generation,到剛才為止我們訓練出來的RNN,如果讓它看唐詩三百首,那么它確實是一個能寫詩的RNN(隨機的詩)。但沒有辦法讓它遇景生情,比如給它一幅畫,讓它寫一首詩。它只能寫一些非常random的東西。那我們其實可以做到讓機器看着某個conditional,根據某一個情景,然后產生適合的output,這個東西就叫做conditional generation。
conditional generation有什么應用呢?
- 機器看到一段video,然后output “A young gril is dancing”。
- 或者是chat-bot,你說一句話,機器回應一個response。
先看一下圖片的conditional generation是怎么做的。給機器看一張圖片,然后告訴你說圖片里有什么東西。剛才我們講過說,可以弄一個RNN出來,這個RNN可以隨機產生句子,給它
那怎么丟進去呢?
其實有各種不同的做法,你可以選擇一個自己喜歡的。比如把圖片通過CNN變成一個vector,然后把這個vector跟
那么就是把紅色的vector存在memory里面,然后產生對應的句子。訓練的時候就是准備圖片和對應的句子,搜集10萬個pair進行訓練。
這樣做就是有一個風險,就是RNN部分,看過第一個時間點的紅色vector,隨着時間點的推移,可能就忘了當初看過什么東西。所以一個常見的做法是,在每個時間點都輸入紅色vector。
seq2seq模型
同樣的技術完全可以做機器翻譯或者是chat-bot。剛才是輸入一張圖片,然后把圖片變成一個vector之后丟給RNN generator。那現在input是一個句子,只要把句子變成一個vector,接下來的步驟就是一模一樣的。
怎么把句子變成vector呢?
使用一個RNN,如上圖下方所示,假設要做翻譯,翻譯“機器學習”為“Machine learning"。這個RNN就把中文sequence”機器學習"input進去,把最后一個時間點的output取出來。其實RNN在每個時間點都會output一個東西(y),但前面的output都不要。因為最后一個時間點的output \(y\),可能包含了所有input 的信息。有時候紅色vector不一定是最后時間點的\(y\),也可能是h(隱藏狀態)、c(記憶狀態)或者h和c的結合。
然后把紅色的vector丟進generator里面,每個時間點都丟,希望可以產生"Machine""learning" "."。當然在訓練的時候,上圖左下方部分叫做encoder,右下方部分叫做decoder。因為左下方部分是把一個sequence變成一個vector,右下方部分是把一個vector變成一個sequence。
當然encoder、decoder是一起訓練的。
在chat-bot里當然也可以使用這種技術,學習一個seq2seq模型,input一個句子,然后output就是另外一個句子。有人可能會覺得說,也許這樣沒有辦法真的讓機器做到跟人聊天,因為機器不記得之前說過什么。比如你說HI的時候,根據seq2seq模型,機器給出一個HI,但也許可能之前機器已經說過一個Hello,HI是你的回答,然后機器仍會給出HI。
那怎么避免這個問題?
就是把歷史的信息也放進encoder里面去。比如之前機器說過hello,然后人說HI,那之后seq2seq模型可能有很多層的,第一層把第一個句子變成vector,第二層把第二個句子變成vector......。然后把之前的句子都做encoder,丟進一個decoder里去,這樣訓練后機器就可以得到過去的信息了。
Dynamic Conditional Generation
很多文獻把這種模型叫做attention base model。之前encoder、decoder會遇到一些問題,即encoder可能沒有足夠的能力把所有東西都壓到一個vector中去。如果input是各種各樣的句子,很難用一個vector就表示句子中所有的信息,這樣就不得不丟掉一些信息后再丟進decoder。
Dynamic Conditional Generation意思是說,decoder中每個時間段從encoder得到的input都是不一樣的,由decoder自己決定如何截取信息。
encoder每個隱藏層狀態為\(h^1,h^2,h^3,h^4\),然后把這些狀態存在一個database。decoder生成一個詞匯的時候,從database中搜尋自己需要的信息。
比如decoder要產生“machine”之前,從database中搜尋\(h^1,h^2\)並抽取出\(c^1\)作為input,在生成“learning”之前會抽取出\(c^2\)......
Machine Translation
假設機器翻譯要做的事情是input 機器學習,ouput 翻譯為machine learning。在訓練的時候,就是給機器很多的中文和英文pair。
attention-base model是怎么做的呢?
input中文的character sequence,每個character依序丟到RNN里,RNN cell產生隱藏狀態\(h^1\)到\(h^4\),RNN會存起來作為一個database,\(h\)比\(y\)的維度要小,因此用\(y\)的話可能計算代價會變高。
向量\(z^0\)是神經網絡參數的一部分,需要訓練數據才能找出來,是搜尋database的一個關鍵字,一般叫做key。
怎么進行搜尋?
會跟database中每個\(h\)向量計算一個匹配程度,match模組(又叫做attention模組)是專門用來計算匹配度的。接收\(z\)(key)、\(h\)作為input,output 一個分數\(\alpha\)(又叫做attention分數)表明\(z\)跟\(h\)有多匹配。
match是什么?
match可以自己設計,比如點積、一個小的神經網絡(input 是\(z、h\),output是分數\(\alpha\)),也可以是一個函數式\(\alpha = h^TWz\) 。
要強調的點是,如果你的match本身有參數的話,比如一個神經網絡,是不需要額外再把它找出來的,因為match模組是和encoder-decoder合在一起學的。
\(\alpha_0^0\)到\(\alpha_0^4\)你可以做一個softmax,也可以不做,有時候會進步有時候沒有進步。\(\hat{\alpha}_0^1\)到\(\hat{\alpha}_0^2\)是\(\alpha_0\)標准化后的值,接下來加權計算\(c\) 。舉個例子\(\hat{\alpha}_0^1=0.5,\hat{\alpha}_0^2=0.5,\hat{\alpha}_0^3=0,\hat{\alpha}_0^4=0\),那么\(c^0=0.5h^1+0.5h^2\)。然后把\(c^0\)當做decoder的input,根據\(z^0,c^0\) output \(z^1\) 和machine。
然后根據\(z^1\)重新算一次\(c^1\)(\(z^1 、h\)重新算一個分數\(\alpha_1\),再得到\(c^1\)),如上圖所示\(c^1=0.5h^3+0.5h^4\)。
然后\(c^1\)作為下一個時間點basic function的input,下一個時間點的basic function還會接收上一個時間的輸出。
持續這個過程直到輸出句點或者\(<EOS>\)時結束。
Image Caption Generation
attention-base model 也可以用在圖片字幕生成上。
把圖片切割成一小塊一小塊的,每一小塊丟進CNN里產生一個向量,在沒有attention-base model的時候,一張圖片就是一個向量,用了attention-base model之后,一張圖片分割成小塊,就是一把向量。
然后把\(z^0\)跟這一把向量算一個分數,所有分數加權求和丟到decoder里面,產生word1和第二個key\(z^1\) 。
第二個key \(z^1\)和向量再算一次分數,再做加權求和,然后產生word2,以此類推。
根據attention的結果你可以分析,機器在產生一個word的時候到底看到了什么東西。你可以讓機器告訴你,它是憑借什么東西或者看到什么東西,才決定寫出這樣的句子。
比如上圖所示,亮點代表特別大的attention,那么第一張圖,在飛盤部分的attention特別大,那么機器產生frisbee。第二張圖,狗部分的attention特別大,那么產生dog......。最后一張圖,長頸鹿周圍的attention特別大,所以說看到樹然后產生trees。機器不是亂蒙的,它真的知道它在看什么。
當然機器也會犯錯,但可能是有道理的錯。比如上圖,第一張圖機器說看到鳥,但其實是兩個長頸鹿疊在一起,看起來像鳥和鳥的翅膀。第二張圖機器說看到一個人拿着一個鍾,其實是衣服上的圖案......
上圖上方所示,機器產生一個句子 A man and a wowan ride a motorcycle。機器在產生road詞匯的時候,attention集中在第三張圖......
上圖下方機器產生一個句子Someone is frying a fish in a pot。機器看到第二張圖才產生someone,看到第一張圖才產生pot......
Tips for Generation
我們可不可以在match的時候(計算attention分數)做一些限制,因為實際上在做attention的時候,會有一些問題。比如上圖左上方所示,i是哪一個圖,t代表時刻。
- 一開始機器會對4張圖都算一個attention分數,然后會產生第一個詞匯\(w_1\)
- 在第2個時刻再算一次attention分數,產生詞匯\(w_2\)
- 第3個時刻算一次attention分數,產生詞匯\(w_3\)
- 第4個時刻算一次attention分數,產生詞匯\(w_4\)
如果attention都集中在某一張圖上,比如時刻2,時刻4機器都集中在第二張圖上,那么都看到women,詞匯\(w_2,w_4\)都是women,這樣句子就很奇怪。
我們希望機器在看片段的時候,某個時刻在每個片段上的attention是平均的,不應該特別看某個片段。一個做法就是,對attention加一個正則。比如在機器學習中加L1正則的時候,你就希望參數距離0越近越好。
比如上圖下方所示的正則,對同一個片段在不同時間點的attention求和(比如\(\alpha_1^2,\alpha_2^2,\alpha_3^2,\alpha_4^2\)求和),希望這個求和值和某一個值(自己定義的)越接近越好。這樣某個時刻的attention就不會集中在一個片段上。
還有另外一個問題是,比如訓練的時候,你的目標是ABB。你的學習過程是:
- input一個condition和
,產生一個AB的概率分布,希望這個分布和A越接近越好(即A的概率越大越好) - 接下來input一個conditon和一個字符,產生第二個概率分布,希望這個分布和B接近越好
但是在產生第二個概率分布的時候,input的字符是什么?我們之前說過,在訓練的時候,是把正確答案當做第二個input的字符。如果你要的結果是ABB,那么第一個output是A還是B都不重要,我們不關心機器的預測,第二個input的字符一直就是A。
然后最喜歡交叉熵得到參數。
但是在測試的時候,我們對第一個概率分布sample,得到B,那么接下來第二個input字符就是B。你會發現測試的時候,input字符都是機器自己生成的,可能會有錯誤,但是訓練的時候卻都是正確的,中間就會有一個暴露誤差。我們希望機器學習的時候,訓練數據和測試數據的分布是一樣的,但是這里卻有偏差。
我們看一下訓練和測試之間的差別,上面是訓練的狀況。假設只有兩個詞匯AB,那么機器產生三個詞的可能性就只有8種。
訓練時走的只有一條路徑,機器犯錯會被糾正:
- 時刻1的output 有A和B,但是數據告訴機器要輸出A
- 時刻2的output有A和B,告訴機器在已經產生A的情況下,要產生B
- 時刻3的output有A和B,告訴機器在已經產生A和B的情況下,要產生B
測試時每個路徑都有可能,如果機器第一步就犯錯,那么接下來就都是錯的。
那你可能會說,為什么不在訓練的時候用output的結果呢?
如果這樣做,參數就很難學習。比如
時刻1的正確答案為A,機器在學習的時候會想辦法增加output中A的概率,雖然可能一開始的時候B的概率比較高,導致sample出B。但是在學習中,A的概率會不斷增大。
時刻2的正確答案為B,機器就會想辦法增大B的概率,但是這是在時刻1的output為B的前提下。
那么隨着訓練的不斷進行,時刻1 A的概率越來越大,直到sample出A,結果反轉,那么之前時刻2學習的東西都沒有用了,因為都是基於B學習的。
Scheduled Sampling
那怎么做呢?
可以取兩者的折衷。在下一個時刻會input什么字符,用一個骰子決定,正面去機器的output,反面取正確的答案。
在訓練的時候,就可以調骰子的概率,動態決定。一開始的時候,正確答案出現的概率比較高,隨着訓練的進行,讓model出現的概率變高,這是合理的。先讓機器學習到一定地步后,一開始的結果已經比較穩定了,再使用model進行訓練。
Beam Search
機器每次ouput的都是一個概率。
- 假設時刻1 A的概率是0.6,B的概率是0.4,那我們可能選擇A
- 時刻2 A的概率是0.4,B的概率是0.6,我們選擇B
- 時刻3 A的概率是0.4,B的概率是0.6,選擇B
這種選擇的方法結果是ABB,但這真的是概率最大的結果嗎?有沒有可能時刻1選擇B,基於B時刻2的B為0.9,時刻3的概率為0.9,那么0.9*0.9*0.4 >0.6*0.6*0.6,一開始選擇B的路徑概率更大。
當然訓練RNN的時候,你沒有辦法全局計算,也就沒辦法知道所有路徑的概率。而beam search就是用來克服這個問題的一種方法。
beam search在每一次sequence 生成的時候,都會保留前N個分數最高的可能。比如,假設保留2個分數最高的可能(beam size=2)
- 時刻1有A和B,機器就保留2個結果的分數
- 時刻2在A后面接A或B,B后面也可以接A或B,有4種可能,那么選擇最好的2條路徑存下來
- 時刻3也有4種可能,選擇最好的2條路徑保存下來
- 最后在從保存的2中路徑中,選擇最好的那條
beam search和訓練完全無關,只在測試中使用。