情感分析系统(预测用户评论积极或消极的概率)

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 = ''
        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,即“积极”
在这里插入图片描述

第三部分: 利用决策树,神经网络,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或博客下方留言~

你可能感兴趣的:(AI,神经网络,交叉验证,机器学习,自然语言处理)