关于人工智能:NLP不要重新造轮子

42次阅读

共计 9983 个字符,预计需要花费 25 分钟才能阅读完成。

作者 |Abhijit Gupta
编译 |VK
起源 |Towards Data Science

介绍

自然语言解决(NLP)是一个令人生畏的畛域名称。从非结构化文本中生成有用的论断是很艰难的,而且有有数的技术和算法,每一种都有本人的用例和复杂性。作为一个接触 NLP 起码的开发人员,很难晓得要应用哪些办法以及如何实现它们。

如果我以最小的致力提供尽量完满的后果。应用 80/20 准则,我将向你展现如何在不显著就义后果(80%)的状况下疾速(20%)交付解决方案。

“80/20 准则认为,多数的起因、投入或致力通常导致大多数后果、产出或回报”

- 理查德·科赫,80/20 准则的作者

咱们将如何实现这一指标?有一些很棒的 Python 库!咱们可能站在伟人的肩膀上,迅速翻新,而不是从新创造轮子。通过事后测试的实现和预训练的模型,咱们将专一于利用这些办法并发明价值。

本文的指标读者是心愿将自然语言解决疾速集成到他们的我的项目中的开发人员。在强调易用性和疾速成果的同时,性能也会降落。依据我的教训,80% 的技术对于我的项目来说是足够的,然而也能够从其余中央寻找相干办法

不必多说了,咱们开始吧!


什么是 NLP?

自然语言解决是语言学、计算机科学和人工智能的一个分支畛域,容许通过软件主动解决文本。NLP 使机器可能浏览、了解和响应横七竖八的非结构化文本。

人们通常将 NLP 视为机器学习的一个子集,但理论状况更为奥妙。

有些 NLP 工具依赖于机器学习,有些甚至应用深度学习。然而,这些办法往往依赖于大数据集,并且难以实现。相同,咱们将专一于更简略、基于规定的办法来放慢开发周期。

术语

从最小的数据单位开始,字符是单个字母、数字或标点符号。一个单词是一个字符列表,一个句子是一个单词列表。文档是句子的列表,而语料库是文档的列表。

预处理

预处理可能是 NLP 我的项目中最重要的一步,它波及到清理输出,这样模型就能够疏忽噪声,并将注意力集中在最重要的内容上。一个弱小的预处理管道将进步所有模型的性能,所以必须强调它的价值。

以下是一些常见的预处理步骤:

  • 分段 :给定一长串字符,咱们能够用空格分隔文档,按句点分隔句子,按空格分隔单词。实现细节将因数据集而异。
  • 应用小写 :大写通常不会减少性能,并且会使字符串比拟更加艰难。所以把所有的货色都改成小写。
  • 删除标点 :咱们可能须要删除逗号、引号和其余不减少意义的标点。
  • 删除停用词 :停用词是像“she”、“the”和“of”这样的词,它们不会减少文本的含意,并且扩散对关键字的注意力。
  • 删除其余不相干单词 :依据你的应用程序,你可能心愿删除某些不相干的单词。例如,如果评估课程回顾,像“传授”和“课程”这样的词可能没有用。
  • 词干 / 词根化 :词干剖析和词根化都会生成词形变化单词的词根模式(例如:“running”到“run”)。词干提取速度更快,但不能保障词根是英语单词。词根化应用语料库来确保词根是一个单词,但代价是速度。
  • 词性标注 :词性标注以词性(名词、动词、介词)为根据,依据词义和语境来标记单词。例如,咱们能够专一于名词进行关键字提取。

无关这些概念的更全面的介绍,请查看以下指南:

https://towardsdatascience.co…

这些步骤是胜利的预处理的根底。依据数据集和工作的不同,你能够跳过某些步骤或增加新步骤。通过预处理手动察看数据,并在呈现问题时进行更正。


Python 库

让咱们来看看 NLP 的两个次要 Python 库。这些工具将在预处理期间,占据十分大的作用

NLTK

自然语言工具包是 Python 中应用最宽泛的 NLP 库。NLTK 是 UPenn 为学术目标而开发的,它有大量的特色和语料库。NLTK 非常适合解决数据和运行预处理:https://www.nltk.org/

NLTK 是构建 Python 程序以解决人类语言数据的当先平台。它提供了易于应用的 API

>>> import nltk

>>> sentence = "At eight o'clock on Thursday morning Arthur didn't feel very good."

>>> tokens = nltk.word_tokenize(sentence)
>>> tokens
['At', 'eight', "o'clock",'on','Thursday','morning','Arthur','did',"n't", 'feel', 'very', 'good', '.']

>>> tagged = nltk.pos_tag(tokens)
>>> tagged[0:6]
[('At', 'IN'), ('eight', 'CD'), ("o'clock",'JJ'), ('or','IN'), ('Thursday','NNP'), ('morning','NN')]

这是 NLTK 网站上的一个例子,它展现了标记句子和标记词性是如许简略。

SpaCy

SpaCy 是一个古代的的库。尽管 NLTK 对每个个性都有多个实现,然而 SpaCy 保留性能最好的实现。Spacy 反对多种性能,无关详细信息,请浏览文档:https://spacy.io/

只需几行代码,咱们就能够应用 SpaCy 执行命名实体辨认。应用 SpaCy api 能够疾速实现许多其余工作。

import spacy

nlp = spacy.load("en_core_web_sm")
text = ("When Sebastian Thrun started working on self-driving cars at"
        "Google in 2007, few people outside of the company took him seriously")
        
doc = nlp(text)
for entity in doc.ents:
    print(entity.text, entity.label_)

# 输入
# Sebastian Thrun
# 谷歌组织
# 2007 日期 

GenSim

与 NLTK 和 SpaCy 不同,GenSim 专门解决信息检索(IR)问题。GenSim 的开发重点是内存治理,它蕴含许多文档相似性模型,包含 Latent Semantic Indexing、Word2Vec 和 FastText:https://github.com/RaRe-Techn…

Gensim 是一个 Python 库,用于主题模型、文档索引和大型语料库的相似性检索:https://github.com/RaRe-Techn…

上面是一个事后训练的 GenSim Word2Vec 模型的例子,它能够发现单词的相似性。不必放心那些横七竖八的细节,咱们能够很快失去后果。

import gensim.downloader as api
wv = api.load("word2vec-google-news-300")

pairs = [('car', 'minivan'),    # 小型货车是一种汽车
    ('car', 'bicycle'),    # 也是有轮子的交通工具
    ('car', 'airplane'),   # 没有轮子,但依然是交通工具
    ('car', 'cereal'),     # ... 等等
    ('car', 'communism'),
]

for w1, w2 in pairs:
    print('%r\t%r\t%.2f % (w1, w2, wv.similarity(w1, w2)))
          
# 输入
# 'car'   'minivan'    0.69
# 'car'   'bicycle'    0.54
# 'car'   'airplane'   0.42
# 'car'   'cereal'     0.14
# 'car'   'communism'  0.06

还有更多…

这个列表并不全面,但涵盖了一些用例。我倡议查看这个存储库以获取更多的工具和参考:https://github.com/keon/aweso…


利用

既然咱们曾经探讨了预处理办法和 Python 库,让咱们用几个例子把它们放在一起。对于每种算法,我将介绍几个 NLP 算法,依据咱们的疾速开发指标抉择一个,并应用其中一个库创立一个简略的实现。

利用 1:预处理

预处理是任何 NLP 解决方案的要害局部,所以让咱们看看如何应用 Python 库来放慢处理速度。依据我的教训,NLTK 领有咱们所需的所有工具,并针对独特的用例进行定制。让咱们加载一个样本语料库:

import nltk

# 加载 brown 语料库
corpus = nltk.corpus.brown

# 拜访语料库的文件
print(corpus.fileids())

# 输入
['ca01', 'ca02', 'ca03', 'ca04', 'ca05', 'ca06', 'ca07', 'ca08', 'ca09', 'ca10', 'ca11', 'ca12', 'ca13', 'ca14', 'ca15', 'ca16',
 'ca17', 'ca18', 'ca19', 'ca20', 'ca21', 'ca22', 'ca23', 'ca24', 'ca25', 'ca26', 'ca27', 'ca28', 'ca29', 'ca30', 'ca31', 'ca32',
 'ca33', 'ca34', 'ca35', 'ca36', 'ca37', 'ca38', 'ca39', 'ca40', 'ca41', 'ca42', 'ca43', 'ca44', 'cb01', 'cb02', 'cb03', 'c...

依照下面定义的管道,咱们能够应用 NLTK 来实现分段、删除标点和停用词、执行词干化等等。看看删除停用词是如许容易:

from nltk.corpus import stopwords

sw = stopwords.words("english")
sw += ""  # 空字符串

def remove_sw(doc):
    sentences = []
    for sentence in doc:
        sentence = [word for word in sentence if word not in sw]
        sentences.append(sentence)
    return sentences

print("With Stopwords")
print(doc1[1])
print()

doc1 = remove_sw(doc1)

print("Without Stopwords")
print(doc1[1])

# 输入
# 有停用词
# ['the', 'jury', 'further', 'said', 'in', 'presentments', 'that', 'the', 'city', 'executive', 'committee', 'which', 'had',
# 'charge', 'of', 'the', 'election', 'deserves', 'the', 'praise', 'and', 'thanks', 'of', 'the', 'city', 'of', 'atlanta', 'for', 
# 'the', 'manner', 'in', 'which', 'the', 'election', 'was', 'conducted']

# 没有停用词
# ['jury', 'said', 'presentments', 'city', 'executive', 'committee', 'charge', 'election', 'deserves', 'praise', 'thanks', 'city',
# 'atlanta', 'manner', 'election', 'conducted']

整个预处理管道占用了我不到 40 行 Python。请参阅此处的残缺代码。记住,这是一个通用的示例,你应该依据你的特定用例的须要批改流程。

利用 2:文档聚类

文档聚类是自然语言解决中的一个常见工作,所以让咱们来探讨一些办法。这里的根本思维是为每个文档调配一个示意所探讨主题的向量:

如果向量是二维的,咱们能够像下面一样可视化文档。在这个例子中,咱们看到文档 A 和 B 是严密相干的,而 D 和 F 是涣散相干的。即便这些向量是 3 维、100 维或 1000 维,应用间隔度量的话,咱们也能够计算相似性。

下一个问题是如何应用非结构化文本输出为每个文档结构这些向量。这里有几个选项,从最简略到最简单的:

  • 词袋:为每个惟一的单词调配一个索引。给定文档的向量是每个单词呈现的频率。
  • TF-IDF:依据单词在其余文档中的常见水平来增强示意。如果两个文档共享一个罕见单词,则它们比共享一个公共单词更类似。
  • 潜在语义索引(LSI):词袋和 TF-IDF 能够创立高维向量,这使得间隔测量的准确性升高。LSI 将这些向量压缩到更易于治理的大小,同时最大限度地缩小信息损失。
  • Word2Vec:应用神经网络,从大型文本语料库中学习单词的关联关系。而后将每个单词的向量相加失去一个文档向量。
  • Doc2Vec:在 Word2Vec 的根底上构建,然而应用更好的办法从单词向量列表中近似文档向量。

Word2Vec 和 Doc2Vec 非常复杂,须要大量的数据集来学习单词嵌入。咱们能够应用预训练过的模型,但它们可能无奈很好地适应畛域内的工作。相同,咱们将应用词袋、TF-IDF 和 LSI。

当初抉择咱们的库。GenSim 是专门为这个工作而构建的,它蕴含所有三种算法的简略实现,所以让咱们应用 GenSim。

对于这个例子,让咱们再次应用 Brown 语料库。它有 15 个文本类别的文档,如“冒险”、“编辑”、“新闻”等。在运行咱们的 NLTK 预处理例程之后,咱们能够开始利用 GenSim 模型。

首先,咱们创立一个将标识映射到惟一索引的字典。

from gensim import corpora, models, similarities

dictionary = corpora.Dictionary(corpus)
dictionary.filter_n_most_frequent(1)  # removes ""
num_words = len(dictionary)
print(dictionary)
print()

print("Most Frequent Words")
top10 = sorted(dictionary.cfs.items(), key=lambda x: x[1], reverse=True)[:10]
for i, (id, freq) in enumerate(top10):
    print(i, freq, dictionary[id])
    
# 输入
# Dictionary(33663 unique tokens: ['1', '10', '125', '15th', '16']...)

# 频率最高的词
# 0 3473 one
# 1 2843 would
# 2 2778 say
# 3 2327 make
# 4 1916 time
# 5 1816 go
# 6 1777 could
# 7 1665 new
# 8 1659 year
# 9 1575 take

接下来,咱们迭代地利用词袋、TF-IDF 和潜在语义索引:

corpus_bow = [dictionary.doc2bow(doc) for doc in corpus]
print(len(corpus_bow[0]))
print(corpus_bow[0][:20])

# 输入
# 6106
# [(0, 1), (1, 3), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1), (8, 1), (9, 2), (10, 1), (11, 1), (12, 2), (13, 2), (14, 2), (15,
# 1), (16, 2), (17, 2), (18, 3), (19, 1)]

tfidf_model = models.TfidfModel(corpus_bow)
corpus_tfidf = tfidf_model[corpus_bow]
print(len(corpus_tfidf[0]))
print(corpus_tfidf[0][:20])

# 输入
# 5575
# [(0, 0.001040495879718581), (1, 0.0011016669638018743), (2, 0.002351365659027428), (3, 0.002351365659027428), (4, 
# 0.0013108697793088472), (5, 0.005170600993729588), (6, 0.003391861538746009), (7, 0.004130105114011007), (8, 
# 0.003391861538746009), (9, 0.008260210228022013), (10, 0.004130105114011007), (11, 0.001955787484706956), (12, 
# 0.0015918258736505996), (13, 0.0015918258736505996), (14, 0.008260210228022013), (15, 0.0013108697793088472), (16, 
# 0.0011452524080876978), (17, 0.002080991759437162), (18, 0.004839366251287288), (19, 0.0013108697793088472)]

lsi_model = models.LsiModel(corpus_tfidf, id2word=dictionary, num_topics=20)
corpus_lsi = lsi_model[corpus_tfidf]
print(len(corpus_lsi[0]))
print(corpus_lsi[0])

# 输入
# 15
# [(0, 0.18682238167974372), (1, -0.4437583954806601), (2, 0.22275580411969662), (3, 0.06534575527078117), (4, 
# -0.10021080420155845), (5, 0.06653745783577146), (6, 0.05025291839076259), (7, 0.7117552624193217), (8, -0.3768886513901333), (9, 
# 0.1650380936828472), (10, 0.13664364557932132), (11, -0.03947144082104315), (12, -0.03177275640769521), (13, 
# -0.00890543444745628), (14, -0.009715808633565214)]

在大概 10 行 Python 代码中,咱们解决了三个独立的模型,并为文档提取了向量示意。利用余弦类似度进行向量比拟,能够找到最类似的文档。

categories = ["adventure", "belles_lettres", "editorial", "fiction", "government", 
              "hobbies", "humor", "learned", "lore", "mystery", "news", "religion",
              "reviews", "romance", "science_fiction"]
num_categories = len(categories)


for i in range(3):
    print(categories[i])
    sims = index[lsi_model[corpus_bow[i]]]
    top3 = sorted(enumerate(sims), key=lambda x: x[1], reverse=True,)[1:4]
    for j, score in top3:
        print(score, categories[j])
    print()
    
# 输入
# adventure
# 0.22929086 fiction
# 0.20346783 romance
# 0.19324714 mystery

# belles_lettres
# 0.3659389 editorial
# 0.3413822 lore
# 0.33065677 news

# editorial
# 0.45590898 news
# 0.38146105 government
# 0.2897901 belles_lettres

就这样,咱们有后果了!冒险小说和浪漫小说最为类似,而社论则相似于新闻和政府。在这里查看残缺的代码:https://github.com/avgupta456…。

利用 3:情感剖析

情感剖析是将非结构化文本解释为侧面、负面或中性。情感剖析是剖析评论、掂量品牌、构建人工智能聊天机器人等的有用工具。

与文档聚类不同,在情感剖析中,咱们不应用预处理。段落的标点符号、流程和上下文能够揭示很多对于情绪的信息,所以咱们不想删除它们。

为了简略无效,我倡议应用基于模式的情感剖析。通过搜寻特定的关键词、句子构造和标点符号,这些模型测量文本的踊跃消极性。以下是两个带有内置情感分析器的库:

VADER 情感剖析:

VADER 是 Valence Aware Dictionary and sEntiment Recognizer 的缩写,是 NLTK 用于情感剖析的扩大。它应用模式来计算情绪,尤其实用于表情符号和短信俚语。它也非常容易实现。

from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer

analyzer = SentimentIntensityAnalyzer()

print(analyzer.polarity_scores("This class is my favorite!!!"))
print(analyzer.polarity_scores("I hate this class :("))

# 输入
# {'neg': 0.0, 'neu': 0.508, 'pos': 0.492, 'compound': 0.5962}
# {'neg': 0.688, 'neu': 0.312, 'pos': 0.0, 'compound': -0.765}
TextBlob 情感剖析:

一个相似的工具是用于情感剖析的 TextBlob。TextBlob 实际上是一个多功能的库,相似于 NLTK 和 SpaCy。在情感剖析工具上,它与 VADER 在报告情感极性和主观性方面都有所不同。从我集体的教训来看,我更喜爱 VADER,但每个人都有本人的短处和短处。TextBlob 也非常容易实现:

from textblob import TextBlob

testimonial = TextBlob("This class is my favorite!!!")
print(testimonial.sentiment)

testimonial = TextBlob("I hate this class :(")
print(testimonial.sentiment)

# 输入
# Sentiment(polarity=0.9765625, subjectivity=1.0)
# Sentiment(polarity=-0.775, subjectivity=0.95)

留神:基于模式的模型在下面的例子中不能很好地解决这样的小文本。我倡议对均匀四句话的文本进行情感剖析。要疾速演示这一点,请参阅 Jupyter Notebook:https://github.com/avgupta456…

其余利用

这里有几个附加的主题和一些有用的算法和工具来减速你的开发。

  • 关键词提取 :命名实体辨认(NER)应用 SpaCy,疾速主动关键字提取(RAKE)应用 ntlk-rake
  • 文本摘要 :TextRank(相似于 PageRank)应用 PyTextRank SpaCy 扩大,TF-IDF 应用 GenSim
  • 拼写查看 :PyEnchant,SymSpell Python 端口

心愿这些示例有助于演示 Python 中可用于自然语言解决的大量资源。不论问题是什么,有人开发了一个库来简化流程。应用这些库能够在短时间内产生很好的后果。


提醒和技巧

通过对 NLP 的介绍、Python 库的概述以及一些示例应用程序,你简直能够应答本人的挑战了。最初,我有一些技巧和技巧来充分利用这些资源。

  • Python 工具 :我举荐 Poetry 用于依赖关系治理,Jupyter Notebook 用于测试新模型,Black 和 / 或 Flake8 用于放弃代码格调,GitHub 用于版本治理。
  • 放弃条理 :从一个库跳到另一个库,复制代码到以后你编写的代码测试尽管很容易实现,然而不好。我倡议采取你采取适合的更谨慎的办法,因为你不想在匆忙中错过一个好的解决方案。
  • 预处理 :垃圾进,垃圾出。实现一个弱小的预处理管道来清理输出十分重要。目视查看解决后的文本,以确保所有内容都按预期工作。
  • 展现后果 :抉择如何展现你的后果会有很大的不同。如果输入的文本看起来有点毛糙,能够思考显示聚合统计信息或数值后果。

原文链接:https://towardsdatascience.co…

欢送关注磐创 AI 博客站:
http://panchuang.net/

sklearn 机器学习中文官网文档:
http://sklearn123.com/

欢送关注磐创博客资源汇总站:
http://docs.panchuang.net/

正文完
 0