情感分析系統(預測使用者評論積極或消極的概率)
1.資料預處理
本部分將要完成資料的預處理過程,包括資料的讀取,資料清洗,分詞,以及把文字轉換成tf-idf向量。在接下來的任務中,正面的情感我們標記為1, 負面的情感我們標記成0。
import re import jieba import numpy as np def process_line(line): new_line = re.sub('([a-zA-Z0-9])','',line) new_line = ''.join(e for e in new_line if e.isalnum()) new_line = ','.join(jieba.cut(new_line)) return new_line def process_train(file_path): comments = [] # 用來儲存評論 labels = [] # 用來儲存標籤(正/負),如果是train_positive.txt,則所有標籤為1, 否則0. with open(file_path) as file: # TODO 提取每一個評論,然後利用process_line函式來做處理,並新增到comments。 text = file.read().replace(' ','').replace('\n','') reg = '<reviewid=.*?</review>' result = re.findall(reg,text) for r in result: r = process_line(r) comments.append(r) if file_path == 'train.positive.txt': labels.append('1') else: labels.append('0') return comments, labels def process_test(file_path): comments = [] # 用來儲存評論 labels = [] # 用來儲存標籤(正/負). with open(file_path) as file: # TODO 提取每一個評論,然後利用process_line函式來做處理,並新增到 # comments。 text = file.read().replace(' ','').replace('\n','') reg = '<reviewid=.*?</review>' result = re.findall(reg,text) for r in result: label = re.findall('label="(\d)"',r)[0] labels.append(label) r = process_line(r) comments.append(r) return comments, labels def read_file(): """ 讀取所提供的.txt檔案,並把內容處理之後寫到list裡面。 這裡需要分別處理四個檔案,“train_positive.txt", "train_negative.txt", "test_combined.txt" 並把每一個檔案裡的內容儲存成列表。 """ # 處理訓練資料,這兩個檔案的格式相同,請指定訓練檔案的路徑 train_pos_comments, train_pos_labels = process_train("train.positive.txt") train_neg_comments, train_neg_labels = process_train("train.negative.txt") # TODO: train_pos_comments和train_neg_comments合併成train_comments, train_pos_labels和train_neg_labels合併成train_labels train_comments = train_pos_comments + train_pos_labels train_labels = train_pos_labels + train_neg_labels # 處理測試資料, 請指定測試檔案的路徑 test_comments, test_labels = process_test("test.combined.txt") return train_comments, train_labels, test_comments, test_labels
讀取文字並校驗資料長度
# 讀取資料,並對文字進行處理
train_comments, train_labels, test_comments, test_labels = read_file()
# 檢視訓練資料與測試資料大小
print (len(train_comments), len(train_labels), len(test_comments), len(test_labels))
把每一個文字內容轉換成tf-idf向量
from sklearn.feature_extraction.text import TfidfVectorizer # 匯入sklearn庫 # TODO: 利用TfidfVectorizer把train_comments轉換成tf-idf,把結果儲存在X_train, 這裡X_train是稀疏矩陣(Sparse Matrix) # 並把train_labels轉換成向量 y_train. 類似的,去建立X_test, y_test。 把文字轉換成tf-idf過程請參考TfidfVectorizer的說明 tfid_vec = TfidfVectorizer() X_train = tfid_vec.fit_transform(train_comments) y_train = np.array(train_labels) X_test = tfid_vec.transform(test_comments) y_test = np.array(test_labels) # 檢視每個矩陣,向量的大小, 保證X_train和y_train, X_test和y_test的長度是一樣的。 print (np.shape(X_train), np.shape(y_train), np.shape(X_test), np.shape(y_test))
第二部分: 利用邏輯迴歸模型搭建情感分析引擎
在本部分將會利用羅迴歸模型(logistic regressiion)來搭建情感分析引擎。
from sklearn.linear_model import LogisticRegression # TODO: 初始化模型model,並利用模型的fit函式來做訓練,暫時用預設的設定。 lr = LogisticRegression().fit(X_train,y_train) # 列印在訓練資料上的準確率 print ("訓練資料上的準確率為:" + str(lr.score(X_train, y_train))) # 列印在測試資料上的準確率 print ("測試資料上的準確率為: " + str(lr.score(X_test, y_test))) # TODO: 利用自己提出的例子來做測試。隨意指定一個評論,接著利用process_line來做預處理,再利用之前構建好的TfidfVectorizer來把文字轉換 # 成tf-idf向量, 然後再利用構建好的model做預測(model.predict函式) test_comment1 = "這個很好" test_comment2 = "垃圾" test_comment3 = "評論區說不爛都是騙人的,超讚" a = [] a.append(process_line(test_comment1)) print(lr.predict(tfid_vec.transform(a)))
列印結果如下,能看到測試評論“這個很好”的預測值為1,即“積極”
第三部分: 利用決策樹,神經網路,SVM來訓練模型。
決策樹模型
from sklearn import tree
# TODO: 初始化決策樹模型,並利用模型的fit函式來做訓練並列印在訓練和測試資料上的準確率,利用決策樹預設的引數設定
dtc1 = tree.DecisionTreeClassifier().fit(X_train,y_train)
# 列印在訓練資料上的準確率
print ("訓練資料上的準確率為:" + str(dtc1.score(X_train, y_train)))
# 列印在測試資料上的準確率
print ("測試資料上的準確率為: " + str(dtc1.score(X_test, y_test)))
# TODO: 初始化決策樹模型,並利用模型的fit函式來做訓練並列印在訓練和測試資料上的準確率,設定max_depth引數為3
dtc2 = tree.DecisionTreeClassifier(max_depth=3).fit(X_train,y_train)
# 列印在訓練資料上的準確率
print ("訓練資料上的準確率為:" + str(dtc2.score(X_train, y_train)))
# 列印在測試資料上的準確率
print ("測試資料上的準確率為: " + str(dtc2.score(X_test, y_test)))
# TODO: 初始化決策樹模型,並利用模型的fit函式來做訓練並列印在訓練和測試資料上的準確率,設定max_depth引數為5
dtc3 = tree.DecisionTreeClassifier(max_depth=5).fit(X_train,y_train)
# 列印在訓練資料上的準確率
print ("訓練資料上的準確率為:" + str(dtc3.score(X_train, y_train)))
# 列印在測試資料上的準確率
print ("測試資料上的準確率為: " + str(dtc3.score(X_test, y_test)))
支援向量機(SMV)模型
from sklearn import svm
# TODO: 初始化SVM模型,並利用模型的fit函式來做訓練並列印在訓練和測試資料上的準確率,SVM模型的kernel設定成“rbf”核函式
svc = svm.SVC(kernel='rbf').fit(X_train,y_train)
# 列印在訓練資料上的準確率
print ("訓練資料上的準確率為:" + str(svc.score(X_train, y_train)))
# 列印在測試資料上的準確率
print ("測試資料上的準確率為: " + str(svc.score(X_test, y_test)))
線性支援向量機(LinearSVM)
from sklearn.svm import LinearSVC
# TODO: 初始化LinearSVC模型,並利用模型的fit函式來做訓練並列印在訓練和測試資料上的準確率,使用模型的預設引數。
clf = LinearSVC()
clf.fit(X_train,y_train)
# 列印在訓練資料上的準確率
print ("訓練資料上的準確率為:" + str(clf.score(X_train, y_train)))
# 列印在測試資料上的準確率
print ("測試資料上的準確率為: " + str(clf.score(X_test, y_test)))
神經網路模型
from sklearn.neural_network import MLPClassifier
# TODO: 初始化MLPClassifier模型,並利用模型的fit函式來做訓練並列印在訓練和測試資料上的準確率,設定為hidden_layer_sizes為100,
# 並使用"lbfgs" solver
mlp = MLPClassifier(solver='lbfgs',hidden_layer_sizes=100)
mlp.fit(X_train,y_train)
# 列印在訓練資料上的準確率
print ("訓練資料上的準確率為:" + str(mlp.score(X_train, y_train)))
# 列印在測試資料上的準確率
print ("測試資料上的準確率為: " + str(mlp.score(X_test, y_test)))
第四部分: 通過交叉驗證找出最好的超引數
呼叫一個sklearn模型本身很簡單,只需要2行程式碼即可以完成所需要的操作。但這裡的關鍵點在於怎麼去尋找最優的超引數(hyperparameter)。 比如對於邏輯迴歸
來說,我們可以設定一些引數的值如“penalty”, C等等,這些我們可以理解成是超引數。通常情況下,超引數對於整個模型的效果有著舉足輕重的作用,這就意味著
我們需要一種方式起來找到一個比較合適的引數。其中一個最常用的方法是grid search, 也就在一個去區間裡面做搜尋,然後找到最優的那個引數值。
舉個例子,對於邏輯迴歸模型,它擁有一個超引數叫做C,在文件裡面解釋叫做“Inverse of regularization strength“, 就是正則的權重,而且這種權重的取值
範圍可以認為通常是(0.01, 1000)區間。這時候,通過grid search的方式我們依次可以嘗試 0.01, 0.1, 1, 10, 100, 1000 這些值,然後找出使得
模型的準確率最高的引數。當然,如果計算條件資源允許的話,可以嘗試更多的值,比如0.01,0.05,0.1, 0.5, 1, 5, 10 …。 當我們嘗試越多值的時候,找到
最優引數的概率就會越大。
邏輯迴歸模型
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import KFold
params_c = np.logspace(-3,3,7) # 對於引數 “C”,嘗試幾個不同的值
best_c = params_c[0] # 儲存最好的C值
best_acc = 0
kf = KFold(n_splits=5,shuffle=False)
for c in params_c:
# TODO: 編寫交叉驗證的過程,對於每一個c值,計算出在驗證集中的平均準確率。 在這裡,我們做5-fold交叉驗證。也就是,每一次把20%
# 的資料作為驗證集來對待,然後準確率為五次的平均值。我們把這個準確率命名為 acc_avg
avg = 0
for train_index, test_index in kf.split(X_train):
lr = LogisticRegression(C=c).fit(X_train[train_index],y_train[train_index])
avg += lr.score(X_train[test_index],y_train[test_index])
acc_avg = avg/5
if acc_avg > best_acc:
best_acc = acc_avg
best_c = c
print ("最好的引數C值為: %f" % (best_c))
# TODO 我們需要在整個訓練資料上重新訓練模型,但這次利用最好的引數best_c值
# 提示: model = LogisticRegression(C=best_c).fit(X_train, y_train)
lr = LogisticRegression(C=best_c).fit(X_train, y_train)
# 列印在訓練資料上的準確率
print ("訓練資料上的準確率為:" + str(lr.score(X_train, y_train)))
# 列印在測試資料上的準確率
print ("測試資料上的準確率為: " + str(lr.score(X_test, y_test)))
神經網路模型
注意:這個模型訓練時間較久,在做交叉驗證的情況下計算時間可能長達幾十個小時
from sklearn.neural_network import MLPClassifier
import numpy as np
param_hidden_layer_sizes = np.linspace(10, 200, 20) # 針對引數 “hidden_layer_sizes”, 嘗試幾個不同的值
param_alphas = np.logspace(-4,1,6) # 對於引數 "alpha", 嘗試幾個不同的值
best_hidden_layer_size = param_hidden_layer_sizes[0]
best_alpha = param_alphas[0]
for size in param_hidden_layer_sizes:
for val in param_alphas:
# TODO 編寫交叉驗證的過程,需要做5-fold交叉驗證。
avg = 0
for train_index, test_index in kf.split(X_train, y_train):
mlp = MLPClassifier(alpha=int(val),hidden_layer_sizes=int(size))
mlp.fit(X_train[train_index],y_train[train_index])
avg += mlp.score(X_train[test_index],y_train[test_index])
acc_avg = avg/5
if acc_avg > best_acc:
best_acc = acc_avg
best_hidden_layer_size = size
best_alpha = val
print ("最好的引數hidden_layer_size值為: %f" % (best_hidden_layer_size))
print ("最好的引數alpha值為: %f" % (best_alpha))
# TODO 我們需要在整個訓練資料上重新訓練模型,但這次使用最好的引數hidden_layer_size和best_alpha
mlp = MLPClassifier(alpha=best_alpha,hidden_layer_sizes=best_hidden_layer_size).fit(X_train,y_train)
# 列印在訓練資料上的準確率
print ("訓練資料上的準確率為:" + str(mlp.score(X_train, y_train)))
# 列印在測試資料上的準確率
print ("測試資料上的準確率為: " + str(mlp.score(X_test, y_test)))
完整程式碼及訓練資料已上傳至github,點選此處可直接檢視,有疑問的同學請提issues或部落格下方留言~