自然语言处理实战第二版(MEAP)(一)(3)https://developer.aliyun.com/article/1517828
2.9.3 规范化你的词汇表
你已经看到了词汇表大小对 NLP 流程性能的重要性。另一种减少词汇表的技术是规范化您的词汇表,以便将意思类似的标记组合成单个规范化的形式。这样做会减少您需要在词汇表中保留的标记数量,同时还提高语料库中该标记或n-gram 的不同“拼写”之间的含义关联。正如我们之前提到的,减少词汇表可以减少过度拟合的
折叠大小写
折叠大小写是指合并只有区分大小写的拼写不同的单词。那为什么我们要使用折叠大小写呢?当单词由于出现在句子开头或以ALL CAPS
加粗书写时而被大写字母表示时,它们可能会变得不规范化。撤消这种不规范化称为大小写规范化,或更普遍地,折叠大小写。规范化单词和字符大小写是减小词汇表大小和泛化 NLP 流程的方法之一。它帮助您将旨在表示(并拼写)相同意思的单词合并为单个标记。
然而,有些信息经常通过单词的大写来传达——例如,“doctor”和“Doctor”通常具有不同的含义。通常,大写用于指示单词是专有名词,即人、地点或事物的名称。如果命名实体识别对您的流水线很重要,您将希望能够将专有名词与其他单词区分开来。然而,如果标记未进行大小写规范化,您的词汇量将大约增加一倍,消耗的内存和处理时间也会增加一倍,可能需要增加您需要为机器学习流水线标记的训练数据的数量,以便该流水线能够收敛到准确、普遍的解决方案。就像在任何其他机器学习流水线中一样,用于训练的标记数据集必须“代表”模型必须处理的所有可能特征向量的空间,包括大小写的变化。对于 100000 维词袋向量,通常必须有 100000 个带标签的示例,有时甚至更多,才能训练一个无过拟合的监督机器学习流水线。在某些情况下,将词汇量减少一半有时可能值得信息内容的损失。
在 Python 中,您可以使用列表推导式轻松规范化标记的大小写。
>>> tokens = ['House', 'Visitor', 'Center'] >>> normalized_tokens = [x.lower() for x in tokens] >>> print(normalized_tokens) ['house', 'visitor', 'center']
如果您确信要对整个文档进行大小写规范化,可以在标记化之前使用lower()
函数一次性将文本字符串转换为小写字母。但是,这将阻止能够分割驼峰大小写单词(如“WordPerfect”、“FedEx”或“stringVariableName”)的高级标记器。^([48]) 或许您希望 WordPerfect 成为自己独特的东西(标记),或者您希望怀念更完美的文字处理时代。您可以决定何时以及如何应用大小写折叠。
通过大小写规范化,您试图将这些标记返回到它们在语法规则和它们在句子中的位置影响其大写之前的“正常”状态。将文本字符串的大小写规范化为最简单和最常见的方法是使用诸如 Python 内置的str.lower()
之类的函数将所有字符都转换为小写字母。^([49]) 不幸的是,这种方法也会“规范化”掉许多有意义的大写字母,除了您打算规范化掉的不那么有意义的句子中的第一个单词的大写字母之外。对于大小写规范化,更好的方法是仅将句子的第一个单词转换为小写字母,然后让所有其他单词保留其大写字母。
在句子的第一个单词上进行小写处理可以保留句子中的专有名词的含义,例如在“Joe Smith”中的“Joe”和“Smith”。并且它可以正确地将属于一起的单词分组在一起,因为它们只有在句子开头才会大写,因为它们不是专有名词。这可以防止在标记化期间将“Joe”与“coffee”(“joe”)混淆。这种方法可以防止“smith”的铁匠含义与句子中的专有名词“Smith”混淆,例如在“A word smith had a cup of joe.”中。即使采用了对句子开头的单词进行小写处理的小心方法,您仍然需要为偶尔在句子开头的专有名词引入大小写错误。“Joe Smith, the word smith, with a cup of joe.”将产生与“Smith the word with a cup of joe, Joe Smith.”不同的标记集合。您可能不希望出现这种情况。此外,对于没有大写概念的语言(如阿拉伯语或印地语),大小写规范化是无用的。
为了避免这种潜在的信息丢失,许多自然语言处理流水线根本不对大小写进行规范化。对于许多应用程序,将词汇量减半的效率收益(在存储和处理方面)被由于专有名词信息的丢失而抵消了。但是即使不进行大小写规范化,也可能会“丢失”一些信息。如果您没有将句子开头的单词“The”识别为停止词,对于某些应用程序来说这可能是个问题。真正复杂的流水线将在有明确不是专有名词的句子开头的词的情况下先检测专有名词,然后选择性地对这些词进行规范化。您应该实施对您的应用程序有意义的任何大小写规范化方法。如果您的语料库中没有很多“Smith”和“word smiths”,并且您不在乎它们是否被分配给相同的标记,您可以将所有内容都转换为小写。找出有效方法的最佳方式是尝试几种不同的方法,看看哪种方法对您自然语言处理项目的目标性能最佳。
将模型推广到处理具有奇怪大写形式的文本时,大小写规范化可以减少机器学习流水线的过拟合。大小写规范化对于搜索引擎特别有用。对于搜索来说,规范化可以增加对特定查询找到的匹配项数量。这通常被称为搜索引擎(或任何其他分类模型)的“召回率”性能指标。^([51])
对于一个没有规范化的搜索引擎,如果你搜索“Age”,你将得到与如果你搜索“age”时不同的文档集合。 “Age”很可能出现在诸如“New Age”或“Age of Reason”之类的短语中。相比之下,“age”更可能出现在你有关托马斯·杰斐逊的句子中的“在…岁时”的短语中。通过在搜索索引(以及查询)中对词汇进行规范化,您可以确保无论用户查询的大小写如何,都会返回关于“age”的这两种类型的文档。
然而,这种额外的召回准确性是以精度的代价为代价的,会返回许多用户不感兴趣的文档。由于这个问题,现代搜索引擎允许用户关闭每个查询的规范化,通常通过引用那些他们只希望返回精确匹配的单词来实现。如果您正在构建这样的搜索引擎管道,为了适应两种类型的查询,您将需要为您的文档构建两个索引:一个具有规范化的n-grams,另一个具有原始大小写。
词干处理
另一种常见的词汇规范化技术是消除词汇的复数或所有格结尾甚至各种动词形式的细微含义差异,即识别单词的各种形式之间的共同词干,称为词干处理。例如,单词housing
和houses
共享相同的词干house
。词干处理尝试从单词中去除后缀,以便将具有相似含义的单词组合在其共同的词干下。词干不需要是一个正确拼写的单词,而仅仅是一个标记或标签,代表单词的几种可能拼写。
人类可以很容易地看出“house”和“houses”是同一名词的单数和复数形式。然而,您需要一些方法将此信息提供给机器。其中一个主要好处是压缩您的软件或语言模型需要跟踪意义的单词数量。它在尽可能限制信息和含义损失的同时缩小了您的词汇量。在机器学习中,这被称为降维。它有助于概括您的语言模型,使该模型能够对包括在词干中的所有单词保持相同的行为。因此,只要您的应用程序不要求机器区分“house”和“houses”,此词干将减少您的编程或数据集大小一半或更多,具体取决于您选择的词干处理器的强度。
词干提取对于关键词搜索或信息检索非常重要。它允许你搜索“在波特兰开发房屋”,并获得同时使用“house”和“houses”甚至“housing”这些词的网页或文档,因为这些词都被词干提取为“hous”标记。同样,你可能会收到包含“developer”和“development”而不是“developing”的页面,因为所有这些词通常缩减到“develop”这个词干。正如你所见,这是对你搜索的“扩展”,确保你不太可能错过相关文档或网页。这种搜索结果的扩展将是搜索引擎“召回”分数的重大改进,表明你的搜索引擎在返回所有相关文档方面的工作效果如何。^([52])
但是,词干提取可能会大大降低搜索引擎的“精确度”分数,因为它可能会返回许多与相关文档一起的无关文档。在某些应用中,这种“误报率”(返回的页面中你不认为有用的比例)可能是一个问题。因此,大多数搜索引擎允许你通过在单词或短语周围加上引号来关闭词干提取甚至大小写规范化。引号表示你只想要包含完全拼写相同的短语的页面,比如“‘波特兰房地产开发软件’”。这将返回一种不同类型的文档,而不是那些谈论“‘波特兰软件开发者的房子’”的文档。有时你想搜索“Dr. House’s calls”,而不是“dr house call”,如果你在该查询上使用了一个词干提取器,后者可能是有效的查询。
这是一个在纯 Python 中实现的简单词干提取器,可以处理末尾的 s
。
>>> def stem(phrase): ... return ' '.join([re.findall('^(.*ss|.*?)(s)?$', ... word)[0][0].strip("'") for word in phrase.lower().split()]) >>> stem('houses') 'house' >>> stem("Doctor House's calls") 'doctor house call'
前面的词干提取器函数遵循了一个简单的规则,就在那一个简短的正则表达式中:
- 如果一个单词以多于一个
s
结尾,那么词干就是该词,后缀为空字符串。 - 如果一个单词以单个
s
结尾,那么词干就是不带s
的该词,后缀是s
。 - 如果一个单词不以
s
结尾,那么词干就是该词,不返回后缀。
strip 方法确保一些所有格词能够与复数形式一起被词干提取。
这个函数对于常规情况效果很好,但无法解决更复杂的情况。例如,这些规则在诸如 dishes
或 heroes
之类的单词上会失败。对于这种更复杂的情况,NLTK 包提供了其他的词干提取器。
它也不能处理你“波特兰房地产”搜索中的“housing”示例。
最流行的两种词干提取算法是波特和 Snowball 词干提取器。波特词干提取器以计算机科学家马丁·波特的名字命名,他在 80 年代和 90 年代大部分时间里不断调整这个硬编码算法。^([53]) 波特还负责改进波特词干提取器以创建 Snowball 词干提取器。^([54]) 波特在他漫长的职业生涯中致力于记录和改进词干提取器,因为它们在信息检索(关键词搜索)中很有价值。这些词干提取器实现了比我们简单的正则表达式更复杂的规则。这使得词干提取器能够处理英语拼写和词尾规则的复杂性。
>>> from nltk.stem.porter import PorterStemmer >>> stemmer = PorterStemmer() >>> ' '.join([stemmer.stem(w).strip("'") for w in ... "dish washer's fairly washed dishes".split()]) 'dish washer fairli wash dish'
注意,波特词干提取器与正则表达式词干提取器一样,保留了尾随的撇号(除非你明确删除它),这确保了所有格词与非所有格词之间的区分。所有格词通常是专有名词,因此这个特性在你想要将名称与其他名词区分对待的应用程序中可能很重要。
更多关于波特词干提取器的内容
Julia Menchavez 慷慨地分享了她对波特原始词干提取器算法的纯 Python 翻译(github.com/jedijulia/porter-stemmer/blob/master/stemmer.py
)。如果你曾经想过开发自己的词干提取器,请考虑这 300 行代码以及波特花在它们身上的精益求精的一生。
波特词干提取算法有八个步骤:1a、1b、1c、2、3、4、5a 和 5b。步骤 1a 有点像处理尾随“s”的正则表达式:^([55])
def step1a(self, word): if word.endswith('sses'): word = self.replace(word, 'sses', 'ss') # #1 elif word.endswith('ies'): word = self.replace(word, 'ies', 'i') elif word.endswith('ss'): word = self.replace(word, 'ss', 'ss') elif word.endswith('s'): word = self.replace(word, 's', '') return word
剩下的七个步骤要复杂得多,因为它们必须处理以下复杂的英语拼写规则:
- 步骤 1a:以“s”和“es”结尾
- 步骤 1b:以“ed”,“ing”和“at”结尾
- 步骤 1c:以“y”结尾
- 步骤 2:使名词化的结尾,比如“ational”,“tional”,“ence”和“able”
- 步骤 3:形容词结尾,比如“icate”,^([56]),“ful”和“alize”
- 步骤 4:形容词和名词结尾,比如“ive”,“ible”,“ent”和“ism”
- 步骤 5a:顽固的“e”结尾,仍然存在
- 步骤 5b:尾随的双辅音,词干将以单个“l”结尾
Snowball 词干提取器比波特词干提取器更具侵略性。注意它将“fairly”词干提取为“fair”,这比波特词干提取器更准确。
>>> from nltk.stem.snowball import SnowballStemmer >>> stemmer = SnowballStemmer(language='english') >>> ' '.join([stemmer.stem(w).strip("'") for w in ... "dish washer's fairly washed dishes".split()]) 'dish washer fair wash dish'
词形归一化
如果你有关于各种单词含义之间的联系的信息,你可能能够将几个单词关联起来,即使它们的拼写非常不同。这种更广泛的归一化到一个单词的语义根 - 它的词元 - 被称为词形归一化。
在第十二章中,我们展示了如何使用词形归一化来减少响应聊天机器人声明所需的逻辑复杂性。任何想要对相同基本根词的多种不同拼写“反应”相同的 NLP 流水线都可以从词形归一化器中受益。它减少了您必须回应的单词数量,您的语言模型的维度。使用它可以使您的模型更通用,但也可能使您的模型不太精确,因为它将给定根词的所有拼写变体都视为相同。例如,在具有词形归一化的 NLP 流水线中,“chat”、“chatter”、“chatty”、“chatting”甚至可能是“chatbot”都将被视为相同,即使它们有不同的含义。同样,“bank”、“banked”和“banking”在词干提取流水线中也将被视为相同,尽管“bank”的含义是“河岸”,“banked”的含义是“机车”,“banking”的含义是“金融”。
当您通过本节时,请考虑在词形归一化会大大改变单词含义的情况下,甚至颠倒其含义并从您的流水线产生相反的预期响应。这种情况被称为欺骗 - 当您试图通过巧妙构造一个困难的输入来引诱机器学习管道产生错误的响应时。
有时,词形归一化可能是规范化词汇的更好方式。您可能会发现,对于您的应用程序来说,词干提取和大小写转换会创建不考虑单词含义的词干和标记。词形归一化器使用单词同义词和词尾的知识库,以确保只有意思相似的单词被合并成一个单一的标记。
一些词形归一化器除了拼写外,还使用单词的词性(POS)标记来帮助提高准确性。单词的词性标记表示其在短语或句子语法中的作用。例如,名词 POS 是指短语中指代“人、地方或物品”的单词。形容词 POS 是用于修饰或描述名词的单词。动词指的是一种动作。无法确定单词在孤立状态下的 POS。必须了解单词的上下文才能确定其 POS。因此,一些高级词形归一化器不能在孤立的单词上运行。
你能想到利用词性来识别比词干提取更好的单词“根”吗?考虑单词better
。词干提取器会从better
中剥离“er”结尾,并返回词干“bett”或“bet”。然而,这会将better
与“betting”、“bets”和“Bet’s”等单词一起,而不是与更相似的单词如“betterment”、“best”或甚至“good”和“goods”一起。
因此,对于大多数应用程序来说,词形还原器比词干提取器更好。词干提取器仅在大规模信息检索应用程序(关键字搜索)中真正有用。如果你真的想要在信息检索管道中获得词干提取器的降维和召回率提升效果,你应该在词干提取器之前使用词形还原器。因为单词的词形是一个有效的英文单词,所以词干提取器在词形还原器的输出上表现良好。这个技巧将比仅使用词干提取器进一步降低你的维度,并增加你的信息检索召回率。^([57])
如何在 Python 中识别单词的词形还原?NLTK 包提供了相应的功能。请注意,如果想找到最准确的词形还原,你必须告诉 WordNetLemmatizer 你感兴趣的词性:
>>> nltk.download('wordnet') True >>> nltk.download('omw-1.4') True >>> from nltk.stem import WordNetLemmatizer >>> lemmatizer = WordNetLemmatizer() >>> lemmatizer.lemmatize("better") # #1 'better' >>> lemmatizer.lemmatize("better", pos="a") # #2 'good' >>> lemmatizer.lemmatize("good", pos="a") 'good' >>> lemmatizer.lemmatize("goods", pos="a") 'goods' >>> lemmatizer.lemmatize("goods", pos="n") 'good' >>> lemmatizer.lemmatize("goodness", pos="n") 'goodness' >>> lemmatizer.lemmatize("best", pos="a") 'best'
你可能会感到惊讶,第一次尝试对单词"better"进行词形还原时并没有改变它。这是因为单词的词性对其含义有很大影响。如果没有为单词指定词性,则 NLTK 词形还原器会假定它是一个名词。一旦指定了正确的词性,比如用 ‘a’ 表示形容词,词形还原器就会返回正确的词形。不幸的是,NLTK 词形还原器受限于普林斯顿 WordNet 单词含义图中的连接。因此,单词"best"并不会被还原为与"better"相同的词根。这个图表还缺少"goodness"和"good"之间的连接。另一方面,波特词干提取器会通过盲目地剥离所有单词的"ness"结尾来建立这种连接。
>>> stemmer.stem('goodness') 'good'
你可以通过以下方式轻松地在 spaCy 中实现词形还原:
>>> import spacy >>> nlp = spacy.load("en_core_web_sm") >>> doc = nlp("better good goods goodness best") >>> for token in doc: >>> print(token.text, token.lemma_) better well good good goods good goodness goodness best good
与 NLTK 不同,spaCy 通过假设"better"是一个副词将其词形还原为"well",并为"best"(“good”)返回了正确的词形。
同义词替换
有五种类型的 “同义词”,有时有助于创建一个一致的更小词汇表,以帮助你的自然语言处理管道良好地概括。
- 打字错误纠正
- 拼写纠正
- 同义词替换
- 缩写扩展
- 表情符号扩展
这些同义词替换算法中的每一个都可以设计得更具侵略性或更加温和。你需要考虑你领域用户使用的语言。例如,在法律、技术或医学领域,很少有替换同义词的好主意。医生不会希望一个聊天机器人告诉他的病人,他们的"heart is broken"是因为一些对心脏表情符号 (“❤️”) 的同义词替换。
尽管如此,词形还原和词干提取的使用案例适用于同义词替换。
使用案例
何时使用词形还原器、词干处理器或同义词替换?词干处理器通常计算速度更快,需要的代码和数据集更简单。但是,词干处理器会产生更多错误并对更多单词进行词干处理,比词形还原器更多地降低了文本的信息内容或含义。词干处理器和词形还原器都会减少您的词汇量并增加文本的歧义性。但是,词形还原器会更好地根据单词在文本中的使用方式和其预期含义保留尽可能多的信息内容。因此,一些最先进的自然语言处理包,如 spaCy,不提供词干处理功能,而只提供词形还原方法。
如果您的应用涉及搜索,词干处理和词形还原将通过将更多文档与相同的查询词相关联来提高搜索的召回率。但是,词干处理、词形还原甚至大小写折叠通常会降低搜索结果的精度和准确性。这些词汇压缩方法可能导致您的信息检索系统(搜索引擎)返回许多与原始含义不相关的文档。这些被称为“假阳性”,是对您的搜索查询的错误匹配。有时“假阳性”比假阴性更不重要。对于搜索引擎来说,假阴性是指它根本没有列出您要查找的文档。
因为搜索结果可以根据相关性进行排名,搜索引擎和文档索引通常在处理您的查询和索引文档时使用词形还原。因为搜索结果可以根据相关性进行排名,搜索引擎和文档索引通常在其自然语言处理管道中使用词形还原。这意味着搜索引擎在对您的搜索文本进行分词和对其抓取的网页等文档集进行索引时都会使用词形还原。
但是,它们会合并单词的未经词干处理的版本的搜索结果,以对向您呈现的搜索结果进行排名。
对于基于搜索的聊天机器人,精确度通常比召回率更重要。一个错误的正向匹配可能导致您的聊天机器人说出不恰当的话。假阴性只会导致您的聊天机器人不得不谦虚地承认它找不到合适的话可说。如果您的自然语言处理流水线首先使用未词干化、未归一化的词搜索用户问题的匹配,您的聊天机器人会听起来更好。如果找不到其他内容,您的搜索算法可以退回到标准化的令牌匹配。您甚至可以通过引入一些警告来降低标准化令牌匹配的排名,从而使您的机器人谦卑透明,比如“我以前没听过这样的话,但使用我的词干器,我找到了……”。在一个充斥着吹牛的聊天机器人的现代世界里,您谦虚的聊天机器人可以立足并获胜!
有 4 种情况适合进行某种形式的同义词替换。
- 搜索引擎
- 数据增强
- 评估您的自然语言处理的健壮性
- 对抗性自然语言处理
搜索引擎可以通过使用同义词替换来提高对罕见术语的召回率。当您有限的标记数据时,您通常可以仅通过同义词替换将数据集扩展 10 倍(10x)。如果您想找到模型准确度的下限,您可以在测试集中大量地使用同义词替换,以查看您的模型对这些变化的健壮性。如果您正在寻找毒害或规避自然语言处理算法的方法,同义词可以为您提供大量的探测文本尝试。您可以想象,将“货币”替换为“现金”、“美元”或“”可能有助于逃避垃圾邮件检测器。
重要
最重要的是,尽量避免使用词干提取、词形还原、大小写转换或同义词替换,除非您有一定数量的文本,其中包含您感兴趣的单词的用法和大写。随着自然语言处理数据集的爆炸式增长,这在英语文档中很少见,除非您的文档使用了大量行话或来自科学、技术或文学的非常小的子领域。尽管如此,对于英语以外的语言,您可能仍然会发现词形还原的用途。斯坦福信息检索课程完全忽略了词干提取和词形还原,因为召回率的几乎不可感知的提高和精确度的显著降低。
情感
无论您在自然语言处理流水线中使用原始的单词标记、n-gram、词干还是词形还原,每个标记都包含一些信息。这些信息中的重要部分是单词的情感 - 单词所引起的整体感觉或情绪。这种情感分析 - 测量短语或文本块的情感 - 是自然语言处理的常见应用。在许多公司中,这是自然语言处理工程师被要求做的主要事情。
公司喜欢知道用户对他们的产品有什么看法。所以他们经常会提供某种方式让你提供反馈。亚马逊或烂番茄上的星级评价是获取有关人们对他们购买的产品感觉的定量数据的一种方式。但更自然的方式是使用自然语言评论。给你的用户一个空白的画布(一个空的文本框)来填写关于你的产品的评论可以产生更详细的反馈。
在过去,你必须阅读所有这些反馈。只有人类才能理解自然语言文本中的情感和情绪,对吧?然而,如果你不得不阅读数千条评论,你会看到人类读者可以是多么单调和容易出错。人类在阅读反馈时表现得非常糟糕,尤其是批评或负面反馈。而且顾客通常并不擅长以一种可以突破你的自然人类触发器和过滤器的方式传达反馈。
但是机器没有这些偏见和情感触发器。而且人类并不是唯一能够处理自然语言文本并从中提取信息,甚至理解意义的事物。一个自然语言处理(NLP)管道可以快速客观地处理大量用户反馈,减少偏见的机会。而且一个 NLP 管道可以输出文本的积极性或消极性或任何其他情感质量的数字评分。
情感分析的另一个常见应用是垃圾邮件和恶意留言过滤。你希望你的聊天机器人能够测量它处理的聊天消息中的情感,以便能够适当地回应。更重要的是,你希望你的聊天机器人能够测量它即将发送的陈述的情感,你可以用来引导你的机器人以友善和亲社会的方式进行陈述。做到这一点的最简单方法可能是做妈妈告诉我们要做的事情:如果你说不出什么好的话,就什么都不要说。所以你需要你的机器人测量你即将说的每件事情的好坏,并使用它来决定是否回应。
你会创建什么样的管道来测量一段文本的情感并产生这个情感积极性数字?比如说你只想测量一段文本的积极性或对一个他们所写的产品或服务的喜爱程度。比如说你希望你的 NLP 管道和情感分析算法输出一个-1 到+1 之间的单个浮点数。你的算法将为像“绝对完美!喜欢!😃 😃 😃”这样具有积极情感的文本输出+1。而且你的算法应该为像“可怕!完全没用。😦"这样具有负面情感的文本输出-1。你的 NLP 管道可以使用接近 0 的值,比如说+0.1,对于像“还行吧。有些好的和一些坏的事情”这样的陈述。
情感分析有两种方法:
- 由人类组成的基于规则的算法
- 通过机器学习从数据中学习的机器学习模型
第一种情感分析方法使用人为设计的规则,有时称为启发式方法,来衡量情感。情感分析的一种常见基于规则的方法是在文本中查找关键词,并将每个关键词映射到字典或“映射”中的数字分数或权重——例如一个 Python dict
。现在您知道如何进行标记化,您可以在字典中使用词干、词形还原或 n-gram 标记,而不仅仅是单词。算法中的“规则”是将这些分数相加,以找到在情感分数字典中可以找到的每个关键词在文档中的分数。当然,在您可以对一篇文本运行此算法之前,您需要手动组合这个关键词和它们的情感分数的字典。我们将在即将到来的列表中向您展示如何使用 VADER 算法(在 sklearn
中)来完成这一点。
第二种方法是机器学习,它依赖于一组带标签的陈述或文档来训练机器学习模型来创建这些规则。机器学习情感模型经过训练,以处理输入文本并输出您尝试测量的情感的数值,比如积极性、垃圾信息或恶意信息。对于机器学习方法,您需要大量的数据,带有“正确”情感分数的文本。Twitter feeds 通常用于此方法,因为标签,比如 \#awesome
或 \#happy
或 \#sarcasm
,通常可用于创建“自标记”数据集。您的公司可能有与评论者评论相关联的五星评价的产品评论。您可以使用星级评分作为每个文本积极性的数值分数。我们将很快向您展示如何处理此类数据集,并在完成 VADER 后训练一种基于令牌的机器学习算法,称为 Naive Bayes,以测量一组评论中情感的积极性。
2.10.1 VADER — 一种基于规则的情感分析器
在 GA Tech,Hutto 和 Gilbert 提出了最早成功的基于规则的情感分析算法之一。他们将他们的算法称为 VADER,代表 Valence Aware Dictionary for sEntiment Reasoning。^([61]) 许多 NLP 软件包实现了此算法的某种形式。NLTK 软件包中有一个 VADER 算法的实现,位于 nltk.sentiment.vader
中。Hutto 本人维护着 Python 软件包 vaderSentiment
。您将直接使用 vaderSentiment
。
您需要执行 pip install vaderSentiment
来运行下面的示例。^([62]) 您没有将其包含在 nlpia
包中。
>>> from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer >>> sa = SentimentIntensityAnalyzer() >>> sa.lexicon # #1 { ... ':(': -1.9, # #2 ':)': 2.0, ... 'pls': 0.3, # #3 'plz': 0.3, ... 'great': 3.1, ... } >>> [(tok, score) for tok, score in sa.lexicon.items() ... if " " in tok] # #4 [("( '}{' )", 1.6), ("can't stand", -2.0), ('fed up', -1.8), ('screwed up', -1.5)] >>> sa.polarity_scores(text=\ ... "Python is very readable and it's great for NLP.") {'compound': 0.6249, 'neg': 0.0, 'neu': 0.661, 'pos': 0.339} # #5 >>> sa.polarity_scores(text=\ ... "Python is not a bad choice for most applications.") {'compound': 0.431, 'neg': 0.0, 'neu': 0.711, 'pos': 0.289} # #6
让我们看看这种基于规则的方法在前面提到的示例陈述中的表现如何。
>>> corpus = ["Absolutely perfect! Love it! :-) :-) :-)", ... "Horrible! Completely useless. :(", ... "It was OK. Some good and some bad things."] >>> for doc in corpus: ... scores = sa.polarity_scores(doc) ... print('{:+}: {}'.format(scores['compound'], doc)) +0.9428: Absolutely perfect! Love it! :-) :-) :-) -0.8768: Horrible! Completely useless. :( -0.1531: It was OK. Some good and some bad things.
这看起来很像你想要的。所以唯一的缺点是 VADER 并不看文档中的所有单词。VADER 只"知道"算法中硬编码的大约 7500 个单词。如果你希望所有的单词都有助于增加情感分数,该怎么办?如果你不想编写自己对单词的理解或向SentimentIntensityAnalyzer.lexicon
字典中添加大量自定义单词,基于规则的方法可能是不可能的!因为你不了解语言,所以你不会知道要在字典(词典)中放入什么样的分数!
这就是机器学习情感分析器的用途。
2.10.2 向量的相似度
为什么我们使用词袋而不是字符袋来表示自然语言文本?对于试图解密未知消息的密码学家来说,对文本中字符的频率进行分析是一个好方法。但对于你的母语自然语言文本来说,词会是一个更好的表示。如果你考虑一下我们使用这些词袋向量的用途,你就会明白这一点。
如果你想一想,你有很多不同的方法来衡量事物的相似度。你可能对什么是亲密的家庭成员有很好的感觉。或者你可以见朋友合作写一本关于人工智能的书的咖啡厅的亲密度。对于咖啡厅,你的大脑可能会使用你所知道的咖啡厅的 2D 位置上的欧几里得距离。或者曼哈顿或出租车距离。
但是你知道如何衡量两段文字的相似度吗?在第四章中,你将学习到编辑距离,它检查两个字符串的相似性。但这并不能真正捕捉到你关心的本质。
在你的脑海中,这些句子彼此有多接近?
我现在要过来见你了。
我不会过来见你。
你看出区别了吗?你更愿意从朋友那里收到一封电子邮件。"现在"和"不"这两个词在意义上相差很远。但在拼写上却很接近。这是一个关于单个字符如何改变整个句子意义的例子。
如果你只是简单地数一下不同的字符,你会得到一个距离为 1。然后你可以除以最长句子的长度,以确保你的距离值在 0 到 1 之间。所以你的字符差异或距离计算将会是 1 除以 32,得到 0.03125,约为 3%。然后,要将距离转换为相似度,你只需将其从 1 中减去。那么你认为这两句话是 0.96875,约为 97% 相同吗?它们的意思相反。所以我们希望有一个更好的衡量方法。
如果你比较单词而不是字符呢?在这种情况下,你会有七个词中的一个被改变。这比 32 个字符中的一个要好一点。现在句子的接近度得分是六除以七,约为 85%。这有点低,但这是我们想要的。
对于自然语言,您不希望您的接近或距离度量仅依赖于单个字符的差异计数。这就是在处理自然语言文本时要使用单词作为您的含义令牌的原因之一。
这两个句子呢?
她和我会在 3:00 来到你的地方。
在 3:00,她和我会顺道去你的公寓。
这两个句子的意思接近吗?它们在字符长度上完全相同。它们使用了一些相同的单词,或者至少是同义词。但这些单词和字符的顺序不同。因此,我们需要确保我们对句子的表示不依赖于单词在句子中的精确位置。
词袋向量通过为词汇表中的每个单词创建一个位置或槽位来实现这一点。您可能已经了解了几个在几何和线性代数中衡量接近度的方法。
作为说明为什么从文本中提取特征很困难的例子,请考虑词干提取——将一个词的各种变形分组到同一个“桶”或簇中。非常聪明的人花了他们的职业生涯开发算法,根据拼写将单词的各种屈折形式分组在一起。想象一下这有多困难。想象一下试图从“ending”中移除动词结尾的"ing",这样你就会有一个称为“end”的词干来表示这两个单词。你想将单词“running”提取为“run”,这样这两个单词就被视为相同了。这有点棘手,因为你不仅要去掉“ing”,还要去掉额外的“n”。但是你希望单词“sing”保持完整。你不想从“sing”中去掉“ing”结尾,否则你会得到一个单字母“s”。
或者想象一下试图区分词尾为“s”的单词(如“words”)和单词“bus”和“lens”末尾的正常“s”。一个词中孤立的个别字母或单词部分是否提供任何关于该单词含义的信息?这些字母可能会误导吗?是的,都可以。
在本章中,我们向您展示如何通过使用传统的词干处理方法来解决这些词汇拼写挑战,使您的自然语言处理流水线变得更加智能。稍后,在第五章中,我们将向您展示只需要收集包含您感兴趣的单词的自然语言文本的统计聚类方法。从该文本集合中,单词使用的统计数据将揭示“语义词干”(实际上,更有用的词簇,如词元或同义词),而无需任何手工制作的正则表达式或词干提取规则。
2.10.3 计数向量化
在前面的章节中,您只关心关键字检测。您的向量指示了单词的存在或不存在。为了处理更长的文档并提高您的 NLP 管道的准确性,您将开始计算文档中单词的出现次数。
您可以将这些计数放入一种柱状图中。就像以前一样,您将为管道中的每个文档创建一个向量。只是在您的向量中,不再是 0 和 1,而是计数。这将提高您使用这些计数进行的所有相似度和距离计算的准确性。就像归一化直方图可以提高您比较两个直方图的能力一样,归一化您的单词计数也是一个好主意。否则,一个真正短的维基百科文章,其中只有一次使用了巴拉克·奥巴马的名字,与所有其他总统并列,可能会得到与一个关于巴拉克·奥巴马的长页相同数量的“巴拉克·奥巴马”信用。试图回答关于奥巴马的问题的用户和问答机器人(如qary
)可能会被列出所有总统的页面所分散,可能会完全错过主要的巴拉克·奥巴马页面。因此,通过将计数除以文档的总长度来归一化您的计数向量是一个好主意。这更公平地表示了文档中标记的分布,并将与其他文档,包括来自qary
的搜索查询文本,创建更好的相似性分数。
您向量中的每个位置都代表一个关键词的计数。拥有一个较小的词汇表可以使得这个向量保持小巧、低维度且易于理解。即使对于大词汇表,您也可以使用这种计数向量化方法。
您需要将这些关键字的计数组织成一个向量。这为进行向量代数打开了一整套强大的工具。
在自然语言处理中,从文本中构建数值向量是一个特别“损失”的特征提取过程。尽管如此,词袋(BOW)向量保留了足够的文本信息内容,以生成有用且有趣的机器学习模型。本章末尾的情感分析技术与谷歌用于拯救被大量垃圾邮件淹没的电子邮件的技术完全相同,这些技术几乎使其无用。
2.10.4 朴素贝叶斯
朴素贝叶斯模型试图找到一组文档中预测目标(输出)变量的关键词。当您的目标变量是您试图预测的情感时,模型将找到预测该情感的词语。朴素贝叶斯模型的好处在于,内部系数将单词或标记映射到分数,就像 VADER 一样。只是这一次,您不必局限于个人决定这些分数应该是什么。机器将为任何问题找到“最佳”分数。
对于任何机器学习算法,你首先需要找到一个数据集。你需要一堆具有其正面情感内容(积极情感)标签的文本文档。Hutto 在他和他的合作者构建 VADER 时为我们编制了四个不同的情感数据集。你将从nlpia
包中加载它们。^([64])
>>> movies = pd.read_csv('https://proai.org/movie-reviews.csv.gz', ... index_col=0) >>> movies.head().round(2) sentiment text id 1 2.27 The Rock is destined to be the 21st Century's ... 2 3.53 The gorgeously elaborate continuation of ''The... 3 -0.60 Effective but too tepid biopic 4 1.47 If you sometimes like to go to the movies to h... 5 1.73 Emerges as something rare, an issue movie that... >>> movies.describe().round(2) sentiment count 10605.00 mean 0.00 # #1 std 1.92 min -3.88 # #2 ... max 3.94 # #3
看起来电影评论已经被居中处理:通过减去均值来标准化,使新的均值为零,它们不会偏向于一侧或另一侧。并且电影评分的范围似乎是-4 到+4。
现在,你可以对所有这些电影评论文本进行标记化,以创建每个文本的词袋。如果你将它们都放入 Pandas DataFrame 中,那将更容易处理。
>>> import pandas as pd >>> pd.options.display.width = 75 # #1 >>> from nltk.tokenize import casual_tokenize # #2 >>> bows = [] >>> from collections import Counter # #3 >>> for text in movies.text: ... bows.append(Counter(casual_tokenize(text))) >>> df_movies = pd.DataFrame.from_records(bows) # #4 >>> df_movies = df_movies.fillna(0).astype(int) # #5 >>> df_movies.shape # #6 (10605, 20756) >>> df_movies.head() ! " # $ % & ' ... zone zoning zzzzzzzzz ½ élan – ’ 0 0 0 0 0 0 0 4 ... 0 0 0 0 0 0 0 1 0 0 0 0 0 0 4 ... 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 >>> df_movies.head()[list(bows[0].keys())] The Rock is destined to be ... Van Damme or Steven Segal . 0 1 1 1 1 2 1 ... 1 1 1 1 1 1 1 2 0 1 0 0 0 ... 0 0 0 0 0 4 2 0 0 0 0 0 0 ... 0 0 0 0 0 0 3 0 0 1 0 4 0 ... 0 0 0 0 0 1 4 0 0 0 0 0 0 ... 0 0 0 0 0 1 [5 rows x 33 columns]
当你不使用大小写规范化、停用词过滤器、词干提取或词形还原时,你的词汇表可能会非常庞大,因为你要跟踪单词拼写或大小写的每一个小差异。尝试在你的管道中插入一些降维步骤,看看它们如何影响你的管道的准确性以及存储所有这些 BOW 所需的内存量。
现在,你拥有了朴素贝叶斯模型需要的所有数据,以从自然语言文本中找到预测情感的关键字。
>>> from sklearn.naive_bayes import MultinomialNB >>> nb = MultinomialNB() >>> nb = nb.fit(df_movies, movies.sentiment > 0) # #1 >>> movies['pred_senti'] = ( ... nb.predict_proba(df_movies))[:, 1] * 8 - 4 # #2 >>> movies['error'] = movies.pred_senti - movies.sentiment >>> mae = movies['error'].abs().mean().round(1) # #3 >>> mae 1.9
要创建一个二元分类标签,你可以使用居中处理的电影评分(情感标签),当评论的情感为正面时,评分为正(大于零)。
>>> movies['senti_ispos'] = (movies['sentiment'] > 0).astype(int) >>> movies['pred_ispos'] = (movies['pred_senti'] > 0).astype(int) >>> columns = [c for c in movies.columns if 'senti' in c or 'pred' in c] >>> movies[columns].head(8) sentiment pred_senti senti_ispos pred_ispos id 1 2.266667 2.511515 1 1 2 3.533333 3.999904 1 1 3 -0.600000 -3.655976 0 0 4 1.466667 1.940954 1 1 5 1.733333 3.910373 1 1 6 2.533333 3.995188 1 1 7 2.466667 3.960466 1 1 8 1.266667 -1.918701 1 0 >>> (movies.pred_ispos == ... movies.senti_ispos).sum() / len(movies) 0.9344648750589345 # #1
这是用几行代码(以及大量数据)构建情感分析器的一个相当不错的开端。你不必猜测与 7500 个词列表相关联的情感,并将它们硬编码到诸如 VADER 之类的算法中。相反,你告诉机器整个文本片段的情感评分。然后,机器会做所有工作,以确定这些文本中每个词相关的情感。这就是机器学习和 NLP 的力量所在!
你认为这个模型对完全不同的文本示例,比如产品评论,会有多好的泛化能力?在电影和产品评论(比如电子产品和家居用品)中,人们用相同的词来描述喜欢的事物吗?可能不会。但通过对来自不同领域的具有挑战性的文本运行语言模型来检查其稳健性是个好主意。通过在新领域测试模型,你可以获得更多示例和数据集的使用想法,以用于训练和测试集。
首先,你需要加载产品评论。并查看你加载的文件内容,以确保你理解数据集中包含的内容。
>>> products = pd.read_csv('https://proai.org/product-reviews.csv.gz') >>> products.columns Index(['id', 'sentiment', 'text'], dtype='object') >>> products.head() id sentiment text 0 1_1 -0.90 troubleshooting ad-2500 and ad-2600 no picture... 1 1_2 -0.15 repost from january 13, 2004 with a better fit... 2 1_3 -0.20 does your apex dvd player only play dvd audio ... 3 1_4 -0.10 or does it play audio and video but scrolling ... 4 1_5 -0.50 before you try to return the player or waste h...
接下来,我们需要加载产品评论。
>>> bows = [] >>> for text in products['text']: ... bows.append(Counter(casual_tokenize(text))) >>> df_products = pd.DataFrame.from_records(bows) >>> df_products = df_products.fillna(0).astype(int) >>> df_products.shape # #1
当你将一个 BOW 向量的数据框与另一个相结合时会发生什么?
>>> df_all_bows = pd.concat([df_movies, df_products]) >>> df_all_bows.columns # #1 Index(['!', '"', ... 'zoomed', 'zooming', 'zooms', 'zx', 'zzzzzzzzz', ...], dtype='object', length=23302)
单词袋的合并数据帧中有一些在产品评论中使用但不在电影评论中使用的标记。现在,您的词汇表中有 23,302 个电影评论和产品的唯一标记。电影评论只包含 20,756 个唯一标记。因此,你之前的词汇表中肯定有 23,302 - 20,756 或 2,546 个新产品标记。
为了使用您的朴素贝叶斯模型对产品评论进行预测,您需要确保您的新产品单词袋的列(标记)与原始电影评论的列(标记)完全相同,并且顺序也相同。毕竟,该模型对这些新标记没有任何经验,因此不知道哪些权重适用于它们。您不希望它混淆权重并将它们应用于产品评论中的错误标记。
>>> vocab = list(df_movies.columns) # #1 >>> df_products = df_all_bows.iloc[len(movies):] # #2 >>> df_products = df_products[vocab] # #3 >>> df_products.shape (3546, 20756) >>> df_movies.shape # #4 (10605, 20756)
现在你的两组向量(数据帧)都有 20,756 列或唯一标记。现在,您需要将产品评论的标签转换为模仿您在原始朴素贝叶斯模型上训练的二进制电影评论分类标签。
>>> products['senti_ispos'] = (products['sentiment'] > 0).astype(int) >>> products['pred_ispos'] = nb.predict(df_products).astype(int) >>> correct = (products['pred_ispos'] ... == products['senti_ispos']) # #1 >>> correct.sum() / len(products) 0.557...
因此,您的朴素贝叶斯模型在预测产品评论的情感(大拇指向上或向下)时表现不佳,只比硬币抛掷稍好。造成这种次优性能的原因之一是您的 casual_tokenize
产品文本中的词汇有 2546 个标记不在电影评论中。这大约占原始电影评论标记化中标记数的 10%,这意味着所有这些词在您的朴素贝叶斯模型中都不会有任何权重或分数。此外,朴素贝叶斯模型处理否定词的能力不及 VADER。您需要将 n-gram 结合到您的标记器中,以将否定词(例如 “not” 或 “never”)与它们可能用于修饰的正面词语连接起来。
我们让您继续改进这个机器学习模型的 NLP 操作。您可以在每个步骤中相对于 VADER 检查您的进度,看看您是否认为机器学习是比硬编码算法更好的 NLP 方法。
2.11 测试自己
- 词形归并器和词干提取器有什么区别?哪一个更好(在大多数情况下)?
- 词形归并器如何增加搜索引擎(例如 You.com)返回包含所需内容的搜索结果的可能性?
- 字母大小写转换、词形还原或停用词移除会提高您典型的 NLP 流水线的准确性吗?对于识别误导性新闻文章标题(点击诱导)这样的问题呢?3
- 您的标记计数中是否有统计数据可供您决定 NLP 流水线中使用的
n
是多少? - 是否有一个网站可以下载以前发布的大多数单词和 n-gram 的标记频率?4
- 什么是使用 NLP 构建的配对编码 AI 助手的风险和可能的好处?你信任什么样的组织和算法来处理你的思想和代码?
2.12 总结
- 你已经实现了分词,并为你的应用程序配置了分词器。
- n-gram 分词有助于保留文档中的“词序”信息。
- 规范化和词干提取将单词整合成一组,以提高搜索引擎的“召回率”,但会降低准确性。
- 词形还原和像
casual_tokenize()
这样的定制分词器可以提高准确性并减少信息丢失。 - 停用词可能包含有用的信息,丢弃它们并不总是有帮助的。
[1] 你可以加入的 Mastodon 服务器(proai.org/mastoserv
)
[2] Mastodon 自定义表情符号文档(docs.joinmastodon.org/methods/custom_emojis/
)
[3] (en.wikipedia.org/wiki/Grapheme
)
[4] Suzi Park 和 Hyopil Shin,《形态丰富语言的字素级词嵌入意识》,(www.aclweb.org/anthology/L18-1471.pdf
)
[5] 如果你想更深入地了解“单词”到底是什么,请阅读 Jerome Packard 撰写的《汉语形态学》介绍,他在其中详细讨论了“单词”的概念。直到 20 世纪,当它从英语语法翻译成中文时,"单词"的概念在中文中并不存在。
[6] 相邻单词对称为 2-grams 或 bigrams。三个单词连续称为 3-grams 或 trigrams。四个单词连续称为 4-grams。5-grams 可能是你在 NLP 流水线中找到的最长的 n-grams。Google 统计了几乎所有已经出版的书中的所有 1 到 5-grams(books.google.com/ngrams
)。
[7] Lysandre 在 Huggingface 文档中解释了各种分词器选项(huggingface.co/transformers/tokenizer_summary.html
)
[8] Markus Zusak,《偷书贼》,第 85 页(en.wikiquote.org/wiki/The_Book_Thief
)
[9] Peter Watts,《盲视》,(rifters.com/real/Blindsight.htm
)
[10] 感谢 Wiktor Stribiżew(stackoverflow.com/a/43094210/623735
)。
[11] 章节 2 的 nlpia2 源代码(proai.org/nlpia2-ch2
)提供了额外的 spaCy 和 displacy 选项和示例。
[12] Andrew Long,《Python NLP 分词器基准测试》,(towardsdatascience.com/benchmarking-python-nlp-tokenizers-3ac4735100c5
)
[13] 在许多应用中,“n-gram”一词指的是字符n-grams,而不是单词 n-grams。例如,领先的关系数据库 PostgreSQL 具有一个 Trigram 索引,该索引将您的文本标记为字符 3-grams 而不是单词 3-grams。在本书中,我们使用“n-gram”来指代单词 grams 的序列,“character n-grams”来指代字符的序列。
[14] Hannes 和 Cole 可能正在大声喊叫“我们告诉过你!”当他们读到这篇文章时。
[15] 实际上,用于 BPE 和 Wordpiece 分词器的标记字符串表示在标记字符串的开头或结尾放置标记字符,以指示单词边界的缺失(通常是空格或标点)。所以你可能在你的 BPE 词汇表中看到“aphr##”标记,表示“aphrodesiac”中的前缀“aphr” ( stackoverflow.com/a/55416944/623735
)
[16] 最近在美国通过了歧视性选民限制法:( proai.org/apnews-wisconsin-restricts-blacks
)
[17] 有关另一个类似的分词器的信息,请参阅第十二章 - sentence piece 分词器。
[18] Lysandre Debut 在 Hugging Face transformers 文档中解释了所有子词分词器的变体 ( huggingface.co/transformers/tokenizer_summary.html
)
[19] Huggingface 关于分词器的文档 ( huggingface.co/docs/transformers/tokenizer_summary
)
[20] 请参阅维基百科上的“Player piano”文章 ( en.wikipedia.org/wiki/Player_piano
)。
[21] 请参阅名为“音乐盒 - 维基百科”的网页 ( en.wikipedia.org/wiki/Music_box
)。
[22] 西部世界是一部关于特别恶毒的人类和类人机器的电视系列,其中包括一个在主要酒吧演奏钢琴的角色。
[23] 本书第二章的 nlpia2 源代码 ( proai.org/nlpia2-ch2
) 包含了额外的 spaCy 和 displacy 选项和示例。
[24] “不要使用 NLTK 的 wordtokenize,使用 NLTK 的 regextokenize”(附有代码的博客)由 Michael Bryan 撰写的“Python NLP Tokenizers 基准测试” ( morioh.com/p/e2cfb73c8e86
)
[25] 在许多应用中,“n-gram”一词指的是字符n-grams,而不是单词 n-grams。例如,领先的关系数据库 PostgreSQL 具有一个 Trigram 索引,该索引将您的文本标记为字符 3-grams 而不是单词 3-grams。在本书中,我们使用“n-gram”来指代单词 grams 的序列,“character n-grams”来指代字符的序列。
[26] Hannes 和 Cole 可能正在大声喊叫“我们告诉过你!”当他们读到这篇文章时。
[27] 实际上,用于 BPE 和 Wordpiece 标记器的令牌的字符串表示在令牌字符串的开始或结尾放置标记字符,以指示单词边界的缺失(通常是空格或标点符号)。因此,你可能会在 BPE 词汇表中看到 “aphr##” 令牌,表示 “aphr” 的前缀在 “aphrodesiac” 中的缺失( stackoverflow.com/a/55416944/623735
)
[28] 最近在美国通过了歧视性的选举限制法律:( proai.org/apnews-wisconsin-restricts-blacks
)
[29] 有关另一种类似的标记器的信息,请参阅第十二章 —— 句子片段标记器
[30] Lysandre Debut 在 Hugging Face transformers 文档中解释了关于子词标记器的所有变体( huggingface.co/transformers/tokenizer_summary.html
)
[31] Huggingface 标记器文档( huggingface.co/docs/transformers/tokenizer_summary
)
[32] Apache Solr 主页和 Java 源代码( solr.apache.org/
)
[33] 总部位于欧洲的 Qwant 网络搜索引擎( www.qwant.com/
)
[34] SearX git 代码库( github.com/searx/searx
)和网络搜索( searx.thegpm.org/
)
[35] ( www.wolframalpha.com/
)
[36] 摘自马丁·A·诺瓦克和罗杰·海菲尔德的 SuperCooperators:利他主义、进化和我们成功需要彼此的原因。纽约:自由出版社,2011 年。
[37] 摘自马丁·A·诺瓦克(Martin A. Nowak)和罗杰·海菲尔德(Roger Highfield)的《超级合作者:利他主义、进化和我们成功需要彼此的原因》。纽约:自由出版社,2011 年。
[38] 语言学和自然语言处理技术经常被用来从 DNA 和 RNA 中提取信息,这个网站提供了一份氨基酸符号列表,可以帮助你将氨基酸语言翻译成人类可读的语言:“氨基酸 - 维基百科”( en.wikipedia.org/wiki/Amino_acid#Table_of_standard_amino_acid_abbreviations_and_properties
)。
[39] 你可能在数据库课程或 PostgreSQL( postgres
)文档中了解过三元索引。但这些是字符的三元组。它们可以帮助你使用 %
和 ~*
SQL 全文搜索查询在庞大的字符串数据库中快速检索字符串的模糊匹配。
[40] 各种语言的更全面的停用词列表可以在 NLTK 的语料库中找到( raw.githubusercontent.com/nltk/nltk_data/gh-pages/packages/corpora/stopwords.zip
)。
[41] 请查看名为“文本数据分析和自然语言处理”的网页(rstudio-pubs-static.s3.amazonaws.com/41251_4c55dff8747c4850a7fb26fb9a969c8f.html
)。
[42] spaCy 包含了一组停用词,你可以根据这个 Stack Overflow 的回答进行自定义(stackoverflow.com/a/51627002/623735
)。
[43] 如果你想帮助其他人找到 SearX,你可以养成在谈论或写作时说“SearX”(发音为“see Ricks”)的习惯。你可以改变你世界中词语的含义,使之变得更美好!
[44] NLTK 包含了许多在线教程中看到的停用词列表(pypi.org/project/nltk
)。
[45] 为了营销和搜索引擎优化,达米安·多伊尔(Damien Doyle)在这里维护了一个按热门程度排名的搜索引擎停用词列表(www.ranks.nl/stopwords
)。
[46] Vadi Kumar 在这里维护了一些停用词列表(github.com/Vadi/stop-words
)。
[47] 出自泰德·张伯伦(Ted Chiang)的《呼吸》中,“事实真相,虚构真相”。
[48] 请参阅名为“驼峰大小写 - 维基百科”的网页(en.wikipedia.org/wiki/Camel_case_case
)。
[49] 我们假设在 Python 3 中 str.lower() 的行为。在 Python 2 中,字节(字符串)可以通过将 ASCII 码空间中的所有字母字符转换为小写来转换,但在 Python 3 中,str.lower
正确地转换字符,因此它可以处理装饰的英文字符(例如 resumé 中 e 上的 “acute accent” 变音符号)以及非英语语言中大写的特殊情况。
[50] 三字词组“cup of joe”(en.wiktionary.org/wiki/cup_of_joe
)是“一杯咖啡”的俚语。
[51] 查阅附录 D 以了解更多关于 精确率 和 召回率 的信息。这是 Webology 网站上各种搜索引擎召回率的比较(www.webology.org/2005/v2n2/a12.html
)。
[52] 如果你忘记了如何衡量召回率,请查阅附录 D 或访问维基百科页面了解更多(en.wikipedia.org/wiki/Precision_and_recall
)。
[53] 请参阅 1980 年的“后缀剥离算法”(www.cs.toronto.edu/~frank/csc2501/Readings/R2_Porter/Porter-1980.pdf
)由 M.F. Porter 所著。
[54] 请参阅名为“Snowball:用于词干提取算法的语言”的网页(snowball.tartarus.org/texts/introduction.html
)。
[55] 这是 Julia Menchavez 在 GitHub 上实现的porter-stemmer
的简化版本(github.com/jedijulia/porter-stemmer/blob/master/stemmer.py
)。
[56] 对不起 Chick,Porter 不喜欢你的 obsfucate
用户名;)
[57] 感谢 Kyle Gorman 指出这一点。
[58] 额外的元数据也用于调整搜索结果的排名。 Duck Duck Go 和其他流行的网络搜索引擎结合了 400 多个独立算法(包括用户贡献的算法)来排名您的搜索结果(duck.co/help/results/sources
)。
[59] “好人先走!”——《超级合作者》作者 M.A. Nowak
[60] 请查看斯坦福 NLP 信息检索(IR)书籍中标题为“词干化和词形还原”的章节(nlp.stanford.edu/IR-book/html/htmledition/stemming-and-lemmatization-1.html
)。
[61] “VADER:基于简约规则的社交媒体文本情感分析模型” 作者:Hutto 和 Gilbert(comp.social.gatech.edu/papers/icwsm14.vader.hutto.pdf
)。
[62] 你可以在 Github 上的包源代码中找到更详细的安装说明(github.com/cjhutto/vaderSentiment
)。
[63] Qary 是一个开源的虚拟助手,它真正帮助你而不是操纵和误导你(docs.qary.ai
)。
[64] 如果你还没有安装 nlpia
,请查看安装说明(gitlab.com/tangibleai/nlpia2
)。
[65] 提示:当你怀疑时做一个实验。这被称为超参数调整。这里有一个假新闻数据集供你实验:(www.kaggle.com/clmentbisaillon/fake-and-real-news-dataset/download
)
[66] 提示:一个曾经立志“不作恶”,但现在却做了恶的公司创建了这个庞大的 NLP 语料库。