2019版CS224N中文筆記(3)神經網路基礎知識-上
筆者公眾號:技術雜學鋪
筆者網站:mwhitelab.com
本文為2019版CS224N中文筆記系列第三篇文章。本節中我們將會快速回顧神經網路的基本知識,並嘗試用神經網路來解決NLP中一個經典的問題——命名實體識別。
CS224N中文筆記又是拖了兩週才寫出來。拖延症是該治一治了……我盡力早日完成該系列內容……
往期文章:
1. 機器學習問題分類
1.1 監督學習、非監督學習與強化學習
監督學習(Supervised Learning) ,指我們有一資料集,資料集中每條資料都有特徵X(自變數)和標記Y(因變數)。我們想要使用某個演算法讓計算機學習到從X到Y之間的對映,學習完後,當計算機見到從未在訓練集中出現的特徵X時,其可以準確地預測出其對應的標記Y。
監督學習可運用到的場景如:給定一個人的X光照片,推斷其是否骨折(特徵X為照片資料,標記Y為 是/否);給定一個房子的房屋面積、地理座標等資訊,預期該房間的房價(特徵X為房屋資訊、標記Y為房屋價格)

而 非監督學習(Unsupervised Learning) 則是指 計算機自己探求無標記Y的資料之間的關係 ,如讓計算機根據使用者的觀影記錄(只有使用者的觀影記錄,即只有特徵X,沒有標記Y)自動給使用者分類,並向用戶推薦他們可能會喜歡的電影。
另外,我們可以不提前準備資料,而是建立一個環境(Environment)。我們讓一個智慧體(Agent)在環境中不斷執行各種各樣的動作(Action),從而到達不同的狀態(State)。環境會在智慧體每次執行一個動作後,根據其所在的狀態,給與智慧體一個獎勵(Reward)。智慧體的目的是不斷與環境互動,從而找到使其獲得的獎勵最大/最小的策略(π)。該方法即為 強化學習(Reinforcement Learning) 。大名鼎鼎的Alpha go就是用該方法實現的。

1.2 分類與迴歸
無論是神經網路,還是經典的機器學習演算法如樸素貝葉斯、SVM、邏輯迴歸、k近鄰演算法,都屬於監督學習(嚴格上來講,神經網路也可以進行非監督學習)。
監督學習可以細分為分類問題和迴歸問題。
分類問題 中,我們資料中的標記Y有有限個可能取值。比如根據預測一個人是否得病(二分類,是/否)又或者預測一段新聞屬於什麼型別(多分類,科技/政治/娛樂/體育……)

而 迴歸問題 中,我們的標記Y的有無限個可能取值。如根據房屋資訊預測房價,標記Y的取值範圍在[0, +∞)。

1.3 分類演算法舉例——softmax
機器學習中有很多典型的分類演算法,如k近鄰、線性分類、SVM等等。
這裡,我們會介紹一個我們以後常見的分類演算法——softmax
softmax是一個多分類演算法,即給定特徵X,softmax可以告訴我們其對應的特徵Y是多種可能取值中的哪一個。如給定一篇新聞(特徵X),我們預期其對應的新聞題材(Y取值為體育、政治、娛樂、科技……有限種可能中的一個)。
假設標記Y的可能取值有C種,那麼已知一個樣本的特徵x,該樣本的標記是y的概率為P(y|x):

上圖演算法即為softmax公式。其中W是需要我們學習的引數。
我們得到的P(y|x)是我們通過演算法預測到的概率,為了評價該預測的結果好壞,我們會計算其交叉熵(Cross entropy):

q(c)為演算法預測樣本的標記y是c的概率,取值在0-1之間。p(c)為樣本的標記y客觀上是否真的為c,取值為0(樣本的標記不是c)或1(樣本的標記取值是c)。
對訓練集中所有樣本的交叉熵取平均值,就得到了損失函式(loss function/cost function)

如果我們的演算法預測的效果很好,則損失函式的取值應該很小。通過 梯度下降法 可以不斷更新我們演算法中的引數W,從而使損失函式的取值達到一個極小點,此時,我們可以人為演算法學習到了訓練集中特徵X到標記Y之間的關係(當然,還要考慮過擬合的問題)。
此處只是softmax的一個簡短介紹,讀者若對機器學習感興趣,可看吳恩達的CS229課程。(另外,筆者也在更新 機器學習演算法詳解 系列文章,只不過更新速度比CS224N中文筆記還慢……)
2. 神經網路-基礎
2.1 神經網路結構
這裡僅為神經網路基礎知識的快速總結。讀者若未曾系統地學習神經網路,可學習 吳恩達的DeepLearning課程 。
神經網路,故名思議,其結構上參考了人腦中神經連線的方式:

我們也類似如上圖的結構, 將前一層神經元們的訊號經過處理後傳到下一層,下一層神經元們的訊號經過處理後再傳到下下層 。
如下圖,上一層神經元的訊號X1、X2、X3 經過處理 後傳到下一層橙色神經元處。

僅僅傳遞一層往往是不夠的,我們可以構建一個傳遞多層的神經網路。

讓傳遞的層數再多點:

2012年驚豔眾人的影象識別網路AlexNet只有8層,後來的VGG網路有16層和19層兩個版本,再之後殘差網路ResNet有50、101和152層,再到現在上千層的神經網路也不在少數。(有時,我們也會將“層數”稱為“深度”)
如今的神經網路的確是往bigger and lagger的方向上來走,(摩爾定律可能在硬體領域已經不再適用了,不過在神經網路大小這個方向上定律十分適用。另外,適用於摩爾定律的還有每年釋出的深度學習paper的數量)不過過大的網路會帶來梯度爆炸、梯度消失等問題,這些問題我們會在之後的內容中講到。
值得注意的是,我們上面所說的神經網路的層數、每一層中神經元的個數 都是人們根據經驗給定的 ,沒有數學原理作為支撐。
不過數學上可以證明的是,神經網路層數越深、每層上神經元的個數越多,該神經網路可以擬合更復雜的函式、應用於更復雜的領域。
2.2 神經網路數學原理
那神經元之間如何傳遞資訊呢?我們先研究 第一層中全部神經元訊號傳遞到第二層中一個神經元 的過程,即如下圖:

每個神經元都儲存著一個浮點數,我們用向量X = [X1, X2, X3]來表示第一層的三個神經元。於是我們可以用矩陣運算的方式來計算X1, X2, X3三個神經元對第二層橙色神經元的影響:
我們先同樣定義一個向量W,其長度和向量X相同。向量W的中資料的取值在(-∞, +∞), W中的第i位代表第一層神經元X中第i個神經元對橙色神經元的重要程度 。Wi大,說明橙色神經元與Xi正相關;Wi為負,說明橙色神經元與Xi負相關;Wi接近0說明橙色神經元與Xi幾乎無關。
因此我們用 W*X(由於符號顯示不便,矩陣的轉置符號此處省略) 表示第一層神經元們的線性加權, 該值的絕對值越大,則說明第一層神經元們有一個很重要的資訊想要傳送給橙色神經元 ;該值絕對值接近零,則說明第一層神經元們沒有什麼重要資訊需要傳遞給橙色神經元。
一般,我們會用W*X+b來表示第一層神經元們的線性加權,其中b是偏置(bias)有稱閾值,可以理解為 只有當W*X的取值遠大於-b或遠小於-b時才可以啟用橙色神經元 。

W和X都是大小為L1*1的向量(L1為第一層神經元們的個數),b為一個標量。
有的時候為了便於矩陣計算,我們會將b併入W中,即W’ = [W1, W2, W3, b], X’ = [X1, X2, X3, +1 ] 這樣W’*X’就等於之前的W*X+b了。這也是為什麼有的神經元示意圖中每層末尾都會有“+1”符號。
經過上述操作後W*X+b的值就可以直接傳送給橙色神經元了嗎?並不可以!我們還需要一個 非線性的啟用函式 ,原因可見2.3節。
啟用函式的作用是將原始的資料進行非線性變換,一個典型的代表就是我們上一篇文章中講過的sigmod函式:y=1/(1+e^(-x))

其可以將(-∞, +∞)的資料變換到(0, 1)。
綜上所述,前一層的所有神經元想要把訊號傳送給下一層神經元中的一個神經元 需要先進行W*X+b的線性變換,之後再經過一個非線性的啟用函式 ,如sigmod啟用函式。由啟用函式得出來的值即為下一層神經元的取值。

我們稱該步驟中的W和b為模型的 引數 , 其的取值是需要演算法自行學習的 。啟用函式不用學習,啟用函式長什麼樣是我們人為選定的,沒有可訓練的引數。
上述我們所進行的操作僅僅是 前一層所有神經元 資訊傳遞到 下一層一個神經元 的過程。若下一層有N個神經元,則上述的 x’ = sigmod( X*W + b ) 過程需要進行N遍(矩陣運算可以同時進行N遍這樣的操作)。
且W和b的取值不是共享的: 下一層神經元不同,其所對應的W和b引數也不同。(很好理解,如果W和b對每一個下一層神經元來說都一樣,那麼下一層神經元的取值則完全相同,每個神經元學習到的東西都一模一樣,這是十分冗餘的)
2.3 啟用函式
2.3.1 為何使用啟用函式?
2.2節中,我們講到,上一層神經元的資料想要傳遞到下一層,需要經過兩步:
第一步:對上一層資料進行線性加權,Z = W*X + b
第二步:對線性加權後的值進行非線性變換,Y = f(Z),其中函式f是一個非線性函式。

為什麼必須要使用啟用函式?神經網路中每層神經元之間資料傳遞只用線性加權,不用進行非線性變換可不可以?
假設我們有一個如下圖的神經網路(我們統計一個神經網路的層數時,不會考慮輸入層。因此下圖的神經網路為一個三層的神經網路):

由上圖可知,輸入層X0維度為3*1,隱藏層X1的維度為4*1,隱藏層X2的維度為4*1。輸入層X0到隱藏層X1的線性加權引數W1維度為3*4,偏置b1的維度為4*1;隱藏層X1到隱藏層X2的線性加權引數W2維度為4*4 ,偏置b2的維度為4*1。(分析各個引數的維度有助於更好地理解神經網路)
下面的計算中,為了計算簡便,我們忽略偏置b(有偏置b的結果相同)。
如果不使用啟用函式(非線性變換)的話,隱藏層X1可以用X0表示為:

那麼隱藏層X2可以用X1表示為:

經過推導後我們發現,X2竟然也可以用X0的線性變換來表示!這就意味著無論是多深的神經網路,只要不使用啟用函式(非線性變換),其效果和層數為1的神經網路是一樣的。

不使用啟用函式的神經網路其本質是Y = W*X+b,是一個 線性分類器 。線性分類器的一個特點是用二維圖形展示線性分類器的分類效果時,其分類邊界是一條直線(softmax也屬於線性分類器)
線性分類器可以進行簡單的分類,但是無法處理像“異或”這樣稍微高階一點的邏輯(線性不可分的問題)。
假設我們的特徵X是一個二維資料,Y的取值有兩種,綠色或紅色。那麼我們可以用下圖直觀地展示使用啟用函式和不使用啟用函式的效果:

圖中每個點為訓練資料,而背景顏色為訓練完後的神經網路預測的結果。
左側的圖即為不使用啟用函式的網路,是線性分類器。可以看到,有很多綠色的點被誤判為紅色。線性分類器的學習能力有限,左側的圖已經是“用一條線劃分紅綠區域”情況下最佳的結果了。
而右側的圖即為使用啟用函式後的神經網路預測的結果。 此時,神經網路可以捕獲訓練資料集中更多有用的資訊,學習出更復雜的邏輯,其分類邊界也更復雜。
2.3.2 啟用函式舉例
啟用函式的目的就是為了讓神經網路中每層神經元之間 傳遞的資料不是簡單的線性關係 。非線性關係的資料可以讓神經網路學習到更復雜的邏輯。
常用的啟用函式有很多。在神經網路剛剛提出時(上個實際八十年代),人們很喜歡用 sigmoid 函式(下圖中左一),但是現在人們反而很少使用sigmoid了,除非是作為二分類神經網路的最後一層。

其原因為sigmoid函式在 無窮大和無窮小處的梯度都極小 ,這就導致在我們用梯度下降法進行引數更新時, 每輪中引數變化的幅度很小,損失函式收斂速度極慢 。
tanh 在NLP中很常用,尤其是在LSTM和GRU中。不過通過計算可以發現,tanh其本質就是2*sigmoid-1 。
hard tanh 啟用函式是tanh的變體,當x的絕對值大於1時,函式的梯度為0;當x的絕對值小於1時,函式的梯度為1。這樣便捷了反向傳播時的計算。
ReLU 是如今最受歡迎的啟用函式,y = max(0, x)。其和hard tanh比較相似:當神經元上的數值小於0時,啟用函式的梯度為0,不更新該神經元對應的引數W和b;而當神經元上的引數大於0時,啟用函式的梯度為1,更新該神經元對應的引數W和b。

使用ReLU,X小於零時,對應的引數不會被更新。有人認為,我們也可以更新那些X小於零的神經元對應的引數,不過更新的梯度要小一點,比如x>0梯度為1,x<0梯度為0.1(如果x<0時梯度也為1,那就和不使用啟用函式一樣了,最後得到的神經網路就是一個線性分類器)
於是,就有了 LeakyReLU :y = max(0, x) + leak*min(0,x) 。

除了leakyReLU,ReLU啟用函式的變體還有很多,如 ELU (下圖綠線),其比ReLU更加的平滑。

實驗證明, ReLU的效果很好 。但ReLU缺少數學上合理的解釋。 GELU (上圖藍線)是一種高效能的神經網路啟用函式,且有一定的數學原理。NLP領域中大名鼎鼎的BERT模型就是使用GELU作為啟用函式的。
讀者若是剛入門深度學習,啟用函式使用ReLU就足夠了。
3. 神經網路應用案例——命名實體識別
經過之前那麼的鋪墊,我們終於開始進行第一個NLP的任務:命名實體識別(Named Entity Recognition)
3.1 何為命名實體識別
命名實體識別(Named Entity Recognition) 簡稱NER,目的是找到並分類文字中的實體。如“崔永元曾經是中央電視臺的主持人”中的實體有“崔永元”、“中央電視臺”和“主持人”,除了找出這些實體,我們還希望AI可以識別出“崔永元”是人名、“中央電視臺”是組織機構名、“主持人”是職位。
命名實體識別是NLP中的一項 基礎 任務,也是一項比較 重要 的任務。因為一句話中重要的語義資訊往往就蘊含在實體之間的關係中,我們在進行問答時,答案也常常是實體名稱。

我們現在的任務是,給定任意一段話,如“我今天去了遊樂園”,我們希望找到該語句中的實體,如“我”、“遊樂園”。(更高階的任務還包括給實體分類,“我”屬於代詞,“遊樂園”屬於地名)
也許你會覺得,這個問題太簡單了,完全不需要使用深度學習來實現,人為建一個字典就行了,這個字典中包含我們提前定義的實體名稱,只要語句中的詞出現在字典中,那這些詞就肯定是實體:當我們在一句話中看到“你”、“他”、“她”,“我”這樣的詞時,這些詞肯定是實體,屬於代詞;當文字中有“遊樂園”、“餐廳”、“車站”時,這些詞肯定也是實體,屬於地名……
只要按照如上步驟,就可以快速地找到一句話中的實體,哪還需要用深度學習來實現啊!
但是,無論是什麼語言,都會存在一詞多義的情況:英語中”To sanction” 可以理解為”to permit”(允許)或者 “to punish”(懲罰);中文中的“可憐”一詞,在“我好可憐”中,“可憐”是形容詞,而在“你們可憐可憐我吧”中,“可憐”又成了動詞……類似的例子數不勝數,我們往往需要依靠一個詞的上下文才能明白該詞的意思。
而且在“南京市長江大橋”這樣的話中,不考慮上下文的實體識別可能會出現分成“南京”、“市長”、“江大橋”這樣的笑話。

3.2 如何實現命名實體識別
注:CS224N中的命名實體識別(NER)的深度學習演算法有些過於簡化,以至於若真正使用該演算法進行NER任務,效果可能並不好。這裡,筆者將會給出課程原版的演算法和筆者改進版演算法。
3.1中,我們講到我們最好通過上下文來理解一個詞。因此,我們讓神經網路判別一個詞是否是實體時,我們不僅告訴神經網路這個詞是什麼,我們還要告訴神經網路這個詞前後N個詞是什麼。
比如N取2,那麼我們可以向神經網路輸入X=[museums, in, Paris, are, amaing],讓神經網路通過“Paris”的上下文來推斷“Paris”是否為實體。

這裡,每個單詞都由一個D維詞向量來表示。(關於詞向量的知識,可見第一講和第二講 )
3.2.1 網路結構
假設每個單詞都是的詞向量維度D為4(取4僅僅是為了繪圖方便,正常情況下,詞向量維度在100-300之間),那麼輸入的資料的長度為5*4。我們可以構建一個輸入節點個數為5*4,輸出節點個數為1的神經網路。如下圖:

該神經網路示意圖中,輸入層長度為100,只有一層隱藏層,其長度為8,輸出層長度為1。這裡啟用函式只在隱藏層中使用,在輸出層不使用。
(筆者改進版) 上圖的神經網路僅僅是一個示意,結合我們第2節所講的知識, 你可以自行DIY你的神經網路 。如輸入9個單詞,每個單詞的詞向量維度為100。那麼輸入層的長度為900,之後我們再設第一層隱藏層維度為300,第二層隱藏層維度為100,第三層隱藏層維度為50,最後的輸出層維度為1。其中輸出層的啟用函式是sigmoid,所有隱藏層的啟用函式均為ReLU。
3.2.2 目標函式
經過神經網路後,輸出層最終會返回一個數值。那麼如何通過輸出層的數值來判斷輸入的文字是不是實體呢?又如何通過判斷我們的演算法預測效果的好壞呢?
我們取一對訓練樣本,如一個正樣本[“我”,“去”,“圖書館”, “看”, “西遊記”](詞“圖書館”是實體)和一個負樣本[“今天”,“天氣”,“很”,“好”,“啊”](詞“很”不是實體)。(這裡假設中文文字已經提前分詞完成,且不考慮“南京市長江大橋”被分詞成“南京”,“市長”,“江大橋”這樣的尷尬情況)
我們希望正樣本經過神經網路後,神經網路的輸出資料是一個比較大的數,而負樣本經過神經網路後,神經網路的輸出資料是一個比較小的數。
我們設每一對訓練樣本中,正樣本經過神經網路後的輸出資料為S,而負樣本經過神經網路後的輸出資料為Sc 。為了滿足我們上述的希望,即S大,Sc小,我們設目標函式為 J = max(0, 1 – S + Sc), 最小化目標函式 J 的取值即可完成我們S取值大Sc取值小的願望。

當S的取值比Sc大很多時(這裡是S比Sc大1)1-S+Sc就會是負數,那麼J = max(0, 1 – S + Sc) 就能取到最小值0。若S比Sc小,或者S並不比Sc大多少時,1-S+Sc就會是一個正數,目標函式J就是一個大於0的數,其還有繼續優化的空間。
這裡 1 – S + Sc中的 1 可以是2、3、100……只要大於零即可,其目的是讓正樣本的輸出S和負樣本的輸出Sc拉開一定的差距。
知識補充:這裡max(0, ∆ − S + Sc)也稱為 Hinge Loss 或者 max-margin objective function, 是SVM中常見的一個目標函式。另外,訓練人臉識別模型時,有時也會選其作為目標函式。
(筆者改進版) 筆者還是習慣以 交叉熵 作為損失函式。接著我們3.2.1末尾介紹的自己DIY設計的神經網路模型。只要你輸出層使用了sigmoid或者softmax函式讓其取值壓縮在0-1之間,你就可以使用交叉熵作為損失函式。(輸出層只有一個節點時用sigmoid,輸出層有多個節點時用softmax。讀者可以自己推導一下,而二分類問題中,softmax就是sigmoid)

輸出層用sigmoid/softmax + 交叉熵 的組合 相較於輸出不用啟用函式+目標函式為Hinge Loss的組合而言 有很多優點 。其中最明顯的一個是前者的輸出層的輸出值就是該模型預測樣本為對應類別的概率。如輸出層若有3個節點,其輸出值分別為0.1, 0.7, 0.2 ,那麼這意味著模型認為樣本是第一個類別的概率為0.1,是第二個類別的概率為0.7,是第三個類別的概率為0.2。
3.2.3 更多改進方向
上述內容中我們只考慮了一個詞是不是命名實體,沒有給改實體進行分類。可以考慮將我們所討論的模型 從二分類問題改為多分類問題 。輸出層的維度不再是1,而是N。如N取4,考慮一個單詞是屬於[非實體, 人名, 地名, 組織機構名]中的哪一個。若屬於“人名”,則輸出層中對應“人名”的結點取值應該很大,其他結點取值很小。
再另外,對於未分詞的中文文字,如輸入不是[ “我”, “今天”, “去”, “遊樂園” ] ,而是[ “我”, “今”, “天”, “去”, “遊”, “樂”, “園”],此時我們當然可以還是把其當成一個二分類問題,即每個字是不是實體詞的一部分,“我”, “遊”, “樂”, “園”是實體詞的一部分, 而“今”, “天”, “去”這三個字不是。我們也可以把其當成一個三分類問題:[實體詞開頭,實體詞結尾,其他詞]
3.2.4 是否更新詞向量?
在上述討論中,詞向量都是提前預訓練好的(可以使用word2vec,glove等方法,詞向量預訓練可見第二講)
預訓練的詞向量中詞義相近的詞其詞向量很相近。TV、telly和television是同義詞。
訓練時不更新詞向量: 假設我們的神經網路在訓練時只見過TV和telly,並將其分類為紅色,由於television的詞向量和TV、telly很接近,因此即使神經網路沒有在訓練集中遇到television這個詞,但它還是成功地把television分類為紅色。

訓練時更新詞向量 :神經網路更新了TV和telly的詞向量,但其沒有在訓練樣本中見過television,所有也就沒有更新television的詞向量。最後telly和TV被正確地分為紅色,但television雖然與TV、telly語義相同,但是被分為了綠色。

那麼這樣看來訓練時更新詞向量很危險,所有就不更新詞向量,只用預訓練的詞向量?不是這樣的。 在訓練樣本巨大的時候,訓練時更新詞向量會帶來更好的結果 。
那我們該如何使用詞向量呢?
首先,在條件允許的情況下,我們應該使用預訓練的詞向量(Almost always) 。預訓練的詞向量已經捕獲了很多有用的語義和語法資訊。使用預訓練的詞向量來訓練神經網路,其收斂速度遠遠快於隨機初始化詞向量,再訓練神經網路。
其次,當我們訓練樣本很小時,不要更新預訓練的詞向量。當我們的訓練樣本很大時更新預訓練的詞向量。 (關於訓練樣本有多大才可以更新詞向量,沒有很明確的界限。筆者認為,起碼上萬句語料吧)
第三 ,更新詞向量的方法叫fine-tune(遷移學習中也會用到), 更新詞向量的學習率α要很小 ,因為詞向量已經捕獲了語義資訊,我們只需要對詞向量進行微調即可。而且 在神經網路訓練剛開始時不更新詞向量,等目標函式收斂到一定程度時才開始訓練詞向量 。因為神經網路開始時是隨機初始化的,此時要是更新詞向量,只會降低詞向量的表現效果。
更新詞向量我們將會使用反向傳播。關於反向傳播的相關知識以及神經網路的更多基礎知識,我們將會放在下一講中一一講述。
總結
- 機器學習任務可分為監督學習、非監督學習和強化學習。其中監督學習包括分類任務和迴歸任務。
- 神經網路參照了人腦的神經網路結構,隨著資料量和計算能力的提升,深度學習逐漸成為了AI的熱門領域。
- 不使用啟用函式的神經網路是線性分類器,效果很差。我們常用ReLU作為啟用函式。
- 執行NLP任務時,建議使用預訓練詞向量。當我們有大量訓練文字時,可以更新詞向量。
額外內容
【注:本文部分配圖來自於網路】