TowardsDataScience 博客中文翻译 2020(七百八十三)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

基于 TextBlob 的远程学习微博情感分析

原文:https://towardsdatascience.com/sentiment-analysis-on-the-tweets-about-distance-learning-with-textblob-cc73702b48bc?source=collection_archive---------14-----------------------

人们对远程学习有什么看法?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

故事横幅,作者图片

大家好,

Covid19 疫情在 2020 学年引入了远程学习。虽然有些人可以很容易适应,但有些人发现这种方法效率很低。如今,正在讨论重新开放学校的问题。大部分专家建议至少一个学期再上线。作为一名通过远程学习度过上学期的学生,我可以找到很多时间花在学习自然语言处理上。最后,我决定探究一下人们对远程学习的看法。

我正在计划这个故事作为一个端到端的项目。我们将探索与远程学习相关的推文,以了解人们的意见(也称为意见挖掘)并发现事实。我将使用基于词典的方法来确定推文的极性(我稍后会解释)。TextBlob 将是我们实现这一目标的工具。我们还将建立一个机器学习模型,通过使用伯努利 朴素贝叶斯分类器来预测推文的正面和负面。

我们的工作流程如下:

  1. 数据收集
    • Twitter API
      -使用 tweepy 检索推文
  2. 预处理和清理
    -删除重复项
    -数据类型转换
    -删除无信息列
    -去掉停用词、标签、标点和一两个字母的单词
    -对单词进行标记
    -应用词条化
    -词频-逆文档频率矢量化
  3. 探索性数据分析
    -可视化数据
    -比较字数
    -调查创建次数分布
    -调查推文位置
    -查看热门推文和最常用词
    -制作词云
  4. 情感分析
  5. 机器学习
  6. 摘要

要求

开始之前,请确保以下库在您的工作区中可用。

pandas
numpy
matplotlib
seaborn
TextBlob
wordcloud
sklearn
nltk
pickle

您可以使用以下命令安装非内置库。

pip install pycountry
pip install nltk
pip install textblob
pip install wordcloud
pip install scikit-learn
pip install pickle

你可以在这里找到完整的代码。

1.数据采集

首先,我们需要一个 Twitter 开发者账号 才能被允许使用 Twitter API 。你可以在这里得到账号。审批可能需要几天时间。我已经完成了这些步骤。一旦我得到了这个帐户,我就创建了一个包含 API 信息的文本文件。它位于项目的向上目录中。文本文件的内容如下。如果你想使用它,你必须用你的信息替换它。

CONSUMER KEY=your_consumer_key
CONSUMER KEY SECRET=your_consumer_key_secret
ACCESS TOKEN=your_access_token
ACCESS TOKEN SECRET=your_access_token_secret

之后我创建了一个名为*get _ tweets . py的 py 文件来收集远程学习相关的推文(只有英文)。您可以在下面看到完整的代码。*

上面的代码搜索包含以下标签的推文

#远程教育,#在线学校,#在线教学,#虚拟学习,#在线教育,#远程教育,#在线课堂,#数字学习,#电子学习,#在线学习

和以下关键字

“远程学习”、“在线教学”、“在线教育”、“在线课程”、“在线学期”、“远程课程”、“远程教育”、“在线课堂”、“电子学习”、“电子学习”

它还过滤转发以避免重复。

get_tweets 函数将检索到的 tweets 存储在临时 pandas 数据帧中,并在输出目录中保存为 CSV 文件。大约花了 40 个小时收集了 202.645 条推文。之后,它给了我以下文件

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

输出文件,按作者分类的图像

为了将所有 CSV 文件连接成一个文件,我创建了包含以下代码的 concatenate.py 文件。

最终,我们有了 tweets_raw.csv 文件我们来看看它是什么样子的。

*# Load the tweets
tweets_raw = pd.read_csv("tweets_raw.csv")# Display the first five rows
display(tweets_raw.head())# Print the summary statistics
print(tweets_raw.describe())# Print the info
print(tweets_raw.info())*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传**外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

乍一看,我们可以看到 DataFrame 中有 202.645 条推文,包括内容、位置、用户名、转发次数、收藏夹数、创建时间特征。位置栏也有一些缺失值。我们将在下一步处理它们。

2.预处理和清洗

根据以上信息,未命名:0未命名:0.1* 列对我们来说没有任何信息,因此我们将删除它们。在列创建的*的数据类型也应该是 datetime。同样,如果有重复的推文,我们也需要删除它们。**

*# We do not need first two columns. Let's drop them out.
tweets_raw.drop(columns=["Unnamed: 0", "Unnamed: 0.1"], axis=1, inplace=True)# Drop duplicated rows
tweets_raw.drop_duplicates(inplace=True)# Created at column's type should be datatime
tweets_raw["Created at"] = pd.to_datetime(tweets_raw["Created at"])# Print the info again
print(tweets_raw.info())*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

tweets 计数已经减少到 187.052 (有 15.593 个重复行)。“创建于”列的数据类型也更改为数据时间 64【ns】

现在,让我们整理一下推文的内容。我们需要去掉停用词*、标点符号标签提及链接一两个字母的单词。我们还需要对推文进行标记。*

标记化就是把一个句子拆分成单词和标点符号。句子“这是一个例子。”可以像[“这个”、“是”、“一个”、“例”、“等”这样进行标记化。”]

停用词是常用的词,它们对句子的意义没有贡献,如“一个”、“一个”、“这个”、“在”、“在”等等。

词汇化 就是把一个单词还原成它的词根形式的过程。这个根形式叫做一个 引理 。比如单词running*runrun*的引理就是 run

让我们定义一个函数来完成所有这些操作。

函数调用后,我们的处理后的列将如下所示。你可以看到推文被标记化了,它们不包含停用词、标签、链接和一两个字母的单词。我们还对它们进行了引理化操作。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们得到了我们想要的。不用担心学习*、在线教育等词汇。我们稍后会处理它们。*

在探索性数据分析中,推文长度和推文中的字数也可能是有趣的。让我们抓住他们!

*# Get the tweet lengths
tweets_raw["Length"] = tweets_raw["Content"].str.len()# Get the number of words in tweets
tweets_raw["Words"] = tweets_raw["Content"].str.split().str.len()# Display the new columns
display(tweets_raw[["Length", "Words"]])*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意,我们没有使用经过处理的* tweets。*

地点呢?

当我们调用 tweets_raw DataFrame info 函数时,我们看到在“Location”列中有一些缺失值。缺失的值显示为 NaN 。我们将用“未知”标签来填充丢失的值。

*# Fill the missing values with unknown tag
tweets_raw["Location"].fillna("unknown", inplace=True)*

我们有多少独特的位置?

*# Print the unique locations and number of unique locations
print("Unique Values:",tweets_raw["Location"].unique())
print("Unique Value count:",len(tweets_raw["Location"].unique()))*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

输出显示位置信息是混乱的。有 37.119 个唯一位置。我们需要按国家对它们进行分组。为了实现这一点,我们将使用 python 中的 pycountry 包。如果你有兴趣,你可以在这里找到进一步的信息

让我们定义一个名为 get_countries 的函数,它返回给定位置的国家代码。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

成功了!现在我们有 156 个独特的国家代码。我们将在探索性数据分析部分使用它们。

现在是时候对推文进行矢量化了。我们将使用 tf-idf(词频-逆文档词频) 矢量化。

Tf-idf(词频—逆词频)**是一个统计概念,用于获取语料库中的词频。我们将使用 scikit-learn 的tfidf 矢量器。矢量器将计算语料库中每个单词的权重,并返回一个 tf-idf 矩阵。您可以在此找到更多信息

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

td =词频(j 中每个 I 出现的次数)
df =文档频率
N =文档数量
w = tf-idf 对每个 ij (文档)的权重。

由于内存限制,我们将只选择前 5000 个单词进行 tf-idf 矢量化。您可以通过使用其他方法,如散列来试验更多。

*# Create our contextual stop words
tfidf_stops = ["online","class","course","learning","learn",\
"teach","teaching","distance","distancelearning","education",\
"teacher","student","grade","classes","computer","onlineeducation",\ "onlinelearning", "school", "students","class","virtual","eschool",\ "virtuallearning", "educated", "educates", "teaches", "studies",\ "study", "semester", "elearning","teachers", "lecturer", "lecture",\ "amp","academic", "admission", "academician", "account", "action" \
"add", "app", "announcement", "application", "adult", "classroom", "system", "video", "essay", "homework","work","assignment","paper",\ "get", "math", "project", "science", "physics", "lesson","courses",\ "assignments", "know", "instruction","email", "discussion","home",\ "college","exam""use","fall","term","proposal","one","review",\
"proposal", "calculus", "search", "research", "algebra"]# Initialize a Tf-idf Vectorizer
vectorizer = TfidfVectorizer(max_features=5000, stop_words= tfidf_stops)# Fit and transform the vectorizer
tfidf_matrix = vectorizer.fit_transform(tweets_processed["Processed"])# Let's see what we have
display(tfidf_matrix)# Create a DataFrame for tf-idf vectors and display the first rows
tfidf_df = pd.DataFrame(tfidf_matrix.toarray(), columns= vectorizer.get_feature_names())
display(tfidf_df.head())*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

它返回给我们一个稀疏矩阵。你可以看看它下面的内容。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

毕竟,我们将新的数据帧保存为 CSV 文件,以便以后使用,而无需再次执行整个操作。

*# Save the processed data as a csv file
tweets_raw.to_csv("tweets_processed.csv")*

3.探索性数据分析

探索性数据分析是数据科学项目不可或缺的一部分。只要我们理解我们的数据告诉我们什么,我们就可以建立我们的模型。

*# Load the processed DataFrame
tweets_processed = pd.read_csv("tweets_processed.csv", parse_dates=["Created at"])*

首先,让我们看看数据集中最早和最新的 tweets 创建时间。

*# Print the minimum datetime
print("Since:",tweets_processed["Created at"].min())# Print the maximum datetime
print("Until",tweets_processed["Created at"].max())*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这些推文是在 2020 年 7 月 23 日至 8 月 14 日之间创建的。创作时间呢?

*# Set the seaborn style
sns.set()# Plot the histogram of hours
sns.distplot(tweets_processed["Created at"].dt.hour, bins=24)
plt.title("Hourly Distribution of Tweets")
plt.show()*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

直方图表明,大多数推文是在一天的 12 点至 17 点之间创建的。最受欢迎的时间是下午 15 点左右。

让我们看看我们已经处理过的位置。

*# Print the value counts of Country column
print(tweets_processed["Country"].value_counts())*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

显然,这些位置对我们来说是无信息的,因为我们有 169.734 个未知的 位置。但我们仍然可以查看最热门的推特国家。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

根据上面的柱状图, 美国英国印度 是我们数据集中排名前 3 的国家。

现在,让我们来看看最受欢迎的推文(就转发和收藏而言)。

*# Display the most popular tweets
display(tweets_processed.sort_values(by=["Favorites","Retweet-Count", ], axis=0, ascending=False)[["Content","Retweet-Count","Favorites"]].head(20))*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

热门推文,点击图片看更好

推文中的常用词也能告诉我们很多。让我们从我们的 Tf-idf 矩阵中获取它们。

*# Create a new DataFrame called frequencies
frequencies = pd.DataFrame(tfidf_matrix.sum(axis=0).T,index=vectorizer.get_feature_names(),columns=['total frequency'])# Display the most 20 frequent words
display(frequencies.sort_values(by='total frequency',ascending=False).head(20))*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

文字云会更好。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

文字云,作者图片

显然,人们谈论的是*【付款】“求助”是使用频率最高的词之一。我们可以说人们正在大量寻求帮助:)***

4。情绪分析

经过预处理和 EDA,我们终于可以专注于我们在这个项目的主要目标。我们将使用 TextBlob 来计算推文的情感特征,如 极性主观性 。它通过使用预定义的单词分数给我们这些值。您可以查看文档了解更多信息。

极性 是在 -1 1 之间变化的一个值。它向我们展示了给出的句子是还是负*。***

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

主观性0 到 1 之间的另一个值变化,它向我们表明句子是关于一个事实还是观点(客观还是主观)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

让我们用 TextBlob 计算极性和主观性得分

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们需要将极性分为积极的、中性的和消极的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们也可以像下面这样把它们数起来。

***# Print the value counts of the Label column
print(tweets_processed["Label"].value_counts())***

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结果和我预料的不一样。正面推文明显比负面多。

到目前为止,我们将推文标记为积极、中立和消极。让我们仔细检查一下我们的发现。我将从标签数开始。

***# Change the datatype as "category"
tweets_processed["Label"] = tweets_processed["Label"].astype("category")# Visualize the Label counts
sns.countplot(tweets_processed["Label"])
plt.title("Label Counts")
plt.show()# Visualize the Polarity scores
plt.figure(figsize = (10, 10)) 
sns.scatterplot(x="Polarity", y="Subjectivity", hue="Label", data=tweets_processed)
plt.title("Subjectivity vs Polarity")
plt.show()***

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由于基于词典的分析并不总是可靠的,我们必须手动检查结果。让我们来看看极性得分最高/最低的热门(根据转发和收藏)推文。

***# Display the positive tweets
display(tweets_processed.sort_values(by=["Polarity", "Retweet-Count", "Favorites"], axis=0, ascending=False)[["Content","Retweet-Count","Favorites","Polarity"]].head(20))# Display the negative tweets
display(tweets_processed.sort_values(by=["Polarity", "Retweet-Count", "Favorites"], axis=0, ascending=[True, False, False])[["Content","Retweet-Count","Favorites","Polarity"]].head(20))***

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正面推文,点击图片看更好

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

负面推文,,点击图片看得更清楚

根据上面的结果,TextBlob 已经正确地完成了它的工作!我们可以像上面那样为每个标签制作单词云。为此,我将定义一个函数。该函数将以一个数据帧和一个标签作为参数,用 tf-idf 矢量器对 处理过的 tweets 进行矢量化。最后,它会为我们制作单词云。由于计算的限制,我们将只查看最受欢迎的 50 条推文。你可以用更多的数据来尝试。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

很明显,推特上负面消息的人发现远程学习很无聊,很可怕,很糟糕。另一方面,有些人喜欢远程学习的选择。

让我们来看看各国的正面和负面推文数量。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

时间和推特的极性有什么关系吗?

***positive = tweets_processed.loc[tweets_processed.Label=="Positive"]["Created at"].dt.hour
negative = tweets_processed.loc[tweets_processed.Label=="Negative"]["Created at"].dt.hourplt.hist(positive, alpha=0.5, bins=24, label="Positive", density=True)
plt.hist(negative, alpha=0.5, bins=24, label="Negative", density=True)
plt.xlabel("Hour")
plt.ylabel("PDF")
plt.title("Hourly Distribution of Tweets")
plt.legend(loc='upper right')
plt.show()***

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上面的直方图表明,时间和推文的极性之间没有关系。

我想在这里结束我的探索,以保持这个故事简短。

5.建立一个机器学习模型

我们根据极性分数给推文贴上了标签。让我们通过使用多项式朴素贝叶斯分类器来建立机器学习模型。我们将使用 tf-idf 向量作为特征,标签作为目标。

***# Encode the labels
le = LabelEncoder()
tweets_processed["Label_enc"] = le.fit_transform(tweets_processed["Label"])# Display the encoded labels
display(tweets_processed[["Label_enc"]].head())***

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们已经对标签进行了编码。

“正”= 2

“空档”= 1

“负”= 0

***# Select the features and the target
X = tweets_processed['Processed']
y = tweets_processed["Label_enc"]***

现在,我们需要将数据分成训练集和测试集。由于我们的数据不平衡,我们将使用 train_test_split分层 参数。

***X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=34, stratify=y)***

现在,我们可以创建我们的模型。由于我们早期的 tf-idf 矢量器适合整个数据集,我们必须初始化一个新的。否则,我们的模型可以通过测试集进行学习。

***# Create the tf-idf vectorizer
model_vectorizer = TfidfVectorizer()# First fit the vectorizer with our training set
tfidf_train = vectorizer.fit_transform(X_train)# Now we can fit our test data with the same vectorizer
tfidf_test = vectorizer.transform(X_test)# Initialize the Bernoulli Naive Bayes classifier
nb = BernoulliNB()# Fit the model
nb.fit(tfidf_train, y_train)# Print the accuracy score
best_accuracy = cross_val_score(nb, tfidf_test, y_test, cv=10, scoring='accuracy').max()
print("Accuracy:",best_accuracy)***

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

虽然我们没有做任何超参数调优,但是精度还不错。我们来看看混淆矩阵和分类报告。

***# Predict the labels
y_pred = nb.predict(tfidf_test)# Print the Confusion Matrix
cm = confusion_matrix(y_test, y_pred)
print("Confusion Matrix\n")
print(cm)# Print the Classification Report
cr = classification_report(y_test, y_pred)
print("\n\nClassification Report\n")
print(cr)***

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

还有很多工作要做,以提高模型在负面推文中的表现,但我把它留给另一个故事:)

最后,我们可以保存模型以备后用。

***# Save the model
pickle.dump(nb, open("model.pkl", 'wb'))***

摘要

综上,让我们记住我们一起做的事。首先,我们使用 Twitter API 和 tweepy 库收集了关于远程学习的推文。之后,我们对它们应用了常见的预处理步骤,比如标记化、词条化、删除停用词等等。我们通过使用汇总统计和可视化工具来研究数据。毕竟,我们使用 TextBlob 来获得推文的极性分数,并解释了我们的发现。因此,我们发现,在我们的数据集中,大多数推文对远程学习持积极态度。不要忘记,我们只使用了基于词典的方法,这是不太可靠的。希望这个故事对你理解推文的情感分析有所帮助。

参考

【1】(教程)在 Python 中简化情感分析。(未注明)。数据营社区。https://www . data camp . com/community/tutorials/simplizing-情操-分析-python**

****【2】李,J. (2020 年 5 月 19 日)。 Twitter 情感分析| NLP |文本分析。中等。https://towards data science . com/Twitter-情操-分析-NLP-文本-分析-b7b296d71fce

****【3】李,c .(2019 . 9 . 20)。用于品牌改进和话题跟踪的实时推特情感分析(第 1/3 章)。中等。https://towards data science . com/real-time-Twitter-opinion-analysis-for-brand-improvement-and-topic-tracking-chapter-1-3-e02f 7652 D8 ff

****【4】randers 112358。(2020 年 7 月 18 日)。如何用 Python 对一个 Twitter 账号做情感分析。中等。https://medium . com/better-programming/Twitter-情操-分析-15d8892c0082

****【5】Python 中的词干化和词条化。(未注明)。数据营社区。https://www . data camp . com/community/tutorials/stemming-lemma tization-python

关于 2020 年美国大选的推特数据情感分析

原文:https://towardsdatascience.com/sentiment-analysis-on-twitter-data-regarding-2020-us-elections-1de4bedbe866?source=collection_archive---------11-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

马库斯·温克勒在 Unsplash 上的照片

据热门科技网站 GeeksforGeeks 称,情感分析是通过“计算”来确定一篇文章是正面、负面还是中性的过程。这也被称为意见挖掘,得出一个发言者的意见或态度。

情绪分析被许多数据分析公司用于各种主题。使用它的一些受欢迎的市场有:

  • 业务:许多公司的营销团队使用它来制定业务战略,了解客户如何看待他们的产品,并了解客户的行为,以提高销售额。
  • 政治:在政治领域,它用于跟踪候选人有利的地区,并致力于候选人不利的地区,以提高他们在选举中的机会。

美国总统选举定于 11 月 3 日举行,还剩不到一周的时间,这是了解美国不同州对候选人——共和党现任总统唐纳德·特朗普和民主党挑战者乔·拜登——的公众情绪和看法的好时机。

为了进行这项分析,我收集了过去一周使用 Twitter 的公开 API 发布的推文,并使用 VADER 情绪分析对这些推文进行了情绪分析。

数据争论

为了使用 Twitter api,我们需要一个 Twitter 开发人员帐户,该帐户允许我们访问访问令牌、访问令牌秘密、API 密钥和 API 秘密密钥。我在一个属性文件中记录了这些参数,这个文件放在我的一个驱动器文件夹中。我使用 python 的 configparser 来读取这些数据,以便使用 twitter API。

import configparser
import os
import tweepy as tw
import pandas as pdconfig = configparser.RawConfigParser()
config.read('/content/drive/My Drive/Colab Notebooks/twitter.properties')accesstoken = config.get('twitter','accesstoken')
accesstokensecret = config.get('twitter','accesstokensecret')
apikey = config.get('twitter','apikey')
apisecretkey = config.get('twitter','apisecretkey')auth = tw.OAuthHandler(apikey, apisecretkey)
auth.set_access_token(accesstoken, accesstokensecret)
api = tw.API(auth, wait_on_rate_limit=True)search_words = "Joe Biden" *# This will be changed to Donald Trump when we retrieve tweets related to him.*
date_since = "2020-10-27"

Twitter 在他们的 API 开发中大量使用分页。为了执行分页,Twitter 为每个请求提供了一个页面/光标参数。因此,这需要大量的 boiler plate 代码来管理分页循环。为了使分页更容易,需要的代码更少,Twitter API 或 Tweepy 使用了 Cursor 对象。

现在,由于游标是通过 callable 传递的,所以我们不能将参数直接传递给方法。相反,参数被传递到游标构造函数方法中。对于搜索 tweets 的 api,我们传递以下参数:

  1. 问:这是推文中要搜索的关键词。对于我们的项目,我们传递候选人的名字(Donald Trump/Joe Biden)。
  2. lang:这是我们想要从 API 中检索的 tweets 的语言。由于美国很大程度上是一个英语国家,英语也是官方语言,我们检索用英语制作的推文。
  3. 因为:这是我们想要检索推文的日期。出于我们的目的,我们对当前的政治对话感兴趣,以了解选举前的当前情绪。所以我们通过了上周二的法案,也就是选举前一周。

另一个需要记住的因素是 tweepy 搜索 api 一次最多检索 1500 条推文,然后有 15 分钟的冷却时间。

tweets = tw.Cursor(api.search, q=search_words, lang="en", since=date_since).items(100)
tweets

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

搜索 api 调用的结果是一个迭代器对象。

搜索 api 调用的结果是一个游标项迭代器对象。从这个迭代器对象中,我们迭代获取地理、文本、用户名和位置细节。一些细节如地理和位置细节取决于特定用户是否共享这些细节。如果用户不共享这些详细信息,我们将获得这些列的空白数据。

然后,我们将列表转换成熊猫数据帧。

tweet_details = [[tweet.geo, tweet.text, tweet.user.screen_name, tweet.user.location] for tweet in tweets]
tweet_df = pd.DataFrame(data=tweet_details, columns=["geo","text","user","location"])
tweet_df.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

查看 tweet 文本,我们注意到有许多字符对情感分析没有任何价值。许多推文前面都有 RT,这表明推文被转发了。推文前面还有转发推文的用户的名字,这也是我们不关心的信息。我们还删除了任何可能出现在推文中的 html 网站链接。所有这些数据清理都是在 Python 的“重新”内置包的帮助下完成的。

import re
def clean_tweets(text):
  text = re.sub("RT @[\w]*:","",text)
  text = re.sub("@[\w]*","",text)
  text = re.sub("https?://[A-Za-z0-9./]*","",text)
  text = re.sub("\n","",text)
  return texttweet_df['text'] = tweet_df['text'].apply(lambda x: clean_tweets(x))
tweet_df['Subject'] = search_words
tweet_df.head(20)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这个分析中,我们最关心的是从 twitter 数据中找出当前美国每个州的政治情绪。为了做到这一点,我们需要对从 tweepy API 中提取的原始数据进行更多的过滤。正如我们之前所讨论的,tweepy API 为我们提供了发布推文的用户的位置,前提是他/她与我们共享这些信息。这个位置数据大部分是空白的。对于其他人,它可能只包含国家名称、城市名称或用逗号分隔的城市和州,等等。

在下一部分中,我们形成一个美国各州的列表和另一个美国各州代码的列表,以便提取位置记录中包含这些代码的 tweets。

P.S .这是一种非常幼稚的净化方式。一个更好的方法是使用 Google Maps API,从位置数据中获取州信息,前提是它在美国。然而,由于我的谷歌地图付费账户的问题,我不得不与这种幼稚的方式作斗争。

states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New York', 'New Mexico', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming']stateCodes = ['AL', 'AK', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DE', 'FL', 'GA', 'HI', 'ID', 'IL', 'IN', 'IA', 'KS', 'KY', 'LA', 'ME', 'MD', 'MA', 'MI', 'MN', 'MS', 'MO', 'MT', 'NE', 'NV', 'NH', 'NJ', 'NM', 'NY', 'NC', 'ND', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VT', 'VA', 'WA', 'WV', 'WI', 'WY']stateMapping = {'AL': 'Alabama', 'AK': 'Alaska', 'AZ': 'Arizona', 'AR': 'Arkansas', 'CA': 'California', 'CO': 'Colorado', 'CT': 'Connecticut', 'DE': 'Delaware', 'FL': 'Florida', 'GA': 'Georgia', 
                  'HI': 'Hawaii', 'ID': 'Idaho', 'IL': 'Illinois', 'IN': 'Indiana', 'IA': 'Iowa', 'KS': 'Kansas', 'KY': 'Kentucky', 'LA': 'Louisiana', 'ME': 'Maine', 'MD': 'Maryland', 'MA': 'Massachusetts', 'MI': 'Michigan', 'MN': 'Minnesota', 'MS': 'Mississippi', 'MO': 'Missouri', 'MT': 'Montana', 'NE': 'Nebraska', 'NV': 'Nevada', 'NH': 'New Hampshire', 'NJ': 'New Jersey', 'NY': 'New York', 'NM': 'New Mexico', 'NC': 'North Carolina', 'ND': 'North Dakota', 'OH': 'Ohio', 'OK': 'Oklahoma', 'OR': 'Oregon', 'PA': 'Pennsylvania', 'RI': 'Rhode Island', 'SC': 'South Carolina', 'SD': 'South Dakota', 'TN': 'Tennessee', 'TX': 'Texas', 'UT':  'Utah', 'VT': 'Vermont', 'VA': 'Virginia', 'WA': 'Washington', 'WV':  'West Virginia', 'WI': 'Wisconsin', 'WY': 'Wyoming'}tweet_copied_df = tweet_df
for index, row in tweet_df.iterrows():
  flag = 0
  if row.location:
    locationSplit = row.location.split(',')
    for word in locationSplit:
      word_stripped = word.strip()
      if word_stripped in states:
        flag = 1
        row['state'] = word_stripped
      elif word_stripped in stateCodes:
        flag = 1
        row['state'] = stateMapping[word_stripped]
  if flag == 0:
    tweet_copied_df = tweet_copied_df.drop(index=index)
  else:
    tweet_copied_df.loc[index, 'state'] = row['state']

最后,我们将清理后的数据附加到一个现有的 csv 文件中,以创建一个 twitter 数据语料库,我们将在其上使用我们的情感分析器。出于这个项目的目的,我在一个循环中运行了上面的代码行,每次迭代之间间隔 15 分钟。

tweet_copied_df.to_csv('tweets_election.csv', header=**False**, mode='a')

情感分析

我们使用 VADER 情感分析器来执行情感分析。根据 GeeksforGeeks 的说法,VADER (Valence Aware 字典和情感推理器)是一个词汇和基于规则的情感分析工具,专门针对社交媒体中表达的情感。情感词典是词汇特征的列表,用更简单的术语来说,这些词通常根据它们的语义取向被标记为正面或负面。VADER 不仅将情绪分为积极或消极,还告诉我们情绪有多积极或消极。

import nltk
from nltk.sentiment.vader import SentimentIntensityAnalyzer
nltk.download('vader_lexicon')sid = SentimentIntensityAnalyzer()

接下来,我们将两位候选人的推文分离到不同的数据帧中。

tweets_election_df = pd.read_csv('tweets_election.csv')
tweets_trump = tweets_election_df[tweets_election_df.Subject == 'Donald Trump']
tweets_trump.drop(tweets_trump.columns[0], axis=1, inplace = **True**)
tweets_trump.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

tweets_biden = tweets_election_df[tweets_election_df.Subject == 'Joe Biden']
tweets_biden.drop(tweets_biden.columns[0], axis=1, inplace = **True**)
tweets_biden.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

df = pd.merge(tweets_trump['state'].value_counts(), tweets_biden['state'].value_counts(), right_index = **True**, 
               left_index = **True**)
df = df.rename(columns = {"state_x": "Total Trump Mentions", "state_y": "Total Biden Mentions"})
ax = df.plot(kind='barh', figsize=(16, 25), zorder=2)

*# Despine*
ax.spines['right'].set_visible(**False**)
ax.spines['top'].set_visible(**False**)
ax.spines['left'].set_visible(**False**)
ax.spines['bottom'].set_visible(**False**)

*#Replacing ticks with horizontal lines*
*#ax.tick_params(axis="both", which="both", bottom="off", top="off", labelbottom="on", left="off", right="off", labelleft="on")*
vals = ax.get_xticks()
**for** tick **in** vals:
      ax.axvline(x=tick, linestyle='dashed', alpha=0.4, color='#eeeeee', zorder=1)

*# Set y-axis label*
ax.set_ylabel("States", labelpad=20, weight='bold', size=12)
ax.set_title('Comparison of Twitter mentions of both candidates in all US states as per data collected',fontweight="bold", size=15)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上图显示了我们分析的一个巨大缺点。像许多其他数据集一样,我们的数据集不是一个分布良好的数据集。这意味着我们有一些州,比如佛罗里达、加利福尼亚、德克萨斯,它们比其他州拥有更多的数据。还有,似乎与特朗普相关的数据量以微弱优势超过拜登的数据。

我们现在继续进行情感分析。首先,我们将 polarity_scores()方法应用于每条 tweet 文本,以便理解 tweet 的情感。这个方法调用的结果是一个字典,显示推文中负面、中性和正面情绪的强度。所有这三个值被用来创建第四个数字,这是推文的总体复合情绪。我们会用这个数字来判断一条推文的情绪是正面的、负面的还是中性的。

tweets_trump['sentiment'] = tweets_trump['text'].apply(lambda x: sid.polarity_scores(x))
tweets_biden['sentiment'] = tweets_biden['text'].apply(lambda x: sid.polarity_scores(x))def sentimentVerdict(sentiment):
  if sentiment['compound'] >= 0.05:
    return "Positive"
  elif sentiment['compound'] <= -0.05:
    return "Negative"
  else:
    return "Neutral"tweets_trump['sentiment_overall'] = tweets_trump['sentiment'].apply(lambda x: sentimentVerdict(x)
tweets_biden['sentiment_overall'] = tweets_biden['sentiment'].apply(lambda x: sentimentVerdict(x))tweets_trump.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

既然我们已经将每条推文数据分为正面、负面或中性,我们可以按州分组,并了解该州公众对特定候选人的总体看法。这是我们的下一步行动。

tweets_trump_location = tweets_trump.groupby(['state', 'sentiment_overall']).count()
tweets_trump_location = tweets_trump_location['user']
tweets_biden_location = tweets_biden.groupby(['state', 'sentiment_overall']).count()
tweets_biden_location = tweets_biden_location['user']tweets_location_df = pd.DataFrame({'State': [state for state in states],
        'Trump Positive': [0 for state in states],
        'Trump Negative': [0 for state in states],
        'Trump Neutral': [0 for state in states],
        'Trump Total': [0 for state in states],
        'Biden Positive': [0 for state in states],
        'Biden Negative': [0 for state in states],
        'Biden Neutral': [0 for state in states],
        'Biden Total': [0 for state in states]})
tweets_location_df.set_index('State', inplace = True)
for state in states:
  positiveTrump, negativeTrump, neutralTrump, positiveBiden, negativeBiden, neutralBiden = 0, 0, 0, 0, 0, 0
  try:
    positiveTrump = tweets_trump_location[state]['Positive']
  except:
    positiveTrump = 0

  try:
    negativeTrump = tweets_trump_location[state]['Negative']
  except:
    negativeTrump = 0

  try:
    neutralTrump = tweets_trump_location[state]['Neutral']
  except:
    neutralTrump = 0

  try:
    positiveBiden = tweets_biden_location[state]['Positive']
  except:
    positiveBiden = 0

  try:
    negativeBiden = tweets_biden_location[state]['Negative']
  except:
    negativeBiden = 0

  try:
    neutralBiden = tweets_biden_location[state]['Neutral']
  except:
    neutralBiden = 0

  totalTrump = positiveTrump + negativeTrump + neutralTrump
  totalBiden = positiveBiden + negativeBiden + neutralBiden

  if totalTrump == 0:
    tweets_location_df.at[state, 'Trump Positive'], tweets_location_df.at[state, 'Trump Negative'], tweets_location_df.at[state, 'Trump Neutral'] = 0,0,0
  else:
    tweets_location_df.at[state, 'Trump Positive'] = round((positiveTrump/totalTrump)*100.0)
    tweets_location_df.at[state, 'Trump Negative'] = round((negativeTrump/totalTrump)*100.0)
    tweets_location_df.at[state, 'Trump Neutral'] = round((neutralTrump/totalTrump)*100.0)
  tweets_location_df.at[state, 'Trump Total'] = totalTrump

  if totalBiden == 0:
    tweets_location_df.at[state, 'Biden Positive'], tweets_location_df.at[state, 'Biden Negative'], tweets_location_df.at[state, 'Biden Neutral'] = 0,0,0
  else:
    tweets_location_df.at[state, 'Biden Positive'] = round((positiveBiden/totalBiden)*100.0)
    tweets_location_df.at[state, 'Biden Negative'] = round((negativeBiden/totalBiden)*100.0)
    tweets_location_df.at[state, 'Biden Neutral'] = round((neutralBiden/totalBiden)*100.0)
  tweets_location_df.at[state, 'Biden Total'] = totalBiden
tweets_location_df

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

总结我们的分析,我们使用上面的分析来分类一个状态是否是下列之一:

  • 强烈共和党:对特朗普的正面推文数量超过对拜登的正面推文数量,对特朗普的负面推文数量少于拜登,或者对特朗普持中立态度的推文数量多于拜登。
  • 强烈民主党:拜登的正面推文数量超过特朗普,拜登的负面推文数量少于特朗普,或者对拜登持中立态度的推文数量多于特朗普。
  • 有点像共和党:特朗普的正面推文和拜登的正面推文之间的差距大于负面推文的差距。
  • 有点民主:正面的特朗普推文和正面的拜登推文之间的差距小于负面推文的差距。
  • 数据不足:任何一个参赛者的推文数量少于 15 条的州。
tweets_location_df['Predicted Judgement'] = 'Neutral'
for index, row in tweets_location_df.iterrows():
  if row['Trump Total'] <= 15 and row['Biden Total'] <= 15:
    tweets_location_df.loc[index, 'Predicted Judgement'] = 'Insufficient Data'
  else:
    if row['Trump Positive'] > row['Biden Positive'] and (row['Trump Negative'] < row['Biden Negative'] or row['Trump Neutral'] > row['Biden Neutral']):
      tweets_location_df.loc[index, 'Predicted Judgement'] = 'Strongly Republican'
    elif row['Biden Positive'] > row['Trump Positive'] and (row['Biden Negative'] < row['Trump Negative'] or row['Biden Neutral'] > row['Trump Neutral']):
      tweets_location_df.loc[index, 'Predicted Judgement'] = 'Strongly Democratic'
    elif row['Trump Positive'] - row['Biden Positive'] > row['Biden Negative'] - row['Trump Negative']:
      tweets_location_df.loc[index, 'Predicted Judgement'] = 'Somewhat Republican'
    elif row['Biden Positive'] - row['Trump Positive'] > row['Trump Negative'] - row['Biden Negative']:
      tweets_location_df.loc[index, 'Predicted Judgement'] = 'Somewhat Democratic'
tweets_location_df = tweets_location_df.rename(columns={'Trump Positive': 'Trump Positive (in %)', 
     'Trump Negative': 'Trump Negative (in %)',
     'Trump Neutral': 'Trump Neutral (in %)',
     'Trump Total': 'Trump Total Mentions',
     'Biden Positive': 'Biden Positive (in %)',
     'Biden Negative': 'Biden Negative (in %)',
     'Biden Neutral': 'Biden Neutral (in %)',
     'Biden Total': 'Biden Total Mentions'})
tweets_location_df

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

tweets_location_df.groupby('Predicted Judgement').size()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从我的分析结果来看,根据目前的情绪,共和党以 21 比 16 领先。然而,由于数据不足,我无法了解 13 个州的观点,这 13 个州的观点可能会对任何一方的命运产生重大影响。最终的选举结果将决定我的分析是否成功。在我们等待星期二的时候,我鼓励所有美国人走出家门,去投票。

需要改进的地方

这个分析有一些缺点,我想在这里指出来。根据我的观察,这里有一些:

  1. 数据的分布很差。我在之前的分析中指出了这一点,我觉得这可能在我的分析中包含了一些偏见。理想情况下,两个候选人在所有州的推文数量应该相同。但在现实世界中,这即使不是不可能,也是很难实现的。然而,一个需要改进的地方是包括一些分布模型,以使我的数据集趋向于状态的均匀分布。
  2. **分析中的位置识别部分需要改进。**如前所述,理想情况下,如果推文来自美国,可以将推文的位置提供给 Google Maps API,以获取推文发布地所在的州。
  3. **对一个国家的预测判断是可以改进的。**我用来预测一个州是民主党还是共和党的整体判断的逻辑还可以改进。我用我在这个领域有限的技术知识和理性为这个计算做了一个逻辑,但这是开放的进一步建议和改进。

这款笔记本就这么多了!如果你还没有弄明白,这个项目的全部是使用 Python 完成的。

以下是 GitHub 储存库的链接:https://GitHub . com/pritamguha 31/sensation-Analysis-on-Twitter-data-ahead-of-2020-US-Elections

随时欢迎分享和反馈。

情感分析:VADER 还是文本斑点?

原文:https://towardsdatascience.com/sentiment-analysis-vader-or-textblob-ff25514ac540?source=collection_archive---------29-----------------------

两种常用库及其性能比较。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Unsplash 上由 Austin Distel 拍摄的照片

什么是情感分析?

对于大多数企业来说,了解客户对其产品/服务的感受是非常有价值的信息,可用于推动业务改进、流程变革,并最终提高盈利能力。
情感分析是通过使用自然语言处理(NLP)来分析信息并确定负面、正面或中性情感的过程。

非常简单地概括这个过程:
1)将输入的内容标记成它的组成句子或单词。
2)用词性成分(即名词、动词、限定词、句子主语等)识别和标记每个标记。3)指定一个从-1 到 1 的情感分数。
4)返回分数和可选分数如复合分数、主观性分数等。

我将研究两个常见的库在这个任务中的表现——text blob 和 VADER。

对于每个图书馆,我将使用来自 IMDB 的更一般的评论声明以及一个包含更多俚语、表情符号等的 Twitter 帖子。

我们要分析的情感陈述将是:

IMDB 审查:

I am a life long Star Wars fan and this was the first time I came out disappointed. And I am not picky, I was mostly happy even with the last two movies, but this one is the worst Star Wars movie yet.

推特声明:

I cannot stop watching the replays of this **incredible** goal. THE perfect strike 💘😁💘😁💘😁

文本 Blob

" TextBlob 是一个用于处理文本数据的 Python (2 和 3)库。它提供了一个简单的 API,用于处理常见的自然语言处理(NLP)任务,如词性标注、名词短语提取、情感分析、分类、翻译等

从 TextBlob 的网站这里

VADER

VADER (Valence Aware 字典和情感推理器)是一个基于词典和规则的情感分析工具,专门针对社交媒体中表达的情感*。*

来自 VADER 的 Github 这里

通过 TextBlob 运行,我们可以看到如下输出:

IMDB: Sentiment(polarity=-0.125, subjectivity=0.5916666666666667)
Twitter: Sentiment(polarity=0.95, subjectivity=0.95)

极性是一个介于-1 和 1 之间的浮点数,其中-1 是一个否定的陈述,1 是一个肯定的陈述。从上面,我们可以看到 IMDB 的声明被认为是负面的,但并不严重,Twitter 的声明是非常积极的。
主观性是 TextBlobs 对陈述是否被视为更多观点或基于事实的评分。较高的主观性分数意味着它不太客观,因此会非常固执己见。

而如果我们用 VADER 的情绪来代替:

IMDB:{'neg': 0.267, 'neu': 0.662, 'pos': 0.072, 'compound': -0.9169}
Twitter:{'neg': 0.026, 'neu': 0.492,'pos': 0.482,'compound': 0.9798}

VADER 的操作略有不同,将输出 3 个分类级别的得分,以及一个复合得分。
从上面,我们可以看到 IMDB 评论中有大约 66%的单词属于中性情感类别,然而它的复合分数——即“标准化、加权、综合分数”——将其标记为非常负面的陈述。基于 0.9798 的复合得分,Twitter 声明再次被评为非常积极。

两个库输出相对相似的结果,但是 VADER 从 IMDB 评论中发现了更多的负面论调,而 TextBlob 忽略了这一点。
这两个库都具有高度的可扩展性,可以查看与自然语言处理相关的许多其他类别,例如:

词性标注

将句子转换成元组列表(单词、标签)的过程。的标记是词性标记,表示这个词是名词、形容词还是动词等。

('I', 'PRP')
('can', 'MD')
('not', 'RB')
('stop', 'VB')
('watching', 'VBG')
('the', 'DT')
('replays', 'NN')
('of', 'IN')
('this', 'DT')
('incredible', 'JJ')
('goal', 'NN')
('THE', 'DT')
('perfect', 'JJ')
('strike', 'NN')
('💘😁💘😁💘😁', 'NN')

标记化

将句子或文本块分解成单独的“记号”进行分析。

['I', 'can', 'not', 'stop', 'watching', 'the', 'replays', 'of', 'this', 'incredible', 'goal', 'THE', 'perfect', 'strike', '💘😁💘😁💘😁']

N-grams

将句子分成大小为 n 的块。在下面的例子中,我使用 n=5,因此它输出所有可能的连续 5 个标记的块。

[WordList(['I', 'can', 'not', 'stop', 'watching']),
WordList(['can', 'not', 'stop', 'watching', 'the']), WordList(['not', 'stop', 'watching', 'the', 'replays']), WordList(['stop', 'watching', 'the', 'replays', 'of']), WordList(['watching', 'the', 'replays', 'of', 'this']), WordList(['the', 'replays', 'of', 'this', 'incredible']), WordList(['replays', 'of', 'this', 'incredible', 'goal']), WordList(['of', 'this', 'incredible', 'goal', 'THE']), WordList(['this', 'incredible', 'goal', 'THE', 'perfect']), WordList(['incredible', 'goal', 'THE', 'perfect', 'strike']), WordList(['goal', 'THE', 'perfect', 'strike', '💘😁💘😁💘😁'])]

还有很多很多!

你真正受到限制的只是你的创造力和你想要深入你的陈述的程度。
这两个库都提供了大量的特性,最好尝试运行一些关于你的主题的样本数据,看看哪个最能满足你的需求。根据我的测试,VADER 似乎在俚语、表情符号等方面表现更好,而 TextBlob 在更正式的语言使用方面表现强劲。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由 Prateek KatyalUnsplash 上拍摄

带有 BERT 的情感分析器(构建、调整、部署)

原文:https://towardsdatascience.com/sentiment-analyzer-with-bert-build-tune-deploy-da84c0f2366d?source=collection_archive---------52-----------------------

简要说明我如何开发情感分析器。它涵盖了文本预处理、模型构建、调优、API、前端创建和容器化。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

资料组

我使用了斯坦福大学 NLP 小组发布的数据集。我合并了两个文件,即包括 239,232 个文本片段的“dictionary.txt”和包含分配给各种文本片段的情感分数的“impression _ labels . txt”。

用正则表达式进行文本预处理

为了清理文本,我通常使用一堆包含正则表达式的函数。在common.py中,您可以找到所有这些,例如下面描述的remove_nonwords:

类似的函数也用于空行、特殊符号、数字和 html 代码删除。

在文本清理之后,是时候创建 BERT 嵌入了。为此,我使用了 bert-as-service 。它非常简单,只包含 3 个步骤:下载一个预先训练好的模型,启动 BERT 服务,使用客户端对指定长度的句子进行编码。

运行服务时,可以设置多个参数。例如,为了定义max_seq_len,我计算了 0.9 分位数的训练数据长度。

预处理数据具有包含 768 个特征的数据帧形式。完整代码请上nlp_preprocess.py

用 Keras 建模

在这一部分,我们在不同的参数上建立和训练模型。假设我们需要如下 5 层神经网络。我们将参数化 batch_size、历元数、前 4 个密集层和 5 个下降层中的节点数。

用神圣的模型调谐

现在我们可以调整参数了。我们将使用sacred模块。这里的要点是:

1。创建一个实验并添加观察者

首先,我们需要创建一个记录各种信息的实验和观察器。很简单!

2.定义主功能

当我们运行 Python 脚本时,@ex.automain装饰器定义并运行实验的主函数。

3。添加配置参数

我们将通过配置范围来定义它们。

4。添加指标

在我们的例子中,我想知道 MAE 和 MSE 分别是T21。我们可以为此使用 Metrics API。

5。运行实验

前面步骤的功能存储在model_experiment.py脚本中。为了对一堆参数运行我们的实验,我们为所有可能的排列创建并运行run_sacred.py.,MAE 和 MSE 将被保存在 MongoDB 中。

我得到的最好成绩是 MAE 分数的 9%。这意味着我们的情感分析器工作得非常好。我们可以用model_inference功能来查看。

请注意,分数是标准化的,因此也可以获得异常值。模型保存后,我们可以构建一个 Web API!

用 Flask 创建 Web API

现在,我们希望创建一个 API 来运行函数中的代码,并在浏览器中显示返回的结果。

语法@app.route('/score', methods=['PUT'])让 Flask 知道函数score应该被映射到端点/分数methods列表是一个关键字参数,它告诉我们允许哪种 HTTP 请求。我们将使用PUT请求来接收用户的句子。在函数score中,我们得到一个字典形式的分数,因为它可以很容易地转换成 JSON 字符串。完整代码在api.py中提供。

前端

对于 web 界面,创建了三个文件:

  • index.html提供了站点的基本结构:标题、描述、输入文本区和带分数的圆圈。
  • style.css用于网站样式。
  • index.js提供交互性。它负责读取用户输入,处理 API 请求并显示计算出的分数。这里的三个主要功能是:

对于坡度**,使用 HSV** 模型。饱和度和值是常量。色调对应于分值。在范围[0;中更改色调;120]产生从红色到黄色到绿色的平滑颜色变化。

码头集装箱化

Docker 的高明之处在于,一旦你将一个应用程序及其所有依赖项打包到容器中,你就能确保它能在任何环境下运行。通常建议通过对每个容器使用一个服务来分隔关注的区域。在我的小应用程序有 3 个部分应该结合起来:伯特即服务,应用程序和前端。帮助您构建 Docker 映像和运行容器的工具是 Docker Compose

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

将我们的代码进行文档化需要做的步骤:

  • 为 bert-as-service、api 和前端创建单独的文件夹,
  • 把相关文件放在那里,
  • requirenments.txtDockerfile添加到每个文件夹中。第一个文件应该包含将通过第二个文件中的命令安装的所有需要的库。其格式在 docker 文档中有描述
  • 在 3 个文件夹目录中创建docker-compose.yaml。在这个文件中定义组成应用程序的 3 个服务,以便它们可以在一个隔离的环境中一起运行。

现在我们已经准备好构建和运行我们的应用程序了!请参见下面的输出示例。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

像往常一样,请随意查看我的 GitHub 上的完整代码。

[## GitHub-zuzadeu/带 BERT 的情感分析器

此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…

github.com](https://github.com/zuzadeu/Sentiment-Analyzer-with-BERT)

Python 中的情感分类

原文:https://towardsdatascience.com/sentiment-classification-in-python-da31833da01b?source=collection_archive---------10-----------------------

使用 VADER 和 TextBlob 进行情感分析,使用 scikit-learn 进行监督文本分类

这篇文章是构建情感分类器的三篇连续文章中的最后一篇。做了一些探索性文本分析预处理文本之后,是时候将评论分类为情绪了。在这篇文章中,我们将首先看看两种不用建立模型就能获得情感的方法,然后建立一个定制模型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由混合Unsplash 上拍摄

在我们开始之前,让我们后退一步,快速地看一下更大的画面。 CRISP-DM 方法概述了成功的数据科学项目的流程。在本帖中,我们将做一些数据科学家在建模阶段会经历的任务。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

CRISP-DM 工艺流程摘录

0.Python 设置

这篇文章假设读者(👀是的,你!)可以访问并熟悉 Python,包括安装包、定义函数和其他基本任务。如果您是 Python 的新手,这个是一个很好的入门地方。

我在 Jupyter 笔记本里测试过 Python 3.7.1 的脚本。

让我们在开始之前确保您已经安装了以下库:
◼️ 数据操作/分析: numpy,pandas ◼️ 数据分区:sk learn ◼️文本预处理/分析: nltk,textblob
◼️ 可视化: matplotlib,seaborn

一旦你安装了 nltk ,请确保你已经从 nltk 下载了*【停用词】**【wordnet】【Vader _ lexicon】*,脚本如下:

import nltk
nltk.download('stopwords') 
nltk.download('wordnet')
nltk.download('vader_lexicon')

如果你已经下载了,运行这个会通知你。

现在,我们准备好导入包了:

# Set random seed
seed = 123# Data manipulation/analysis
import numpy as np
import pandas as pd# Text preprocessing/analysis
import re
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer
from nltk.tokenize import RegexpTokenizer
from nltk.sentiment.vader import SentimentIntensityAnalyzer
from textblob import TextBlob
from scipy.sparse import hstack, csr_matrix
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.preprocessing import MinMaxScaler# Modelling
from sklearn.model_selection import train_test_split, cross_validate, GridSearchCV, RandomizedSearchCV
from sklearn.linear_model import LogisticRegression, SGDClassifier
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.pipeline import Pipeline# Visualisation
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
sns.set(style="whitegrid", context='talk')

1.数据📦

我们将使用 IMDB 电影评论数据集。您可以在这里下载数据集,并将其保存在您的工作目录中。保存后,让我们将其导入 Python:

sample = pd.read_csv('IMDB Dataset.csv')
print(f"{sample.shape[0]} rows and {sample.shape[1]} columns")
sample.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

让我们来看看情绪之间的分歧:

sample['sentiment'].value_counts()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在样本数据中,情感是平均分配的。让我们将目标编码成数字值,其中正数为 1,负数为 0:

# Encode to numeric
sample['target'] = np.where(sample['sentiment']=='positive', 1, 0)# Check values
sample.groupby(['sentiment', 'target']).count().unstack()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

让我们留出 5000 个案例进行测试:

# Split data into train & test
X_train, X_test, y_train, y_test = train_test_split(sample['review'], sample['sentiment'], test_size=5000, random_state=seed, 
                                                    stratify=sample['sentiment'])# Append sentiment back using indices
train = pd.concat([X_train, y_train], axis=1)
test = pd.concat([X_test, y_test], axis=1)# Check dimensions
print(f"Train: {train.shape[0]} rows and {train.shape[1]} columns")
print(f"{train['sentiment'].value_counts()}\n")print(f"Test: {test.shape[0]} rows and {test.shape[1]} columns")
print(test['sentiment'].value_counts())

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们将快速检查训练数据集的头部:

train.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

好吧,我们开始吧!🐳

1.情感分析💛

在这一节中,我想向您展示两种非常简单的方法来获得情感,而无需构建自定义模型。我们将用 VADER文本块提取极性强度分数。

1.1.VADER 的情感分析

“VADER (Valence Aware 字典和情感推理器)是一个基于词典和规则的情感分析工具,专门针对社交媒体中表达的情感。”

让我们从一个简单的例子开始,看看我们如何使用 VADER 情感分析器提取情感强度分数:

example = 'The movie was awesome.'
sid = SentimentIntensityAnalyzer()
sid.polarity_scores(example)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

neg,neu,pos: 这三个分数相加为 1。这些分数显示了属于该类别的文本的比例。 复合: 这个分数范围从-1(最负)到 1(最正。

虽然不是所有的评论都像我们手头的例子一样简单,但很高兴看到例子评论的分数看起来大多是正面的。现在,让我们将强度分数添加到训练数据中:

train[['neg', 'neu', 'pos', 'compound']] = train['review'].apply(sid.polarity_scores).apply(pd.Series)
train.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一旦我们初始化了分析器对象,获得情感分数只需要一行代码。我们要进一步检查分数吗?让我们从得分最高的 5 条记录开始:

train.nlargest(5, ['pos'])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

很高兴看到所有的评论都是正面的。让我们为阴性做同样的事情:

train.nlargest(5, ['neg'])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个看起来也不错。但我们可能会看到数据的极端,那里的情绪更加明显。让我们用直方图直观显示分数,以便更好地理解:

for var in ['pos', 'neg', 'neu', 'compound']:
    plt.figure(figsize=(12,4))
    sns.distplot(train.query("target==1")[var], bins=30, kde=False, 
                 color='green', label='Positive')
    sns.distplot(train.query("target==0")[var], bins=30, kde=False, 
                 color='red', label='Negative')
    plt.legend()
    plt.title(f'Histogram of {var} by true sentiment');

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从直方图来看,似乎 pos、neg 和潜在的 compound 列在对积极和消极情绪进行分类时是有用的。我们可以使用这些分数快速地将每个评论分为正面或负面类别。让我们看看它会做得多好:

train['vader_polarity'] = np.where(train['pos']>train['neg'], 1, 0)
target_names=['negative', 'positive']
print(classification_report(train['target'], 
                            train['vader_polarity'], 
                            target_names=target_names))

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 VADER ,我们可以不费吹灰之力获得大约 69%的准确率。不过,正面和负面评论的表现看起来有所不同。我们对正面评论的召回率更高,准确率更低——这意味着我们有更多的误报(看到我在那里做了什么吗?你明白我为什么把正面评价编码为 1 了吗?🙊).让我们看看混淆矩阵:

# Create function so that we could reuse later
def plot_cm(y_test, y_pred, target_names=['negative', 'positive'], 
            figsize=(5,3)):
    """Create a labelled confusion matrix plot."""
    cm = confusion_matrix(y_test, y_pred)
    fig, ax = plt.subplots(figsize=figsize)
    sns.heatmap(cm, annot=True, fmt='g', cmap='BuGn', cbar=False, 
                ax=ax)
    ax.set_title('Confusion matrix')
    ax.set_xlabel('Predicted')
    ax.set_xticklabels(target_names)
    ax.set_ylabel('Actual')
    ax.set_yticklabels(target_names, 
                       fontdict={'verticalalignment': 'center'});# Plot confusion matrix
plot_cm(train['target'], train['vader_polarity'])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正如我们所见,我们有许多真阳性和假阳性。事实上,大约 67%的预测是积极的。让我们看看如果使用复合分数,性能是否会提高。

train['vader_compound'] = np.where(train['compound']>0, 1, 0)
print(classification_report(train['target'], 
                            train['vader_compound'], 
                            target_names=target_names))

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

plot_cm(train['target'], train['vader_compound'])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

性能看起来非常相似。我使用训练数据集进行评估,因为我们在这里不是训练模型。但是,如果对测试数据进行同样的操作,结果应该非常相似。

🔗nltk 中关于【VADER】VADER 的更多信息。

1.2.使用 TextBlob 进行情感分析

另一种获得情感分数的方法是利用 TextBlob 库。使用来自 TextBlob 对象的情感属性,我们也可以提取相似的分数。下面是我们如何使用之前的示例进行提取:

*TextBlob(example).sentiment*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

极性: 范围从-1(最负)到 1(最正) 主观性: 范围从 0(非常客观)到 1(非常主观)

我们的例子被分析为非常主观的肯定陈述。是真的,不是吗?在这两个分数中,极性与我们更相关。让我们将强度分数添加到训练数据中,并检查具有最高极性分数的 5 个记录:

*train[['polarity', 'subjectivity']] = train['review'].apply(lambda x:TextBlob(x).sentiment).to_list()columns = ['review', 'target', 'polarity', 'subjectivity']
train[columns].nlargest(5, ['polarity'])*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如你所见,用 TextBlob 添加情感强度分数也很简单。让我们来看看极性分数最低的 5 条记录:**

*train[columns].nsmallest(5, ['polarity'])*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

是时候绘制一些直方图来更好地理解分数了:

*for var in ['polarity', 'subjectivity']:
    plt.figure(figsize=(12,4))
    sns.distplot(train.query("target==1")[var], bins=30, kde=False, 
                 color='green', label='Positive')
    sns.distplot(train.query("target==0")[var], bins=30, kde=False, 
                 color='red', label='Negative')
    plt.legend()
    plt.title(f'Histogram of {var} by true sentiment');*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传**外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正如所料,极性分数看起来可能有助于对积极情绪和消极情绪进行分类。让我们使用极性分数进行分类,并查看性能:

*train['blob_polarity'] = np.where(train['polarity']>0, 1, 0)
print(classification_report(train['target'], 
                            train['blob_polarity'], 
                            target_names=target_names))*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 TextBlob ,我们可以不费吹灰之力获得大约 69%的准确率。同样,我们有许多假阳性,事实上,甚至比以前更多。让我们看看混淆矩阵:

*plot_cm(train['target'], train['blob_polarity'])*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这一次,假阳性的数量高于真阴性的数量。预测偏向积极情绪,因为 76%的预测是积极的。

🔗关于 TextBlob 的更多信息。

1.3.两者之间的关系

让我们来比较一下 VADERTextBlob 的分数有多相似:

*pd.crosstab(train['vader_polarity'], train['blob_polarity'])*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

他们的分类中有大约 79%的重叠,大多数是正面情绪。让我们来看看极性得分:

*plt.figure(figsize=(12,12))
sns.scatterplot(data=train, x='polarity', y='compound',
                hue='target', palette=['red', 'green'], 
                alpha=.3)
plt.axhline(0, linestyle='--', color='k')
plt.axvline(0, linestyle='--', color='k')
plt.title('Scatterplot between polarity intensity scores');*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这张图显示的信息比前一张表多一点。在左下象限,我们主要看到红色圆圈,因为两种方法中的负面分类都更精确。在右上象限,有大量的圆圈,大部分是绿色的,但颜色混合不像以前那么纯。剩下的两个象限显示了两个分数不一致的地方。总的来说,在图的右半部分,颜色比左半部分更混合。

我们从两者中得到非常相似的 69%的总体准确度;然而,当我们仔细观察预测时,这两种方法之间的性能是不同的。

现在你知道如何用 VADER文本块获得情感极性分数。如果您有未标记的数据,这两个工具为自动标记您的数据提供了一个很好的起点。是时候建立模型了!✨

2.模拟ⓜ️

在本节中,我们将:

  1. 选择合适的预处理方法和算法
  2. 探索添加 VADER文本斑点情感分数作为特征是否会提高模型的预测能力
  3. 构建管道并调整其超参数
  4. 在看不见的数据上测试最终管道

情感分类是监督分类模型的一个应用。因此,我们在这里采用的方法可以推广到任何监督分类任务。

2.1.选择合适的预处理方法和算法

我之前的帖子中,我们探索了三种不同的文本预处理方法,并列出其中两种:简单方法简单方法*。在这两个选项中,我们现在将测试这两个选项之间的模型性能是否有任何差异,并选择其中一个来使用向前移动。为了使事情变得简单,我们将创建两个函数(这些函数的想法是从这里得到的启发)😗

*# Define functions
def create_baseline_models():
    """Create list of baseline models."""
    models = []
    models.append(('log', LogisticRegression(random_state=seed, 
                                             max_iter=1000)))
    models.append(('sgd', SGDClassifier(random_state=seed)))
    models.append(('mnb', MultinomialNB()))
    return modelsdef assess(X, y, models, cv=5, scoring=['roc_auc', 
                                        'accuracy', 
                                        'f1']):
    """Provide summary of cross validation results for models."""
    results = pd.DataFrame()
    for name, model in models:
        result = pd.DataFrame(cross_validate(model, X, y, cv=cv, 
                                             scoring=scoring))
        mean = result.mean().rename('{}_mean'.format)
        std = result.std().rename('{}_std'.format)
        results[name] = pd.concat([mean, std], axis=0)
    return results.sort_index()*

我挑了三个算法来试: 逻辑回归分类器随机梯度下降分类器多项朴素贝叶斯分类器 。让我们启动模型:

*models = create_baseline_models()
models*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,让我们检查使用更简单方法时的模型性能:

*# Preprocess the data
vectoriser = TfidfVectorizer(token_pattern=r'[a-z]+', 
                             stop_words='english', 
                             min_df=30, 
                             max_df=.7)
X_train_simpler = vectoriser.fit_transform(X_train)# Assess the model
assess(X_train_simpler, y_train, models)*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

很高兴看到我们获得了更好的性能:与仅使用情感分数相比,基线模型的准确率为 86–89%。由于这些职业相当平衡,我们将主要关注准确性。但是,我们将确保稍后更仔细地检查预测,以评估模型。性能指标在逻辑回归随机梯度下降之间看起来非常接近,后者在训练中更快(参见 fit_time )。朴素贝叶斯是三个人中训练速度最快的,但是表现比其他两个人稍差。现在让我们来评估一下简单方法*😗

*# Define function
def preprocess_text(text):
    # 1\. Tokenise to alphabetic tokens
    tokeniser = RegexpTokenizer(r'[A-Za-z]+')
    tokens = tokeniser.tokenize(text)

    # 2\. Lowercase and lemmatise 
    lemmatiser = WordNetLemmatizer()
    tokens = [lemmatiser.lemmatize(t.lower(), pos='v') 
              for t in tokens]
    return tokens# Preprocess the data
vectoriser = TfidfVectorizer(analyzer=preprocess_text, 
                             min_df=30, 
                             max_df=.7)
X_train_simple = vectoriser.fit_transform(X_train)# Assess models
assess(X_train_simple, y_train, models)*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

性能看起来和以前差不多。因此,我们将倾向于使用更简单的方法*,并继续使用它。在这三种算法中,我们将选择随机梯度下降,因为它最能平衡速度和预测能力。*

2.2.评估附加功能

在本节中,我们将探讨添加 VADER文本斑点情感分数作为特征是否会提高模型的预测能力。让我们快速检查一下是否有任何高度相关的特性:

*plt.figure(figsize = (14,5))
columns = ['target', 'neg', 'neu', 'pos', 'compound', 'polarity', 
           'subjectivity']
sns.heatmap(train[columns].corr(), annot=True, cmap='seismic_r');*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

最相关的特征是复合负*。让我们运行一个快速模型,看看哪些分数更有用:*

*# Initialise a model
sgd = SGDClassifier(random_state=seed)# Initialise a scaler
scaler = MinMaxScaler()# Assess the model using scores
scores = train[['neg', 'neu', 'pos', 'compound', 'polarity', 
                'subjectivity']]
assess(scaler.fit_transform(scores), y_train, [('sgd', sgd)])*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们使用分数得到大约 77%的准确率。现在让我们检查系数:

*# Fit to training data
sgd.fit(scores, y_train)# Get coefficients
coefs = pd.DataFrame(data=sgd.coef_, columns=scores.columns).T
coefs.rename(columns={0: 'coef'}, inplace=True)# Plot
plt.figure(figsize=(10,5))
sns.barplot(x=coefs.index, y='coef', data=coefs)
plt.title('Coefficients');*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

似乎我们只能使用阴性、阳性极性*,因为它们是得分*中最主要的特征。让我们看看是否可以通过将这些选择的分数添加到先前预处理的数据来改进模型结果。

*# Add features to sparse matrix
selected_scores = train[['neg', 'pos', 'polarity']]
X_train_extended = hstack([X_train_simpler, csr_matrix(scaler.fit_transform(selected_scores))])# Assess
assess(X_train_extended, y_train, [('sgd', sgd)])*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由于添加这些分数并没有改进模型,因此没有必要将它们作为特征添加。这也将使我们的渠道保持简单!

2.3.构建管道并调整其超参数

是时候构建一个小管道,将预处理器和模型放在一起了。我们将微调它的超参数,看看我们是否能改进这个模型。首先,让我们尝试理解三个超参数的影响:对于向量机的min_dfmax_df和对于随机搜索模型的loss:

*# Create a pipeline
pipe = Pipeline([('vectoriser', TfidfVectorizer(token_pattern=r'[a-z]+')),
                 ('model', SGDClassifier(random_state=seed))])# Prepare a random search
param_distributions = {'vectoriser__min_df': np.arange(10, 1000, 10),
                       'vectoriser__max_df': np.linspace(.2, 1, 40),
                       'model__loss': ['log', 'hinge']}
r_search = RandomizedSearchCV(estimator=pipe, param_distributions=param_distributions, 
                              n_iter=30, cv=5, n_jobs=-1, random_state=seed)
r_search.fit(X_train, y_train)# Save results to a dataframe
r_search_results = pd.DataFrame(r_search.cv_results_).sort_values(by='rank_test_score')*

这里,我们正在尝试 30 种不同的超参数空间指定的随机组合。这需要一段时间来运行。随机搜索的输出将保存在名为r_search_results的数据帧中。让我们创建另一个数据框架,其中包含一些我们更感兴趣的列:

*columns = [col for col in r_search_results.columns 
           if re.search(r"split|param_", col)]
r_summary = r_search_results[columns].copy()
r_summary.columns = [re.sub(r'_test_score|param_', '', col) 
                     for col in r_summary.columns]
columns = [col.split('__')[1] if '__' in col else col 
           for col in r_summary.columns ]
r_summary.columns = columns
r_summary.head()*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

让我们将输出可视化,以便更好地理解超参数的影响:

*# Create a long dataframe
r_summary_long = pd.melt(r_summary, 
                         id_vars=['min_df', 
                                  'max_df', 
                                  'loss'], 
                         value_vars=['split0', 
                                     'split1', 
                                     'split2', 
                                     'split3', 
                                     'split4'])# Plot hyperparameter 'loss'
plt.figure(figsize=(8,4))
plt.title('Performance by loss')
sns.boxplot(x='value', y='loss', data=r_summary_long, 
            orient='h')
plt.xlim(.8, .9);*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

看起来loss='hinge'会带来稍微好一点的性能。让我们看看数字超参数:

*for param in ['min_df', 'max_df']:
    plt.figure(figsize=(8,4))
    sns.scatterplot(x=param, y="value", data=r_summary_long, 
                    x_jitter=True, alpha=0.5)
    plt.ylim(.8, .9);*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由于min_df和准确度之间似乎存在负相关关系,我们将把min_df保持在 200 以下。max_df没有明显的趋势,可能是因为业绩受min_dfloss的影响更大。虽然他们三个都是这样,但是对于max_df来说更明显。现在,我们对这些超参数如何影响模型有了一些了解,让我们更精确地定义管道(max_df=.6loss=’hinge')并尝试使用网格搜索进一步调整它:

*# Create a pipeline
pipe = Pipeline([('vectoriser', TfidfVectorizer(token_pattern=r'[a-z]+', max_df=.6)),
                 ('model', SGDClassifier(random_state=seed, loss='hinge'))])# Prepare a grid search
param_grid = {'vectoriser__min_df': [30, 90, 150],
              'vectoriser__ngram_range': [(1,1), (1,2)],
              'vectoriser__stop_words': [None, 'english'],
              'model__fit_intercept': [True, False]}
g_search = GridSearchCV(estimator=pipe, param_grid=param_grid, cv=5, n_jobs=-1)
g_search.fit(X_train, y_train)# Save results to a dataframe
g_search_results = pd.DataFrame(g_search.cv_results_).sort_values(by='rank_test_score')*

网格搜索也需要一些时间,因为我们有 24 种不同的超参数组合要尝试。像以前一样,输出将保存到名为g_search_results的数据帧中。让我们将更多相关列提取到另一个数据框架中:

*columns = [col for col in g_search_results.columns 
           if re.search(r"split|param_", col)]
g_summary = g_search_results[columns+['mean_test_score']].copy()
g_summary.columns = [re.sub(r'_test_score|param_', '', col) 
                     for col in g_summary.columns]
columns = [col.split('__')[1] if '__' in col else col 
           for col in g_summary.columns ]
g_summary.columns = columns
g_summary.head()*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用这些组合中的任何一种,我们都可以达到约 0.9 的交叉验证精度。很高兴看到边际增长。

*# Create a long dataframe
g_summary_long = pd.melt(g_summary, 
                         id_vars=['min_df', 
                                  'ngram_range', 
                                  'stop_words', 
                                  'fit_intercept'], 
                         value_vars=['split0', 
                                     'split1', 
                                     'split2', 
                                     'split3', 
                                     'split4'])
g_summary_long.replace({None: 'None'}, inplace=True)# Plot performance
for param in ['ngram_range', 'stop_words', 'fit_intercept']:
    plt.figure(figsize=(8,4))
    plt.title(f'Performance by {param}')
    sns.boxplot(x='value', y=param, data=g_summary_long, orient='h')
    plt.xlim(.85, .95);*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到,换成ngram_range=(1,2),model 表现更好。stop_words=None也是如此。另一方面,我们是否拟合截距并没有太大的影响,这意味着我们可以将这个超参数保留为默认值。我认为这已经足够好了,我们现在可以定义最终的管道了。

2.4.在看不见的数据上测试最终管道

使用网格搜索中的顶部组合,这是我们最终管道的样子:

*pipe = Pipeline([('vectoriser', TfidfVectorizer(token_pattern=r'[a-z]+', min_df=30, max_df=.6, ngram_range=(1,2))),
                 ('model', SGDClassifier(random_state=seed, loss='hinge'))])pipe.fit(X_train, y_train)*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们的管道很小很简单。让我们看看它的系数:

*coefs = pd.DataFrame(pipe['model'].coef_, 
                     columns=pipe['vectoriser'].get_feature_names())
coefs = coefs.T.rename(columns={0:'coef'}).sort_values('coef')
coefs*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

具有最高或最低系数的特征看起来很直观。但是看看我们拥有的特性数量:49,577!这主要是因为放松了min_df,增加了二元模型,没有删除停用词。如果我们热衷于减少特征的数量,我们可以改变管道中的这些超参数。如果我们开始减少特征,我们会注意到特征数量和模型精度之间的权衡。最佳平衡是什么样的取决于具体情况。让我们来评估管道:

*train_pred = pipe.predict(X_train)
print(classification_report(train_pred, 
                            y_train, 
                            target_names=target_names))*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*test_pred = pipe.predict(X_test)
print(classification_report(test_pred, 
                            y_test, 
                            target_names=target_names))*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在训练集和测试集上的准确率分别约为 0.94 和 0.92。这两种观点的精确度和召回率看起来非常相似。我们有稍微多一点的假阴性。让我们绘制混淆矩阵:

*plot_cm(test_pred, y_test, target_names=target_names)*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

看起来不错。这么🎊现在,我们有了一个将大约 90%的评论归类为正确观点的管道。让我们看看做一次预测需要多长时间。我们将使用 Jupyter 笔记本的魔法命令%timeit:

*for i in range(10):
    lead = X_test.sample(1)
    %timeit pipe.predict(lead)*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

尽管%timeit运行了多个循环,并给出了运行时间的均值和标准差,但我注意到我每次得到的输出都略有不同。因此,我们正在查看%timeit的 10 个循环以观察范围。

单次预测大约需要 1.5 到 4 毫秒。这需要在用例的生产环境的上下文中进行评估。

好了,这就是这篇文章的内容。💫

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由伯爵克里斯Unsplash 上拍摄

您想访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果您使用 我的推荐链接成为会员,您的一部分会费将直接用于支持我。

谢谢你看我的帖子。希望您已经学会了一些不同的实用方法,可以在构建或不构建定制模型的情况下将文本分类为情感。以下是本系列其他两篇文章的链接:◼️python 中的探索性文本分析
◼️python 中的文本预处理

以下是我的其他 NLP 相关帖子的链接:
◼️Python 中的简单 word cloud
(下面列出了一系列关于 NLP 介绍的帖子)
◼️ 第一部分:Python 中的预处理文本
◼️ 第二部分:词条满足和词干的区别
◼️ 第三部分:TF-IDF 解释
◼️ 第四部分:python 中的监督文本分类模型

再见🏃💨

PyTorch 中基于 CNN 的餐馆评论情感分类

原文:https://towardsdatascience.com/sentiment-classification-using-cnn-in-pytorch-fba3c6840430?source=collection_archive---------10-----------------------

使用 Word2Vec 嵌入作为输入实现卷积神经网络(CNN)以分类 PyTorch 中的 Yelp 餐馆评论

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 PyTorch 中使用 CNN 进行情感分类

在本文中,我将解释 CNN 如何用于文本分类问题,以及如何设计网络以接受 word2vec 预训练的嵌入作为网络的输入。您将了解如何在 PyTorch 中为情感分类问题构建自定义 CNN。

我之前的帖子中,我介绍了 PyTorch 的基础知识以及如何实现用于情感分类的逻辑回归。如果你是 PyTorch 新手,可以参考一下。如果你想习惯用 PyTorch 定义网络,请参考的前馈神经网络文章。我在以前的帖子中解释了使用决策树分类器使用 BOWTF-IDFWord2VecDoc2Vec 向量进行情感分类的其他方法,最后也会进行比较。现在让我们开始加载数据吧!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迪皮卡·巴德提供的按情感分类的餐馆评论示例

加载数据

Yelp 餐馆评论数据集可以从他们的网站下载,那里的数据格式是 JSON。提供的数据实际上不是 python 可读的正确 json 格式。每一行都是 dictionary,但是为了使它成为有效的 json 格式,应该在文件的开头和结尾加上方括号,并在每一行的末尾添加,。将INPUT_FOLDER定义为 yelp review.json 文件所在的本地目录中的文件夹路径。将OUTPUT_FOLDER声明为一个路径,您希望在该路径中写入以下函数的输出。json 数据的加载和前 100,000 行的写入在下面的函数中完成:

一旦运行了上面的函数,您就可以为接下来的步骤将它加载到 pandas dataframe 中了。对于这个实验,只取了少量的数据,这样就可以更快地运行以查看结果。

探索数据

在加载数据之后,创建用于情感指示的新列。原始数据集中并不总是存在带有您想要的预测标签的某个列。在大多数情况下,这可以是一个派生列。对于这种情况,数据中的stars列用于导出情绪。

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在数据可用之后,完成从星星到情绪的映射,并绘制每个情绪的分布。

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

完成后,检查每个情感的行数。情感类别如下:

  1. 阳性:1
  2. 负数:-1
  3. 中性:0

这三种情绪的行数分布不均匀。在这篇文章中,不平衡类的问题将不会被处理,这就是为什么,简单的函数检索每个情绪的前几个记录被写。在本例中,top_n是 10000,这意味着将获取总共 30,000 条记录。

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如何对文本数据进行预处理?

预处理包括许多步骤,如标记化、去除停用词、词干化/词条化等。这些常用的技巧在我之前的弓的帖子里有详细解释。这里,下一阶段只解释必要的步骤。

为什么需要对这段文字进行预处理?—并非所有信息都对预测或分类有用。减少字数将减少模型的输入维度。这种语言的书写方式,包含了大量语法特有的信息。因此,当转换成数字格式,像大写,标点符号,后缀/前缀等字的具体特征。都是多余的。以相似单词映射到单个单词的方式清理数据,并从文本中删除语法相关信息,可以极大地减少词汇量。应用哪种方法和跳过哪种方法取决于手头的问题。

1.停用词的删除

停用词是在不同的自然语言处理(NLP)任务中作为前置步骤经常使用并从句子中移除的词。停用词的例子有:a、an、the、this、not 等。每个工具都使用一组稍微不同的停用词列表,但是在短语结构很重要的情况下,比如在情感分析的情况下,就避免使用这种技术。

移除停用字词的示例:

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从输出中可以看出,删除停用词就删除了获取情感所需的必要词,有时它可以完全改变句子的意思。在上面这段代码打印的例子中,很明显,它可以将否定句转换成肯定句。因此,对于情感分类来说,这个步骤被跳过。

2.标记化

标记化是将句子/文本分割成称为标记的单词阵列的过程。这有助于分别对每个单词进行转换,也是将单词转换为数字所必需的。执行标记化有不同的方式。我已经在我的上一篇文章的标记化部分解释了这些方法,所以如果你感兴趣的话,你可以去看看。

Gensim 的[simple_preprocess](https://radimrehurek.com/gensim/utils.html)允许你将文本转换成小写,并删除标点符号。它还有minmax长度参数,这有助于过滤掉那些长度范围内的罕见单词和最常见的单词。

这里,simple_preprocess用于获取数据帧的标记,因为它已经为我们做了大部分预处理。让我们应用此方法来获取数据帧的令牌:

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.堵塞物

词干处理将单词还原为其“词根”。不同于使用语法规则和字典将单词映射到词根形式的词干化,词干化只是删除后缀/前缀。词干分析广泛用于 SEOs、Web 搜索结果和信息检索的应用中,因为只要词根在文本的某个地方匹配,它就有助于检索搜索中的所有相关文档。

有不同的算法用来做词干分析。PorterStammer(1979)、LancasterStammer (1990)和 SnowballStemmer(可以添加自定义规则)。NLTK 或 Gensim 包可用于实现这些词干提取算法。兰开斯特比波特慢一点,所以我们可以根据大小和所需的响应时间来使用它。Snowball stemmer 是 Porter stemmer 的略微改进版本,通常比后者更受欢迎。不太清楚哪种方法会产生准确的结果,因此必须试验不同的方法,并选择给出更好结果的方法。在这个例子中,波特斯特梅尔被使用,这是简单和快速的。以下代码显示了如何在数据帧上实现词干,并创建新列stemmed_tokens:

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分成训练集和测试集:

训练数据将用于训练模型,测试数据是模型预测分类的数据,它将与原始标签进行比较,以检查准确性或其他模型测试指标。

  • 训练数据(用于训练 ML 模型的数据子集)~70%
  • 测试数据(用于测试从训练数据训练的 ML 模型的数据子集)~30%

尽量平衡两个集合中的类数,使结果不偏不倚或者成为模型训练不足的原因之一。这是机器学习模型的关键部分。在现实世界的问题中,存在不平衡类的情况,需要使用过采样少数类、欠采样多数类(scikit-learn 包中的重采样函数)或使用 Imblearn 包中的 SMOTE 功能生成合成样本等技术。

在这种情况下,数据分为两部分,训练和测试,70%在训练中,30%在测试中。在进行拆分时,最好在训练和测试数据中平均分布类别。这里使用了 scikit-learn 包中的函数 train_test_split

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从上面的输出可以看出,数据是按比例分配给每个类的。打印训练和测试中每个情感的行数。

用于文本分类的卷积神经网络

现在,我们准备深入研究如何使用 CNN 进行文本分类,以及如何构建输入。CNN 涉及两种操作,可以认为是特征提取器:卷积和池化。这些操作的输出最终连接到多层感知器以获得最终输出。

神经网络只对数字数据起作用,因此首要任务是找到从文字到数字格式的合适转换。这里我们将使用大小为 500 的 Word2Vec 向量作为输入,这在我之前的文章中已经解释过了。

  • 卷积层——这些层用于通过在输入上滑动小内核窗口来发现模式。它不是在图像的小区域上增加过滤器,而是通过嵌入窗口大小提到的几个词的向量来滑动。为了查看单词嵌入序列,窗口必须查看一个序列中的多个单词嵌入。它们将是尺寸为window_size * embedding_size的矩形。例如,在我们的例子中,如果窗口大小是 3,那么内核将是 3*500。这实质上代表了模型中的 n 元语法。内核权重(过滤器)被成对地乘以单词嵌入,并被求和以获得输出值。随着网络被学习,这些内核权重也被学习。以下显示了如何在卷积层中进行计算的示例(使用了填充,这将在后面解释)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过 Dipika Baad 进行文本分类的 CNN 计算

  • 卷积的输入和输出通道—这里,nn.Conv2d用于创建卷积层。在图像的情况下,不同颜料的输入被分别给出,在这种情况下,如果是 RGB,则输入通道的数量是 3,如果是灰度,则输入通道的数量是 1。在这种情况下,我们只输入一个特征,即单词嵌入,因此conv2d的第一个参数是1,输出通道是特征总数,将是NUM_FILTERS。我们可以为每个窗口大小设置多个滤波器,因此总输出会有这么多。
  • 填充——有时滑动时,内核大小不会完全覆盖。因此,为了使高度相同的大小,填充使用。这里我们用了window_size-1。下面的动画展示了它如何使用 window_size-1 填充。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

带填充的滑动窗口,用于 CNN 分类,文本由迪皮卡·巴德提供

  • Maxpooling —一旦我们有了特征向量,并且它已经提取了重要的特征,就足以知道它存在于句子中,就像一些积极的短语“棒极了的食物”,并且它出现在句子中的什么地方并不重要。Maxpooling 用于获取这些信息,并丢弃其余的信息。例如,在上面的动画中,我们有特征向量,在应用最大池后,将选择最大值。在上面的例子中,当verydelicious在短语中时,它显示 max,这是有意义的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迪皮卡巴德对特征向量的最大池化结果

一旦对所有特征向量应用了最大池,就可以像前馈神经网络一样添加更多层。让我们开始实施吧!下面的代码显示了在构建网络之前需要包含的基本库。我在 Google colab 上用的是 GPU,所以设备指示的是 cuda。

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

生成输入和输出张量

输入将是用嵌入大小 500 训练的 Word2Vec 向量。由于我们希望保持句子的长度不变,当句子的长度小于语料库中最长的句子时,将使用填充标记来填充多余的剩余单词。让我们使用下面的函数来训练 Word2Vec 模型。如果你有兴趣了解更多关于 Word2Vec 的知识,那么可以参考我之前的文章。模型是在整个语料库上训练的。

一旦模型准备好了,我们就可以创建一个函数来生成输入张量。

找到的句子的最大长度为 927。所以每个输入张量都是这个大小。为了创建输出张量,必须完成从标签到正值的映射。目前我们用-1 表示阴性,这在神经网络中是不可能的。输出层中的三个神经元将给出每个标签的概率,因此我们只需要映射到正数。功能如下:

定义 CNN

下面的代码显示了如何定义 CNN。一些参数,如窗口大小,过滤器的数量可以调整,以获得不同的结果。在这里,我已经加载了上面生成的模型,这是为了当你在不同时间训练 word2vec 和在不同时间运行 CNN 时,最好使用保存文件中的模型。

训练 CNN 模型

一旦定义了网络,我们就可以开始初始化用于训练的必要对象,如模型对象、损耗和优化对象。下面的代码显示了如何训练 CNN 模型。我已经运行了 30 个时代。在训练数据的每一步都记录损失。

测试模型

测试模型代码如下所示。损失图也绘制和代码保存图。当您正在进行多个实验,并且想要比较不同超参数的所有组合后的结果时,这非常有用。

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从损失图中可以清楚地看出,损失在稳步减少,并且损失波动不大,这表明学习率不是太高。与以前的方法相比,0.72的准确性非常好,在以前的方法中,决策分类器与 BOWTF-IDFWord2VecDoc2Vec 以及使用 PyTorch 的逻辑回归一起使用。这个精度接近我们使用简单的前馈神经网络的精度。因此,使用 CNN 总是没有必要的。根据问题的复杂性和可用于计算的资源,应该使用适当的方法。

所以现在你可以很容易地用这种方法对你自己的数据集进行实验!我希望这有助于您理解如何使用 PyTorch 构建 CNN 模型来对餐馆评论数据进行情感分析。请随意扩展这段代码!这适用于存在多个类别的任何其他文本分类问题。如果我可以考虑改进这个模型,我会使用不同的学习速率、时期、不同的窗口大小、嵌入大小、滤波器数量和其他优化算法,如SGDRMSProp等。预处理可以改为使用词汇化或其他词干算法来查看结果如何变化。你的项目有很大的试验空间。

一如既往—愉快的实验和学习:)

PyTorch 中基于逻辑回归的情感分类

原文:https://towardsdatascience.com/sentiment-classification-using-logistic-regression-in-pytorch-e0c43de9eb66?source=collection_archive---------25-----------------------

在 PyTorch 中实现逻辑回归,用于 Yelp 餐馆评论数据上的情感分类,其中输入特征是单词包(BOW)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迪皮卡·巴德在 PyTorch 中使用逻辑回归进行情感分类

将评论数据分类为不同情感的逻辑回归将在深度学习框架 PyTorch 中实现。这是为了熟悉 PyTorch 框架的基本功能,比如如何定义神经网络?以及如何在 PyTorch 中调优模型的超参数?将在这篇文章中讨论。在我之前的帖子中,决策树分类器与不同输入特征(如 BOWTF-IDFWord2VecDoc2Vec )的比较将在最后进行。

在那些帖子中,我已经讨论了预处理文本和加载数据的主题。这将类似于那些职位。让我们从加载数据开始。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迪皮卡·巴德提供的按情感分类的餐馆评论示例

加载数据

Yelp 餐馆评论数据集可以从他们的网站下载,那里的数据格式是 JSON。提供的数据实际上不是 python 可读的正确 json 格式。每一行都是 dictionary,但是为了使它成为有效的 json 格式,应该在文件的开头和结尾加上方括号,并在每一行的末尾添加,。将INPUT_FOLDER定义为 yelp review.json 文件所在的本地目录中的文件夹路径。将OUTPUT_FOLDER声明为一个路径,您希望在该路径中写入以下函数的输出。json 数据的加载和前 100,000 行的写入在下面的函数中完成:

一旦运行了上面的函数,您就可以为接下来的步骤将它加载到 pandas dataframe 中了。对于这个实验,只取了少量的数据,这样就可以更快地运行以查看结果。

探索数据

在加载数据之后,创建用于情感指示的新列。原始数据集中并不总是存在带有您想要的预测标签的某个列。在大多数情况下,这可以是一个派生列。对于这种情况,数据中的stars列用于导出情绪。

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在数据可用之后,完成从星星到情绪的映射,并绘制每个情绪的分布。

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

完成后,检查每个情感的行数。情感类别如下:

  1. 阳性:1
  2. 负数:-1
  3. 中性:0

这三种情绪的行数分布不均匀。在这篇文章中,不平衡类的问题将不会被处理,这就是为什么,简单的函数检索每个情绪的前几个记录被写。在本例中,top_n是 10000,这意味着将获取总共 30,000 条记录。

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如何对文本数据进行预处理?

预处理包括许多步骤,如标记化、去除停用词、词干化/词条化等。这些常用的技巧在我之前的弓的帖子里有详细的讲解。这里,下一阶段只解释必要的步骤。

为什么需要对这段文字进行预处理?—并非所有信息都对预测或分类有用。减少字数将减少模型的输入维度。这种语言的书写方式,包含了大量语法特有的信息。因此,当转换成数字格式,像大写,标点符号,后缀/前缀等字的具体特征。都是多余的。以相似单词映射到单个单词的方式清理数据,并从文本中删除语法相关信息,可以极大地减少词汇量。应用哪种方法和跳过哪种方法取决于手头的问题。

1.停用词的删除

停用词是在不同的自然语言处理(NLP)任务中作为前置步骤经常使用并从句子中移除的词。停用词的例子有:a、an、the、this、not 等。每个工具都使用一组稍微不同的停用词列表,但是在短语结构很重要的情况下,比如在情感分析的情况下,就避免使用这种技术。

移除停用字词的示例:

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从输出中可以看出,删除停用词就删除了获取情感所需的必要词,有时它可以完全改变句子的意思。在上面这段代码打印的例子中,很明显,它可以将否定句转换成肯定句。因此,对于情感分类来说,这个步骤被跳过。

2.标记化

标记化是将句子/文本分割成称为标记的单词阵列的过程。这有助于分别对每个单词进行转换,也是将单词转换为数字所必需的。执行标记化有不同的方式。我已经在我的上一篇文章的标记化部分解释了这些方法,所以如果你感兴趣的话可以去看看。

Gensim 的[simple_preprocess](https://radimrehurek.com/gensim/utils.html)允许你将文本转换成小写,并删除标点符号。它还有minmax长度参数,这有助于过滤掉那些长度范围内的罕见单词和最常见的单词。

这里,simple_preprocess用于获取数据帧的标记,因为它已经为我们做了大部分预处理。让我们应用此方法来获取数据帧的令牌:

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3。词干

词干处理将单词还原为其“词根”。不同于使用语法规则和字典将单词映射到词根形式的词干化,词干化只是删除后缀/前缀。词干分析广泛用于 SEOs、Web 搜索结果和信息检索的应用中,因为只要词根在文本的某个地方匹配,它就有助于检索搜索中的所有相关文档。

有不同的算法用来做词干分析。PorterStammer(1979)、LancasterStammer (1990)和 SnowballStemmer(可以添加自定义规则)。NLTK 或 Gensim 包可用于实现这些词干提取算法。兰开斯特比波特慢一点,所以我们可以根据大小和所需的响应时间来使用它。Snowball stemmer 是 Porter stemmer 的略微改进版本,通常比后者更受欢迎。不太清楚哪种方法会产生准确的结果,因此必须试验不同的方法,并选择给出更好结果的方法。在这个例子中,波特斯特梅尔被用来简单和快速。以下代码显示了如何在数据帧上实现词干,并创建新列stemmed_tokens:

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分成训练集和测试集:

训练数据将用于训练模型,测试数据是模型预测分类的数据,它将与原始标签进行比较,以检查准确性或其他模型测试指标。

  • 训练数据(用于训练 ML 模型的数据子集)~70%
  • 测试数据(用于测试从训练数据训练的 ML 模型的数据子集)~30%

尽量平衡两个集合中的类数,使结果不偏不倚或者成为模型训练不足的原因之一。这是机器学习模型的关键部分。在现实世界的问题中,存在不平衡类的情况,需要使用像过采样少数类、欠采样多数类(scikit-learn 包中的重采样函数)或使用 Imblearn 包中的 SMOTE 功能生成合成样本等技术。

在这种情况下,数据分为两部分,训练和测试,70%在训练中,30%在测试中。在进行拆分时,最好在训练和测试数据中平均分布类别。这里使用了 scikit-learn 包中的函数 train_test_split

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从上面的输出可以看出,数据是按比例分配给每个类的。打印训练和测试中每个情感的行数。

PyTorch 入门

PyTorch 是一个开源的机器学习库,用于计算机视觉和自然语言处理,基于 Torch 库。PyTorch 的主要特点是使用 GPU 和深度神经网络进行张量计算。张量被定义为torch.tensor它们是像 Numpy 数组一样的多维数字数组,但是能够在 GPU 上运行。你可以从这里通过简单的教程,熟悉不同类型的张量

PyTorch 中深度学习的构建模块

  1. 亲笔签名 PyTorch 使用了一种叫做自动微分的方法。在神经网络中,您需要计算梯度,这样可以节省操作次数,因为它会记录完成的操作,并重放这些操作来计算梯度。
  2. Optim 要使用torch.optim,我们必须使用构造优化器对象。这通常需要包含需要优化的模型参数和优化相关参数(如学习速率、权重衰减等)的 iterable。
  3. nn 神经网络可以使用torch.nn构建。一个nn.Module包含层和一个返回输出的方法 forward(input)。

下面的代码显示了需要包含的主库以及如何识别当前设备。加载张量和进行计算的位置由神经网络层中使用的不同函数中的device参数决定。我已经使用 google colab 进行了实验,并在硬件加速器中设置了运行时 GPU,这就是为什么我可以看到torch.cuda.is_available在我的情况下是正确的。

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我不会详细讨论神经网络是如何工作的,因为这不是这篇文章的主题。为了获得了解培训过程所需的基本信息,您可以在这里阅读

神经网络中使用的不同函数

通常在获得标注预测的输出图层上,softmax 函数与F.softmax一起使用。其他功能通过torch.nn.functional可用。目标函数是您的网络被训练以最小化的函数,在这种情况下称为损失函数或成本函数。在神经网络通过训练实例的每次迭代结束时计算损失。它们通过nn用于例如nn.NLLLoss()。为了优化网络,不同的算法,如 SGD,Adam 和 RMSProp 等。被使用。例如,要使用 SGD,您需要初始化optim.SGD。然后在初始化对象上的step()函数是在网络上完成优化的地方。

下面我们来深入探讨一下如何构建神经网络!我们将通过建立一个进行逻辑回归的基本网络来了解这一点。

生成输入和标签张量

第一步是要有函数,可以创建输入张量和相应标签,即输出张量,输入到网络进行训练。对于逻辑回归,我们将使用 BOW 向量作为输入,它只不过是语料库中词汇大小的数组,值是语料库中单词的频率,索引是单词的唯一 id。我们将从使用 Gensim 包的corpora.Dictionary构建的字典中获得唯一的 id。这类似于我在 BOW post 中所做的,但我添加了另一个名为 padding 的参数,它将用于其他任务,如 CNN,其中您希望对文档中的每个单词使用单词嵌入。对于此示例,填充被关闭。

之后,你就可以创建弓矢量函数了,如下所示。Vocab 大小为30056。您可以看到我在创建张量时是如何分配设备的:

为了创建输出张量,必须完成标签到正值的映射。目前我们用-1 表示阴性,这在神经网络中是不可能的。输出层中的三个神经元将给出每个标签的概率,因此我们只需要映射到正数。功能如下:

使用 BOW 的逻辑回归

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 Softmax (s rc 的逻辑回归函数示例

逻辑回归是一种回归模型,但当对每一类的预测概率使用阈值时,可用于分类问题。它使用 Sigmoid 函数或 Softmax 函数来获得类的概率。Softmax 函数通常用于多类分类的情况。标签节点的最高概率将被选为该输入的预测类标签。Softmax 取输入向量 i = 1,2,…,K,并输出由与输入数的指数成比例的 K 个概率组成的概率分布。输出大小相同,值在范围(0,1)内,它们的总和都是 1。每个元素 Xi 被应用到 softmax,j 从 1 到 k。它如何工作的例子如下所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Softmax 公式(s rc

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

逻辑回归的架构:

  • 输入将与字典中的词汇大小相同
  • 输出大小将与标签数量相同
  • 正向函数将首先运行线性图层,然后计算值的 Log softmax。
  • SGD 优化器通常用于逻辑回归,因此在这里也以适当的学习速率使用。

首先让我们通过创建一个继承nn.Module的类来定义神经网络。forward功能被覆盖,以告诉网络如何向前传球。

让我们初始化模型对象,使用负对数似然损失函数和 SGD 进行优化的损失函数。对于损失,通常使用交叉熵损失函数,在这种情况下,您不需要单独计算 Log Softmax。这里我们分开使用它,这样你可以了解每个步骤的组成部分,以及如何在其他情况下实现或改变它们,如二进制分类。

终于我们准备好训练模型了!😃

训练逻辑回归模型

下面的代码将在 epochs 设置为 100 的训练数据上训练模型。

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

测试模型

一旦模型准备好了,我们现在就可以测试它。为了比较这些数字,我把张量带回了 cpu。我们将使用用于获取测试数据集上的输入张量的相同函数。

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分类报告显示平均准确度为0.70。与用于训练的数据量相比,这是一个相当好的结果。可以对预测的概率值使用torch.argmax函数来获得标签。正面和负面情绪的准确性优于中性,这是有意义的,因为与正面和负面情绪中的常用词相比,很难区分中性评论。在上面的结果中,01分别代表负和正。

这种准确性优于我在以前的帖子中实现的方法,其中使用决策树分类器基于 BOW、TF-IDF、Word2Vec 和 Doc2Vec 向量作为输入进行分类。这表明,实现简单逻辑回归的神经网络可以更好地执行针对多个时期训练的简单 BOW 向量。它可以更好地提取词语和情感之间的关系并进行分类。本教程介绍 PyTorch 以及如何用它构建简单的分类器。

所以现在你可以很容易地用这种方法对你自己的数据集进行实验!我希望这有助于您理解如何使用 PyTorch 构建神经网络模型来对餐馆评论数据进行情感分析。请随意扩展这段代码!这适用于存在多个类别的任何其他文本分类问题。如果我可以考虑改进这个模型,我会使用不同的学习速率、时期,并尝试使用不同的输入类型,而不仅仅是弓。可以试试 TFIDF,Word2Vec,Doc2Vec 等。看看你会得到什么结果。预处理可以改为使用词汇化或其他词干算法来查看结果如何变化。

一如既往—愉快的实验和学习:)

基于逻辑回归的情感分类 Yelp 评论分析

原文:https://towardsdatascience.com/sentiment-classification-with-logistic-regression-analyzing-yelp-reviews-3981678c3b44?source=collection_archive---------14-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

柴坦尼亚·皮拉拉在 Unsplash 上的照片

几周前你刚开了自己的公司。

你想了解顾客对你的产品和服务的感受,所以你去了社交媒体平台(推特、脸书等)。)来看看人家都说了些什么。

哇哦。好像已经有几千条关于你的生意的帖子了。你很兴奋,但是你很快意识到自己一个人读完所有的书是不可行的。

你开始怀疑:有没有办法从文本信息中提取客户的情绪?

古玩目录

  1. 为什么情感分析很重要?
  2. 什么是逻辑回归?
  3. 我们应该使用哪些指标来评估模型?
  4. 模型如何理解文本输入?
  5. 我们如何知道哪些文本特征是重要的?
  6. 我们能进一步改进我们的模型吗?
  7. 接下来我们能做什么?

动机

情感分析是一种非常有效的工具,不仅可以让企业了解整体品牌认知,还可以评估客户对特定产品线或服务的态度和情感[1]。

这种数据驱动的方法可以帮助企业更好地了解客户,发现他们观点的微妙变化,以满足不断变化的需求。

概观

这篇文章是我探索 Yelp 数据集的第二部分。有关数据集的更多信息,以及对其业务数据和提示数据的一些探索性数据分析,请参见我下面的帖子:

[## 发现你下一个最喜欢的餐馆 Yelp 数据集上的探索和可视化

你用 Yelp 找好餐馆吗?这篇文章揭示了流行的 Yelp 数据集中的见解和模式。

towardsdatascience.com](/discover-your-next-favorite-restaurant-exploration-and-visualization-on-yelps-dataset-157d9799123c)

这篇文章主要关注 Yelp 数据集中的 review.json 文件,它包含了 Yelp 用户写的评论。此外,每个评论都包括一个相应的“星级”,或用户对企业的评级,可以作为情绪的代理。目标是建立一个模型,在给定文本数据的情况下,可以对评论的情绪进行分类(正面或负面)。此外,我们感兴趣的是哪些文本特征是对该分类任务最有帮助的预测器。

逻辑回归

一般来说,有两种不同类型的分类模型:生成式模型(朴素贝叶斯、隐马尔可夫模型等。)和判别型模型(Logistic 回归、SVM 等)。).最终,两个模型都试图计算 p(类|要素)或 p(y|x)。关键区别在于,生成模型试图首先对联合概率分布 p(x,y)建模,然后使用 Baye 定理计算条件概率 p(y|x),而判别模型直接对 p(y|x)建模。关于这两种模型之间的比较的详细讨论,参见吴恩达的论文这里

在这篇文章中,我将检验一个流行的判别模型——逻辑回归。关于它的数学基础(sigmoid 函数、成本函数、决策边界等)的更多细节,见这篇文章。).

要查看我的完整 Python 代码,请查看我的 Kaggle 内核或我的 Github 页面。现在让我们开始吧!

偷看评论

让我们从评论数据集中取出126】百万条记录来进行分析。“文本”列将是我们的模型输入。让我们来看看一个有积极情绪的随机评论(评分为 5.0):

"I love Deagan's. I do. I really do. The atmosphere is cozy and festive. The shrimp tacos and house fries are my standbys. The fries are sometimes good and sometimes great, and the spicy dipping sauce they come with is to die for. The beer list is amazing and the cocktails are great. The prices are mid-level, so it's not a cheap dive you can go to every week, but rather a treat when you do. Try it out. You won't be disappointed!"

当我们考虑特征提取时,这里的几个线索可以帮助我们推断这是积极的情绪,如“爱”、“舒适”、“为…而死”、“令人惊叹”和“不会失望”。

让我们看一个负面评价(评分 1.0):

"If I could give less than one star, that would have been my choice.  I rent a home and Per my lease agreement it is MY responsibility to pay their Pool Service company.  Within the last year they changed to PoolServ.  I have had  major issues with new techs every week, never checking PH balances, cleaning the filter, and not showing up at all 2 weeks in the past 2 months. I have had 4 different techs in the past 4 weeks.   I have emailed and called them and they never respond back nor even acknowledged my concerns or requests.  I cannot change companies but I'm required to still pay for lousy or no service.  Attached are a couple pictures of my pool recently due to one tech just didn't put any chlorine in it at all according to the tech who came the following week to attempt to clean it up.  Please think twice before working with these people.  No one wants to work with a business that doesn't return phone calls or emails."

尽管它很长,但我们仍然可以看到诸如“不到一星”、“糟糕”、“从不回复”、“没有服务”等线索是有用的预测因素。

让我们快速确认数据中没有缺失值:

text     0.0
stars    0.0
dtype: float64

将评分转化为积极和消极情绪

让我们绘制收视率分布图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到,在 100 万条评论中,几乎有一半包含 5.0 的评级。

在这个任务中,我们希望将所有的评论文本分为两类:积极情绪和消极情绪。因此,我们首先需要将“星星”值转换成两个类别。在这里,我们可以将‘4.0’和‘5.0’视为正面情绪,将‘1.0’和‘2.0’视为负面情绪。我们可以将“3.0”视为中性,甚至将每个明星视为其自己的情感类别,这将使其成为一个多类分类问题。但是,为了简单的二进制分类,我们可以将带有“3.0”的排除在外。

然后,我们将积极情绪编码为 0 类,消极情绪编码为 1 类。因为我们知道类 0 中的样本比类 1 中的样本多,所以我们的基线模型可以是简单地将每个评论标记为类 0 的模型。让我们检查一下基线精度:

0.74

在我们继续之前,让我们花点时间了解一下评估指标。

评估指标

Jason Brownlee 在他的一篇文章中说,“分类器的好坏取决于用来评估它的度量标准”[2]。

在每个分类问题中,准确性可能不是合适的评估标准,特别是当类别分布不平衡时,以及当假阳性和假阴性的业务影响不相等时。例如,在信用卡欺诈检测问题中,预测每个交易是非欺诈的基线模型将具有超过 99.99%的准确度,但这并不意味着它是合适的模型,因为假阴性的百分比将是 100%,并且与假阳性相比,每个假阴性(未检测到欺诈交易)对企业和客户来说可能具有高得多的成本。

在此任务中,以下是一些合适的评估指标:

  1. 精度 — TP/(TP+FP),意思是模型归类为阳性的点实际上是阳性的比例。
  2. 回忆 — TP/(TP+FN),意思是被模型正确分类的实际阳性的比例。
  3. F1 得分—精确度和召回率的调和平均值。

查看这篇文章,了解关于这些指标的更多详细讨论。

在此任务中,我们将使用测试集上的 F1 分数作为关键评估指标。

列车测试分割

让我们留出 30%的数据作为测试集,按类标签分层。

train, test = train_test_split(df_reviews, test_size = 0.3, stratify = df_reviews['labels'], random_state = 42)

文本预处理

在大多数文本挖掘或 NLP 相关的任务中,清理文本是至关重要的一步。让我们首先删除所有非字母字符,标点符号,并确保所有字母都是小写字母。稍后我们还将评估移除停用词和词干化/词条化的效果。

矢量化

为了让模型能够处理文本输入,我们需要将它们转换成向量。有几种不同的方法来表示这些文本特征,下面是最常见的几种:二进制,例如,单词“good”是否存在。2.计数,例如“好”这个词在这篇综述中出现了多少次,类似于朴素贝叶斯中的单词袋模型。3.TF-IDF,这是与文档相关的每个文本特征的加权重要性(在此阅读更多)。

让我们首先尝试使用所有单字的二进制表示。

cv= CountVectorizer(binary=True, analyzer = text_prep, min_df = 10, max_df = 0.95)
cv.fit_transform(train['text'].values)
train_feature_set=cv.transform(train['text'].values)
test_feature_set=cv.transform(test['text'].values)

我使用了 sklearn 的 CountVectorizer 对象来提取所有的单词特征,如果这个单词出现在少于 10 条评论或超过 95%的评论中,就会被排除。

让我们检查一下我们的字典里有多少独特的单词:

train_feature_set.shape[1]
--------------------------------------------------------------------
40245

大约有 40K 个独特的单词。例如,让我们检查单词“tasty”的索引:

cv.vocabulary_['tasty']
--------------------------------------------------------------------
35283

适合 LR 车型

现在,我们准备使用 sklearn 拟合我们的第一个逻辑回归模型:

lr = LogisticRegression(solver = 'liblinear', random_state = 42, max_iter=1000)
lr.fit(train_feature_set,y_train)
y_pred = lr.predict(test_feature_set)
print("Accuracy: ",round(metrics.accuracy_score(y_test,y_pred),3))
print("F1: ",round(metrics.f1_score(y_test, y_pred),3))
--------------------------------------------------------------------
Accuracy:  0.955
F1:  0.914

让我们绘制一个混淆矩阵来可视化预测结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可视化特征重要性

逻辑回归的一个好处是,我们可以很容易地找到每个特征的重要性。让我们想象一下与消极情绪最相关的前 10 个词:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这些话在我们的预料之中。让我们检查所有包含“中毒”一词的评论,看看有多少属于负面情绪类别:

0.904

同样,让我们来看看积极情绪的前 10 个相关词:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意,高肯定特征重要性与类 1 的高可能性相关,低否定(高绝对值)特征重要性与类 0 的高可能性相关。

在检查了积极的评论后,我意识到“对接”这个词在顶部,因为大多数给“4.0”星的评论都提到“我对接一颗星是因为……”。

改进策略

在我们建立了第一个模型之后,让我们检查几个想法,看看我们的模型是否可以进一步改进。

想法 1:降低概率截止阈值

为了减少假阴性,一种直觉是降低截止阈值(默认为 0.5)。这将提高召回率,但也会降低准确率。因此,我们需要检查这是否会提高 F1 的整体得分:

******** For i = 0.3 ******
F1: 0.91

******** For i = 0.4 ******
F1: 0.915

******** For i = 0.45 ******
F1: 0.915

******** For i = 0.5 ******
F1: 0.914

我们可以看到,F1 分数对该阈值的变化相对稳健。

想法 2:过采样类别 1 或欠采样类别 0

过采样少数类和欠采样多数类是处理不平衡分类的常用方法(此处阅读更多)。然而,在这种情况下,F1 分数并没有提高。

过采样等级 1 的性能:

Accuracy:  0.95
F1:  0.908

欠采样等级 0 的性能:

Accuracy:  0.947
F1:  0.904

想法 3:去掉停用词和词干

去除停用词和词干可以去除噪音,从而减少词汇量。然而,准确度和 F1 分数都略微降低

Accuracy:  0.949
F1:  0.902

想法 4:用 TF-IDF 代替二进制表示

这一次,F1 分数略有增加,但增幅不大。

Accuracy:  0.958
F1:  0.919

想法 5:将单词和双词作为特征包含进来

动机可以用这个例子来说明:

以我们最初开发的 LR 模型为例,然后在这篇评论中预测——“我不喜欢食物或服务”:

test_review = cv.transform(["I did not enjoy the food or the service"])
lr.predict_proba(test_review)
--------------------------------------------------------------------
array([[0.50069323, 0.49930677]])

该模型认为这一评论是积极的,因为该模型只接受与积极情绪相关联的单词,如“enjoy”,而不考虑否定“enjoy”含义的“did not”。

在我们把单词和双词(两个单词的序列)都考虑进去之后,我们首先看到词汇量的增加:

488683

拟合该模型后,我们看到两个指标都有所改善,尤其是 F1 得分。

Accuracy:  0.969
F1:  0.942

让我们再次检查它对同一个句子的预测:

test_review = cv.transform(["I did not enjoy the food or the service"])
lr.predict_proba(test_review)
--------------------------------------------------------------------
array([[0.2678198, 0.7321802]])

现在,它以相对较高的可信度做出正确的预测。

我们可以再次看到正面和负面情绪的 10 大新特征:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们开始看到诸如“两颗星”、“不值得”、“不谢谢”、“不推荐”之类的二元词出现在热门功能中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

请注意,“失望”可能看起来是一种消极的情绪,但这可能是因为它是“不要失望”短语的一部分。

我们应该包括三元模型(三个单词的序列)或者更高阶的 N 元模型吗?请注意,随着我们包含越来越高阶的 N 元文法,我们的特征大小变得越来越大,这消耗了更多的内存空间,并且还存在极度稀疏的问题(在此阅读更多)。

后续步骤

以下是对后续步骤的一些想法:

  1. LR 模型中有几个超参数我们可以调整,调整它们可以得到更优化的模型。此外,为了避免过度拟合,尝试交叉验证以获得每个模型更准确的指标(点击这里查看 grid search)。
  2. 将“3.0”视为中性,或将每个类别视为其自己的类别,并重新制定这些模型。
  3. 建立一个交互式情感分析器,允许用户输入评论,并给出对其情感的预测。具有增量学习的内置功能,用户可以帮助模型在做出错误预测时进行学习。

摘要

让我们回顾一下。

我们使用逻辑回归建立了一个情感分类模型,并尝试了不同的策略来改进这个简单的模型。在这些想法中,包含二元模型作为特征对 F1 分数的提高最大。对于简单模型和改进模型,我们还分析了其最重要的文本特征。

我希望你喜欢这篇文章,并请分享你的想法:)

DS/ML 初学者?查看我的另一篇关于如何使用经典 Iris 数据集构建第一个 Python 分类器的帖子:

[## 使用 Python Scikit-learn-Iris 数据集探索分类器

如何用 Python 构建第一个分类器的分步指南。

towardsdatascience.com](/exploring-classifiers-with-python-scikit-learn-iris-dataset-2bcb490d2e1b)

参考

[1]http://copper Taurus . com/insights/opinion-analysis-Product-management/#:~:text = Product % 20 reviews % 20 provide % 20 opinion % 20 analysis,successive % 20 refinement % 20 of % 20 their % 20 offering。【2】https://machine learning mastery . com/tour-of-evaluation-metrics-for-unbalanced-class ification/

使用 VADER 的情感分析

原文:https://towardsdatascience.com/sentimental-analysis-using-vader-a3415fef7664?source=collection_archive---------1-----------------------

情绪的解释和分类

情感分析是一种文本分析方法,它检测文本中的极性(例如,正面意见或负面意见,无论是整个文档、段落、句子还是子句。

情感分析旨在基于文本中主观性的计算处理来测量说话者/作者的态度、情感、评价、态度和情绪。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:MonkeyLearn,图片链接:【https://bit.ly/2X806dW

为什么情感分析很难执行?

尽管理论上看起来很容易,但情绪分析是一个棘手的问题。一个文本可以同时包含多种情感。举个例子,

“演技不错,但电影本来可以更好”

上面这句话包含了两个极性!!!

VADER

VADER(用于情感推理的效价感知词典)是用于文本情感分析的模型,其对情感的极性(积极/消极)和强度(强度)都敏感。它在 NLTK 包中提供,可以直接应用于未标记的文本数据。

VADER 情感分析依赖于一个字典,该字典将词汇特征映射到情感强度,即情感得分。文本的情感得分可以通过对文本中每个词的强度求和得到。

例如,像*、【爱】、【享受】、【快乐】、【喜欢】这些词都传达了一种积极的情绪。VADER 也足够聪明,能够理解这些词的基本语境,比如*“不爱”作为否定陈述。也理解大写和标点符号的侧重点,比如“享受”**

极性分类

我们不会试图确定一个句子是客观的还是主观的,是事实还是观点。相反,我们只关心文本是否表达了积极的、消极的或中立的 T21 观点。

文档级范围

我们还会尝试将一个文档或段落中的所有句子聚合在一起,得出一个整体观点。

粗略分析

我们不会尝试进行精细的分析来确定积极/消极的程度。也就是说,我们并不试图猜测一个评论者给了多少颗星,只是想知道这个评论是正面的还是负面的。

主要步骤:

  • 首先,考虑被分析的文本。一个基于整段评论的模型可能是无效的。确保为手头的任务使用合适的模型。
  • 接下来,决定要执行的分析类型。一些基本的情感分析模型更进一步,考虑两个词的组合,或二元模型。我们将学习完整的句子,为此我们将导入一个名为 VADER 的经过训练的 NLTK 词典。

要使用的数据集

对于这个模型,您可以使用各种数据集,如亚马逊评论、电影评论或任何产品的任何其他评论。

**import** **nltk**
nltk.download('vader_lexicon')**from** **nltk.sentiment.vader** **import** SentimentIntensityAnalyzer

sid = SentimentIntensityAnalyzer()

VADER 的SentimentIntensityAnalyzer()接受一个字符串,并返回四个类别的得分字典:

  • 否定的;消极的;负面的;负的
  • 中立的
  • 积极的
  • 复合值*(通过对上述*的分数进行归一化计算)

让我们通过情感分析仪来分析一些随机陈述

a = 'This was a good movie.'
sid.polarity_scores(a)OUTPUT-{'neg': 0.0, 'neu': 0.508, 'pos': 0.492, 'compound': 0.4404}a = 'This was the best, most awesome movie EVER MADE!!!'
sid.polarity_scores(a)OUTPUT-{'neg': 0.0, 'neu': 0.425, 'pos': 0.575, 'compound': 0.8877}

使用 VADER 分析评论

**import** **numpy** **as** **np**
**import** **pandas** **as** **pd**

df = pd.read_csv('../TextFiles/reviews.tsv', sep='**\t**')
df.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

df['label'].value_counts() OUTPUT-neg    5097
pos    4903
Name: label, dtype: int64

清理数据(可选)

这一步是为了清除评论中的任何空白。

*# REMOVE NaN VALUES AND EMPTY STRINGS:*
df.dropna(inplace=**True**)

blanks = []  *# start with an empty list*

**for** i,lb,rv **in** df.itertuples():  
    **if** type(rv)==str:            
        **if** rv.isspace():        
            blanks.append(i)     

df.drop(blanks, inplace=**True**)

向数据帧添加分数和标签

现在,我们将向原始数据帧添加列,以存储 polarity_score 字典、提取的复合得分以及从复合得分派生的新“正/负”标签。我们将使用这最后一列来执行准确性测试。这种方法中的评论将被分为负面、正面和中性比率。

df['sc ores'] = df['review'].apply(**lambda** review: sid.polarity_scores(review))

df.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在会将化合物作为单独的列,所有大于零的值都将被视为正面评价,所有小于零的值都将被视为负面评价。

df['compound']  = df['scores'].apply(**lambda** score_dict: score_dict['compound'])

df.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

df['comp_score'] = df['compound'].apply(**lambda** c: 'pos' **if** c >=0 **else** 'neg')

df.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以现在我们有了一个完整的分析,无论是正面的还是负面的。

现在让我们通过一些新的评论来测试我们的模型表现如何!

*# Write a review as one continuous string (multiple sentences are ok)*
review = 'The shoes I brought were amazing.' # Obtain the sid scores for your review
sid.polarity_scores(review)OUTPUT-
{'neg': 0.0, 'neu': 0.513, 'pos': 0.487, 'compound': 0.5859}review='The mobile phone I bought was the WORST and very BAD'# Obtain the sid scores for your review
sid.polarity_scores(review) OUTPUT-{'neg': 0.539, 'neu': 0.461, 'pos': 0.0, 'compound': -0.8849}

结论

VADER 分析的结果似乎不仅显著,而且非常令人鼓舞。结果显示了在网站中文本数据可能是一系列文本的复杂混合的情况下,通过利用 VADER 将获得的优势。

额外资源

我在《走向数据科学》杂志上发表了两篇关于这个博客相关主题的文章。为了更好地理解自然语言处理,请阅读这些文章

词干 vs 词汇化——https://link.medium.com/JWpURpQjt6

词语向量和语义—https://link.medium.com/tuVCswhYu6

这只是对什么是情绪分析以及 VADER 如何工作的一个小小的窥探。如有任何疑问和澄清,请随时回复本博客。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值