目录标题
一、自然语言处理概述
我们平日使用的语言,如汉语或英语,称为自然语言。自然语言处理(Natural Language Processing,NLP)的目标就是让计算机理解人类语言,进而完成对我们有帮助的事情。
说到计算机可以理解的语言,我们可能会想到编程语言或者标记语言等。这些语言的语法定义可以唯一性地解释代码含义,计算机也能根据确定的规则分析代码。编程语言是一种机械的、缺乏活力的语言。换句话说,它是一种"硬语言"。而汉语或英语等自然语言是"软语言",其含义和形式会灵活变化,并且会不断出现新的词语或新的含义。要让计算机去理解自然语言,使用常规方法是无法办到的。下面是几个解决方法
1、基于同义词词典的方法
具有相同(同义词)或类似(近义词)含义的单词,可以归到同一个类别中;而根据单词"整体-部分"或者"上位-下位"关系,可以构建出层级的树状图。
这样,就可以构成一个庞大的"单词网络",用它就可以教会计算机单词之间的关系,从而计算出单词的"相似度"。
主要缺点:
- 需要人工逐个定义单词之间的相关性,非常费时费力;
- 新词不断出现,语言不断变化,词典维护成本极高;
- 在表现力上也有限制。
2、基于计数的方法
大量的文本数据,构成了 语料库(corpus)。
我们的目的,就是从语料库中,自动且高效地提取出语言的"本质"。最简单的做法,就是统计"词频"
- 分词:对词进行统计,首先需要对文本内容进行切分,找出一个个基本单元
- 词关联ID:给单词标上一个 ID,构建单词和ID的关联字典(称为"词表")
- 词向量化:用一个固定长度的向量来表示单词,也称为词的"分布式表示"
对每一个词,可以统计它周围出现了什么单词、出现了多少次(称为"上下文");把这些词频统计出来,就构成了一个向量;这个向量就可以表示当前的词了,称为"词向量"(word vector)。这样,所有词对应的向量,汇总起来就是一个矩阵,被称为 共现矩阵(co-occurrence matrix)。
主要缺点
对所有词进行向量化表示的计算复杂度极高。
3、基于推理的方法
除了基于计数的方法,还可以使用推理的方法把词用向量表示出来。
我们希望在已知上下文的前提下,"推测"当前位置的词是什么。
利用神经网络,接收上下文信息作为输入,通过模型计算,输出各个单词可能得出现概率;从而就可以根据上下文,预测该出现的单词了。
二、词嵌入层
什么是词嵌入
自然语言是由文字构成的,而语言的含义是由单词构成的。即单词是含义的最小单位。因此为了让计算机理解自然语言,首先要让它理解单词含义。
词向量是用于表示单词意义的向量,也可以看作词的特征向量。将词映射到向量的技术称为 词嵌入(Word Embedding)。

还有一种使用向量表示单词意义的方式是独热向量,独热向量很容易构建,但它们通常不是一个好的选择。一个主要原因是独热向量不能准确表达不同词之间的相似度。比如使用余弦相似度 x T y ∣ ∣ x ∣ ∣ ∣ ∣ y ∣ ∣ \frac{x^Ty}{||x|| ||y||} ∣∣x∣∣∣∣y∣∣xTy 来表示两个词之间的相似程度,由于任意两个不同词的独热向量之间的余弦相似度为 0,所以独热向量不能编码词之间的相似性。另一个原因是随着词汇量的增大,独热向量表示的向量大小也会增大,在词汇量较大的情况下会消耗大量的存储资源与计算资源。
将词转换为词向量时:
首先需要对文本进行分词,再根据需要进行清洗和标准化。
构建词表(Vocabulary),每个词对应一个索引。
使用词嵌入矩阵将词索引转换为词向量。
API使用(这里词嵌入矩阵随机生成,没经过训练)
可使用torch.nn.Embedding来初始化词嵌入矩阵:
python
torch.nn.Embedding(num_embeddings, embedding_dim)
# num_embeddings:词的数量
# embedding_dim:词向量的维度
例:
先安装jieba库用于分词:pip install jieba。
python
#%%
import torch
import torch.nn as nn
import jieba
# 设置随机种子
torch.manual_seed(42)
text = "自然语言是由文字构成的,而语言的含义是由单词构成的。即单词是含义的最小单位。因此为了让计算机理解自然语言,首先要让它理解单词含义。"
# 自定义停用词和标点符号
stopwords = {"的", "是", "而", "由", ",", "。", "、"}
# 分词,过滤停用词和标点,去重,构建词表
words = [word for word in jieba.lcut(text) if word not in stopwords]
vocab = list(set(words)) # 词表
print(f'去重后的列表f{vocab}')# 去重后的列表f['首先', '语言', '单词', '文字', '即', '因此', '为了', '最小', '计算机', '让', '要', '它', '单位', '理解', '自然语言', '含义', '构成']
# 构建词到索引的映射
word2idx = dict()
for idx, word in enumerate(vocab):
word2idx[word] = idx
print(f'word2idx,根据单词找到下标:{word2idx}')
# 初始化嵌入层
embed = nn.Embedding(num_embeddings=len(word2idx), embedding_dim=5)
# 打印词向量
for idx, word in enumerate(vocab):
word_vec = embed(torch.tensor(idx)) # 通过索引获取词向量
print(f"{idx:>2}:{word:8}\t{word_vec.detach().numpy()}")
#打印输出如下
word2idx,根据单词找到下标:{'首先': 0, '语言': 1, '单词': 2, '文字': 3, '即': 4, '因此': 5, '为了': 6, '最小': 7, '计算机': 8, '让': 9, '要': 10, '它': 11, '单位': 12, '理解': 13, '自然语言': 14, '含义': 15, '构成': 16}
0:首先 [ 1.9269153 1.4872841 0.9007172 -2.105521 0.6784184]
1:语言 [-1.2345449 -0.04306748 -1.604667 -0.7521353 1.648723 ]
2:单词 [-0.39247864 -1.4036071 -0.7278813 -0.5594302 -0.7688389 ]
3:文字 [ 0.7624454 1.6423169 -0.15959747 -0.49739754 0.43958926]
4:即 [-0.75813115 1.0783176 0.80080056 1.6806206 1.2791244 ]
5:因此 [ 1.2964228 0.6104665 1.3347378 -0.23162432 0.04175949]
6:为了 [-0.2515753 0.8598585 -1.3846737 -0.87123615 -0.22336592]
7:最小 [ 1.7173615 0.31888032 -0.42451897 0.30572093 -0.7745925 ]
8:计算机 [-1.5575725 0.9956361 -0.87978584 -0.60114205 -1.2741512 ]
9:让 [ 2.122785 -1.2346531 -0.48791388 -0.913823 -0.65813726]
10:要 [ 0.07802387 0.52580875 -0.48799172 1.191369 -0.81400764]
11:它 [-0.7359928 -1.4032478 0.03600367 -0.06347727 0.6756149 ]
12:单位 [-0.09780689 1.844594 -1.1845374 1.3835493 1.4451338 ]
13:理解 [0.85641253 2.2180758 0.5231655 0.34664667 0.5246226 ]
14:自然语言 [ 1.1412016 0.0516436 0.72810954 -0.7106419 -0.6020683 ]
15:含义 [ 0.9604488 0.40481427 -0.82776886 1.334703 0.48353928]
16:构成 [-0.19756168 1.2683114 1.2242628 0.09811721 1.7422527 ]
这段代码是 Python 的 **f-string(格式化字符串)**语法。> 和 : 后面跟的数字是用来控制输出格式的。
具体拆解如下:
1. {idx:>2} 中的 >2
>:表示右对齐(Right align)。2:表示该字段的最小宽度为 2 个字符。- 效果: 如果
idx是1,输出就是" 1"(前面补一个空格);如果是12,输出就是"12"。这样可以 让输出的序号整齐排列。
2. {word:8} 中的 8
- 默认行为: 对于字符串,默认是左对齐。
8:表示该字段的最小宽度为 8 个字符。- 效果: 如果
word是"cat",输出就是"cat "(后面补 5 个空格);如果单词超过 8 个字符,则原样输出,不会截断。这同样是为了让后面的向量数据对齐,看起来更美观。