lesson7 part5 RNN

這是目前為止我們在這個課程里學到的主要的東西。我們學習了包含有仿射函數組成的三明治夾層(就是矩陣相乘,更通用的叫法),夾層里還有像ReLU這樣的非線性運算的神經網絡。我們學習了這些計算的結果叫激活值。在計算中,模型需要學習的內容,稱之為參數。參數是隨機初始化的,或者是從預訓練模型里復制過來的,


image

然后我們用SGD或更快的優化算法訓練它們。我們學到了卷積函數是一種仿射函數,它對自相關數據(auto correlated data),比如圖片之類的,效果很好。我們還學了batch norm、dropout、 data augmentation、weight decay作為正則化的方法。同時,batch norm也有助于提高模型的訓練速度,今天我們學習了Res/dense模塊,
然后,今天,我們學了Res/Dense blocks。我們學了很多關于圖片分類、回歸、嵌入、分類變量和連續變量、協同過率、語言模型、NLP分類的東西。還有圖像分割、U-Net和GAN。

好好復習這些類容,確保你對它們很很熟悉。如果你只看了一遍這些視頻,你肯定沒有熟悉。人們一般要看三遍才能真正理解這些細節。

遞歸神經網絡Recurrent Neural Network (RNN) [1:38:31]

一個還沒有學的是RNN。這是最后一個要學的東西。我要用圖示的方法來解釋RNN。我先給你們看一個只有一個隱藏層的基本的神經網絡。


image

矩形代表一個輸入,它的輸入尺寸是批次數量(batch_size) x Inputs的數量。箭頭代表一個神經網絡,比如說一個矩陣乘法,后面跟著ReLU。一個圓圈代表一個激活值。這種情況下,我們只有一組隱藏層的激活值。考慮到單個樣本的輸出,就是輸入的數目。這里(右邊第一個箭頭)


image.png

是一個矩陣,尺寸是Input的數量 x activations的數量。因此,輸出的尺寸是batch_size x activations的數量。

知道怎樣計算形狀是很重要的。通過learn.summary()查看所有變量/輸入/輸出的維度。

image.png

然后,這里有另一個箭頭,這代表它是另外一個層,點乘矩陣加上非線性項(此處不是Relu)。這里,考慮到下一層是輸出,所以我們非線性項用的softmax。

三角形代表一個輸出。這個點乘矩陣的尺寸是激活值數量x類別數量,所以我們的輸出的尺寸是批次數量x類別數量。

再用一次這些符號。


image

三角形是輸出,圓形是激活值(我們也叫它隱藏狀態hidden state),矩形是輸入。我們來想象我們下,我們要把一個很大的文檔拆分成每3個單詞一組的集合,對每個集合,用前兩個單詞,預測第三個單詞。如果我們現在有這個數據集,我們可以:

  • 把第一個單詞作為輸入
  • 讓它經過一個embedding,創建一些激活值
  • 讓它經過一個矩陣乘法和非線性計算
  • 取第二個單詞
  • 讓它經過一個embedding
  • 然后我們可以把這兩個東西加在一起,或者連接在一起(concatenate)。一般來說,你見到兩組激活值在一個圖里走到一起,你一般可以選擇連接或者相加。這會創建生成一組新的激活值。
  • 然后你可以將其送入全連接層,再用softmax函數(歸一化函數)來產生一個輸出

這就是一個完全標準的全連接的神經網絡,多了一個小東西,就是在這里做了連接或者相加,我們要用這個網絡來通過前兩個單詞預測第三個單詞。


image.png

記住,箭頭代表層操作,我在這里刪除了細節。因為這里一定是一個仿射函數和一個非線性計算。

再多做些。如果我們要用前3個單詞預測第4個單詞,要怎樣做呢?就是在上次的圖片基礎上,加一個額外的輸入和一個額外的圓圈。


image.png

但是我要說的一點是,每次我們從矩形走到圓形中間的過程,我們在做相同的事情,我們在做embedding。就是一種特別的矩陣乘法,其輸入是one-hot編碼的輸入。每次從圓形到圓形的過程,基本上就是取到一個隱藏狀態(激活值),并將其變成另一組激活值的過程。這個過程完成后即處于下一詞,從圓形到三角形的過程,做的事又不同,是將隱藏狀態。激活值轉換成輸出的過程。因此,我最好還是把這些箭頭用不同的顏色標示出來,每個箭頭都很可能代表相同的權重矩陣。因為它們做的都是同樣的事。那么為什么對每個詞都要有不同的嵌入。或者與不同的權重相乘。從這個隱藏狀態到這個隱藏狀態再到這個,這就是我們要打造的東西。

Human numbers [1:43:11]

現在我們來看下human numbers(手寫數字識別),它在lesson7-human-numbers.ipynb里。這是一個我創建的數據集,里面包含了用英文寫的從1到9,999的數字。

我們要創建一個可以預測這個文檔里下一個單詞的語言模型。這只是一個簡單示例。這里我們的例子中只有一個文檔,內容是一個數字列表。因此我們可以使用一個TextList來創建用于訓練和驗證的文本列表。

image.png

這里驗證集是8,000之后的數字,訓練集是1到8,000。我們可以把它們合并,轉成一個data bunch。

這樣我們就只有一個文檔,train[0]通過.text得到文本列表的內容。這是它的前80個字符,以特殊標記(special token)的'xxbos'開頭。所有以xx開頭的是fastai 的特殊token,bos(beginning of stream)是stream的起始標記。它的意思是說這是文檔的起點,在NLP里,知道文檔在哪開始很有用,這樣你的模型知道從哪里開始識別。

image.png

驗證集有13,000個token。就是13,000個單詞或者標點符號,因為每個用空格隔開的部分都是一個標記。

我們設的批次大小(batch size)是64,這里有個屬性叫bptt,默認值是70,bptt,我們簡單提過‘bptt’ (“back prop through time”)。它的意思是隨時間反向傳播,即序列的長度。因此,對64個文檔片段中的每一個,我們把它分成70個單詞為一組分開,每次處理一組。


image.png

我們要做的就是,取出含13,000個token的整個字符串作為驗證集,然后將其分成64個大小差不多的部分。人們經常把這個理解錯。我不是說“各部分的長度是64”,不是這樣。它們是“64個長度差不多的片段”。因此,文檔的第一個1/64的片段是第一部分,第二個1/64的分段是第二部分,然后對于64個分段中的每一個,再將其分成長度為70個詞的小片,因此每一批,對于13000個標記,一共可以分成多少批?總數除以批大小,再除以70,大約是2.9批,即3批。讓我們用一個迭代器(iter)做數據加載。取第1,2,3批數據,包括x和y。

我們把3批數據的元素個數加起來,得到一個略低于總個數(13017)的值,因為分了3批之后,最后還剩一點。但剩下的又不足以成為新的一批。你以后會經常遇到這一類的東西,各種形狀、大小、內容、迭代。你可以看到,這里是95×64,之前說過是70×64


image.png

這是因為語言模型的數據加載器,將bptt(序列)稍微打亂一下。做了一下洗牌,使數據更加隨機化,這有利于模型。這里你可以看到第一批中的x,記住,所有這些英文文本已經被數值化了,這是第一批的y。你可以看到x1是[2,18,10,11,8.....],y1是[18,10,11,8...]


image.png

y1與x1有一位數字的偏移,因為這正是語言模型所要求的的,我們要預測下一詞。因此2之后是18,18之后應該是10.你可以得到數據集的vocab屬性,


image.png

vocab有一個textify方法(文本化),所以如果我們用textify查看同樣的內容,它會去vocab中查找相應的文本。在這里你可以看到xxbos eight thousand one,y里沒有了'xxbos',只是eight thousand one。所以xxbos之后是eight,之后是thousand,之后是one。在eight thousand twenty three之后,就是x2,注意這里,我們總是看第0列,這代表著第一個批次(第一個mini batch),之后是eight thousand twenty four,然后是x3,一直到eight thousand forty

image.png

然后早從頭開始,但是取第一列,也就是第二批(x1[1])。現在我們繼續。這里從eight thousand forty跳到eight thousand forty six,這是因為上一個mini-batch不完整。這意味著每一個mini-batch 會與之前的mini-batch連接,這樣可以直接從x1[0]過渡到x2[0],每個mini batch和前一個mini batch連接在一起。你可以直接從x1[0]到x2[0],繼續eight thousand twenty threeeight thousand twenty four。第一列也是如此,你會看到前后也是互相連接的。所有的mini-batch都是相互連接的。

image.png

這是數據,我們可以用show_batch來查看它。

這是我們的模型,它做了我們在圖里看到的事情:


image.png

把代碼拷到了這里:


image

它包含一個embedding(綠色箭頭),一個隱藏層到隱藏層的過程(棕色箭頭),一個隱藏狀態到輸出的過程。每個標顏色的箭頭都對應一個矩陣。在forward過程中,我們取第一個輸入x[0],讓它通過輸入到隱藏狀態層(綠色箭頭),得到第一組激活值h。假設還有第2個單詞,因為有時在一批的結尾可能就沒有第2個詞了。這里我們假設有第2個詞,把它加到h上,x[1]的結果,完成綠色的箭頭(i_h)。然后我們可以說,好了,我們的新h是這兩個相加的結果,完成從隱藏層到隱藏層的橙色箭頭,然后ReLU激活,然后batch norm。然后,對這第二個單詞,做相同的事情。最后藍色箭頭調用h_o()

這就是怎樣把圖轉成代碼。沒有什么新東西。現在讓我們來做,我們可以在學習器里驗證,得到訓練到46%的準確率。現在來看看這個代碼,其實很糟糕,有很多重復的代碼。作為程序員,看到重復的代碼時,怎么辦?我們要對代碼進行重構,把這一段寫成循環,現在寫成了循環的形式,現在遍歷x中的每個xi。猜猜看,這就是RNN了,RNN只是重構,不是什么新東西,現在這就是一個RNN,

image.png

現在將這個結構圖重構為這個結構圖,


原結構圖

新的結構圖

這是相同的結構圖, 我只是用循環代替了它。它們做同樣的事,這是代碼。


image.png

它的init()函數也一樣,完全一樣,只是這里出現了循環,開始前,我必須確保我有一堆零作為初始值。當然,訓練出的結果是一樣的,

image.png

接下來,你可能會想到,循環的一個好處是,現在代碼可以在不同的情況下運行,即使我不是從前三個單詞預測第四個單詞,而是從前八個單詞預測第九個 ,也沒有問題,任意長度的序列都可以,讓我們將bptt提高到20,現在可以這樣做了。


image.png

然后,不僅僅是從前N-1個詞預測第N個詞,讓我們試著從第一個詞預測第二個詞,從第三個詞預測第四個詞,以此類推。因為,之前,看一下損失函數,之前模型結果比較的對象只是序列的最后一個單詞,這很浪費,因為序列里有很多個單詞。我們來把x的每個單詞和y里的每個單詞做比較。要做到這個,我們要改變原來的結構圖,


image

循環的最后不再是一個三角形,現在三角形在循環中。換句話說,每個循環之后,預測,再循環,預測,循環,預測.....

代碼在這里,和之前的代碼一樣,但我創建了一個數組,每經過一個循環,我把h_o(h)的結果添加到數組末尾,n個輸入,我得到n個輸出,我預測了每個單詞,

image.png

之前的準確率是39%,現在是34%。為什么變差了?因為我在預測第二個單詞時,只有一個單詞可以用。當我預測第三個單詞時,有兩個單詞的狀態可以用。這是一個更難的解決的問題。一個明顯的解決辦法是,關鍵在這里:


image.png

這一步每次將狀態重置為0,開始了另一個bptt序列,現在不這樣做,我們保留h,然后就可以把每個批次連接到上一個batch,不想圖像分類那樣,發生了重排。我們取原來的模型,復制,但是把h的創建轉移到構造函數里。

就像這樣,它現在是self.h,


image.png

是完全相同的代碼,只是最后h改成了self.h,兩段代碼做著同樣的事,它現在并沒有拋棄中間的狀態,所以現在結果變好了,最后的準確率達到了44%。


image.png

這就是RNN。你總是希望保留中間的狀態。但要記住,RNN沒什么特別的,它就是一個正常的全連接神經網絡,只是你重構了一個循環。

在每個循環最后,你可以不單單輸出一個結果,你可以把它輸出到另外一個RNN里。


image

你可以從一個RNN進到另外一個RNN。這很好,因為我們有了更多層來計算,它的效果預期會更好。
要做到這個,我們要再重構更多代碼。我們取Model3的代碼,用PyTorch內置的等效代碼替換它,可以這樣寫:

image.png

nn.RNN就是做循環。我們還有相同的embedding、相同的輸出、相同的batch norm、相同的h初始化,但是沒有了循環。RNN的一個好處是你現在就可以定義你想要多少層。當然,得到的準確率是一樣的。在這里,我準備用2層,

image.png

但要注意,這個圖,沒有循環的版本:


image

我們繼續把BPTT設為20,所以這里有20層。我們從可視化函數可視化的論文里知道,深度網絡有著糟糕的凹凸不平的的損失表面。所以,當你創建很長的時間尺度和很多層的神經網絡時,訓練變得不可能了,你可以用一些技巧,其中之一就是添加跳過連接。
但人們經常不再單單把這些(綠色箭頭和橙色箭頭)加在一起,而是用一個mini神經網絡決定要保持多少綠色的箭頭,多少橙色的箭頭。當你這樣做時,你得到了叫GRU或LSTM的東西,至于是哪個,這取決于這個小網絡的細節。我們會在課程第二部分學習這些網絡的細節。說實話,它們不是很重要。

現在,我們可以創建一個GRU來替代。它的功能和我們之前的很像,只是它可以在更深的網絡里處理更長的序列。我們用兩層試試。

image.png

哇,準確率到了75%,這就是RNN。我講它的主要原因,是要揭開最后的一個神秘的東西。這是深度學習里最簡單的魔法之一。它只是重構了一個全連接網絡。不要讓RNN嚇倒你。使用它時,你有一個輸入序列,一個輸出序列,長度都為n,我們將其用于語言模型,也可以用于其他任務,例如,輸出序列可以是,對于每個單詞,輸出可能是是否有敏感的東西,而決定是否做匿名化。比如說,這是否是隱私數據,也可能是這個詞的詞性標注。或者可能是這個詞該如何構詞,等等。這些被稱為序列標注任務。你可以使用相同的方法,解決幾乎所有的序列標注任務,你也可以完成我之前的課里做的,構建一個語言模型,你可以去掉h_o,用一個標準的分類器,然后就可以做自然語言處理分類了。就像前面看到的,得到最領先的結果,甚至在長文件上也行。這是一種非常有價值的技術。而且完全不神秘。

這就是深度學習,至少在我看來實用的部分就是這些。只看一遍,你不會全部掌握。我不建議你第一次看得很慢,在第一遍就全部掌握。你可以回過來再看一遍,慢慢來,有些地方你可能回覺得“噢,我現在知道他在講什么了”。然后,你可以實現你以前做不到的東西,你可以比以前更深入。一定要再看一遍。并且,看的同時要寫代碼,不只是自己寫,還要把它提交到github上。無論你是不是覺得它們是好代碼。你寫代碼并且分享它這件事本身就是有巨大意義的,如果你在論壇上告訴人們,“嘿,我寫了這些代碼。它不是很棒,但這是我的第一次努力。你們看有什么問題嗎” 。人們會說“噢,這里做得很好。但是,這里,你可以用這個庫,這會省些時間”。你會從同伴那里學到很多。

你們已經注意到了,我開始介紹越來越多的論文。課程第二部分會有很多論文,現在可以開始閱讀一些已經介紹過的論文。對那些推導、定理、引理之類的東西,你可以跳過它們。我就是這樣的。它們對你做深度學習實踐來說沒有什么影響。那些說為什么我們要解決這個問題、這些結果是什么之類的部分就非常有意義。然后試著寫些文章,


image

不是用來給Geoffrey Hinton和Yann LeCun看的文章,是你希望你自己寫下并閱讀的英文文章,就像你在六個月前寫的那樣。因為六個月前,你的讀者中有比像Geoffrey Hinton和Yann LeCun這樣的讀者更多的人。這些人是你最了解的。你知道他們需要什么。

去尋求幫助,也幫助別人。告訴我們你成功的故事。可能最重要的是和其它人一起學。如果有社區交流,人們會學得更好。可以辦一個讀書聚會,參與meetup,創建學習小組,做些項目。還是一樣,這些不需要做出特別令人驚奇的成績。只需要做些你覺得能讓世界變得更好些的東西,或者你覺得能讓你兩歲的孩子看到后能開心的東西,或者在你的兄弟來看你在做什么時,你想展示給他們的東西,等等。去做一些東西,把它做完,然后再試著讓它更好些。

image

比如我今天下午看到的Elon Musk的推文生成器。它讀了很多老的推文,創建了一個Elon Musk的語言模型,然后產生這些新的推文,比如 "Humanity will also have an option to publish on its own journey as an alien civilization. it will always like all human being." "Mars is no longer possible," "AI will definitely be the central intelligence agency."

這很棒,我喜歡這個。我喜歡Dave Smith寫的“這是我第一次提交。感謝在八周里教會一個做金融的人怎樣構建一個app”。我覺得這很棒。我覺得這個項目會受到很多熱心的關注。這會改變未來的社會發展方向嗎?大概不會。但Elon Musk可能會看到這個,然后想“噢,可能我要重新考慮我說話的方式了”。我覺得這很棒。所以,去創建一些東西,提交到這里,花點時間去做它。

或者參與到fastai里來。這個fastai項目,有很事情要做。你可以幫助寫文檔、做測試,這些可能聽起來有點無聊。但你會驚奇地發現這并不無聊。拿一段沒有文檔的代碼,研究它,理解它,在論壇上問Sylvain和我,發生了什么?為什么這樣寫?我們會發給你我們在實現的論文。編寫一個測試需要深刻地理解機器學習里的這部分內容來知道它應該怎樣運行。這是很有意思的。

Stats Bakman 創建了這個很好的Dev Projects Index,你可以到論壇上fastai開發項目板塊,找到正在進行的,你想參加的項目。

創建一個學習小組。Deena已經在一月創建了一個舊金山學習小組,創建學習小組很簡單,到論壇里,找到你所在時區的板塊,添加一個文章,說“我們來成立一個學習小組吧”。但是要給人們一個Googel sheet之類的東西來登記,真正做些事情。

一個很好的例子是Pierre,他在巴西組織了上一期課程的學習小組,做得很好。他不斷貼出人們一起學習深度學習、創建wiki、創建項目的照片,很棒的經歷。

在課程第二部分,我們會學習所有這些有趣的東西。在實踐上,深入fastai代碼,理解我們是怎樣構建它的。我們會過一遍代碼。我們構造它們的時候,在每個階段,都創建notebook來學習我們在做什么,我們會看到軟件開發過程。我們會講做研究的過程,怎樣讀學術論文,怎樣把它從數學符號變成代碼。然后是一堆各種類型的沒有學過的模型。這會從深度學習實踐進入到實際的前沿研究。

提問(Ask Jeremy Anything) [2:05:26]

我們在線上有AMA(ask me anything)活動,我們有時間講幾個社區提問最多的問題。

提問:第一個是Jeremy的請求,盡管它不是被提問最多的問題。你的典型的一天是怎樣的,你怎樣在這么多事情上分配時間?

我總是聽到這個問題,所以我覺得我需要回答下,有些人為這個投票了。來到我們學習小組的人總是會被我的沒有條理和沒有效率震驚,我總是聽到人們說“哇,我以前覺得你是深度學習的模范,我想看看怎樣能變得像你一樣,現在,我不太確定你是什么樣的人了”。對我來說,這與你做得是否開心有關,我沒有做過很多計劃。我只是堅持去完成我開了頭的工作。如果你得不到樂趣,就很難繼續下去,因為在深度學習里有很多挫折,這不像寫一個web app,就是做授權、檢查、后臺服務檢查、用戶憑證、檢查,你在做流程。另外一邊,對GAN這樣的東西,是這樣的:它沒有效果、它沒有效果、它沒有效果、它還是沒有效果、它還是沒有效果,直到“哦,天,這太神奇了。這是一只貓”。它是這樣的東西。我們沒有那么規律的反饋。所以,你需要對這有興趣。所以,我的日子有點,你明白的,另外,我不參加什么會議,我不打電話,我不喝咖啡,我不看電視,我不玩電腦游戲。我花很多時間陪家人,花很多時間鍛煉,花很多時間閱讀、寫代碼、做我喜歡的事。主要就是去把事情做完,徹底地完成它。你完成了80%,但還沒有創建一個README,安裝過程還有點麻煩,github上99%的項目都是這樣。你會看到README里寫著:“TODO:完成基線實驗文檔......”。不要做這樣的人。徹底完成一些東西,可以和一些人一起做,把它做完。

提問:什么是最讓你興奮的、有前途的深度學習/機器學習的東西?你去年講過你不是強化學習的粉絲。現在你還是這樣覺得嗎?

和三年前開始做這個課程時一樣,我還是認為是那些都是關于遷移學習。它的價值沒有被充分認識,還沒有被充分研究。每次我們把遷移學習用到其他東西時,它就會變好很多。我們在NLP上用遷移學習的論文改變了NLP發展的方向,被《紐約時報》報道了,只是一件不值一提的小事。那是我們一起拼湊到一起的文章。所以我仍然對遷移學習感到興奮。我對強化學習仍然沒有興趣 。我沒有看到它被普通人用在普遍的任務上。對那些可以被很簡單很快速解決的問題,它是一個難以置信的低效的方法。它會有它擅長的領域,但是不會用到大多數人日常工作中。

提問:對于要參加2019年第二部分課程的人,在課程開始前,你建議做什么學習實踐呢?

就是寫代碼。是的,就是一直寫代碼。我知道這完全可行,我聽到有人學到這里還沒有寫任何代碼。如果你是這樣的,那沒關系。你只要再學一遍,這次要寫代碼。看看輸入的形狀,看看輸出,要知道怎樣取一個mini batch,看看它的平均數和標準差,把它畫出來。你們有很多資料。如果你能自己從頭寫出這些notebook,我說的從頭寫是說用fastai庫,不是完全從頭寫,你會成為頭部梯隊的一員,因為你能自己做所有這些東西,這非常非常難得。這會讓你對part2有充分的準備。

提問:你認為未來fastai庫會怎樣,比如說在五年內?

像我說過的,我不做什么計劃,很隨便,所以...我們唯一的計劃是,fast.ai做為一個組織,來讓深度學習成為一個普通人做普通工作時能使用的工具。只要我們還需要寫代碼,就做不到這個,因為世界上99.8%的人不會寫代碼。所以主要的目標會是,讓人們不再用一個庫,而是能用上一個不需要用戶寫代碼的軟件。并且,也不再需要一個像這個一樣的非常長的、很困難的課程。所以,我希望能不需要這個課程、不需要寫代碼,我希望能做到這樣,那你們可以只做有用的東西,能快速地、簡單地做。這會在五年之內做到嗎?可能要更長。

好了。希望能在課程第二部分看到你們所有人。謝謝。

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

推薦閱讀更多精彩內容