NLP:词中的数学( 四 )

注意
TreebankWordTokenizer会返回“kite.”(带有句点)作为一个词条 。 Treebank分词器假定文档已经被分割成独立的句子 , 因此它只会忽略字符串最末端的标点符号 。 句子分割也是一件棘手的事情 , 我们将在第11章中介绍 。 尽管如此 , 由于在一趟扫描中就完成句子的分割和分词处理(还有很多其他处理) , spaCy分析器表现得更快且更精确 。 因此 , 在生产型应用中 , 可以使用spaCy而不是前面在一些简单例子中使用的NLTK组件 。
好了 , 回到刚才的例子 , 里面有很多停用词 。 这篇维基百科的文章不太可能会与“the”“a”、连词“and”以及其他停用词相关 。 下面把这些词去掉:
>>> import nltk>>> nltk.download('stopwords', quiet=True)True>>> stopwords = nltk.corpus.stopwords.words('english')>>> tokens = [x for x in tokens if x not in stopwords]>>> kite_counts = Counter(tokens)>>> kite_countsCounter({'kite': 16,'traditionally': 1,'tethered': 2,'heavier-than-air': 1,'craft': 2,'wing': 5,'surfaces': 1,'react': 1,'air': 2,...,'made': 1})}单纯凭借浏览词在文档中出现的次数 , 我们就可以学到一些东西 。 词项kite(s)、wing和lift都很重要 。 并且 , 如果我们不知道这篇文章的主题是什么 , 只是碰巧在大规模的类谷歌知识库中浏览到这篇文章 , 那么我们可能“程序化”地推断出 , 这篇文章与“flight”或者“lift”相关 , 或者实际上和“kite”相关 。
如果考虑语料库中的多篇文档 , 事情就会变得更加有趣 。 有一个文档集 , 这个文档集中的每篇文档都和某个主题有关 , 如放飞风筝(kite flying)的主题 。 可以想象 , 在所有这些文档中“string”和“wind”的出现次数很多 , 因此这些文档中的词项频率TF("string")和TF("wind")都会很高 。 下面我们将基于数学意图来更优雅地表示这些数值 。
3.2 向量化我们已经将文本转换为基本的数值 。 虽然仍然只是把它们存储在字典中 , 但我们已经从基于文本的世界中走出一步 , 而进入了数学王国 。 接下来我们要一直沿着这个方向走下去 。 我们不使用频率字典来描述文档 , 而是构建词频向量 , 在Python中 , 这可以使用列表来实现 , 但通常它是一个有序的集合或数组 。 通过下列片段可以快速实现这一点:
>>> document_vector = []>>> doc_length = len(tokens)>>> for key, value in kite_counts.most_common():...document_vector.append(value / doc_length)>>> document_vector[0.07207207207207207, 0.06756756756756757, 0.036036036036036036, ..., 0.0045045045045045045]对于上述列表或者向量 , 我们可以直接对它们进行数学运算 。
提示
我们可以通过多种方式加快对上述数据结构的处理[2] 。 现在我们只是基于基本要素进行处理 , 但很快我们就会想加快上面的步骤 。
如果只处理一个元素 , 那么在数学上没什么意思 。 只有一篇文档对应一个向量是不够的 , 我们可以获取更多的文档 , 并为每篇文档创建其对应的向量 。 但是每个向量内部的值必须都要相对于某个在所有向量上的一致性结果进行计算(即所有文档上有个通用的东西 , 大家都要对它来计算) 。 如果要对这些向量进行计算 , 那么需要相对于一些一致的东西 , 在公共空间中表示一个位置 。 向量之间需要有相同的原点 , 在每个维度上都有相同的表示尺度(scale)或者“单位” 。 这个过程的第一步是计算归一化词项频率 , 而不是像在上一节中那样计算文档中的原始词频 。 第二步是将所有向量都转换到标准长度或维度上去 。
此外 , 我们还希望每个文档向量同一维上的元素值代表同一个词 。 但我们可能会注意到 , 我们写给兽医的电子邮件中不会包含《战争与和平》(War--tt-darkmode-color: #666666;">)中的许多词 。 (也许会 , 谁知道呢?)但是 , 如果允许向量在不同的位置上都包含0 , 那么这也是可以的(事实上也是必要的) 。 我们会在每篇文档中找到独立的词 , 然后将这些词集合求并集后从中找到每个独立的词 。 词汇表中的这些词集合通常称为词库(lexicon) , 这与前面章节中所引用的概念相同 , 只是前面都考虑的是某个特定的语料库 。 下面我们看看比《战争与和平》更短的电影会是什么样子 。 我们去看看Harry , 我们已经有了一篇“文档” , 下面我们用更多的文档来扩充语料库:
>>> docs = ["The faster Harry got to the store, the faster and faster Harry? would get home."]>>> docs.append("Harry is hairy and faster than Jill.")>>> docs.append("Jill is not as hairy as Harry.")提示
如果我们不只是在计算机上敲出来这段程序 , 而是想一起玩转的话 , 那么可以从nlpia包导入这段程序:from nlpia.data.loaders import harry_docs as docs 。