Keras文字分類實戰(上)
很多時候,人們在網上晒各種東西、抒發情感。個體的情感分析可能沒有多大用處,但對大多數人的情感進行分析,就能得到比較有趣的結果。想象一下,當一個熱點新聞事件出現後,你可以通過分析大多數人的留言感知輿情,瞭解網路平臺中人們的心情。本教程將會教你如何在社交平臺上執行類似的分析操作。
用機器學習從文字中讀取情緒稱為 ofollow,noindex" target="_blank">情感分析(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])
結果如下:
sentenceWow... Loved this place. label1 sourceyelp 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
執行詞語切分,將句子分成一組單詞列表,正如之前在詞彙表中看到的那樣。此外,它還可以刪除標點符號和特殊字元,並可以對每個單詞應用其他預處理。
注意: CountVectorizer()
使用了很多額外的引數,例如新增ngrams,這是因為目標是建立一個簡單的基線模型。詞語切分本身預設為 token_pattern=’(?u)\b\w\w+\b
,這是一個正則表示式模式,表示“一個單詞是由單詞邊界包圍的2個或更多Unicode字元組成”。
下面將使用[邏輯迴歸]()分類模型,這是一種常用的分類模型。從數學上講,實際上是基於輸入特徵向量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 ShapeParam # ================================================================= 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。
文章為簡譯,更為詳細的內容, 請檢視原文 。