請添加圖片描述
訓練Word2Vec模型
概述
問題
- 我們如何訓練Word2Vec模型?
- 在特定數據集上訓練Word2Vec模型何時是有利的?
目標
- 理解在自有數據上訓練Word2Vec模型而非使用預訓練模型的優勢
Colab環境配置
運行以下代碼以啟用輔助函數并重新讀取數據。
PYTHON代碼
# 運行此單元格以掛載你的Google Drive
from google.colab import drive
drive.mount('/content/drive')# 顯示現有Colab筆記本和helpers.py文件
from os import listdir
wksp_dir = '/content/drive/My Drive/Colab Notebooks/text-analysis/code'
print(listdir(wksp_dir))# 將文件夾添加到Colab的路徑中,以便導入輔助函數
import sys
sys.path.insert(0, wksp_dir)
輸出結果
Mounted at /content/drive
['analysis.py','pyldavis.py','.gitkeep','helpers.py','preprocessing.py','attentionviz.py','mit_restaurants.py','plotfrequency.py','__pycache__']
PYTHON代碼
# 安裝parse模塊(helpers.py中調用)所需的依賴
!pip install parse
加載數據
PYTHON代碼
# 重新讀取數據
from pandas import read_csv
data = read_csv("/content/drive/My Drive/Colab Notebooks/text-analysis/data/data.csv")
創建我們分析時要用到的文件列表。我們先將Word2Vec模型擬合到列表中的一本書——《白鯨記》(Moby Dick)。
PYTHON代碼
single_file = data.loc[data['Title'] == 'moby_dick','File'].item()
single_file
輸出結果
'/content/drive/My Drive/Colab Notebooks/text-analysis/data/melville-moby_dick.txt'
我們預覽文件內容,確保代碼和目錄設置正常工作。
PYTHON代碼
# 打開并讀取文件
f = open(single_file,'r')
file_contents = f.read()
f.close()# 預覽文件內容
preview_len = 500
print(file_contents[0:preview_len])
輸出結果
[Moby Dick by Herman Melville 1851]ETYMOLOGY.(Supplied by a Late Consumptive Usher to a Grammar School)The pale Usher--threadbare in coat, heart, body, and brain; I see him
now. He was ever dusting his old lexicons and grammars, with a queer
handkerchief, mockingly embellished with all the gay flags of all the
known nations of the world. He loved to dust his old grammars; it
somehow mildly reminded him of his mortality."While you take in hand to school others, and to teach them by wha
PYTHON代碼
file_contents[0:preview_len] # 注意實際字符串中仍包含\n(print()會將其處理為換行)
輸出結果
'[Moby Dick by Herman Melville 1851]\n\n\nETYMOLOGY.\n\n(Supplied by a Late Consumptive Usher to a Grammar School)\n\nThe pale Usher--threadbare in coat, heart, body, and brain; I see him\nnow. He was ever dusting his old lexicons and grammars, with a queer\nhandkerchief, mockingly embellished with all the gay flags of all the\nknown nations of the world. He loved to dust his old grammars; it\nsomehow mildly reminded him of his mortality.\n\n"While you take in hand to school others, and to teach them by wha'
預處理步驟
- 將文本拆分為句子
- 對文本進行分詞
- 對所有詞元進行詞形還原并轉為小寫
- 移除停用詞
1. 將文本轉換為句子列表
要記住,我們利用句子中單詞的序列來學習有意義的詞嵌入。一個句子的最后一個單詞并不總是和下一個句子的第一個單詞相關。因此,在進一步處理前,我們要將文本拆分為單個句子。
Punkt句子分詞器
NLTK的句子分詞器(“punkt”)在大多數情況下表現良好,但當遇到包含大量標點、感嘆號、縮寫或重復符號的復雜段落時,可能無法正確檢測句子。要解決這些問題沒有通用的標準方法。如果希望確保用于訓練Word2Vec的每個“句子”都是真正的句子,需要編寫一些額外的(且高度依賴數據的)代碼,利用正則表達式和字符串操作來處理罕見錯誤。
就我們的目的而言,愿意容忍少量句子分詞錯誤。如果這項工作要發表,仔細檢查punkt的分詞結果是值得的。
PYTHON代碼
import nltk
nltk.download('punkt') # sent_tokenize函數的依賴
sentences = nltk.sent_tokenize(file_contents)
輸出結果
[nltk_data] Downloading package punkt to /root/nltk_data...
[nltk_data] Package punkt is already up-to-date!
PYTHON代碼
sentences[300:305]
輸出結果
['How then is this?','Are the green fields gone?','What do they\nhere?','But look!','here come more crowds, pacing straight for the water, and\nseemingly bound for a dive.']
2-4:分詞、詞形還原與移除停用詞
調用文本預處理輔助函數并拆解代碼邏輯……
- 我們將在每個句子上運行此函數
- 詞形還原、分詞、小寫轉換和停用詞處理都是之前學過的內容
- 在詞形還原步驟,我們使用NLTK的詞形還原器,它運行速度很快
- 我們還會使用NLTK的停用詞列表和分詞函數。回憶一下,停用詞通常是語言中最常見的單詞。移除它們后,Word2Vec模型可以只關注有意義單詞的序列。
PYTHON代碼
from helpers import preprocess_text
PYTHON代碼
# 測試函數
string = 'It is not down on any map; true places never are.'
tokens = preprocess_text(string,remove_stopwords=True,verbose=True)
print('Result', tokens)
輸出結果
Tokens ['It', 'is', 'not', 'down', 'on', 'any', 'map', 'true', 'places', 'never', 'are']
Lowercase ['it', 'is', 'not', 'down', 'on', 'any', 'map', 'true', 'places', 'never', 'are']
Lemmas ['it', 'is', 'not', 'down', 'on', 'any', 'map', 'true', 'place', 'never', 'are']
StopRemoved ['map', 'true', 'place', 'never']
Result ['map', 'true', 'place', 'never']
PYTHON代碼
# 將句子列表轉換為pandas Series,以便使用apply功能
import pandas as pd
sentences_series = pd.Series(sentences)
PYTHON代碼
tokens_cleaned = sentences_series.apply(preprocess_text,remove_stopwords=True,verbose=False)
PYTHON代碼
# 查看清洗前的句子
sentences[300:305]
輸出結果
['How then is this?','Are the green fields gone?','What do they\nhere?','But look!','here come more crowds, pacing straight for the water, and\nseemingly bound for a dive.']
PYTHON代碼
# 查看清洗后的句子
tokens_cleaned[300:305]
輸出結果
300 []301 [green, field, gone]302 []303 [look]304 [come, crowd, pacing, straight, water, seeming...dtype: object
PYTHON代碼
tokens_cleaned.shape # 共9852個句子
PYTHON代碼
# 移除空句子和僅含1個單詞的句子(全為停用詞)
tokens_cleaned = tokens_cleaned[tokens_cleaned.apply(len) > 1]
tokens_cleaned.shape
使用分詞后文本訓練Word2Vec模型
現在我們可以用這些數據訓練Word2Vec模型。首先從gensim
導入Word2Vec
模塊。然后將分詞后的句子列表傳入Word2Vec
函數,并設置sg=0
(“skip-gram”)以使用**連續詞袋(CBOW)**訓練方法。
為完全確定性運行設置種子和工作線程:接下來我們設置一些可復現性參數。設置種子,確保每次運行代碼時向量的隨機初始化方式相同。為了實現完全確定性的復現,我們還將模型限制為單工作線程(workers=1
),以消除操作系統線程調度帶來的順序抖動
PYTHON代碼
# 導入gensim的Word2Vec模塊
from gensim.models import Word2Vec# 使用清洗后的數據訓練Word2Vec模型
model = Word2Vec(sentences=tokens_cleaned, seed=0, workers=1, sg=0)
Gensim的實現基于Tomas Mikolov最初的word2vec模型,該模型會根據頻率自動下采樣所有高頻詞。下采樣能節省模型訓練時間。
后續步驟:詞嵌入應用場景
現在我們已獲得《白鯨記》中所有(經過詞形還原和去停用詞的)單詞的向量表示。看看如何用這些向量從文本數據中獲取洞見。
最相似單詞
和預訓練Word2Vec模型一樣,我們可以用most_similar
函數找到與查詢詞有意義關聯的單詞。
PYTHON代碼
# 默認設置
model.wv.most_similar(positive=['whale'], topn=10)
輸出結果
[('great', 0.9986481070518494),('white', 0.9984517097473145),('fishery', 0.9984385371208191),('sperm', 0.9984176158905029),('among', 0.9983417987823486),('right', 0.9983320832252502),('three', 0.9983301758766174),('day', 0.9983181357383728),('length', 0.9983041882514954),('seen', 0.998255729675293)]
詞匯表限制
注意,Word2Vec只能為訓練數據中出現過的單詞生成向量表示。
PYTHON代碼
model.wv.most_similar(positive=['orca'], topn=30)
KeyError: "Key 'orca' not present in vocabulary"
fastText解決OOV問題
若需獲取未登錄詞(OOV)的詞向量,可改用fastText
詞嵌入模型(Gensim
也提供該模型)。fastText
模型可通過對單詞的字符n-gram分量向量求和,為未登錄詞生成向量(只要至少有一個字符n-gram在訓練數據中出現過)。
Word2Vec用于命名實體識別
這個“最相似”功能能用來做什么?一種用法是構建相似單詞列表來表示某類概念。例如,我們想知道《白鯨記》中還提到了哪些海洋生物。可以用gensim
的most_similar
函數來構建平均代表“海洋生物”類別的單詞列表。
我們將使用以下步驟:
- 初始化一個表示“海洋生物”類別的小單詞列表
- 計算該單詞列表的平均向量表示
- 用這個平均向量找到最相似的前N個向量(單詞)
- 檢查相似單詞并更新海洋生物列表
- 重復步驟1-4,直到找不到新的海洋生物
PYTHON代碼
# 初始化表示海洋生物的小單詞列表
sea_creatures = ['whale', 'fish', 'creature', 'animal']# 以下代碼將計算列表中單詞的平均向量,
# 并找到與該平均向量最相似的向量/單詞
model.wv.most_similar(positive=sea_creatures, topn=30)
輸出結果
[('great', 0.9997826814651489),('part', 0.9997532963752747),('though', 0.9997507333755493),('full', 0.999735951423645),('small', 0.9997267127037048),('among', 0.9997209906578064),('case', 0.9997204542160034),('like', 0.9997190833091736),('many', 0.9997131824493408),('fishery', 0.9997081756591797),('present', 0.9997068643569946),('body', 0.9997056722640991),('almost', 0.9997050166130066),('found', 0.9997038245201111),('whole', 0.9997023940086365),('water', 0.9996949434280396),('even', 0.9996913075447083),('time', 0.9996898174285889),('two', 0.9996897578239441),('air', 0.9996871948242188),('length', 0.9996850490570068),('vast', 0.9996834397315979),('line', 0.9996828436851501),('made', 0.9996813535690308),('upon', 0.9996812343597412),('large', 0.9996775984764099),('known', 0.9996767640113831),('harpooneer', 0.9996761679649353),('sea', 0.9996750354766846),('shark', 0.9996744990348816)]
PYTHON代碼
# 將shark加入列表
model.wv.most_similar(positive=['whale', 'fish', 'creature', 'animal', 'shark'], topn=30)
輸出結果
[('great', 0.9997999668121338),('though', 0.9997922778129578),('part', 0.999788761138916),('full', 0.999781608581543),('small', 0.9997766017913818),('like', 0.9997683763504028),('among', 0.9997652769088745),('many', 0.9997631311416626),('case', 0.9997614622116089),('even', 0.9997515678405762),('body', 0.9997514486312866),('almost', 0.9997509717941284),('present', 0.9997479319572449),('found', 0.999747633934021),('water', 0.9997465014457703),('made', 0.9997431635856628),('air', 0.9997406601905823),('whole', 0.9997400641441345),('fishery', 0.9997299909591675),('harpooneer', 0.9997295141220093),('time', 0.9997290372848511),('two', 0.9997289776802063),('sea', 0.9997265934944153),('strange', 0.9997244477272034),('large', 0.999722421169281),('place', 0.9997209906578064),('dead', 0.9997198581695557),('leviathan', 0.9997192025184631),('sometimes', 0.9997178316116333),('high', 0.9997177720069885)]
PYTHON代碼
# 將leviathan(海 serpent)加入列表
model.wv.most_similar(positive=['whale', 'fish', 'creature', 'animal', 'shark', 'leviathan'], topn=30)
輸出結果
[('though', 0.9998274445533752),('part', 0.9998168349266052),('full', 0.9998133182525635),('small', 0.9998107552528381),('great', 0.9998067021369934),('like', 0.9998064041137695),('even', 0.9997999668121338),('many', 0.9997966885566711),('body', 0.9997950196266174),('among', 0.999794602394104),('found', 0.9997929334640503),('case', 0.9997885823249817),('almost', 0.9997871518135071),('made', 0.9997868537902832),('air', 0.999786376953125),('water', 0.9997802972793579),('whole', 0.9997780919075012),('present', 0.9997757077217102),('harpooneer', 0.999768853187561),('place', 0.9997684955596924),('much', 0.9997658729553223),('time', 0.999765157699585),('sea', 0.999765157699585),('dead', 0.999764621257782),('strange', 0.9997624158859253),('high', 0.9997615218162537),('two', 0.999760091304779),('sometimes', 0.9997592568397522),('half', 0.9997562170028687),('vast', 0.9997541904449463)]
沒有找到新的海洋生物。看來我們已用Word2Vec找回了海洋生物列表。
局限性
我們的列表中至少遺漏了一種海洋生物——大王烏賊。大王烏賊在《白鯨記》中僅被提及幾次,因此我們的Word2Vec模型可能無法為“squid”(烏賊)訓練出良好的表示。神經網絡只有在數據量充足時才能表現良好。
探索skip-gram算法
skip-gram
算法在捕捉訓練數據中罕見單詞的語義方面有時表現更優。用skip-gram
算法訓練新的Word2Vec模型,看看能否重復上述類別搜索任務找到“squid”(烏賊)這個詞。
PYTHON代碼
# 導入gensim的Word2Vec模塊
from gensim.models import Word2Vec# 使用清洗后的數據訓練Word2Vec模型
model = Word2Vec(sentences=tokens_cleaned, seed=0, workers=1, sg=1)
model.wv.most_similar(positive=['whale', 'fish', 'creature', 'animal', 'shark', 'leviathan'], topn=100) # 仍未找到squid
輸出結果
[('whalemen', 0.9931729435920715),('specie', 0.9919217824935913),('bulk', 0.9917919635772705),('ground', 0.9913252592086792),('skeleton', 0.9905602931976318),('among', 0.9898401498794556),('small', 0.9887762665748596),('full', 0.9885162115097046),('captured', 0.9883950352668762),('found', 0.9883666634559631),('sometimes', 0.9882548451423645),('snow', 0.9880553483963013),('magnitude', 0.9880378842353821),('various', 0.9878063201904297),('hump', 0.9876748919487),('cuvier', 0.9875931739807129),('fisherman', 0.9874721765518188),('general', 0.9873012900352478),('living', 0.9872495532035828),('wholly', 0.9872384667396545),('bone', 0.987160861492157),('mouth', 0.9867696762084961),('natural', 0.9867129921913147),('monster', 0.9865870475769043),('blubber', 0.9865683317184448),('indeed', 0.9864518046379089),('teeth', 0.9862186908721924),('entire', 0.9861844182014465),('latter', 0.9859246015548706),('book', 0.9858523607254028)]
討論練習結果:使用Word2Vec揭示某類別中的項目時,可能會遺漏很少被提及的項目。即使使用在罕見詞上表現更優的Skip-gram
訓練方法,也是如此。因此,有時將此類任務留待處理更大的文本語料庫更合適。在后續課程中,我們將探索大語言模型(LLM)如何在命名實體識別相關任務中表現更優。
實體識別應用
在你的研究中,還可以如何利用這類分析?和小組分享你的想法。
示例:在19世紀的報紙文章上訓練模型,收集語料庫中提及的食物列表(所選主題)。對20世紀的報紙文章做同樣處理,比較流行食物隨時間的變化。
跨作者比較向量表示
回憶一下,Word2Vec模型基于單詞最常見的上下文單詞來學習編碼單詞的語義/表示。例如,在兩位不同作者的書籍(每位作者對應一個模型)上訓練兩個獨立的Word2Vec模型,我們可以比較不同作者用詞的差異。這種方法可以用來研究哪些研究問題或單詞?
一種可能的方法是比較作者如何表現不同性別。由于歷史性別規范,早期(過時的!)書籍中“man”和“woman”的詞向量可能比新書的詞向量距離更遠。
其他詞嵌入模型
盡管Word2Vec是著名模型且至今仍在許多NLP應用中使用,但還有其他一些詞嵌入模型值得探索。GloVe
和fastText
是目前最流行的兩種選擇。
PYTHON代碼
# 預覽可用的其他詞嵌入模型
print(list(api.info()['models'].keys()))
輸出結果
['fasttext-wiki-news-subwords-300', 'conceptnet-numberbatch-17-06-300', 'word2vec-ruscorpora-300', 'word2vec-google-news-300', 'glove-wiki-gigaword-50', 'glove-wiki-gigaword-100', 'glove-wiki-gigaword-200', 'glove-wiki-gigaword-300', 'glove-twitter-25', 'glove-twitter-50', 'glove-twitter-100', 'glove-twitter-200', '__testing_word2vec-matrix-synopsis']
相似性
- 三種算法都在高維空間中生成單詞的向量表示
- 它們可用于解決多種自然語言處理任務
- 它們都是開源的,在研究社區中廣泛使用
差異
- Word2Vec通過給定句子中單詞的周圍單詞來預測其上下文以生成嵌入,而
GloVe
和fastText
通過預測語料庫中單詞與其他單詞共現的概率來生成嵌入 fastText
還包含字符n-gram,使其能為訓練中未見過的單詞生成嵌入,在處理未登錄詞時特別有用- 總體而言,在三種嵌入技術(
GloVe
、fastText
、Word2Vec
)中,fastText
被認為是訓練最快的。這是因為fastText
使用子詞信息,減少了詞匯量并允許模型處理未登錄詞。此外,fastText
在訓練中使用分層softmax,比Word2Vec使用的傳統softmax更快。最后,fastText
可在多線程上訓練,進一步加快訓練過程
關鍵要點
- 作為使用預訓練模型的替代方案,在特定數據集上訓練Word2Vec模型可讓你將Word2Vec用于**命名實體識別(NER)**相關任務。