《Python机器学习及实践:从零开始通往Kaggle竞赛之路》第4章 实战篇 学习笔记(三)4.3IMDB影评得分估计总结

目录

4.3IMDB影评得分估计

1、背景

2、下载数据

3、搭建模型

4、编程实践

5、提交结果


4.3IMDB影评得分估计

1、背景

“4.2Titanic罹难乘客预测总结”一节所使用的数据,不论是其形式还是规模都无法与大量现实分析任务涉及的数据相当。因此在本节,如图4-8所示,另选Kaggle上的一项竞赛任务:IMDB影评得分估计。与上节结构化良好的小规模档案数据不同的是,本节的竞赛任务要求参赛者分析电影评论网站的留言,判断每条留言的情感倾向。不仅在规模上要比泰坦尼克号乘客数据大上几个量级,而且原始数据也不及之前的格式化。

2、下载数据

IMDB影评得分估计竞赛任务一共为参赛者提供了4份不同的数据文件,其中包括:已经标有情感倾向的训练文件labeledTrainData.tsv,里面有25000条影评以及对应的情感倾向标识;待测试文件testData.tsv,同样也另有25000条电影评论;还有一份无标注但是数据量更大的影评文件unlabeledTrainData.tsv;最后是一份样例文件sampleSubmission.csv用来告知参赛者最终结果的提交格式。

3、搭建模型

接下来分别采用Scikit-learn中的朴素贝叶斯模型以及隶属于集成模型的梯度提升树分类模型,对电影评论进行文本情感分析。具体而言,在朴素贝叶斯模型中依然使用“词袋法”对每条电影评论进行特征向量化,并且借助CountVectorizer和TfidfVectorizer;另一方面,先利用无标注影评文件中训练词向量,然后将每条电影评论中所有词汇的平均向量作为特征训练梯度提升树分类模型。

4、编程实践

# 代码78:IMDB影评得分估计竞赛编码示例
# 导入pandas用于读取和写入数据操作。
import pandas as pd

# 从本地读入训练与测试数据集。
train = pd.read_csv('../Datasets/IMDB/labeledTrainData.tsv', delimiter='\t')
test = pd.read_csv('../Datasets/IMDB/testData.tsv', delimiter='\t')

# 查验一下前几条训练数据。
train.head()
print(train.head())
# 查验一下前几条测试数据。
test.head()
print(test.head())

# 从bs4导入BeautifulSoup用于整洁原始文本。
from bs4 import BeautifulSoup
# 导入正则表达式工具包。
import re
# 从nltk.corpus里导入停用词列表。
from nltk.corpus import 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.model_selection import GridSearchCV

# 使用Pipeline搭建两组使用朴素贝叶斯模型的分类器,区别在于分别使用CountVectorizer和TfidfVectorizer对文本特征进行抽取。
pip_count = Pipeline([('count_vec', CountVectorizer(analyzer='word')), ('mnb', MultinomialNB())])
pip_tfidf = Pipeline([('tfidf_vec', TfidfVectorizer(analyzer='word')), ('mnb', MultinomialNB())])

# 分别配置用于模型超参数搜索的组合。
params_count = {'count_vec__binary': [True, False], 'count_vec__ngram_range': [(1, 1), (1, 2)], 'mnb__alpha': [0.1, 1.0, 10.0]}
params_tfidf = {'tfidf_vec__binary': [True, False], 'tfidf_vec__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_)

# 以最佳的超参数组合配置模型并对测试数据进行预测。
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_)

# 以最佳的超参数组合配置模型并对测试数据进行预测。
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('../Datasets/IMDB/submission_count.csv', index=False)
submission_tfidf.to_csv('../Datasets/IMDB/submission_tfidf.csv', index=False)

# 从本地读入未标记数据。
unlabeled_train = pd.read_csv('../Datasets/IMDB/unlabeledTrainData.tsv', delimiter='\t', quoting=3)

# 导入nltk。
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_sentence in raw_sentences:
        if len(raw_sentence) > 0:
            sentences.append(review_to_text(raw_sentence, False))
    return sentences


corpora = []
# 准备用于训练词向量的数据。
for review in unlabeled_train['review']:
    corpora += review_to_sentences(review.decode('utf8'), tokenizer)

# 配置训练词向量模型的超参数。
# Set values for various parameters
num_features = 300  # Word vector dimensionality
min_word_count = 20  # Minimum word count
num_workers = 4  # Number of threads to run in parallel
context = 10  # Context window size
downsampling = 1e-3  # Downsample setting for frequent words

# 从gensim.models中导入word2vec
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 = "../Datasets/IMDB/300features_20minwords_10context"

# 可以将词向量模型的训练结果长期保存于本地硬盘。
model.save(model_name)

# 直接读入已经训练好的词向量模型。
from gensim.models import Word2Vec

model = Word2Vec.load("../Datasets/IMDB/300features_20minwords_10context")

# 探查一下该词向量模型的训练效果。
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.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)

# 从sklearn.ensemble中导入GradientBoostingClassifier模型进行影评情感分析。
from sklearn.ensemble import GradientBoostingClassifier
# 从sklearn.grid_search中导入GridSearchCV用于超参数的网格搜索。
from sklearn.model_selection import GridSearchCV

gbc = GradientBoostingClassifier()

# 配置超参数的搜索组合。
params_gbc = {'n_estimators': [10, 100, 500], 'learning_rate': [0.01, 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_)

# 使用超参数调优后的梯度上升树模型进行预测。
result = gs.predict(testDataVecs)
output = pd.DataFrame( data={"id": test["id"], "sentiment": result})
output.to_csv("../Datasets/IMDB/submission_w2v.csv", index=False, quoting=3)

备注1:原来的导入模型from sklearn.grid_search import GridSearchCV的时候,提示错误:

from sklearn.grid_search import GridSearchCV
ModuleNotFoundError: No module named 'sklearn.grid_search'

需要替换grid_search:

from sklearn.model_selection import GridSearchCV

备注2:原来的会报错,错误提示为:

    corpora += review_to_sentences(review.decode('utf8'), tokenizer)
AttributeError: 'str' object has no attribute 'decode'

需要替换decode:

corpora += review_to_sentences(review.encode('utf8'), tokenizer)

5、提交结果

最后,按照上面代码所设定的输出,得到三个用于提交预测结果的文件,分别是:submission_count.csv、submission_tfidf.csv以及submission_w2v.csv;并且如图4-9所示,Kaggle竞赛平台的自动测评系统给出了上述3个提交文件的最终性能表现。其中使用TfidfVectorizer搭配朴素贝叶斯模型取得了最好的预测性能。


版权声明:本文为wyatt007原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。