作者|Shraddha Anala
編譯|VK
來(lái)源|Towards Data Science
無(wú)論我們是誰(shuí),閱讀、理解、交流并最終產(chǎn)生新的內(nèi)容是我們?cè)诼殬I(yè)生活中都要做的事情。
當(dāng)涉及到從給定的文本體中提取有用的特征時(shí),所涉及的過(guò)程與連續(xù)整數(shù)向量(詞袋)相比是根本不同的。這是因?yàn)榫渥踊蛭谋局械男畔⑹且越Y(jié)構(gòu)化的順序編碼的,單詞的語(yǔ)義位置傳達(dá)了文本的意思。
因此,在保持文本的上下文意義的同時(shí),對(duì)數(shù)據(jù)進(jìn)行適當(dāng)表示的雙重要求促使我學(xué)習(xí)并實(shí)現(xiàn)了兩種不同的NLP模型來(lái)實(shí)現(xiàn)文本分類的任務(wù)。
詞嵌入是文本中單個(gè)單詞的密集表示,考慮到上下文和其他與之相關(guān)的單詞。
與簡(jiǎn)單的詞袋模型相比,該實(shí)值向量可以更有效地選擇維數(shù),更有效地捕捉詞與詞之間的語(yǔ)義關(guān)系。
簡(jiǎn)單地說(shuō),具有相似含義或經(jīng)常出現(xiàn)在相似上下文中的詞,將具有相似的向量表示,這取決于這些詞在其含義中的“接近”或“相距”有多遠(yuǎn)。
在本文中,我將探討兩個(gè)詞的嵌入-
- 訓(xùn)練我們自己的嵌入
- 預(yù)訓(xùn)練的GloVe 詞嵌入
數(shù)據(jù)集
對(duì)于這個(gè)案例研究,我們將使用Kaggle的Stack Overflow 數(shù)據(jù)集(https://www./imoore/60k-stack-overflow-questions-with-quality-rate)。這個(gè)數(shù)據(jù)集包含了6萬(wàn)個(gè)用戶在網(wǎng)站上提出的問(wèn)題,主要任務(wù)是將問(wèn)題分為3類。
現(xiàn)在讓我們看看這個(gè)多分類NLP項(xiàng)目的實(shí)際模型本身。
但是,在開(kāi)始之前,請(qǐng)確保你已經(jīng)安裝了這些包/庫(kù)。
pip install gensim # 用于NLP預(yù)處理任務(wù)
pip install keras # 嵌入層
1. 訓(xùn)練詞嵌入
如果你希望跳過(guò)解釋,請(qǐng)?jiān)L問(wèn)第一個(gè)模型的完整代碼:https://github.com/shraddha-an/nlp/blob/main/word_embedding_classification.ipynb
1) 數(shù)據(jù)預(yù)處理
在第一個(gè)模型中,我們將訓(xùn)練一個(gè)神經(jīng)網(wǎng)絡(luò)來(lái)從我們的文本語(yǔ)料庫(kù)中學(xué)習(xí)嵌入。具體地說(shuō),我們將使用Keras庫(kù)為神經(jīng)網(wǎng)絡(luò)的嵌入層提供詞標(biāo)識(shí)及其索引。
在訓(xùn)練我們的網(wǎng)絡(luò)之前,必須確定一些關(guān)鍵參數(shù)。這些包括詞匯的大小或語(yǔ)料庫(kù)中唯一單詞的數(shù)量以及嵌入向量的維數(shù)。
以下鏈接是用于訓(xùn)練和測(cè)試的數(shù)據(jù)集?,F(xiàn)在我們將導(dǎo)入它們,只保留問(wèn)題和質(zhì)量列以供分析:https://www./imoore/60k-stack-overflow-questions-with-quality-rate
我還更改了列名并定義了一個(gè)函數(shù)text_clean來(lái)清理問(wèn)題。
# 導(dǎo)入庫(kù)
# 數(shù)據(jù)操作/處理
import pandas as pd, numpy as np
# 可視化
import seaborn as sb, matplotlib.pyplot as plt
# NLP
import re
from nltk.corpus import stopwords
from gensim.utils import simple_preprocess
stop_words = set(stopwords.words('english'))
# 導(dǎo)入數(shù)據(jù)集
dataset = pd.read_csv('train.csv')[['Body', 'Y']].rename(columns = {'Body': 'question', 'Y': 'category'})
ds = pd.read_csv('valid.csv')[['Body', 'Y']].rename(columns = {'Body': 'question', 'Y': 'category'})
# 清理符號(hào)和HTML標(biāo)簽
symbols = re.compile(pattern = '[/<>(){}\[\]\|@,;]')
tags = ['href', 'http', 'https', 'www']
def text_clean(s: str) -> str:
s = symbols.sub(' ', s)
for i in tags:
s = s.replace(i, ' ')
return ' '.join(word for word in simple_preprocess(s) if not word in stop_words)
dataset.iloc[:, 0] = dataset.iloc[:, 0].apply(text_clean)
ds.iloc[:, 0] = ds.iloc[:, 0].apply(text_clean)
# 訓(xùn)練和測(cè)試集
X_train, y_train = dataset.iloc[:, 0].values, dataset.iloc[:, 1].values.reshape(-1, 1)
X_test, y_test = ds.iloc[:, 0].values, ds.iloc[:, 1].values.reshape(-1, 1)
# one-hot編碼
from sklearn.preprocessing import OneHotEncoder as ohe
from sklearn.compose import ColumnTransformer
ct = ColumnTransformer(transformers = [('one_hot_encoder', ohe(categories = 'auto'), [0])],
remainder = 'passthrough')
y_train = ct.fit_transform(y_train)
y_test = ct.transform(y_test)
# 設(shè)置參數(shù)
vocab_size = 2000
sequence_length = 100
如果你瀏覽原始數(shù)據(jù)集,你會(huì)發(fā)現(xiàn)HTML標(biāo)記中包含的問(wèn)題,例如, …question 。此外,還有一些詞,如href,https等,在整個(gè)文本中都有,所以我要確保從文本中刪除這兩組不需要的字符。
Gensim的simple_preprocess方法返回一個(gè)小寫(xiě)的標(biāo)記列表,去掉重音符號(hào)。
在這里使用apply方法將通過(guò)預(yù)處理函數(shù)迭代運(yùn)行每一行,并在繼續(xù)下一行之前返回輸出。對(duì)訓(xùn)練和測(cè)試數(shù)據(jù)集應(yīng)用文本預(yù)處理功能。
因?yàn)樵谝蜃兞肯蛄恐杏?個(gè)類別,我們將應(yīng)用one-hot編碼并初始化一些參數(shù)以備以后使用。
2) 標(biāo)識(shí)化
接下來(lái),我們將使用Keras Tokenizer類將單詞組成的問(wèn)題轉(zhuǎn)換成一個(gè)數(shù)組,用它們的索引表示單詞。
因此,我們首先必須使用fit_on_texts方法,從數(shù)據(jù)集中出現(xiàn)的單詞構(gòu)建索引詞匯表。
在建立詞匯表之后,我們使用text_to_sequences方法將句子轉(zhuǎn)換成表示單詞的數(shù)字列表。
pad_sequences函數(shù)確保所有觀察值的長(zhǎng)度相同,可以設(shè)置為任意數(shù)字或數(shù)據(jù)集中最長(zhǎng)問(wèn)題的長(zhǎng)度。
我們先前初始化的vocab_size參數(shù)只是我們?cè)~匯表的大?。ㄓ糜趯W(xué)習(xí)和索引)。
# Keras的標(biāo)識(shí)器
from keras.preprocessing.text import Tokenizer
tk = Tokenizer(num_words = vocab_size)
tk.fit_on_texts(X_train)
X_train = tk.texts_to_sequences(X_train)
X_test = tk.texts_to_sequences(X_test)
# 用0填充所有
from keras.preprocessing.sequence import pad_sequences
X_train_seq = pad_sequences(X_train, maxlen = sequence_length, padding = 'post')
X_test_seq = pad_sequences(X_test, maxlen = sequence_length, padding = 'post')
3) 訓(xùn)練嵌入層
最后,在這一部分中,我們將構(gòu)建和訓(xùn)練我們的模型,它由兩個(gè)主要層組成,一個(gè)嵌入層將學(xué)習(xí)上面準(zhǔn)備的訓(xùn)練文檔,以及一個(gè)密集的輸出層來(lái)實(shí)現(xiàn)分類任務(wù)。
嵌入層將學(xué)習(xí)單詞的表示,同時(shí)訓(xùn)練神經(jīng)網(wǎng)絡(luò),需要大量的文本數(shù)據(jù)來(lái)提供準(zhǔn)確的預(yù)測(cè)。在我們的例子中,45000個(gè)訓(xùn)練觀察值足以有效地學(xué)習(xí)語(yǔ)料庫(kù)并對(duì)問(wèn)題的質(zhì)量進(jìn)行分類。我們將從指標(biāo)中看到。
# 訓(xùn)練嵌入層和神經(jīng)網(wǎng)絡(luò)
from keras.models import Sequential
from keras.layers import Embedding, Dense, Flatten
model = Sequential()
model.add(Embedding(input_dim = vocab_size, output_dim = 5, input_length = sequence_length))
model.add(Flatten())
model.add(Dense(units = 3, activation = 'softmax'))
model.compile(loss = 'categorical_crossentropy',
optimizer = 'rmsprop',
metrics = ['accuracy'])
model.summary()
history = model.fit(X_train_seq, y_train, epochs = 20, batch_size = 512, verbose = 1)
# 完成訓(xùn)練后保存模型
#model.save("model.h5")
4) 評(píng)估和度量圖
剩下的就是評(píng)估我們的模型的性能,并繪制圖來(lái)查看模型的準(zhǔn)確性和損失指標(biāo)是如何隨時(shí)間變化的。
我們模型的性能指標(biāo)顯示在下面的屏幕截圖中。
代碼與下面顯示的代碼相同。
# 在測(cè)試集上評(píng)估模型的性能
loss, accuracy = model.evaluate(X_test_seq, y_test, verbose = 1)
print("\nAccuracy: {}\nLoss: {}".format(accuracy, loss))
# 畫(huà)出準(zhǔn)確度和損失
sb.set_style('darkgrid')
# 1) 準(zhǔn)確度
plt.plot(history.history['accuracy'], label = 'training', color = '#003399')
plt.legend(shadow = True, loc = 'lower right')
plt.title('Accuracy Plot over Epochs')
plt.show()
# 2) 損失
plt.plot(history.history['loss'], label = 'training loss', color = '#FF0033')
plt.legend(shadow = True, loc = 'upper right')
plt.title('Loss Plot over Epochs')
plt.show()
以下是訓(xùn)練中準(zhǔn)確度的提高
20個(gè)epoch的損失圖
2.預(yù)訓(xùn)練的GloVe詞嵌入
如果你只想運(yùn)行模型,這里有完整的代碼:https://github.com/shraddha-an/nlp/blob/main/pretrained_glove_classification.ipynb
代替訓(xùn)練你自己的嵌入,另一個(gè)選擇是使用預(yù)訓(xùn)練好的詞嵌入,比如GloVe或Word2Vec。在這一部分中,我們將使用在Wikipedia+gigaword5上訓(xùn)練的GloVe詞嵌入;從這里下載:https://nlp./projects/glove/
i) 選擇一個(gè)預(yù)訓(xùn)練的詞嵌入,如果
你的數(shù)據(jù)集是由更“通用”的語(yǔ)言組成的,一般來(lái)說(shuō)你沒(méi)有那么大的數(shù)據(jù)集。
由于這些嵌入已經(jīng)根據(jù)來(lái)自不同來(lái)源的大量單詞進(jìn)行了訓(xùn)練,如果你的數(shù)據(jù)也是通用的,那么預(yù)訓(xùn)練的模型可能會(huì)做得很好。
此外,通過(guò)預(yù)訓(xùn)練的嵌入,你可以節(jié)省時(shí)間和計(jì)算資源。
ii)選擇訓(xùn)練你自己的嵌入,如果
你的數(shù)據(jù)(和項(xiàng)目)是基于一個(gè)利基行業(yè),如醫(yī)藥、金融或任何其他非通用和高度特定的領(lǐng)域。
在這種情況下,一般的詞嵌入表示法可能不適合你,并且一些單詞可能不在詞匯表中。
需要大量的領(lǐng)域數(shù)據(jù)來(lái)確保所學(xué)的詞嵌入能夠正確地表示不同的單詞以及它們之間的語(yǔ)義關(guān)系
此外,它需要大量的計(jì)算資源來(lái)瀏覽你的語(yǔ)料庫(kù)和建立詞嵌入。
最終,是根據(jù)已有的數(shù)據(jù)訓(xùn)練你自己的嵌入,還是使用預(yù)訓(xùn)練好的嵌入,將取決于你的項(xiàng)目。
顯然,你仍然可以試驗(yàn)這兩種模型,并選擇一種精度更高的模型,但上面的教程只是一個(gè)簡(jiǎn)化的教程,可以幫助你做出決策。
過(guò)程
前面的部分已經(jīng)采取了所需的大部分步驟,只需進(jìn)行一些調(diào)整。
我們只需要構(gòu)建一個(gè)單詞及其向量的嵌入矩陣,然后用它來(lái)設(shè)置嵌入層的權(quán)重。
所以,保持預(yù)處理、標(biāo)識(shí)化和填充步驟不變。
一旦我們導(dǎo)入了原始數(shù)據(jù)集并運(yùn)行了前面的文本清理步驟,我們將運(yùn)行下面的代碼來(lái)構(gòu)建嵌入矩陣。
以下決定要嵌入多少個(gè)維度(50、100、200),并將其名稱包含在下面的路徑變量中。
# # 導(dǎo)入嵌入
path = 'Full path to your glove file (with the dimensions)'
embeddings = dict()
with open(path, 'r', encoding = 'utf-8') as f:
for line in f:
# 文件中的每一行都是一個(gè)單詞外加50個(gè)數(shù)(表示這個(gè)單詞的向量)
values = line.split()
# 每一行的第一個(gè)元素是一個(gè)單詞,其余的50個(gè)是它的向量
embeddings[values[0]] = np.array(values[1:], 'float32')
# 設(shè)置一些參數(shù)
vocab_size = 2100
glove_dim = 50
sequence_length = 200
# 從語(yǔ)料庫(kù)中的單詞構(gòu)建嵌入矩陣
embedding_matrix = np.zeros((vocab_size, glove_dim))
for word, index in word_index.items():
if index < vocab_size:
try:
# 如果給定單詞的嵌入存在,檢索它并將其映射到單詞。
embedding_matrix[index] = embeddings[word]
except:
pass
構(gòu)建和訓(xùn)練嵌入層和神經(jīng)網(wǎng)絡(luò)的代碼應(yīng)該稍作修改,以允許將嵌入矩陣用作權(quán)重。
# 神經(jīng)網(wǎng)絡(luò)
from keras.models import Sequential
from keras.layers import Embedding, Dense, Flatten
model = Sequential()
model.add(Embedding(input_dim = vocab_size,
output_dim = glove_dim,
input_length = sequence))
model.add(Flatten())
model.add(Dense(units = 3, activation = 'softmax'))
model.compile(optimizer = 'adam', metrics = ['accuracy'], loss = 'categorical_crossentropy')
# 加載我們預(yù)訓(xùn)練好的嵌入矩陣到嵌入層
model.layers[0].set_weights([embedding_matrix])
model.layers[0].trainable = False # 訓(xùn)練時(shí)權(quán)重不會(huì)被更新
# 訓(xùn)練模型
history = model.fit(X_train_seq, y_train, epochs = 20, batch_size = 512, verbose = 1)
下面是我們預(yù)訓(xùn)練的模型在測(cè)試集中的性能指標(biāo)。
結(jié)論
從兩個(gè)模型的性能指標(biāo)來(lái)看,訓(xùn)練嵌入層似乎更適合這個(gè)數(shù)據(jù)集。
一些原因可能是
1) 關(guān)于堆棧溢出的大多數(shù)問(wèn)題都與IT和編程有關(guān),也就是說(shuō),這是一個(gè)特定領(lǐng)域的場(chǎng)景。
2) 45000個(gè)樣本的大型訓(xùn)練數(shù)據(jù)集為我們的嵌入層提供了一個(gè)很好的學(xué)習(xí)場(chǎng)景。
希望本教程對(duì)你有幫助,謝謝你的閱讀,下一篇文章再見(jiàn)。
原文鏈接:https:///a-guide-to-word-embeddings-8a23817ab60f
|