很多時候,人們在網上曬各種東西、抒發情感。個體的情感分析可能沒有多大用處,但對大多數人的情感進行分析,就能得到比較有趣的結果。想象一下,當一個熱點新聞事件出現後,你可以通過分析大多數人的留言感覺輿情,了解網絡平台中人們的心情。本教程将會教你如何在社交平台上執行類似的分析操作。
用機器學習從文本中讀取情緒稱為
情感分析(sentiment analysis) ,它是文本分類中突出的用例之一,屬于 自然語言處理(NLP) 非常活躍的研究領域。其它應用比如,檢測垃圾郵件、自動标記客戶查詢以及将文本分類為已定義的主題等。那麼,如何做到這一點呢?選擇資料集
在開始之前,首先看看手上有什麼資料。本文資料集來自UCI機器學習庫中下載下傳的
Sentiment Labeled Sentences Data Set資料集。此資料集包括來自IMDb、Amazon和Yelp的标記評論。其中,對于負面情緒,每個評論的得分為0,對于積極的情緒,評分為1。将檔案夾解壓縮到一個data檔案夾中,然後使用
Pandas加載資料:
import pandas as pd
filepath_dict = {'yelp': 'data/sentiment_analysis/yelp_labelled.txt',
'amazon': 'data/sentiment_analysis/amazon_cells_labelled.txt',
'imdb': 'data/sentiment_analysis/imdb_labelled.txt'}
df_list = []
for source, filepath in filepath_dict.items():
df = pd.read_csv(filepath, names=['sentence', 'label'], sep='\t')
df['source'] = source # Add another column filled with the source name
df_list.append(df)
df = pd.concat(df_list)
print(df.iloc[0])
結果如下:
sentence Wow... Loved this place.
label 1
source yelp
Name: 0, dtype: object
使用此資料集,可以訓練模型來預測句子的情緒,下面可以考慮如何預測資料。
一種常見方法是計算每個句子中每個單詞的頻率,并将此計數與資料集中的整個單詞組相關聯。首先從建立詞彙開始,收集好的詞彙庫在NLP中也被稱為語料庫。
在這種情況下,詞彙表是在文本中出現的單詞清單,每個單詞都有自己的索引。然後為每個句子建立向量,并計算詞彙表中的每個詞的頻次,得到的向量将具有詞彙表的長度和詞彙表中每個單詞的次數,該向量也被稱作特征向量。
在特征向量中,每個次元可以是數字或分類特征,例如建築物的高度、股票的價格,或者是詞彙表中單詞的計數。這些特征向量是資料科學和機器學習的關鍵部分,因為訓練的模型是根據特征向量來學習得到。
舉例來說明這一點,假設有以下兩句話:
sentences = ['John likes ice cream', 'John hates chocolate.']
接下來,可以使用scikit-learn庫提供的CurrVoCurrisher來對句子進行矢量化,建立好詞彙表後,可以使用該詞彙來建立單詞頻次的特征向量:
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer(min_df=0, lowercase=False)
vectorizer.fit(sentences)
vectorizer.vocabulary_
輸出:
{'John': 0, 'chocolate': 1, 'cream': 2, 'hates': 3, 'ice': 4, 'likes': 5}
這個詞彙表也可以作為每個單詞的索引。上述句子中是由五個單詞組成,每個單詞代表詞彙表中的一個單詞。當使用該詞彙表對兩個句子進行CountVectorizer變換後,每個句子對應一個向量,表示句子中每個單詞的計數:
vectorizer.transform(sentences).toarray()
輸出:
array([[1, 0, 1, 0, 1, 1],
[1, 1, 0, 1, 0, 0]])
現在,可以根據之前的詞彙檢視每個句子的結果特征向量。例如,如果檢視第一列,可以看到兩個向量都有是1,這意味着兩個句子都有一次出現John,并在詞彙表中排在第一位。
以上被認為是一個
詞袋(BOW))模型,這是NLP中用于建立文本向量的常用方法,每個文檔都表示為一個向量。現在就可以将這些向量用作機器學習模型的特征向量。下面進入下一部分内容。
定義基線模型(baseline model)
使用機器學習方法時,一個重要的步驟就是定義基線模型。基線模型一般是一個簡單的模型,然後進一步開發更進階模型。在這種情況下,将使用基線模型與更進階模型的性能進行比較,這也是本教程的主要内容。
首先,要将
資料拆分為訓練集和測試集,這樣就可以評估訓練好模型的準确性、泛化能力和過拟合情況。過拟合是指模型在訓練資料上訓練得太好,而在測試集上表現很差。有關過拟合(overfitting)處理的方法可以看
這篇文章。
首先從資料集中提取Yelp資料集。之後得到句子和标簽。
.values
返還NumPy array類型,而不是pandas類型對象,這是由于在這種情況下,array類型的資料更易于使用:
from sklearn.model_selection import train_test_split
df_yelp = df[df['source'] == 'yelp']
sentences = df_yelp['sentence'].values
y = df_yelp['label'].values
sentences_train, sentences_test, y_train, y_test = train_test_split(
sentences, y, test_size=0.25, random_state=1000)
之後,将再次使用BOW模型來對句子進行向量化。由于在訓練期間沒有可用的測試資料,是以僅使用訓練資料建立詞彙表。使用此詞彙表為訓練和測試集的每個句子建立特征向量:
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer()
vectorizer.fit(sentences_train)
X_train = vectorizer.transform(sentences_train)
X_test = vectorizer.transform(sentences_test)
X_trai
n
輸出:
<750x1714 sparse matrix of type '<class 'numpy.int64'>'
with 7368 stored elements in Compressed Sparse Row format>
可以看到生成的特征向量有750個樣本,這些樣本對資料分割後獲得的訓練樣本數。每個樣本有1714個次元,這也是詞彙量的大小。此外,可以看到得到的是一個
稀疏矩陣CountVectorizer
執行詞語切分,将句子分成一組單詞清單,正如之前在詞彙表中看到的那樣。此外,它還可以删除标點符号和特殊字元,并可以對每個單詞應用其他預處理。
注意:使用了很多額外的參數,例如添加ngrams,這是因為目标是建立一個簡單的基線模型。詞語切分本身預設為
CountVectorizer()
,這是一個正規表達式模式,表示“一個單詞是由單詞邊界包圍的2個或更多Unicode字元組成”。
token_pattern=’(?u)\b\w\w+\b
下面将使用[邏輯回歸]()分類模型,這是一種常用的分類模型。從數學上講,實際上是基于輸入特征向量0到1之間的回歸。通過指定門檻值(預設為0.5),将回歸模型用于分類。可以使用scikit-learn庫中提供的LogisticRegression分類器完成該操作:
from sklearn.linear_model import LogisticRegression
classifier = LogisticRegression()
classifier.fit(X_train, y_train)
score = classifier.score(X_test, y_test)
print("Accuracy:", score)
輸出:
Accuracy: 0.796
可以看到,yelp資料的準确度:0.7960,效果不錯。将此方法應用于其它資料集上:
for source in df['source'].unique():
df_source = df[df['source'] == source]
sentences = df_source['sentence'].values
y = df_source['label'].values
sentences_train, sentences_test, y_train, y_test = train_test_split(
sentences, y, test_size=0.25, random_state=1000)
vectorizer = CountVectorizer()
vectorizer.fit(sentences_train)
X_train = vectorizer.transform(sentences_train)
X_test = vectorizer.transform(sentences_test)
classifier = LogisticRegression()
classifier.fit(X_train, y_train)
score = classifier.score(X_test, y_test)
print('Accuracy for {} data: {:.4f}'.format(source, score))
輸出結果
Accuracy for yelp data: 0.7960
Accuracy for amazon data: 0.7960
Accuracy for imdb data: 0.7487
可以看到,這個相當簡單的模型表現不錯。之後看看我們是否能夠超越這個基線模型。接下來,我們将了解神經網絡相關内容以及如何将它們應用于文本分類。
建構第一個Keras模型
人工智能和深度學習近年來非常火熱,這裡假設你已經熟悉神經網絡相關的基本知識,如果你不了解的話,可以檢視部落客的
。此外,随着深度學習方法的興起,相應的開源工具箱也有很多,比如Tensorflow、Keras、Theano或Caffe等,本文使用keras建構相應的神經網絡模型。有關keras的安裝和配置可以查閱相關的教程安裝,這裡不做過多的介紹。下面建構你的第一個Keras模型。
在構模組化型之前,需要知道特征向量的輸入次元,這僅在輸入層需要設定,之後按順序逐個添加圖層,如下所示:
>>> from keras.models import Sequential
>>> from keras import layers
>>> input_dim = X_train.shape[1] # Number of features
>>> model = Sequential()
>>> model.add(layers.Dense(10, input_dim=input_dim, activation='relu'))
>>> model.add(layers.Dense(1, activation='sigmoid'))
Using TensorFlow backend.
在開始模型訓練之前,需要配置學習過程,通過
.compile()
完成。此方法指定具體的優化方法和損失函數。
此外,可以添加用于評估的名額。本文使用二進制交叉熵作為損失函數和Adam優化器。Keras還具有
.summary()
函數,可以概述模型和用于訓練的參數數量:
>>> model.compile(loss='binary_crossentropy',
... optimizer='adam',
... metrics=['accuracy'])
>>> model.summary()
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_1 (Dense) (None, 10) 17150
_________________________________________________________________
dense_2 (Dense) (None, 1) 11
=================================================================
Total params: 17,161
Trainable params: 17,161
Non-trainable params: 0
_______________________________
可以看到,每個特征向量有1714個次元、5個節點。之後需要為每個特征次元和每個節點考慮
1714 * 5 = 8570
個參數的權重(weight),然後為每個節點增加5個額外偏差(bias),總共得到8575個參數。在最後一個節點中,有另外5個權重和一個偏差,總共得到6個參數。現在開始使用
.fit()
函數進行訓練。
由于神經網絡中的訓練是一個疊代過程,是以需要指定模型訓練的疊代次數。完成一次疊代通常稱為
epochs
。我們運作100個epoch,以便能夠看到每個epoch後訓練損失和準确性如何變化。
另一個需要設定的參數是
batchsize
,它負責設定在一個epoch中使用多少樣本。由于本文資料集比較小,可以将該數值設定比較小:
>>> history = model.fit(X_train, y_train,
... epochs=100,
... verbose=False,
... validation_data=(X_test, y_test)
... batch_size=10)
現在可以使用
.evaluate()
函數來評估模型的準确性,可以在訓練資料和測試資料執行此操作。一般而言,訓練資料的準确度高于測試資料。否則,出現過拟合的可能性就越大。
請注意,如果重新運作
.fit()
函數,将從之前訓練計算出的權重開始。確定在再次開始訓練模型之前再次編譯模型。下面評估模型的準确度:
>>> loss, accuracy = model.evaluate(X_train, y_train, verbose=False)
>>> print("Training Accuracy: {:.4f}".format(accuracy))
>>> loss, accuracy = model.evaluate(X_test, y_test, verbose=False)
>>> print("Testing Accuracy: {:.4f}".format(accuracy))
Training Accuracy: 1.0000
Testing Accuracy: 0.7960
從結果中可以看到,模型已經過拟合了,因為在訓練集上達到100%準确度,而測試集上隻有79.6%。但該測試集的準确性已經超過了之前使用的基線模型——邏輯回歸,這也算是一種進步。
為了實驗更加友善,可以使用小的輔助函數,根據曆史回調可視化訓練和測試資料的損失和準确性。在這種情況下,輔助函數使用matplotlib繪圖庫繪制模型的準确性:
import matplotlib.pyplot as plt
plt.style.use('ggplot')
def plot_history(history):
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
x = range(1, len(acc) + 1)
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(x, acc, 'b', label='Training acc')
plt.plot(x, val_acc, 'r', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(x, loss, 'b', label='Training loss')
plt.plot(x, val_loss, 'r', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
要使用此功能,隻需使用
plot_history()
即可:
>>> plot_history(history)

基線模型的準确率和loss損失
從上可以看到,模型已經訓練了很長時間,在訓練集上達到了100%的準确性。模型何時開始過拟合的一個判斷方法是驗證資料集上的損失曲線再次開始上升(20-40 epoch)。這個時刻也是阻止模型的一個好時機,可以提前停止訓練(early stop)。
注意:在訓練神經網絡時,應該使用單獨的測試和驗證集。通常會采用在驗證集上具有最高精度的模型,然後使用測試集測試該模型,這樣可以確定不會過度使用模型。使用驗證集來選擇最佳模型是資料洩漏的一種形式,以便從數百次訓練中選擇産生最佳測試分數時的模型。當在該模型中使用訓練資料集之外的資訊時,會發生資料洩漏。
在這種情況下,測試和驗證集是相同的,因為本文采用的樣本量較小。正如之前所述,神經網絡一般在大量樣本資料集上表現最佳。在下一部分中,可以看到将單詞表示為向量的不同方式。
作者資訊
Nikolai Janakiev ,機器學習和資料科學
本文由阿裡雲雲栖社群組織翻譯。
文章原标題《Practical Text Classification With Python and Keras》,譯者:海棠,審校:Uncle_LLD。
文章為簡譯,更為詳細的内容,
請檢視原文