深度学习进阶-自然语言处理-自然语言和单词的分布式表示
自然语言(natural language):我们平常使用的语言,如汉语或英语,称为自然语言(natural language)。
自然语言处理(Natural Language Processing,NLP):顾名思义,就是处理自然语言的科学。简单地说,它是一种能够让计算机理解人类语言的技术。换言之,自然与处理的目标就是让计算机理解人说的话,进而完成对我们有帮助的事情。
我们的语言是由文字构成的,而语言的含义是由单词构成的。换句话说,单词是含义的最小单位。
同义词词典(thesaurus):具有相同含义的单词(同义词)或含义类似的单词(近义词)被归类到同一个组中。比如,使用同义词词典,我们可以知道 car 的同义词有 automobile、motorcar等(图 2-1)。
图 2-1 同义词的例子在自然语言处理中用到的同义词词典有时会定义比单词之间的粒度更细的关系,比如 “上位-下位” 关系、“整体-部分” 关系。举个例子,如图 2-2 所示,我们利用图结构定义了各个单词之间的关系。
图 2-2 根据各单词的含义,基于上位-下位关系形成的图在图 2-2 中,单词 motor vehicle(机动车)是单词 car 的上位概念。car 的下位概念有 SUV、compact car 和 hatch-back 等更加具体的车种。
像这样,通过对所有单词创建近义词集合,并用图表示各个单词的关系,可以定义单词之间的联系。利用这个 “单词网络”,可以教会计算机单词之间的相关性。也就是说,我们可以将单词含义(间接地)教给计算机,然后利用这一知识,就能让计算机做一些对我们有用的事情。
如何使用同义词词典根据自然语言处理的具体应用的不同而不同。比如,在信息检索场景中,如果事先知道 automobile 和 car 是近义词,就可以将 automobile 的检索结果添加到 car 的检索结果中。
WordNet:自然语言处理领域最著名的同义词词典。由普林斯顿大学于 1985 年开始开发的同义词词典。
使用 WordNet,可以获得单词的近义词,或者利用单词网络。使用单词网络,可以计算单词之间的相似度。
难以顺应时代变化
我们使用的语言是活的。随着时间的推移,新词不断出现,而那些落满尘埃的旧词不知哪天就会被遗忘。比如,“众筹”(crowdfunding)就是一个最近才开始使用的新词。
另外,语言的含义也会随着时间的推移而变化。比如,英语中的 heavy 一词,现在有 “事态严重” 的含义(主要用于俚语),但以前是没有这种用法的。在电影《回到未来》中,有这样一个场景:从 1985 年穿越回来的马蒂和生活在 1955 年的博士的对话中,对 heavy 的含义有不同的理解。如果要处理这样的单词变化,就需要人工不停地更新同义词词典。
人力成本高
制作词典需要巨大的人力成本。以英文为例,据说现有的英文单词总数超过 1000 万个。在极端情况下,还需要对如此大规模的单词进行单词之间的关联。
顺便提一下,WordNet 中收录了超过 20 万个的单词。
无法表示单词的微妙关系
同义词词典中将含义相近的词分到一组。但实际上,即使含义相近的单词,也有细微的差别。比如,wintage(复古) 和 retro(复古)虽然表示相同的含义,但是用法不同,而这种细微的差别在同义词词典中是无法表示出来的。
语料库(corpus):大量的文本数据。语料库中包含了大量的关于自然语言的实践知识,即文章的写作方法、单词的选择方法和单词含义等。
import re
import numpy as np
def preprocess(text):
"""
文本语料预处理
:param text: 需要进行预处理的文本内容
:return:
"""
text = text.lower() # 将所有字母转化为小写
# 方案一
words = re.split("\W+", text) # \W 匹配任何非单词字符。等价于 "[^A-Za-z0-9_]"
# 方案二
# text = text.replace(".", " .") # 句尾的句号前插入一个空格
# words = text.split(" ") # 通过空格分割句子
word_to_id = {} # 将单词转化为单词ID(健是单词,值是单词ID)
id_to_word = {} # 将单词ID转化为单词(健是单词ID,值是单词)
for word in words:
if word not in word_to_id:
new_id = len(word_to_id)
word_to_id[word] = new_id
id_to_word[new_id] = word
# 单词列表转化为单词ID列表
corpus = np.array([word_to_id[w] for w in words])
return corpus, word_to_id, id_to_word
if __name__ == "__main__":
text = "You say goodbye and I say hello."
corpus, word_to_id, id_to_word = preprocess(text)
print(corpus)
print(word_to_id)
print(id_to_word)
[0 1 2 3 4 1 5 6]
{'you': 0, 'say': 1, 'goodbye': 2, 'and': 3, 'i': 4, 'hello': 5, '': 6}
{0: 'you', 1: 'say', 2: 'goodbye', 3: 'and', 4: 'i', 5: 'hello', 6: ''}
单词的分布式表示:将单词表示为固定长度的向量。
这种向量的特征在于它是用密集向量表示的。密集向量的意思是,向量的各个元素(大多数)是由非 0 实数表示的。例如,三维分布表示是 [0.21, -0.45, 0.83]。
分布式假设(distributional hypothesis):某个单词的含义由它周围的单词形成。
单词本身没有含义,单词含义由它所在的上下文(语境)形成。的确,含义相同的单词经常出现在相同的语境中。比如 “I drink beer.” “I drink wine.”,drink 的附近常有饮料出现。另外,从 “I guzzle beer.” “I guzzle wine.” 可知,guzzle(大口喝) 和 drink(喝) 所在的语境相同。进而我们可以推测出,guzzle 和 drink 是近义词。
上下文:某个居中单词(关注词)周围的单词(词汇)。在图 2-3 的例子中,左侧和右侧的 2 个单词就是上下文。
图 2-3 窗口大小为 2 的上下文的例子将左右两边相同数量的单词作为上下文。但是,根据具体情况,也可以仅将左边的单词或者右边的单词作为上下文。此外,也可以使用考虑了句子分隔符的上下文。
窗口大小(window size):将上下文的大小(即周围的单词有多少个)称为窗口大小(window size)。
窗口大小为 1,上下文包含左右各 1个单词;窗口大小为 2,上下文包含左右各 2 个单词,以此类推。
基于计数的方法:在关注某个单词的情况下,对它的周围出现了多少次什么单词进行计数,然后汇总。
计算 “you say goodbye and i say hello.” 中各个单词的上下文中包含的单词的频数:
设置窗口大小为 1,从单词 ID 为 0 的 you 开始。
图 2-4 “you say goodbye and i say hello.”中各个单词的上下文中包含的单词的频数图 2-4 是汇总了所有单词的共现单词的表格。这个表格的各行对应相应单词的向量。因为图 2-4 的表格呈矩阵状,所以称为共现矩阵(co-occurence matrix)。