基于机器学习的文本情感分类

前言:
上一节介绍了基于情感词典的文本情感分类方法,这一节主要介绍采用机器学习的方法,对已有分类的数据集进行训练并测试,我从网上下载了一些关于外卖评论的情感语料,分成两个txt文件,一个是正向评论的文本文档,一个是负面评论的文本文档,如下图所示,本文基于此进行分类学习。
在这里插入图片描述
在这里插入图片描述
目录:
1.读取数据
2.分词
3.提取特征向量
4.机器学习之分类

1.读取数据

import numpy as np
import pandas as pd
import jieba
from sklearn.model_selection import train_test_split           #划分训练/测试集
from sklearn.feature_extraction.text import CountVectorizer    #抽取特征
from sklearn.naive_bayes import MultinomialNB
from sklearn.feature_extraction.text import TfidfVectorizer
#读取并清洗数据
#因为几个文档的编码不大一样,所以兼容了三种编码模式,根据经验,这三种是经常会遇到的
def get_txt_data(txt_file):
    mostwords=[]
    try:
        file=open(txt_file,'r',encoding='utf-8')
        for line in file.readlines():
            curline=line.strip().split("\t")
            mostwords.append(curline)
    except:
        try:
            file=open(txt_file,'r',encoding='gb2312')
            for line in file.readlines():
                curline=line.strip().split("\t")
                mostwords.append(curline)
        except:
            try:
                file=open(txt_file,'r',encoding='gbk')
                for line in file.readlines():
                    curline=line.strip().split("\t")
                    mostwords.append(curline)
            except:
                ''   
    return mostwords

neg_doc=get_txt_data(r'D:\nltk_data\waimai_nlp\情感分析语料\waimai_neg.txt')
pos_doc=get_txt_data(r'D:\nltk_data\waimai_nlp\情感分析语料\waimai_pos.txt')

2.分词
分词工具有很多,主流的主要包括以下几种:

  1. 哈工大的LTP HIT-SCIR/ltp
  2. jieba分词工具 yanyiwu/cppjieba
  3. 清华大学THULAC https://github.com/thunlp/THULAC
  4. 斯坦福分词器 https://nlp.stanford.edu/software/segmenter.shtml
  5. Hanlp分词 hankcs/HanLP
  6. 字嵌入+Bi-LSTM+CRF分词器 https://github.com/koth/kcws
  7. ZPar分词器 frcchang/zpar

本篇主要采用了比较主流的jieba分词包,具体内容可以详见:jieba分词完整文档

另外,我将每个评论中一些垃圾词汇去掉了。所谓的垃圾词汇,就是指意义模糊的词,或者一些语气助词,标点符号等等,通常他们对文本起不了分类特征的意义。这些垃圾词汇我们称之为停用词。把所有停用词集合起来构成一张停用词表格,这样,以后我们处理文本时,就可以从这个根据表格,过滤掉文本中的一些垃圾词汇了。我的停用词是从网上下载了几个,合并起来的。大家可以自行下载或者准备自己需要的停用词。

def context_cut(sentence):
    words_list=[]
    #获取停用词
    stop=open(r'D:\nltk_data\NLP\情感分析\停用词库.txt','r+',encoding='utf-8')
    stopwords=stop.read().split('\n')
    cut_words=list(jieba.cut(sentence))
    for word in cut_words:
        if not(word in stopwords):
            words_list.append(word)
        words_str=','.join(words_list)
    return words_str,words_list 

#合并两个数据集,并且打上标签,分成测试集和训练集
words=[]
word_list=[]
for i in neg_doc:
    cut_words_str,cut_words_list=context_cut(i[0])
    word_list.append((cut_words_str,-1))
    words.append(cut_words_list)
for j in pos_doc:
    cut_words_str2,cut_words_list2=context_cut(j[0])
    word_list.append((cut_words_str2,1))
    words.append(cut_words_list2)
#word_list=[('菜品,质量,好,味道,好,就是,百度,的,问题,总是,用,运力,原因,来,解释,我,也,不,懂,这,是,什么,原因,晚,了,三个,小时,呵呵,厉害,吧,反正,订,了,就,退,不了,只能,干,等', -1),...,...]
#将word_list中的值和标签分别赋予给x,y
x,y=zip(*word_list)
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.2,random_state=0)

3.提取特征向量
3.1 ##将内容转为词袋向量(Bag of words)
在正式使用之前,我们首先举个例子来看一下词袋向量是什么样的:

data1= '今天上海的天气真好!我的心情非常高兴!如果去旅游的话我会非常兴奋!和你一起去旅游我会更加幸福!'
data2= '救命,你是个坏人,救命,你不要碰我,救命,你个大坏蛋!'
data3= '美国华裔科学家,祖籍江苏扬州市高邮县,生于上海,斯坦福大学物理系,电子工程系和应用物理系终身教授!'
words_str1,words_list1=context_cut(data1)
words_str2,words_list2=context_cut(data2)
words_str3,words_list3=context_cut(data3)
count = CountVectorizer()#这里可以加上max_features=10的参数,不加就是这三句话的全部词语。
doc = np.array([words_str1,words_str2,words_str3])
bag = count.fit_transform(doc)
print(count.vocabulary_) #查看词袋字典
[out]:{'今天': 3, '上海': 1, '天气': 8, '心情': 13, '非常高兴': 31, '如果': 9, '旅游': 19, '的话': 25, '我会': 14, '非常': 30,'兴奋': 4, '一起': 0, '更加': 20, '幸福': 11, '救命': 16, '坏人': 6, '不要': 2, '大坏蛋': 7, '美国': 29, '华裔': 5, '科学家': 27, '祖籍': 26, '江苏': 21, '扬州市': 15, '高邮县': 32,'生于': 23, '斯坦福大学': 18, '物理系': 22, '电子': 24, '工程系': 10, '应用': 12, '终身': 28, '教授': 17}
#三个句子的所有词的字典,后面的数字为每个词的唯一索引
print(bag.toarray()) #将语句转化为向量列表
#按照上述词袋的索引值从小到大进行排列,['一起','上海','不要',······],根据此顺序列出每句话的词语出现的频次,所以下列输出重得值为词的出现频率
[out]:[[1 1 0 1 1 0 0 0 1 0 1 0 1 2 0 0 0 0 2 1 0 0 0 0 0 0 0 0 1 1 0]
 [0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 1 0 0 0 1 0 0 0 1 0 1 0 0 1 0 1 1 0 0 1 2 1 1 1 1 1 1 0 0 1]]

回归正题,下面将上面的分词文本转化成结构化向量:

#注意,countvectorizer只能训练str格式的,因为它里面会有lower之类的函数,不适用于列表等其它格式
count = CountVectorizer(max_features=500) #这里的max_features实根据后面的机器学习模型确定的,原先定为200的时候,机器学习效果较差,后改为500效果较好
bag = count.fit_transform(x_train)

3.2 TF-IDF(词频-逆向文件频率)
TF-IDF的主要思想是:如果某个单词在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。具体原理可以参照TF-IDF算法介绍及实现
具体实现:

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer

count = CountVectorizer(max_features=500) #该类会将文本中的词语转换为词频矩阵,矩阵元素a[i][j] 表示j词在i类文本下的词频
tfidf = TfidfTransformer(use_idf = True, norm = 'l2', smooth_idf = True) #该类会统计每个词语的tf-idf权值
tfidf_x_train=tfidf.fit_transform(count.fit_transform(x_train)) #将文本转为词频矩阵并计算tf-idf
np.set_printoptions(precision = 2)
x_train_weight = tfidf_x_train.toarray()

print(x_train_weight)
#还有另一种方法是TfidfVectorizer,
#关于TfidfVectorizer和TfidfTransformer的关联可以参照[TfidfVectorizer和TfidfTransformer](https://blog.csdn.net/liuchenbaidu/article/details/105063535?biz_id=102&utm_term=TfidfVectorizer&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-105063535&spm=1018.2118.3001.4187)
from sklearn.feature_extraction.text import TfidfVectorizer
vec=TfidfVectorizer(analyzer='word', ngram_range=(1,4), max_features=500)
tfidf_x_train=vec.fit_transform(x_tain) #与上面一种TfidfTransformer
tfidf_x_test=vec.fit_transform(x_test)

4.模型训练
4.1 回归模型

from sklearn.linear_model import LinearRegression

lin_reg=LinearRegression()

lin_reg.fit(vec.transform(x_train),y_train)

from sklearn.metrics import mean_squared_error

y_test_hat=lin_reg.predict(vec.transform(x_test))
y_train_hat=lin_reg.predict(vec.transform(x_train))
lin_mse1=mean_squared_error(y_test,y_test_hat)
lin_mse2=mean_squared_error(y_train,y_train_hat)
lin_rmse1=np.sqrt(lin_mse1)
lin_rmse2=np.sqrt(lin_mse2)
print ('测试集:',lin_rmse1)
print ('训练集:',lin_rmse2)
#使用tf-idf特征向量,使用线性回归分类,mean_squared_error均方误差为0.71440708
'''测试集: 0.7144070856551623
训练集: 0.6777972230714499'''

4.2 决策树模型

##----------2.决策树-----##
#模型效果不好的时候(拟合不足),考虑换个更强大的模型,决策树
from sklearn.tree import DecisionTreeRegressor
tree_reg=DecisionTreeRegressor()

tree_reg.fit(vec.transform(x_train),y_train)

y_test_hat=tree_reg.predict(vec.transform(x_test))
y_train_hat=tree_reg.predict(vec.transform(x_train))
tree_mse1=mean_squared_error(y_test,y_test_hat)
tree_mse2=mean_squared_error(y_train,y_train_hat)
tree_rmse1=np.sqrt(tree_mse1)
tree_rmse2=np.sqrt(tree_mse2)
print ('测试集',tree_rmse1)
print ('训练集',tree_rmse2)
#使用tf-idf特征向量,使用决策树,
#如果tree_rmse接近0,可能模型师完美的,更可能的是数据严重过度拟合了
#在有信心启动模型之前,都不要触碰测试集,所以,建议拿训练集中的一部分用户训练,另一部分用于模型的验证
'''测试集 0.7432269884814903
训练集 0.31452945551691786'''

4.3 贝叶斯分类器–MultinomialNB

'''
使用CountVectorizer进行特征提取,使用MultinomialNB分类训练,max_features=200时,结果为0.4728125
max_features=500时,结果为0.5665625
'''
classfier = MultinomialNB()
classfier.fit(count.fit_transform(x_train),y_train)
print(classfier.score(count.fit_transform(x_test),y_test))

#改用tf-idf进行特征提取,max_features=500时,结果为0.834375
classfier = MultinomialNB()
classfier.fit(vec.transform(x_train),y_train)
print('训练集:',classfier.score(vec.transform(x_test),y_test))
print('测试集:',classfier.score(vec.transform(x_train),y_train))
'''训练集: 0.8275
测试集: 0.83953125'''

4.4 SVM模型

##----------4.使用SVM模型进行训练----##
from sklearn import svm 
#使用TF-IDF提取特征,使用SVM训练,结果为0.83125
from sklearn.metrics import accuracy_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
from sklearn.metrics import roc_auc_score

classfier = svm.SVC(kernel='linear')
classfier.fit(vec.transform(x_train),y_train)
#print(classfier.score(vec.transform(x_test),y_test))

print ('训练集:',classfier.score(vec.transform(x_train), y_train))  # 精度
print ('测试集:',classfier.score(vec.transform(x_test), y_test))
'''训练集: 0.85234375
测试集: 0.83125'''
y_train_hat=classfier.predict(vec.transform(x_train))
print ('训练集准确率:',accuracy_score(y_train_hat,y_train))
print ('训练集召回率:',recall_score(y_train_hat,y_train))
print ('F1:',f1_score(y_train_hat,y_train))
print ('ROC值:',roc_auc_score(y_train_hat,y_train))
'''训练集准确率: 0.85234375
训练集召回率: 0.8585552543453995
F1: 0.8506873123716229
ROC值: 0.852466476919981'''

# 分类报告:precision/recall/fi-score/均值/分类个数
from sklearn.metrics import classification_report
target_names = ['-1','1']
print(classification_report(y_train_hat,y_train, target_names=target_names))

4.5 随机森林(RandomForestRegressor)

from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import cross_val_score
from IPython import display

forest_reg=RandomForestRegressor()
forest_reg.fit(vec.transform(x_train),y_train)
print('训练集:',forest_reg.score(vec.transform(x_test),y_test))
print('测试集:',forest_reg.score(vec.transform(x_train),y_train))
'''训练集: 0.5592796842264617
测试集: 0.8471523627994759'''

##引入交叉验证,对模型进行调优
x_train_tf=tfidf.fit_transform(count.fit_transform(x_train)).toarray()
forest_scores=cross_val_score(forest_reg,x_train_tf,y_train,scoring='neg_mean_squared_error',cv=10)
forest_rmse_scores=np.sqrt(-forest_scores)
display(forest_rmse_scores)

4.6 逻辑回归

#引入随机搜索,选择最优模型参数
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import GridSearchCV
 
tfidf = TfidfVectorizer(strip_accents=None,
                        lowercase=False,
                        preprocessor=None)
 
param_grid = [{'vect__ngram_range': [(1, 1)],
               'clf__penalty': ['l1', 'l2'],
               'clf__C': [1.0, 10.0, 100.0]},
              {'vect__ngram_range': [(1, 1)],
               'vect__use_idf':[False],
               'vect__norm':[None],
               'clf__penalty': ['l1', 'l2'],
               'clf__C': [1.0, 10.0, 100.0]},
              ]
 
lr_tfidf = Pipeline([('vect', tfidf),
                     ('clf', LogisticRegression(random_state=0))])
 
gs_lr_tfidf = GridSearchCV(lr_tfidf, param_grid,
                           scoring='accuracy',
                           cv=5,
                           verbose=1,
                           n_jobs=-1)

gs_lr_tfidf.fit(x_train, y_train)
print('Best parameter set: %s ' % gs_lr_tfidf.best_params_)
print('CV Accuracy: %.3f' % gs_lr_tfidf.best_score_)

clf = gs_lr_tfidf.best_estimator_
print('Test Accuracy: %.3f' % clf.score(x_test, y_test))
'''
Best parameter set: {'clf__C': 10.0, 'clf__penalty': 'l2', 'vect__ngram_range': (1, 1)}
CV Accuracy: 0.877
Test Accuracy: 0.888'''

总结:
从结果看下来,比较好的模型是逻辑回归,MultinomialNB和SVM三种,本文采用的是tf-idf进行的特征提取,后面计划尝试用word2vec进行看看。以上,为一个NLP的新手学习,有任何不正确的欢迎指正!

  • 19
    点赞
  • 190
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值