看Andrew Ng視頻,總結的學習心得。
雖然本篇文章可能不是那麼細緻入微,甚至可能有了解誤差。
可是,我喜歡用更直白的方式去理解知識。
上一篇文章傳送門: https://segmentfault.com/a/11...面試
我最開始接觸的是ML (但只限於Sklearn的簡單應用,工程化的內容當時一點都不瞭解。)
後來有幸瞭解到DL (這個瞭解比較多)
我面的是普通Python崗, 由於個人小項目中涉及到 (聊天機器人)。因此第二個面試官揪着這個聊了聊。
與面試官交談時,我也直接挑明瞭,模型是Github找的,當時本身爬了些問答對,處理後放入模型本身訓練的。
面試官一頓(特徵提取,語義)等各類 ML-NLP工程化的過程,把我直接問懵了。。segmentfault
怎麼提取特徵(問號臉,難道是TF-IDF,分詞之類的??)?????
我也不知道說啥,以僅有的能力,和他聊聊(LSTM、Embedding, Seq2Seq的 Encoder-Vector-Decoder)。。網絡
面試官說:「你說了這些, 那你特徵工程是怎麼作的???」
我感受已經沒有任何反駁的能力了。。。接下來的事情,我不說,你們也應該清楚了。ide
我回來後也反思過, 作了什麼特徵工程??
我看視頻中 也是,數據簡單預處理下,而後分詞,詞頻過濾,構建詞典
而後,直接就是構建NN層(包括Embedding層)。函數
直到最後瞭解了"端到端這個概念" 與 傳統ML的區別。
才清楚, 當時面試的場景是怎麼個狀況。。。源碼分析
傳統ML: 原數據 -> 數據特徵工程(各類複雜的人工處理) ---> 模型
端到端DL:原數據 -----------------------------------------------------> 模型 學習
端到端:(一步到位):優化
傳統的ML作的中間層人工"手動"特徵工程處理出來的特徵。 這些特徵,端到端的NN均可能"自動學習"的到。 這也多是當時爲何面試官一直追問我"特徵如何處理"的緣由吧。也肯能他另有目的QAQ... 或者咱們真的不在一個頻道上。。。可是交流的過程真的使我受益不淺,有了更廣闊的視野(3Q!)
強調一點:編碼
雖然端到端 模型很便捷。可是須要大量的數據,才能訓練出好的效果。
卷積層(激活函數) + 池化層 + 全鏈接層 Convolution + Pooling + Dense
有人喜歡把: 卷積層 + 池化層 做爲一層網絡 (由於池化層無訓練訓練,後面會提到) 也有人喜歡把: 卷積層 和 池化層 各自單獨算一個層(也是沒問題的。Tensorflow的API就是這樣設計的)
卷積計算過程就不說了。沒有案例圖。 但你能夠理解爲: 兩個 正方體 的 對應位置的元素 (相乘再相加)的結果。。。 (互相關,,,)
輸出圖像大小計算公式:spa
h圖片輸出 = (h圖片輸入 - h卷積核 + 2padding) / strides + 1 w圖片輸出 = (w圖片輸入 - w卷積核 + 2padding) / strides + 1 首先聲明: 這個式子因爲不必定可以整除, 所以除不盡的狀況下,向下取整, 也叫地板除 由於有個原則: 卷積核滑動的時候(一般是,步長>1的狀況下) 若是越界了一部分。則捨棄掉
根據上面的公式,求一個輸出圖像大小的例子(此處,不作paddding, 而且步長爲1)
eg: 輸入8x8 的圖像 , 並使用3x3的卷積核 輸出圖像高度爲: h圖片輸出 = (8-3 + 2x0) / 1 + 1 = 6 輸出圖像寬度爲: w圖片輸出 = (8-3 + 2x0) / 1 + 1 = 6 因此輸出圖像爲: 6x6 很明顯: 卷積一次,圖像變小了。 若是再卷積幾回, 圖像就看不到了。。。 因此: 咱們須要解決這個問題 原則上: 增長 padding 能解決步長爲1時,卷積後的圖片縮放問題。
假如咱們但願輸出圖像的大小 等於 輸出圖像的大小,而咱們想要求padding須要設置爲多少。
通常這種場景適用於 步長strides = 1, 因此參考開始的公式,可寫出以下公式:
由於: w 和 h是同樣的公式,此處只寫出一個h,來推導: h圖片輸出 = (h圖片輸入 - h卷積核 + 2padding) / strides + 1 化簡: padding =( h圖片輸出 - h圖片輸入 + h卷積核 - 1 ) / 2 由於咱們但願的條件: h圖片輸出 等於 h圖片輸入, 因此可繼續化簡: padding =( h卷積核 - 1 ) / 2
因此步長爲1的狀況下卷積, 而且想讓圖片不變形,你的padding的取值,就須要受到 卷積核大小的影響。
如今經常使用的卷積核大多都是 1x一、 3x三、 5x3 。因此看上面化簡好的公式:
padding =( h卷積核 - 1 ) / 2 <============= 1x1, 3x3, 5x5 奇數-1總等於偶數。 因此不用擔憂除不盡的狀況 還須要注意一下: 填充padding通常是環形填充, 假如padding=1, 那麼上下左右 都會添加一層。 固然: tensorflow的padding是能夠設置形狀的
valid:
不填充
same:
自動去填充,使得輸入圖像 與 輸出圖像 的大小相等
三通道卷積:
假如你只有一個卷積核 即便你圖片是3通道的(三層) 即便你卷積核也是三通道的(三層) 可是: 卷積輸出結果是依然是一個 m x n 的形狀 (一層"薄紙") 你的疑惑: 不是三層嘛? 最後怎麼變成一層了 ??? 個人解釋: 每滑動一次,3層通道,各自都會計算各自層的卷積,而後求總和,並填入一層"薄紙"的對應位置
多個卷積核:
上面說了: 1個卷積核,不管圖片是什麼形狀的、有幾個通道,卷積後輸出的形狀 是 一層薄紙: m x n 而若是你有多個卷積核: 那麼你就會有多層薄紙摞起來。 就像 一個長方形 摞成了一個 長方體 明確點: 假如你用了 6個卷積核, 那麼你的輸出就變成了 m x n x 6 (三維的一個長方體了)
上面說的:就是我最開始學的CNN時候常常理解不清楚的地方。我相信你也同樣, qaq ....
下面作個總結:
C個通道: 一點都不會影響輸出維度。 注意我說的是維度。 假如你的輸入是 m x n , 那麼你的輸出依然是 p x q (注意維度便可,維度沒變,二維) f個卷積核: 會影響輸出的維度。 輸出的維度將會增長一個維度f 假如你的輸入是 m x n , 那麼你的輸出依然是 p x q x f (增長一個維度F,變成了) 也許你當你學TF的時候會有另外一個理解障礙:那就是TF數據格式(以圖片爲例): 一般TF數據格式是這樣的: [圖片數量B, 圖片高度H, 圖片寬度W, 通道數C] 假如你使用 F 個卷積核作了卷積: 那麼他的卷積結果的特徵的形狀就變變成: [B, H, W, F] 發現沒輸出結果和通道數C,沒有關係的。 只和 卷積核的個數 f有關係。 可是注意: 雖然結果和C不要緊。可是 須要卷積核中具備 C的數量,還作惟獨匹配。橋樑運算。 對應上例, 咱們的卷積核的形狀應該是這樣的 : [F, C, H, W] 注意一下: 這裏面有 卷積核數量f, 也有通道數量C。
若是最後一步的卷積核形狀不理解:
不要緊。之後是TF20的天下。 對應API不須要你指定卷積核的形狀。 所以,你不必記住卷積核的形狀。 你只須要 傳遞,卷積核的個數, 和 寬高 和 步長 便可。 固然這些都是獨立的命名參數。 摘一小段Conv2D的源碼: def __init__(self, filters, # 你只須要傳遞這個參數, 卷積核的個數 kernel_size, # 卷積核的寬高,假如 3x3 你只需寫 [3,3] 便可 strides=(1, 1), # 這是步長, 你不傳,他也會給你填默認值, 步長爲1 padding='valid', # 這時 padding策略,前面說過,這個通常都會設爲 "same" 或許你還有些疑問: 剛纔上面不是提到了卷積核應該設置 通道數C麼。 原則上是的。由於要和 輸出的樣本作卷積。要匹配才行。 可是在Tensorflow中。 特別是 Tenosrflow.Keras中,定義模型層 咱們只須要把整個模型,從上到下鏈接起來。(就像前後排隊同樣) 而對於一些先後流動貫通的參數,好比剛纔提到的通道C。 這些參數,Tensorflow會自動幫咱們上下文識別管理。 因此咱們作的只是須要,把原始數據的形狀設置好傳 給第一層(給排頭髮數據) 至於你這些在中間層流動的參數,Tensorflow會自動幫你計算,你不用傳。 雖然不用傳,但你最好清楚每層是什麼結構(固然這時後話,可能須要一些時間理解) 到最後,我再給你設置一個輸出形狀,你能給我輸出出來便可 (隊尾接數據) 基本TF參數流動機制講到這裏,剛開始學的時候,也是各類苦思冥想,想不明白qaq...
其實咱們作的每一步 (每個)卷積就至關於一個矩陣線性操做: x1 @ w1
以後,基於常理話, 我會還會給它一個誤差: b 變成 ===> x1 @ w1 + b
咱們說過,可能會給出不少個卷積核進行運算。
上面 x1 @ w1 + b 是每個卷積核的卷積結果 咱們還須要講全部卷積覈計算結果堆疊在一塊兒: 記爲 X @ W + b # m x n x f 最後將堆疊在一塊兒的結果,作一層非線性變換 relu ( X @ W + b ) # CNN 一般用 relu eg: 現有圖片 5 x 5 x 3 的圖像 (暫時不考慮樣本個數,就認爲是一個樣本). 咱們用的是 2 x 2 x 20 的卷積核 (步長爲1,不作padding) 那麼輸出結果就是 (5-2+1) x (5-2+1) x 20 === 4 x 4 x 20
忘記說了,還有一個公式,用來計算 每層卷積的權重參數量的個數的:
公式: 每層權重參數量(W) = 卷積核個數 x 卷積核高 x 卷積核寬 x 卷積核通道數 公式: 每層誤差數量(b) = 1 x 卷積核的個數 # 由於每一個卷積核只有一個誤差b 舒適提示: 有太多人喜歡把卷積核個數 與 卷積核通道稱做:"輸入/輸出"通道。 這樣的稱呼是沒問題的, 但我在計算參數量的時候,不喜歡這樣的稱呼,易混淆。 前情回顧: 記不記得普通神經網絡了。每一個神經元節點,都有它們本身的參數。所以它們的參數量是巨大的 迴歸正文: 而卷積核是共享的, 由於它是在一張圖片上滑動的。(挨個服務)因此權重參數也是共享的。
卷積層(激活函數) => 池化層
池化層主要分兩種: MaxPooling 和 AvgPooling
聲明: 池化層也有滑動窗口,而且輸出形狀計算公式,和 卷積的輸出形狀計算公式同樣:
h圖片輸出 = (h圖片輸入 - h卷積核 + 2padding) / strides + 1 w圖片輸出 = (w圖片輸入 - w卷積核 + 2padding) / strides + 1
由於池化層,的基本都是放在卷積層以後,所以池化層的通道數 也就瓜熟蒂落的 和 卷積層通道同樣
舉個例子: 卷積層數據形狀爲: m x n x f 那麼池化層形狀同爲: p x q x f 我想主要強調的是: 通道數不變,變得是 寬高。
仍是,把Tensorflow, 源碼搬過來,標註一下:
def __init__(self, pool_size=(2, 2), # 滑動窗口大小 2x2 strides=None, # 步長,一般設爲2 padding='valid', # Maxpooling 一般不用padding
通常都是使用組合 pool_size=(2, 2) 和 stride = 2
因此,公式來了: 輸入h 滑動窗口h 輸出h = (輸入h - 滑動窗口h) / stride + 1 = ---------- - -------- + 1 stride stride
一般咱們把 pooling層做稱做數據的降採樣:
因此大多數經驗者,都會把 滑動窗口 和 stride步長 設爲相等大小。 因此帶入上面公式: 輸入h 1 輸入h 輸出h = (輸入h - 滑動窗口h) / stride + 1 = ---------- - ----- + 1 = ------- stride 1 步長 簡化一下: (當 pool_size 和 strides 設置相等大小時): 輸出 = 輸入 / 步長 因此當咱們: 步長設爲2時, 輸出就是輸出的一半。 步長設爲3時, 輸出就是輸出的1/3。 ...
不知道有沒有這樣一個疑問:」爲何滑動窗口沒有設置 窗口數量 (就像設置卷積核數量)「
再次說一下Tensorflow的原理。 由於Pooling的上一層基本徹底是 Conv卷積層, 卷積層的 卷積核的個數已經設置好了。 卷積層對接池化層的時候, Tensorflow會自動判斷,並設置: 池化層滑動窗口的個數===卷積核個數 池化層通道個數的個數===卷積層通道個數===圖片的原始通道個數
卷積操做:以前咱們卷積不是拿着滑動窗口,對應元素相乘再相加麼?
池化操做:池化層也是拿着滑動窗口同樣滑,可是不作運算,而是隻取每一個窗口內最大值。放在一層"薄紙"上
同樣滑動窗口,各類滑, 而後取每一個窗口內的數據的"平均值", 其餘就不說了,同 MaxPooling
池化層的是"沒有"參數能夠訓練的。因此,反向傳播,也不爲所動~~~
你很熟悉的, 全鏈接層其實就是以前講的普通的NN(神經網絡),因此並無什麼好說的。
只是拼接在池化層以後罷了。
但其實仍是有一些細節須要注意。尤爲以前的東西沒搞懂,那麼這裏的參數形狀你會垮掉~~~
以前爲了圖方便,參數我都沒怎麼提到樣本參數。
下面我要把樣本參數也加進來一塊兒嘮嘮了。我感受講這裏,直接上例子比較直觀。
好了,如今咱們有個需求, 想要作一個10分類的任務:
卷積層-池化層: 這個照常作, 設置你還能夠堆疊 卷積層1 + 池化層1 + 卷積層2 + 池化層2 ... 等堆疊的差很少了: (你自我感受良好了。。。),咱們須要作一層展平處理!
展平處理(特地拿出來講)
假如你疊加到最後一層池化層數據形狀是:(1000,4,4,8)==> 1000個樣本,寬高8 x 8, 輸出通道爲8 你展平後的形狀爲: (1000, 4*4*8) == (1000, 128) 展平操做第一種API: tf.keras.Flatten() # tensorflow2.0的Flatten被做爲網絡層使用 展平操做第一種API: tf.reshape(x, [-1, 128]) # 手動變換, -1是補位計算的意思 而後在加全鏈接層,形狀爲: (1000, 50) # 50表明輸出,起到過渡做用 而後在加全鏈接層,形狀爲: (1000, 10) # 最終,10表明輸出,由於咱們說了,要作10分類嘛 1. 其實你中間能夠加不少全鏈接層,我這裏只加了一層,控制最後一層全鏈接是你想要的輸出就行。 2. 特別注意, 這裏的每一層全鏈接計算,是須要有激活函數跟着的。 除了最後一層全鏈接,其餘層的全鏈接都設置爲 Relu 激活函數便可。 3. 由於咱們作的是10分類(多分類天然應想到 softmax參數, 若是是其餘業務,你也能夠不加激活函數) 沒作,也就是最後一層。咱們要再添加一層激活函數 Softmax。
降採樣(控制輸出通道數量):
假如,前一個卷積層參數爲: (1000,32,32,256) 若是你下一層使用1x1x128的卷積,則對應參數爲: (1000,32,32,128) # 256通道變成了128通道
一般CNN大多數都是用來作CV工做。對於某些文本分類。CNN也能夠完成。以下變通概念:
是否網絡層數越多越好,雖然堆疊更多的網絡,可使得參數豐富,而且能夠學到更多更好的特徵。
可是實際效果並不是如此,而是出現,過擬合等現象。
ResNet做者 何凱明:有感而發:按理說模型是應該越豐富越好的。但是出現了過擬合這種現象。
最少,更深層的網絡的效果,應該比淺層網絡的效果好吧。不至於差了那麼多。
所以,他將此問題轉換爲一個深度模型優化的問題。
能夠看到,上圖中有一些輸入和輸出:慢慢捋清。
而後將上述途中最後2行的公式化簡,可獲得以下形式:
若是你看過了上面的圖,你會很清楚, 有多少個x, 就會輸出多少個y。
上面第7點說過 : "其實每層的輸出y1 會替代下一層的x做爲下一層的輸入", 該如何理解這句話???
假如你有這樣一段文本: "我精通各類語言" => 分詞後的結果會變成 "我","精通","各類","語言" 通常的問答對這種的句子,處理流程是: (這裏只先說一個): 那就是:在句子的末尾添加一個 <END> 標識符 因此句子變成了: "我","精通","各類","語言", "END" 這些單詞都會預先轉爲 (One-Hot編碼 或者Embedding編碼) x1(初始值0) => y1(我) y1有必定機率輸出 "我", 下面全部的y同理,只是機率性。 x2(y1) => y2(精通) 如此每一層嵌套下來,至關於條件機率 P(精通|我) x3(y2) => y3(各類) P(各類|(我,精通)) x4(y3) => y4(語言) ... x5(y4) => y5(<END>) ...
不知道看了上例,你會不會有下面一連串的問號臉??:
不是說x1-x5每一個x應該輸入固定句子的每一個單詞麼???爲何變成了輸入y1-y5
答:的確是這樣的,可是咱們的 y1-y5 都是朝着預測x1-x5的方向前進的。(這也是咱們要訓練的目標)
因此: 能夠近似把y1-y5等價於x1-x5。 因此用 y1-y5 替代了 x1-x5 這樣: 也能夠把先後單詞串聯起來,讓整個模型具備很強的關聯性。 好比: 你第一個y1就預測錯了。那麼以後的y極可能都預測錯。(個人例子是:雙色球機率) 可是: 假如咱們預測對了。那說明咱們的模型的效果,已經特別好了(雙色球每一個球都預測對了~)
既然你說y 是從詞典選拔出來的詞的機率屬性。 那麼這個機率怎麼算?
答: 這問得太好了~~~
前面說了: 通常都會預先給數據作 One-Hot或 Embedding編碼。 因此數據格式爲: [0,0,....,1,...] # 只有一個爲1 基本上咱們最後給輸出都會套一層: softmax激活函數, softmax應該知道吧:e^x /(e^x1+..+e^x) 因此: softmax結果就是一個 和One-Hot形狀同樣的機率列表集合: [.....,最高几率,...] softmax的結果(機率列表) :(表明着預測 在詞典中每個單詞的可能性)
那麼損失函數怎麼算呢??
答:沒錯,損失函數也是咱們最關注的。
前面:咱們已經求出了softmax對應的結果列表 (....,最高几率,...) 損失函數: 咱們使用的是交叉熵。 交叉熵知道吧: -( Σp*logq ) # p爲真實值One-hot, q爲預測值 簡單舉個例子: 假如 softmax預測結果列表爲 :[0.01,0.35, 0.09, 0.55] # 舒適提示,softmax和爲1 你的真實標籤One-Hot列表爲: [0, 0, 0, 1] 那麼交叉熵損失就等於: -( 0*log0.01 + 0*log0.35 + 0*log0.09 + 1*log0.55 ) = ... 到此爲止,咱們第一層NN的輸出的損失函數就已經計算完畢了。 而咱們訓練整個網絡須要計算總體的損失函數。 因此,咱們須要把上面的交叉熵損失求和, 優化損失。
RNN 的梯度是累乘,因此NN層若是不少,可能會達到 指數級的梯度。
你應該聽過一個小關於指數的小案例吧~~ (學如逆水行舟,不進則退~) >>> 1.01 ** 365 37.78343433288728 # 天天進步0.01 ,一年能夠進步這些 (對應梯度爆炸) >>> 0.99 ** 365 0.025517964452291125 # 天天退步0.01 , 一年能夠淪落至此(對應梯度消失)
梯度爆炸:
就是上面例子的原理。 就很少說了。 解決方式:梯度裁剪
梯度消失:
同上例, 很差解決(因而LSTM網絡出現, 和LSTM)
import tensorflow.keras as tk # 注意我用的是TF20標準版,因此這樣導入 tk.layers.SimpleRNN( units=單元層, # units單元數,對應着你每一個單詞的個數 return_sequences=False # 默認值就是False )
GRU比RNN的每一層的多了一個 記憶信息(至關於RNN的 h),這個記憶信息就像傳送帶同樣,一直流通各層RNN
而後還多了2個門 (r門和U門), 這2個門就是負責控制(是否從傳送帶上取記憶, 且取多少記憶)
簡化版(只有U門):
C新' = tanh( w @ [C舊, x新] + b ) # 根據傳動帶的舊信息, 生產出 傳送帶的新信息 u門 = sigmoid (w @ [c舊, x新] + b) # 一個門控單元,起到過濾信息的做用 C新 = u門 * C新' + (1-u門) * C舊 # 通過u門控單元的控制過濾後, 最終放到傳送帶的信息 若是: u門爲1,則傳送帶上全是新信息(舊的全忘掉) 若是: u門爲0,則傳送帶上全是舊信息(新的不要) 強調一下: 我不方便寫公式負號,因而用了 "新","舊" 代替 新: 表明當前 t 舊: 表明前一時刻 t-1
完整版(同時具備r門和u門)
添加這一行:
r門 = sigmoid (w @ [c舊, x新] + b) # 和下面的U門幾乎類似,只不過換了一下權重和誤差 C新' = tanh( w @ [r門 @ C舊, x新] + b ) # 修改這一行: C舊 ==變爲===> r門 @ C舊 u門 = sigmoid (w @ [c舊, x新] + b) # 一個門控單元,起到過濾信息的做用 C新 = u門 * C新' + (1-u門) * C舊 # 通過u門控單元的控制過濾後, 最終放到傳送帶的信息
import tensorflow.keras as tk # 注意我用的是TF20標準版,因此這樣導入 tk.layers.GRU( # 參數同上面RNN我就不解釋了 units=64, return_sequences=False # 這些參數看下面LSTM我會講到 )
LSTM和GRU很像,可是比GRU複雜。
LSTM結構包括: u門(更新門)+ f門(遺忘門)+ o門(輸出門)
f門 = sigmoid (w @ [c舊, x新] + b) # 和下面的U門幾乎類似,只不過換了一下權重和誤差 o門 = sigmoid (w @ [c舊, x新] + b) # 和下面的U門幾乎類似,只不過換了一下權重和誤差 C新' = tanh( w @ [C舊, x新] + b ) # 注意,這裏沒有r門了 u門 = sigmoid (w @ [c舊, x新] + b) # 一個門控單元,起到過濾信息的做用 C新 = u門 * C新' + f門 * C舊 # "(1-u門)" 換成了 f門 h = o門 * tanh( C新 )
import tensorflow.keras as tk # 注意我用的是TF20標準版,因此這樣導入 keras.layers.LSTM( units=64, return_state=True # 佔坑,下面剖析 return_sequences=False # 佔坑,下面源碼剖析 recurrent_initializer='glorot_uniform', # 均勻分佈的權重參數初始化 # stateful=True, # API文檔:若爲True,則每一批樣本的state的狀態,都會繼續賦予下一批樣本 )
return_state 和 return_sequences 這兩個參數到底有什麼用???
個人另外一篇文章單獨源碼分析這兩個參數:https://segmentfault.com/a/11...
GRU有2個門: u門 和 r門 LSTM有3個門: u門 和 f門 和 o門 GRU有一個C: # 就有一條傳送帶c, 他的先後單元信息僅靠這一條傳送帶來溝通(捨棄與保留) LSTM有一個C和一個h: # 不只有傳送帶c, 還有h, 他的先後單元信息 靠 c 和 h 聯合溝通。 再說一下每一個門控單元: 無論你是什麼門, 都是由 Sigmoid() 包裹着的。 因此: 說是 0 和 1 , 但嚴格意義上,只是無窮接近。可是微乎其微,因此咱們理解爲近似相等0和1
首先說明:
雙向模型,對於 RNN/LSTM/GRU 所有都適用
因爲單向的模型,不能關聯以後信息。好比:你只能根據以前的單詞預測下一個單詞。
而雙向的模型,能夠根據先後上下文的語境,來預測當前的下一個單詞。
或者舉一個更直白的例子(我本身認爲):
好比說: 你作英語完型填空題, 你是否是 須要 把空缺部分的 前面 和 後面 都得讀一遍,才能填上。
單向與雙向結構對好比下:
單向: 1 -> 2 -> 3 -> 4 雙向: 1 -> 2 -> 3 -> 4 | 1 <- 2 <- 3 <- 4 注意: 上下對齊,表明一層。
import tensorflow.keras as tk # 注意我用的是TF20標準版,因此這樣導入 tk.layers.Bidirectional( # 就在上上面那些API的基礎上,外面嵌套一個 這個層便可。 tk.layers.GRU( units=64, return_sequences=False ) ),
首先說明:
層疊模型對於 RNN/LSTM/GRU 一樣所有都適用
以前單層單向模型是這種結構
1 -> 2 -> 3 計算公式是: 單元 = tanh ( W @ (x, h左) )
而多層單向是這種結構(咱們以2層爲例):
y1 y2 y3 輸出層 ^ ^ ^ | | | 7 -> 8 -> 9 二層單元 ^ ^ ^ | | | 4 -> 5 -> 6 一層單元 ^ ^ ^ | | | x1-> x2 ->x3 輸入層 你 好 啊 計算公式是: (我寫的可能只按本身的意思了~) 一層每一個單元 = tanh ( W @ (x, h左) ) # 由於是第一層嘛:因此輸入爲 x 和 左邊單元h 二層每一個單元 = tanh ( W @ (h下, h左) ) # 第二層就沒有x了:而是下邊單元h 和 左邊單元h
c1 @ c2 餘弦定理,求 cosθ = ------------------ ||c1|| * ||c2|| 或者你可使用歐氏距離。
E @ O 矩陣乘積後記爲詞向量 W
可見以下案例:
若是: 咱們分詞後詞典總大小爲1000 那麼: 他的 One-Hot 矩陣形狀爲 [6, 1000] (假如咱們這裏經過句子6個詞來預測最後一個詞) 而且: 隨機高維權重矩陣 形狀爲 [1000, 300] (注意,這個300是維度,可自行調整選擇) 注意: 上面權重矩陣是隨機初始化的, 後面訓練調節的。 最後: E @ O 後獲得詞向量W 的形狀爲 [6, 300]
說下我的的理解,可能不對
skip-grams: 拿出中間 一個詞,來預測若干(這是詞距,本身給定)上下文的單詞。
例子以下:
seq = 今天去吃飯 給定單詞 標籤值(y_true) 去 今 去 天 去 吃 去 飯
訓練過程就是上面說過的小節 "原始詞嵌入並訓練",你只需把 y_true改成 "今","天","吃","飯"訓練便可。
Word2Vec 除了 skip-grams, 還有 CBOW 模型。 它的做用是 給定上下文,來預測中間的詞。
聽說效率等某種緣由(softmax計算慢,由於分母巨大),這兩個都沒看。(Pass~)
解決Word2Vec 的 softmax計算慢。
負採樣說明(假如咱們有1000長度的詞典):
從上下文(指定詞距): 隨機,選擇一個正樣本對,n個負樣本對(5-10個便可) 主要機制: 將 Word2Vec的 softmax(1000分類) 換成 1000個 sigmoid作二分類。 由於: 是隨機採樣(假設,採樣1個正樣本 和 5個負樣本)。 因此: 1000個 sigmoid二分類器,每次只用到 6 個對應分類器(1個正樣本分類器,5個負樣本分類器)
負採樣,樣本隨機選擇公式:
單個詞頻 ^ (3/4) ----------------- Σ(全部詞頻 ^ (3/4))