TowardsDataScience 博客中文翻译 2020(六百六十五)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

从时间序列的角度看 NLP

原文:https://towardsdatascience.com/nlp-from-a-time-series-perspective-39c37bc18156?source=collection_archive---------24-----------------------

时间序列分析如何补充自然语言处理

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

来源:来自 Pixabay免费照片

从表面上看,自然语言处理(NLP)和时间序列分析似乎没有太多的共同点。

在数据科学的背景下,分析文本的主要原因通常如下:

  1. 文本摘要(即对文本进行摘要以便更好地理解)
  2. 文本分类(例如,根据某些特征对文本进行分类,如检测垃圾邮件)
  3. 情感分析(使用文本分类来确定特定群体对某个主题的情感,例如书评)
  4. 文本生成(即使用机器学习技术生成特定主题的新文本)

当文本分类模型失败时

在这方面,我特别希望解决文本分类和情感分析的领域。

让我们考虑一个例子。假设有人在 2019 年建立了一个情绪分析模型,以衡量旅行情绪。数据可能来自各种社交网络,如 Twitter、Reddit 等。

尽管由于旅游对气候变化的影响存在一定程度的担忧,但人们对旅游的情绪可能仍然相当积极。

然而,2020 年对于旅行来说是一个非常不同的场景(或者说没有),由于新冠肺炎疫情,航空乘客数量急剧下降。

因此,如果今天运行,任何根据 2019 年数据训练的情绪模型都可能表现很差。旅行限制、对病毒的恐惧和经济问题可能在任何用于训练文本分类模型以衡量旅行情绪的语料库中都没有得到充分的体现。此外,术语“新冠肺炎”在今年之前并不存在,并且文本分类模型不知道在旅行的上下文中将负面情绪分配给该术语。

时间序列分析的潜力

文本分类和情感分析技术是最近的创新。

因此,在很长一段时间内,即几年内,没有太多的空间来制定一个跟踪情绪的时间序列。然而,随着 NLP 技术现在变得越来越普遍,这种技术很可能从这里开始变得越来越普遍。

回到旅行的例子,假设我们有一个随时间整理的训练集,其中包含按日期排序的句子,这些句子表达了对旅行的特定情感。仅举两个例子:

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

来源:作者创建的图像和数据

现在,假设一个情绪分数是通过每周的所有个人分数相加计算出来的。以下时间序列仅仅是假设性的,但出于所有意图和目的,让我们假设从 2019 年初到现在的旅行情绪看起来像这样:

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

来源:作者创建的数据

在这个假设的例子中,在新冠肺炎到来之前,市场情绪总体上是积极的,尽管时间序列往往会伴随着不可避免的“波动和低谷”。

如上图所示,COVID 之后,旅游人气可能会比之前更加不稳定。有些人发誓在疫情完全被控制之前不进行任何形式的旅行,而有些人则选择在任何限制取消后立即旅行。

在现实世界中,用于情感模型的训练数据需要不断更新,以确保在所有单独的文本条目中准确地衡量情感。

然而,情感数据时间序列的公式不仅允许随着时间的推移跟踪这种情感,而且还可以与时间序列建模技术一起使用,以与预测常规时间序列相同的方式预测未来的情感。这可以通过传统的时间序列技术(如 ARIMA)或基于机器学习的技术(如 LSTM、CNN 等)来完成。

在旅游环境中,如果有足够的情绪数据,预测情绪趋势以确定旅游情绪何时会上升是可能的。这不一定是万无一失的,因为这个特殊的例子更依赖于新冠肺炎的结果,而不是从过去的趋势推断,但很可能是一种有用的分析形式。

事实上,考虑到 NLP 是一个相当新的研究领域,关于情感分析是否可能在某些时间序列领域中作为领先指标的问题仍然存在。

例如,假设在某个时间点,在社交媒体帖子中检测到股市的异常负面情绪。根据主要股票指数(如标准普尔 500)的价格变化,人们可以使用时间序列来验证情绪在股票价格波动中的作用。

股价是在负面情绪表达的那一刻下跌的——还是有一个小时左右的滞后?另一方面,在任何负面情绪通过社交媒体表达出来之前,市场调整了吗?

结合时间序列分析和 NLP 来回答这些问题,可以在确定情绪分析在金融市场中扮演的角色方面产生巨大的洞察力。

结论

作为一名时间序列专家,我的观点是 NLP 是一种很好的分析形式——但是当与时间序列技术结合时,它的潜力可以被完全释放。

为了充分理解情绪分析对某些业务领域的影响,我们还必须了解情绪如何随着时间的推移而变化,以及支撑这种变化的催化剂。

到目前为止,NLP 还是一个相当新的领域,这个领域还没有被充分开发。然而,我认为,如果正确使用,自然语言处理和时间序列分析可以证明是向前发展的杀手组合。

非常感谢您的阅读,您也可以在michael-grogan.com找到更多我的数据科学内容。

免责声明:本文是在“原样”的基础上编写的,没有担保。它旨在提供数据科学概念的概述,不应以任何方式解释为专业、医疗或投资建议。

Python 中的 NLP 数据清理

原文:https://towardsdatascience.com/nlp-in-python-data-cleaning-6313a404a470?source=collection_archive---------6-----------------------

使用 Kaggle 的真实或虚假新闻数据集的典型 NLP 机器学习模型管道中涉及的数据清洗步骤。

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

来自 UnsplashRoman Kraft 的照片

在任何机器学习模型中,数据清洗都是非常关键的一步,但对于 NLP 来说更是如此。如果没有清理过程,数据集通常是计算机无法理解的一串单词。在这里,我们将回顾在典型的机器学习文本管道中完成的清理数据的步骤。

我们将使用一个将新闻分为真假的数据集。数据集可以在 Kaggle 上找到,数据集的链接在下面,

https://www . ka ggle . com/clmentbisaillon/fake-and-real-news-dataset

有两个数据集 Kaggle 数据集中的假新闻和真新闻。我把假新闻和真实新闻的数据集连接起来。十大真假新闻如下:

news.head(10)

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

10 条假新闻

news.tail(10)

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

10 条真实新闻

我们将通过几个步骤来清理新闻数据集,删除不必要的内容,并突出适合 ML 模型的关键属性。

第一步:标点

标题文本有几个标点符号。标点符号通常是不必要的,因为它不会增加 NLP 模型的价值或意义。“字符串”库有 32 个标点符号。标点符号是:

import string
string.punctuation

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

字符串库中的标点符号

要删除数据集中的标点符号,让我们创建一个函数并将该函数应用于数据集:

def remove_punctuation(text):
    no_punct=[words for words in text if words not in string.punctation]
    words_wo_punct=''.join(no_punct)
    return words_wo_punctnews['title_wo_punct']=news['title'].apply(lambda x: remove_punctuation(x))
news.head()

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

新闻数据集(包括标题,不带标点符号)

步骤 1 之后的列已经从标题文本中删除了标点符号。

步骤 2:标记化

标记化是将字符串拆分成单词列表的过程。我们将利用正则表达式或正则表达式来进行拆分。正则表达式可以用来描述一个搜索模式。

def tokenize(text):
    split=re.split("\W+",text) 
    return split
news['title_wo_punct_split']=news['title_wo_punct'].apply(lambda x: tokenize(x.lower()))
news.head()

这里,“\W+”拆分一个或多个非单词字符

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

新闻数据集(包括标题,不带标点和拆分)

步骤 2 之后的列通过拆分所有非单词字符创建了一个列表。

第三步:停用词

现在,我们有一个没有任何标点符号的单词列表。让我们继续并删除停用词。停用词是不相关的词,无助于识别文本的真伪。我们将对停用词使用“nltk”库,该库中的停用词包括:

stopword = nltk.corpus.stopwords.words('english')
print(stopword[:11])

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

这个库中有 179 个停用词。

def remove_stopwords(text):
    text=[word for word in text if word not in stopword]
    return text
news['title_wo_punct_split_wo_stopwords'] = news['title_wo_punct_split'].apply(lambda x: remove_stopwords(x))
news.head()

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

新闻数据集(包括标题,不带标点,不带停用词和拆分)

步骤 3 之后的列已经删除了不必要的停用词。

第四步:词尾/词干

词干化和词尾化是将一个单词简化为其词根形式的过程。主要目的是减少同一个单词的变化,从而减少模型中包含的单词集。词干化和词汇化的区别在于,词干化会去掉单词的末尾,而不考虑单词的上下文。然而,词汇化考虑单词的上下文,并根据字典定义将单词缩短为其词根形式。与词干化相比,词干化是一个更快的过程。因此,这是速度和准确性之间的权衡。

让我们以“信念”这个词为例。相信的不同变体可以是相信、被相信、相信和相信。

print(ps.stem('believe'))
print(ps.stem('believing'))
print(ps.stem('believed'))
print(ps.stem('believes'))

以上所有的 stem 结果是相信

print(wn.lemmatize(“believe”))
print(wn.lemmatize(“believing”))
print(wn.lemmatize(“believed”))
print(wn.lemmatize(“believes”))

按照书面陈述的顺序,词汇化结果是——相信、相信、相信和相信。

如果单词不在语料库中,Lemmatize 会产生相同的结果。Believe 被词条化为 belief(词根)

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

新闻数据集(词汇化与词干化)

如上图所示,lemmatize 和 stem 产生不同的结果。我们可以为我们的最终模型选择任何一个。

第五步:其他步骤

可以基于该数据执行其他清洁步骤。我在下面列出了其中的一些,

  1. 删除 URL
  2. 删除 HTML 标签
  3. 移除表情符号
  4. 删除号码

我很想听听你对我的文章的想法和反馈。请在下面的评论区留下它们。

Python 中的 NLP 矢量化

原文:https://towardsdatascience.com/nlp-in-python-vectorizing-a2b4fc1a339e?source=collection_archive---------20-----------------------

在典型的 NLP 机器学习模型管道中使用的常见矢量化技术,使用来自 Kaggle 的真实假新闻数据集。

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

来自 UnsplashRoman Kraft 的照片

在本文中,我们将学习矢量化以及 NLP 模型中采用的不同矢量化技术。然后,我们将把这些概念应用到一个问题的上下文中。

我们将使用一个将新闻分为真假的数据集。该数据集可在 Kaggle 上获得,数据集的链接如下:

https://www . ka ggle . com/clmentbisaillon/fake-and-real-news-dataset

典型的机器学习文本管道的第一步是数据清理。这一步在以前的文章中有详细介绍,链接如下:

[## Python 中的 NLP 数据清理

使用真实或虚假新闻数据集的典型 NLP 机器学习模型管道中涉及的数据清洗步骤…

towardsdatascience.com](/nlp-in-python-data-cleaning-6313a404a470) 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据清理后的数据集

原始新闻标题被转换成一种干净的格式,只包含基本信息(上图的最后一栏)。下一步是将清洗后的文本进一步转化为机器学习模型可以理解的形式。这个过程被称为向量化。在我们的上下文中,每个新闻标题都被转换成一个代表该特定标题的数字向量。有许多矢量化技术,但在本文中,我们将重点介绍三种广泛使用的矢量化技术——计数矢量化、N-Grams、TF-IDF,以及它们在 Python 中的实现。

  1. 计数矢量化

如上所述,矢量化是将文本转换为矩阵形式的数字条目的过程。在计数矢量化技术中,生成文档术语矩阵,其中每个单元是对应于新闻标题的计数,指示单词在文档中出现的次数,也称为术语频率。文档术语矩阵是一组虚拟变量,指示特定单词是否出现在文档中。语料库中的每个单词都有一个专栏。该计数与新闻标题类别的相关性成正比。这意味着,如果一个特定的词在假新闻标题或真实新闻标题中出现多次,那么该特定的词具有确定该新闻标题是假还是真的高预测能力。

def clean_title(text):
   text = "".join([word.lower() for word in text if word not in            string.punctuation])
   title = re.split('\W+', text)
   text = [ps.stem(word) for word in title if word not in nltk.corpus.stopwords.words('english')]
   return text
count_vectorize = CountVectorizer(analyzer=clean_title) 
vectorized = count_vectorize.fit_transform(news['title'])

剖析上面的代码,函数“clean _ title”——连接小写的新闻标题,不加标点。然后,在任何非单词字符上拆分文本。最后,对不间断单词进行词干分析,并以列表的形式呈现出来。这篇文章中给出了清洁过程的详细描述。

接下来,我们使用了 sklearn 库中 sk learn . feature _ extraction . text 下的“计数矢量器”包。默认值和定义可在 scikit-learn — 计数矢量器文档中找到。在上面的代码中,我们实例化了计数矢量器并定义了一个参数— 分析器。其他参数是它的默认值。分析器参数调用一个字符串,我们传递了一个函数,它接收原始文本并返回一个干净的字符串。

文档术语矩阵的形状是 44898,15824。共有 44898 个新闻标题,所有标题中有 15824 个独特的词。

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

新闻标题中 15824 个唯一单词的子集

矢量器产生稀疏矩阵输出,如图所示。为了节省空间,只存储非零值的位置。因此,矢量化的输出如下所示:

<20x158 sparse matrix of type '<class 'numpy.int64'>'
	with 206 stored elements in Compressed Sparse Row format>

但是,将上述内容转换为数组形式会产生以下结果:

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

如图所示,大多数单元格包含 0 值,这被称为稀疏矩阵。许多矢量化输出看起来与此类似,因为许多标题自然不会包含特定的单词。

  1. N-Grams

与计数向量化技术类似,在 N 元语法方法中,生成一个文档术语矩阵,每个单元代表一个计数。N-grams 方法的区别在于,计数表示标题中长度为 N 的相邻单词的组合。计数矢量化是 N 元语法,其中 n=1。比如“我爱这篇文章”有四个字,n=4。

如果 n=2,即 bigram,那么列将是— [“我爱”,“爱这个”,“这篇文章”]

如果 n=3,即三元组,那么列将是— [“我喜欢这个”,“喜欢这篇文章”]

如果 n=4,即 4 克,那么列应该是-['“我喜欢这篇文章”]

基于性能选择 n 值。

对于 python 代码,清理过程的执行类似于计数矢量化技术,但单词不是以标记化列表的形式。将标记化的单词连接起来形成一个字符串,因此可以将相邻的单词聚集在一起以有效地执行 N 元语法。

清理后的标题文本如下所示:

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

剩下的矢量化技术与我们上面使用的计数矢量化方法相同。

权衡是在 N 个值之间进行的。选择较小的 N 值可能不足以提供最有用的信息。而选择一个高的 N 值,将产生一个具有大量特征的巨大矩阵。N-gram 可能很强大,但是需要多一点小心。

3。术语频率-逆文档频率(TF-IDF)

与计数矢量化方法类似,在 TF-IDF 方法中,生成一个文档术语矩阵,每一列代表一个唯一的单词。TF-IDF 方法的不同之处在于,每个单元格都不表示词频,但是单元格值表示一个权重,它突出了特定单词对文档的重要性。

TF-IDF 公式:

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

等式的第二项有助于抽出生僻字。那是什么意思?如果一个单词在许多文档中多次出现,那么分母 df 将增加,从而减少第二个词的值。词频或 tf 是一个词(x)在文档(y)中出现的次数除以 y 中总字数的百分比。

对于 python 代码,我们将使用与计数矢量器方法相同的清理过程。Sklearn 的 TfidfVectorizer 可以用于 Python 中的矢量化部分。

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

该方法的稀疏矩阵输出显示代表文档中单词权重的小数。高权重意味着该单词在几个文档中出现多次,低权重意味着该单词在许多文档中出现的次数较少或者在多个文档中重复出现。

总结思路

选择矢量化方法没有经验法则。我经常根据手头的业务问题做出决定。如果没有限制,我经常从最简单的方法开始(通常是最快的)。

我很想听听你对我的文章的想法和反馈。请在下面的评论区留下它们。

张量流中的 NLP

原文:https://towardsdatascience.com/nlp-in-tensorflow-generate-an-ed-sheeran-song-8f99fe76662d?source=collection_archive---------52-----------------------

生成一首艾德·希兰歌曲

写歌是有史以来最困难的任务之一。在本文中,我将向您介绍我编写一个模型的经历,这个模型将学习一些艾德·希兰歌曲,并尝试为一首歌曲创作一些开头的句子。我很清楚,最好是制作一个能产生声音的模型,而不仅仅是一首诗,因为艾德·希兰的作品主要是留在我们脑海中的朗朗上口的节奏。然而,我并不想创造莎士比亚的文本。我想要更现代的东西。用它的音频乐队创作歌词应该很棒,但让我们先集中精力创作歌词,一步一步来!

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

摄影爱好在 Unsplash

让我们开始吧!

对于我的代码的基本概念,请看看我的文章在 Tensorflow 的 NLP:情绪分析

首先,让我们导入一些艾德·希兰歌曲的库和源文件。我拿了最后两张专辑开始我们的训练。你可以在我的 Github 这里找到它。

在标记化、排序和填充之后,我们就可以预测了。生成文本就是预测下一个单词是我们语料库中的哪个单词。一个几乎简单的多类预测不是吗?!是的,差不多了!

所以让我们建立一个简单的模型。我们从嵌入层开始。请看我之前关于嵌入的文章。

通过嵌入,我们得到了每个单词的向量。向量按距离排序。例如“草是……”下一个单词预测应该是“绿色”。简单!但是有时候你需要在段落的开头提供信息。那么我们如何分析我们的句子,并在记忆中保留之前的单词,不管之前有多少个句子。

答案是 RNN 循环神经网络。对于视频中的帧序列、文字序列中的单词、音乐序列中的音符,它都非常方便。所以序列数据通常需要 RNN。请阅读 Christopher Olah 关于理解 LSTM 网络的基础文章和 Andrej Karpathy 的递归神经网络的不合理有效性以获得更多信息。

如果你对 RNN 和 LSTM 背后的数学或理论不感兴趣,请随意跳到文章的结尾,但我希望你会和我一样发现学习人工智能背后的数学魔力是令人惊讶和兴奋的。

简而言之,RNN 是一个神经网络,其中每个细胞都有来自前一个细胞的隐藏信息作为其条目之一。这里有三个带有 tanh 激活功能的节点。

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

图片来自 Christopher Olah 博客

理论上,这应该能很好地记忆上下文,但事实并非如此。这可以用消失梯度问题来解释。事实上,为了测量我们预测的质量,我们需要知道输出单元——预测向量(通过权重乘以先前单元输出的激活函数加上偏差的连续计算——顺便说一下,这是正向传播)和实际向量之间的差异。在我们的例子中,输出是一个单词的预测。所以预测向量减去实际向量,得到损失值或误差值。这种差异越小,我们的预测就越接近。最小化这个误差就是通过提高我们的权重值(学习的能力)来最大化我们预测的准确性。在数学中,为了最小化一个函数,我们计算偏导数,从最后一个节点到第一个节点。这是反向传播。我们在每一层反向传播误差梯度。通常,梯度很小,以至于导数变得越来越小,然后消失。因此,当我们开始反向传播时,改变权重值的幅度在最后的节点中比在第一个节点中更有效。所以当我们反向传播时,我们不能记住梯度的大小。那么我们如何记住节点间的梯度呢?RNN 的黄金之星 LSTM 来了!

LSTM

LSTM 或长短期记忆。对于每个 RNN 单元格,我们用一个 LSTM 单元格来代替,如下所示:

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

图片来自克里斯托弗·奥拉的博客

在每个细胞中,我们制作一个小的神经网络,它的度量能够基于梯度下降来学习要记住什么和要注意什么。是不是很神奇?!

模型

所以,回到我们的艾德·希兰艾歌。我们的简单模型是一个嵌入层,一个双向的 LSTM - 请阅读 本文 了解更多信息- 所以我们解析过去和未来的句子,并用一个密集的层来包装这一切。让我们创作歌曲吧!

因此,我们的艾德·希兰人工智能模型将从“爱是什么”这句话中创造出什么:

以下是生成的文本:

I don't know about me 
know i love someone else 
i've either way to dress 
that i really wanna know
that i'll be creeping a long time 
a bit too way up we found love 
right where we are 
oh a mystery 
a purse a time to move home 
that could breathe 
still oh still time can it takes to get a long

让我们试试“爱是什么”

What love is a blaze time 
that it takes to give out all 
when you stand upon my bed 
are changed of crisps 
but i guess that plays never leave me 
a stage a song that i wrote 
that mixing fitting at my erasing 
and a song my heart is a getting ahead of a couple of days 
a new you take a

对于第一个简单的模型来说还不错。它可以给人灵感来重写它,使它更准确,但不知何故,它是诗意的。

我肯定会在我的语料库中添加更多的歌曲,看看它的表现如何。但是在内存饱和之前,我们不能添加那么多到我们的语料库中。

你可以在 Github 这里找到我的代码。

我希望你喜欢这个与 TensorFlow 艾德·希兰一代歌曲代码的演练。喜欢的请给它掌声。这是一个基线,所以请随意复制我的代码,并让我知道你对它的看法。编码快乐!

金融市场中的 NLP 情绪分析

原文:https://towardsdatascience.com/nlp-in-the-financial-market-sentiment-analysis-9de0dda95dc?source=collection_archive---------8-----------------------

最新的自然语言处理模型在多大程度上优于传统模型——从词汇方法到用于金融文本情感分析任务的 BERT

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

马库斯·斯皮斯克在 Unsplash 上的照片

D 自 2012 年先驱 CNN 在 ImageNet 上推出 AlexNet 以来,计算机视觉中的 eep 学习已经成功应用于各种应用中。相反,NLP 在深度神经网络应用方面已经落后了。许多声称使用人工智能的应用程序通常使用某种基于规则的算法和传统的机器学习,而不是深度神经网络。2018 年,一种名为 BERT 的最先进(STOA)模型在一些自然语言处理任务中的表现超过了人类的分数。在这里,我将几个模型用于情绪分析任务,看看它们在我所在的金融市场中有多大用处。代码在 jupyter 笔记本中,可以在 git repo 中获得。

1.介绍

NLP 任务可以大致分为以下几类。

  1. 文本分类—过滤垃圾邮件,对文档进行分类
  2. 单词序列—单词翻译、词性标签、命名实体识别
  3. 文本含义—主题建模、搜索、问题回答
  4. 序列对序列—机器翻译、文本摘要、问答
  5. 对话系统

不同的任务需要不同的方法,在大多数情况下是多种自然语言处理技术的结合。开发机器人时,后端逻辑通常是基于规则的搜索引擎和排名算法,以形成自然的交流。

这是有充分理由的。语言有语法和词序,这可以通过基于规则的方法更好地处理,而机器学习方法可以更好地学习搭配和单词相似性。word2vec、词袋等矢量化技术有助于模型以数学方式表达文本。最著名的例子是:

King - Man + Woman = QueenParis - France + UK = London

第一个例子描述了性别关系,第二个例子描述了首都的概念。然而,在这些方法中,由于在任何文本中相同的单词总是由相同的向量表示,所以上下文没有被捕获,这在许多情况下是不正确的。递归神经网络( RNN )架构使用来自输入序列的先前信息并处理时间序列数据,在捕获和记忆上下文方面表现良好。典型的架构之一是长短期记忆( LSTM ),由输入门、输出门和遗忘门组成,以克服 RNN 的清漆梯度问题。有许多基于 LSTM 的改进模型,例如双向 LSTM,不仅可以从前面的单词中捕捉上下文,还可以从后面捕捉上下文。这些对于一些特定的任务是好的,但是在实际应用中不太好。

2017 年,我们看到了一种无需递归或卷积架构即可解决该问题的新方法。注意力是你需要的全部提出了一种 transformer 架构,这是一种基于注意力机制的编解码栈。来自 Transformers ( BERT ) 的双向编码器表示,是 Google 在 2018 年推出的具有多个编码器堆栈的掩码语言模型,在 GLUE、SQuAD 和 SWAG 基准测试中实现了较大的改进。有很多文章和博客解释这个架构,比如 Jay Alammar 的文章。

在金融行业工作,我很难看到过去几年在交易系统中生产使用的 NLP 上的机器学习模型在我们过去的 R&D 中足够稳健的性能。现在,由于 Huggingface 的实施,基于 BERT 的模型变得越来越成熟和易于使用,并且许多预先训练的模型已经公开。我的目标是看看 NLP 的最新发展是否达到了在我的领域中使用的良好水平。在这篇文章中,我在一个相当简单的金融文本情感分析任务上比较了不同的模型,作为判断是否值得在实际解决方案中尝试另一个 R & D 的基线。

这里比较的模型有:

  1. 使用词典的基于规则的方法
  2. 使用 Tfidf 的传统机器学习方法
  3. 作为递归神经网络结构的 LSTM
  4. 伯特(和艾伯特)

2.输入数据

对于情感分析任务,我采用以下两种输入来代表行业中的不同语言。

  1. 金融标题作为更正式的风格
  2. 来自 Stocktwits 的 Tweets 作为交易者的非正式谈话

后者我会再写一篇,所以这里重点说一下前者的数据。这是一个包含更正式的金融领域特定语言的文本示例,我使用了 Malo 等人(2014) 的 FinancialPhraseBank,它由 16 个人的 4845 个手写标题文本组成,并提供了 agree 级别。我用 75%同意的标签和 3448 个文本作为训练数据。

## Input text samples**positive** "Finnish steel maker Rautaruukki Oyj ( Ruukki ) said on July 7 , 2008 that it won a 9.0 mln euro ( $ 14.1 mln ) contract to supply and install steel superstructures for Partihallsforbindelsen bridge project in Gothenburg , western Sweden."**neutral** "In 2008 , the steel industry accounted for 64 percent of the cargo volumes transported , whereas the energy industry accounted for 28 percent and other industries for 8 percent."**negative** "The period-end cash and cash equivalents totaled EUR6 .5 m , compared to EUR10 .5 m in the previous year."

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

词云为第一次输入训练文本,图片由作者提供

请注意,所有的数据属于来源,人们必须尊重他们的版权和许可条款。

3.模型

下面是我对比的四款机型的性能。

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

NLP 模型评估,图片由作者提供

A.基于词典的方法

创建特定领域的词典是一种传统的方法,在某些情况下,当来源来自特定的人或媒体时,这种方法简单而有效。拉夫兰和麦克唐纳感情词表。这个列表包含超过 4k 个单词,出现在带有情绪标签的财务报表上。注意:这些数据需要许可证才能用于商业应用。请在使用前查看他们的网站。

## Samplenegative: ABANDON
negative: ABANDONED
constraining: STRICTLY

我用了负 2355 字,正 354 字。它包括单词形式,所以不要对输入进行词干分析和词条解释。对于这种方法,考虑否定是很重要的。不,不,不要等词语。把否定词的意思改成肯定词,如果否定词之一出现在肯定词之前的三个词里,我就简单地颠倒一下情绪。

然后,音调分数定义如下,并沿正/负计数送入分类器。

tone_score = 100 * (pos_count — neg_count) / word_count

使用默认参数训练 14 个不同的分类器,然后使用网格搜索交叉验证进行随机森林的超参数调整。

B.基于 Tfidf 向量的传统机器学习

输入由 NLTK word_tokenize()进行标记化,然后移除词条和停用词。然后输入到 tfidf 矢量器,通过逻辑回归和随机森林分类器进行分类。

C.LSTM——一种递归神经网络

由于 LSTM 被设计为记住表达上下文的长期记忆,所以使用定制的标记器来提取字母,因为它们没有词条满足或停用词移除。然后输入被送入一个嵌入层,然后是两个 lstm 层。为了避免过度拟合,通常情况下应用 dropout,然后是完全连接的层,最后是 log softmax。

作为替代,也尝试了斯坦福的 GloVe word embedding,这是一种无监督学习算法,用于获取单词的矢量表示。在这里,采取了预先训练的维基百科和千兆字 6B 令牌,40 万词汇大小和 300 维向量。我们的词汇库中大约 90%的单词是在这个手套词汇库中找到的,其余的是随机初始化的。

D.伯特(还有阿尔伯特作为伯特模型的替代)

我用 pytorch 实现了来自 Huggingface 的变形金刚的 BERT 模型。现在(v3)他们提供了标记器和编码器,可以生成文本 id、填充掩码和段 id,可以直接在他们 BertModel 中使用,标准训练过程不需要定制实现。

类似于 LSTM 模型,来自 BERT 的输出然后被传递到退出的、完全连接的 layters,然后应用 log softmax。如果没有足够的计算资源预算,也没有足够的数据,从头开始训练模型不是一个选项,所以我使用预训练的模型并进行微调。预训练模型使用如下:

  • 伯特:伯特-基地-无壳
  • 艾伯特:艾伯特-基地-v2

预训练的 bert 的训练过程如下所示。

4.估价

首先,输入数据以 8:2 的比例分成训练集和测试集。测试集保持不变,直到所有参数都固定下来,并且每个模型只使用一次。由于数据集不大,交叉验证用于评估参数集。此外,为了克服不平衡和较小数据集的问题,分层 K-Fold 交叉验证被用于超参数调整。

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

交叉验证和测试集分割,按作者分类的图像

由于输入数据不平衡,评估基于 F1 分数,同时也参考了准确性。

网格搜索交叉验证用于模型 A 和 B,而定制交叉验证用于深度神经网络模型 C 和 d

5.结果

在花费或多或少相似的时间进行超参数调优后,基于微调的 BERT 模型明显优于其他模型。

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

每个模型的评估分数,按作者排序的图像

模型 A 表现不佳,因为输入过于简化为音调得分,这是判断情绪的单个值,随机森林模型最终对大多数数据标注了中性的多数类别。简单线性模型仅通过将阈值应用于音调得分而表现得更好,但是在准确性和 f1 得分方面仍然相当低。

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

每个分类器的准确性,按作者分类的图像

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

混淆矩阵,作者图片

我们没有使用欠采样/过采样或 SMOTE 等方法来平衡输入数据,因为它可以纠正这个问题,但会偏离不平衡存在的实际情况。对该模型的潜在改进是,如果为要解决的每个问题建立词典的成本是合理的,则建立定制词典来代替 L-M 词典。更复杂的否定也可以提高预测的准确性。

模型 B 比前一个模型好得多,但是它以几乎 100%的准确度和 f1 分数过度适合训练集,并且未能被通用化。我试图降低模型的复杂性,以避免过度拟合,但它最终在验证集中得分较低。平衡数据可以帮助解决这个问题或收集更多的数据。

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

网格搜索交叉验证结果,按作者排序的图像

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

混淆矩阵,作者图片

模型 C 产生了与先前模型相似的结果,但改进不大。事实上,训练数据的数量不足以从零开始训练神经网络,并且需要多个时期,这往往会过度拟合。预训练的手套嵌入不会改善结果。对后一种模型的一个可能的改进是使用来自类似领域(如 10K、10Q 财务报表)的一串文本来训练手套,而不是使用来自维基百科的预训练模型。

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

混淆矩阵,作者图片

D 型表现相当不错,在交叉验证和最终测试中,准确率和 f1 分均超过 90%。它对负面文本的正确分类率为 84%,而对正面文本的正确分类率为 94%,这可能是由于输入的数量,但最好仔细观察以进一步提高性能。这表明,由于迁移学习和语言模型,预训练模型的微调在这个小数据集上表现良好。

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

最终测试集上的混淆矩阵显示了良好的性能,图片由作者提供

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

训练数据的丢失随着训练的进行而减少,图片由作者提供

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

验证数据的损失并没有像训练数据那样下降

5.结论

这个实验显示了基于 BERT 的模型在我的领域中的应用潜力,在这个领域中,以前的模型未能产生足够的性能。然而,结果是不确定的,并且基于在自由层 GPU 上的相当简单的手动超参数调整,并且它可能根据输入数据和调整方法而不同。

同样值得注意的是,在实际应用中,获得适当的输入数据也是相当重要的。如果没有高质量的数据,就不能很好地训练模型,这通常被称为“垃圾输入,垃圾输出”。

我将在下次讨论这些问题。这里使用的所有代码都可以在 git repo 中获得。

现实世界中的 NLP:Google Duplex

原文:https://towardsdatascience.com/nlp-in-the-real-world-google-duplex-d96160d3770b?source=collection_archive---------36-----------------------

随着 NLP 的所有最新进展,看到该领域的实际应用中的新发展令人耳目一新。

几年前,我看到谷歌展示了他们新的谷歌双工系统。如果你不熟悉 Duplex,它本质上是一个人工智能,可以帮助通过电话完成任务(例如,安排约会,预订预订等。).

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

照片由米切尔罗Unsplash

问题。也许这只是我这一代人,但我宁愿发短信也不愿打电话,因为发短信意味着我可以发短信,然后很快忘掉它。然而,这并不总是可能的,因为许多任务都需要给企业打电话。这就是 Duplex 的用武之地。

想法。Duplex 背后的 overaching 思想是,例如,你只需让谷歌助手在某个餐馆预订,然后输入一些参数,如人数、日期和时间,以及预订名称。一旦你这样做了,Google Assistant 将自动使用 Duplex 给那家餐馆打电话并为你预订,一旦完成就发送一个确认。双工将在模仿人类的同时尝试这样做,以便电话对话是自然的(并且不会惊动另一端的人)。

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

这里有一个系统打电话安排发廊预约的例子。

这是另一个 Duplex 呼叫餐馆的例子。

Duplex 的成功。在这些例子中,我对 Duplex 模仿人类的能力印象深刻。这些声音听起来像真人,Duplex 甚至添加了像“嗯”这样的感叹词,让它听起来更像人(确实没有比说“嗯”更像人的方法了)。双工还可以控制其响应的延迟。例如,如果这个人说“你好?”然后你必须迅速做出反应,但如果他们说了一个很长的句子,你必须花一些时间在模仿思维时间之前做出反应。尽管它很强大,但 Duplex 在这些示例呼叫中仍然会犯一些小错误(例如,当 Duplex 在第一次呼叫中说“上午 10 点至下午 12 点之间的任何时间”时,“下午 12 点”会有不自然和机械的音调变化),但这些甚至不是真正的问题,因为它们足够小,电话另一端的人不会太在意,如果他们甚至注意到它的话。

**需要改进的地方。**尽管 Duplex 可以很好地处理基本任务,但总有一些边缘情况需要进一步调查。双工可能无法轻松处理复杂的语句[ 示例 ]或背景噪音或音质问题。这些案例需要进一步改进。目前,在 Duplex 无法完成其任务的情况下,内置了一个故障保险来移交给人类操作员来完成任务。

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

双工系统的简单示意图。RNN 使用来自电话呼叫的音频、自动语音识别(ASR)软件和对话参数来获得文本响应。该响应通过文本到语音转换程序运行,以获得口头响应。

Duplex 与 NLP 的关系。当然,能够在打电话时模仿人类需要一些疯狂的自然语言处理。首先,需要有一个准确的语音到文本的翻译器,甚至能够理解打电话的人在说什么。接下来,另一个模型必须在电话呼叫目标的上下文中解释这一点。然后必须做出适当的反应。最后,文本到语音转换模型需要将这种响应翻译成类似人类的声音在电话中说出来。这些步骤需要在整个通话过程中实时地不断重复,因此模型需要既准确又快速。

**必要的 NLP 模型。**为了做到这一点,Duplex 使用递归神经网络(RNN)结合谷歌的自动语音识别(ASR)技术、对话参数(例如,所需时间、姓名)和文本到语音(TTS)系统。值得注意的是,这些模型必须在狭窄的领域进行训练。例如,预约和预订需要单独的培训——Duplex 不是作为一般的聊天机器人进行培训的,谷歌希望使用 Duplex 的任何新领域都需要新的培训。

**结论。**该系统已经推出,并将逐步完善。尽管还有待改进,但我仍然对 Google 如何能够结合多种 NLP 模型并创建一个像 Duplex 一样工作的可用应用程序印象深刻。我相信,如果我们能够以这种方式不断进行新的创新,NLP 将有一个光明的未来,因为单个模型很棒,但将多个模型组合起来形成一个发电站系统可能是在现实世界中应用 NLP 的未来所在。

延伸阅读:

股票市场中的自然语言处理

原文:https://towardsdatascience.com/nlp-in-the-stock-market-8760d062eb92?source=collection_archive---------4-----------------------

金融中的人工智能

利用对 10k 填充物的情感分析作为优势

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

图片来源:纽约时报

在交易中实现的机器学习模型通常根据历史股价和其他定量数据进行训练,以预测未来的股价。然而,自然语言处理(NLP)使我们能够分析财务文档,如 10-k 表单,以预测股票走势。10-k 表是由公司提交的年度报告,用于提供其财务业绩的综合摘要(这些报告是由证券交易委员会强制要求的)。对投资者来说,梳理这些报告通常很乏味。通过自然语言处理的一个分支——情感分析,投资者可以快速了解报告的语气是积极的、消极的还是诉讼的等等。以 10-k 形式表达的总体情绪可以用来帮助投资者决定他们是否应该投资该公司。

NLP 解释道

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

图片来源:亚当·盖特基

自然语言处理是人工智能的一个分支,涉及教计算机阅读并从语言中获取意义。由于语言如此复杂,计算机在理解文本之前必须经过一系列的步骤。以下是对典型 NLP 管道中出现的步骤的快速解释。

  1. 句子分割
    文本文档被分割成单独的句子。
  2. 一旦文档被分解成句子,我们进一步将句子分解成单个单词。每个单词称为一个标记,因此得名标记化。
  3. 词性标注
    我们将每个单词及其周围的一些单词输入到预先训练的词性分类模型中,以接收该单词的词性作为输出。
  4. 词汇化
    词语在指代同一个物体/动作时,往往以不同的形式出现。为了防止计算机将一个单词的不同形式视为不同的单词,我们执行了词条化,即将一个单词的各种词形变化组合在一起,作为一个单一项目进行分析的过程,通过该单词的词条(该单词在字典中的出现方式)进行识别。
  5. 停用词
    非常常见的词,如“and”、“the”和“a”不提供任何值,因此我们将它们标识为停用词,以将其排除在对文本执行的任何分析之外。
  6. 依存解析
    给句子分配一个句法结构,并通过将单词输入依存解析器来理解句子中的单词是如何相互关联的。
  7. 名词短语
    把一个句子中的名词短语组合在一起,有助于在我们不关心形容词的情况下简化句子。
  8. 命名实体识别
    命名实体识别模型可以标记对象,例如人名、
    公司名称和地理位置。
  9. 共指消解
    由于 NLP 模型分析单个句子,它们会被其他句子中指代名词的代词所混淆。为了解决这个问题,我们使用了共指消解来跟踪句子中的代词以避免混淆。

关于 NLP 更深入的描述:阅读 这个

完成这些步骤后,我们的文本就可以进行分析了。现在我们更好地理解了 NLP,让我们来看看我的项目代码(来自 Udacity 的 AI for Trading 课程的项目 5)。点击这里查看完整的 Github 库

导入/下载

首先,我们进行必要的进口; project_helper 包含各种实用程序和图形函数。

import nltk
import numpy as np
import pandas as pd
import pickle
import pprint
import project_helper

from tqdm import tqdm

然后,我们下载停用词语料库来移除停用词,并下载 wordnet 语料库来进行词汇化。

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

获得 10 公里

10-k 文档包括公司历史、组织结构、高管薪酬、股权、子公司和经审计的财务报表等信息。为了查找 10-k 文档,我们使用每个公司的唯一 CIK(中央索引键)。

cik_lookup = {
    'AMZN': '0001018724',
    'BMY': '0000014272',   
    'CNP': '0001130310',
    'CVX': '0000093410',
    'FL': '0000850209',
    'FRT': '0000034903',
    'HON': '0000773840'}

现在,我们从 SEC 提取一个已备案的 10-k 列表,并显示 Amazon 数据作为示例。

sec_api = project_helper.SecAPI()from bs4 import BeautifulSoupdef get_sec_data(cik, doc_type, start=0, count=60):
    rss_url = '[https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany'](https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany') \
        '&CIK={}&type={}&start={}&count={}&owner=exclude&output=atom' \
        .format(cik, doc_type, start, count)
    sec_data = sec_api.get(rss_url)
    feed = BeautifulSoup(sec_data.encode('ascii'), 'xml').feed
    entries = [
        (
            entry.content.find('filing-href').getText(),
            entry.content.find('filing-type').getText(),
            entry.content.find('filing-date').getText())
        for entry in feed.find_all('entry', recursive=False)]return entriesexample_ticker = 'AMZN'
sec_data = {}for ticker, cik in cik_lookup.items():
    sec_data[ticker] = get_sec_data(cik, '10-K')pprint.pprint(sec_data[example_ticker][:5])

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

我们收到一个 URL 列表,指向包含与每次填充相关的元数据的文件。元数据与我们无关,所以我们通过用填充 url 替换 url 来提取填充。让我们使用 tqdm 查看下载进度,并看一个示例文档。

raw_fillings_by_ticker = {}for ticker, data in sec_data.items():
    raw_fillings_by_ticker[ticker] = {}
    for index_url, file_type, file_date in tqdm(data, desc='Downloading {} Fillings'.format(ticker), unit='filling'):
        if (file_type == '10-K'):
            file_url = index_url.replace('-index.htm', '.txt').replace('.txtl', '.txt')            

            raw_fillings_by_ticker[ticker][file_date] = sec_api.get(file_url)print('Example Document:\n\n{}...'.format(next(iter(raw_fillings_by_ticker[example_ticker].values()))[:1000]))

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

将下载的文件分解成相关的文件,这些文件被分割成多个部分,每个文件的开头用标签表示,每个文件的结尾用标签表示。

import redef get_documents(text): extracted_docs = []

    doc_start_pattern = re.compile(r'<DOCUMENT>')
    doc_end_pattern = re.compile(r'</DOCUMENT>')   

    doc_start_is = [x.end() for x in      doc_start_pattern.finditer(text)]
    doc_end_is = [x.start() for x in doc_end_pattern.finditer(text)]

    for doc_start_i, doc_end_i in zip(doc_start_is, doc_end_is):
            extracted_docs.append(text[doc_start_i:doc_end_i])

    return extracted_docsfilling_documents_by_ticker = {}for ticker, raw_fillings in raw_fillings_by_ticker.items():
    filling_documents_by_ticker[ticker] = {}
    for file_date, filling in tqdm(raw_fillings.items(), desc='Getting Documents from {} Fillings'.format(ticker), unit='filling'):
        filling_documents_by_ticker[ticker][file_date] = get_documents(filling)print('\n\n'.join([
    'Document {} Filed on {}:\n{}...'.format(doc_i, file_date, doc[:200])
    for file_date, docs in filling_documents_by_ticker[example_ticker].items()
    for doc_i, doc in enumerate(docs)][:3]))

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

定义 get_document_type 函数以返回给定的文档类型。

def get_document_type(doc):

    type_pattern = re.compile(r'<TYPE>[^\n]+')

    doc_type = type_pattern.findall(doc)[0][len('<TYPE>'):] 

    return doc_type.lower()

使用 get_document_type 函数从填充物中过滤出非 10k 文档。

ten_ks_by_ticker = {}for ticker, filling_documents in filling_documents_by_ticker.items():
    ten_ks_by_ticker[ticker] = []
    for file_date, documents in filling_documents.items():
        for document in documents:
            if get_document_type(document) == '10-k':
                ten_ks_by_ticker[ticker].append({
                    'cik': cik_lookup[ticker],
                    'file': document,
                    'file_date': file_date})project_helper.print_ten_k_data(ten_ks_by_ticker[example_ticker][:5], ['cik', 'file', 'file_date'])

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

预处理数据

删除 html 并使所有文本小写,以清理文档文本。

def remove_html_tags(text):
    text = BeautifulSoup(text, 'html.parser').get_text()

    return textdef clean_text(text):
    text = text.lower()
    text = remove_html_tags(text)

    return text

使用 clean_text 函数清理文档。

for ticker, ten_ks in ten_ks_by_ticker.items():
    for ten_k in tqdm(ten_ks, desc='Cleaning {} 10-Ks'.format(ticker), unit='10-K'):
        ten_k['file_clean'] = clean_text(ten_k['file'])project_helper.print_ten_k_data(ten_ks_by_ticker[example_ticker][:5], ['file_clean'])

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

现在我们对所有数据进行假设。

from nltk.stem import WordNetLemmatizer
from nltk.corpus import wordnetdef lemmatize_words(words):

    lemmatized_words = [WordNetLemmatizer().lemmatize(word, 'v') for word in words]

    return lemmatized_wordsword_pattern = re.compile('\w+')for ticker, ten_ks in ten_ks_by_ticker.items():
    for ten_k in tqdm(ten_ks, desc='Lemmatize {} 10-Ks'.format(ticker), unit='10-K'):
        ten_k['file_lemma'] = lemmatize_words(word_pattern.findall(ten_k['file_clean']))project_helper.print_ten_k_data(ten_ks_by_ticker[example_ticker][:5], ['file_lemma'])

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

删除停用词。

from nltk.corpus import stopwordslemma_english_stopwords = lemmatize_words(stopwords.words('english'))for ticker, ten_ks in ten_ks_by_ticker.items():
    for ten_k in tqdm(ten_ks, desc='Remove Stop Words for {} 10-Ks'.format(ticker), unit='10-K'):
        ten_k['file_lemma'] = [word for word in ten_k['file_lemma'] if word not in lemma_english_stopwords]print('Stop Words Removed')

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

10k 上的情感分析

使用 Loughran-McDonald 情感词表对 10-k 进行情感分析(这是专门为与金融相关的文本分析而构建的)。

sentiments = ['negative', 'positive', 'uncertainty', 'litigious', 'constraining', 'interesting']

sentiment_df = pd.read_csv('loughran_mcdonald_master_dic_2018.csv')
sentiment_df.columns = [column.lower() for column in sentiment_df.columns] *# Lowercase the columns for ease of use*

*# Remove unused information*
sentiment_df = sentiment_df[sentiments + ['word']]
sentiment_df[sentiments] = sentiment_df[sentiments].astype(bool)
sentiment_df = sentiment_df[(sentiment_df[sentiments]).any(1)]

*# Apply the same preprocessing to these words as the 10-k words*
sentiment_df['word'] = lemmatize_words(sentiment_df['word'].str.lower())
sentiment_df = sentiment_df.drop_duplicates('word')

sentiment_df.head()

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

使用情感单词列表从 10k 文档生成情感单词包。单词包统计每篇文档中的情感词数量。

from collections import defaultdict, Counter
from sklearn.feature_extraction.text import CountVectorizerdef get_bag_of_words(sentiment_words, docs):

    vec = CountVectorizer(vocabulary=sentiment_words)
    vectors = vec.fit_transform(docs)
    words_list = vec.get_feature_names()
    bag_of_words = np.zeros([len(docs), len(words_list)])

    for i in range(len(docs)):
        bag_of_words[i] = vectors[i].toarray()[0]return bag_of_words.astype(int)sentiment_bow_ten_ks = {}for ticker, ten_ks in ten_ks_by_ticker.items():
    lemma_docs = [' '.join(ten_k['file_lemma']) for ten_k in ten_ks]

    sentiment_bow_ten_ks[ticker] = {
        sentiment: get_bag_of_words(sentiment_df[sentiment_df[sentiment]]['word'], lemma_docs)
        for sentiment in sentiments}project_helper.print_ten_k_data([sentiment_bow_ten_ks[example_ticker]], sentiments)

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

雅克卡相似性

现在我们有了单词包,我们可以将它转换成一个布尔数组,并计算 jaccard 相似度。jaccard 相似性被定义为交集的大小除以两个集合的并集的大小。例如,两个句子之间的 jaccard 相似度是两个句子之间的常用单词数除以两个句子中唯一单词的总数。jaccard 相似性值越接近 1,集合就越相似。为了更容易理解我们的计算,我们绘制了 jaccard 的相似点。

from sklearn.metrics import jaccard_similarity_scoredef get_jaccard_similarity(bag_of_words_matrix):

    jaccard_similarities = []
    bag_of_words_matrix = np.array(bag_of_words_matrix, dtype=bool)

    for i in range(len(bag_of_words_matrix)-1):
            u = bag_of_words_matrix[i]
            v = bag_of_words_matrix[i+1]

    jaccard_similarities.append(jaccard_similarity_score(u,v))    

    return jaccard_similarities# Get dates for the universe
file_dates = {
    ticker: [ten_k['file_date'] for ten_k in ten_ks]
    for ticker, ten_ks in ten_ks_by_ticker.items()}jaccard_similarities = {
    ticker: {
        sentiment_name: get_jaccard_similarity(sentiment_values)
        for sentiment_name, sentiment_values in ten_k_sentiments.items()}
    for ticker, ten_k_sentiments in sentiment_bow_ten_ks.items()}project_helper.plot_similarities(
    [jaccard_similarities[example_ticker][sentiment] for sentiment in sentiments],
    file_dates[example_ticker][1:],
    'Jaccard Similarities for {} Sentiment'.format(example_ticker),
    sentiments)

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

TFIDF

从情感词表中,让我们从 10k 文档中生成情感词频-逆文档频率(TFIDF)。TFIDF 是一种信息检索技术,用于揭示一个单词/术语在所选文本集合中出现的频率。每个术语被分配一个术语频率(TF)和逆文档频率(IDF)分数。这些分数的乘积被称为该项的 TFIDF 权重。较高的 TFIDF 权重表示较罕见的术语,较低的 TFIDF 分数表示较常见的术语。

from sklearn.feature_extraction.text import TfidfVectorizerdef get_tfidf(sentiment_words, docs):

    vec = TfidfVectorizer(vocabulary=sentiment_words)
    tfidf = vec.fit_transform(docs)

    return tfidf.toarray()sentiment_tfidf_ten_ks = {}for ticker, ten_ks in ten_ks_by_ticker.items():
    lemma_docs = [' '.join(ten_k['file_lemma']) for ten_k in ten_ks]

    sentiment_tfidf_ten_ks[ticker] = {
        sentiment: get_tfidf(sentiment_df[sentiment_df[sentiment]]['word'], lemma_docs)
        for sentiment in sentiments}project_helper.print_ten_k_data([sentiment_tfidf_ten_ks[example_ticker]], sentiments)

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

余弦相似性

根据我们的 TFIDF 值,我们可以计算余弦相似度,并绘制出随时间变化的曲线。与 jaccard 相似性类似,余弦相似性是一种用于确定文档相似程度的度量。余弦相似性通过测量在多维空间中投影的两个向量之间的角度余弦来计算相似性,而不考虑大小。对于文本分析,使用的两个向量通常是包含两个文档字数的数组。

from sklearn.metrics.pairwise import cosine_similaritydef get_cosine_similarity(tfidf_matrix):

    cosine_similarities = []    

    for i in range(len(tfidf_matrix)-1):

cosine_similarities.append(cosine_similarity(tfidf_matrix[i].reshape(1, -1),tfidf_matrix[i+1].reshape(1, -1))[0,0])

    return cosine_similaritiescosine_similarities = {
    ticker: {
        sentiment_name: get_cosine_similarity(sentiment_values)
        for sentiment_name, sentiment_values in ten_k_sentiments.items()}
    for ticker, ten_k_sentiments in sentiment_tfidf_ten_ks.items()}project_helper.plot_similarities(
    [cosine_similarities[example_ticker][sentiment] for sentiment in sentiments],
    file_dates[example_ticker][1:],
    'Cosine Similarities for {} Sentiment'.format(example_ticker),
    sentiments)

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

价格数据

现在我们将通过与股票的年度定价进行比较来评估阿尔法因子。我们可以从 QuoteMedia 下载价格数据。

pricing = pd.read_csv('yr-quotemedia.csv', parse_dates=['date'])
pricing = pricing.pivot(index='date', columns='ticker', values='adj_close')

pricing

转换为数据帧

Alphalens 是一个用于 alpha 因子性能分析的 python 库,它使用数据帧,所以我们必须将字典转换成数据帧。

cosine_similarities_df_dict = {'date': [], 'ticker': [], 'sentiment': [], 'value': []}for ticker, ten_k_sentiments in cosine_similarities.items():
    for sentiment_name, sentiment_values in ten_k_sentiments.items():
        for sentiment_values, sentiment_value in enumerate(sentiment_values):
            cosine_similarities_df_dict['ticker'].append(ticker)
            cosine_similarities_df_dict['sentiment'].append(sentiment_name)
            cosine_similarities_df_dict['value'].append(sentiment_value)
            cosine_similarities_df_dict['date'].append(file_dates[ticker][1:][sentiment_values])cosine_similarities_df = pd.DataFrame(cosine_similarities_df_dict)
cosine_similarities_df['date'] = pd.DatetimeIndex(cosine_similarities_df['date']).year
cosine_similarities_df['date'] = pd.to_datetime(cosine_similarities_df['date'], format='%Y')cosine_similarities_df.head()

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

在我们可以使用许多 alphalens 函数之前,我们需要对齐索引并将时间转换为 unix 时间戳。

import alphalens as alfactor_data = {}
skipped_sentiments = []for sentiment in sentiments:
    cs_df = cosine_similarities_df[(cosine_similarities_df['sentiment'] == sentiment)]
    cs_df = cs_df.pivot(index='date', columns='ticker', values='value')

    try:
        data = al.utils.get_clean_factor_and_forward_returns(cs_df.stack(), pricing.loc[cs_df.index], quantiles=5, bins=None, periods=[1])
        factor_data[sentiment] = data
    except:
        skipped_sentiments.append(sentiment)if skipped_sentiments:
    print('\nSkipped the following sentiments:\n{}'.format('\n'.join(skipped_sentiments)))
factor_data[sentiments[0]].head()

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

为了与 alphalen 的 factor _ rank _ 自相关和 mean_return_by_quantile 函数兼容,我们还必须使用 unix time 创建因子数据帧。

unixt_factor_data = {
    factor: data.set_index(pd.MultiIndex.from_tuples(
        [(x.timestamp(), y) for x, y in data.index.values],
        names=['date', 'asset']))
    for factor, data in factor_data.items()}

因子回报

让我们来看看一段时间内的因子回报

ls_factor_returns = pd.DataFrame()for factor_name, data in factor_data.items():
    ls_factor_returns[factor_name] = al.performance.factor_returns(data).iloc[:, 0](1 + ls_factor_returns).cumprod().plot()

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

正如预期的那样,表达积极情绪的 10-k 报告产生了最多的收益,而包含消极情绪的 10-k 报告导致了最多的损失。

营业额分析

使用因子等级自相关,我们可以分析阿尔法随着时间的推移有多稳定。我们希望阿尔法等级在不同时期保持相对一致。

ls_FRA = pd.DataFrame()for factor, data in unixt_factor_data.items():
    ls_FRA[factor] = al.performance.factor_rank_autocorrelation(data)ls_FRA.plot(title="Factor Rank Autocorrelation")

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

夏普比率

最后,让我们来计算夏普比率,即平均回报减去无风险回报除以投资回报的标准差。

daily_annualization_factor = np.sqrt(252) (daily_annualization_factor * ls_factor_returns.mean() / ls_factor_returns.std()).round(2)

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

夏普比率 1 被认为是可接受的,比率 2 是非常好的,比率 3 是极好的。正如所料,我们可以看到积极情绪与高夏普比率相关,而消极情绪与低夏普比率相关。其他情绪也与高夏普比率相关。然而,由于影响股票价格的复杂因素如此之多,要在现实世界中复制这些回报要困难得多。

参考

[1] Udacity,用于交易的人工智能,Github

先别走

我是 Roshan,16 岁,对人工智能和金融的交叉领域充满热情。关于人工智能在金融领域的广泛观点,请查看这篇文章:https://towards data science . com/artificial-intelligence-and-its-application-in-finance-9f1e 0588 e 777

在 Linkedin 上联系我:https://www.linkedin.com/in/roshan-adusumilli-96b104194/

ESG-BERT: NLP 符合可持续投资

原文:https://towardsdatascience.com/nlp-meets-sustainable-investing-d0542b3c264b?source=collection_archive---------11-----------------------

Parabole ,我在 Charan Pothireddi 的指导下工作,探索自然语言处理如何应用于可持续性分析。这篇文章概述了我们的发现。

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

照片由 Nathália RosaUnsplash 拍摄

可持续投资是一种不断发展的投资策略,旨在寻求丰厚的财务回报,同时让世界变得更加美好。然而,对于许多投资者来说,这往往是一种具有挑战性的投资策略。幸运的是,自然语言处理可以帮上忙,下面是方法—

什么是可持续投资?

可持续、负责任和有影响力的投资(SRI)是一种投资原则,它考虑了环境、社会和公司治理(ESG)标准,以产生长期有竞争力的财务回报和积极的社会影响。

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

作者图片

可持续投资有几个动机,包括个人价值观和目标、机构使命以及客户、委托人或计划参与者的需求。

可持续投资者的目标是强劲的财务表现,但也相信这些投资应该用于促进社会、环境和治理实践。[6]

他们可能会积极寻找可能提供重要社会或环境效益的投资,如社区发展贷款基金或清洁技术投资组合。

一些投资者采用可持续投资策略来管理风险和履行受托责任;他们审查环境、社会和公司治理标准,以评估其投资组合公司的管理质量和应对未来挑战的可能弹性。一些人寻求长期的财务优势;越来越多的学术研究表明,ESG 和财务表现之间有着密切的联系。[1]

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

全球可持续投资的增长(万亿美元)—作者图片

以可持续为卖点的投资——这意味着它们专注于将环境和社会公司治理实践纳入长期公司战略的公司——正在经历爆炸式增长。

尽管可持续投资出现于 20 世纪 70 年代,但这一运动在过去几年获得了令人瞩目的发展。

自 2012 年以来,可持续投资的总资产增加了一倍多。[2]

随着可持续投资成为主流,它不会简单地作为一个更广泛的战略中的利基——相反,它将自然地融入整个投资组合。

“随着可持续性对投资回报的影响越来越大,我们相信可持续投资是客户投资组合未来最坚实的基础。”

—贝莱德董事长兼首席执行官拉里·芬克

可持续发展是一种全球性的力量,将继续成为日常决策的考虑因素。

可持续投资——挑战

目前围绕可持续发展的数据过于依赖企业自愿披露,如机构投资者汇总的年度可持续发展报告和公司问卷——其中许多人会提出不同的问题。[3]

“个人投资者很难以容易获得的方式获得这种类型的信息,并为投资决策提供信息”

——琼·罗杰斯,首席执行官,非营利组织可持续发展会计标准委员会的创始人

对于投资者来说,仅仅依靠年度可持续发展报告等做出可持续的投资选择是具有挑战性的。它们通常长达数百页,并占用大量人力资源进行分析。随着时间的推移,随着可持续资产数量的增加,这个问题会变得更加复杂。这些报告也从来不是完全透明的。公司可能会选择在年度报告中略去某些内容。

年度可持续发展报告等也非常静态。它们并不实时反映公司的变化,它们只反映一段时间内变化的累积。这种方法错过了所有实时发生的变化,这些变化可能会反映在新闻报道中。

一种更具活力的可持续投资方法将考虑实时变化,同时降低分析年度可持续性报告的复杂性。这将使可持续投资更具可扩展性,提高效率,同时减少人为失误。

NLP 如何提供帮助

自然语言处理可用于分析可持续发展报告和新闻文章,从中提取重要的 ESG 中心见解。这降低了手动分析报告的复杂性,同时还通过查看新闻文章中的实时变化使该方法更加动态。

让我们来看一个例子:

我们自豪地为苹果设施实现了 100%的可再生电力,并为苹果的企业排放实现了碳中和,包括商务旅行和员工通勤。我们正在着手一个新的目标,即到 2030 年实现我们整个碳足迹的碳中和。

——摘自 2020 年苹果可持续发展报告[4]

NLP 模型可以对报告执行下游 NLP 任务,如文本分类情感分析,而不是手动读取和分析报告,从而降低分析报告的复杂性,并使整个过程更加节省时间和资源。在这种情况下,NLP 模型会将摘录分类为与“气候变化”相关,情绪值为“积极”。

NLP 使投资者能够对报告和文章进行更好和更有效的分析,从而做出更明智的可持续投资决策。

在我在parabole . ai的实习中,我能够通过进一步预先训练谷歌的“BERT”语言模型,在大型非结构化可持续发展文本语料库上开发 ESG-BERT。

我曾尝试使用’ sci-kit learn 模型和’计数矢量器’来解决这个问题。鉴于这一领域的性质及其独特的词汇,传统的 ML 模型并没有产生令人满意的结果。另一方面,深度学习模型需要大量的结构化文本数据,这正是我们在这种情况下所缺乏的。有大量的非结构化文本数据,但是结构化数据却很少。

在尝试了这些方法之后,我转向了谷歌的 BERT,它是在大型非结构化文本语料库上预先训练的,因此对于下游的 NLP 任务,例如文本分类,需要更少的结构化数据。这似乎非常符合我们的情况。[5]

**BERT(来自变形金刚的双向编码器表示)**是 Google 开发的一项技术,用于自然语言处理模型的预训练。官方的 BERT repo 包含不同的预训练模型,可以在下游 NLP 任务上进行训练,增加了一个输出层。然而,这些模型是在通用英语文本语料库上预先训练的,并且它们不能够理解特定领域的词汇。[5]

可持续投资作为一个领域有一个独特的词汇,ESG-BERT 能够理解。 ESG-BERT 在非结构化文本数据上进一步训练,准确率为 100%98% ,用于下一句预测和屏蔽语言建模任务。针对文本分类的微调 ESG-BERT 产生了 0.90 的 F-1 分数。作为对比,一般的 BERT (BERT-base)模型在微调后得分 0.79 ,sci-kit learn 方法得分 0.67

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

作者图片

ESG-BERT 的应用可以扩展到不仅仅是文本分类。它可以进行微调,以执行可持续投资领域中的各种其他下游 NLP 任务。

如何使用 ESG-BERT?

预先训练好的特定领域 ESG-BERT 模型可以从 GitHub 资源库这里下载。可以对其进行微调,以执行下游的 NLP 任务,如情感分析等。

ESG-BERT 也进行了微调,以对可持续投资文本数据进行文本分类。如 GitHub repo 的 readme 部分所述,可以下载并提供微调后的模型。

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

照片由诺亚·布舍尔Unsplash 上拍摄

结论

这是可持续投资文本挖掘的重要一步。

ESG-BERT 可用于让投资者更容易获得可持续投资。它通过弥合复杂的可持续性数据和投资者之间的差距,使可持续性作为一个目标更容易实现。然而,它的影响不仅仅局限于文本挖掘。可持续发展报告通常长达数百页,充斥着大多数人无法理解的 ESG 术语。这个工具使这些可持续发展报告更具可读性,每个人都可以访问,因此增加了可持续投资的影响。这让我们离更绿色、更安全、更可持续的未来更近了一步。

在不久的将来,我将发布教程,介绍我如何进一步预训练 BERT 以创建 ESG-BERT,我如何使用 PyTorch 微调 BERT,并讨论其他使用“计数-矢量器”和“单词袋”模型的 NLP 方法。

参考

[1] — 可持续投资基础知识,美国 SIF:可持续和负责任投资论坛

[2] — I 曼·戈什,可视化可持续投资的全球崛起(2020) ,视觉资本家

[3] — Alex Davidson,《可持续投资指南》(2015) ,华尔街日报

[4] — 环境进度报告(2020) ,苹果

[5] —Jacob Devlin 和 Chang Ming-Wei,开源 BERT:自然语言处理的最先进预培训 (2018),谷歌人工智能博客

[6] —凯伦·华莱士,对可持续投资感兴趣?以下是你需要了解的关于可持续基金的信息

随时在 LinkedIn 上联系我,给我发消息 这里

Office 系列中的 NLP

原文:https://towardsdatascience.com/nlp-on-the-office-series-cf0ed44430d1?source=collection_archive---------38-----------------------

利用文本挖掘技术,如标记化、tf-idf 和情感分析来分析电视连续剧的脚本。

负责💬👂:为了让你相信我的分析和发现是有效的,你需要对这个节目“相当”熟悉。否则,您可能无法认识到文本挖掘和 NLP 提供了多么强大的工具来进行具有惊人发现的研究项目。

这篇博客有两个主要目标

  1. 介绍文本分析方法,如标记化、n-grams、tf-idf、不同的情感提取和评分工具,并涵盖可用于查找主题/“聚类”文本的 LDA 算法的基础知识。
  2. 当粉丝阅读他们最喜欢的节目时,给他们一种怀旧的感觉,看看数据科学如何支持信念和假设,例如安吉拉总体上是一个卑鄙的人,但对德怀特最好,或者如何从文本中轻松提取个人词汇,例如安迪著名的(大)金枪鱼,或瑞安的 WUPHF,并用于识别人。

我将在整个过程中使用 R,并将利用以下库:

  • tidyversedata.table 进行数据操作
  • 用于 NLP 相关工作的 tidytext、stringr、textdata、textstem、stopwords、sentimentr 和 topicmodels
  • 用于可视化的 ggplot、igraphvisnetwork

我不会做任何文本分析的深度学习,所以请不要期待任何嵌入层知识或 RNN / LSTM 网络。

在进行任何分析之前,我需要从互联网上获取所有的脚本(所有季节的所有剧集),为此我使用了网络抓取。要找到刮刀,请访问以下链接到我的 GitHub。

[## kristofrabay/web_scraping

网页抓取任务。在 GitHub 上创建一个帐户,为 kristofrabay/web_scraping 开发做贡献。

github.com](https://github.com/kristofrabay/web_scraping/blob/master/the_office/office_transcripts_scraper.R)

我还预先在 GitHub 上粘贴了实际分析代码的链接。分析的原始输出是一个闪亮的仪表板应用程序,所以代码可以在应用程序中找到。r 文件。我会在博客中嵌入 Github Gists,但是如果有人对我的部分分析特别感兴趣,可以在脚本的服务器端找到剧情和网络渲染。(对于不熟悉 Shiny 的,抱歉,有问题联系我,或者拉我回购,运行 app。享受我创建的漂亮的仪表板吧!)

[## kristofrabay/办公室-NLP

对 Office 系列进行 NLP(符号化、tf-idf、情感、主题建模)。在闪亮的应用程序中输出…

github.com](https://github.com/kristofrabay/The-Office-NLP/blob/master/app.R)

我想提到几个人,他们给了我与世界分享我的分析的想法:(1)爱德华多·阿里诺,他是硅谷的首席数据科学家,并教会了我 NLP(https://www.linkedin.com/in/earino)和(2)米哈里·奥索斯,他举办了 R 课程并展示了快速有效的网络抓取——这是一项非常非常有用的技能(https://github.com/misrori)。这两门课程都是匈牙利布达佩斯 CEU 大学商业分析硕士项目的选修课程(https://economics . ceu . edu/program/Master-science-Business-Analytics)。

我们开始吧!

资料来源:giphy

我把整篇博文分成 4 块。这是“教学大纲”:

  1. 按季节统计字数,选择前 x 名合作者(有限的数据用于更好的可视化,更容易理解的结果)
  2. 分析最常用的单词、短语(二元模型)和最个性化的单词(tf-idf)
  3. 运行有无季节性的情绪分析,确定“谁对谁好,谁对谁坏,坏到什么程度”
  4. 展示 LDA 的能力——注意:在此用例中没有用,使用新闻文章或博客的主题建模(其中可指定的主题数量确实有限)

#1.熟悉数据,选择要处理的子数据

如上所述,我将和有限的几个人一起工作来降低我的视觉效果的复杂性。我会选出整个系列中台词最多的前 12 个人。他们覆盖了几乎 80%的台词,所以我觉得这是一个很好的比例。此外,当比较 12 个人时,我可以使用 3×4 的 ggplot 网格结构,使我的图形对称。

让我们看看这些人总共有多少条线,按季节划分。

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

前 12 个字符的总行数

很明显迈克尔有最多的台词,即使没有参加最后两季。如果我们按季节检查线路数,我们就能确切地看到谁在迈克尔离开后得到了大多数线路。

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

按季节的行数

ggplot 提供了一个很好的方法来自动排序每个类别/方面中的条形/柱形(在本例中是季节)。我提供了一个示例代码供您查看:

数据帧有 3 列使用:(1)姓名,表示谁说的线,(2)实际的线,和(3)给定季节的#号。我们在这里所做的是按季节和名称对数据进行分组,汇总计数,按季节选择前 12 个结果(人),然后在季节内重新排列条形(指示名称)。这个简单的 reorder_within 给了我们非常漂亮、平滑的图表。

现在我们知道了我们将和哪些人一起工作,我们可以开始做一些文本分析了。

#2:文本分析(标记化、二元模型、tf-idf)

A.标记化—检查最常用的单词

作为第一步,让我们看看人们最常用的词是什么。TidyText 提供了一个非常容易使用的函数,名为 unnest_tokens ,可以在任何字符串向量上运行,从其中取出每个字符串(单词)并将其存储在数据帧的“列”中。如果我们的字符串是**‘我的名字是约翰’,那么它会把这个句子 unnest 成一个 4×1 的字符串向量,元素有[‘我的’,‘名字’,‘是’,‘约翰’]**。

第二步,让我们意识到,在前面的字符串向量示例中,4 个单词中只有 2 个有意义/兴趣:“name”和“John”。“My”和“is”被视为停用词。这些是没有意义的常用词,在没有进一步信息的情况下增强文本。幸运的是,包含任何给定语言的停用词的词典是公开可用的,我们只需要将它们加载到我们的脚本中。英语的例子有‘onix’,‘snowball’,‘SMART’,但是有你想要的任何语言。

为了去掉新创建的字符串列中的停用词,其中每个单元格包含一个单词,我们需要做的是与 join 方法相反的事情。通过一个简单的连接(假设是左连接),我们可以匹配左侧可以找到的所有内容。然而,在这里,如果一个停用词出现在左列,我们希望删除该观察/行。这就是为什么 dplyr 库提供 R 用户 anti_join 的原因,这就是为什么。在对先前的字符串向量应用了停用词的 anti_join 之后,我们从 [‘My ‘,’ name ‘,’ is ‘,’ John’][‘name ‘,’ John’]

在结果出来之前,这里是代码,使用 2 个不同的停用词词典和一个手动创建的字符串向量,这些字符串在文本中很常见,但没有“意义”。这里,我们取消了包含人的行的“文本”列,以创建一个名为“单词”的新列。然后我们删除带有停用词的行。

这是实际输出。

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

人们最常说的话

这里有些有趣的东西。对于每个人来说,包含他们的热门单词的列表包含其他人的名字。这是合乎逻辑的,因为这是一个基于对话的系列,当不考虑停用词时,最常见的标记是当人们转向他们的同事时,称呼他们的名字。有趣的是,有些人,像德怀特和帕姆,他们的名字在最上面。这是因为他们必须通过电话介绍自己:德怀特是推销员,帕姆是办公室接待员。

这真的给了我一个想法:让我们看看人们通常和谁交谈。为此,我将指示谁说了那句台词的列移动一行,然后我将看到谁说了那句台词以及这句台词是对谁说的,最后我将删除由同一个人说的和对同一个人说的台词(多行,或关闭和打开一个场景)。

不幸的是,我不能在这里粘贴一个交互式的 visNetwork 对象,但这里有一个截图。

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

它试图表明大多数谈话发生在 Michael、Dwight、Jim 和 Pam 之间,同时我还选择了 Dwight 来展示如何按人检查结果。我将边缘的宽度设置为与两个字符之间的行数一致,并将节点的大小设置为代表总行数。visNetwork 是一个很棒的工具,非常容易使用!

B.二元模型(n grams)——频繁短语分析

我们现在知道热门词汇包括其他人的名字,偶尔还有一些暗示一个人身份的符号(例如:达里尔说的“迈克”,安迪说的“金枪鱼”,菲利斯说的“万斯”)。让我们开始寻找人们最常用的短语。

短语是指你有不止一个单词。你可以分析任意数量的互相跟随的单词——这就是为什么这种方法被称为 ngrams analytics。如果 n 恰好是 2,那么我们称它们为二元模型。这就是我们现在要做的。

下面是如何做到这一点的代码。

我们可以利用相同的 unnest_tokens 函数,但是这次我们将调用输出列“bigram”,我们将使用“text”列来创建输出,我们将 token 参数设置为“ngrams”而不是“words”(默认设置),并将“n”参数设置为 2,表示我们想要创建 bigram。

坚持**'我的名字是约翰’的例子,而 tokenization (token = 'words ')创建了这个向量: [‘我的’,‘名字’,‘是’,‘约翰’] ,现在,bigram 方法产生了下面这个向量: [‘我的名字’,‘名字是’,‘是约翰’] 。然后我们通过获取二元模型的第一个和第二个令牌,将这个新列分成两部分。下一步,我们只对两个单词都不是停用词的二元模型感兴趣,因此两个单词都包含某种信息,所以我们从第一个和第二个单词向量中过滤出停用词。最后一步,我们用一个空间把两个柱子粘在一起。我们完成了,我们已经“大化”了文本数据。以下是按人分类的结果:**

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

顶级人物使用的顶级二元模型(短语)

这下可好了!如果你熟悉这个节目,你会清楚地看到双面人物非常有能力识别人。除了安迪,还有谁会用‘纳德狗’和‘西兰花劫’呢?除了瑞安,还有谁会谈论“商学院”和“米夫林无限”?德怀特显然喜欢“区域经理”和“区域助理”这两个词。如果我们使用三元组(3 个单词组成一个短语),我们肯定会在德怀特的列表中看到“助理区域经理”。

二元模型比简单的记号更能识别某个人。然而,有一种方法甚至比 ngrams 更值得信赖。它叫做 tf-idf ,是下一个话题的主题。

C.tf-idf —按个人查找最个性化/独特的词语

我暗示了这个算法的能力,但是让我快速解释一下它是如何做到的。tf-idf 的 tf 部分代表词频,idf 表示逆文档频率。

第一部分很简单:它获取单词,按照文档中单词的绝对数量对它们进行排序(例如,Michael 在这里是一个“文档”,至少他的词汇表是——TF 找到 Michael 的热门单词)。基本的标记化和计数聚合。

IDF 是奇迹发生的地方。IDF 进行检查,考虑某些单词排名的所有文档(在这种情况下是人的词汇表)。它决定了,如果一个词出现在大多数文档中,那么它是常见的,如果它只出现在某些文档中,那么它是罕见的。

然后,tf-idf 将某个文档内的总频率与逆文档频率进行比较(相乘),并确定任何单词的对于某个文档是否是唯一的。例如,迈克尔可能说过很多次狂犬病,但是其他人并没有真正提到这个词。因此,tf-idf 算法将确定狂犬病在迈克尔的词汇中是一个独特的词。

理解了算法的基础之后,让我们将它应用到数据中,看看它发现了什么。首先是代码,然后是解释。

这里面好像有很多东西,但是很简单。

  1. 我将字符串数据转换成记号——简单的单词
  2. 这次我应用术语化。这是一个尝试将单词恢复到“正常”、“词根”形式的过程。它可以采用以下单词:[’ studing ‘,’ studies ‘,’ studied ‘,’ study’] 并且在词汇化之后,所有单词都将是 [‘study’] ,因为所有单词都源自这个单词。通过这种方式,我删除了单词的“结构”信息,但获得了关于哪个“词根”单词最常用的信息。
  3. 然后我按人对单词进行计数,这是 tf-idf 确定一个单词的 idf 所必需的输入( count 函数是一个 group_by 函数,然后是一个 summary(count)函数)
  4. 然后是最重要的部分:tidytext 的 bind_tf_idf 函数。没有比这更简单的了。它接受文档(人名)列、标记列(词汇化和取消嵌套后的单词)和计数列(单词在给定文档中出现的次数)并运行 tf-idf 数学公式。
  5. 最后一步,由于关系,我决定只可视化每个人的前 8 个独特的单词。

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

tf-idf 前 12 名

让我们来看看。已经识别出最独特的单词。真的,所有人看 tf-idf 的字就能被一个真正的办公室粉丝识别出来。

  • 安迪:金枪鱼,纳德,高音,杰西卡
  • 安琪拉:糖粉,小鼓手男孩送的帕伦彭
  • 达里尔:杰斯丁的豆豆,迈克
  • 德怀特:副警长,摩西,警长,农场
  • 等等…

如此简单的工具却能产生如此巨大的效果。

熟悉词汇之后,让我们开始关注人们通常将 NLP 与联系在一起的“另一件大事”:情感分析。

#3.情感分析

对文本数据进行情感分析/情感评分有多种方式。一些可能的方法是

  • 按词分类的情感(即积极/消极类,如 Bing 词典,或情绪类,如愤怒、喜悦、信任、NRC 词典中的预期)
  • 情绪的数字评分(AFINN 词典:美丽、令人惊叹+3、困扰、不便-2)
  • 在 ngram /句子级别上运行情感评分—算法确定句子的总体情感,介于-1 和 1 之间,其中-1 表示全部负面,+1 表示全部正面,0 表示中性/不可分类。

在上述方法中,我将利用 3:

  1. 我将标记分为正面和负面,按人计数,并为每个人创建一个列表,列出他们最常用的正面和负面词汇。
  2. 我将应用 AFINN 词典,对每个单词进行评分,然后用该单词的频率乘以情绪得分,并创建一个单词列表,这些单词对人们的词汇产生了最积极或消极的影响。
  3. 我将按字符级别对说出的句子/句子进行情感评分,并通过 AFINN 分数将结果与令牌级别的情感聚合进行比较。

A.使用类别分类运行情感分析

这只是一个简单的介绍步骤。我把所有的单词,分成正反两类,统计每个单词,按人确定最常用的正反两类单词。这真的只是一个热身运动。

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

按字符排列的最常用的正面和负面单词

我们在这里看到的是,比较正面和负面词汇的数量,大多数人使用正面词汇的频率高于负面词汇。安吉拉可能是个例外,她最积极的词(好)和最不积极的词(坏)出现的次数几乎一样多(约 22 次)。这本身并不能真正代表个性。为此,我将使用 AFINN 词典。

B.使用 AFINN 的数字情感评分

这一次,我不是简单地将单词分类,而是给每个单词分配代表积极和消极的数字。然后,我会将情感分数乘以单词数,得出一个“贡献”因子:一个单词对一个人的词汇有多少积极或消极的贡献。例如,‘恶心这个词的分数是-3,而‘漂亮这个词的分数是+1。这意味着需要 3 个漂亮的来抵消 1 个恶心的

在显示结果之前,让我向您展示这样做的代码。

事情是这样的:

  1. 我们由人们将台词分解成单词(保留关于谁说了台词的信息)
  2. 我们应用词汇化使单词回到它们的词根形式(“学习”→“研究”)
  3. 我们从 2 个词典、一个手动创建的列表和人名列表中删除了停用词(我们不能对姓名进行情感评分,它们将被归类为中性,并影响平均值和中值统计数据)
  4. 我们应用“计数”函数,该函数首先按“名称”对数据进行分组,然后按计数聚合对数据进行“汇总”。我们现在有 3 列:(1)名称,(2)单词和(3)计数。
  5. 我们通过“单词”列将 AFINN 词典加入到我们的数据中。现在每个单词的分数都在-3 到+3 之间(我们使用内部连接,所以最后只保留匹配)
  6. 最后一步,我们将“分数”乘以“计数”来获得贡献因子

让我们检查一下视觉效果。

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

人们贡献的情感

现在我们需要寻找的是红色条最长的地方,以找到对他们的谈话贡献最消极的人。安吉拉、达里尔和德怀特似乎是红色条的平均长度接近绿色(正)条的人。

C.人与人之间的感情

我意识到我应该对情感做更多的事情。其中之一是检查谁对谁最好,谁对谁最刻薄。为此,我将使用与我的“对话网络”相似的方法。我将确定谁对谁说了这句话,通过“ from — to ”列进行情感评分,并可视化我的结果!

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

人与人之间的情感(条形图)

就拿安吉拉来说吧:她对德怀特最好,对奥斯卡最差。条形图很容易理解,但是网络会让它看起来更好。同样,我不能在这里粘贴一个 HTML 元素,所以请查看另外两个交互式网络的截图。

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

情感网络

多好啊!我还能做一件事让它看起来更好。我不一定要把所有的关系形象化,而是把重点放在最积极和最消极的关系上。但是,如何决定哪些“边缘”保留给网络呢?让我们按人对分数进行分布分析,去掉平均值/中值,只显示“极端”关系。

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

分数分布

下面是上图的意思。以吉姆为例——他对其他人的情感评分在 15 到 100 之间,有很高的极端值。大多数分数都在-10 到+30 之间,所以我会把所有的分数都保持在这个范围之外,以应对极端情况。包含大多数“极端”关系的 visNetwork 如下:

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

具有表示积极/消极度量的边的情感网络

我们可以看到(再次抱歉,这是一个互动网络)吉姆和帕姆之间的绿色边缘最宽,这意味着他们之间的情绪是所有人中最积极的。奥斯卡和安吉拉互相相当消极对方采取另一个例子,从网络。

D.季节性情绪趋势

作为情绪分析的最后一步,让我快速运行所有人的逐集和逐季情绪趋势,看看我们是否能跟踪他们的快乐/悲伤。

首先,剧集层面的概述。

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

上面的图表没有提供任何信息。每集的平均情绪(通过使用senmentr包的情绪 _by 函数计算,其中每行的情绪得分在-1 和 1 之间)波动太大,无法提供任何见解。让我们检查季节性数据。

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

这在某种程度上更容易解释,但无法提取出明确的趋势。也许安迪的解雇是有暗示的,但季节平均情绪得分和人的快乐/悲伤之间没有明确的关系。

除此之外,我们还可以看到办公室的两个主要竞争对手吉姆和德怀特之间的平均情绪是如何随着时间的推移而发展的。

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

我对此运行了两个算法,首先在标记级别使用 AFINN,然后在行/句子级别使用 sentimentR,结果非常相似。

两人都暗示这对情侣的关系在接近尾声时变得更好,这与故事相符,因为竞争停止了,友谊开始了。这是有争议的,情感趋势很难在这里建模。

#4.LDA 快速介绍

在结束之前,让我向您展示另一个典型的 NLP 工作:使用 LDA(潜在 Dirichlet 分配)运行主题建模。对我来说,这非常类似于一种无监督的机器学习算法,k-means 聚类完成它的工作。它在方法论上有很大的不同,但结果是相似的:最后,聚类找到多少“属于”在一起的数据点,形成“相似但未标记的组”。LDA 的输出是一个单词列表,这些单词“属于一起”,“组成一个未标记的主题”。

我不会详细说明算法实际上是如何运行的,但我会给你一个例子。我们一直在和前 12 名合作(按行数)。在这种情况下,我们可以使用 LDA 来寻找词汇相似的人。也就是让(force) LDA 创建 12 个聚类/主题,并吐出某个人是给定主题的一部分的概率。

我们就这么做吧。

下面是上面 3 行代码中发生的情况

  1. 输入数据具有 3 列:(1)说出该行的人的姓名(通常称为文档),(2)单词列(在标记化之后)以及(3)指示该单词在给定文档中出现了多少次的列
  2. 我们需要为运行 LDA 创建一个文档术语矩阵
  3. 我们将集群/主题的数量设置为 12,并设置一个随机状态以使我们的工作可重复
  4. 一旦 LDA 算法完成,我们可以提取两个概率:(1)beta——一个单词成为主题的一部分的概率,以及(2) gammas —一个主题成为文档的一部分的概率:这里我们提取 gammas ,因为我们想要一个主题(词汇)成为文档(人)的一部分的概率

这是我们可视化后得到的结果

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

12 个人的 LDA 词汇

大多数人都有自己使用的特定词汇,然而德怀特和迈克尔似乎都根据他们选择的词汇组成了 2-2 个词汇。Oscar 和 Angela 共享一个群集,这意味着他们有相似的发音词汇(他们都是会计师),有趣的是,虽然 Jim 和 Pam 有各自的话题,但他们共享一个话题(话题# 8 ),可能是他们的个人(办公室之外)生活,如他们的家庭、女儿、婚礼策划等…

这远非完美,LDA 不保证实际的主题,如“金融”或“IT”,这些主题需要由分析师在一些创造性的,但可能是主观的思考后命名。

收尾工作

在这篇博文中,我提到了以下 NLP 主题:

  1. 标记化、双分枝化和 tf-idf 来从文本数据中提取单词、短语和唯一标记
  2. 使用分类和数字结果的情感分析,如何使用它们来显示对文本的情感贡献
  3. 最小 LDA 将发音相似的人“聚集”在一起,或者至少提取一个人与另一个人分享话题的可能性

我在 Office 脚本中展示了所有上述 NLP 方法,作为一个非常熟悉该节目的人,我可以诚实地说,这些方法中的一些,虽然很容易使用,但会产生令人敬畏的结果。关于 tf-idf 和 ngrams,毫无疑问它们能够对任何文本数据创造奇迹。甚至情感评分似乎也站得住脚,尽管句子级别的聚合和趋势分析很困难,但标记级别的比较是有希望的。关于 LDA,让我们只是“小心行事”。

总的来说,这是我尝试这些方法的一个很好的方式,看看它们如何处理“实时”数据。如果对我的代码有疑问,请访问我的 GitHub 页面(https://github.com/kristofrabay)并在那里联系我。

资料来源:giphy

NLP:使用 TensorFlow2 为深度学习模型准备文本

原文:https://towardsdatascience.com/nlp-preparing-text-for-deep-learning-model-using-tensorflow2-461428138657?source=collection_archive---------8-----------------------

TensorFlow2 中的文本预处理(标记化、排序、填充)是如何工作的。

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

作者图片

自然语言处理(NLP)通常用于文本分类任务,例如垃圾邮件检测和情感分析、文本生成、语言翻译和文档分类。文本数据可以被认为是字符序列、单词序列或句子序列。最常见的是,对于大多数问题,文本数据被认为是单词序列。在本文中,我们将深入研究使用简单示例文本数据的预处理。然而,这里讨论的步骤适用于任何 NLP 任务。特别是,我们将使用 TensorFlow2 Keras 进行文本预处理,包括:

  • 标记化
  • 定序
  • 填料

下图描述了文本预处理过程以及示例输出。

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

从原始句子开始到填充序列的逐步文本预处理示例

首先,让我们导入所需的库。(我的 GitHub 页面里有完整的 Jupyter 笔记本)。

import tensorflow as tf
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences

Tokenizer 是 TensorFlow Keras 中提供的一个 API,用来对句子进行分词。我们将文本数据定义为句子(每个句子用逗号分隔)和一组字符串。共有 4 个句子,包括 1 个最大长度为 5 的句子。我们的文本数据还包括标点符号,如下所示。

sentences = ["I want to go out.",
             " I like to play.",
             " No eating - ",
             "No play!",
            ]
sentences['I want to go out.', ' I like to play.', ' No eating - ', 'No play!']

标记化

由于深度学习模型不理解文本,我们需要将文本转换成数字表示。为此,第一步是标记化。TensorFlow Keras 的 Tokenizer API 将句子拆分成单词,并将这些单词编码成整数。以下是在记号赋予器 API 中使用的超参数:

  • num_words:限制训练时保留的最常用单词的最大数量。
  • 过滤器:如果没有提供,默认情况下过滤掉所有标点符号(!idspnonenote)。"#$%&()*+,-./:;<=>?@[]^_'{|}~\t\n).
  • 下限=1。这是默认设置,将所有单词转换为小写
  • oov_tok:当它被使用时,词汇外标记将被添加到用于建立模型的语料库中的单词索引中。这用于在 text_to_sequence 调用期间替换词汇表外的单词(不在我们的语料库中的单词)(见下文)。
  • word_index:将所有单词转换为整数索引。单词完整列表可用作键值属性:key =单词,value =单词的令牌

让我们使用分词器并打印出单词索引。我们已经使用了 num_words= 100,这对于这个数据来说是很多的,因为只有 9 个不同的单词和用于词汇外令牌的< OOV >字符串。

tokenizer = Tokenizer(num_words=100, lower= 1, oov_token="<OOV>")
tokenizer.fit_on_texts(sentences)
word_index = tokenizer.word_indexprint(word_index)
{'<OOV>': 1, 'i': 2, 'to': 3, 'play': 4, 'no': 5, 'want': 6, 'go': 7, 'out': 8, 'like': 9, 'eating': 10}

如上所述,我们句子中的每个单词都被转换成了数字符号。例如,“I”的值为 2。分词器也忽略了单词后面的感叹号。例如,单词“play”或“play!”只有一个标记即 4。

排序

接下来,让我们使用来自 tokenizer 对象的 texts_to_sequences 用数字序列表示每个句子。下面,我们打印出原始句子、单词索引和序列。

sequences = tokenizer.texts_to_sequences(sentences)
print(sentences)
print(word_index)
print(sequences)['I want to go out', ' I like to play', ' No eating - ', 'No play!']{'<OOV>': 1, 'i': 2, 'to': 3, 'play': 4, 'no': 5, 'want': 6, 'go': 7, 'out': 8, 'like': 9, 'eating': 10}[[2, 6, 3, 7, 8], [2, 9, 3, 4], [5, 10], [5, 4]]

如上所示,文本由序列表示。举个例子,

  • “我要出去”——>[2,6,3,7,8]
  • “我喜欢玩”——>[2,9,3,4]
  • “不准吃东西”——>[5,10]
  • “不玩了!”— -> [5, 4]

填充

在任何原始文本数据中,自然会有不同长度的句子。然而,所有的神经网络都要求具有相同大小的输入。为此,需要进行填充。下面,我们用 pad_sequences 进行填充。 pad_sequences 使用 sequences、padding、maxlen、truncating、value 和 dtype 等参数。

  • 序列:我们之前创建的序列列表
  • padding = 'pre ‘或’ post(默认 pre)。通过使用 pre,我们将在每个序列之前填充(添加 0 ), post 将在每个序列之后填充。
  • maxlen =所有序列的最大长度。如果没有提供,默认情况下它将使用最长句子的最大长度。
  • truncating = 'pre ‘或’ post ‘(默认为’ pre ')。如果序列长度大于提供的 maxlen 值,则这些值将被截断为 maxlen 值。“前”选项将在序列的开头截断,而“后”将在序列的结尾截断。
  • 值:填充值(默认值为 0)
  • dtype:输出序列类型(默认为 int32)

让我们关注一下 pad_sequences 中使用的重要参数:padding、maxlen 和 truncating。

前后填充

使用“前”还是“后”填充取决于分析。在某些情况下,开头填充是合适的,而在其他情况下则不合适。例如,如果我们使用递归神经网络(RNN)进行垃圾邮件检测,那么在开始填充将是合适的,因为 RNN 不能学习长距离模式。开始时的填充允许我们保留最后的序列,因此 RNN 可以利用这些序列来预测下一个。然而,在任何情况下,填充都应该在仔细考虑和业务知识之后进行。

下面显示了“前置”和“后置”填充的输出,带有序列最大长度的默认 maxlen 值。

**# pre padding**
pre_pad = pad_sequences(sequences, padding='pre')print("\nword_index = ", word_index)
print("\nsequences = ", sequences)
print("\npadded_seq = " )
print(pre_pad)word_index =  {'<OOV>': 1, 'i': 2, 'to': 3, 'play': 4, 'no': 5, 'want': 6, 'go': 7, 'out': 8, 'like': 9, 'eating': 10}

sequences =  [[2, 6, 3, 7, 8], [2, 9, 3, 4], [5, 10], [5, 4]]

padded_seq = 
[[ 2  6  3  7  8]
 [ **0**  2  9  3  4] <---------- **0** Padded at the beginning
 [ **0  0  0**  5 10] 
 [ **0  0  0**  5  4]]

在我们上面的例子中,最大长度的序列是[ 2,6,3,7,8],对应的是“我想出去”。当使用 padding ='pre '时,在所有其他序列的开头添加填充值 0。因为其他序列比[ 2,6,3,7,8]更短,填充实际上使所有其他序列与此序列大小相同。

然而,当使用 padding =“post”时,在序列的末尾添加填充值,即 0。

**# post padding**
post_pad = pad_sequences(sequences, padding='post')
print("\nword_index = ", word_index)
print("\nsequences = ", sequences)
print("\npadded_seq = " )
print(post_pad)word_index =  {'<OOV>': 1, 'i': 2, 'to': 3, 'play': 4, 'no': 5, 'want': 6, 'go': 7, 'out': 8, 'like': 9, 'eating': 10}

sequences =  [[2, 6, 3, 7, 8], [2, 9, 3, 4], [5, 10], [5, 4]]

padded_seq = 
[[ 2  6  3  7  8]
 [ 2  9  3  4  **0**]<---------- **0** Padded at the end
 [ 5 10  **0  0  0**]
 [ 5  4  **0  0  0**]]

使用 maxlen 和截断选项进行前后填充

如果需要,我们可以同时使用填充和截断参数。下面我们展示了两种情况,1)预截断的预填充和 2)后截断的预填充

使用“pre”选项截断允许我们在开始时截断序列。然而,用“post”截断将在最后截断序列。

让我们看一下使用前置截断进行前置填充的例子。

**# pre padding, maxlen and pre truncation**
prepad_maxlen_pretrunc = pad_sequences(sequences, padding = ‘pre’, maxlen =4, truncating = ‘pre’)
print(prepad_maxlen_pretrunc)[[ 6  3  7  8]<-----Truncated from length 5 to 4, at the beginning
 [ 2  9  3  4]
 [ **0  0**  5 10]<---------- Padded at the beginning
 [ **0  0 ** 5  4]]

通过使用 maxlen =4,我们将填充序列的长度截断为 4。如上所示,maxlen=4 的使用影响了第一个序列[2,6,3,7,8]。该序列的长度为 5,被截断为 4。截断发生在开始时,因为我们使用了 truncating = 'pre '选项。

让我们看看 truncation = 'post '选项。

**# pre padding, maxlen and post truncation**
prepad_maxlen_posttrunc = pad_sequences(sequences, padding = 'pre', maxlen =4, truncating = 'post')
print(prepad_maxlen_posttrunc)[[ 2  6  3  7]<-----Truncated from length 5 to 4, at the end
 [ 2  9  3  4]
 [ **0  0**  5 10]<---------- Padded at the beginning
 [ **0  0**  5  4]]

当我们使用 truncating = 'post '选项时,截断发生在最后。当应用后截断时,它影响第一个序列[ 2,6,3,7,8]并被截断为长度 4,从而产生序列[ 2,6,3,7]。

总结

在本文中,我们专注于预处理原始文本数据,并为深度学习模型做准备。具体来说,我们讨论了对句子进行标记,将其表示为序列,并对其进行填充以使所有序列长度相同。该填充序列现在准备好用于神经网络的训练/测试分割。请参考 Laurence Moroney 的 NLP zero to hero 的视频进行进一步阅读。

在未来的文章中,我将解释我们如何在真实世界的数据中使用预处理,并在深度学习模型中使用这种嵌入的填充序列数据。

感谢您的阅读,如果您有任何意见或建议,请留下。

快乐学习!!!

NLP-预处理临床数据以找到切片

原文:https://towardsdatascience.com/nlp-preprocessing-clinical-data-to-find-sections-461fdadbec77?source=collection_archive---------46-----------------------

清洗医学文本

在本帖中,我们将使用医疗图表笔记数据 ( 医生的潦草笔记 ) 对临床笔记中存在的主题进行建模。记住,写这些笔记是没有结构的。

在后面的故事中,我们将总结这些笔记。

将在 4 篇文章中讨论的 NLP 任务:

  1. 预处理和清洗
  2. 文本摘要
  3. 使用潜在狄利克雷分配(LDA)的主题建模
  4. 聚类

如果您想亲自尝试整个代码或跟随,请访问我在 GitHub 上发布的 jupyter 笔记本:https://GitHub . com/gaurikatyagi/Natural-Language-Processing/blob/master/introduction % 20 to % 20 NLP-Clustering % 20 text . ipynb

数据:

来源:https://mimic.physionet.org/about/mimic/

医生在他们的电脑上做笔记,80%的记录是没有结构的。这使得信息处理更加困难。我们不要忘记,解释医疗术语也不是一件容易的事情。它需要大量的上下文来解释。让我们看看我们有什么:

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

按作者分类的图像:作为输入的文本数据

我们立即注意到的事情:

  1. 这是没有标记的纯文本。如果它有标记,我们可以使用像美丽的汤这样的库
  2. 这些行被人工换行(每当您看到单个\n
  3. 没有错别字…哇哦,但是太多首字母缩写和大写字母了
  4. 有像逗号、撇号、引号、问号这样的标点,还有像“后续”这样的连字符描述
  5. 使用了大量有序数据,因此出现了“1”, '2.'诸如此类。但是,请注意,即使在这些数字之前,实际上也有一个单线制动(如 2 中所示)
  6. 看看那些不确定的名字是如何被替换成“姓”或“名 3”,“医院病房名”之类的,好在这些都在方括号内,很容易识别。这些后面也总是跟着一个括号。耶!一些我们可以从一开始就去除的东西!
  7. 注意可能需要如何处理日期(如果它们都不是相同的格式)
  8. 注意一些格式:比如:\n\n ******* \n\n 或者 n??????\tT。我们需要处理好这一切

1.预处理

a.正则表达式:我们将使用正则表达式模式来清理我们的文本。是的,我们确实需要多次反复的清理!

图片作者:Regex Cleaning

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

作者图片:正则表达式清理的输出

b.添加 Context 和 Lemmatize text: 看,我们是怎么讲 Lemmatize 和 not stemming 的。理解两个之间的差异很重要。

在本节中,我们使用大写单词提取“潜在主题”来识别图表中的子主题。然后,专家手动将这些短语标记为“T”或“F”,以表示接受。一旦我们有了它,我们就改变了所有其他单词的大小写。等等,没那么容易,记住我们需要把它归类。

作者图片:为上下文添加子部分

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

作者图片:确定了 124 个副标题

现在,我们将把所有大写单词的大小写改为小写,除非它们是上面确定的主题

通过运行 lemmatizer 找到上述所有主题的词条**。我们删除了主题中的停用词,以便在“大写单词列表”**中捕捉更多的主题。

那么,这里发生了什么变化?

for index in range(len(lemmatized_topics_index)):
    print("Original Topic: %s \n New Topic Index: %s\n New Topic value: %s"%(topics[index],                                                                                       lemmatized_topics_index[index],                                                                lemmatized_topics_values[index]
)
)
    ##you can remove this to see all pairs
    if index>2:
        break
    print("\n\n")

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

作者图片:词汇化和词干化的区别

比较每个主题的词干,如果与词条化主题的词干不匹配,则将其改为小写。

为什么先做词条解释,然后只匹配词干?因为词汇化是根据单词属于哪个词类来完成的。然而,词干提取只是试图通过去除常见的复数(etc)字母来找到单词的词根。

因此,我们与词干部分进行比较,但保留已识别主题的词汇作为最终主题。这甚至允许我们在不同的图表注释中获得标准化的主题

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

作者图片:已清理主题

这里是我们最终的主题列表,分析师可以使用 Spacy 中的 spans 来解析和提取所需的信息。这不是很好吗!

在我的下一篇文章中,我将谈论文本摘要。

用 NLTK 进行文本预处理

原文:https://towardsdatascience.com/nlp-preprocessing-with-nltk-3c04ee00edc0?source=collection_archive---------8-----------------------

自然语言处理的常用预处理方法

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

照片由卡洛斯·穆扎Unsplash 上拍摄

介绍

几乎每个 自然语言处理(NLP) 任务都需要在训练模型之前对文本进行预处理。深度学习模型不能直接使用原始文本,因此需要我们研究人员自己清理文本。根据任务的性质,预处理方法可以不同。本教程将使用 NLTK(自然语言工具包) 教授最常见的预处理方法,以适应各种 NLP 任务。

为什么是 NLTK?

  • 流行度 : NLTK 是处理语言数据的领先平台之一。
  • 简单性:为多种文本预处理方法提供易于使用的 API
  • 社区:它有一个庞大而活跃的社区来支持和改进图书馆
  • 开源:免费开源,适用于 Windows、Mac OSX 和 Linux。

现在你知道了 NLTK 的好处,让我们开始吧!

教程概述

  1. 小写字母
  2. 删除标点符号
  3. 标记化
  4. 停用词过滤
  5. 堵塞物
  6. 词性标注器

本教程中显示的所有代码都可以在我的 Github repo 中访问。

导入 NLTK

在预处理之前,我们需要先下载 NLTK 库

pip install nltk

然后,我们可以在 Python 笔记本中导入这个库,并下载它的内容。

小写字母

举个例子,我们从《傲慢与偏见》一书中抓取第一句话作为正文。我们通过text.lower()将句子转换成小写。

删除标点符号

要去掉标点符号,我们只保存不是标点符号的字符,可以用[string.punctuation](https://docs.python.org/3/library/string.html)来检查。

标记化

可以通过[nltk.word_tokenize](https://www.nltk.org/api/nltk.tokenize.html)将字符串标记成标记。

停用词过滤

我们可以使用[nltk.corpus.stopwords.words(‘english’)](https://www.nltk.org/book/ch02.html)来获取英语词典中的停用词列表。然后,我们删除停用词。

堵塞物

我们使用[nltk.stem.porter.PorterStemmer](https://www.nltk.org/_modules/nltk/stem/porter.html)对标记进行词干处理,以获得经过词干处理的标记。

POS 标签

最后,我们可以使用[nltk.pos_tag](https://www.nltk.org/book/ch05.html)来检索列表中每个单词的词性。

完整的笔记本可以在这里看到

结合在一起

我们可以结合上面所有的预处理方法,创建一个接受一个preprocess函数。txt 文件并处理所有的预处理。我们打印出标记、过滤的单词(在停用词过滤之后)、词干单词和词性,其中一个通常被传递给模型或用于进一步处理。我们使用傲慢与偏见这本书(此处可访问)并对其进行预处理。

此笔记本可在处访问。

结论

对于任何 NLP 应用程序来说,文本预处理都是重要的第一步。在本教程中,我们讨论了几种流行的使用 NLTK 的预处理方法:小写、删除标点、标记化、停用词过滤、词干提取和词性标记。

文本分类建模的 NLP 管道教程

原文:https://towardsdatascience.com/nlp-python-pipeline-tutorial-for-text-classification-modeling-6eb1000f909c?source=collection_archive---------27-----------------------

关于使用 sklearn 的 FeatureUnion、Pipeline 和 transformers 预处理组合文本和数字数据的数据科学 python 教程

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

文本和数字特征转换器的 Python 代码。作者截图来自GitHub【1】代码。

目录

  1. 什么是特征转换?
  2. NLP 对您的模型的好处
  3. 关于数据
  4. 文本和数字转换器教程
  5. 摘要
  6. 参考

什么是特征转换?

机器学习最困难的方面之一是为您的模型摄取文本数据。在构建模型时,有您的输入或特征,然后是您试图预测的内容。虽然某些类型的模型可以按数值或分类预测值或目标变量进行分组,但当您不仅要使用数值,还要使用文本作为要素类型时,并没有太多的文档说明该如何操作。要素变换是一种将两种类型结合起来并将其转换为可用格式的方法,可作为 X 变量来预测 Y 预测值或变量。下面的代码使用 sklearn 库将这两种类型的数据转换并组合成一个输入。

NLP 对您的模型的好处

NLP,也称为自然语言处理,可用于将文本特征转换为数字表示。好处如下:

  • 将输入要素转换为较小的尺寸
  • 将目标变量转换成较小的大小
  • 将分类数据转换成数字
  • 将文本数据转换为数字
  • 标准化您的输入数据
  • 清理您的数据

关于数据

这里的数据集是随机的,有三个数字特征的虚拟数据;‘已确认 _ 测试’,‘已确认 _ 恢复’,‘已确认 _ 新建’,具有一个文本特征;‘正文 _ 特写’。在这种情况下,目标变量被标记为*‘Text _ Predictor’*。正常情况下,这个数据集很容易被消化到机器学习模型中,但这里我们引入了一个文本特征,这确实带来了一个问题。问题是,我们有两种截然不同的数据。按照下面的教程,这个问题是用 Python、sk learn【2】和pandas【3】解决和描述的。

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

仔细观察所用的数据帧。作者截图来自GitHub【1】代码。

文本和数字转换器教程

下面是如何设置 Jupyter 笔记本的教程。ipynb 文件或您的 Python。py 文件。使用 sklearn 的库,您可以读入数据帧,转换输入要素和目标变量,然后开发一个随机森林模型来帮助预测新标签。

  • TfidfVectorizer —术语频率-逆文档频率矢量器,根据您的特征在词的语料库中找到您的术语的唯一性
  • base estimator——sk learn 库的基类
  • 用于 sklearn 库的 transformer Mixin-Mixin 类
  • 管道-将要素和分类器结合在一起
  • FeatureUnion 组合所有特征;文本和数字一起
  • pandas.get _ dummies 将分类目标转换为数字

要遵循本教程,请查找一个类似的数据集,该数据集同时具有数字和文本特征以及分类目标变量。首先,导入必要的库。参考 sklearn 代码和类:

BaseEstimator, TransformerMixin that are used in TextTransfomer and NumberTransformer classes.

接下来,读入你的数据帧。然后,创建您的 vec_tdidf 对象,该对象使用 TfidVectorizer ,通过利用其术语频率逆文档频率矢量器将文本计数转换为术语重要性的加权矩阵。使用管道创建一个管道对象,为“文本特征”一起编译转换器和矢量器。将相同的规则应用于数字要素,并分别对其进行标注。现在所有的特征类型都被转换了,使用 FeatureUnion 将它们合并成一个。

下一步是创建您的随机森林分类器。然后,将特征和分类器结合在一起,这就是你的模型对象。使用 pd.get_dummies 将目标变量从分类变量转换为数字变量也很重要。

为文本和数字特征创建一个列表,并分配目标变量。这些新项目将在 train_test_split 中从您的数据帧中引用。接下来,使用 X_trainy_train 装配您的模型。然后,可以从测试集 X_test 中预测。

最后,使用 sklearn 指标,从你的 y_testpreds (预测)计算出你的 accuracy_score

整个 nlp-example.py 文件。由作者连同sk learn【2】和熊猫【3】一起编码。

摘要

总之,sklearn 及其作者,以及 pandas,已经使编译杂乱和异构的数据变得简单,以便干净有效地输入到您的机器学习模型中。使用这些公共库,每个数据科学家现在不必仅仅依靠数字特征来预测他们的目标变量,而是也可以使用文本/单词/术语数据。使用这些 NLP 技术,可以很容易地对文本分类问题(如术语分类)进行建模。

参考

旁注——在我的代码、文本和文件中,我使用术语“预测器”作为预测的东西,也称为目标变量,或人们试图分类的东西。

[1] M.Przybyla, GitHub (2020)

[2] sklearn, sklearn (2020)

[3]熊猫,熊猫 (2020)

NLP 情感分析手册

原文:https://towardsdatascience.com/nlp-sentiment-analysis-for-beginners-e7897f976897?source=collection_archive---------12-----------------------

逐步了解文本块、NLTK、Scikit-Learn 和 LSTM 网络的方法

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

Romain VignesUnsplash 上拍摄的照片

介绍

自然语言处理(NLP)是机器学习的领域,专注于语言的生成和理解。它的主要目标是让机器能够理解人类,以自然的方式与人类交流和互动。

自然语言处理有许多任务,如文本生成、文本分类、机器翻译、语音识别、情感分析等。对于 NLP 的初学者来说,查看这些任务以及处理这些任务所涉及的所有技术可能会令人望而生畏。而且事实上,对于一个新手来说,确切地知道从哪里开始以及如何开始是非常困难的。

在所有的 NLP 任务中,我个人认为情感分析(SA)可能是最容易的,这使得它成为任何想开始进入 NLP 的人最合适的起点。

在本文中,我编译了各种如何执行 SA 的技术,从简单的 TextBlob 和 NLTK 到更高级的 Sklearn 和长短期记忆(LSTM)网络。

读完这篇文章后,你可以理解以下内容:

  1. 在 SA: TextBlob 和 NLTK 中使用的工具包
  2. SA 中使用的算法:朴素贝叶斯、SVM、逻辑回归和 LSTM
  3. 术语如停用词移除、词干、单词包、语料库、标记化等。
  4. 创建一个字云

这篇文章的流程:

  1. 数据清理和预处理
  2. 文本 Blob
  3. 算法:逻辑回归,朴素贝叶斯,SVM 和 LSTM

我们开始吧!

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

远程办公

问题定式化

在本文中,我将介绍由 3000 个句子组成的sentiment数据集,这些句子来自对imdb.comamazon.comyelp.com的评论。每个句子根据它是来自正面评论(标记为1)还是负面评论(标记为0)来标记。

数据可以从网站下载。或者也可以从这里下载(强烈推荐)。文件夹sentiment_labelled_sentences(包含数据文件full_set.txt)应该和你的笔记本在同一个目录下。

加载和预处理数据

设置和导入库

%matplotlib inline
import string
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
matplotlib.rc('xtick', labelsize=14) 
matplotlib.rc('ytick', labelsize=14)

现在,我们加载数据并查看前 10 条评论

with open("sentiment_labelled_sentences/full_set.txt") as f:
    content = f.readlines()content[0:10]

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

## Remove leading and trailing white space
content = [x.strip() for x in content]## Separate the sentences from the labels
sentences = [x.split("\t")[0] for x in content]
labels = [x.split("\t")[1] for x in content]sentences[0:10]
labels[0:10]

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

分开句子和标签

我们可以在这里停下来。但对我来说,我更喜欢把 y 变换成(-1,1)的形式,其中-1 代表负,1 代表正。

## Transform the labels from '0 v.s. 1' to '-1 v.s. 1'
y = np.array(labels, dtype='int8')
y = 2*y - 1

请注意,到目前为止,我们还没有对单词做任何修改!!!下一节重点是句子中的单词。

预处理文本数据

要将数据输入到任何模型中,输入的数据必须为矢量形式**。我们将进行以下转换:**

  • 删除标点和数字
  • 将所有单词转换成小写
  • 删除停用词(例如,the,a,that,this,it,…)
  • 将文本符号化
  • 使用单词袋表示法将句子转换成向量

我将在这里解释一些术语。

  1. Stop words:对手头的任务不感兴趣的常用词。这些通常包括冠词,如“a”和“the”,代词,如“I”和“他们”,以及介词,如“to”和“from”,…
## Demonstrate ##
def removeStopWords(stopWords, txt):
    newtxt = ' '.join([word for word in txt.split() if word not in stopWords])
    return newtxt stoppers = ['a', 'is', 'of','the','this','uhm','uh']
removeStopWords(stoppers, "this is a test of the stop word removal code")

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

或者,如果我们确实想要使用一套完整的常用停用词,我们可以使用NLTK

from nltk.corpus import stopwordsstops = stopwords.words("English")removeStopWords(stops, "this is a test of the stop word removal code.")

同样的结果

2.Corpus:简单的文字集合。语序事关。“不伟大”和“伟大”是不同的

3.Document-Term MatrixBag of Words (BOW)只是文本句子(或文档)的矢量表示

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

像这样表示一组要素的一种常见方法称为一键矢量。例如,假设我们文本集中的词汇是:

*today, here, I, a, fine, sun, moon, bird, saw*

我们要为之造弓的句子是:

*I saw a bird today*

*1 0 1 1 0 0 1 1*

为了创造一个单词包,我们需要将一个长句或一份文件分解成更小的部分。这个过程叫做Tokenization。最常见的标记化技术是将文本分解成单词。我们可以在 Scikit-Learn 中使用CountVectorizer来实现这一点,其中每一行代表一个不同的文档,每一列代表一个不同的单词。

现在让我们把所有东西放在一起,组成我们的数据集

def full_remove(x, removal_list):
    for w in removal_list:
        x = x.replace(w, ' ')
    return x## Remove digits ##
digits = [str(x) for x in range(10)]
remove_digits = [full_remove(x, digits) for x in sentences]## Remove punctuation ##
remove_punc = [full_remove(x, list(string.punctuation)) for x in remove_digits]## Make everything lower-case and remove any white space ##
sents_lower = [x.lower() for x in remove_punc]
sents_lower = [x.strip() for x in sents_lower]## Remove stop words ##
from nltk.corpus import stopwords
stops = stopwords.words("English")def removeStopWords(stopWords, txt):
    newtxt = ' '.join([word for word in txt.split() if word not in stopWords])
    return newtxtsents_processed = [removeStopWords(stops,x) for x in sents_lower]

让我们看看现在我们的句子是什么样子的

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

嗯,等一下!去掉很多停用词会让很多句子失去意义。例如,“way plug us unless go converter”对我来说没有任何意义。这是因为我们使用 NLTK 删除了所有常见的英语停用词。为了克服这个意义问题,让我们创建一套自己的停用词。

stop_set = ['the', 'a', 'an', 'i', 'he', 'she', 'they', 'to', 'of', 'it', 'from']sents_processed = [removeStopWords(stop_set,x) for x in sents_lower]

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

就此打住,转到标记化就可以了。然而,人们可以继续使用stemmingstemming的目标是去掉单词中的前缀和后缀,将单词转换成它的基本形式,e.g. studying->study, beautiful->beauty, cared->care,…在 NLTK 中,有两种流行的stemming技术,称为 porter 和 lanscaster。[参考:数据营

import nltk
def stem_with_porter(words):
    porter = nltk.PorterStemmer()
    new_words = [porter.stem(w) for w in words]
    return new_words

def stem_with_lancaster(words):
    porter = nltk.LancasterStemmer()
    new_words = [porter.stem(w) for w in words]
    return new_words ## Demonstrate ##    
str = "Please don't unbuckle your seat-belt while I am driving, he said"print("porter:", stem_with_porter(str.split()))

print("lancaster:", stem_with_lancaster(str.split()))

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

让我们试试我们的sents_processed看看它是否有意义。

porter = [stem_with_porter(x.split()) for x in sents_processed]porter = [" ".join(i) for i in porter]porter[0:10]

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

出现一些奇怪的变化,例如非常->真实,质量->质量,价值->价值,…

4.Term Document Inverse Document Frequency (TD/IDF)。这是在多个文档的上下文中,一个单词在一个文档中的相对重要性的度量。在我们这里,多重审查。

我们从 TD 部分开始——这只是文档中单词的归一化频率:

(word count in document) / (total words in document)

IDF 是所有文档中单词唯一性的加权。以下是 TD/IDF 的完整公式:

td_idf(t,d) = wc(t,d)/wc(d) / dc(t)/dc()

其中:
wc(t,d)=【t 项在文档 d
中的出现次数】— wc(d) =【文档 d】中的字数
dc(t)=【t 项在集合中至少出现 1 次的文档数】— dc() =【集合中的文档数】

现在,让我们创建一个单词包,并规范文本

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformervectorizer = CountVectorizer(analyzer = "word", 
                             preprocessor = None, 
                             stop_words =  'english', 
                             max_features = 6000, ngram_range=(1,5))data_features = vectorizer.fit_transform(sents_processed)
tfidf_transformer = TfidfTransformer()
data_features_tfidf = tfidf_transformer.fit_transform(data_features)
data_mat = data_features_tfidf.toarray()

现在data_mat是我们的文档术语矩阵。输入已准备好投入模型。让我们创建训练集和测试集。在这里,我将数据分为 2500 个句子的训练集和 500 个句子的测试集(其中 250 个是正面的,250 个是负面的)。

np.random.seed(0)
test_index = np.append(np.random.choice((np.where(y==-1))[0], 250, replace=False), np.random.choice((np.where(y==1))[0], 250, replace=False))train_index = list(set(range(len(labels))) - set(test_index))train_data = data_mat[train_index,]
train_labels = y[train_index]test_data = data_mat[test_index,]
test_labels = y[test_index]

文本 Blob

  1. **TextBlob**:语言学研究者根据他们的专业领域给单词的情感贴上标签。单词的情感可以根据它在句子中的位置而变化。TextBlob模块允许我们利用这些标签。TextBlod找到它可以分配极性主观性的所有单词和短语,然后将它们全部平均
  2. 情感标签:语料库中的每个单词都根据极性和主观性进行标记(还有更多标签,但我们现在将忽略它们)。一个语料库的情感是这些的平均值。
  • 极性 :一个字有多正或多负。-1 是非常消极的。+1 很正。
  • 主观性 :一个词有多主观,或者说有多固执己见。0 是事实。+1 在很大程度上是一种观点。
from textblob import TextBlob#Create polarity function and subjectivity functionpol = lambda x: TextBlob(x).sentiment.polaritysub = lambda x: TextBlob(x).sentiment.subjectivitypol_list = [pol(x) for x in sents_processed]sub_list = [sub(x) for x in sents_processed]

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

这是一种基于规则的方法,决定了评论的情绪(极性和主观性)。

下一节将介绍各种算法。

逻辑回归

from sklearn.linear_model import SGDClassifier## Fit logistic classifier on training data
clf = SGDClassifier(loss="log", penalty="none")
clf.fit(train_data, train_labels)## Pull out the parameters (w,b) of the logistic regression model
w = clf.coef_[0,:]
b = clf.intercept_## Get predictions on training and test data
preds_train = clf.predict(train_data)
preds_test = clf.predict(test_data)## Compute errors
errs_train = np.sum((preds_train > 0.0) != (train_labels > 0.0))
errs_test = np.sum((preds_test > 0.0) != (test_labels > 0.0))print("Training error: ", float(errs_train)/len(train_labels))
print("Test error: ", float(errs_test)/len(test_labels))**Training error:  0.0116
Test error:  0.184**

影响力大的词

决定一个句子是否为肯定的,哪些词最重要?作为对此的第一近似,我们简单地取其系数在w中具有最大正值的单词。

同样,我们看那些在w中系数具有最大负值的单词,我们认为这些单词对负面预测有影响。

## Convert vocabulary into a list:
vocab = np.array([z[0] for z in sorted(vectorizer.vocabulary_.items(), key=lambda x:x[1])])## Get indices of sorting w
inds = np.argsort(w)## Words with large negative values
neg_inds = inds[0:50]
print("Highly negative words: ")
print([str(x) for x in list(vocab[neg_inds])])## Words with large positive values
pos_inds = inds[-49:-1]
print("Highly positive words: ")print([str(x) for x in list(vocab[pos_inds])])

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

创建字云

from wordcloud import WordCloud
wc = WordCloud(stopwords=stop_set, background_color="white", colormap="Dark2",
               max_font_size=150, random_state=42)#plt.rcParams['figure.figsize'] = [16, 6]wc.generate(" ".join(list(vocab[neg_inds])))plt.imshow(wc, interpolation="bilinear")
plt.axis("off")    
plt.show()

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

朴素贝叶斯

from sklearn.naive_bayes import MultinomialNB
nb_clf = MultinomialNB().fit(train_data, train_labels)nb_preds_test = nb_clf.predict(test_data)
nb_errs_test = np.sum((nb_preds_test > 0.0) != (test_labels > 0.0))
print("Test error: ", float(nb_errs_test)/len(test_labels))**Test error:  0.174**

我们来做一些预测案例。[1]表示正,而[-1]表示负

print(nb_clf.predict(vectorizer.transform(["**It's a sad movie but very good**"])))**[1]**print(nb_clf.predict(vectorizer.transform(["**Waste of my time**"])))**[-1]**print(nb_clf.predict(vectorizer.transform(["**It is not what like**"])))**[-1]**print(nb_clf.predict(vectorizer.transform(["**It is not what I m looking for**"])))**[1]**

最后一个测试用例有问题。这应该是负面评论,但模型预测是正面的。

SVM

from sklearn.linear_model import SGDClassifiersvm_clf = SGDClassifier(loss="hinge", penalty='l2')
svm_clf.fit(train_data, train_labels)svm_preds_test = svm_clf.predict(test_data)
svm_errs_test = np.sum((svm_preds_test > 0.0) != (test_labels > 0.0))print("Test error: ", float(svm_errs_test)/len(test_labels))**Test error:  0.2**

再次,让我们做一些预测

print(svm_clf.predict(vectorizer.transform(["**This is not what I like**"])))**[-1]**print(svm_clf.predict(vectorizer.transform(["**It is not what I am looking for**"])))**[-1]**print(svm_clf.predict(vectorizer.transform(["**I would not recommend this movie**"])))**[1]**

SVM 可以正确预测评论“这不是我要找的”。然而,它无法预测评论“我不推荐这部电影”。

LSTM 网络公司

关于 LSTM 网络的详细讨论可以在这里找到。

from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from keras.layers import SpatialDropout1D
from keras.layers.embeddings import Embedding
from keras.preprocessing import sequence
from keras.preprocessing.text import Tokenizer
from keras.callbacks import EarlyStoppingmax_review_length = 200tokenizer = Tokenizer(num_words=10000,  #max no. of unique words to keep
                      filters='!"#$%&()*+,-./:;<=>?@[\]^_`{|}~', 
                      lower=True #convert to lower case
                     )
tokenizer.fit_on_texts(sents_processed)

截断并填充输入序列,使它们长度相同

X = tokenizer.texts_to_sequences(sents_processed)
X = sequence.pad_sequences(X, maxlen= max_review_length)
print('Shape of data tensor:', X.shape)**Shape of data tensor: (3000, 200)**

回想一下,y 是 1 和-1 的向量。现在我把它改成一个有 2 列的矩阵,分别代表-1 和 1。

import pandas as pd
Y=pd.get_dummies(y).values
Y

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

np.random.seed(0)
test_inds = np.append(np.random.choice((np.where(y==-1))[0], 250, replace=False), np.random.choice((np.where(y==1))[0], 250, replace=False))
train_inds = list(set(range(len(labels))) - set(test_inds))train_data = X[train_inds,]
train_labels = Y[train_inds]test_data = X[test_inds,]
test_labels = Y[test_inds]

创建网络

EMBEDDING_DIM = 200model = Sequential()
model.add(Embedding(10000, EMBEDDING_DIM, input_length=X.shape[1]))
model.add(SpatialDropout1D(0.2))
model.add(LSTM(250, dropout=0.2,return_sequences=True))
model.add(LSTM(100, dropout=0.2, recurrent_dropout=0.2))
model.add(Dense(2, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
print(model.summary())

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

epochs = 2
batch_size = 40model.fit(train_data, train_labels, 
          epochs=epochs, 
          batch_size=batch_size,
          validation_split=0.1)

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

loss, acc = model.evaluate(test_data, test_labels, verbose=2,
                            batch_size=batch_size)print(f"loss: {loss}")
print(f"Validation accuracy: {acc}")

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

在迄今为止训练的所有模型中,LSTM 表现最好,即逻辑斯蒂、朴素贝叶斯和 SVM。现在让我们看看它如何预测一个测试用例

outcome_labels = ['Negative', 'Positive']new = ["**I would not recommend this movie**"]

seq = tokenizer.texts_to_sequences(new)
padded = sequence.pad_sequences(seq, maxlen=max_review_length)
pred = model.predict(padded)
print("Probability distribution: ", pred)
print("Is this a Positive or Negative review? ")
print(outcome_labels[np.argmax(pred)])

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

new = ["**It is not what i am looking for**"]

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

new = ["**This isn't what i am looking for**"]

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

在这种情况下,否定和肯定的概率差别不大。LSTM 模型认为这是积极的。

new = ["**I wouldn't recommend this movie**"]

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

这个评论也是如此。因此,这意味着我们的模型不能区分n'tnot。一个可能的解决方案是,在预处理步骤中,不是删除所有标点符号,而是将所有的n't简写改为not。这可以简单地用 Python 中的re模块来完成。您可以亲自查看一下,看看我们的模型预测是如何改进的。

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

将“不”改为“不”并再次运行模型后

就是这样!希望你们喜欢这篇文章,并从中有所收获。如果你有任何问题,请在下面的评论区写下来。谢谢你的阅读。祝您愉快,保重!!

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

照片由卢卡斯·克拉拉Unsplash 上拍摄

NLP:使用深度学习检测短信(文本)数据中的垃圾邮件

原文:https://towardsdatascience.com/nlp-spam-detection-in-sms-text-data-using-deep-learning-b8632db85cc8?source=collection_archive---------3-----------------------

TensorFlow2 中使用密集网络、LSTM 和双 LSTM 架构的文本分类

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

作者图片

介绍

如今,互联网和社交媒体已经成为获取信息最快捷、最简单的方式。在这个时代,评论、意见、反馈、信息和建议已经成为重要的信息来源。由于技术的进步,我们现在能够使用各种自然语言处理(NLP)技术从这些数据中提取有意义的信息。NLP 是人工智能(AI)的一个分支,它利用计算机和人类自然语言输出有价值的信息。NLP 通常用于文本分类任务,例如垃圾邮件检测和情感分析、文本生成、语言翻译和文档分类。

目的

本文的目的是了解我们如何使用 TensorFlow2 来构建垃圾短信检测模型。特别地,我们将建立一个二进制分类模型来检测一个文本消息是否是垃圾邮件。此外,我们将了解如何在 TensorFlow2 Keras API 中实现密集的长短期记忆(LSTM)和双向 LSTM(双 LSTM)深度学习模型。

数据

短信(文本)数据是从 UCI 数据集下载的。它包含 5,574 条手机短信。这些数据是为了手机垃圾邮件研究而收集的,已经被贴上了垃圾邮件或垃圾邮件的标签。

方法

我们将使用密集文本分类器,LSTM 和双 LSTM,并比较这些方法在性能方面,并选择一个最终的。

以下是我们在本文中涉及的部分:

  • 加载并浏览垃圾邮件数据
  • 准备列车测试数据
  • 使用上述三种方法训练垃圾邮件检测模型
  • 比较并选择最终型号
  • 使用最终训练的分类器对新消息进行分类

让我们开始吧,首先确定 TensrFlow 的版本。

import tensorflow as tf
print(tf.__version__)
2.2.0

导入所需包

将所有的导入放在代码的开头是一个很好的做法。

# import libraries for reading data, exploring and plotting
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator
%matplotlib inline# library for train test split
from sklearn.model_selection import train_test_split# deep learning libraries for text pre-processing
import tensorflow as tf
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences# Modeling 
from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, GlobalAveragePooling1D, Dense, Dropout, LSTM, Bidirectional

加载并浏览垃圾邮件数据

数据可以从 UCI 数据集下载并保存在本地文件夹中。文本文件(和一个完整的 Jupyter 笔记本)也在我的 github 位置提供,因此可以使用下面的语法阅读。文本文件是一个制表符分隔的(\t)文件,因此,我们可以使用 pandas 来读取数据。我们还可以通过传递名称来提供列名,称之为标签和消息。

url = '[https://raw.githubusercontent.com/ShresthaSudip/SMS_Spam_Detection_DNN_LSTM_BiLSTM/master/SMSSpamCollection'](https://raw.githubusercontent.com/ShresthaSudip/SMS_Spam_Detection_DNN_LSTM_BiLSTM/master/SMSSpamCollection')
messages = pd.read_csv(url, sep ='\t',names=["label", "message"])
messages[:3] 

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

数据帧的前 3 行

让我们获得汇总统计数据并可视化数据。熊猫的 describe() 方法提供了一个汇总统计数据。例如,有 5,572 个标签和消息。有两个独特的标签标明“火腿”和“垃圾邮件”。我们还可以观察到,唯一消息(5,169)比总消息数(5,572)少,这表明有一些重复的消息。最上面的标签是“火腿”,数据中最上面的消息是“对不起,我稍后再打”。下面的重复箭头显示,有 403 条重复消息。

messages.describe()

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

汇总统计数据

duplicatedRow = messages[messages.duplicated()]
print(duplicatedRow[:5])

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

重复行

与 747 条垃圾消息相比,有 4,825 条 ham。这表示不平衡的数据,我们稍后会修复。最受欢迎的业余消息是“对不起,我稍后再打”,而最受欢迎的垃圾消息是“请打电话给我们的客户服务部……”,分别出现了 30 次和 4 次。

messages.groupby('label').describe().T

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

按标签(垃圾邮件)统计摘要

下面,我们通过创建一个 WordCloud 和一个条形图来进一步探索标签组的数据。首先,让我们为 ham 和 spam 消息创建一个单独的数据帧,并将其转换为 numpy 数组以生成 WordCloud。

# Get all the ham and spam emails
ham_msg = messages[messages.label =='ham']
spam_msg = messages[messages.label=='spam']# Create numpy list to visualize using wordcloud
ham_msg_text = " ".join(ham_msg.message.to_numpy().tolist())
spam_msg_text = " ".join(spam_msg.message.to_numpy().tolist())

为了使用 WordCloud() 进行可视化,我们提取了在垃圾邮件中最常见的单词,删除了无意义的停用词,如“the”、“a”、“is”等,并绘制了它。单词云将给定文本中最常用的单词可视化。

# wordcloud of ham messages
ham_msg_cloud = WordCloud(width =520, height =260, stopwords=STOPWORDS,max_font_size=50, background_color ="black", colormap='Blues').generate(ham_msg_text)
plt.figure(figsize=(16,10))
plt.imshow(ham_msg_cloud, interpolation='bilinear')
plt.axis('off') # turn off axis
plt.show()

下面的火腿消息 WordCloud 显示,“现在”、“工作”、“怎么样”、“还好”、“对不起”是火腿消息中最常出现的词。

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

业余消息的单词库

下面的垃圾短信词云显示,“免费”、“通话”、“短信”、“认领”、“回复”是垃圾短信中最常出现的词。

# wordcloud of spam messages
spam_msg_cloud = WordCloud(width =520, height =260, stopwords=STOPWORDS,max_font_size=50, background_color ="black", colormap='Blues').generate(spam_msg_text)
plt.figure(figsize=(16,10))
plt.imshow(spam_msg_cloud, interpolation='bilinear')
plt.axis('off') # turn off axis
plt.show()

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

垃圾短信的文字云

现在,让我们进一步探讨不平衡的数据。下面的条形图显示了班级的不平衡。最常见的垃圾邮件(85%)多于垃圾邮件(15%)。

# we can observe imbalance data here 
plt.figure(figsize=(8,6))
sns.countplot(messages.label)
# Percentage of spam messages
(len(spam_msg)/len(ham_msg))*100 # 15.48%

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

显示不平衡数据的条形图

有几种方法可以处理不平衡数据,例如

  • 使用适当的评估指标
  • 对训练集进行重采样:过采样/上采样或欠采样/下采样
  • 集成不同重采样数据集

然而,对于我们的问题,我们使用下采样只是为了说明它是如何实现的。当然,您可以尝试其他技术并比较结果。

缩减采样是从多数类中随机删除一些观察值的过程,以便多数类和少数类中的数字相匹配。下面,我们对 ham 消息进行了缩减采样(多数类)。现在每个班有 747 条消息。

# one way to fix it is to downsample the ham msg
ham_msg_df = ham_msg.sample(n = len(spam_msg), random_state = 44)
spam_msg_df = spam_msg
print(ham_msg_df.shape, spam_msg_df.shape)(747, 2) (747, 2)

下图显示了在考虑不平衡数据后,不同消息类型之间的类似分布。

# Create a dataframe with these ham and spam msg
msg_df = ham_msg_df.append(spam_msg_df).reset_index(drop=True)
plt.figure(figsize=(8,6))
sns.countplot(msg_df.label)
plt.title('Distribution of ham and spam email messages (after downsampling)')
plt.xlabel('Message types')

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

考虑不平衡数据后的条形图

此外,平均而言,ham 消息具有 73 个字的长度,而 spam 消息具有 138 个字。当我们稍后设置 maxlen 参数时,长度信息可能有用。

# Get length column for each text
msg_df['text_length'] = msg_df['message'].apply(len)#Calculate average length by label types
labels = msg_df.groupby('label').mean()
labels

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

表中显示了垃圾邮件的文本长度

准备培训/测试数据和预处理文本

在探索和解决不平衡数据之后,接下来让我们将文本标签转换为数字,并将数据分成训练集和测试集。此外,将标签转换为 numpy 数组,以适应深度学习模型。80%的数据用于训练,20%用于测试。

# Map ham label as 0 and spam as 1
msg_df['msg_type']= msg_df['label'].map({'ham': 0, 'spam': 1})
msg_label = msg_df['msg_type'].values# Split data into train and test
train_msg, test_msg, train_labels, test_labels = train_test_split(msg_df['message'], msg_label, test_size=0.2, random_state=434)

现在,让我们使用文本预处理,包括标记化、排序和填充。

下图描述了文本预处理的流程示例以及预期输出。

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

关于使用 TensorFlow2 预处理文本数据的基本理解,请参考我之前的文章关于预处理深度学习模型的数据。

标记化

由于深度学习模型不理解文本,让我们将文本转换为数字表示。为此,第一步是标记化。TensorFlow Keras 的 Tokenizer API 将句子拆分成单词,并将这些单词编码成整数。 Tokenizer() 执行所有必需的预处理,例如

  • 将符号化为单词或字符——这里我们在单词级别使用
  • num_words 表示唯一令牌的最大数量,因此我们可以过滤掉罕见的单词
  • 过滤掉标点符号
  • 将所有单词转换成小写
  • 将所有单词转换为整数索引

首先,定义用于预处理的超参数。我们稍后将描述这些超参数。

# Defining pre-processing hyperparameters
max_len = 50 
trunc_type = "post" 
padding_type = "post" 
oov_tok = "<OOV>" 
vocab_size = 500

下面,我们用 Tokenizer() 对单词进行分词。

tokenizer = Tokenizer(num_words = vocab_size, char_level=False, oov_token = oov_tok)
tokenizer.fit_on_texts(train_msg)

Tokenizer 对象中使用的超级参数是:num_words 和 oov_token,char_level。

  • num_words:指示在训练和测试数据中要加载多少个唯一的单词。出于我们的目的,我们选择了 500 个单词(vocab_size)
  • oov_token:当它被使用时,词汇外的 token 将被添加到用于建立模型的语料库中的单词索引中。这用于在 text_to_sequence 调用期间替换词汇表之外的单词(不在我们的语料库中的单词)(见下文)。
  • char_level:如果为“真”,那么每个字符都将被视为一个令牌。我们将其设置为“假”,因此每个将被视为一个令牌。

我们可以使用 tokenizer.word_index 得到 word_index。下面是 word_index 的快照。

# Get the word_index 
word_index = tokenizer.word_index
word_index

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

印刷单词索引的快照

# check how many words 
tot_words = len(word_index)
print('There are %s unique tokens in training data. ' % tot_words)There are 4169 unique tokens in training data.

排序和填充

在标记化之后,我们使用来自标记化器对象的 texts_to_sequences() 用数字序列表示每个句子。随后,我们使用 pad_sequences() 使得每个序列具有相同的长度。对训练和测试数据进行排序和填充。

# Sequencing and padding on training and testing 
training_sequences = tokenizer.texts_to_sequences(train_msg)
training_padded = pad_sequences (training_sequences, maxlen = max_len, padding = padding_type, truncating = trunc_type )testing_sequences = tokenizer.texts_to_sequences(test_msg)
testing_padded = pad_sequences(testing_sequences, maxlen = max_len,
padding = padding_type, truncating = trunc_type)
  • padding = 'pre ‘或’ post(默认 pre)。通过使用 pre,我们在每个序列之前填充,通过使用 post,我们在每个序列之后填充。
  • maxlen =所有序列的最大长度。这里 max_len=50 因此,我们在一个句子中只使用 50 个单词。如果没有提供,默认情况下它将使用最长句子的最大长度。
  • truncating = 'pre ‘或’ post ‘(默认为’ pre ')。如果序列长度大于提供的 maxlen 值,则这些值将被截断为 maxlen。“pre”选项将在序列的开头截断,而“post”将在序列的结尾截断。
# Shape of train tensor
print('Shape of training tensor: ', training_padded.shape)
print('Shape of testing tensor: ', testing_padded.shape)Shape of training tensor:  (1195, 50)
Shape of testing tensor:  (299, 50)

在填充之前,第一个序列是 27 个字长,而第二个是 24 个字长。一旦应用了填充,两个序列的长度都是 50。

# Before padding
len(training_sequences[0]), len(training_sequences[1])Output:
(27, 24)# After padding
len(training_padded[0]), len(training_padded[1])Output:
(50, 50)

如下所示,填充序列的长度为 50。

print(training_padded[0])

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

第一句的填充序列

密集垃圾邮件检测模型

随着数据的加载和预处理,我们现在已经准备好使用神经网络架构对文本消息进行分类。让我们使用 LSTM 和双 LSTM 之后的密集架构来训练模型。

定义超参数:

vocab_size = 500 # As defined earlier
embeding_dim = 16
drop_value = 0.2 # dropout
n_dense = 24

下面是密集垃圾邮件检测模型的模型架构。

#Dense model architecture
model = Sequential()
model.add(Embedding(vocab_size, embeding_dim, input_length=max_len))
model.add(GlobalAveragePooling1D())
model.add(Dense(24, activation='relu'))
model.add(Dropout(drop_value))
model.add(Dense(1, activation='sigmoid'))
  • 顺序调用 Keras 顺序模型,其中各层按顺序添加
  • 第一层,即嵌入层,将每个单词映射到实数的 N 维向量。embeding _ dim 是这个向量的大小,在我们的例子中是 16。嵌入层表明具有相似意思的两个单词倾向于具有非常接近的向量。因为,嵌入层是我们模型网络中的第一个隐藏层,我们需要传递由 input_length (max_len =50)定义的输入层的形状。
  • 池层有助于减少模型中的参数数量,从而有助于避免过度拟合。我们在这里使用了平均池,并将层转换为一维。
  • 接下来,我们使用一个带有激活函数“relu”的密集层,然后是一个用于避免过拟合的下降层和一个带有 sigmoid 激活函数的最终输出层。由于只有两类(垃圾邮件或业余爱好者)要分类,我们只使用一个输出神经元。sigmoid 激活函数输出 0 到 1 之间的概率。

这里,我们使用一个相当浅的神经网络结构,然而,你可以通过增加更多的层使它更密集。

下面的 model.summary() 提供了层、形状和每个层中使用的参数数量。在嵌入层,8000 参数来自 500 个单词(vocab_size),每个单词有一个 16 维的单词向量空间(500×16 = 8000)嵌入层通过globalaveragepool1d并进入具有 16 形状的密集层(由于沿 16 嵌入维度的平均汇集)。我们为密集隐藏层选择了 24 个神经元。密集层中的 24 个神经元中的每一个都从来自 GlobalAveragePooling1D 层的 16 个值中的每一个获得输入,总共 384 (16 X 24)个权重和 24 个偏差(每 24 个神经元一个)。因此,总参数是 408。最后,输出层有 24 个权重(每个神经元一个)和一个偏置项,总共产生 25 个参数。

model.summary()

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

密集模型体系结构的模型概述

编译密集模型

让我们编译我们的密集垃圾邮件分类器模型。由于二进制输出,我们使用“二进制交叉熵”作为损失函数,“adam”作为优化器,其利用动量来避免局部最小值,而“准确性”作为模型性能的度量。

model.compile(loss='binary_crossentropy',optimizer='adam' ,metrics=['accuracy'])

训练和评估密集模型

接下来,让我们使用 model.fit() 参数来拟合我们的密集分类器。它使用填充的训练数据和训练标签来训练模型,并使用验证数据来进行验证。

  • epoch:学习算法在整个训练数据集中工作的次数。我们把它设置为 20。
  • 回调:回调用于传递早期停止参数。early stopping(monitor = ’ val _ loss ',patience=2)用于定义我们希望监控验证损失,如果验证损失在两个时期后没有改善,则停止模型训练。它有助于避免过度适应问题,并在学习者开始过度适应之前指示何时停止训练。如下面的历史结果所示,在时段 23 之后的 2 个时段(时段 24: 0.12 和时段 25: 0.13)中,验证损失连续增加(即,没有改善),因此模型拟合已经在时段 26 处停止。
  • verbose =2:让打印每个时期的损失和准确性
# fitting a dense spam detector model
num_epochs = 30
early_stop = EarlyStopping(monitor='val_loss', patience=3)history = model.fit(training_padded, train_labels, epochs=num_epochs, validation_data=(testing_padded, test_labels),callbacks =[early_stop], verbose=2)

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

各时期的准确性和损失:密集分类器

模型得出,训练损失:0.07,训练准确率:97%,验证损失:0.13,验证准确率:94%。

# Model performance on test data 
model.evaluate(testing_padded, test_labels)Output: loss: 0.13 - accuracy: 0.94

我们可以通过按时期数绘制损失和准确性来进一步可视化历史结果。

# Read as a dataframe 
metrics = pd.DataFrame(history.history)
# Rename column
metrics.rename(columns = {'loss': 'Training_Loss', 'accuracy': 'Training_Accuracy', 'val_loss': 'Validation_Loss', 'val_accuracy': 'Validation_Accuracy'}, inplace = True)def plot_graphs1(var1, var2, string):
    metrics[[var1, var2]].plot()
    plt.title('Training and Validation ' + string)
    plt.xlabel ('Number of epochs')
    plt.ylabel(string)
    plt.legend([var1, var2])

下图显示了训练和验证数据集的历元数损失。正如预期的那样,随着历元数的增加,损耗在减少。在大约 5 个时期之后,验证损失高于训练损失,并且随着时期的增加,差异更加明显。

plot_graphs1('Training_Loss', 'Validation_Loss', 'loss')

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

跟踪各时期的损失:密集分类器

下面的精度图显示,精度随着时间的推移而增加。正如预期的那样,该模型在训练集中的表现优于验证集。然而,如果模型在训练数据上表现得非常好,那么它在测试数据中的表现就更差,那么它是过度拟合的指示。在我们的模型中,我们没有看到过度拟合的重大问题。此外,我们已经通过前面使用的丢弃层和回调解决了过拟合问题。

plot_graphs1('Training_Accuracy', 'Validation_Accuracy', 'accuracy')

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

跟踪不同时期的准确性:密集分类器

长短期记忆(LSTM)模型

下面,我们使用 LSTM 拟合垃圾邮件检测模型。下面是在 LSTM 使用的一些新的超级参数 n_lstmreturn_sequences

  • n_lstm = 20 是 lstm 单元内隐藏层中的节点数
  • return_sequences=True 确保 LSTM 单元格返回一段时间内展开的 LSTM 单元格的所有输出。如果不使用该参数,LSTM 单元将简单地提供上一步中 LSTM 单元的输出。

以下是用于 LSTM 模型的超参数。

#LSTM hyperparameters
n_lstm = 20
drop_lstm =0.2

让我们定义一下 LSTM 垃圾邮件检测模型架构。在 Keras,通过简单地添加 LSTM,我们可以适应 LSTM 模型。

#LSTM Spam detection architecture
model1 = Sequential()
model1.add(Embedding(vocab_size, embeding_dim, input_length=max_len))
model1.add(LSTM(n_lstm, dropout=drop_lstm, return_sequences=True))
model1.add(LSTM(n_lstm, dropout=drop_lstm, return_sequences=True))
model1.add(Dense(1, activation='sigmoid'))

编制 LSTM 模型

model1.compile(loss = 'binary_crossentropy', optimizer = 'adam', metrics=['accuracy'])

培训和评估 LSTM 模型

训练和评估与我们对上面的密集模型所做的相同。

num_epochs = 30
early_stop = EarlyStopping(monitor='val_loss', patience=2)
history = model1.fit(training_padded, train_labels, epochs=num_epochs, validation_data=(testing_padded, test_labels),callbacks =[early_stop], verbose=2)

来自 LSTM 的验证损失和准确度分别为 0.31%和 91%。

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

跨时代的准确性和损失:LSTM

# Create a dataframe
metrics = pd.DataFrame(history.history)# Rename column
metrics.rename(columns = {'loss': 'Training_Loss', 'accuracy': 'Training_Accuracy',
                         'val_loss': 'Validation_Loss', 'val_accuracy': 'Validation_Accuracy'}, inplace = True)
def plot_graphs1(var1, var2, string):
    metrics[[var1, var2]].plot()
    plt.title('LSTM Model: Training and Validation ' + string)
    plt.xlabel ('Number of epochs')
    plt.ylabel(string)
    plt.legend([var1, var2])plot_graphs1('Training_Loss', 'Validation_Loss', 'loss')
plot_graphs1('Training_Accuracy', 'Validation_Accuracy', 'accuracy')

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

记录跨时代的损失:LSTM

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

跟踪不同时代的准确性:LSTM

双向长短期记忆模型

与 LSTM 不同,双 LSTM 从文档中给定标记的前后学习模式。双 LSTM 在时间上向后和向前两个方向反向传播。因此,与 LSTM 相比,计算时间增加了。然而,在大多数情况下,双 LSTM 导致更好的准确性。

下面,我们可以看到**双向 LSTM 架构,**与 LSTM 的唯一区别是,我们使用双向包装器到 LSTM。

# Biderectional LSTM Spam detection architecture
model2 = Sequential()
model2.add(Embedding(vocab_size, embeding_dim, input_length=max_len))
model2.add(Bidirectional(LSTM(n_lstm, dropout=drop_lstm, return_sequences=True)))
model2.add(Dense(1, activation='sigmoid'))

编译 BiLSTM 模型

model2.compile(loss = 'binary_crossentropy', optimizer = 'adam', metrics=['accuracy'])

培训和评估 BiLSTM 模型

# Training
num_epochs = 30
early_stop = EarlyStopping(monitor='val_loss', patience=2)
history = model2.fit(training_padded, train_labels, epochs=num_epochs, 
                    validation_data=(testing_padded, test_labels),callbacks =[early_stop], verbose=2)

BiLSTM 的验证损失和准确度分别为 0.18 和 95%。

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

跨时代的准确性和损失:BiLSTM

# Create a dataframe
metrics = pd.DataFrame(history.history)# Rename column
metrics.rename(columns = {'loss': 'Training_Loss', 'accuracy': 'Training_Accuracy',
                         'val_loss': 'Validation_Loss', 'val_accuracy': 'Validation_Accuracy'}, inplace = True)
def plot_graphs1(var1, var2, string):
    metrics[[var1, var2]].plot()
    plt.title('BiLSTM Model: Training and Validation ' + string)
    plt.xlabel ('Number of epochs')
    plt.ylabel(string)
    plt.legend([var1, var2])# Plot
plot_graphs1('Training_Loss', 'Validation_Loss', 'loss')
plot_graphs1('Training_Accuracy', 'Validation_Accuracy', 'accuracy')

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

记录跨时代的损失

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

跟踪不同时期的准确性:BiLSTM

比较三种不同的型号,选择最后一种

所有密集、LSTM 和双 LSTM 模型在损失和准确性方面都是可比较的。这三个模型的验证损失分别为 0.13、0.31 和 0.18。验证准确率分别为 94%、91%和 95%。

其中,Dense 和 BiLSTM 的表现都优于 LSTM。基于损失、准确性和上面的图,我们选择密集架构作为用于分类垃圾邮件或 ham 的文本消息的最终模型。密集分类器具有简单的结构,并且在历元上的损失和精度比 BiLSTM 更稳定。

# Comparing three different models
print(f"Dense architecture loss and accuracy: {model.evaluate(testing_padded, test_labels)} " )
print(f"LSTM architecture loss and accuracy: {model1.evaluate(testing_padded, test_labels)} " )
print(f"Bi-LSTM architecture loss and accuracy: {model2.evaluate(testing_padded, test_labels)} " )

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

3 个模型的验证损失和准确性

预测新邮件中的垃圾邮件

场景 1:使用我们数据中的原始文本:

让我们评估我们的密集垃圾邮件检测模型如何根据原始数据中的文本预测/分类垃圾邮件或火腿。下面的第一条和第二条消息是垃圾邮件,而第三条是垃圾邮件。我们使用了之前在代码中创建的相同的标记器来将它们转换成序列。这确保了新单词将具有与训练集中相同的标记。标记化后,我们像前面一样使用填充,并提供与训练集中相同的维度。

# display long string 
pd.options.display.max_colwidth=100
messages[:3]

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

原始数据的前 3 个观察值

predict_msg = ["Go until jurong point, crazy.. Available only in bugis n great world la e buffet... Cine there got amore wat...",
          "Ok lar... Joking wif u oni...",
          "Free entry in 2 a wkly comp to win FA Cup final tkts 21st May 2005\. Text FA to 87121 to receive entry question(std txt rate)T&C's apply 08452810075over18's"]

predict_spam 函数定义如下。

# Defining prediction functiondef predict_spam(predict_msg):
    new_seq = tokenizer.texts_to_sequences(predict_msg)
    padded = pad_sequences(new_seq, maxlen =max_len,
                      padding = padding_type,
                      truncating=trunc_type)
    return (model.predict(padded))predict_spam(predict_msg)

如下所示,该模型正确地预测前两个句子不是垃圾邮件,而第三个句子是垃圾邮件。第三句有 99%的可能是垃圾邮件。

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

原始数据的预测结果

场景 2:使用新创建的文本消息,看看模型如何对它们进行分类。

下面,第一句更像是垃圾,而其余两句更像是火腿。

# The third one gives 0.99 indicating very high probabilty of spam
predict_msg = ["You are awarded a Nikon Digital Camera. Call now",
               "Call me",
          "What's up?"]
predict_spam(predict_msg)

我们的模型正确地将第一封邮件分类为垃圾邮件(94%的机会是垃圾邮件),其余的分类为垃圾邮件。

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

新短信的预测结果

摘要

我们使用来自 UCI 数据集的文本消息,拟合深度学习模型,如密集架构、LSTM 和双 LSTM,并比较这些模型的准确性和验证集损失。最后,我们选择密集架构深度学习模型来将文本消息分类为垃圾邮件或 ham,并使用它来分类新的文本消息。本文概述了使用 TensorFlow2 Keras 使用不同的架构深度学习模型来解决 NLP 问题。

下一步/改进

接下来,我们可以探索更多的采样方法,如上采样、SMOTE、整体采样。我们还可以尝试使用不同的超参数,增加样本量,以进一步改善模型。

鸣谢!

我要感谢乔恩·克罗恩的书《深度学习图解》,感谢懒惰程序员公司的优秀课程《Tensorflow 2.0:深度学习和人工智能》,感谢何塞·波尔蒂利亚在 Udemy 上的精彩课程《完整的 Tensorflow 2 和 Keras 深度学习训练营》。

谢谢大家!

感谢大家的阅读,并请随时留下意见/建议。

快乐学习!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值