规模语义学:BERT + Elasticsearch
随着 BERT、bert-as-service 等工具的出现,以及对弹性搜索中密集向量操作的支持,大规模语义搜索成为可能。虽然程度可能因使用案例而异,但搜索结果肯定会受益于用语义结果增加基于关键字的结果…
跨文本库的基于关键字的搜索是一项众所周知的技术。Lucene 库和像 Elasticsearch 这样的工具擅长闪电般快速地检索给定查询的匹配文档。搜索结果由术语/令牌及其周围的 tf-idf 指标驱动。一般来说,与查询不共享任何公共术语的文档不会成为结果集的一部分。这是基于关键字的搜索的特征。这可以清楚地排除许多否则相关的文档,但是那些不与查询共享任何关键词的文档。仔细使用同义词和词干有助于提高回忆。但是什么时候同义词意味着完全平等了?我们可能认为 sunny 是 bright 的同义词。有明月却没有晴月——从来没有。至少在我们的星球上没有!此外,用于扩展查询的罕见同义词可能会将本来较差的结果推到最前面。还有词干?是的,让我们甚至不要谈论它。一个流道不同于流道或流道!显然,所有这些围绕关键词的阴谋诡计都无法解决文本中的语义问题。
诸如潜在语义分析(LSA)的方法在过去已经被用于在搜索结果中包括语义相关的文档。但是奇异值分解(SVD)在从分布在节点集群上的数百万个文档构建的术语-文档矩阵上的应用不是微不足道的。基于奇异值分解的语义搜索在弹性搜索处理的规模和吞吐量上是不切实际的。那么,如果我们想用 Elasticsearch 实现语义,我们该怎么办呢?
最近流行的单词固定大小的数字向量会有所帮助。使用单词包方法的单词嵌入可以将一个句子或一个文档变成一个短的密集的数字向量。我们已经在以前的 文章中详细讨论过这个问题。像从像 BERT 这样的语言模型中获得的嵌入是上下文敏感的,也不同于一键单词向量或 fastText 嵌入。也就是说,对于“吃饭为了活着”vs“活着为了吃饭”,我们用 BERT 得到不同的句子向量,让我们可以区分它们。实现大规模语义搜索的关键是将这些向量与弹性搜索相结合。
幸运的是,Elasticsearch 的当前版本(7.3+)支持一个具有各种相关性度量的 dense_vector 字段,如余弦相似性、欧几里德距离等,可以通过 script_score 计算。这正是我们所需要的,因为我们可以通过查询的密集向量表示,根据这些指标的得分对索引中的文档进行排序。弹性搜索闪电般的速度适用于分布在一群节点上的数百万个密集向量。这基本上是这篇文章的主旨。让我们开始吧。
1.作为经纪人的伯特
架构再简单不过了。这些部分都在开源中,我们所要做的就是把它们放在一起。我们使用 bert-as-service 来获得文档和查询的密集向量表示。索引和搜索请求通过 BERT 服务器代理,该服务器为所提供的文档或查询文本生成密集向量。
Figure 1. Indexing and querying BERT dense vectors in an Elasticsearch index
下面是一个简单的配置,它用一个句子(在我们的例子中是一个短引号)和它的数字向量作为唯一的字段定义了一个索引。根据无壳基 BERT (无壳 _L-12_H-768_A-12),向量定义为 768 长。
从文件中读取报价,通过调用 bert-as-service 计算密集向量,并批量索引到 Elasticsearch 中。
2.使用密集向量的相关性评分
Elasticsearch 采用 Lucene 的实用评分功能进行传统的基于关键字的搜索。它不适用于我们这里,因为我们处理的是数值向量。我们可以用密集向量周围的任何定制评分函数来覆盖缺省值。但出于效率原因,最好使用弹性搜索预定义的函数,如余弦相似性、L1 或 L2 范数。搜索结果的相关性顺序当然会根据使用的度量标准而有所不同。不确定是否其中一个总是比其他的好,但是余弦相似性似乎对我的测试很好。让我们看一个简单的例子。考虑下面一个文件中的三句话 sentences.txt 。
- 在公园玩耍的孩子们
- 在草地上奔跑的孩子们
- 今天交通很糟糕
明明前两句差不多。它们都与第三个不同。我们可以很容易地计算每个句子的向量,并计算不同的度量。运行Bert _ sentence _ similarity . py与:
pipenv run python ./bert_sentence_similarity.py sentences.txt
我们得到:
1 & 2 1 & 2 2 & 3
Cosine Similarity: 0.852 0.677 0.69
Inv. Manhattan Distance (L1): 0.083 0.056 0.058
Inv. Euclidean Distance (L2): 1.839 1.243 1.27
所有指标都做了正确的事情,为 1–2 对得出了最高分。在这篇文章的剩余部分,我们将坚持使用 BERT 查询的余弦相似度&句子密集向量作为弹性搜索的相关性分数。如果我们选择 L1 或 L2,热门搜索的顺序会有所不同,但我们在这里的任务是比较 BERT powered 搜索与传统的基于关键字的搜索。
3.引文索引
为了测试这个方案对我们的效果如何,我们准备了一个相当大的索引,由不同的人引用的话组成。该索引类似于第 1 节中的索引。引语长度不超过 50 个单词。该索引有几千条引用,其中肯定有一些重复的。以下是一个示例:
一旦游戏结束,国王和士兵回到同一个盒子里
他使用统计学,就像一个醉汉用灯柱来支撑而不是照明一样
你应该经常去参加别人的葬礼;否则他们不会去你家。
其他星球上的智慧生命?我甚至不确定地球上有没有!
我们知道许多不同的引语表达了相似的意思。我们用一个引用(或它的转述版本)查询索引,并检查顶部结果的质量。我们希望看到最热门的搜索结果与我们所理解的查询引用*——最相似*。我们可以用 Elasticsearch 提供的更像这个 (MLT)的查询直接做到这一点,当然还有我们的 BERT 衍生向量的余弦相似性,如图 1 所示。任务是评估伯特向量是否有意义地提高了结果的质量。
查询弹性搜索
对于 MLT 查询,我们覆盖了一些默认值,以便不排除查询或文档中的任何术语。对于我们用于语义搜索的 script_score 查询,我们从 bert-as-service 获得密集的 query_vector。我们从以下内容开始:
bert-serving-start -model_dir $BERT_BASE_DIR -max_seq_len=52 -num_worker=1
其中 BERT_BASE_DIR 指向磁盘上uncased _ L-12 _ H-768 _ A-12所在的目录。下面是以这两种不同方式查询同一索引的代码片段。
4.结果
设备准备就绪后,剩下要做的就是运行一些示例查询/报价,看看 BERT powered Elasticsearch 是否能够返回比仅基于关键字丰度更有意义的结果。我们挑选了一些引言,并列比较了伯特& MLT 公司的 10 大业绩。每个结果都是根据比赛质量评分的——红色 1 分,蓝色 0.5 分,默认 0 分——当然是主观的。让我们从第一个开始。
抓住愤怒不放就像抓住一块热煤想要扔向别人——你才是那个被烧伤的人
佛
Figure 2. BERT 6 & MLT 2.5
MLT 的热门歌曲完全不相关,因为它已经被“某人”这个词劫持了。它的第八次点击被“煤”误导了,这是一个在引语库中可能很少见的术语。但是 MLT 的第六支安打被伯特错过了。总的来说,我们看到 BERT 能够提取出与查询意思相同但使用了与查询不同的单词的引号(参见 4、6 和 7)。让我们看看另一个。
对于大多数人来说,他们认定自己有多幸福,就有多幸福
亚伯林罕·林肯
Figure 3. BERT 3 & MLT 0
在这种情况下,对 MLT 来说不幸的问题是查询中使用的词的选择。最热门的话题是伯特锁定了它,但 MLT 错过了它,因为它正在寻找术语“头脑”或“制造”,但看到了“头脑”和“制造”。它自己的热门搜索完全被短语“下定决心”所占据——与查询完全匹配。使用词干分析器可能有助于 MLT 抓住伯特的热门歌曲。对于它的第二部热播剧,MLT 可能被一个可能很少见的词岔开了,比如“乡亲”和短语“化妆”,因为这句话肯定不是关于打扮的!
但是基于以上两个例子,认为 BERT 已经对传统的一键词向量造成了致命的打击还为时过早。以最后这个例子为例。
命运站在敢于冒险的人一边
维吉尔
Figure 4. BERT 0 & MLT 4
敢这个词可能是回购协议中一个罕见的词,MLT 很好地坚持了这个词,也许是整个短语谁敢。另一方面,BERT 认为(从逻辑上来说,我们可以加上) dares 与激情、灾难等相关,并发现非常不同的匹配。这通常是过度思考的问题,有人可能会说——这是对伯特不友好的攻击。
5.结论
我们已经证明,我们可以通过 Elasticsearch 获得大规模的语义搜索结果。随着 BERT、bert-as-service 等工具的出现,以及对弹性搜索中密集向量操作的支持,这一切都成为可能。语义搜索结果的质量将取决于索引中文档的性质以及语义在这些文档中是否重要。语义在大多数自由流动的文本和演讲中是很重要的,除非你在谈论方程式!这是我们结束前的最后一段引言。
数学课本如此深奥和专业的一个原因是因为所有的规范和条件都必须放在定理上,以防止它们出现裂缝。从这个意义上说,它们就像法律文件,读起来也同样有趣。
大卫·埃德加·华莱士
因此,可能会有一些没有语义或文字游戏的枯燥文本,但很少有人会想去读它,更不用说去搜索它了。所以我们开始了——任何值得搜索的文本都有语义相关的文档。
总结一下:
- 我们已经看到,用 BERT 向量增强的 Elasticsearch 可以从文档报告中提取出语义相关的结果
- 坚持使用实际的关键词有时是正确的做法,而不是像 BERT 那样试图找到向量之间的关系。在这种情况下,基于关键字的搜索占了上风。
- 有了基于关键字的结果,我们可以很容易地解释为什么一个结果上升到顶部。甚至有一个解释 api 来挖掘如何获得相关性分数的细节。这对于基于关键字的方法来说是一个明显的优势。
- 有了 BERT 驱动的语义结果,就很难理解为什么一个结果出现在最上面或者没有出现。正是密集向量决定了相关性分数。谁知道这些密集向量的所有参数是什么……除此之外,还有 1.1 亿个密集向量!
那么,我们准备好用这些语义结果取代传统的基于关键词的结果了吗?不。但是增加他们并让最终用户投票也许没有坏处!
….
原载于 2019 年 10 月 29 日【http://xplordat.com。
半监督人脸标记
使用人工智能构建您自己的自动人脸标记应用程序
自动面部标记是指当你从手机中点击一张新图片并将其上传到社交媒体时,它能够识别图像中的人,并建议你标记他们。它广泛出现在各种应用程序和平台中,包括谷歌照片和脸书。
我的目标是构建一个应用程序,可以将所有相似的面孔组合在一起。该应用程序应该是健壮的,应该根据添加的图像进行调整,而不需要任何人工干预。
这可能是你的数据科学项目之一,可以为你的简历增加价值。
这是我期望系统做的-
- 将相似的图像分组在一起;我只需要给它们贴上标签。
- 在任何新图像的情况下,它应该自动处理它们。它应该直接问我新面孔的标签。
- 只需点击一个按钮或脚本,即可改进(重新培训)系统。
这是我遵循的方法。
无人监督的
我想到的第一个方法是使用 K-Means 聚类算法。我想把相似的面孔聚集在一起,但有一个问题。您需要指定想要组成的组的数量。这给你如何决定一个最佳的 k 值制造了许多障碍。
例如,如果你有 400 张照片想要添加标签,但令人惊讶的是,所有的照片都是同一个人。在这种情况下,确定 k 的最佳值几乎是不可能的。
这种方法没有像我预期的那样奏效。
监督
我需要某种监督学习,因此我决定用 KNN 算法。我创建了一个文件夹,其中有以图像名称作为标签的唯一图像。它的工作,但这不是我最初想要的。
如果我试着在一些相似的新图像上使用它,那么它不会把它们聚集在一起。它只是要告诉他们是否属于被监督的组。
此外,随着不同的新面孔的加入,我需要在代表该群体的独特图像上重新训练模型。工作量太大了。
半监督的
如果你的算法超级聪明,可以像聚类一样将相似的脸分组在一起,但没有指定 k 值,那会怎么样?这就是为什么我称这种方法为半监督。
所以,这就是我们要做的。
- 将所有需要标记的图像复制到一个文件夹中。
- 迭代图像,找到所有图像的面部标志点,并将它们存储在字典中。
- 获取第一幅图像及其面部标志点,并在其上训练 KNN。
- 迭代所有剩余的图像,并尝试用新的关键点预测结果。如果它们匹配,则认为它是相同的图像,否则在其上训练新的分类器。
- 最终,每个集群都有一个单独的 KNN 模型。
这种方法可能看起来有点奇怪,但是让我们了解一下在处理一些新图像时它的一些好处。
我们正在考虑一些新图像需要预处理的情况。
- 我们将通过已经存在的每个分类器来预测新图像的结果。如果它与其中任何一个匹配,那么它将被添加到该组。
- 在不匹配的情况下,将执行类似于训练的步骤,并且将训练新的分类器,并且将在其上预测所有剩余的图像。
这是开始自动面部标记的最基本的方法之一。
那么,我们如何给被创建的组贴标签呢?
我有一个想法,但不是最聪明的。您需要创建一个映射,将一个人的名字与该组进行映射。每当添加新图像时,它会告诉使用该映射的人的姓名。听起来很酷,对吧?
这是 GitHub 回购协议的链接。
装置
我在普通的 python 环境下安装 dlib 有困难,这就是我选择 Docker 的原因。
如果你已经成功安装了工作 dlib,那么只需将face _ recognition _ API . py文件复制到半监督或监督文件夹,你需要将方法从 docker 改为 direct type。
如果你不想要这种混乱,那么就使用 docker。如果你想了解更多关于 Docker 的信息,这里有一个链接。
使用 Docker 的机器学习模型部署中的动手学习,并提供各种示例。
towardsdatascience.com](/docker-made-easy-for-data-scientists-b32efbc23165)
要使用 Docker way(假设您已经安装了 Docker ),请在面部标志 API 文件夹中键入以下命令。
$ docker build -t facial_landmark_api .
$ docker run -d -p 5000:5000 facial_landmark_api
要检查 docker 容器是否正在运行,请键入
$ docker ps -a
你会看到一个集装箱在运行。
在此之后,将所有您想要分类的图像复制到图像文件夹中(在半监督文件夹中)。然后简单运行 classify.py 文件。
这个项目从一开始就很好。
这里有一些你可以贡献的东西。
- 将重新训练模型的脚本。
- 考虑到我们希望面向百万用户进行扩展,这是一个更好的架构。
- 任何你想补充的东西。
快乐学习!
使用 Python SMTP 发送数据警报
数据专业人员权威指南(商业智能)
使用 Python smtplib 发送业务报告和电子邮件
Source: Unsplash
问题陈述:
对于数据科学家来说,将报告传达给非技术用户是非常重要的,尤其是当数据中存在异常时。根据我的工作经验,大多数分析师会手动向用户发送业务报告,他们会处理数据,运行一些验证,并生成报告通过 Outlook/gmail 发送。所有这些都需要很多时间,并为人为错误留有余地。
如果我们可以通过分析我们数据的同一个 Python 程序来自动化业务报告和警报,那会怎么样?
这将节省大量的时间和麻烦!
输入 Python SMTP 库
简单邮件传输协议(SMTP)允许用户向另一个用户发送邮件。当您从 Python 应用程序推送邮件时,用户将通过邮局协议(POP)和互联网消息访问协议(IMAP)接收邮件。这些是 Outlook 和 Gmail 等标准电子邮件界面的基础。
SMTP 允许服务器通过 TCP 连接监听电子邮件请求,然后通过端口 587 发送电子邮件。
Python SMTP 的实际应用
Our Application in Big Picture, today we are going to learn how to build Python Alerts with SMTP
在本教程中,您将学习如何使用 Python SMTP 生成电子邮件提醒。
我们将访问我们以前在构建仪表板 Web 应用的项目,使用任务调度器到从 Lazada (电子商务)网站抓取数据,并将其转储到 SQLite RDBMS 数据库。根据用户确认的产品,我们将检查价格是否从原始价格下降,并使用它来生成警报。
请随意欣赏这篇文章或访问我的 Github Repo 获取完整代码。或者向下滚动,欣赏这篇文章。
对于不熟悉我提到的仪表板 Web 应用程序的人来说。我正在建立一个基于 Lazada 报废产品价格变化的仪表板。每当用户修改输入下拉菜单时,如果产品价格下降,应用程序会提醒用户。
我们的任务是分配一个 webhook,一旦你修改了下拉菜单参数,它就会发送一封电子邮件。
Price Optimization Dashboard Scraped from Lazada over 3 days + Email SMTP
以下是实现这一目标的关键步骤:
- 导入 Python SMTP
- 生成电子邮件和 SMTP 服务器属性
- 发送电子邮件请求
- 使用我们的 Python Dash 仪表板准备 Webhooks
- 为电子邮件验证机密创建配置文件
导入和激活 Python SMTP
当您下载 Python 包时,Python smtplib 已经内置。如果您已经运行了它,您可以像下面这样导入 smtplib
import smtplib
要激活 SMTP 连接,您需要通过替换如下标记来插入您的身份验证凭据:
gmail_user = '<input your gmail user. No need to use @gmail.com>'
gmail_password = '<input your gmail password>'
仅此而已!非常容易。
Source: Giphy
生成电子邮件和 SMTP 服务器属性
创建电子邮件时,您需要考虑两个部分:电子邮件属性和电子邮件发送请求。
在邮件属性中,您可以设置:
#email properties
sent_from = gmail_user
to = [<email_send_address>]
subject = 'Alert for reduced in price'
email_text =
"""
Alert for reduced in price
"""
sent_from
:发送邮件的发件人to
:邮件的收件人subject
:邮件主题email_text
:邮件正文的内容。
在电子邮件发送请求中,这是设置 SMTP 服务器的位置:
#email send request
try:
server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
server.ehlo()
server.login(gmail_user, gmail_password)
server.sendmail(sent_from, to, email_text)
server.close()
print ('Email sent!')
except Exception as e:
print(e)
print ('Something went wrong...')
smtplib
正在设置带有 gmail 安全套接字层(SSL)通道和端口 465 的 SMTP 服务器。这将允许我们从自己的 gmail 帐户发送邮件。ehlo()
用于向服务器表明您的身份。这将检查我们设置的 gmail 服务器的 SMTP 服务扩展(e SMTP)。login
非常简单。我们将使用自己的用户名和密码登录 gmail 服务器。- 然后,我们将使用我们的帐户
sendmail
来使用我们之前设置的电子邮件属性。 - 最后,我们将
close
服务器,这将为服务器释放我们的端口 465 和内存使用。
发送电子邮件请求
一旦设置了属性,一切都设置好了。你只需要运行它。还有…
您遇到了问题。
解决您的问题
您很可能会遇到这个错误
Troubleshooting credentials/gmail server error
如果您遇到此错误,并且您已经仔细检查了您的用户名和密码是否正确,那么您需要打开您的 Gmail 设置并打开不太安全的应用程序访问选项。这将允许 Python SMTP 访问您的 gmail 帐户并代表您发送电子邮件。
当然,为了维护您的 gmail 安全,您可以在不再使用该功能后将其关闭。
Activating the option “Less Secure App Access” from your gmail setting
最后,一旦你运行你的脚本。你会收到这封邮件。(注意,目前我正在使用我自己的电子邮件来测试应用程序)。
恭喜你。您刚刚发送了您的第一个自动电子邮件发送请求!
Source: Makeameme
使用我们的 Python Dash 仪表板准备 Webhooks
现在我们知道我们的警报起作用了,让我们设置 web 挂钩。
Web hooks 是服务器端应用程序的资源灯事件反应,用于通知客户端应用程序触发事件。在这种情况下,我们将设置 Dash,以便在用户修改仪表板的下拉菜单时激活警报逻辑。如果我们的产品降价,我们会发出警报。
Additional Callback to call alerts
product_df_specific
将按日期对特定产品的值进行排序。original_price
取产品的最早日期latest_price
取产品的最新日期- 然后,一旦我们发现
latest_price
比original_price
低,就会发送电子邮件。
最终结果是,我们将有一个特定的电子邮件详细信息,列出用户关心的所有产品和价格警报。
Inserting alerts in emails
为电子邮件验证机密创建配置文件
还记得步骤 1 中的凭证吗?
gmail_user = '<input your gmail user. No need to use @gmail.com>'
gmail_password = '<input your gmail password>'
让我们将这些敏感信息放在一个属性文件中,您可以轻松地保护和配置该文件。
首先让我们创建一个名为 properties.ini 的 Python 属性文件:
**[EMAIL]** user= <gmail username>
password = <gmail password>
这些是你需要保护的私人文件。要访问它,只需使用 Python 内置的 configparser 库。
import configparserconfig = configparser.ConfigParser()
config.read('properties.ini')
gmail_user = config['EMAIL']['user']
gmail_password = config['EMAIL']['password']
你将首先启动一个config parser
,这将把你的属性组织成一个树形目录来提取。
然后您将read
properties ini 文件并提取您的用户名和密码,就像您如何从 2D 数组中提取一个元素一样。
最终结果
恭喜你!!您已经在仪表板中创建了第一个 SMTP。如果你做得好,你会得到这个结果。如果没有,请随意查阅我的 Github 代码或者在这里提出你的问题。
Email sent when users update the product dropdown
现在释放并创建您自己的 Dash Dashboard 和 Python SMTP!
更多参考
如果您需要更多的例子和对 Python SMTP 能做什么的更好的理解。请随意访问以下链接。
最后…
Source: Unsplash
我真的希望这是一本很棒的读物,是你发展和创新的灵感来源。
请在下面评论出来建议和反馈。就像你一样,我也在学习如何成为一名更好的数据科学家和工程师。请帮助我改进,以便我可以在后续的文章发布中更好地帮助您。
谢谢大家,编码快乐:)
关于作者
Vincent Tatan 是一名数据和技术爱好者,拥有在 Visa Inc .和 Lazada 实施微服务架构、商业智能和分析管道项目的相关工作经验。
Vincent 是土生土长的印度尼西亚人,在解决问题方面成绩斐然,擅长全栈开发、数据分析和战略规划。
他一直积极咨询 SMU BI & Analytics Club,指导来自不同背景的有抱负的数据科学家和工程师,并为企业开发他们的产品开放他的专业知识。
请通过 LinkedIn ,Medium或 Youtube 频道 联系文森特
发送电子邮件与美丽的形式在安卓系统
在本文中,我们将发送一封包含精心设计的表单的电子邮件,以显示随邮件发送的数据。
Image from Unsplash
众所周知,使用电子邮件对我们所有人来说已经变得必要和重要。有各种电子邮件服务提供商,如雅虎、Outlook、iCloud 和 Gmail 等。这些科技巨头为每个用户提供免费电子邮件服务。如果你使用互联网,那么就没有办法忽视它,因为无论何时你访问任何网站/应用程序来在线购买/销售产品。首先,你必须在这些网站/应用上注册,以证明你在现实世界中的身份。
在本文中,我们将学习如何使用 JavaMail API 将 Gmail 收件箱中的电子邮件发送到指定的电子邮件地址。我们将发送普通用户数据,如用户名、电子邮件地址、手机号码等。
所以让我们开始吧。
第一步
- 首先用空活动创建 android 项目,并在根目录下创建名为 MailSender.java 和 JSSEProvider.java 的 java 类。
- 现在我们需要将 Java 邮件 API jar 文件导入到我们的项目中。从这里下载 jar 文件,解压后粘贴到 libs 文件夹中。
- 如果你的项目不能识别你的库,那么重启 Android studio。
- 右键单击应用程序->新建->文件夹->资产文件夹,创建资产文件夹。我们将把 HTML 文件放在这里,以便在 Gmail 上显示数据。
- 要发送电子邮件,你的应用需要互联网连接,所以在你的 AndroidManifest.xml 文件中声明这个权限。
<**uses-permission android:name="android.permission.INTERNET"**/>
第二步
- 在 MailSender.java 类中创建一些实例变量
*// change this host name accordingly* **private** String **mailhost** = **"webmail.xyz.in"**;
**private** String **user**;
**private** String **password**;
**private** Session **session**;
Context **context**;
**private** Multipart **_multipart** = **new** MimeMultipart();
- 现在在 MailSender.java 文件中创建静态块来调用 JSSEProvider.java 类。JSSEProvider 用于链接 SSLContext、X509 的 KeyManagerFactory、X509 的 TrustManagerFactory 和 AndroidCAstore 的修改版本。
**static** {
Security.*addProvider*(**new** JSSEProvider());
}
- 创建参数化的构造函数(传递上下文,“发件人电子邮件 id”和“密码”等。)来初始化实例变量,并创建一个 Properties 类的对象来设置构造函数内部的属性。在其中传递上下文、用户电子邮件 id 和密码。看一看构造函数代码。
**public** MailSender(Context context,String user, String password) {
**this**.**user** = user;
**this**.**password** = password;
**this**.**context** = context;
Properties props = **new** Properties();
props.setProperty(**"mail.transport.protocol"**, **"smtp"**);
props.setProperty(**"mail.host"**, **mailhost**);
props.put(**"mail.smtp.auth"**, **"true"**);
props.put(**"mail.smtp.port"**, **"465"**);
props.put(**"mail.smtp.socketFactory.port"**, **"465"**);
props.put(**"mail.smtp.socketFactory.class"**, **"javax.net.ssl.SSLSocketFactory"**);
props.put(**"mail.smtp.socketFactory.fallback"**, **"false"**);
props.setProperty(**"mail.smtp.quitwait"**, **"false"**);
**session** = Session.*getDefaultInstance*(props, **this**);
}
- 创建密码验证器方法来验证您的帐户。
**protected** PasswordAuthentication getPasswordAuthentication() {
**return new** PasswordAuthentication(**user**, **password**);
}
- 最后,创建用户定义的方法 sendUserDetailWithImage()将数据发送到邮件。下面的代码片段从 assets 文件夹中提取 user_profile.html,将其转换为一个缓冲区,然后从缓冲区中读取它,最后将其转换为一个 string 对象,用实际的用户名替换关键字“ u s e r n a m e username username”等等。查看完整的*senduserdetailwithiimage()*代码。
看看完整的 MainSender.java 代码
第三步
要在表单中显示数据,请在 assets 文件夹中创建名为 user_profile.html 的 HTML 文件,并粘贴以下代码。
HTML 代码的输出看起来像这样
第四步
最后,将下面的代码粘贴到 MainActivity 中。
结果
Data received on Gmail
**注意:**如果你这边一切正常,但还是没有收到邮件,请检查你的垃圾邮件文件夹,如果有,点击“标记为非垃圾邮件”。
结论
我创建了一个 android 项目,以电子邮件的形式发送用户数据。我使用过 JavaMail API 发送电子邮件。在本文中,我发送了 HTML 表单和数据。
如果你对这篇文章有任何疑问,请在评论区问我。另外,你可以在我的 Github 账户上找到这篇文章的完整源代码。
我希望你喜欢读这篇文章,你也可以访问我的 网站 ,在那里我会定期发布文章。
订阅 我的邮件列表,以便直接在您的收件箱中提前获得我的文章,或者关注我自己在 Medium 上发表的文章The Code Monster以完善您的技术知识。
了解你的作者
希曼舒·维尔马毕业于印度勒克瑙的 APJ 阿卜杜勒·卡拉姆大学博士。他是 Android & IOS 开发人员、机器学习和数据科学学习者、金融顾问和博客作者。
使用 Python 从 Blackbaud CRM (BBEC)发送自动电子邮件
Blackbaud CRM Python 教程
连接到 BBEC 的基本设置
我的新工作使用 Blackbaud CRM 作为他们数据基础设施的主干,我的经验主要是 Python。令我惊讶的是,关于使用 Python 连接到 Blackbaud Enterprise CRM (BBEC)的资源并不多。最近,我需要拉一个选民名单,并给他们发送一封定制的电子邮件,其中包含他们的帐户信息。这比我预想的要困难得多,我想分享一下我的技巧和工具,以备其他人想要处理类似的问题时使用。我发现的主要方法是使用 API,特别是 AppFxWebService 端点。
要实现这一点,需要几个主要步骤。API 是基于 SOAP 的,这意味着我们必须使用 HTTP 和 XML 来与之通信。我发现通过创建 Jinja2 模板可以很容易地将这些合并到 Python 脚本中。之后,我们将使用pandas
和xmltodict
将 XML 响应转换成数据帧。一旦数据变得更易于管理,我们将向选出的选民发送一些个性化的电子邮件。这篇文章将重点介绍如何使用 Python 建立到 Blackbaud CRM 的连接。我将在以后的文章中更详细地介绍这些其他主题,但这里是从 CRM 中提取信息并使用 Python 发送电子邮件所需的整个过程的概述:
- 使用 Python 建立与 Blackbaud CRM 的连接
- 使用 Jinja2 模板与 API 通信
- 将 XML 结果解析成熊猫数据帧
- 使用我们从 CRM 收到的数据发送电子邮件
在这个过程中, Blackbaud CRM API 文档非常有用。根据您对这些概念的熟悉程度,您可能需要阅读入门指南。但是要注意,当我搜索这些文档时,我找不到任何 Python 示例。希望这篇指南能让你在下一个涉及 Blackbaud CRM 的项目中使用 Python。
另外,应该注意的是,这只是一个人解决这个问题的方法。如果你有任何建议或不同的方法来解决这些问题,我很乐意听到你的想法。
本指南假设您有一些 Python 经验,一些使用requests
包的经验,也许还有一些使用pandas
和数据分析的经验。如果你认为你能处理,我们将通过打开 IDE 或编辑器直接进入。我更喜欢 PyCharm,这也是本指南中的演示将使用的。然而,我会尽可能地包含使用替代方法完成这些步骤的链接。
第一步是在 PyCharm 中创建新项目。如果您选择另一条路线,主要步骤是为您的项目创建一个新目录,创建并激活一个虚拟环境,并安装一些必需的包。如果你像我一样使用 PyCharm,你需要从欢迎界面选择“创建一个新项目”。然后在接下来的屏幕上从列表顶部选择“纯 Python”。默认情况下,我的配置是用 conda 创建一个新环境。通过将“位置”栏中的“无标题”更改为项目的描述性名称来更改项目名称。对于这个例子,我选择了bbec_demo
。PyCharm 很好地自动创建了一个同名的新 conda 环境,并将其指定为这个项目的项目解释器。
接下来,我们想要在我们的项目中创建一个名为config.py
的新 Python 文件。如果您非常喜欢 PyCharm 快捷方式,那么当您选择了项目根目录时,可以使用 Alt + Insert +'pf '创建一个新的 python 文件。然后,我们要将以下代码添加到该文件中:
from requests import Session
from requests.auth import HTTPBasicAuth
为了向 API 发送请求,我们将结合使用requests
包和其他一些工具。如果您使用 PyCharm,它可能会为您建立一个虚拟环境,您只需安装 requests 包。在 PyCharm 中,最简单的方法是将光标放在标有红色下划线的包(requests
)上,然后按 Alt + Shift + Enter。这将自动选择最合适的上下文菜单项,即“安装包请求”
接下来,在开始与 Blackbaud CRM 通信之前,我们需要收集一些不同的 URL。您可能会发现关于制作 HTTP 请求的文档页面和 Fiddler 演示有助于找到这些请求。如果你登录了你公司的客户关系管理系统,你可以通过这个网址找到你需要的信息。例如,您可能有一个如下所示的 URL:
[https://bbisec02pro.blackbaudhosting.com/1234ABC_](https://bbisec04pro.blackbaudhosting.com/4249COL_fa341b46-12a4-4119-a334-8379e2e59d29/webui/WebShellLogin.aspx?databaseName=4249COL&url=https%3A%2F%2Fbbisec04pro.blackbaudhosting.com%2F4249COL_fa341b46-12a4-4119-a334-8379e2e59d29%2Fwebui%2FWebShellPage.aspx%3FdatabaseName%3D4249COL)fbd2546c-1d4d-4508-812c-5d4d915d856a[/webui/WebShellLogin.aspx](https://bbisec04pro.blackbaudhosting.com/4249COL_fa341b46-12a4-4119-a334-8379e2e59d29/webui/WebShellLogin.aspx?databaseName=4249COL&url=https%3A%2F%2Fbbisec04pro.blackbaudhosting.com%2F4249COL_fa341b46-12a4-4119-a334-8379e2e59d29%2Fwebui%2FWebShellPage.aspx%3FdatabaseName%3D4249COL)
可能有一个以“?”开头的查询字符串紧跟在[WebShellLogin.aspx](https://bbisec04pro.blackbaudhosting.com/4249COL_fa341b46-12a4-4119-a334-8379e2e59d29/webui/WebShellLogin.aspx?databaseName=4249COL&url=https%3A%2F%2Fbbisec04pro.blackbaudhosting.com%2F4249COL_fa341b46-12a4-4119-a334-8379e2e59d29%2Fwebui%2FWebShellPage.aspx%3FdatabaseName%3D4249COL)
之后,但是我们可以忽略该信息。在本例中,我们希望所有内容都达到/webui
。我们将把它记录在配置文件中,现在完整的文件应该是这样的:
from requests import Session
from requests.auth import HTTPBasicAuthbase_url = 'https://bbisec02pro.blackbaudhosting.com/'
database_name = '1234ABC_fbd2546c-1d4d-4508-812c-5d4d915d856a'
我把 URL 分成两部分,这样我们的代码可读性更好一些。我们需要的最后一部分是 AppFxWebService 端点,即/appfxwebservice.asmx
。我们将使用 f 弦把所有这些放在一起。请注意,f 字符串仅在 Python 3.6 及更高版本中可用,如果您使用的是 Python 的旧版本,则必须使用.format()
字符串方法。我们的配置文件现在应该如下所示:
from requests import Session
from requests.auth import HTTPBasicAuth
# URLs used for various API calls
base_url = 'https://bbisec02pro.blackbaudhosting.com/'
database_name = '1234ABC_fbd2546c-1d4d-4508-812c-5d4d915d856a'
appfx = '/AppFxWebService.asmx'
api = f'{base_url}{database_name}{appfx}'
我们现在已经精心制作了将用于与 API 通信的 URL。我们需要的下一部分是登录数据库的凭证。我选择使用 AWS Secret Management 来存储我的凭据。这相对容易,但是设置它可能超出了本文的范围。你需要按照[boto3](https://pypi.org/project/boto3/)
和的设置指南添加一个新的秘密。与此相关的成本很小,但它非常安全,只需几分钱。如果你以前没有用过,甚至可以免费试用 30 天。
如果 AWS 方法不适合您,一个快速的替代方法是创建一个名为secret.py
的新 Python 文件,但是要确保这个文件不受源代码控制(将其添加到您的.gitignore
文件中)。这个文件应该和config.py
在同一个目录下。它可以简单到:
username = 'BLACKBAUDHOST\your_username1234ABC'
password = 'your password'
我在这里遇到的一个问题是你用户名前后的前缀和后缀。如果您的环境由 Blackbaud 托管(如果您正在阅读本文,很可能就是这样),那么合适的前缀是“BLACKBAUDHOST\”。请注意,它只适用于反斜杠,不适用于正斜杠。后缀通常作为用户名的一部分显示在 Blackbaud 中,但它应该是数据库名称。登录 Blackbaud CRM 后,您可以通过查看 URL 中的查询字符串来确认这一点:
.../WebShellPage.aspx?databaseName=1234ABC
1234ABC
是需要添加到您的用户名后面的内容。确认您的用户名格式正确的另一种方法是在 Blackbaud CRM 中导航至管理>安全>应用程序用户,并查看您的姓名旁边列出的“登录名”内容。这将为您提供我们进行身份验证所需的完整登录名。我还想包含 AWS 的这个代码片段,展示如何访问存储在秘密管理器中的秘密,以防您决定走这条路:
import boto3
import base64
from botocore.exceptions import ClientError
def get_secret():
secret_name = "credentials"
region_name = "us-east-1"
# Create a Secrets Manager client
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager',
region_name=region_name
)
# In this sample we only handle the specific exceptions for the 'GetSecretValue' API.
# See https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
# We rethrow the exception by default.
try:
get_secret_value_response = client.get_secret_value(
SecretId=secret_name
)
except ClientError as e:
if e.response['Error']['Code'] == 'DecryptionFailureException':
# Secrets Manager can't decrypt the protected secret text using the provided KMS key.
# Deal with the exception here, and/or rethrow at your discretion.
raise e
elif e.response['Error']['Code'] == 'InternalServiceErrorException':
# An error occurred on the server side.
# Deal with the exception here, and/or rethrow at your discretion.
raise e
elif e.response['Error']['Code'] == 'InvalidParameterException':
# You provided an invalid value for a parameter.
# Deal with the exception here, and/or rethrow at your discretion.
raise e
elif e.response['Error']['Code'] == 'InvalidRequestException':
# You provided a parameter value that is not valid for the current state of the resource.
# Deal with the exception here, and/or rethrow at your discretion.
raise e
elif e.response['Error']['Code'] == 'ResourceNotFoundException':
# We can't find the resource that you asked for.
# Deal with the exception here, and/or rethrow at your discretion.
raise e
else:
# Decrypts secret using the associated KMS CMK.
# Depending on whether the secret is a string or binary, one of these fields will be populated.
if 'SecretString' in get_secret_value_response:
secret = get_secret_value_response['SecretString']
secret = secret.strip('{}').replace('"', '')
username, password = secret.split(':')
else:
decoded_binary_secret = base64.b64decode(get_secret_value_response['SecretBinary'])
return username, password
它包括一些基本的错误处理,我只需要为自己的目的添加三行代码。
secret = secret.strip('{}').replace('"', '')
username, password = secret.split(':')
...
return username, password
这只是一些文本解析,将响应格式化为用户名和密码对。一旦完成,我就在函数的末尾添加return username, password
。我要说的是,由于“\”字符,在我的用户名中包含“BLACKBAUDHOST”前缀时,我得到了一些奇怪的结果。我无法让它正确地转义,所以我选择从 AWS 中存储的秘密中删除它,并在检索到秘密后添加它。因为它不是用户名的唯一部分,我不认为这里有任何安全问题。
如果您走第一条路,将用户名和密码作为明文存储在源代码控制之外的文件中,那么您会想要将这个 import 语句添加到config.py
:
from secret import username, password
如果您走 AWS Secrets Manager 路线,您的导入看起来会稍有不同,您必须分配用户名和密码:
import secretusername, password = secret.get_secret()
username = 'BLACKBAUDHOST\\' + username
由于get_secret()
函数返回一个带有用户名密码对的元组,我们可以编写username, password
来告诉 Python 解包元组并分别分配变量。然后我们会加上我之前提到的前缀。这里需要一个双反斜杠,因为单反斜杠是转义序列的开始,您需要第二个反斜杠来表示被转义的字符(" \ ")。
现在凭证已经处理好了,并且可以安全地访问它们,我们只需要做最后一点设置。我们必须用requests
包创建一个会话,并使用我们的凭证授权该会话。
session = Session()
session.auth = HTTPBasicAuth(username, password)
我将在这里包括一个其他的位,这是我们所有的 API 调用将使用的头。在发送请求之前,我们将添加一点点信息,但是我喜欢设置一个默认的信息。一旦我们包含了这些,完整的config.py
文件应该是这样的:
from requests import Session
from requests.auth import HTTPBasicAuth
import secret
# URLs used for various API calls
base_url = 'https://bbisec02pro.blackbaudhosting.com/'
database_name = '1234ABC_fbd2546c-1d4d-4508-812c-5d4d915d856a'
appfx = '/AppFxWebService.asmx'
api = f'{base_url}{database_name}{appfx}'
base_endpoint = 'Blackbaud.AppFx.WebService.API.1/'# Credentials
username, password = secret.get_secret()
username = 'BLACKBAUDHOST\\' + username# Authorization
session = Session()
session.auth = HTTPBasicAuth(username, password)# Headers
headers = {'Content-Type': 'text/xml; charset=utf-8',
'Host': 'bbisec02pro.blackbaudhosting.com',
'SOAPAction': ''}
我还偷偷添加了一行代码来定义base_endpoint
。这是我们将用来配置我们的 SOAP 请求的东西。不是超级关键,但是为了方便可能要加上。Content-Type
头将根据您使用的 SOAP 版本(1.1 或 1.2)而变化。到目前为止,我只使用了 1.1,所以这就是这里所反映的。您也可以通过自己导航到端点来找到此信息。登录你的 Blackbaud CRM,用.../appfxwebservice.asmx
替换网址的.../webui/WebShellPage.aspx?
部分和其后的所有内容。在这里,您将能够看到所有可用的操作以及使用它们所需的 SOAP 模板。模板显示了填充了你的数据库信息的标题,如果对你来说更容易的话,你可以窃取这些并把它们放在如上图所示的config.py
中。
我们就要连接到数据库了,但是我们需要再创建一个名为api.py
的文件。在这里,让我们多写一点代码来测试我们的连接:
import config
action = 'AdHocQueryGetIDByName'
body = '''<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<AdHocQueryGetIDByNameRequest >
<ClientAppInfo REDatabaseToUse="1234ABC"
ClientAppName="bbec_demo"
TimeOutSeconds="5"/>
<Name>Interaction Data List Query</Name>
</AdHocQueryGetIDByNameRequest>
</soap:Body>
</soap:Envelope>'''
现在,我从/appfxwebservice.asmx
端点借用了一个模板,具体来说就是名为“AdHocQueryGetIDByName”的模板,这就是为什么action = ‘AdHocQueryGetIDByName'
。然后,我将文本直接粘贴到我们的文件中。我去掉了所有我不会使用的不重要的标签,然后我必须输入一些其他的信息。你可能会注意到ClientAppInfo
标签。这不会出现在/appfxwebservice.asmx
端点的模板中,但是我们的请求必须有效。他们只是在文档中简单地提到了它,当我第一次开始做这个的时候,它对我来说绝对是一个陷阱。不过,你所要做的就是将REDatabaseToUse
设置为你的数据库名称(在你的 CRM 的 URL 的查询字符串中找到)并为ClientAppName
输入一些名称。按照惯例,我喜欢将它设置为我们的项目名称,但它可以是您想要的任何名称。我们需要做的最后一件事是在Name
标签中输入我们的信息库中存在的查询的名称。我选择“交互数据列表查询”是因为它是一个现成的报告,应该默认包含在您的 CRM 中。
接下来,让我们使用给定的操作创建一个函数来设置我们的标题:
def set_head(endpoint):
headers = config.headers.copy()
headers.update({'SOAPAction': f'{config.base_endpoint}{endpoint}'})
return headers
这将更新我们在config.py
文件中定义的头的副本,并返回给SOAPAction
合适的头。
最后一步是使用我们在config.py
文件中设置的requests
和session
发送 POST 请求。我们将添加我们的头部和身体,我们应该准备好了。
res = config.session.post(config.api, headers=set_head(action), data=body)
print(res)
我们的api.py
文件作为一个整体现在应该看起来像这样:
import config
action = 'AdHocQueryGetIDByName'
body = '''<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<AdHocQueryGetIDByNameRequest >
<ClientAppInfo REDatabaseToUse="1234ABC"
ClientAppName="bbec_demo"
TimeOutSeconds="5"/>
<Name>AdHocQueryGetIDByName</Name>
</AdHocQueryGetIDByNameRequest>
</soap:Body>
</soap:Envelope>'''
def set_head(endpoint):
headers = config.headers.copy()
headers.update({'SOAPAction': f'{config.base_endpoint}{endpoint}'})
return headers
res = config.session.post(config.api, headers=set_head(action), data=body)
print(res)
运气好的话,当您运行它时,它会打印出<Response [200]>
。这意味着我们已经成功地建立了到数据库的连接,我们可以开始从中提取信息了!
在下一节中,我们将去掉 body 的那个难看的三重引号字符串,并用一些漂亮的 Jinja2 模板替换它。欢迎在下面提出建议或问题。你可以在我的 GitHub 上找到本节涉及的所有代码。
感知空气质量
基于 RaspberryPi 4 的低成本物联网空气质量监测仪
Santiago, Chile during a winter environmental emergency
我有幸生活在世界上最美丽的国家之一,但不幸的是,这并不总是美好的。智利冬季空气污染严重,主要是由灰尘和烟雾等颗粒物质造成的。
由于天气寒冷,在南部,空气污染主要是由于木材引起的,而在圣地亚哥(该国中部的主要首都),空气污染则是由工业、汽车以及两大山脉之间的独特地理位置造成的。
如今,空气污染是全世界的一个大问题,在这篇文章中,我们将探讨如何开发一个廉价的自制空气质量监测器,基于树莓皮。
如果你有兴趣了解更多关于空气质量的信息,请访问 “世界空气质量指数”项目 。
颗粒物(PM)是什么,它是如何进入空气中的?
因此,要了解污染或空气污染,我们必须研究与之相关的颗粒,也就是众所周知的颗粒物。查看上一部分的图表,我们可以发现他们提到了 PM2.5 和 PM10 。让我们对此进行一个快速的概述。
PM 代表颗粒物质(也称为颗粒污染):空气中固体颗粒和液滴混合物的术语。有些微粒,如灰尘、污垢、煤烟或烟雾,很大或很暗,足以用肉眼看到。其他的非常小,只有用电子显微镜才能发现。
颗粒的尺寸范围很广。直径小于或等于 10 微米的颗粒非常小,以至于它们可以进入肺部,潜在地导致严重的健康问题。十微米还不到一根人类头发的宽度。
颗粒污染包括:
- 粗尘粒(PM10) :可吸入颗粒物,直径一般在 10 微米及以下。来源包括破碎或研磨作业以及道路上车辆扬起的灰尘。
- 细颗粒物(PM2.5) :可吸入的细颗粒物,直径一般在 2.5 微米及以下。所有类型的燃烧都会产生细颗粒物,包括机动车、发电厂、住宅木材燃烧、森林火灾、农业燃烧和一些工业过程
你可以在环保局网站上找到更多关于颗粒物的信息: 美国环境保护局
为什么重要的是关心那些颗粒物?
正如 GERARDO ALVARADO Z .在他在智利大学的工作中所描述的那样,对 1930 年默兹山谷(比利时)、1948 年多诺拉(宾夕法尼亚州)和 1952 年伦敦的高度空气污染事件的研究是第一个将死亡率与颗粒污染联系起来的文献资料来源(Préndez,1993 年)。在调查空气污染对人们健康的影响方面取得的进展已经确定,健康风险是由可吸入颗粒造成的,这取决于它们在呼吸系统不同部分的渗透和沉积,以及对沉积物质的生物反应。
最厚的颗粒,约 5 微米,通过鼻腔纤毛和覆盖鼻腔和气管的粘膜的共同作用被过滤。直径在 0.5 和 5 微米之间的颗粒可以沉积在支气管中,甚至沉积在肺泡中,然而,几小时后它们被支气管和细支气管的纤毛清除。小于 0.5 微米的颗粒可以穿透很深,直到它们沉积在肺泡中,保留几周到几年,因为没有促进消除的粘膜纤毛运输机制。
下图显示了颗粒在呼吸系统中的渗透程度,取决于它们的大小。
因此,发现这两种类型的颗粒(PM2.5 和 PM10)非常重要,好消息是这两种颗粒都可以通过简单且不昂贵的传感器 SDS011 读取。
粒子传感器— SDS011
空气质量监测是众所周知的既定科学,始于 80 年代。当时,技术相当有限,用于量化空气污染的解决方案复杂、繁琐且非常昂贵。
幸运的是,如今,利用最新的现代技术,用于空气质量监测的解决方案不仅变得更加精确,而且测量速度也更快。设备变得越来越小,价格也比以前便宜得多。
在本文中,我们将重点介绍一种粒子传感器,它可以检测空气中的灰尘量。虽然第一代传感器只能检测不透明度,但最近的传感器,如从暨南大学(山东)剥离出来的 INOVAFIT 公司的 SDS011 ,现在可以检测 PM2.5 和 PM10。
就其尺寸而言,SDS011 可能是精度和价格(低于 40.00 美元)最好的传感器之一。
规范
- 测量值:PM2.5,PM10
- 范围:0–999.9 微克/立方米
- 电源电压:5V(4.7–5.3V)
- 功耗(工作):70mA 10mA
- 功耗(睡眠模式激光和风扇):< 4mA
- 储存温度:-20 至+60C
- 工作温度:-10 至+50C
- 湿度(储存):最大值。90%
- 湿度(工作):最大值。70%(水蒸气凝结会伪造读数)
- 精度:0.3μm 为 70%,0.5μm 为 98%
- 尺寸:71x70x23 毫米
- 认证:CE,FCC,RoHS
SD011 使用 PCB 作为外壳的一面,可以降低成本。接收器二极管安装在 PCB 侧(这是强制性的,因为应避免二极管和 LNA 之间的任何噪声)。发射器激光器安装在塑料盒上,并通过软线连接到 PCB。
简而言之,Nova Fitness SDS011 是一款专业的激光粉尘传感器。安装在传感器上的风扇自动吸入空气。该传感器使用激光散射原理*来测量悬浮在空气中的灰尘颗粒值。该传感器提供高精度和可靠的 PM2.5 和 PM10 值读数。环境的任何变化都可以在 10 秒以下的短响应时间内被观察到。标准模式下的传感器以 1 秒的间隔报告读数。
*激光散射原理:当颗粒通过检测区时,会引起光散射。散射光被转换成电信号,这些信号将被放大和处理。由于信号波形与颗粒直径有一定的关系,通过分析可以得到颗粒的数量和直径。
但是 SDS011 如何捕捉这些粒子呢?
如前所述,SDS011 使用的原理是光散射或更好的说法,动态光散射(DLS),这是一种物理学技术,可用于确定悬浮液中的小颗粒或溶液中的聚合物的尺寸分布。在 DLS 范围内,通常通过强度或光子自相关函数(也称为光子相关光谱或准弹性光散射)来分析时间波动。在时域分析中,自相关函数(ACF)通常从零延迟时间开始衰减,并且由于更小的粒子,更快的动态导致散射强度轨迹的更快去相关。已经表明,强度 ACF 是功率谱的傅立叶变换,因此 DLS 测量可以在谱域中同样很好地执行。
下面是两个样品的假设动态光散射:较大的颗粒(如 PM10)在顶部;较小的颗粒(如 PM2.5)在底部;
观察传感器内部,我们可以看到光散射原理是如何实现的:
二极管上捕获的电信号进入低噪声放大器,然后通过 ADC 转换为数字信号,再通过 UART 发送到外部。
要了解更多关于 SDS011 的真实科学体验,请看看 Konstantinos et al .低成本便携式 PM2.5 浓度监测系统的开发和现场测试2018 年的工作。
表演时间到了。
让我们暂时放下这些理论,专注于如何使用 Raspberry Pi 和 SDS011 传感器测量颗粒物
硬件连接实际上非常简单。该传感器与 USB 适配器一起出售,以将其 7 针 UART 的输出数据与 RPi 的标准 USB 连接器之一进行接口。
SDS011 引脚排列:
- 引脚 1 —未连接
- 针 2—pm 2.5:0–999 微克/立方米;PWM 输出
- 引脚 3–5V
- 针 4—PM10:0–999 微克/立方米;PWM 输出
- 引脚 5 — GND
- 引脚 6 — RX UART (TTL) 3.3V
- 引脚 7 — TX UART (TTL) 3.3V
对于本教程,我第一次使用全新的树莓-Pi 4。但是当然,任何以前的模型也可以很好地工作。
一旦您将传感器连接到 RPi USB 端口之一,您将自动开始听到其风扇的声音。噪音有点烦人,所以也许你应该拔下它,等你用软件设置好了再说。
传感器和 RPi 之间的通信将通过串行协议进行。关于这个协议的细节可以在这里找到:激光灰尘传感器控制协议 V1.3 。
但是对于这个项目来说,最好是用一个 python 接口来简化要开发的代码。你可以创建自己的界面,或者使用互联网上的一些界面,比如弗兰克·豪雅的或者伊万·卡尔切夫的。我们将使用最后一个,它非常简单并且运行良好(您可以从它的 GitHub 或 mine 下载 sds011.py 脚本)。
文件 sds011.py 必须位于创建脚本的同一目录下。
在开发阶段,我将使用 Jupyter 笔记本,但您可以使用任何您喜欢的 IDE(例如,作为 Raspberry Pi Debian 软件包一部分的 Thonny 或 Geany 都非常好)。
开始导入 sds011,并创建传感器实例。 SDS011 提供了一种使用 UART 从传感器读取数据的方法。
from sds011 import *
sensor = SDS011("/dev/ttyUSB0")
你可以用命令睡眠打开或关闭你的传感器:
# Turn-on sensor
sensor.sleep(sleep=False)# Turn-off sensor
sensor.sleep(sleep=True)
打开传感器后,您可以使用以下方法从传感器获取数据:
pmt_2_5, pmt_10 = sensor.query()
测量前至少等待 10 秒钟稳定,至少等待 2 秒钟开始新的测量:
这就是使用传感器所需了解的所有软件。但是,让我们更深入地了解空气质量控制!在本文的开始,如果你已经浏览了提供空气好坏信息的网站,你应该意识到颜色与这些值相关联。每种颜色都是一个索引。其中最著名的是在美国和其他几个国家使用的空气质量指数。
空气质量指数
空气质量指数是报告每日空气质量的指数。它告诉你你的空气有多干净或被污染,以及你可能担心的相关健康影响。空气质量指数关注的是呼吸污染空气后几小时或几天内你可能经历的健康影响。
例如,EPA(美国环境保护局)不仅计算颗粒污染(PM2.5 和 PM10)的 AQI,还计算《清洁空气法案》规定的其他主要空气污染物:地面臭氧、一氧化碳、二氧化硫和二氧化氮。对于每一种污染物,环境保护局已经建立了国家空气质量标准来保护公众健康。
AQI 值,颜色和健康信息关联:
如前所述,这些 AQI 值和颜色与每一种污染物相关,但如何将传感器产生的值与它们联系起来呢?
一个附加表将它们全部连接起来:
但是当然,使用这样的表是没有意义的。最后,这是一个简单的数学算法,使计算。为此,我们将导入 AQI 值与污染物浓度(g/m)之间的转换库:
python-aqi 。
使用 PIP 安装库:
pip install python-aqi
智利怎么样?
智利也使用了类似的指数,ICAP: 可吸入颗粒物的空气质量指数。共和国总统府秘书长部 1998 年 3 月 16 日颁布的第 59 号最高法令在其第 1 条 g)款中规定,定义 ICAP 可吸入颗粒物 ICA 的水平如下表所示:
各部分之间的数值将呈线性变化,数值 500 将对应于极限值,当暴露于这些浓度时,超过该极限值将对人群产生风险。根据 ICAP 值,建立了类别(见下表),限定了人们接触的 MP10 的浓度水平:
Bueno:Good → Peligroso: Danger
本地记录数据
在这一点上,我们有所有的工具来捕捉来自传感器的数据,并将其转换为更“可读”的值,即 AQI 指数。
让我们创建一个函数来捕获这些值。我们将依次获取 3 个值,取其中的平均值:
def get_data(n=3):
sensor.sleep(sleep=False)
pmt_2_5 = 0
pmt_10 = 0
time.sleep(10)
for i in range (n):
x = sensor.query()
pmt_2_5 = pmt_2_5 + x[0]
pmt_10 = pmt_10 + x[1]
time.sleep(2)
pmt_2_5 = round(pmt_2_5/n, 1)
pmt_10 = round(pmt_10/n, 1)
sensor.sleep(sleep=True)
time.sleep(2)
return pmt_2_5, pmt_10
让我们来测试一下:
Ben Orlin — MathWithBadDrawings
我们再来做一个函数,转换 AQI 指数中 PM 的数值:
def conv_aqi(pmt_2_5, pmt_10):
aqi_2_5 = aqi.to_iaqi(aqi.POLLUTANT_PM25, str(pmt_2_5))
aqi_10 = aqi.to_iaqi(aqi.POLLUTANT_PM10, str(pmt_10))
return aqi_2_5, aqi_10
并一起测试这两种功能:
但是该拿它们怎么办呢?
最简单的答案是创建一个函数来保存捕获的数据,将它们保存在本地文件中:
def save_log():
with open("YOUR PATH HERE/air_quality.csv", "a") as log:
dt = datetime.now()
log.write("{},{},{},{},{}\n".format(dt, pmt_2_5, aqi_2_5, pmt_10,aqi_10))
log.close()
通过一个循环,您可以在本地文件中定期记录数据,例如,每分钟:
while(True):
pmt_2_5, pmt_10 = get_data()
aqi_2_5, aqi_10 = conv_aqi(pmt_2_5, pmt_10)
try:
save_log()
except:
print ("[INFO] Failure in logging data")
time.sleep(60)
每 60 秒,时间戳加上数据将被“附加”到这个文件中,正如我们在上面看到的。
向云服务发送数据
至此,我们已经了解了如何从传感器捕获数据,并将其保存在本地 CSV 文件中。现在,是时候看看如何将这些数据发送到物联网平台了。在本教程中,我们将使用ThingSpeak.com。
“ThingSpeak 是一个开源的物联网(IoT)应用程序,使用 REST 和 MQTT APIs 来存储和检索数据。ThingSpeak 支持创建传感器日志应用程序、位置跟踪应用程序和具有状态更新的社交网络。”
首先,你必须在 ThinkSpeak.com 有一个账户。接下来,按照说明创建一个通道,记下它的通道 ID 和写 API 键。
创建频道时,您还必须定义将上传到 8 个字段中的每个字段的信息,如上所示(在我们的示例中,仅使用其中的 4 个字段)。
MQTT 协议和 ThingSpeak 连接
MQTT 是一种发布/订阅架构,主要用于通过无线网络连接带宽和功率受限的设备。它是一个简单的轻量级协议,运行在 TCP/IP 套接字或 web 套接字上。WebSockets 上的 MQTT 可以用 SSL 保护。发布/订阅体系结构使得消息能够被推送到客户端设备,而无需设备持续轮询服务器。
MQTT 代理是通信的中心点,它负责在发送者和合法接收者之间分发所有消息。客户机是连接到代理的任何设备,可以发布或订阅主题以访问信息。主题包含代理的路由信息。每个想要发送消息的客户端将消息发布到某个主题,每个想要接收消息的客户端订阅某个主题。代理将带有匹配主题的所有消息传递给适当的客户端。
ThingSpeak 在 URLmqtt.thingspeak.com和端口 1883 有一个 MQTT 代理。ThingSpeak 代理支持 MQTT 发布和 MQTT 订阅。
在我们的例子中,我们将使用 MQTT 发布。
MQTT 发布
首先,让我们安装 Eclipse Paho MQTT Python 客户端库,它实现了 MQTT 协议的 3.1 和 3.1.1 版本
sudo pip install paho-mqtt
接下来,让我们导入 paho 库:
import paho.mqtt.publish as publish
并启动 Thingspeak 通道和 MQTT 协议。这种连接方法最简单,需要的系统资源最少:
channelID = "YOUR CHANNEL ID"
apiKey = "YOUR WRITE KEY"
topic = "channels/" + channelID + "/publish/" + apiKey
mqttHost = "mqtt.thingspeak.com"
现在我们必须定义我们的“有效载荷”:
tPayload = "field1=" + str(pmt_2_5)+ "&field2=" + str(aqi_2_5)+ "&field3=" + str(pmt_10)+ "&field4=" + str(aqi_10)
就是这样!我们准备好开始向云发送数据了!
让我们重写前面的循环函数,使其包含 ThingSpeak 部分。
# Sending all data to ThingSpeak every 1 minute
while(True):
pmt_2_5, pmt_10 = get_data()
aqi_2_5, aqi_10 = conv_aqi(pmt_2_5, pmt_10)
tPayload = "field1=" + str(pmt_2_5)+ "&field2=" + str(aqi_2_5)+ "&field3=" + str(pmt_10)+ "&field4=" + str(aqi_10)
try:
publish.single(topic, payload=tPayload, hostname=mqttHost, port=tPort, tls=tTLS, transport=tTransport)
save_log()
except:
print ("[INFO] Failure in sending data")
time.sleep(60)
如果一切正常,您一定会在 thingspeak.com 上看到数据也出现在您的频道上:
最终剧本
需要指出的是,Jupyter Notebook 对于开发和报告来说是一个非常好的工具,但是不适合创建代码投入生产。您现在应该做的是获取代码的相关部分,创建一个. py 脚本,并在您的终端上运行它。
例如,“ts_air_quality_logger.py”,您应该使用以下命令运行:
python 3 ts_air_quality_logger.py
这个脚本以及 Jupyter 笔记本和 sds011.py 可以在我的存储库 RPi_Air_Quality_Sensor 中找到。
请注意,该脚本仅适用于测试。最好不要在最终循环中使用延迟(这会使代码处于“暂停”状态),而是使用计时器。或者对于一个真正的应用程序,最好不要使用循环,让 Linux 程序定期用 crontab 执行脚本。
测试显示器
当我的 Raspberry Pi 空气质量监测器工作时,我将 RPi 组装在一个塑料盒中,将传感器放在外面,并将其放置在我家外面。
有两个经验:
- 汽油发动机燃烧:传感器放置在距离兰姆布雷塔汽车气景约 1 米处,发动机开启:
我让发动机运转了几分钟,然后关掉了。从日志文件,这是我得到的结果:
有趣的是证实了 PM2.5 是发动机产生的最危险的微粒。
2.燃木:燃木火放在鼻子正下方;
查看日志文件:
传感器数据瞬间“超出范围”,AQI 转换库无法很好地捕获,因此我更改了以前的代码来处理它:
def conv_aqi(pmt_2_5, pmt_10):
try:
aqi_2_5 = aqi.to_iaqi(aqi.POLLUTANT_PM25, str(pmt_2_5))
aqi_10 = aqi.to_iaqi(aqi.POLLUTANT_PM10, str(pmt_10))
return aqi_2_5, aqi_10
except:
return 600, 600
这种情况在外地也能发生,这是可以的。请记住,实际上,您应该使用移动平均线来真正获得 AQI(至少每小时一次,但通常每天一次)。
结论
一如既往,我希望这个项目可以帮助其他人找到进入令人兴奋的电子和数据科学世界的方法!
详情和最终代码请访问我的 GitHub 仓库: RPi_Air_Quality_Sensor
来自世界南部的 Saludos!
我的下一篇文章再见!
谢谢你,
马塞洛
基于双 LSTM 的句子分类
基于双向 LSTM 模型的句子分类及其与其他基线模型的比较
因此,有各种各样的方法来进行句子分类,如单词袋方法或神经网络等。在这篇文章中,我将主要讨论使用深度学习模型(特别是双 LSTM)的句子分类任务
本文主要关注一些基本的介绍,然后直接进入实现。如果您需要更深入的信息,我在参考资料中提供了链接。
我把文章按不同的类别组织起来,
- 介绍
- 实现:(预处理和基线模型)
- 双 LSTM 模型
- 结果和结论
随意跳到一个特定的类别。
一、简介
对于句子分类,我们主要有两种方法:
- 单词袋模型(BOW)
- 深度神经网络模型
BOW 模型的工作原理是分别对待每个单词,并对每个单词进行编码。对于 BOW 方法,我们可以使用 TF-IDF 方法,但它不能保留句子中每个单词的上下文。
因此,为了更好地完成命名实体抽取、情感分析等任务,我们使用深度神经网络。
二世。实施
数据集:
在这篇文章中,我使用了 Reddit - 数据集 [2],它基于四种情绪类别,如愤怒、快乐、血腥和毛骨悚然。
对于深度神经模型,我们需要文本的嵌入。嵌入捕捉了单词在更高维度平面中的表示。通过嵌入,我们创建了单词的向量表示,通过理解单词的上下文来学习单词。我们可以使用预先训练的嵌入,如 glove、fasttext,它们在数十亿个文档上进行训练,或者我们可以使用 gensim 包等创建自己的嵌入(在我们自己的语料库上进行训练)。
在本文中,我使用了预先训练好的 glove-twitter 嵌入,它适用于我们的社交网络数据环境。此外,我选择 100 维嵌入,它表现很好,不需要花太多时间训练。可以选择其他(25、50、300 D 也可以)。
embedding_path = "~/glove.twitter.27B.100d.txt" ## change
# create the word2vec dict from the dictionary
def get_word2vec(file_path):
file = open(embedding_path, "r")
if (file):
word2vec = dict()
split = file.read().splitlines()
for line in split:
key = line.split(' ',1)[0] # the first word is the key
value = np.array([float(val) for val in line.split(' ')[1:]])
word2vec[key] = value
return (word2vec)
else:
print("invalid fiel path")w2v = get_word2vec(embedding_path)
文本预处理:
所以他们的数据有四个代表四种不同情绪的文件,所以我们需要合并这些文件来完成多类别分类任务。
df_rage = pd.read_csv(os.path.join(dir_path,'processed_rage.csv'))
df_happy = pd.read_csv(os.path.join(dir_path,'processed_happy.csv'))
df_gore = pd.read_csv(os.path.join(dir_path,'processed_gore.csv'))
df_creepy = pd.read_csv(os.path.join(dir_path,'processed_creepy.csv'))# create a random balances dataset of all of the categories
length = np.min([len(df_rage),len(df_happy),len(df_creepy),len(df_gore)])df_final = pd.concat([df_rage[:length], df_happy[:length], df_gore[:length], df_creepy[:length]], ignore_index=True)
标记化:
为了将句子分解成更简单的记号或单词,我们对文本进行记号化。在这里,我们将使用 nltk Tweet tokenizer,因为它可以很好地处理社交网络数据。
import nltk
from nltk.corpus import stopwords
stopwords = set(stopwords.words('english'))
nltk.download('wordnet')
nltk.download('stopwords')
from nltk.tokenize import TweetTokenizer
from nltk.corpus import wordnet as wn
tknzr = TweetTokenizer()def get_tokens(sentence):
# tokens = nltk.word_tokenize(sentence) # now using tweet tokenizer
tokens = tknzr.tokenize(sentence)
tokens = [token for token in tokens if (token not in stopwords and len(token) > 1)]
tokens = [get_lemma(token) for token in tokens]
return (tokens)def get_lemma(word):
lemma = wn.morphy(word)
if lemma is None:
return word
else:
return lemmatoken_list = (df_final['title'].apply(get_tokens))
准备输入变量
# integer encode the documents
encoded_docs = t.texts_to_sequences(sentences)
# pad documents to a max length of 4 words
max_length = max_len
X = pad_sequences(encoded_docs, maxlen=max_length, padding='post')
输出变量:
from sklearn import preprocessing
le = preprocessing.LabelEncoder()
Y_new = df_final['subreddit']
Y_new = le.fit_transform(Y_new)
将数据拆分为培训和测试
## now splitting into test and training data
from sklearn.model_selection import train_test_split
X_train,X_test, Y_train, Y_test = train_test_split(X, y,test_size =0.20,random_state= 4 )
基准模型
在获得 LSTM 模型的分数之前,我已经从我们的基线模型中获得了一些指标:
对于基线模型,我们可以简单地计算 word2vec 嵌入的平均值。
# the object is a word2vec dictionary with value as array vector,
# creates a mean of word vecotr for sentences
class MeanVect(object):
def __init__(self, word2vec):
self.word2vec = word2vec
# if a text is empty we should return a vector of zeros
# with the same dimensionality as all the other vectors
self.dim = len(next(iter(word2vec.values())))
# pass a word list
def transform(self, X):
return np.array([
np.mean([self.word2vec[w] for w in words if w in self.word2vec]
or [np.zeros(self.dim)], axis=0)
for words in (X)
])
SVM
def svm_wrapper(X_train,Y_train):
param_grid = [
{'C': [1, 10], 'kernel': ['linear']},
{'C': [1, 10], 'gamma': [0.1,0.01], 'kernel': ['rbf']},]
svm = GridSearchCV(SVC(),param_grid)
svm.fit(X_train, Y_train)
return(svm)
韵律学
# svm
svm = svm_wrapper(X_train,Y_train)
Y_pred = svm.predict(X_test)
score = accuracy_score(Y_test,Y_pred)
print("accuarcy :", score)0.70
对于基线,你可以进一步应用其他分类器(如随机森林等),但我用 SVM 得到了最好的 F1 分数。
对于语言环境中的神经模型,最流行的是 LSTMs(长短期记忆),这是一种 RNN(递归神经网络),它保留了文本的长期依赖性。我在参考文献中加入了链接,这些参考文献似乎详细解释了 LSTM 的观点。
三世。双向 LSTM:
对于双向 LSTM,我们有一个嵌入层,而不是加载随机权重,我们将从手套嵌入中加载权重
# get the embedding matrix from the embedding layer
from numpy import zeros
embedding_matrix = zeros((vocab_size, 100))
for word, i in t.word_index.items():
embedding_vector = w2v.get(word)
if embedding_vector is not None:
embedding_matrix[i] = embedding_vector
我们还想计算神经模型的 vocab 大小。
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
# prepare tokenizer
t = Tokenizer()
t.fit_on_texts(token_list)
vocab_size = len(t.word_index) + 1# integer encode the documents
encoded_docs = t.texts_to_sequences(sentences)
# pad documents to a max length of 4 words
max_length = max_len
X = pad_sequences(encoded_docs, maxlen=max_length, padding='post')
y = Y_new
最终模型
# main model
input = Input(shape=(max_len,))
model = Embedding(vocab_size,100,weights=[embedding_matrix],input_length=max_len)(input)
model = Bidirectional (LSTM (100,return_sequences=True,dropout=0.50),merge_mode='concat')(model)
model = TimeDistributed(Dense(100,activation='relu'))(model)
model = Flatten()(model)
model = Dense(100,activation='relu')(model)
output = Dense(3,activation='softmax')(model)
model = Model(input,output)
model.compile(loss='sparse_categorical_crossentropy',optimizer='adam', metrics=['accuracy'])
对于我们的神经模型,上面的 max_len 必须是固定的,它可以是具有最大字数的句子,也可以是静态值。我把它定义为 60。
模型总结-
Layer (type) Output Shape Param #
=================================================================
input_32 (InputLayer) (None, 60) 0
_________________________________________________________________
embedding_34 (Embedding) (None, 60, 100) 284300
_________________________________________________________________
bidirectional_29 (Bidirectio (None, 60, 200) 160800
_________________________________________________________________
time_distributed_28 (TimeDis (None, 60, 100) 20100
_________________________________________________________________
flatten_24 (Flatten) (None, 6000) 0
_________________________________________________________________
dense_76 (Dense) (None, 100) 600100
_________________________________________________________________
dense_77 (Dense) (None, 3) 303
=================================================================
Total params: 1,065,603
Trainable params: 1,065,603
Non-trainable params: 0
_________________________________________________________________
因此,在针对 ner 模型的通用架构的论文[1]中,他们在双 LSTM 之上使用了 CRF 层,但是对于简单的多类别句子分类,我们可以跳过它。
使训练数据符合模型:
model.fit(X_train,Y_train,validation_split=0.25, nb_epoch = 10, verbose = 2)
四:成绩
评估模式
# evaluate the model
loss, accuracy = model.evaluate(X_test, Y_test, verbose=2)
print('Accuracy: %f' % (accuracy*100))Accuracy: 74.593496
分类报告
from sklearn.metrics import classification_report,confusion_matrix
Y_pred = model.predict(X_test)
y_pred = np.array([np.argmax(pred) for pred in Y_pred])
print(' Classification Report:\n',classification_report(Y_test,y_pred),'\n')Classification Report:
precision recall f1-score support
0 0.74 0.72 0.73 129
1 0.75 0.64 0.69 106
2 0.76 0.89 0.82 127
3 0.73 0.72 0.72 130
micro avg 0.75 0.75 0.75 492
macro avg 0.75 0.74 0.74 492
weighted avg 0.75 0.75 0.74 492
Comparison with other models
结论:
因此,我们看到,对于保留文本序列及其上下文的神经模型,我们得到了比 BOW 模型更好的分数,但这取决于上下文和应用。因此,在某些情况下,与复杂的神经模型相比,简单的模型可能是有益的。此外,我们有一个较小的数据集,所以训练时间很短,但对于较大的数据集(> 100k),可能需要 1 个多小时的训练。
希望你喜欢,如果你有任何疑问或意见,请随时添加到评论区。谢了。
参考资料:
[1]:兰普尔、纪尧姆、米格尔·巴列斯特罗斯、桑迪普·苏布拉曼尼安、川上和也和克里斯·戴尔。“命名实体识别的神经架构.” arXiv 预印本 arXiv:1603.01360 (2016)。
[2] Duong、Chi Thang、Remi Lebret 和 Karl Aberer。"用于分析社交媒体的多模态分类."2017 年第 27 届欧洲机器学习和数据库知识发现原理与实践会议(ECML-PKDD)
[## 了解 LSTM 网络——colah 的博客
这些循环使得循环神经网络看起来有点神秘。然而,如果你想得更多一点,事实证明…
colah.github.io](http://colah.github.io/posts/2015-08-Understanding-LSTMs/) [## 如何用 Keras 在 Python 中开发用于序列分类的双向 LSTM
双向 LSTMs 是传统 LSTMs 的扩展,可以提高模型在序列分类上的性能
machinelearningmastery.com](https://machinelearningmastery.com/develop-bidirectional-lstm-sequence-classification-python-keras/) [## 嵌入|机器学习速成班|谷歌开发者
嵌入是一个相对低维的空间,您可以将高维向量转换到其中。嵌入…
developers.google.com](https://developers.google.com/machine-learning/crash-course/embeddings/video-lecture) [## 用 Word2Vec 进行文本分类
在上一篇文章中,我谈到了主题模型对于非 NLP 任务的有用性,这次又回到了 NLP 领域。我…
nadbordrozd.github.io](http://nadbordrozd.github.io/blog/2016/05/20/text-classification-with-word2vec/)
单词暗示特征背后的语言模型概念
N 元语法的简单介绍
Photo by Ksenia Makagonova on Unsplash
介绍
你可能在智能手机上见过单词建议功能。当您键入一个单词时,键盘会理解您键入的内容,并建议下一个相关的单词。如果你选择了建议的单词,它会建议另一个,一个又一个,直到你得到这些有趣的句子。
这是一个简单的概念,称为 语言建模 。所以语言建模所做的是,它阅读书面文本,并试图为下一个单词分配概率。让我们看一个例子。
Figure 1: Language model tries to guess what the next word is. (number on the right is probability)
直观地,假设我们有 100 个单词要考虑下一个单词,语言模型会考虑上一个单词,并给出这 100 个单词的概率。然后,作为单词建议功能的一部分,它可以向用户提供前 3 个选项。现在,让我们给 LM 一个正式的定义。
语言模型
Figure 2: An example sentence with its notation
通常,语言模型被称为**,一种赋予句子概率的方式**。
但是等等!我们之前不是提到过 LM 给下一个单词分配概率吗?让我们分解上面的等式来进一步研究这个问题。
Equation 1: Probability of a sentence based on probability of predicting next word
正如你在等式 1 中看到的,LM 不仅可以给下一个给定的单词分配概率,还可以给整个句子分配概率。然后,LM 可以理解哪个句子听起来好,哪个不好。
Figure 3: Language model determines which sentence is more suitable than the others
太棒了,对吧?但是在如此强大的功能背后有什么呢?有许多方法可以创建这种语言模型。在这篇文章中,我们将探索一个简单的方法,叫做 n-gram 。
n 元语法
一个关键的想法是 ngram 一次只看 n 个单词。让我们以“猫坐在垫子上”为例。
Figure 4: Behavior of bigram
假设 n=2(也称为 bigram),它会尝试根据第一个词预测第二个词。例如:
- 猫 : Bigram 试图记住“the”后面应该是“cat”。
- cat sat:“cat”后面应该是“sat”。
- 坐在上:“坐”之后是“开”
n=3 的情况也是如此。对于单词“猫坐”,三元模型知道“坐”在“猫”之后。我想你明白了。
这个概念可扩展到 n 个单词(n-gram)。那么,ngram 如何知道所有这些模式呢?是概率论。
二元模型
为了简单起见,我们将看看二元模型的情况。从上面的例子中,我们知道“cat”在“the”之后,可以表示为 P(cat | the) 。
P (cat | the):给定单词“the”,下一个单词是“cat”的概率。
要计算这个概率,只需使用下面的公式。
Equation 2: Probability for Bigram
其中 *c(猫)*是“猫”在整个语料库中的出现次数。同样的道理也适用于 c(the) 。直观地说,这个等式表示:在所有的单词“the”中,有多少个单词后面是“cat”?瞧,现在我们有了。
二元模型与三元模型
Bigram 很简单,功能也很强大,但是在很多情况下它无法执行。例如:
Figure 3: Bigram fails when it has the same previous word
在句子的开头,模型看到“the”后面跟着“cat”。如果我们使用 bigram,并且仅依靠之前的一个单词,则很难猜出下划线单词是“mat ”,因为它可能再次生成“cat”。幸运的是,三元模型或四元模型可以解决这个问题。也就是说,如果我们看到“坐在”,下一个词很可能是“垫子”。
然而,这并不意味着我们考虑的序列越长,预测就越好。例如,对于 6-gram 来说,可能很难看到确切的“猫坐在”。因此,它不是很有效。
大多数时候,二元模型和三元模型是常见的选择。
后退和插值
也有这样的时候,你的数据集很小,3-gram 不能识别一些精确的单词;因此,导致概率为 0。这将使您的整个 3-gram 实现不再有用。
你可以很容易地做一个简单的 if-else,并在 3-gram 失败的情况下使用 2-gram。或者在 2 克失败的情况下使用 1 克。有种叫做,退避。
另一种技术,插值,是同时考虑不同的 ngrams。这个想法是给你的 n-gram 赋予权重(λ),这样你的 3-gram 模型也可以查看 2-gram 和 1-gram 的值。即
- 3 克装 60%
- 2 克装 35%
- 1 克含 05%
Equation 3: Interpolation of 3-gram
手动配置重量,直到你觉得它是正确的。你也可以使用一些自动的方法,但是我们不会在这篇文章中讨论。有关如何正确设置这些重量的更多信息,您可以参考和。
履行
现在让我们试着用这个 LM 来生成一个基于随机首字的句子。对于这个任务,我们将使用哈利波特与魔法石电影剧本来训练我们的 ngram。对于下面的代码,你也可以在上找到这个内核。
然后,我们就可以统计出一元词、二元词和三元词的出现频率。这将在以后用于计算概率。
既然我们可以计算 n-gram 的数目,我们就可以计算它的概率。对于 1 克、2 克和 3 克,我们将分别使用 0.01、0.4 和 0.5 的线性插值。
让我们试着造 20 个单词。首字母将被随机化,并选择最好的下一个单词。
那么,这将如何表现呢?下面是每一代的结果。
Generation #1:
protecting the stone , but i think i 'll be a wizard . . . . . . . .Generation #2:
platform 9 3/4 ? think you 're going to be the same . . . . . . . Generation #3:
sacrifice himself , he 's got a little bit . . . . . . . . . . .
结论
N-gram 是一种简单而强大的语言建模技术,只需查看 2 到 3 个单词。由于计算简单快速,它在许多应用中非常实用,例如移动设备上的单词建议。然而,N-gram 在生成长句时并不健壮,因为长句中的当前单词依赖于句子中的第一个单词。这种长期依赖关系更适合神经网络之类的模型,但这是以后文章的主题。
参考
- https://web.stanford.edu/~jurafsky/slp3/3.pdf
- https://towards data science . com/introduction-to-language-models-n-gram-e 323081503d 9
- http://www . cs . UMD . edu/class/fall 2018/cmsc 470/slides/slides _ 10 . pdf
情感分析:一个实用的基准
使用 FCNNs、CNN、RNNs 和 Python 中的嵌入对客户评论进行分类。
通过动手实践 Python 代码,我们展示了简单递归神经网络的局限性,并展示了嵌入如何改进用于情感分类的全连接神经网络和卷积神经网络。
我们通过对电影评论数据集进行情感分类,展示了如何处理序列数据。情感基本上是感情,包括用自然语言写的情感、态度和观点。
IMDB 电影评论数据集
我们首先使用 Keras API 加载 IMDB 数据集。评论已经被符号化了。我们希望有一个有限的词汇表,以确保我们的单词矩阵不是任意小的。我们还希望有一个有限长度的评论,而不是必须处理非常长的句子。我们的训练数据集有 25,000 条客户评论,以及它们的正确标签(正面或负面)。
上面你可以看到一个例子,是一个数字列表,也叫做单词向量。每个数字代表一个单词在词汇表中的索引。如果您需要更深入地了解机器学习的文本表示,您可能想在继续之前看看下面的文章。
理解书面单词:温习 Word2vec、GloVe、TF-IDF、单词袋、N-grams、1-hot 编码…
towardsdatascience.com](/representing-text-in-natural-language-processing-1eead30e57d8)
通过将单词 vectors 转换回文本,下面的代码可以用来以纯文本显示评论。我们运行一个样本正面和一个样本负面审查的代码。
FCNN、CNN、RNN 的情感分析基准
一个句子可以被认为是一系列具有跨时间语义联系的单词。就语义联系而言,我们的意思是,出现在句子前面的词在句子的后半部分影响句子的结构和意义。在理想情况下,一个句子中还有向后的语义联系。它们通常是通过将句子以相反的顺序输入到一个模型中来捕获的。
递归神经网络可用于提取、识别或表征文本的情感内容,并将其分类为正面或负面。
一个有趣的和有插图的指南来理解直觉。
towardsdatascience.com](/recurrent-neural-networks-explained-ffb9f94c5e09)
我们将这种方法与全连接神经网络、卷积神经网络以及两者的组合进行比较。
下面的代码运行 ca。在 Google Cloud 的 GPU 实例上运行 2 小时。
未看过的电影评论(测试数据集)的分类精度如上表所示。令人惊讶的是,具有嵌入的全连接神经网络优于其余的网络。这是一个 250 个节点完全连接的单层网络。卷积神经网络具有类似的精度。虽然 CNN 被设计为尊重图像数据中的空间结构,同时对场景中学习到的对象的位置和方向具有鲁棒性,但这一相同的原理也可以用于序列,例如电影评论中的一维单词序列。使 CNN 模型对于学习识别图像中的对象有吸引力的相同属性可以帮助学习单词段落中的结构,即对于特征的特定位置的技术不变性。在研究社区中,关于 CNNs vs RNNs 仍然存在公开的争论。在电影评论分类的情况下,与没有嵌入的全连接网络相比,简单的 RNN 在处理序列数据中是有用的。
自由文本上的情感预测
下面我们在自由文本评论上测试我们的模型。每个评论首先被标记化,然后被转换成用于预测的词向量。正面评论的概率被选择为大于 0.5。
带有嵌入的完全连接的神经网络对所有四种情绪都是正确的,而其余的模型有假阳性和假阴性。我们的 RNN 和 CNN 的性能肯定可以通过在更强的 GPU 机器上增加训练时段的数量来提高。
结论
在本文中,我们展示了神经网络如何解决电影评论的分类问题,同时对卷积神经网络的全连接方法进行了基准测试。简单的 RNN 是一个“非常深”的前馈网络(当在时间上展开时)。除了爆炸和消失梯度的问题,在实践中,他们不能学习长期的依赖关系。LSTMs 的明确设计是为了避免长期依赖问题。这里没有涉及它们,也没有涉及朴素贝叶斯,朴素贝叶斯是一种概率性的情感分析方法。
有助于理解 RNN 和 LSTM 运作的两个最好的博客是:
- http://karpathy.github.io/2015/05/21/rnn-effectiveness/
- http://colah.github.io/posts/2015-08-Understanding-LSTMs/
你也可以看看我的其他文章,详细解释 LSTMs 和一般的深度学习。
厌倦了德法数据集?看看 Yemba,脱颖而出。力学的 LSTM,GRU 解释和应用,与…
towardsdatascience.com](/lstm-based-african-language-classification-e4f644c0f29e) [## 深度学习为什么有效:解决一个农民的问题
在开始是神经元:梯度下降,反向传播,回归,自动编码器,细胞神经网络…
towardsdatascience.com](/why-deep-learning-works-289f17cab01a)
情感分析——电影评论指南
现实世界中的数据科学
你觉得情感分析怎么样?好/坏?到这篇文章结束的时候,你会觉得积极向上!
Image retrieved from Kaggle
那么,到底什么是情操?情感与一个单词或一系列单词的意思有关,通常与一种观点或情感有关。还有分析?嗯,这是看数据,做推论的过程;在这种情况下,使用机器学习来学习和预测电影评论是正面还是负面。
也许你有兴趣知道电影评论是正面还是负面,公司在各种场合使用情感分析,特别是出于营销目的。用途包括社交媒体监控、品牌监控、客户反馈、客户服务和市场研究(“情绪分析”)。
这篇文章将涵盖:
- 竞争
- 预处理数据
- 特征
- 系统模型化
- 估价
- 后续步骤
竞争
我们将使用 IMDB 电影数据集,其中有 25,000 条带标签的评论用于训练,25,000 条评论用于测试。 Kaggle 挑战赛要求二进制分类(“一袋单词和一袋爆米花”)。
让我们来看看数据集的一些汇总统计(李,2019)。我们被告知,积极和消极的电影评论平分秋色。以下是一些正面和负面的评论:
Selection of reviews including all the formatting and typos.
看到电影评论(字数)长度的分布根据情绪分裂也很有趣。这两种类型的评论的传播形状相似,但是负面评论平均来说要短一些。
Graph of word count frequency according to sentiment.
预处理
文字是杂乱的,人们喜欢通过添加过多的标点符号和拼写错误来更清楚地表达自己。然而,机器学习模型无法处理作为输入的文本,因此我们需要将字符和单词映射为数字表示。
文本的基本预处理包括删除非字母字符、停用词(一组非常常见的词,如 the、a 和等)。)并将所有单词改为小写。在这种情况下,我们还需要从电影评论中删除 HTML 标签。这些步骤可以打包成以下函数。
Code to process movie reviews.
下表显示了当停用词从评论中删除后,最常用的词是如何变化的。
Table of frequent words with and without stop words.
特征
我们应该如何将单词表示转换成模型可以解释的数字形式?有许多方法,但是现在,让我们尝试一些简单的方法。
一袋单词
我们从语料库(整个文本数据集)中获得词汇表。词汇表的长度等于我们应用单词包(BOW)时输出的向量的长度。对于每一项(可能是一个条目、一句话、一行文本),我们将文本转换成向量形式的频率计数。在这种情况下,我们将其限制为前 5000 个单词,以限制数据的维度。我们通过从 sklearn 的库中设置一个计数矢量器来对此进行编码,使其适合训练数据,然后转换训练和测试数据。
下面的灰框中给出了一个例子。我们查看语料库中所有独特的单词,然后计算这些单词在每段文本中出现的次数,从而得到每段文本的向量表示。
Text:It was the best of times,it was the worst of times,it was the age of wisdom,it was the age of foolishness.Unique words:
"it", "was", "the", "best", "of", "times", "worst", "age", "wisdom" "foolishness""it was the worst of times" = [1, 1, 1, 0, 1, 1, 1, 0, 0, 0]"it was the age of wisdom" = [1, 1, 1, 0, 1, 0, 0, 1, 1, 0]"it was the age of foolishness" = [1, 1, 1, 0, 1, 0, 0, 1, 0, 1]
Word2Vec
Word2vec (Word to Vector)是一个处理文本的两层神经网络。它的输入是一个文本语料库,输出是一组向量:语料库中单词的特征向量。该算法由谷歌在 2013 年创建。50 维空间可以通过使用经典的投影方法(例如 PCA)来可视化,以将向量减少到可以在图上绘制的二维数据。
为什么我们要用 Word2vec 而不是 BOW?它更擅长在周围环境中学习单词。这个过程比实现 BOW 要复杂一些,所以我不会在这里概述,但可以在的 GitHub 文件夹中找到(Dar,Green,Kurban & Mitchell,2019)。简而言之,它需要将评论标记为句子而不是单词,确定向量表示,然后适当地平均它们。
Tf-Idf
这是 Term-Frequency-Inverse-Document-Frequency 的缩写,它为我们提供了一个衡量单词在文档中的重要性的方法。
术语频率(与单词袋相同):
逆文档频率衡量一个术语在所有文档中的稀有程度(值越高,单词越稀有)【1】:
我们将这两者结合起来得到 Tf-Idf,如下所示:
为了实现这个表示,我们使用了来自 sklearn 库的 TfidfTransformer 函数。对训练集进行拟合,并为测试集确定相同单词的值。
Code for some of the features.
【1】其中 ln 为自然对数。
系统模型化
我们为这个数据集尝试了两种不同的模型:朴素贝叶斯和随机森林。朴素贝叶斯建立在贝叶斯定理的基础上,而随机森林是一组决策树。这两个实现的合并代码可以在 GitHub 存储库中的 NLP _ IMDb·朱皮特笔记本中找到(Dar 等人,2019)。
朴素贝叶斯
这是一个用于分类的概率机器学习模型,基于贝叶斯定理:
b 是证据,而 A 是假设(Gandhi,2018)。我们必须假设这些特征是独立的(一个不影响另一个)。
根据这个问题,我们用 y 表示正面或负面的评论,X 表示评论的特征向量。我们最后得到的寻找类的等式是:
这是使用 sklearn 的朴素贝叶斯包实现的!
随机森林
随机森林是一系列决策树,其中的叶节点表示预测的类别。我们可以使用 sklearn 的 ensemble 包中的 RandomForestClassifier 函数。
每个决策树都包含一组特征,并在最后输出一个决策。然后将所有决策树的结果结合起来,给出最终的类别预测。来自 DataCamp 的下图很好地展示了这一过程。
Image retrieved from DataCamp
估价
Kaggle 指定使用 ROC 曲线下的面积作为本次比赛的衡量标准。ROC 是接收运营商特征的缩写,是一种概率曲线。它将真阳性率与假阳性率进行对比。【2】这条曲线下的面积越高,模型预测产量就越好。下面是单词袋和随机森林的图表。虚线表示基线,如果预测是随机的,那么这是可以预期的。
The area under the ROC curve for Bag of Words with a Random Forest.
我们可以在下表中检查一些不同的排列。
Table of results for various feature and model combinations.
有趣的是,与随机森林相比,Tf-Idf 表现稍好,而朴素贝叶斯表现稍好。然而,具有 Word2Vec 特性的朴素贝叶斯的性能会显著下降。这可能是因为当表示转换为正数时丢失了一些信息(因为朴素贝叶斯只允许正值),而使用了高斯朴素贝叶斯。
[2] True positive 表示当一个项目为正数时,它被预测为正数。假阳性表示阴性项目被预测为阳性。
后续步骤
这只是情感分析的开始。进一步的方法可以使用二元模型(两个单词的序列)来试图保留更多的上下文含义,使用 LSTMs(长短期记忆)等神经网络来扩展评论中单词之间的关系距离等。
参考
一袋文字遇上一袋爆米花。(未注明)。从https://www.kaggle.com/c/word2vec-nlp-tutorial/取回
达尔,奥,格林,s,库尔班,r .,,米切尔,C. (2019)。冲刺三,一队。从 https://github.com/shiaoligreen/practical-data-science取回
甘地,R. (2018 年 5 月 5 日)。朴素贝叶斯分类器。检索自https://towards data science . com/naive-Bayes-classifier-81d 512 f 50 a7c
李,s .(2019 . 3 . 18)。一个完整的探索性数据分析和文本数据的可视化。检索自https://towards data science . com/a-complete-explorative-data-analysis-and-visualization-for-text-data-29fb 1b 96 FB 6a
情感分析。(未注明)。从 https://monkeylearn.com/sentiment-analysis/取回
情感分析和登月
应用机器学习结合基于规则的方法来理解第一次载人登月 50 周年后的情绪
The lunar lander ‘The Eagle’ on its descent towards the surface of the moon. Nasa
1969 年 7 月 20 日,月球着陆器向休斯顿报告:
休斯敦,这里是宁静湾。老鹰着陆了。
在过去的几天里,世界各地的媒体参加了庆祝首次将我们带到月球的史诗般的月球任务 50 周年的活动。从存档材料中可以清楚地看出,这一里程碑被广泛誉为人类成就的巅峰。然而,与此同时,人们也对阿波罗计划的巨额费用提出了质疑。这些资源本可以更明智地使用吗?这种观点认为,我们仍然在地球上与挑战作斗争,所以为什么要去太空寻找更多的挑战。
我们如何去了解今天的普遍意见呢?自 1969 年以来发生了一些戏剧性的变化,使我们能够对最普遍的情绪有一个更广泛的了解。通过社交媒体以及强大算法的出现,我们拥有了丰富的信息,这些算法允许我们以各种方式自动处理数据。想象一下拥有可以告诉我们一个陈述的情感内容的机器!情感分析的应用在我们的社会中扮演着越来越重要的角色,从产品开发到衡量不同公共政策的受欢迎程度。我们将探索情绪分析是如何完成的,并看看如何利用它来解码基于 50 周年前后收集的 twitter 数据的公众情绪。
为什么要进行情绪分析,这是怎么一回事?
情感分析,或观点挖掘,是关于发现人们对产品、服务或话题的观点、情感和感受。自动情绪分析的效用来自于这样一个事实,即可用的结构化反馈,如从您的酒店评论部分的星级评级中收集的反馈,通常非常稀少且缺乏深度。我们更倾向于在社交媒体上发布更微妙的反馈和评论。随着处理大量文本形式的非结构化数据的现代工具的出现,公司和组织开始挖掘这个洞察力的金矿。
情绪分析涵盖不同的类别,如极性、主观性,甚至包括快乐、悲伤、愤怒和惊讶等情绪:
我们在本文中关注的情绪类型与书面陈述的“极性”有关,即特定推文的整体积极或消极程度。需要注意的是,情感分析并不是一个完全被征服的领域。即使对于最先进的算法,这仍然是一项艰巨的任务,这使得它成为研究人员和商业开发人员非常感兴趣的领域。
对社交媒体数据进行情感分析时面临的挑战
我们表达自己的方式因个体差异和文化背景而异。甚至我们一时的情绪也会影响我们选择如何表达自己,并且会在一天中发生变化。不同文化之间也有很大的差异,例如,我们表达不同感情的强烈程度。在社交媒体数据的情感分析方面,一些最常见的挑战是:
俚语、拼写错误和非常规的语言使用
b)一条推文中不同主题的多种观点
c)修饰词和否定词(强化/弱化或转化情感)
d)模棱两可的话(“生病”可能意味着你生病了,或者某事实际上非常好)
e)符号、表情符号和表情符号;-)
f)讽刺和讽刺,甚至人类犯的错误比我们想象的还要多
除了这些特定语言的挑战,还有偏见和不平衡数据的挑战。与某些主题相关的各种社交媒体平台可能有偏见,不代表全体民众的意见。不平衡的数据对于机器学习模型来说可能是具有挑战性的。想象一下,90%的“训练”数据有积极的情绪,那么模型在区分中性和消极情绪方面可能表现不佳。语言也不是一成不变的,而是不断发展的,任何模型都需要不断地被监控和更新。甚至人类也不能总是完全处理与情感分析相关的所有挑战,尤其是在文本形式中,其中存在有限的视觉线索,例如发送者的面部表情。
基于规则 vs 机器学习
进行情感分析有两种主要方法:
- 基于规则的专家系统(又称词典方法)
- 机器学习(人工智能的子领域)
基于规则的方法依赖于“词典”,它是一个包含大量单词的词汇表,每个单词都有一个预定义的情感值。例如,单词“恨”的情感值为-0.57,而“爱”的情感值为+0.63。在其最简单的形式中,该算法将文本中每个单词的所有情感值相加,以计算整体情感。基于规则的方法的优点是,给定足够大的词典,它们是相当准确的,并且人们可以容易地描述每个情感分类是如何形成的。
第二种方法基于机器学习,像人类一样从经验中学习。一般来说,经历或训练越多,这些模型在预测情绪方面就越好。有许多机器学习算法,数据科学家的任务之一是了解哪些算法与哪种类型的问题相关。机器学习方法有可能比基于规则的方法更好地扩展,并且已经被证明是惊人地准确。然而,与更透明的基于规则的方法相比,它需要大量的训练数据,并且对于一些算法来说,更难解释如何做出某个预测。我们将进一步研究如何应用机器学习和基于规则的方法来进行情感分析,并对它们进行比较。第三种选择,通常在工业中使用,是使用一种结合了两者优点的混合方法。
让我们开始吧
在我们准备好计算整体情绪之前,我们需要执行许多步骤。我们采用的方法是数据科学家的方法:
1.阐明假设和定义目标
2.获取和探索数据
3.训练和评估模型
4.预测情绪并分析结果
1。 制定假设,明确目标
在我们进入数据之前,定义一个明确的目标并决定如何衡量我们的成功率是非常重要的。我们的目的是看看我们是否能对当前与美国宇航局在阿波罗计划期间进行的太空探索有关的情绪有所了解。假设是,我们将会注意到登月 50 周年所带来的所有关注所带来的更大影响。这项任务是预测 7 月 20 日前后几天 Twitter 消息的极性。极性将被定义为属于三类之一:正、负和中性。极性还会有一个振幅,例如,一条推文可以是正的(+0.23)或非常正的(+0.91)。
如上所述,我们将评估两种不同的情感分析方法,基于规则和基于机器学习。为了比较它们,我们将使用一个所谓的“混淆矩阵”。混淆矩阵通常用于这些类型的分类任务,并以结构化格式展示正确和不正确的分类:
The confusion matrix
我们希望最大化正确预测情感(绿色)的数量占所有预测情感的比例。这被称为模型的准确性。相反,我们希望最大限度地减少错误预测情绪的数量(黄色和红色)。由于我们对积极情绪和消极情绪之间的比例感兴趣,我们将格外关注避免假阴性和假阳性(用红色标记)。这些类型的错误分类将在最大程度上影响正面和负面之间的比率。错误分类的中性情绪(黄色)也值得最小化,但不如假阳性/假阴性率优先。
混淆矩阵有时会令人混淆,所以让我们来看一个例子。看看下面这条推文:
“阿波罗 11 号的一个伟大时刻是美国国家航空航天局宣布发射期间宇航员的心率”
一个情绪模型预测它带有积极的情绪(+0.62,其中+1.00 是最积极的),这与人类对它的评价一致,使它成为真正积极的*😗
Confusion matrix with one correctly made sentiment prediction
现在考虑这条推文:
“美国国家航空和宇宙航行局只是隐藏秘密太空计划和其中的先进技术的烟幕”
它显然是负面的,但一个情绪模型预测它是正面的* (+0.08),使它成为一个假正面的预测(使用 0 的截止值,所有低于 0 的都被归类为负面)😗
Confusion matrix with one incorrectly made sentiment prediction
另一个值得注意的评估分类器的方法是接收器操作特性。但是为了简单起见,我们将在这里集中讨论混淆矩阵。
因此,总结一下:我们希望通过捕获每个类别中的所有情感(回忆)来最大化绿盒(准确性),同时我们希望尽可能多的预测是正确的(精确度)。我们预计会有一部分未分类的推文。但是,由于我们正在寻找积极/消极情绪的比率,我们理想地想要一个在分类积极和消极情绪方面同样好(或坏)的模型。
2。 数据的采集和勘探
为了获取 Twitter 数据,我们设置了一个 Twitter 开发者账户来访问 Twitter API。有了安全的 api,我们将使用 Python 和开源库 Tweepy 连接到 API 并收集 tweets:
具体来说,我们正在查看 7 月 14 日至 7 月 22 日期间收集的英语推文。总共收集了数百万条推文,其中 21 000 条包含关键词[NASA,Apollo,Saturn V 等等。].这些推文被收集到一个数据库中( SQLite ),然后就可以使用 Python 的 Pandas 库进行访问和操作。
此外,我们需要另一个带有标签数据的 twitter 数据集,以便训练和评估我们的两个竞争模型。带标签的数据是指我们知道正确情绪的推文。为了进行适当的评估,我们应该尽可能使用与手头任务相似的数据,例如主题、长度、语言、音调等。一个流行的数据集是斯坦福 Twitter 数据集。它包含了自 2009 年 4 月以来的 160 万条带有注释的推文。
Two datasets: the “Stanford Twitter” for Training & evaluation and the newly scraped sentiments using Twitters API
仔细观察“训练和评估”数据集,我们可以看到它在负(0)和正(4)之间达到了完美的平衡,各有 80 万。请注意,数据集缺乏我们在评估模型时需要处理的任何中性情绪:
Balanced ‘training & evaluation’ dataset
该数据集的标签是使用机器学习算法(参见论文此处)而不是人工注释创建的,因此可能包含一些奇怪和误导性的分类。这个例子似乎普遍适用:
使用牙线。我总是害怕晚上的这个时候(0,即消极)
3。 训练和评估模型
基于规则的模型(无需培训)
如前所述,我们希望评估基于规则和机器学习模型的准确性。对于基于规则的评估,我们将研究一个开源框架 VADER (效价感知词典和情感推理机)。Vader(让我们坚持使用小写字母)是一个基于词汇和规则的情绪分析工具,专门针对社交媒体中表达的情绪。通过规则引擎,Vader“倾听”修饰词(加强/削弱),使用大写字母和感叹号(加强感情)。维达甚至支持表情符号和表情符号。
***from** vaderSentiment.vaderSentiment **import** SentimentIntensityAnalyzer
analyzer = SentimentIntensityAnalyzer()analyzer.polarity_scores('great minds think alike')['compound']0.6249*
compound
分数是通过对词典中每个单词的化合价分数求和计算出来的,根据规则进行调整,然后归一化到-1(最极端的负面)和+1(最极端的正面)之间。
让我们对来自“培训和评估”数据集的推文子集(100 k)进行情感分析,并评估:
*df['Vader_compound'] = df['SentimentText'].apply(**lambda** x: analyzer.polarity_scores(x)['compound'])df.sample()*
基于这些分数,我们可以使用维德文章建议的默认临界值来决定情感分类:
正面情绪:compound
得分> = 0.05
中性情绪:(compound
得分> -0.05) & ( compound
得分< 0.05)
负面情绪:compound
得分< = -0.05
根据上述截止值,我们得出以下基于 100 k 样本的混淆矩阵(四舍五入到最接近的 1000):
这相当于 51%的总体准确率(从左上角到右下角的对角线总和,并按预测总数进行划分)。这不是一个令人印象深刻的数字。跳过中性的(在这种情况下,不会影响正/负的总比率,因为它们是近似平均分布的),我们得到 71% 的准确度。请记住,该数据集不包含任何被标记为中性的推文,但模型预测约有 28%属于这一类别。让我们看看这与基于机器学习的方法相比如何。
机器学习
如上所述,我们不仅有一个机器学习模型可供选择,而是有一个由不同算法和方法组成的生态系统。我们将利用所谓的人工神经网络,这种网络近年来以深度学习的名义引起了很多热议。这些神经网络最初是受人脑如何通过以各种方式连接计算机模拟的神经元来运作的启发。这些人工神经元可以被视为实际生物神经元的数学表示。如同生物学上的对应物一样,学习和记忆并不编码在神经元本身,而是编码在神经元之间的连接中。多年来,已经尝试和测试了各种专门的神经网络架构。出于我们的目的,我们将更密切地关注所谓的递归神经网络*,它特别能够处理数据序列,如文本,它可以被视为一系列单词和以特定顺序设置的其他令牌。在循环神经网络家族中,我们发现了一种叫做长短期记忆 ( LSTM )的变体。开发 LSTM 是为了应对与测序数据相关的特定挑战,即长期依赖性。对于文本数据,这意味着 LSTM 可以连接跨越大跨度文本序列的模式。其他架构往往会“忘记”,并主要关注文本序列的最后部分。*
如上所述,这些模型需要大量的培训。这意味着我们必须向神经网络“展示”数千条不同的推文以及正确的答案,然后才能用它来预测情绪。
我们可以花很多很多文章来研究神经网络,尤其是 LSTM 是如何工作的。但是在这篇文章中,我们将集中讨论如何设置它们来进行分类情感的特定任务的训练。对于那些对 LSTM 详细工作原理感兴趣的人,我推荐 Christopher Olah 关于这个话题的博客文章。
在获取和探索我们的数据之后,我们继续进行数据清理或其他预处理步骤。我们将删除任何无助于神经网络将特定文本输入与特定情感相关联的内容。这包括:超链接、用户名、转发前缀、数字和标点符号。其他预处理步骤通常包括移除非常频繁的单词(停用单词)、纠正拼写错误的单词、移除表情符号/表情符号以及将单词简化为其基本形式(词干)。人们必须注意,这些结构中的一些可能实际上包含有助于预测情绪的信息。例如,当删除非常频繁的单词时,我们可能会意外地删除否定词(例如,不要,不会),这些否定词可以通过颠倒句子的整个情感来发挥重要作用。
在我们将清理过的推文输入神经网络之前,我们需要将单词转换成数值,以便模型能够消化它们。这意味着我们需要使用以下方法之一将每个单词映射成一个数字:
- 为文本中出现的每个单词分配一个数字,例如,第一个单词被赋予数字 1,最后一个单词将被映射到 38041(如果所有推文中有那么多独特的单词)
- 使用一种叫做TF-IDF*(Term Frequency-Inverse Document Frequency)的技术:我们给每个单词分配一个实数,这个实数与该单词在一条推文中出现的频率除以该单词在整个推文中出现的频率成比例。这个词在所有推文中出现的频率越高,实际数字就越低。理论是,一个词在所有推文中越不寻常,它能提供的信息就越多。最常见的词不太可能帮助我们区分不同的推文,以预测情绪。*
- 应用单词嵌入*(例如 Word2Vec):一种复杂的方法,不仅将每个单词映射到单个数字,还映射到一系列数字(向量),旨在捕捉一个单词可能包含的许多细微差别和维度。这些向量的一个特别有用的特性是,两个单词彼此越相关,向量就越相似。*
为简单起见,我们将使用(1)并将单词映射到出现时的数字,并将单词限制在最常见的前 20 000 个:
*# Encode the tweets into sequences of integers
**from** keras.preprocessing.text **import** Tokenizertokenizer = Tokenizer(num_words=20000)
tokenizer.fit_on_texts(X_train)
sequences_train = tokenizer.texts_to_sequences(X_train)
sequences_test = tokenizer.texts_to_sequences(X_test)*
例如,下面这条推文:“现在要睡觉了,晚安各位”
映射到:[43,2,137,27,456,181]
最后,我们需要让推文具有相同的维度(长度),我们可以通过将序列“填充”到最大 25 个单词的长度来实现这一点:
*# Pad sequences
max_length = 25X_train = pad_sequences(tokenizer.texts_to_sequences(X_train), maxlen= max_length)*
tweets 现在是填充的,前面的例子是:
[ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,2,137,27,456,181]
巧妙的是,0 位数字就是为了这个目的而保留的。
现在我们终于准备好训练神经网络了。我们将使用谷歌称为 Tensorflow 的深度学习开源框架来定义、编译和训练神经网络。为了让它更容易访问,我们将使用 Keras API,它工作在 Tensorflow 之上,非常适合像我们这样的实验性探索。用于训练的数据是来自我们“训练&评估”数据集的一百万条推文。
*# Define neural network modelmodel = Sequential()
model.add(Embedding(20000, 64))
model.add(Bidirectional(LSTM(64, dropout=0.8, recurrent_dropout=0.8)))
model.add(Dense(1, activation=’sigmoid’))model.summary()*
*# Compile model
model.compile(loss=’binary_crossentropy’, optimizer=’Adam’, metrics=[‘accuracy’])# Train the neural network
model.fit(X_train, y_train, batch_size=32, epochs=5, verbose=2, validation_split=0.1)*
经过近两个小时的培训,我们已经准备好对它进行评估。使用与 Vader 相同的数据进行评估,我们达到了 82%的准确率!这意味着在这个测试集中,神经网络可以正确区分 82%的推文中的消极和积极情绪。想象一下,在几行代码中,我们已经能够建立和训练一个“阅读”推特的神经网络,并且可以相当准确地判断情绪是积极的还是消极的。)神经网络在以适当的格式给出足够的训练数据的情况下,其识别模式的能力简直令人敬畏。
与仅使用 CPU(英特尔 i7–7700 HQ)运行相比,GPU (Nvidia GeForce GTX 1060)帮助将训练时间减少了不到一半。
神经网络的混淆矩阵;
神经网络的这种特殊配置(参见上面代码中的“sigmoid”激活函数)输出一个从 0 到 1 的分数(1 是最积极的情绪)。0.5 左右的值介于两个极端之间,可以解释为这些预测更不确定是正还是负。为了减少错误分类的积极和消极情绪的数量,同时使其与 Vader 结果更具可比性,我们可以更仔细地研究这些更中性的预测分数。
下面是一种使用 0.5 左右的临界值引入中性情绪的方法,从而整理出模型对极性不太确定的情绪:
正面情绪:activation function
> = 0.65
中性情绪:(activation function
< 0.65) & ( activation function
≥ 0.35)
负面情绪:activation function
< 0.35
这产生了以下混淆矩阵:
我们已经设法降低了假阴性和假阳性的比例。但我们同时妥协,通过引入 15 k 中性情绪预测,降低了真正正面和真正负面的数量。忽略中性预测,我们现在的准确率为 86% 。设置截止值是为了以平衡的方式分割不正确的预测。我们现在终于可以比较这两个模型了。机器学习模型以 86%的准确率优于基于规则的 Vader 模型,而当孤立地看正面和负面预测时,准确率为 71%。我们有相当大比例的中性错误分类(分别为 28%和 15%),至少在测试的推文中是这样。但我们应该注意的是,我们想要对其进行预测的 twitter 数据可能与“训练评估”数据集中发现的数据具有不同的特征。例如,我们知道我们想要预测的 twitter feed 实际上会包含中性或中性倾向的推文:
在月球形成之前,地球上的一天只有 6 个小时
(中性情绪)
我们实际上不能说不同的模型在识别中性情绪方面有多好,因为评估数据只包含积极和消极的标签。此外,我们可能会遇到带有大量表情符号、大写字母和感叹号的推文,我们还没有训练我们的机器学习模型来融入这些内容。为了这个效果,我们将使用一个混合版本,使用基于 Vaders 规则的预测结合基于机器学习的*。为此,我们将采用“协商一致”的方法。这意味着,当神经网络预测与 Vader 模型相同的情绪(积极或消极)时,我们将继续使用神经网络分数作为情绪的基础。当神经网络和维达之间缺乏共识时,我们会将推文分类为中性。*
4。 预测情绪,分析结果
最后,我们准备好预测收集的 tweets 上的情绪,并看看结果。我们只需要根据我们定义的关键词过滤掉所有的推文
关键词=[美国宇航局,阿波罗,土星五号,登月,陆地月球]*
查看最频繁出现的转发,我们注意到一些与实际的太空任务无关,所以我们去掉了这些。我们让神经网络和 Vader 算法就情感达成一致,对通过神经网络的推文应用所有需要的预处理。为了使视觉化更直观地解释,我们将把消极情绪和积极情绪分开。我们将转换来自神经网络的负面情绪得分,因此我们最终得到的情绪得分范围是从-1 到 1(而不是从 0 到 1)。我们使用基于 Python 的图形工具 Plotly 及其图表工作室,通过分别绘制所有积极情绪和消极情绪的总和(采用移动平均值使其不那么跳动)来可视化结果:
Predicted sentiments visualized using Plotly’s Chart studio. Times are in UCT (formerly known as GMT)
总体趋势偏向积极情绪,但有趣的是,在登月日前夕,我们看到了明显的消极情绪。此外,向积极情绪高峰的快速转变几乎与实际着陆时间(世界协调时 7 月 20 日 20:17)完全一致。最有可能的是,积极的洪水更多的是由美国晚间新闻广播的时间和影响,全面关注 50 周年。为了进行深入的分析,我们需要仔细观察实际的推文,寻找不同的主题。通过统计 7 月 20 日期间最常用的词(停用词和“关键词”已删除),可以获得一个概览:
*('years', 2302),
('anniversary', 2170),
('ago', 1475),
('one', 1264),
('astronauts', 1213),
('today', 1199),
('go', 1075),
('rocket', 953),
('mission', 938),
('day', 904),*
我们还需要分析任何可能触发特定推文的外部因素。这些外部触发因素可能是与我们想要研究的主题相关的本地或全球新闻。在我们的场景中,这方面的一个例子是 7 月 22 日印度号月船 2 号的成功发射,现在它正在登陆月球表面的路上。
我们用几条示例推文及其预测情绪来总结这一分析。你自己会如何评价这些推文的情绪?
我厌倦了听到我的手机今天的计算能力比美国宇航局阿波罗 11 号的休斯敦任务控制中心还要强
“阿波罗 11 号”的一个伟大时刻是美国宇航局宣布宇航员在发射期间的心率(+0.72)
沃利不是我们家乐高土星五号火箭的粉丝(-0.90)
What is not to like about the LEGO Saturn V rocket?
结论
我个人认为,阿波罗计划执行的登月任务是一项真正鼓舞人心的成就。在我看来,积极的贡献远远超过了成本,主要是通过激励人们追求科学和工程职业。这些地区的受欢迎程度的提高对我们现代的、以技术为基础的经济产生了深远的影响。
在 2018 年期间,许多方法变得出名,这些方法进一步提高了基于机器学习的情感分析的准确性。谷歌的 BERT (变形金刚的双向编码器表示)引起了特别的兴趣。BERT 代表了一个非常大的神经网络,它利用预训练来实现对语言的上下文理解(而不是仅仅在独立的基础上理解不同的单词)。这种上下文理解是通过在海量数据集上对神经网络进行预训练来实现的(想想整个英文维基百科)。在对神经网络进行预训练以“模拟”一种语言之后,它可以通过更有限的训练集来进一步调整以用于特定的任务,例如,用于情感分析,具有最先进的准确性。
除了新的和令人兴奋的神经网络架构之外,还有旨在不仅捕捉文本的极性,而且解码更微妙的情感,例如情感方面(快乐、悲伤、愤怒、惊讶等)的努力。)关于基于机器学习的情感分析的最新发展,请查看代码为的论文。
感谢您花时间阅读这篇文章。希望你已经学到了一些新的东西:
- 情感分析,它的目的和挑战
- 从目标定义到可视化的数据科学周期
- 如何使用混淆矩阵评估分类器
- 如何将基于规则的模型和机器学习模型应用于情感分析
最美好的祝愿/彼得
参考文献
VADER:
休顿,C.J .和吉尔伯特,E.E. (2014 年)。VADER:基于规则的社交媒体文本情感分析的简约模型。第八届网络日志和社交媒体国际会议。密歇根州安阿伯,2014 年 6 月。
LSTM:
Hochreiter,Sepp 和 Jürgen Schmidhuber。"长短期记忆"神经计算9(1997):1735–1780。
情感分析:无以言表
任何纽约移民都知道,在纽约之外很难找到好吃的披萨。加上意大利的家庭传统,好的披萨是一件大事。因此,当我努力开发一个自然语言处理项目来探索情感分析工具时,还有什么比帮助像我这样的人找到完美的比萨饼店更好的使用这些工具呢!
在这个项目中,我使用了来自 Yelp 挑战数据集的餐馆评论。特别是,我使用了 Yelp 归类为“披萨餐厅”或“餐厅,披萨”类别的餐厅。
我知道食客喜欢和不喜欢餐馆的原因各不相同,但我对用户给某个地方的评分并不感兴趣。相反,我想了解就餐者喜欢和不喜欢的特定方面,这样潜在的就餐者就可以更好地了解这个地方是否符合他们的需求。这对于那些获得 3-4 星评价的餐厅来说尤其重要,这些餐厅可能会因为各种不同的原因获得这一评级。
例如,也许你正在寻找一片地道的纽约式比萨饼。在这种情况下,服务和及时性可能就不那么重要了,只要厨师能送货上门。另一方面,也许你正在为朋友的生日计划一次集体聚餐。在这种情况下,服务和氛围对体验非常重要——如果你有一些挑食或对食物敏感的朋友,那么菜单上提供的食物选择不仅仅是披萨。最后,如果你正在午休,但渴望任何形式的融化奶酪,食物质量和氛围可能没有食物准备的及时性重要。
那么如何为披萨店获得这种基于方面的情感呢?首先,我将每个评论拆分成句子,并使用 spaCy 和 gensim 获得评论者在每个句子中提到的不同主题(即,食品质量、服务、等待时间、氛围和菜单品种)。一旦我有了自己的主题(我将把主题建模留给另一个博客),我需要弄清楚评论者对餐馆的这一方面是积极还是消极。这篇文章比较了两种模拟评论者情绪的方法:VADER 和 StanfordCoreNLP。
与 VADER 的感情得分
首先,我尝试了 VADER 情绪包,并定义了一个函数 perspective _ analyzer _ scores()来返回从-1(非常负面)到 1(非常正面)的整体情绪评级。
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
import re
import stringanalyzer = SentimentIntensityAnalyzer()def sentiment_analyzer_scores(text):
score = analyzer.polarity_scores(text)
print(text)
print(score)
我尝试的第一句话非常简单:“这个地方的食物和氛围都非常棒”。这篇评论显然是正面的,果然,VADER 综合情绪得分为 0.84。到目前为止一切顺利。
text_pos = 'this place was amazing great food and atmosphere'
sentiment_analyzer_scores(text_pos)
VADER 在这个非常简单的负面评价中也表现不错,综合得分为-0.66:
text_neg = 'i didnt like their italian sub though just seemed like lower quality meats on it and american cheese'
sentiment_analyzer_scores(text_neg)
然而,在这个更微妙的例子中,它卡住了。以“所有东西对我来说都像垃圾,但我们不断回来,因为我的妻子喜欢意大利面”为例。这位评论者显然不喜欢这家餐厅,尽管他或她的妻子“喜欢”意大利面(附带说明,这位评论者应该赢得年度配偶奖,因为他继续吃垃圾来取悦妻子!)任何带有垃圾这个词的美食评论都应该立刻遭到否定,但 VADER 给出了非常正面的 0.7 分。
text_amb = "everything tastes like garbage to me but we keep coming back because my wife loves the pasta"
sentiment_analyzer_scores(text_amb)
发生了什么事?下面这个函数返回一个单词列表,VADER 把这些单词分为积极的、中性的和消极的。根据自述文件,“VADER (Valence Aware 字典和情感推理器)是一个词汇和基于规则的情感分析工具,专门针对社交媒体中表达的情感。”因此,它依赖于某些单词的极性来确定整体情绪。
import nltk
nltk.download('punkt')
nltk.download('vader_lexicon')
from nltk.tokenize import word_tokenize, RegexpTokenizerdef get_word_sentiment(text):
tokenized_text = nltk.word_tokenize(text)
pos_word_list=[]
neu_word_list=[]
neg_word_list=[]for word in tokenized_text:
if (analyzer.polarity_scores(word)['compound']) >= 0.1:
pos_word_list.append(word)
elif (analyzer.polarity_scores(word)['compound']) <= -0.1:
neg_word_list.append(word)
else:
neu_word_list.append(word)print('Positive:',pos_word_list)
print('Neutral:',neu_word_list)
print('Negative:',neg_word_list)
正如下面的输出所示,单词“loves”和“like”的正极性必须相当高。此外,如果没有对这个句子的更广泛的句法理解,将这个句子注册为否定的唯一单词是“垃圾”。在这种情况下,“垃圾”被认为是中性的,整体文本被确定为相当积极的。
get_word_sentiment(text_amb)
进入斯坦福核心 NLP
斯坦福的核心 NLP 程序正好有这个问题的解决方案,因为它是在电影评论上训练的,其中评论者可能会在同一个句子中讨论电影的积极和消极方面(例如,“情节缓慢,但表演很棒”)。
据该网站称,该模型“实际上是基于句子结构建立了一个完整句子的表示,而不是看单个单词的情绪。它根据单词如何组成更长短语的意思来计算情感。这样,模型就不像以前的模型那样容易被愚弄了。”
完美!幸运的是,有一个 Python 包装器可以让您调用核心 NLP 服务器(它返回结果的速度惊人地快)。要进行调用,您需要 pip 安装 pycorenlp,并从 pycorenlp 导入 StanfordCoreNLP。然后,在终端中,将 cd 放入 Stanford CoreNLP 文件夹,并使用以下命令启动服务器:
cd stanford-corenlp-full-2018-10-05
java -mx5g -cp "*" edu.stanford.nlp.pipeline.StanfordCoreNLPServer -timeout 10000
太好了——现在让我们看看效果如何。
#!pip install pycorenlp
from pycorenlp import StanfordCoreNLPnlp = StanfordCoreNLP('[http://localhost:9000'](http://localhost:9000'))def get_sentiment(text):
res = nlp.annotate(text,
properties={'annotators': 'sentiment',
'outputFormat': 'json',
'timeout': 1000,
})
print(text)
print('Sentiment:', res['sentences'][0]['sentiment'])
print('Sentiment score:', res['sentences'][0]['sentimentValue'])
print('Sentiment distribution (0-v. negative, 5-v. positive:', res['sentences'][0]['sentimentDistribution'])
在将食物视为垃圾的评论中,该模型将整个句子分类为非常负面(0 是最负面的,4 是最正面的)。情绪分布显示,这句话有一些中性甚至积极的方面,但总体来说这不是一个好的评估。
get_sentiment(text_amb)
还有一个酷直播演示,展示了该模型如何将句子的不同点解析为积极和消极的方面:
http://nlp.stanford.edu:8080/sentiment/rntnDemo.html
为了更好地衡量,我将把上面的肯定句和否定句放进去:
get_sentiment(text_pos)
get_sentiment(text_neg)
这就是你要的,一个细致入微的情感分析包,非常适合评论电影、书籍、消费品和…披萨!
我应该指出,这篇文章绝不是对 VADER 的批评——它有一些很棒的功能,例如它能够识别社交媒体口语(“LOL”,表情符号),并从所有大写字母和标点符号中提取重点。相反,我的目的是强调一种情绪分析工具,它非常适合包含积极和消极方面的客户评论。
我希望这篇文章对你有所帮助,欢迎在评论中提出任何反馈或问题!
酒店评论的情感分析
Photo by Marten Bjork on Unsplash
不管你喜不喜欢,客人评论正成为影响人们预订/购买的一个突出因素。
想想你过去的经历。当你在 Expedia/Booking/猫途鹰寻找一个度假的地方时,你做了什么?我敢打赌,在你知道之前,你会滚动屏幕查看评论。
作为一名企业主或员工,如果你仍然怀疑客人评论对企业的影响有多重要,不妨看看一些统计数据:
换句话说,客人的评论显然会影响人们的预订决定,这意味着,你最好注意人们对你的酒店的评论!
你不仅想要好的评论,还希望它们能帮助你最大程度地了解你的客户。评论可以告诉你是否符合客户的期望,这对于根据客户的角色制定营销策略至关重要。
点评很重要,作为酒店老板,你需要开始利用它。
什么是情感分析
情感分析,也称为观点挖掘,是一种文本挖掘技术,可以提取给定文本的情感——无论是积极的、消极的还是中性的,并返回一个情感分数。这种技巧通常用于评论或社交媒体文本。
在本文中,我将向您展示如何使用 网络抓取工具有效地收集酒店评论,并使用 Python* 进行情感分析。*
使用 Octoparse 抓取评论
我使用的网页抓取工具叫做octoporparse。这是一个为像我一样没有编码背景的人建立的自己动手的网络刮刀。我将向您展示如何使用 Octoparse 来收集纽约市排名第一的酒店——由图书馆酒店收藏的长颈鹿酒店的评论。
以下是该网页的链接:
https://www . tripadvisor . com/Hotel _ Review-g 60763-d 99762-Reviews-Hotel _ 长颈鹿 _ by _ 图书馆 _ 酒店 _ 收藏-New _ York _ City _ New _ York . html #点评
首先,我们将在 Octoparse 中导入我们的目标网址。
注意每页只有 5 条评论,所以如果我们需要浏览所有评论,我们需要让 Octoparse 对所有评论页面进行分页。
怎么会?当我们仔细查看评论时,我们可以看到一些评论上有一个“阅读更多”按钮。在这种情况下,我们的爬虫需要在提取之前点击按钮来加载整个评论。
接下来,我们循环遍历所有评论项,并提取每个评论。
**最后但同样重要的是,**将新创建的“循环项目”拖出来,放在第一个“循环项目”的下面。这是因为我们想在提取实际评论之前先点击所有的“阅读更多”。
一旦我们成功提取了该酒店的所有评论,我们就可以使用 Python 获得每个评论的情感分数了。
小贴士:如果你想获得更详细的关于搜集访客评论的分步指南,可以看看这篇 帖子 。
用 Python 进行情感分析
首先,我们要导入库。这里我们将使用两个库进行分析。
第一个叫 pandas ,是一个开源库,用 Python 提供了简单易用的数据结构和分析函数。
第二个我们要使用的是 Python 中一个强大的库,叫做【NLTK】。NLTK 代表自然语言工具包,这是一个常用的 NLP 库,有很多语料库、模型和算法。
让我们继续导入刮掉的评论。
下面,我们应用了来自 nltk .情操.维德包的一个名为*SentimentIntensityAnalyzer()*的函数。情感分析器可以利用 NLTK 算法和特性实现和促进情感分析任务,因此无需复杂编码即可生成情感得分。在使用它之前,我们需要调用它。
现在我们已经调用了函数,应用它来生成极性得分。有四种类型的分数:负面的、中性的、正面的和复合的。通过使用 apply() 和 lambda ,我们可以转换结果并将它们放入“reviews”数据框架中。
然后我们有每个评论的情感分数。
每个评论都有一个负面、中性、正面和复合分数。复合得分是前三项得分的综合评定。这个分数从-1 到 1 不等。通常我们会设置一个复合分数的阈值来识别情感。这里我们可以将阈值设置为 0.2。如果评论的综合得分大于 0.2,则该评论是正面的。如果一个评论的综合得分小于 0.2,则该评论是负面的。如果复合得分在-0.2 到 0.2 之间,那么复习就是神经的。
我们可以看到,97.2%的评价是正面的,只有 1.22%的评价是负面的。根据分析,可以肯定地说,图书馆酒店收藏的长颈鹿酒店是一家广受欢迎的酒店。
当然,我们还可以做更多的事情来进一步分析评论,例如:
- 构建一个词云或话题建模模型,识别人们喜爱这家酒店的关键原因是什么。
- 通过提取其他酒店的点评,按照上述步骤进行分析,与其他酒店进行情感评分对比。
- 提取更多信息,如审查日期、审查者的贡献、审查者的有益投票、审查有益投票、份额数量等,将其可视化并应用业务分析方法。
你现在知道评论对你的事业成功有多重要了。为什么不去 Octoparse 亲自尝试一下呢?Octoparse 是一款易于使用的网络抓取工具,可以帮助你点击一下就将网站转化为结构化数据。更好的是,有针对不同流行网站的现成模板和终身免费版本。如果你的网络抓取相关项目需要任何帮助,请随意评论!
原载于 2019 年 9 月 11 日https://www.octoparse.com。