【Hanlp源碼分析】基於感知機實現人名性別預測
本文是分析github中著名的開源NLP工業級代碼——hanlp的實現細節,其作者是何晗。
0.總結
Get to the points first. The article comes from LawsonAbs!
- part1:感知機算法簡介
- part2:【Hanlp源碼分析】基於感知機實現人名性別預測
1.什麼是感知機算法?
-
屬於監督學習算法
比如在後面給出的實戰代碼中,我們給出了人名對應的性別,會根據這個預測是否準確去完成一次更新操作。 -
屬於迭代式算法
每次讀入一個樣本,執行預測,將預測結果與正確答案進行對比,計算誤差,根據誤差更信模型參數。
2.有什麼用途?
- 可以用於問題分類
3.有什麼利弊?
-
模型過於簡單
參數較少的模型就容易出現**“不擬合”**的情況,同時,也會因爲數個偏離點而對迭代更新的參數有很大的影響。從而難以準確的預測結果,造成較大偏差。 -
迭代偏差
這裏說的迭代偏差是因爲我們總是傾向於取最後一次迭代的模型。
每次迭代都產生一個模型,而且每個模型的對應的每個參數不一定相同。如果我們單純的取最後一次迭代產生的模型作爲“最優模型”的話,很有可能是 “補了西牆,拆了東牆”。
3.怎麼改進?
針對上面敘述的問題,對應的改進方法有投票感知機和平均感知機,分別簡單敘述一下。
3.1 投票感知機
3.1.1 思想
每次迭代都會產生一個不同參數的模型,那麼這些模型間的準確率就不一樣,把這個準確率稱之爲權重。根據各個模型的權重可以得到每個模型的加權平均值。根據各個模型的加權平均值與參數相乘,得到最終的一個模型。
3.1.2 缺點
- 計算開銷大
- 存儲開銷大
3.2 平均感知機
3.2.1 思想
每次迭代都會產生一個不同參數的模型,最後得到的模型就取每次迭代模型的一個平均。【這個平均其實指的是模型參數的平均】【《自然語言處理入門》這本書中講的是:取多個模型權重的平均。我不是很理解。】
3.2.2 特點
- 好實現
- 藉助一定的方法【累積參數和,而不是傻傻的每迭代一次就算一次平均】,可以避免存儲中間模型,可以以更少內存得到最終的模型
下面開始講第二部分,分析感知機算法中最簡單的【樸素感知機】的代碼實現。因爲原作者用的是Java,所以會涉及到一些Java的語法知識。後期我會造出 python 版的輪子。
4.主要類
4.1 NameGenderClassification
main()
是入口函數, trainAndEvaluate()
中會訓練模型【classifier.train(…)】,會將得到的模型進行效果評估【classifier.evaluate(…)】。
4.2 PerceptronClassifier
這個抽象類中實現了 上述的 train方法。如下所示:
這裏主要做了幾件事兒:
- 初始化一個特徵字典(
featureMap
),這個就是將人名中除去姓氏的名字做一個映射。 - 讀取語料庫【
reandInstance(corpus,featureMap)
】
這是很重要的一部分內容,其關鍵問題是:如何把語料庫信息轉換爲數據信息? 下面慢慢談。
(1)corpus是語料庫的地址,爲了便於測試,這裏我用了一個自己編輯的語料庫做訓練集。內容如下:
這裏傳入featureMap
的原因就是因爲在讀取語料庫時 將特徵寫入Map中。在看如何將語料信息轉換爲數據信息之前,有必要了解一下 類Instance
中的定義,這裏的Instance
就代表是語料庫中的一行數據。
這裏的Instance
包含兩個屬性:
(1)特徵向量
(2)標籤【即應該得到的正確分類結果】
這裏的特徵向量存在方式很特別。通常的文字轉向量採用的編碼方式有one-hot編碼,但是這裏採用的是記錄下標的方式。這樣做是爲了減少內存開銷,加速計算。
下面我以上面的語料庫舉個例子:由 “范仲淹,男”這行數據,可以得到一個兩個特徵=> 仲和淹。因爲它們都是第一次出現,所以就直接放到featureMap
中,得到的就是0和1
,因爲范仲淹是男性,所以最終得到的Instance
實例就是x=[0,1], y=-1
。其中x
是一個LinkedList
型的對象。
featureList
中的值如下所示:仲對應0,淹對應1…後面同樣的道理。
其中會調用的幾個方法如下:
注意到 readInstance
方法返回的是一個 Instance 的數組。如果用Instance[] instArray = new Instance[200]; //直接使用 Instance[] 去直接生成一個數組
將顯得有寫生硬且浪費。
- 根據參數
averagePerceptron
選擇是進行何種訓練方式?我這裏就分析樸素感知機,其他的都很相似,除了平均感知機做了一個簡單的內存和計算優化,也沒啥的。
這裏是最主要的內容。下面我們慢慢談。
這裏的感知機機制就是: y=w*x + b
。提煉一下就得到y = wx
,因爲代碼中存儲的特徵是下標值【其實際含義是:該值下標對應的值爲1】,所以直接採用y+=parameter[f]
的這種形式就可以得到一個模擬結果。數組parameter[]
的初始值賦爲0。代碼如下:
如果預測結果部隊,則需要對該預測過程涉及到的參數進行更新:
這裏的更新過程也是結合了相關的推導進行了簡化。因爲只有預測錯了纔會對參數進行更新。分類討論一下:
當預測結果爲-1,而正確結果爲1時,因爲特徵向量x的值不可變,且其恆爲正,所以我們可以將w的值變大,從而得到一個更大結果值,所以可以直接進行parameter[f]+=y
操作;
相應的,當預測結果爲1時,可得到相反的結論。
- 最後評價這個模型的效果【
evaluate(instanceList)
】
跑出的結果如下所示:
因爲訓練集合就一點兒數據,所得到的準確率很低,當將訓練集換成正常的語料庫之後,可以得到一個很好的預測效果: