文本情感分析(一):基於詞袋模型(VSM、LSA、n-gram)的文本表示

如今天然語言處理用深度學習作的比較多,我還沒試過用傳統的監督學習方法作分類器,好比SVM、Xgboost、隨機森林,來訓練模型。所以,用Kaggle上經典的電影評論情感分析題,來學習如何用傳統機器學習方法解決分類問題。html

經過這個情感分析的題目,我會整理作特徵工程、參數調優和模型融合的方法,這一系列會有四篇文章。這篇文章整理文本特徵工程的內容。app

文本的特徵工程主要包括數據清洗、特徵構造、降維和特徵選擇等。dom

首先是數據清洗,好比去停用詞、去非字母漢字的特殊字符、大寫轉小寫、去掉html標籤等。機器學習

而後是特徵構建,能夠基於詞袋模型構造文本特徵,好比向量空間模型的詞頻矩陣、Tf-Idf矩陣,又好比LSA和LDA,也能夠用word2vec、glove等文本分佈式表示方法,來構造文本特徵。此外還能夠用n-gram構造文本特徵。分佈式

接下來能夠選擇是否降維,能夠用PCA或SVD等方法對文本特徵矩陣進行降維。性能

最後選擇效果比較突出的1個或幾個特徵來訓練模型。學習

 

1、基於向量空間模型的文本特徵表示測試

向量空間模型(Vector Space Model,VSM)也就是單詞向量空間模型,區別於LSA、PLSA、LDA這些話題向量空間模型,可是單詞向量空間模型和話題向量空間模型都屬於詞袋模型,又和word2vec等文本分佈式表示方法相區別。spa

向量空間模型的基本想法是:給定一個文本,用一個向量表示該文本的語義,向量的每一維對應一個單詞,其數值是該單詞在該文本中出現的頻數或Tf-Idf。那麼每一個文本就是一個向量,特徵數量爲全部文本中的單詞總數,經過計算向量之間的餘弦類似度能夠獲得文本的類似度。rest

而文本集合中的全部文本的向量就會構成一個單詞-文本矩陣,元素爲單詞的頻數或Tf-Idf。

在咱們這個Kaggle案例中,單詞-文本矩陣的行數爲樣本的數量,列數爲單詞的數量,訓練集中樣本有25000條,選取最高頻的5000個單詞,故矩陣X是(25000,5000)的矩陣。咱們以詞頻和Tf-Idf做爲文本特徵,計算出兩個單詞-文本矩陣,而後分別訓練隨機森林二分類器。

首先導入所須要的庫。

import os,re import numpy as np import pandas as pd from bs4 import BeautifulSoup from sklearn.feature_extraction.text import CountVectorizer from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.ensemble import RandomForestClassifier from sklearn import metrics import nltk from nltk.corpus import stopwords

第一步:讀取訓練數據,一共25000條評論數據。

"""第一步:用pandas讀取訓練數據""" datafile = os.path.join('..', 'data', 'labeledTrainData.tsv') # escapechar='\\'用來去掉轉義字符'\'
df = pd.read_csv(datafile, sep='\t', escapechar='\\') print('Number of reviews: {}'.format(len(df))) df.head()

 

第二步:對影評數據作預處理。

大概有如下環節:

  1. 去掉html標籤
  2. 移除標點
  3. 切分紅詞/token
  4. 去掉停用詞
  5. 重組爲新的句子
"""第二步:數據預處理""" eng_stopwords = stopwords.words('english') # 去掉html標籤 # 去掉非英文字符 # 去停用詞 # 從新組合爲句子
def clean_text(text): text = BeautifulSoup(text, 'html.parser').get_text() text = re.sub(r'[^a-zA-Z]', ' ', text) words = text.lower().split() words = [w for w in words if w not in eng_stopwords] return ' '.join(words) df['clean_review'] = df.review.apply(clean_text) df.head()

 

第三步:用向量空間模型抽取文本特徵

分別計算單詞的詞頻和Tf-Idf,做爲文本特徵,計算單詞-文本矩陣。

"""第三步:用VSM抽取文本特徵"""

# 統計詞頻,做爲文本特徵,計算文本-單詞矩陣
vectorizer_freq = CountVectorizer(max_features = 5000) train_vsm_freq = vectorizer_freq.fit_transform(df.clean_review).toarray() print("以詞頻爲元素的文本-單詞矩陣的維度是:\n\n",train_vsm_freq.shape) # 計算tfidf,做爲另外一種文本特徵,計算文本-單詞矩陣
vectorizer_tfidf=TfidfVectorizer(max_features=5000) train_vsm_tfidf=vectorizer_tfidf.fit_transform(df.clean_review).toarray() print("\n用單詞向量空間模型成功抽取文本特徵!\n")
以詞頻爲元素的文本-單詞矩陣的維度是: (25000, 5000) 用單詞向量空間模型成功抽取文本特徵!

 第四步:訓練分類器

決策樹爲200棵。

"""第四步:用隨機森林訓練二分類器"""

"""首先使用以詞頻爲元素的文本-單詞矩陣訓練一個分類器"""
# 使用包外估計做爲模型泛化偏差的估計,即oob_score=True,那麼無須再作交叉驗證
forest = RandomForestClassifier(oob_score=True,n_estimators = 200) forest = forest.fit(train_vsm_freq, df.sentiment)

第五步:評估分類器的性能

這裏沒有進行交叉驗證了,沒有劃分驗證集來計算準確率、召回率和AUC,直接用訓練集來計算這些指標。由於隨機森林經過自助採樣,能夠獲得大約36.8%的驗證集,用於評估模型的泛化偏差,稱這爲包外估計,所以咱們主要觀察包外估計這個指標,來評估分類器的性能。

從評估結果來看,包外估計爲0.84232。

"""第五步:評估模型"""

def model_eval(train_data): print("一、混淆矩陣爲:\n") print(metrics.confusion_matrix(df.sentiment, forest.predict(train_data))) print("\n二、準確率、召回率和F1值爲:\n") print(metrics.classification_report(df.sentiment,forest.predict(train_data))) print("\n三、包外估計爲:\n") print(forest.oob_score_) print("\n四、AUC Score爲:\n") y_predprob = forest.predict_proba(train_data)[:,1] print(metrics.roc_auc_score(df.sentiment, y_predprob)) print("\n====================評估以詞頻爲特徵訓練的模型==================\n") model_eval(train_vsm_freq)

第六步:以Tf-Idf做爲文本特徵,訓練分類器

結果包外估計爲0.84168,比詞頻矩陣的要低一點,問題不大。

"""再使用以tfidf爲元素的文本-單詞矩陣訓練一個分類器""" forest = RandomForestClassifier(oob_score=True,n_estimators = 200) forest = forest.fit(train_vsm_tfidf, df.sentiment) print("\n====================評估以tfidf爲特徵訓練的模型==================\n") model_eval(train_vsm_tfidf)

2、基於潛在語義分析的文本特徵表示

潛在語義分析(Laten Semantic Analysis,LSA)是一種文本話題分析的方法,特色是能夠經過矩陣分解(SVD或者NMF),來發現文本與單詞之間的基於話題的語義關係。

LSA和VSM有什麼關係呢?

一、VSM的優勢是單詞向量稀疏,計算效率高,可是因爲天然語言中一詞多義和多詞一義現象的存在,基於單詞向量的文本表示未必能準確表達兩個文本的類似度。而LSA是用文本的話題來表示文本,文本的話題類似則文本的語義也類似,這樣能夠解決同義詞和多義詞的問題。

二、VSM獲得的是單詞-文本矩陣,而LSA獲得的是話題-文本矩陣。LSA的話題-文本矩陣就是經過對VSM的矩陣進行矩陣分解獲得的,矩陣分解的方法包括SVD奇異值分解和NMF非負矩陣分解。

以下圖,NMF非負矩陣分解後獲得話題-文本矩陣Y,話題爲k個,樣本爲n個。

在這個情感分析案例中,咱們把話題設爲300個,把單詞-文本矩陣降維成(25000, 300)的話題-文本矩陣,採用的是NMF非負矩陣分解的方法。

要注意的一點是,若是單詞-文本矩陣的維度是(單詞數,文本數)這種格式,也就是咱們在代碼中用到的格式,那麼在用 sklearn.decomposition.NMF 這個包計算話題-文本矩陣時,NMF().fit_transform() 所得的是話題-文本矩陣,NMF().components_獲得的是單詞-話題矩陣。

而若是單詞-文本矩陣的格式和上圖的格式同樣(轉置了),那麼NMF().components_獲得的是話題-文本矩陣。

下面首先用NMF計算LSA的話題-文本矩陣,取以詞頻爲元素的單詞-文本矩陣來計算。對高維矩陣進行矩陣分解的時間複雜度很是高,因此我用一下LSA就行了,LDA就不敢再去嘗試,由於LDA的時間複雜度更高,效果可能不必定好。

第一步:用NMF計算LSA的話題-文本矩陣

"""用NMF計算LSA的話題-文本矩陣"""

from sklearn.decomposition import NMF # 對以詞頻爲特徵的單詞-文本矩陣進行NMF分解
nmf = NMF(n_components=300) # 獲得話題-文本矩陣,注意若是輸入進行了轉置,那麼獲得的是單詞-話題矩陣
train_lsa_freq = nmf.fit_transform(train_vsm_freq) print("話題-文本矩陣的維度是:\n\n",train_lsa_freq.shape)
話題-文本矩陣的維度是: (25000, 300)

第二步:使用LSA的話題-文本矩陣訓練隨機森林分類器

包外估計爲0.82236,比基於VSM的效果要差2個百分點左右,畢竟特徵維度下降了。原本想把話題設定爲500,也就是把特徵維度降到500維,但是計算時間太恐怖了,久久得不到結果。

這真是費力不討好。

"""再使用LSA的話題-文本矩陣訓練一個分類器""" forest = RandomForestClassifier(oob_score=True,n_estimators = 200) forest = forest.fit(train_lsa_freq, df.sentiment) print("\n====================評估以LSA爲特徵訓練的模型==================\n") model_eval(train_lsa_freq)

3、用n-gram作文本表示

n-gram的意思比較簡單了,沒啥好說的。我這裏讓n-gram=(2,2),也就是每一個單元都是兩個單詞組合而成的。

"""使用sklearn計算2-gram,獲得詞語-文本矩陣"""

# token_pattern的做用是,出現"bi-gram"、"two:three"這種時,能夠切成"bi gram"、"two three"的形式
vectorizer_2gram = CountVectorizer(ngram_range=(2,2),token_pattern=r'\b\w+\b',max_features=5000) train_vsm_2gram = vectorizer_2gram.fit_transform(df.clean_review).toarray() print("2-gram構成的語料庫中前10個元素爲:\n") print(vectorizer_2gram.get_feature_names()[:10])
2-gram構成的語料庫中前10個元素爲: ['able get', 'able make', 'able see', 'able watch', 'absolute worst', 'absolutely brilliant', 
'absolutely hilarious', 'absolutely love', 'absolutely loved', 'absolutely nothing']

接着再訓練隨機森林分類器,並評估模型。不知道怎麼回事,跑了好久,死活收斂不了,有毒。。。算了,放着讓它跑,我也再想一想到底咋回事。

"""使用以2-gram的詞頻爲元素的單詞-文本矩陣訓練一個分類器""" forest = RandomForestClassifier(oob_score=True,n_estimators = 200) forest = forest.fit(train_vsm_2gram, df.sentiment) print("\n====================評估以2-gram爲特徵訓練的模型==================\n") model_eval(train_vsm_2gram)

最後仍是從新訓練第一個模型,也就是用詞頻做爲特徵的單詞-文本矩陣,訓練隨機森林分類器。咱們讀取測試數據進行預測,並保存預測結果。

# 刪除不用的佔內容變量 #del df #del train_vsm_freq

# 讀取測試數據
datafile = os.path.join('..', 'data', 'testData.tsv') df = pd.read_csv(datafile, sep='\t', escapechar='\\') print('Number of reviews: {}'.format(len(df))) df['clean_review'] = df.review.apply(clean_text) vectorizer = CountVectorizer(max_features = 5000) test_data_features = vectorizer.fit_transform(df.clean_review).toarray() result = forest.predict(test_data_features) output = pd.DataFrame({'id':df.id, 'sentiment':result}) # 保存
output.to_csv(os.path.join('..', 'data', 'Bag_of_Words_model.csv'), index=False)

 

 

參考資料:

李航:《統計學習方法》(第二版) 第17章

原文出處:https://www.cnblogs.com/Luv-GEM/p/10888026.html

相關文章
相關標籤/搜索