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