下载数据。
已经标有情感倾向的训练文件labeledTrainData.tsv,里面有25000条影评以及对应的情感倾向标识;
待测试文件testData.tsv,同样也另有25000条电影评论;
无标注但是数据量更大的影评文件unlabeledTrainData.tsv。搭建模型。
接下来分别采用scikit-learn中的朴素贝叶斯模型以及隶属于集成模型的梯度提升树分类模型,对电影评论进行文本情感分析。具体而言,在朴素贝叶斯模型中依然使用“词袋法”对每条电影评论进行特征向量化,并且借助CountVectorizer和TfidfVectorizer:另一方面,先利用无标注影评文件中训练词向量,然后将每条电影评论中所有词汇的平均向量作为特征训练梯度提升树分类模型。
import pandas as pd
train=pd.read_csv('/Users/daqi/Documents/ipython/Python机器学习及实践/官方资料/Datasets/IMDB/labeledTrainData.tsv', header=0, delimiter="\t",quoting=3)
test=pd.read_csv('/Users/daqi/Documents/ipython/Python机器学习及实践/官方资料/Datasets/IMDB/testData.tsv', header=0, delimiter="\t",quoting=3)
train.head()
test.head()
from bs4 import BeautifulSoup
import re
from nltk.corpus import stopwords
import nltk
#nltk.download('stopwords')如果报错找不到stopwords加上这行代码
#定义review_to_text函数,完成对原始评论的三项数据预处理任务。
def review_to_text(review,remove_stopwords):
#任务1:去掉html标记。
raw_text=BeautifulSoup(review,'html').get_text()
#任务2:去掉非字母字符。
letters=re.sub('[^a-zA-Z]',' ',raw_text)
words=letters.lower().split()
#任务3:如果remove_stopwords被激活,则进一步去掉评论中的停用词。
if remove_stopwords:
stop_words=set(stopwords.words('english'))
words=[w for w in words if w not in stop_words]
#返回每条评论经此三项预处理任务的词汇列表
return words
#分别对原始训练和测试数据集进行上述三项预处理。
X_train=[]
for review in train['review']:
X_train.append(' '.join(review_to_text(review,True)))
X_test=[]
for review in test['review']:
X_test.append(' '.join(review_to_text(review,True)))
y_train=train['sentiment']
#导入文本特征抽取器CountVectorizer与TfidfVectorizer.
from sklearn.feature_extraction.text import CountVectorizer,TfidfVectorizer
#从Scikit-learn中导入朴素贝叶斯模型
from sklearn.naive_bayes import MultinomialNB
#导入Pipeline用于方便搭建系统流程
from sklearn.pipeline import Pipeline
#导入GridSearchCV用于超参数组合的网格搜索。
from sklearn.grid_search import GridSearchCV
#使用Pipeline搭建两组使用朴素贝叶斯模型的分类器,区别在于分别使用CountVectorizer与TfidfVectorizer对文本特征进行抽取
pip_count=Pipeline([('count_vect',CountVectorizer(analyzer='word')),('mnb',MultinomialNB())])
pip_tfidf=Pipeline([('tfidf_vect',TfidfVectorizer(analyzer='word')),('mnb',MultinomialNB())])
#分别配置用于模型超参数搜索的组合
params_count={'count_vect__binary':[True,False],'count_vect__ngram_range':[(1,1),(1,2)],'mnb__alpha':[0.1,1.0,10.0]}
params_tfidf={'tfidf_vect__binary':[True,False],'tfidf_vect__ngram_range':[(1,1),(1,2)],'mnb__alpha':[0.1,1.0,10.0]}
#使用采用4折交叉验证的方法对使用CountVectorizer的朴素贝叶斯模型进行并行化超参数搜索。
gs_count=GridSearchCV(pip_count,params_count,cv=4,n_jobs=-1,verbose=1)
gs_count.fit(X_train,y_train)
#输出交叉验证中最佳的准确性得分以及超参数组合。
print(gs_count.best_score_)
print(gs_count.best_params_)
0.88216
{'count_vect__binary': True, 'count_vect__ngram_range': (1, 2), 'mnb__alpha': 1.0}
#以最佳的超参数组合配置模型并对测试数据进行预测
count_y_predict=gs_count.predict(X_test)
#使用采用4折交叉验证的方法对使用TfidfVectorizer的朴素贝叶斯模型进行并行化超参数搜索。
gs_tfidf=GridSearchCV(pip_tfidf,params_tfidf,cv=4,n_jobs=-1,verbose=1)
gs_tfidf.fit(X_train,y_train)
#输出交叉验证中最佳的准确性得分以及超参数组合
print(gs_tfidf.best_score_)
print(gs_tfidf.best_params_)
0.88712
{'mnb__alpha': 0.1, 'tfidf_vect__binary': True, 'tfidf_vect__ngram_range': (1, 2)}
#以最佳的超参数组合配置模型并对测试数据进行预测
tfidf_y_predict=gs_tfidf.predict(X_test)
#使用pandas对需要提交的数据进行格式化。
submission_count=pd.DataFrame({'id':test['id'],'sentiment':count_y_predict})
submission_tfidf=pd.DataFrame({'id':test['id'],'sentiment':tfidf_y_predict})
#结果输出到本地硬盘
submission_count.to_csv('/Users/daqi/Documents/ipython/Python机器学习及实践/submission_count.csv',index=False)
submission_tfidf.to_csv('/Users/daqi/Documents/ipython/Python机器学习及实践/submission_tfidf.csv',index=False)
#从本地读入未标记数据
unlabeled_train=pd.read_csv('/Users/daqi/Documents/ipython/Python机器学习及实践/官方资料/Datasets/IMDB/unlabeledTrainData.tsv',delimiter='\t',quoting=3)
import nltk.data
#使用使用nltk的tokenizer对影评中的英文句子进行分割
tokenizer=nltk.data.load('tokenizers/punkt/english.pickle')
#定义函数review_to_sentences逐条对影评进行分句。
def review_to_sentences(review,tokenizer):
raw_sentences=tokenizer.tokenize(review.strip())
sentences=[]
for raw_sentences in raw_sentences:
if len(raw_sentences)>0:
sentences.append(review_to_text(raw_sentences,False))
return sentences
corpora=[]
#准备用于训练词向量的数据
for review in unlabeled_train['review']:
corpora+=review_to_sentences(review,tokenizer)
#配置训练词向量模型的超参数
num_features=300
min_word_count=20
num_workers=4
context=10
downsampling = 1e-3
from gensim.models import word2vec
#开始词向量模型的训练
model=word2vec.Word2Vec(corpora,workers=num_workers,\
size=num_features,min_count=min_word_count,\
window=context,sample=downsampling)
model.init_sims(replace=True)
model_name='/Users/daqi/Documents/ipython/Python机器学习及实践/300features_20minwords_10context'
#可以将词向量模型的训练结果长期保存于本地硬盘。
model.save(model_name)
#直接读入已经训练好的词向量模型
from gensim.models import Word2Vec
model=Word2Vec.load(model_name)
#探查一下该向量模型的训练结果
model.most_similar('man')
import numpy as np
#定义一个函数使用词向量产生文本特征向量
def makeFeatureVec(words,model,num_features):
featureVec=np.zeros((num_features,),dtype='float32')
nwords=0
index2word_set=set(model.wv.index2word)
for word in words:
if word in index2word_set:
nwords=nwords+1.
featureVec=np.add(featureVec,model[word])
featureVec=np.divide(featureVec,nwords)
return featureVec
#定义另一个每条影评转化为基于词向量的特征向量(平均词向量)。
def getAvgFeatureVecs(reviews,model,num_features):
counter=0
reviewFeatureVecs=np.zeros((len(reviews),num_features),dtype='float32')
for review in reviews:
reviewFeatureVecs[counter]=makeFeatureVec(review,model,num_features)
counter+=1
return reviewFeatureVecs
#准备新的基于词向量表示的训练和测试特征向量。
clean_train_reviews=[]
for review in train['review']:
clean_train_reviews.append(review_to_text(review,remove_stopwords=True))
trainDataVecs=getAvgFeatureVecs(clean_train_reviews,model,num_features)
clean_test_reviews=[]
for review in test['review']:
clean_test_reviews.append(review_to_text(review,remove_stopwords=True))
testDataVecs=getAvgFeatureVecs(clean_test_reviews,model,num_features)
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.grid_search import GridSearchCV
gbc=GradientBoostingClassifier()
# 配置超参数的搜索组合
params_gbc={'n_estimators':[10,100,300],'learning_rate':[0.001,0.1,1.0],'max_depth':[2,3,4]}
gs=GridSearchCV(gbc,params_gbc,cv=4,n_jobs=-1,verbose=1)
gs.fit(trainDataVecs,y_train)
#输出网格搜索得到的最佳性能以及最优超参数组合。
print(gs.best_score_)
print(gs.best_params_)
0.85464
{'learning_rate': 0.1, 'max_depth': 4, 'n_estimators': 300}