自然语言处理,简称NLP,目标是使得机器具有和人类一样的语言理解与运用能力。
绪论
NLP,也叫计算语言学:Computational Linguistics,CL。
AI的发展经历从运算智能(关注的是机器的基础运算和存储能力)到感知智能(强调机器的模式识别能力),再到认知智能(NLP、常识建模和推理)三个发展阶段。
8个难点,即语言的抽象性、组合性、歧义性、进化性、非规范性、主观性、知识性及难移植性。
任务体系

划分方式
- 任务层级:资源建设、基础任务、应用任务及应用系统四个层级;
- 任务类型:回归、分类、匹配、解析、生成五大问题;
- 研究对象:形式、语义、推理及语用分析四个等级;
- 从历史上看:理性主义和经验主义两大发展阶段;经验主义又可分成基于统计模型、深度学习模型、预训练模型三个阶段。

NLP的研究由浅入深,分为四个层次:
- 形式:研究语言符号层面的处理,如通过编辑距离等计算文本之间的相似度
- 语义:研究语言符号和其背后所要表达的含义之间的关系
- 推理:引入知识的运用
- 语用:引入语言所处环境因素

基于理性主义的规则方法,通过专家总结的符号逻辑知识处理通用的自然语言现象。
特征提取:需要事先利用经验性规则将原始的自然语言输入转化为机器能够处理的向量形式。
特征工程:特征提取需要细致的人工操作和一定的专业知识,形成体系化工程。
表示学习:指机器能根据输入自动地发现可以用于识别或分类等任务的表示。具体地,深度学习模型在结构上通常包含多层的处理层。底层的处理层接收原始输入,然后对其进行抽象处理,其后的每一层都在前一层的结果上进行更深层次的抽象,最后一层抽象结果即为输入的一个表示,用于最终的目标任务。抽象处理,是由模型内部的参数进行控制的,而参数的更新值则是根据训练数据上模型的表现,使用反向传播算法学习得到。优势:
- 可自动地发现有效特征;
- 打通不同任务之间的壁垒。能够将不同任务在相同的向量空间内进行表示,从而具备跨任务迁移的能力。进一步,可实现跨语言、跨模态的迁移。
无监督学习(Unsupervised Learning):不需要人工标注数据的预训练学习方法,准确叫法应该是自监督学习(Self-supervised Learning)。
NLP基础
NLP最基础最本质的问题,即文本如何在计算机内表示,才能达到易于处理和计算的目的。
基于规则的方法存在很多问题:
- 规则的归纳依赖专家的经验,需要花费大量的人力、物力和财力;
- 规则的表达能力有限,很多语言现象无法用简单的规则描述;
- 规则增多后,规则之间可能存在矛盾和冲突的情况,导致最终无法做出决策。
基础工具集与常用数据集
NLTK
Natural Language Toolkit,一个Python模块,提供多种语料库(Corpora)和词典(Lexicon)资源,如WordNet等,以及一系列基本的自然语言处理工具集,包括:分句、标记解析(Tokenization)、词干提取(Stemming)、词性标注(POS Tagging)和句法分析(Syntactic Parsing)等 适合英文。
语料库(文本数据集)可分为2类,未标注语料库(生语料库或生文本,Raw Text)和人工标注语料库(Annotated Corpus)。
WordNet是普林斯顿大学构建的英文语义词典(辞典,Thesaurus),其主要特色是定义同义词集合(Synset),每个同义词集合由具有相同意义的词义组成。WordNet为每一个同义词集合提供简短的释义(Gloss),不同同义词集合之间还具有一定的语义关系。
SentiWordNet(Sentiment WordNet),基于WordNet标注的词语情感倾向性词典,它为WordNet中每个同义词集合人工标注三个情感值,褒义、贬义和中性。
sentence_polarity,句子极性语料库。
词性(词类)是词语所承担的语法功能类别,如名词、动词和形容词等,词性标注就是根据词语所处的上下文,确定其具体的词性。POS Tagger是NLTK自带的词性标注器,采用宾州树库(Penn Treebank)的标注标准。
其他工具,如斯坦福大学使用Java开发的CoreNLP、基于Python/Cython开发的spaCy等。
py
import nltk
nltk.download()
from nltk.corpus import stopwords
from nltk.corpus import gutenberg
gutenberg.raw("austen-emma.txt")
from nltk.tokenize import word tokenize
from nltk.corpus import sentence_polarity
[(sentence, category)
for category in sentence_polarity.categories()
for sentence in sentence_polarity.sents(categories=category)]
from nltk import pos_tag
pos_tag(word_tokenize("They sat by the fire."))
[('They','PRP'),('sat','VBP'),('by','IN'),('the','DT'),('fire','NN'),('.','.')]
pos_tag(word_tokenize("They fire a gun."))
[('They','PRP'),('fire','VBP'),('a','DT'),('gun','NN'),('.','.')]
nltk.help.upenn_tagset() #返回全部词性标记集以及各词性的示例
LTP
Language Technology Platform,哈工大推出,集词法分析(分词、词性标注和NER)、句法分析(依存句法分析)和语义分析(语义角色标注和语义依存分析)等多项NLP技术于一体。LTP 4.0版本使用Python编写,采用预训练模型以及多任务学习机制,能够以较小的模型获得非常高的分析精度。
py
from ltp import LTP
ltp = LTP() #默认加载Sma11模型,首次使用时会自动下载并加载模型
segment, hidden = ltp.seg(["南京市长江大桥。"]) # 对句子进行分词,结果使用segment访问,hidden用于访问每个词的隐含层向量,用于后续分析步骤
sentences = ltp.sent_split(["南京市长江大桥。", "汤姆生病了。他去了医院。"]) #分句
segment, hidden = ltp.seg(sentences)
pos_tags = ltp.pos(hidden) #词性标注
PyTorch基础
PyTorch是一个基于张量(Tensor)的数学运算工具包,提供两个高级功能:
- 具有强大的GPU加速的张量计算功能;
- 能够自动进行微分计算,从而可以使用基于梯度的方法对模型参数进行优化。
张量,多维数组,2维张量即矩阵(Matrix),1维张量即向量(Vector),0维张量即标量(Scalar)即数值。
张量默认存储在内存中,使用CPU进行运算。若要在GPU中创建和计算张量,则需要显式地将其存入GPU中,前提是已配置NVIDIA的GPU并安装CUDA库)。
张量的运算:向量点积(torch.dot)、矩阵相乘(torch.mm)、三角函数、数学函数、聚合、拼接、比较、随机采样和序列化等。
维:Dim,也叫轴,Axis。dim赋值后,PyTorch就会遍历这个维去做运算(沿着该维运算),其他维顺序不变。聚合操作还提供keepdim参数,默认False。
PyTorch的优势:执行速度更高效,尤其是当张量存储的数据较多+有GPU时。
自动微分:PyTorch功能,也叫自动计算梯度,自动计算函数关于一个变量在某一取值下的导数。通过该功能,就可使用基于梯度的方法对参数(变量,张量)进行优化(即学习或训练)。仅需要执行tensor.backward()
函数,就可通过反向传播算法自动完成。PyTorch要求显式设置可求导,否则默认不能对该变量求导;在张量生成时,设置requires_grad=True
。
4种调整张量形状的函数:
- view:用于设置新的张量形状,需保证张量元素总个数不变;要求连续,is_conuous函数判断一个张量是否为连续的。非连续张量,需先调用contiguous函数将其变为连续的。
- reshape:可直接对非连续张量进行形状调整,与view功能一致。
- transpose:用于交换张量中的两个维度,参数分别为相应的维;只能同时交换两个维度,若要交换更多维度,需要多次调用该函数。
- permute:需要提供全部维度信息作为参数,即便有些维度无须交换也需要提供。
广播:两个张量的形状不同时;首先对其中一个或同时对两个张量的元素进行复制,使其形状相同;然后在扩展之后的张量上再执行按元素运算。
索引与切片:与Python列表类似,PyTorch中也可对张量进行索引和切片操作,规则也基本一致,即索引值是从0开始的,切片[m:n]
的范围是从m开始,至n的前一个元素结束。不同的是,PyTorch可对张量的任意一个维度进行索引或切片。
升维:通过调用torch.unsqueeze(input, dim, out=None)
函数,对输入张量的dim位置插入维度1,并返回一个新张量。与索引相同,dim值也可为负数。
降维:使用torch.squeeze(input, dim=None, out=None)
函数,在不指定dim时,张量中形状为1的所有维都将被除去。
py
import torch
torch.empty(2, 3)
torch.rand(2, 3)
torch.randn(2, 3)
torch.zeros(2, 3, dtype=torch.long)
torch.tensor([[1, 2, 3], [4, 5, 6]])
torch.arange(10)
torch.rand(2, 3).cuda()
torch.rand(2, 3).to("cuda")
torch.rand(2, 3, device="cuda")
预训练数据
中文维基百科快照内容
文件名 | 内容 | 大小/MB |
---|---|---|
zhwiki-latest-abstract.xml.gz | 所有词条摘要 | ≈147 |
zhwiki-latest-all-titles.gz | 所有词条标题 | ≈33 |
zhwiki-latest-page.sql.gz | 所有词条标题及摘要 | ≈204 |
zhwiki-latest-pagelinks.sql.gz | 所有词条外链 | ≈890 |
zhwiki-latest-pages-articles.xml.bz2 | 所有词条正文 | ≈1952 |
WikiExtractor:Python工具包,专门用于处理维基百科的快照。
OpenCC:中文繁简体转换工具。可将简体中文、繁体中文(包括中国香港繁体、中国台湾繁体)和日本新字体等中文进行互转。使用:
py
python convert_t2s.py input_file > output_file
convert_t2s.py
内容如下:
py
import sys
import opencc
# 载入繁简体转换配置文件
converter = opencc.OpenCC("t2s.json")
f_in = open(sys.argv[0], "r")
for line in f_in.readlines():
line = line.strip()
line_t2s = converter.convert(line)
t2s.json
配置文件如下:
json
{
"name": "Traditional Chinese to Simplified Chinese",
"segmentation": {
"type": "mmseg",
"dict": {
"type": "ocd2",
"file": "TSPhrases.ocd2"
}
},
"conversion_chain": {
"dict": {
"type" : "group",
"dicts": [
{
"type": "ocd2",
"file": "TSPhrases.ocd2"
},
{
"type": "ocd2",
"file": "TSCharacters.ocd2'
}
]
}
}
}
数据清洗:
- 删除空的成对符号,例如
()《》【】[]
等; - 删除
<br>
等残留的HTML标签; - 删除不可见控制字符,避免意外导致数据处理中断。
CC-Net:Facebook推出的Python工具,可用于获取Common Crawl数据(包含超过7年的网络爬虫数据集:原始网页数据、元数据提取和文本提取),并提供一套相对完整的数据处理流程。
HuggingFace Datasets特点:
- 数据集数目多;
- 兼容性好;
- 数据读取效率高;
- 丰富的评价方法。
py
pip install datasets
from datasets import list_datasets, load_dataset
from datasets import list_metrics, load_metric
datasets_list = list_datasets() # 全部数据集列表
dataset = load_dataset('sst', split='train') # 加载SST(Stanford Sentiment Treebank)数据集(训练数据部分)。在第一次执行时,程序会自动下载相应的数据集并放入本地的缓存目录中,当下次再运行时,会直接从本地加载
metrics_list = list_metrics() # 全部评价方法
','.join(metrics_list)
# 输出:accuracy, bertscore, bleu, bleurt, comet, coval, f1, gleu, glue, indic_glue, meteor, precision, recall, rouge, sacrebleu, sari, seqeval, squad, squad_v2, super_glue, wer, xnli
accuracy_metric = load_metric('accuracy') # 加载准确率评价方法
results = accuracy_metric.compute(references=[0, 1, 0], predictions=[1, 1, 0]) # 通过参考答案(references)与预测结果(predictions)的对比,计算准确率
用户还可增加自定义评价方法,甚至提交到HuggingFace Hub上供他人使用。
神经网络基础
多层感知器模型
使用感知器模型时,有两个棘手的问题需要加以解决:
- 如何将一个问题的原始输入转换成输入向量 x x x,即特征提取;NLP中,就是如何用数值向量表示文本;
- 如何合理地设置权重 w w w和偏差项 b b b(模型参数),即参数学习(参数优化或模型训练)。
两类模型:
- 分类:输出结果为离散数值;
- 回归:Regression,与分类模型的本质区别在于输出的结果是连续的实数值。
线性回归是最简单的回归模型,将输出 y y y建模为对输入 x x x中各个元素的线性加权和,最后也可以再加上偏差项 b b b,即 y = w 1 x 1 + w 2 x 2 + ⋅ ⋅ ⋅ + w n x n + b = w ⋅ x + b y=w_1x_1+w_2x_2+···+w_nx_n+b=w·x+b y=w1x1+w2x2+⋅⋅⋅+wnxn+b=w⋅x+b。
线性回归输出值的大小(值域)是任意的,有时需要将其限制在一定的范围内。实现此功能的函数,被称为激活函数。其中,最有名的是Logistic函数: y = L 1 + e − k ( z − z 0 ) y=\frac{L}{1+e^{-k(z-z_0)}} y=1+e−k(z−z0)L
该函数能将y的值限制在0( z z z趋向于负无穷大)到 L L L( z z z趋向于正无穷大)之间,当 z = z 0 z=z0 z=z0时, y = L / 2 y=L/2 y=L/2; k k k控制函数的陡峭程度。若 z = w 1 x 1 + w 2 x 2 + ⋅ ⋅ ⋅ + w n x n + b z=w_1x_1+w_2x_2+···+w_nx_n+b z=w1x1+w2x2+⋅⋅⋅+wnxn+b,此模型又被称为Logistic回归模型。经常被用于分类问题。
L = 1 , k = 1 , z 0 = 0 L=1,k=1,z_0=0 L=1,k=1,z0=0时,Logistic函数特化为Sigmoid函数,其导数比较容易求得。
Softmax回归,多分类问题,对多个分数使用指数函数进行归一化计算,并获得一个输入属于某个类别的概率。
除了线性模型外,还有非线性问题(如异或问题,Exclusive OR,XOR),即无法使用一条直线、平面或超平面来分割成不同的类别的问题,解决非线性问题的模型,即非线性模型。
多层感知器(Multi-layer Perceptron,MLP)是解决线性不可分问题的一种解决方案,堆叠多层线性分类器,并在中间层(隐含层)增加非线性激活函数。如ReLU(Rectified Linear Unit),定义为当某一项输入小于0时,输出为0;否则输出相应的输入值。
MLP解决异或问题的两个关键的技术:
- 增加一个含两个节点的隐含层
- 引入ReLU
通过设置恰当的参数值,将在原始输入空间中线性不可分的问题映射到新的隐含层空间,使其在该空间内线性可分。
输入层和输出层的大小一般是固定的,与输入数据的维度以及所处理问题的类别相对应,而隐含层的大小、层数和激活函数的类型等需要根据经验以及实验结果设置,它们又被称为超参数。一般来讲,隐含层越大、层数越多,即模型的参数越多、容量越大,多层感知器的表达能力就越强,但是此时较难优化网络的参数。而如果隐含层太小、层数过少,则模型的表达能力会不足。为了在模型容量和学习难度中间寻找到一个平衡点,需要根据不同的问题和数据,通过调参过程寻找合适的超参数组合。
卷积神经网络
在多层感知器中,每层输入的各个元素都需要乘以一个独立的参数(权重),这一层又叫作全连接层(Fully Connected Layer)或稠密层(Dense Layer)。
使用一个小的稠密层提取这些局部特征,如图像中固定大小的像素区域、文本中词的N-gram等。为了解决关键信息位置不固定的问题,可以依次扫描输入的每个区域,该操作又被称为卷积(Con-volution)操作。其中,每个小的、用于提取局部特征的稠密层又被称为卷积核或滤波器。
卷积核的构造方式大致有两种:
- 使用不同组的参数,并且使用不同的初始化参数,获得不同的卷积核;
- 提取不同尺度的局部特征,如在情感分类中提取不同大小的N-gram。
二维卷积:对于图像等数据,卷积核不但需要横向滑动,还需要纵向滑动。
三维卷积。
PyTorch的torch.nn
包中使用Conv1d、Conv2d或Conv3d类实现卷积层,分别表示一维卷积、二维卷积和三维卷积。
Conv1d的构造函数至少需要提供三个参数:
- in_channels:为输入通道的个数,在输入层对应词向量的维度;
- out_channels:为输出通道的个数,对应卷积核的个数;
- kernel_size:为每个卷积核的宽度。
当调用该Conv1d对象时,输入数据形状为(batch, in_channels, seq_len)
,输出数据形状为(batch, out_channels, seq_len)
,其中在输入数据和输出数据中,seq_len
分别表示输入的序列长度和输出的序列长度。
池化:Pooling,卷积操作输出的结果进行进一步聚合的过程。常用池化操作:最大池化、平均池化和加和池化等。池化操作的好处是可解决样本的输入大小不一致的问题。以最大池化为例,其含义是仅保留最有意义的局部特征。
循环神经网络
注意力模型
注意力机制:为了解决序列到序列模型记忆长序列能力不足的问题;不光考虑前一个时刻的状态和已经生成的单词,还考虑当前要生成的单词和源语言句子中的哪些单词更相关,即更关注源语言的哪些词。
自注意力机制:表示序列中某一时刻的状态时,可以通过该状态与其他时刻状态之间的相关性(注意力)计算,即所谓的观其伴、知其义。
通过自注意力机制,可以直接计算两个距离较远的时刻之间的关系。而在循环神经网络中,由于信息是沿着时刻逐层传递的,因此当两个相关性较大的时刻距离较远时,会产生较大的信息损失。
要想真正取代循环神经网络,自注意力模型还需要解决如下问题:
- 在计算自注意力时,没有考虑输入的位置信息,因此无法对序列进行建模;
- 输入向量 x i xi xi同时承担三种角色,即计算注意力权重时的两个向量以及被加权的向量,导致其不容易学习;
- 只考虑两个输入序列单元之间的关系,无法建模多个输入序列单元之间更复杂的关系;
- 自注意力计算结果互斥,无法同时关注多个输入。
Transformer模型的核心技术点:
- 融入位置信息
- 输入向量角色信息
- 多层自注意力
- 自注意力计算结果互斥
Transformer模型的优缺点
优点:
- 能够直接建模输入序列单元之间更长距离的依赖关系,对长序列建模的能力更强;
- 更快的训练速度。
缺点:参数量过于庞大。
有两种引入位置信息的方式:
- 位置嵌入:Position Embeddings,和词嵌入类似,即为序列中每个绝对位置赋予一个连续、低维、稠密的向量表示;
- 位置编码:Position Encodings,使用函数,直接将一个整数(位置索引值)映射到一个 d d d维向量上。
训练神经网络模型
模型训练或学习:寻找一组优化参数的过程。
损失函数:评估一组参数的好坏的准则;用于衡量在训练数据集上模型的输出与真实输出之间的差异。其值越小,模型输出与真实输出越相似,可认为此时模型表现越好。不过如果损失函数的值过小,模型就会与训练数据集过拟合(Overfit),反倒不适用于新数据。避免过拟合的技术,如正则化(Regularization)、丢弃正则化(Dropout)和早停法(Early Stopping)等。
两种常用的损失函数:
- MSE:均方误差,Mean Squared Error,指的是每个样本的平均平方损失
- CE:交叉熵,Cross-Entropy,常用于分类问题,比MSE学习速度更快
更本质地讲,CE公式右侧是对多类输出结果的分布(伯努利分布)求极大似然中的对数似然函数(Log-Likelihood)。CE也被称为负对数似然损失(Negative Log Likelihood,NLL)。
CE学习速度更高的原因:当模型错误较大时,即对正确类别的预测结果偏小(趋近于0),负对数的值会非常大;而当模型错误较小时,即对正确类别的预测结果偏大(趋近于1),负对数的值会趋近于0。这种变化是呈指数形的,即当模型错误较大时,损失函数的梯度较大,因此模型学得更快;而当模型错误较小时,损失函数的梯度较小,此时模型学得更慢。
梯度的物理意义是函数值增加最快的方向,即,沿着梯度的方向更加容易找到函数的极大值;反之,沿着梯度相反的方向,更加容易找到函数的极小值。
梯度下降算法,循环的终止条件根据实际情况有多种,如给定的循环次数、算法两次循环之间梯度变化的差小于一定的阈值和在开发集上算法的准确率不再提升等。
小批次梯度下降:Mini-batch Gradient Descent,当训练数据的规模较大时,若每次都遍历全部训练数据来计算梯度,算法的运行时间会非常久。为提高算法运行速度,每次可随机采样一定规模的训练数据来估计梯度。与原始梯度下降法相比,小批次梯度下降法每次计算的梯度可能不那么准确,但由于其梯度计算的速度较高,因此可通过更多的迭代次数弥补梯度计算不准确的问题。小批次的数目被设为 b = 1 b=1 b=1时,则被称为随机梯度下降(Stochastic Gradient Descent,SGD)。
除梯度下降外,PyTorch还提供其他优化器,如Adam、Adagrad和Adadelta等,改进思路包括动态调整学习率、对梯度累积等。
py
情感分类实战
词袋模型在表示序列时,不考虑其中元素的顺序,而是将其简单地看成是一个集合。
词性标注实战
与情感分类类似,可以将词性标注任务看作多类别文本分类问题,即取目标词的上下文词作为输入,目标词的词性作为输出类别。
参考
- 自然语言处理:基于预训练模型的方法