本部分将要完成数据的预处理过程,包括数据的读取,数据清洗,分词,以及把文本转换成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 = ''
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 = ''
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_neg_comments
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,即“积极”
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)))
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)))
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或博客下方留言~