本项目实验流程如下:
项目背景介绍
情感分析就是根据文本推测出这段文本所蕴含的感情:积极或者是消极的,实际上情感不只是有积极或者消极,人还会有生气、开心、悲伤等各种情绪,但是计算机不同于人,理论上只要有足够多各种情绪标注的文本的话,可以识别出各种情绪的文本。但是由于获取情感标注的文本比较少,一般只有积极和消极两种文本。
原理介绍
情感分析关键在于如何把文字转化成机器能够识别的向量表示,有三种方法:
一、可以通过统计一个文本当中消极的词汇和积极的词汇的综合得分来评估这个文本是积极的还是消极的。
比如对于“这篇文章写得挺不错的,我很喜欢”这个句子就可以分成 ['这篇', '文章', '写得', '挺不错', '我', '很', '喜欢'] 这样的一个词列表,有一个标记了积极词汇和消极词汇的词汇表,句子中的积极词汇在这个词汇表中出现的话就+1,消极词汇就-1,没有出现就为0,算最后的总分,如果是大于某个值,那么就标记这个文本为积极情感。
二、TF-IDF逆文档频率
也就是统计每个词在单个文本和整个语料库中出现的频率,然后通过乘积得到TF-IDF逆文档频率。之所以要引入TF-IDF逆文档频率,这是为了要找出能够比较有区分能力的词。如果一个词汇在某篇词汇当中出现频率很高,但在所有的文档中出现的文档数目很少,说明这个词很有区分能力,相对应的TF-IDF逆文档频率就会比较高;相反,如果一个词汇在某篇词汇当中出现频率很高,但在所有的文档中出现的文档数目很多,说明这个词是个常用词,没有多少区分能力。
公式计算如下:
其中是这个词在该文档当中出现的频率,而是总文档数目除以出现该词的文档数目。
第二个公式当中是总文档的数目,而分母是词出现的文档的集合的数目。
这种逆文档频率最后返回的是独热编码,没有办法反映上下文关系。
三、Word2Vec
将每个词表示为单独的一个向量,每个向量之间可以计算上下文距离,也就是如果一篇文档当中大部分跟我们之前标注的文本里面的词都很相近,那么分类器更有可能把这篇文档分成跟这个标注的文本同样的一个类别。
数据分析和工具
数据概况
思路
def create_dataset():
import pandas as pd
import jieba
import numpy as np
neg=pd.read_excel('neg.xls',header=None,index=None)
pos=pd.read_excel('pos.xls',header=None,index=None)
cut_word=lambda x:jieba.lcut(x)
neg_list=neg.iloc[:,0].apply(cut_word)
pos_list=pos.iloc[:,0].apply(cut_word)
x_train=np.concatenate((neg_list, pos_list), axis=0)
y_train=np.concatenate((np.ones(len(neg)),np.zeros(len(pos))),axis=0)
np.save('xtrain.npy',x_train)
np.save('ytrain.npy',y_train)
print ('done')
if __name__=='__main__':
create_dataset()
以上就得到了结巴分词后的词列表numpy 阵列,并且保存到了xtrain.npy和ytrain.npy的两个阵列当中。
接下来就是要把xtrain这个包含了很多分词后的词列表转化成词 向量。
import pandas as pd
from gensim.models import Word2Vec
import numpy as np
#导入特征向量
xtrain=np.load('xtrain.npy')
#在这里我们设置维度为300,最小出现次数为10
model=Word2Vec(size=300, min_count=10)
model.build_vocab(xtrain)
model.train(xtrain, total_examples=model.corpus_count, epochs=model.iter)
构建好词向量以后,我们可以查看一个词的向量表示,也可以计算词与词的相似度。
接下来就是要将一个文本中的多个词平均后求这个文本的向量表示。
#定义一个函数
def average_vector(sentence):
count=0
vector=np.zeros((1,300))
for word in sentence:
try:
wv=model.wv[word]
count+=1
vector+=wv
except:
continue
return vector/count
最终返回的是一个平均后的向量表示。
然后要把这个函数应用到每个文本当中。
x_train_df=np.array([average_vector(z) for z in x_train])
#最终返回的应该是shape为(,300)这样的向量表示,到这里就构建好了,接下来就保存起来向量.
np.save('trained_vector.npy',x_train_df)
训练SVM分类模型
from sklearn.SVC import svm
from sklearn.externals import joblib
#加载x_train和y_train
x=np.load('trained_vectors.npy')
y=np.load('ytrain.npy')
clf=svm(kernel='rbf')
clf.fit(x,y)
joblib.dump(clf,'svm_clf.pkl')
这样我们就得到了一个简单的分类模型。
接下来应该是载入词向量和分类模型,对实验楼的comments文件进行转换之后,进行预测。
利用分类模型对实验楼数据进行标注
载入预先训练好的词向量
wordvectors=np.load('trained_vectors.npy')
data=pd.read_excel('comments.csv',header=True,encoding='utf-8')
x_train=jieba.lcut(data['评论内容'])
model=joblib.load('w2v_model.pkl')
载入实验楼数据并转化
def average_w2v():
import pandas as pd
import jieba
count=0
vectors=np.zeros((1, 300))
for word in jieba.lcut(sentence):
try:
wv=model.wv[word]
count+=1
vectors+=wv
except:
continue
return vectors/count
def svm_predict():
clf_model=joblib.load('svm_clf.pkl')
results=[]
for string in x_train:
vector=average_w2v(string)
try:
result=clf_model.predict(vector)
results.append(result[0])
except:
results.append(2)
if int(result[0]) == 1:
print ('【积极】')
elif int(result[0])==0:
pritn ('【消极】')
else:
print ('Unknown')
merged=pd.concat(data['评论内容'], pd.Series(results, name=['情绪']), axis=1)
merged.to_csv('merged_out.csv', index=False)
print ('Done')
数据可视化
结论与分析
通过上面的比例可以看出绝大部分是对实验楼的课程持正面评价,有一部分是由于没有语库当中的词,所以不知道;还有很小一部分是有负面评价。分类结果从句子的意思推测,还是比较符合的。