本文概述
- 了解欺骗性意见垃圾邮件数据集
- 从路径中获取所有文本文件并从中提取标签并创建标签的数据框
- 合并审阅数据框和标签数据框
- 从”酒店评论”列中删除停用词
- 从酒店评论中提取词性, 并将其作为模型的特征输入
- 将数据分为80%的训练和20%的测试数据两部分
- 使用TfidfVectorizer对训练和测试数据进行向量化
- 实施模型
- 预测测试数据
- 绘制混淆矩阵, 准确性得分和分类报告以分析模型的性能
- 使用不同的随机状态测试模型
- 注意
在本教程中, 你将以文本数据集欺骗性意见垃圾邮件语料库为例。
简而言之, 你将在本教程中解决以下主题:
- 首先, 将向你简要介绍酒店评论数据集, 然后导入所有必需的模块,
- 然后, 你将开始理解数据的结构:从数据路径中获取所有文本文件, 从中提取标签, 创建标签和评论的数据框, 最后合并两个数据框,
- 然后, 你将对数据进行预处理:使用自然语言工具包库从酒店评论中删除停用词, 并为数据集中的每个单词提取词性。
- 对数据进行预处理之后, 就可以准备进行数据训练了:首先将数据分为训练和测试, 了解Tfidf, 向量化训练和测试数据, 实施支持向量机模型, 并保存两者:用于数据和模型的Tfidf矢量化器, 然后将其加载回。
- 最后, 你将对测试数据进行预测:绘制混淆矩阵, 准确性得分和混淆矩阵以检查模型的有效性, 还将通过将训练和测试数据划分为不同的随机状态来测试模型, 最后, 将使用两个在线Yelp评论, 并在它们上测试你的模型。
了解欺骗性意见垃圾邮件数据集
在继续加载数据之前, 最好先看看将要使用的数据!欺骗性意见垃圾邮件数据集是一个语料库, 由20个芝加哥酒店的真实和欺骗性酒店评论组成。根据评论的观点, 数据在两篇论文中进行了介绍。尤其是, 本文在1中讨论了积极的情绪评论, 在2中讨论了消极的情绪评论, 请随时参阅论文以获取更深入的知识。
语料库包含:
- 来自TripAdvisor的400条真实, 积极的评论
- 来自Mechanical Turk的400次欺骗性正面评价
- 400条来自Expedia, Hotels.com, Orbitz, Priceline, TripAdvisor和Yelp的真实, 负面评论
- 来自Mechanical Turk的400个欺骗性负面评论
你总共有1600条评论, 你的任务将是使用机器学习算法对真实和具有欺骗性的酒店评论进行分类。
所以, 让我们开始吧!
首先要导入所有必需的模块, 例如os, pandas, nltk, regex和最重要的Sklearn, 因为在今天的教程中, 你将使用Sklearn库提供的机器学习算法Support Vector Machine!
你将需要os来遍历文本文件所在的文件夹和子文件夹, fnmatch将仅用于过滤文本文件并忽略文件夹中存在的所有其他内容, pandas将帮助你将数据放入行形式和列, 你可以使用熊猫执行各种数据操作。正则表达式可帮助你根据你指定的匹配模式提取数据, 因此你也将需要它! Nltk是一种自然语言工具包, 它将帮助你删除你不希望模型学习的停用词。然后最重要的是, 你将导入Sklearn, 它将为你提供一堆库, 你将使用它们来处理数据, 向量化数据, 使用机器学习算法学习数据点之间的边界并最终绘制准确性。
import os
import fnmatch
from textblob import TextBlob
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from nltk.corpus import stopwords
from nltk import pos_tag, pos_tag_sents
import regex as re
import operator
from sklearn.svm import SVC, LinearSVC
from sklearn.metrics import classification_report, accuracy_score, confusion_matrix
from sklearn.cross_validation import train_test_split
from sklearn import metrics
from sklearn import svm
from sklearn.grid_search import GridSearchCV
import pickle
from nltk.corpus import stopwords
从路径中获取所有文本文件并从中提取标签并创建标签的数据框
图:数据结构
导入库后, 你的下一个任务是提取数据标签。标签将帮助模型区分评论, 无论特定评论属于真实类别还是欺骗性类别。
你将首先遍历所有文本文件, 并获取所有文本文件的绝对路径, 然后通过该绝对路径提取相应的标签。
path = 'op_spam_train/'
label = []
configfiles = [os.path.join(subdir, f)
for subdir, dirs, files in os.walk(path)
for f in fnmatch.filter(files, '*.txt')]
应该有1600个路径, 每个路径代表一个新的文本文件。让我们快速进行验证!
len(configfiles)
1600
让我们也打印文本文件路径之一。
configfiles[1]
'op_spam_train/negative_polarity/deceptive_from_MTurk/fold4/d_swissotel_14.txt'
因此, 从上面的输出中, 你可以观察到为了提取标签, 我们需要某种过滤器。为此, 你将使用正则表达式(也称为正则表达式)。
for f in configfiles:
c = re.search('(trut|deceptiv)\w', f)
label.append(c.group())
将标签提取到称为标签的列表中后, 让我们创建该列表的数据框。
labels = pd.DataFrame(label, columns = ['Labels'])
让我们打印标签数据框的前五行。
labels.head(5)
标签 | |
---|---|
0 | 欺骗性的 |
1 | 欺骗性的 |
2 | 欺骗性的 |
3 | 欺骗性的 |
4 | 欺骗性的 |
提取所有标签后, 就可以从文本文件中提取评论了!
review = []
directory =os.path.join("op_spam_train/")
for subdir, dirs , files in os.walk(directory):
# print (subdir)
for file in files:
if fnmatch.filter(files, '*.txt'):
f=open(os.path.join(subdir, file), 'r')
a = f.read()
review.append(a)
和以前一样, 你现在将创建审阅列表的数据框。
reviews = pd.DataFrame(review, columns = ['HotelReviews'])
让我们打印评论数据帧的前五行。
reviews.head(5)
酒店点评 | |
---|---|
0 | 放心吧, 这家酒店似乎很好, 但是我很想… |
1 | 我最近在芝加哥瑞士酒店住了… |
2 | 我对喜来登酒店感到非常失望。 |
3 | 我和我的家人住在芝加哥喜来登酒店… |
4 | 我最近住在嗨的霍姆伍德套房… |
大!因此, 到目前为止, 它还是很直观的。是不是
现在, 让我们合并标签和评论数据框!
合并审阅数据框和标签数据框
result = pd.merge(reviews, labels, right_index=True, left_index = True)
result['HotelReviews'] = result['HotelReviews'].map(lambda x: x.lower())
合并后, 你将在名称结果中得到一个新的数据框。让我们也打印此新数据框的几行。
result.head()
酒店点评 | 标签 | |
---|---|---|
0 | 同意, 这家酒店似乎很好, 但是我很想… | 欺骗性的 |
1 | 我最近在芝加哥瑞士酒店住了… | 欺骗性的 |
2 | 我对喜来登城堡感到非常失望。 | 欺骗性的 |
3 | 我的家人和我住在芝加哥喜来登酒店… | 欺骗性的 |
4 | 我最近住在嗨的霍姆伍德套房… | 欺骗性的 |
从”酒店评论”列中删除停用词
处理文本数据时, 删除停用词至关重要。停用词没有意义, 并且它们没有提供任何可帮助模型学习数据模式的信息。
你将在数据框中创建一个新列, 名称为review_without_stopwords。 lambda函数将在HotelReviews列的所有行上处理。你将使用列表推导, 仅将那些在stop变量中不存在的单词存储在新列中。
import ntlk
nltk.download('stopwords')
注意:为了执行以下单元格, 你可能需要在终端中运行以上代码行。
stop = stopwords.words('english')
result['review_without_stopwords'] = result['HotelReviews'].apply(lambda x: ' '.join([word for word in x.split() if word not in (stop)]))
让我们快速打印不带停用词的数据框!
result.head()
酒店点评 | 标签 | review_without_stopwords | |
---|---|---|---|
0 | 同意, 这家酒店似乎很好, 但是我很想… | 欺骗性的 | 授予它, 酒店似乎不错, 很高兴住在这里。 |
1 | 我最近在芝加哥瑞士酒店住了… | 欺骗性的 | 最近住过瑞士瑞士芝加哥丈夫两… |
2 | 我对喜来登城堡感到非常失望。 | 欺骗性的 | 希拉顿芝加哥感到非常失望。外面… |
3 | 我的家人和我住在芝加哥喜来登酒店… | 欺骗性的 | 一家人住在谢拉顿芝加哥酒店塔楼… |
4 | 我最近住在嗨的霍姆伍德套房… | 欺骗性的 | 芝加哥希尔顿惠庭套房酒店 |
从酒店评论中提取词性, 并将其作为模型的特征输入
根据第1条, 这表明诚实和欺骗性意见可能分别分为信息性和想象性体裁。信息性写作和想象性写作之间存在很大的分布差异, 即前者通常包含更多的名词, 形容词, 介词, 限定词和协调连词, 而后者则包含更多的动词, 副词, 代词和前置词。但是, 欺骗性意见包含更多的最高级并不是什么意外的事情, 因为欺骗性写作(但不一定是一般的想象性写作)通常包含夸大的语言。
因此, 现在让我们为数据帧行中的每个单词提取其各自的语音部分, 然后将其作为特征向量输入模型中。
对于pos标记, 你将使用TextBlob, 这是一个用于处理文本数据的Python库。它提供了一个简单的API, 可用于深入研究普通自然语言处理(NLP)任务, 例如词性标记, 名词短语提取, 情感分析, 分类, 翻译等。
注意:为了运行pos函数单元, 你可能需要在终端窗口中运行以下代码行。
import nltk
nltk.download('punkt')
nltk.download('averaged_perceptron_tagger')
def pos(review_without_stopwords):
return TextBlob(review_without_stopwords).tags
os = result.review_without_stopwords.apply(pos)
os1 = pd.DataFrame(os)
os1数据帧的每一行将包含单词列表以及它们各自的语音部分。你将无法向量化将要输入模型的列表。因此, 你必须将列表的这些行转换为字符串。
os1.head()
review_without_stopwords | |
---|---|
0 | [(grant, NN), (it, PRP), (hotel, NN), (似乎, … |
1 | [(最近是RB), (保持为VBN), (swissotel, NN … |
2 | [(过去, RB, (失望, JJ), (谢拉顿, … |
3 | [(family, NN), (stayed, VBD), (sheraton, JJ), … |
4 | [(最近是RB), (固定是VBN), (霍姆伍德, NN)… |
让我们将每一行转换成一个字符串, 其中每个单词将使用正斜杠与对应的pos相连, 并且用一个空格分隔单词。
os1['pos'] = os1['review_without_stopwords'].map(lambda x:" ".join(["/".join(x) for x in x ]) )
最后, 让我们将pos列与主要结果数据框合并, 并打印它的前几行!
result = result = pd.merge(result, os1, right_index=True, left_index = True)
result.head()
将数据分为80%的训练和20%的测试数据两部分
提取特征后, 现在就可以将数据分为训练和测试了。对于本教程, 你将把80%的数据分为训练, 其余20%的数据分为测试。
review_train, review_test, label_train, label_test = train_test_split(result['pos'], result['Labels'], test_size=0.2, random_state=13)
使用TfidfVectorizer对训练和测试数据进行向量化
让我们详细了解TfidfVectorizer
Tfidf有两个部分:
术语频率(Tf):一个单词在一个文档中出现多少次。为了更好地理解它, 让我们用一个词:” the”这个词很常见, 在你所有的文档中都会频繁出现。但是, 如果你考虑一下, ” the”不会提供有关文档的任何其他信息。但是, 如果我们谈论” messi”一词, 它将为我们提供上下文。我们可以肯定地说文件可能是关于足球的。因此, 我们需要以某种方式减轻” the”的重量。现在你可能会说, 你可以对它们各自的频率进行倒数运算, 以使” messi”更具分量。好吧, 关闭, 但是有一个陷阱。我们的文档中可能存在非常罕见的单词。例如:” floccinaucinihilipilification”。这是一个真实的词, 意为:”估计某物毫无价值的行为或习惯。”现在, 如果你对这个词使用倒数, 它肯定会接近于1, 但不会再告诉你上下文。因此, (以数字为例)tf(” the”)= 100 tf(” messi”)= 5 tf(” floccinaucinihilipilification”)= 1
逆文档频率(idf):通过添加此术语可以解决上述常见单词和稀有单词的问题。那是什么它是通过将{你的语料库中的文档数除以出现该术语的文档数}的对数计算得出的。因此, 对于” the”, 该比率将接近1。因此, 对数会将其取为0。idf(” the”)= 0 idf(” messi”)= log(10/3)= 0.52(自语料库起)关于足球)idf(” floccinaucinihilipilification”)= log(10/1)= 1
最后一步:现在, 我们希望赋予” messi”的权重应高于其他两个词。为此, 我们将两个项相乘:tfidf = tf x idf” the” = 100 x 0 = 0, ” floccinaucinihilipilification” = 1 x 1 = 1, ” messi” = 5 x 0.52 = 2.6
加权技术可在分类过程中帮助机器学习(ML)模型, 因为我们明确指出了哪个单词的权重更高/更低。
现在, 你已经知道了, 让我们在培训和测试数据上实施它。
tf_vect = TfidfVectorizer(lowercase = True, use_idf=True, smooth_idf=True, sublinear_tf=False)
X_train_tf = tf_vect.fit_transform(review_train)
X_test_tf = tf_vect.transform(review_test)
实施模型
现在, 你将实现一种称为支持向量机(SVM)的机器学习模型。为了理解它, 你可以点击此链接。
要为你的ML算法选择最佳的超参数, 你将使用GridSearchCV, 它会根据你的训练数据和标签为你指定列表中指定的值之外的最佳超参数值。你将根据数据选择五个不同的Cs和gamma值。你将获得最佳的超参数值。
def svc_param_selection(X, y, nfolds):
Cs = [0.001, 0.01, 0.1, 1, 10]
gammas = [0.001, 0.01, 0.1, 1]
param_grid = {'C': Cs, 'gamma' : gammas}
grid_search = GridSearchCV(svm.SVC(kernel='linear'), param_grid, cv=nfolds)
grid_search.fit(X, y)
return grid_search.best_params_
svc_param_selection(X_train_tf, label_train, 5)
{'C': 10, 'gamma': 0.001}
clf = svm.SVC(C=10, gamma=0.001, kernel='linear')
clf.fit(X_train_tf, label_train)
pred = clf.predict(X_test_tf)
保存Tfidf矢量化器和ML模型
让我们使用你刚开始导入的pickle库将刚刚训练的模型与Tfidf矢量化器一起保存, 以便以后可以只加载数据, 矢量化并使用ML模型进行预测。
with open('vectorizer.pickle', 'wb') as fin:
pickle.dump(tf_vect, fin)
with open('mlmodel.pickle', 'wb') as f:
pickle.dump(clf, f)
加载Tfidf矢量化器和ML模型
pkl = open('mlmodel.pickle', 'rb')
clf = pickle.load(pkl)
vec = open('vectorizer.pickle', 'rb')
tf_vect = pickle.load(vec)
/home/naveksha/.local/lib/python3.5/site-packages/sklearn/base.py:311: UserWarning: Trying to unpickle estimator SVC from version pre-0.18 when using version 0.19.1. This might lead to breaking code or invalid results. Use at your own risk.
UserWarning)
/home/naveksha/.local/lib/python3.5/site-packages/sklearn/base.py:311: UserWarning: Trying to unpickle estimator TfidfTransformer from version pre-0.18 when using version 0.19.1. This might lead to breaking code or invalid results. Use at your own risk.
UserWarning)
/home/naveksha/.local/lib/python3.5/site-packages/sklearn/base.py:311: UserWarning: Trying to unpickle estimator TfidfVectorizer from version pre-0.18 when using version 0.19.1. This might lead to breaking code or invalid results. Use at your own risk.
UserWarning)
预测测试数据
X_test_tf = tf_vect.transform(review_test)
pred = clf.predict(X_test_tf)
绘制混淆矩阵, 准确性得分和分类报告以分析模型的性能
print(metrics.accuracy_score(label_test, pred))
0.96875
print (confusion_matrix(label_test, pred))
[[149 6]
[ 4 161]]
上面的混淆矩阵是一个2 x 2矩阵, 其中, 第一行的第一元素是”真正”(TP), 第一行的第二元素是”假负”(FN), 第二行的第一元素是”假正”(FP), 第二行的第一元素是”假正”是真负数(TN)。
从上面的混淆矩阵中, 你可以得出320个样本中只有10个被错误分类的结论。
print (classification_report(label_test, pred))
precision recall f1-score support
deceptive 0.97 0.96 0.97 155
truth 0.96 0.98 0.97 165
avg / total 0.97 0.97 0.97 320
使用不同的随机状态测试模型
- 随机状态1
review_train, review_test, label_train, label_test = train_test_split(result['pos'], result['Labels'], test_size=0.2, random_state=1)
X_test_tf = tf_vect.transform(review_test)
pred = clf.predict(X_test_tf)
print(metrics.accuracy_score(label_test, pred))
0.978125
print (confusion_matrix(label_test, pred))
[[146 5]
[ 2 167]]
print (classification_report(label_test, pred))
precision recall f1-score support
deceptive 0.99 0.97 0.98 151
truth 0.97 0.99 0.98 169
avg / total 0.98 0.98 0.98 320
随机状态10
review_train, review_test, label_train, label_test = train_test_split(result['pos'], result['Labels'], test_size=0.2, random_state=10)
X_test_tf = tf_vect.transform(review_test)
pred = clf.predict(X_test_tf)
print(metrics.accuracy_score(label_test, pred))
0.98125
print (confusion_matrix(label_test, pred))
[[155 5]
[ 1 159]]
print (classification_report(label_test, pred))
precision recall f1-score support
deceptive 0.99 0.97 0.98 160
truth 0.97 0.99 0.98 160
avg / total 0.98 0.98 0.98 320
随机状态42
review_train, review_test, label_train, label_test = train_test_split(result['pos'], result['Labels'], test_size=0.2, random_state=42)
X_test_tf = tf_vect.transform(review_test)
pred = clf.predict(X_test_tf)
print(metrics.accuracy_score(label_test, pred))
0.984375
print (confusion_matrix(label_test, pred))
[[165 3]
[ 2 150]]
print (classification_report(label_test, pred))
precision recall f1-score support
deceptive 0.99 0.98 0.99 168
truth 0.98 0.99 0.98 152
avg / total 0.98 0.98 0.98 320
从以上预测中, 你可以观察到该模型完成了出色的工作, 并且没有过拟合, 因为你通过每次对数据进行不同的拆分多次测试了该模型。
用Yelp的两个评论测试模型
def test_string(s):
X_test_tf = tf_vect.transform([s])
y_predict = clf.predict(X_test_tf)
return y_predict
test_string("The hotel was good.The room had a 27-inch Samsung led tv, a microwave.The room had a double bed")
array(['truth'], dtype=object)
test_string("My family and I are huge fans of this place. The staff is super nice, and the food is great. The chicken is very good, and the garlic sauce is perfect. Ice cream topped with fruit is delicious too. Highly recommended!")
array(['deceptive'], dtype=object)
好了, 该模型正确地预测了以上两个评论。第一个评论是真实的评论, 而第二个评论是欺骗性的!
注意
当在具有不同规格的不同处理系统上实施时, 模型的性能会有所不同, 这是观察到的异常行为。如果社区可以帮助解决这个问题, 那么它将对包括本文作者在内的许多人有所帮助。对于社区而言, 这意味着将阅读本教程的读者!
学习愉快!
希望本教程确实对你有所帮助, 并为你的技能设置增加一些价值。
如果你想了解有关机器学习的更多信息, 请参加srcmini的Building Chatbots in Python课程。
评论前必须登录!
注册