自然语言处理基础学习笔记
自然语言处理基础学习笔记
自然语言处理简介
自然语言处理(Natural Language Processing,简称 NLP)是计算机科学与语言学的交叉领域,旨在使计算机能够“理解”和“生成”人类语言。换言之,NLP让计算机可以处理文本或语音等语言数据,从中提取信息或作出智能反应。自然语言处理涵盖了多个层次,包括词法分析(如分词、词性标注)、句法分析(分析句子的语法结构)、语义理解(理解句子的含义)、语用分析(结合上下文和常识理解)等。通过NLP技术,我们得以实现机器翻译、智能问答、语音助手、聊天机器人等丰富的应用。
NLP的应用任务十分广泛,常见的有:将文本自动归类的文本分类(如垃圾邮件检测、新闻分类),判断文本情感倾向的情感分析(如评论中是正面还是负面情绪),识别文本中特定实体名称的命名实体识别(如人名、地名提取),生成文本摘要的自动摘要,把一种语言翻译成另一种的机器翻译,让计算机回答问题的问答系统,以及对话式的聊天机器人等。每个任务都有其独特的挑战,但通常都需要经过类似的建模流程,将语言数据转化为计算机可处理的形式并训练模型来完成任务。
NLP建模流程
解决NLP任务的一般流程通常包含以下几个步骤:
- 数据收集与预处理:首先获取足够的语料数据,并对原始文本进行预处理。这包括分词(将文本拆分为单词或词语序列,中文需要特别进行分词)、规范化(如转换为小写、去除标点符号和特殊符号)、停用词过滤(去除诸如“的、了、在”等高频无意义词)等步骤。良好的预处理可以降低噪声对模型的干扰,提高后续特征表示的质量。
- 特征表示(向量化):将文本转换为数值特征向量以便于模型处理。传统方法有词袋模型和TF-IDF向量,将每个文档表示为词频向量;进阶方法有词向量嵌入(如Word2Vec、GloVe),将词映射为稠密向量,捕捉语义关系;以及上下文嵌入(如BERT等预训练模型生成的向量),可根据上下文动态表示词义。这一步的目标是把不定长的文本转换为定长的、能被机器学习模型理解的向量表示。
- 模型选择与训练:根据任务选择合适的模型并进行训练。在早期,常用的有朴素贝叶斯、逻辑回归、支持向量机(SVM)等传统机器学习模型,需人为设计特征;近年来,基于深度学习的模型如卷积神经网络(CNN)、循环神经网络(RNN/LSTM)以及Transformer架构的预训练模型(如BERT、GPT等)成为主流。训练过程中,将特征向量作为模型输入,通过前向传播计算预测,与标注的真实结果比较计算损失函数,再通过反向传播不断调整模型参数。通常需要迭代多个epoch直到模型在验证集上效果收敛。
- 模型评估与优化:在独立的测试集上评估模型性能。对于分类任务常用准确率、精确率、召回率、F1分数等指标;对于生成类任务则有BLEU、ROUGE等指标。根据评估结果,可以调整模型参数、引入正则化防止过拟合、或尝试不同模型结构等改进性能。同时也要进行错误分析,看看模型在哪些情况下出错,以便有针对性地改进。
- 部署与应用:当模型效果满意后,将其部署到实际系统中,为用户提供服务。例如,将文本分类模型部署到邮件服务器实现垃圾邮件过滤,将问答模型集成到聊天机器人后台等。在部署时还需考虑模型的效率、可扩展性,以及对输入进行安全过滤和异常处理等。
上述流程并非一成不变,不同任务可能会有额外步骤或特殊处理。但整体而言,从数据到特征到模型再到评估应用,是自然语言处理建模的基本路径。在掌握了这一流程后,我们再来深入探讨NLP中的一些核心理论概念和技术,包括语言模型、词向量、句法和语义分析等。
语言模型
语言模型(Language Model, LM)是NLP中的基础概念,用于对文本序列的概率进行建模。直观来说,语言模型可以判断一个句子是否符合语言习惯,即给出一个句子是“自然”的概率高低。例如,语言模型会认为“我今天吃了一个苹果”出现的概率要远高于“苹果个我今天吃了一”,因为前者符合人类语言的语法和常识,而后者不通顺。形式化定义上,给定一个由词或字组成的序列 $w_1, w_2, \dots, w_m$,语言模型试图估计这个序列出现的联合概率$P(w_1, w_2, ..., w_m)$。语言模型的目标就是让有意义的句子概率高,而无意义的组合概率低。这对很多应用都很重要,例如在输入法中选择最可能的下一个字词、在语音识别中选择最可能的转写结果、在机器翻译中约束生成的译文符合目标语言习惯等。
计算$P(w_1,\dots,w_m)$可以利用链式法则展开为条件概率的乘积,即:
P(w1,w2,…,wm)=P(w1)⋅P(w2∣w1)⋅P(w3∣w1,w2)⋯P(wm∣w1,…,wm−1).P(w_1, w_2, \dots, w_m) = P(w_1) \cdot P(w_2 \mid w_1) \cdot P(w_3 \mid w_1, w_2) \cdots P(w_m \mid w_1,\dots,w_{m-1}).P(w1,w2,…,wm)=P(w1)⋅P(w2∣w1)⋅P(w3∣w1,w2)⋯P(wm∣w1,…,wm−1).
直接计算上述概率非常困难,因为对于长句子条件概率涉及的历史非常长,数据稀疏且计算复杂。为此,人们引入了马尔可夫假设,假设每个词的出现只与前面的固定数量的词有关,而不需要考虑更远的历史。这就形成了N元语法模型(N-gram)。例如,当采用二元模型(bigram)时,假设每个词只与前一个词相关,则:
P(wm∣w1,...,wm−1)≈P(wm∣wm−1)。P(w_m \mid w_1, ..., w_{m-1}) \approx P(w_m \mid w_{m-1})。P(wm∣w1,...,wm−1)≈P(wm∣wm−1)。
更一般地,N元模型假设$P(w_i \mid w_{1},...,w_{i-1}) \approx P(w_i \mid w_{i-N+1},...,w_{i-1})$。通过统计大量语料中相邻词的共现频率,可以估计这些N元概率。然而N元模型也存在明显局限:一方面,N取值太小难以捕获长距离依赖;但N过大又会导致数据极度稀疏,需要海量语料才能估计可靠。据研究,二元模型要达到较优性能需要数以亿计的词语量,三元模型则需要数十亿规模的语料。为缓解数据稀疏,通常会采用平滑技术(如拉普拉斯平滑、Kneser-Ney平滑等)对概率估计进行修正。另外,语言模型输出的概率往往使用**困惑度(perplexity)**指标来衡量模型对测试集的预测能力,困惑度越低表示模型预测序列的困惑程度越小,即模型越好。
神经网络语言模型的出现极大改善了语言模型的效果。2003年Bengio等人提出将分布式表示(即词向量,下一节详述)引入语言模型中,用一个前馈神经网络同时学习词的低维表示和下一词预测。这种方法通过让每个词都对应一个稠密向量,大幅缓解了传统N元模型的维度灾难和数据稀疏问题。典型的神经语言模型结构是:输入前 $N!-!1$ 个词,通过嵌入层映射为向量并拼接,然后经过一系列隐藏层,最后用Softmax输出下一个词的概率分布。神经语言模型能够自动学到语言中的语法和一定语义模式,并作为副产品产出词向量参数。
随后,循环神经网络(RNN)语言模型进一步提升了建模长序列的能力。RNN通过隐藏状态的循环连接,可以在理论上融合任意长的上下文。但是标准RNN存在梯度消失问题,难以捕捉特别长距离的信息。为此,人们引入了门控机制的变种,如**长短期记忆网络(LSTM)**和门控循环单元(GRU),有效缓解了长期依赖问题,使语言模型能够记忆更长的上下文信息。
近年来,随着深度学习的发展和计算资源的增强,语言模型进入了预训练大模型时代。以Transformer架构为基础的模型(下文详述)如OpenAI的GPT系列、Google的BERT等,都是大规模预训练语言模型。它们在海量文本上训练,自监督学习语言模型任务(如下一个词预测或掩码词预测),学习到丰富的语言知识,然后可以通过微调适配到各类下游任务。这些大型语言模型拥有数以亿计甚至千亿的参数,被称为大语言模型(LLM),在语言生成和理解任务上表现出前所未有的强大能力。例如,GPT类模型作为生成式语言模型能够根据之前的文本续写内容,回答问题,对话等;BERT则作为双向语言模型提供了强大的文本表示,可用于分类、问答、推理等任务。在实际应用中,人们常常直接利用预训练模型作为新的语言模型范式,使得很多NLP任务的效果得到了突破性的提升。
简而言之,语言模型是NLP的底层支柱。无论是早期基于统计的N元模型还是现代深度学习的预训练模型,其核心都是在于掌握语言序列的规律,评估句子的合理性并提供对下一个词的预测能力。语言模型既是独立的任务(如文本生成),也是许多复杂NLP系统中的关键组件。
举个实际例子:搜索引擎会利用语言模型来做拼写校正或查询补全。当用户输入查询"how long is a football bame"时,其中“bame”是个错词。搜索引擎可以计算这个短语的语言模型概率,发现其值很低;而将“bame”替换为“game”的短语"how long is a football game"的概率要高得多。因此系统会建议用户“Did you mean: how long is a football game?”,这正是语言模型在背后发挥作用的实例。
词向量与分布式表示
词向量(也称词嵌入,Word Embedding)是指用一个稠密向量来表示词语的方法,是NLP中将符号化的词映射为数学空间的核心技术。最初,计算机处理文本是基于离散符号的表示,例如使用**独热编码(one-hot)**为每个词编号:给定词汇表大小为$V$,每个词用$V$维向量表示,在该词对应的位置为1,其余为0。这种表示简单但存在明显缺陷:向量维度随词汇表增长而增长,且不同词的one-hot向量彼此正交,无法体现任何词与词之间的关系或相似度。
语言学中有一个重要观点叫做分布式假设:词在相似的上下文中出现,则它们的语义也可能相似。据此,研究者提出用分布式表示(distributed representation)来表示词语,即让每个词对应一个低维的稠密实数向量,并通过学习使得语义相近的词向量在向量空间中距离也近。与独热向量相比,词向量通常维度小得多(比如100维、300维),每一维不再是某个特定词的指示器,而是隐含了某种语义特征。通过这种方式,词语间的相似性和类比关系可以用向量间的距离或方向来刻画。例如,在一个训练好的词向量空间中,“国王”(king)与“皇后”(queen)两个向量可能距离较近,“男子”(man)与“女人”(woman)的向量也相近,而且king - man + woman的向量结果会接近“queen”,展示出一定的类比推理能力。
获取词向量的方法主要有两大类:
- 基于统计的计数方法:如共现矩阵 + 降维。我们可以统计一个大型语料库中各个词与其他词共现的次数,构造一个$|V|\times|V|$的共现矩阵X,其中$X_{ij}$表示词$i$与词$j$在一定窗口内共同出现的次数。这种矩阵通常非常稀疏且高维,需要对其做降维处理(如奇异值分解SVD)得到低维表示。早期的潜在语义分析(LSA)就是典型方法,通过SVD从词-文档矩阵中提取潜在语义因素。随后GloVe模型将共现统计和矩阵因子分解思想结合,通过构造特定的目标函数分解共现频次。统计方法的优点是利用全局共现信息,得到的词向量包含一定的语义结构,例如GloVe能捕获类似“巴黎-法国”与“东京-日本”的对应关系。但其缺点是需要计算和存储整个共现矩阵,维度极高且需要大量预处理和内存,同时得到的向量主要基于全局共现统计,可能无法很好适应特定下游任务。
- 基于预测的上下文学习方法:这类方法直接通过神经网络在大规模语料上训练,使词向量在预测任务中被隐式学出。最有代表性的是Word2Vec模型,由Mikolov等人在2013年提出。Word2Vec包含两个架构:连续词袋模型(CBOW)和跳字模型(Skip-gram)。CBOW以周围上下文词预测中心词,Skip-gram则以当前词预测周围上下文。训练时,模型输入一个词(或上下文窗口),通过嵌入层查表得到词向量,然后在隐藏层进行非线性变换,输出层用Softmax计算目标词出现的概率,最大化真实目标词的概率。经过这样的训练,每个词的嵌入向量会逐渐调整,变得能够更有效地完成预测任务,这些向量就是我们想要的词向量。Word2Vec由于巧妙的架构和高效的训练,在大语料上可以学到质量很高的词向量。除Word2Vec外,后来还有一些改进方法,例如FastText通过考虑词内部的n-gram字符成分来获取词表示,能处理未登录词;以及前述的GloVe等。总体而言,预测类的方法得到的向量在捕捉语义关系上效果很好,并且计算效率高,是实际应用中广泛使用的词嵌入技术。
通过词向量,我们赋予了计算机“理解”词语含义的一种方式:即用数学空间中的点来表示词语的意义。两个语义相近的词(如“汽车”和“卡车”)会在向量空间中距离很近,而语义无关的词则距离较远。可以用降维可视化技术如t-SNE将高维词向量投影到二维平面,以观察这种聚类现象。词向量的语义还可以通过向量运算体现,例如经典的“王 - 男 + 女 = 后”示例,即$\mathbf{v}(\text{king}) - \mathbf{v}(\text{man}) + \mathbf{v}(\text{woman}) \approx \mathbf{v}(\text{queen})$,显示了词向量捕捉类比关系的能力。
然而,传统的词向量(例如Word2Vec训练所得)有一个固有缺陷:每个词只有一个静态向量表示,无法区分一词多义的情况。例如,“苹果”这个词在不同句子中可能指水果或公司,但静态词向量只能表示其中某种主导含义。如果训练语料中关于水果“苹果”的内容占多数,那么“苹果”的词向量主要会编码水果的语义,这在涉及科技公司Apple的文本中就会产生偏差。
为了解决多义词的问题,近年来提出了基于上下文的词表示。这类模型让词的表示随上下文变化,即同一个词在不同句子中会产生不同的向量。代表性成果有ELMo和BERT等。ELMo通过双向LSTM语言模型根据上下文动态生成词向量,实现了对多义词语义的区分;BERT更是预训练了深层Transformer网络,能够根据一个词左右两侧的上下文共同决定这个词的表示。例如,在BERT中,“苹果”在谈论水果的句子和谈论公司的句子中,会得到不同的向量表示,从而更精准地传达该词此时此刻的含义。BERT可以被看作一种生成上下文表示词向量的语言模型,在预训练后用于下游任务微调,取得了显著效果。有了上下文嵌入,我们的NLP模型在应对多义现象时就更加游刃有余。
总而言之,词向量技术让计算机对词语有了“语义上的感觉”。从最初的独热表示,到后来低维稠密的Word2Vec/GloVe,再到如今上下文敏感的BERT等模型,词向量的演进贯穿了NLP发展的始终。它既是很多简单模型的特征基础,又可以内嵌在复杂模型中随训练更新。在构建NLP模型时,选择和使用合适的词表示手段,是取得良好性能的重要因素之一。
句法分析
句法分析(Syntactic Parsing)旨在分析一个句子的内部结构和成分关系,即确定句子由哪些语法成分组成以及它们之间的层次从属关系。换言之,句法分析就是让计算机读懂一句话的语法结构,如找出主语、谓语、宾语,或者划分出名词短语、动词短语等部分。句法分析是自然语言理解的重要基础,对于语义理解、机器翻译、对话理解等应用至关重要。只有搞清了句子的结构,后续才能更好地理解句子表达的意义。
句法分析通常有两种主要的表示方式,即成分句法分析和依存句法分析:
- 成分句法分析(Constituency Parsing),也叫短语结构分析。它基于短语结构文法(短语句法),将句子逐层分解成短语成分,直至词语级别,形成一棵语法树。树的每个节点对应一个短语成分(如名词短语NP、动词短语VP),叶节点是具体的词语。例如句子“[我] [今天] [吃了] [一个苹果]”,成分句法分析可能生成如下结构:(略)顶层是句子S,下面分成名词短语(主语)“我”、副词短语“今天”、动词短语(谓语部分)包含动词和宾语等。成分分析强调短语结构,体现的是句子的逐层嵌套关系。
- 依存句法分析(Dependency Parsing)则直接建立句子中词与词的依存关系。它将句子表示为一个依存树,其中每个词作为节点,通过有向边连接,边表示一个词是另一个词的从属(修饰或补充)关系。例如在句子“我今天吃了一个苹果”中,依存解析可能标注“吃-主语-我”、“吃-宾语-苹果”、“苹果-数量修饰-一个”、“吃-状语-今天”等依存关系。依存句法突出词语之间的关系,如主谓关系、动宾关系、修饰关系等,让我们直接看到句子中谁是动作执行者、动作作用于何物等信息。
这两种句法分析方法关注的角度不同,但本质目标都是解析句子的结构。成分分析偏重层次结构,依存分析偏重二元关系,可以根据需要选择使用。在下图中可以看到二者的对比(成分树与依存树)。简单来说,成分句法像把句子按语法规则逐级分块,而依存句法则像用语法关系链接句子中的词。两者提供的视角各有优势:成分句法对于生成类任务、句子重组有用,而依存句法非常直观地支持信息抽取、语义角色标注等任务。
句法分析的实现可以基于语法规则或者数据驱动的统计方法。早期方法是定义一套上下文无关文法(CFG)产生式规则,然后使用解析算法(如自顶向下、CKY算法等)从句子出发搜索一棵符合文法的语法树。上下文无关文法由非终结符、终结符、产生式规则和起始符组成。例如简单的规则:S → NP VP(句子可由名词短语+动词短语构成),NP → Det N(名词短语可由限定词+名词构成),等等。基于这些规则,解析算法试图对输入句子推导出符合规则的树形结构。这类方法需要人工制定文法规则,或从树库语料中学习概率上下文无关文法(PCFG)的规则概率,以选择最有可能的解析树。但即使有概率,规则方法在实际应用中仍面临文法不完备、歧义多等挑战。此外还有依存文法的规则方法,但类似地存在规则编写繁琐、覆盖有限的问题。
统计学习的方法试图自动从已标注树库中学习解析模型。例如,早期的概率上下文无关文法通过计算文法规则在树库中的出现频率来评估解析树概率,使用类似CKY的算法找到概率最高的树。后来出现了判别式的解析模型,如最大熵模型、感知机,以及著名的基于转移的依存解析算法等。其中转移依存解析利用栈操作模拟人工分析过程,通过机器学习学得动作序列选择策略,可以高效地输出依存树。**条件随机场(CRF)**等序列标注模型也可用于简化的句法任务(如浅层句法分析/短语边界标注)。
近年来,深度学习方法大幅推进了句法分析的性能。基于神经网络的句法解析通过端到端的方法学习,从词向量输入直接预测句法结构。对于依存解析,有基于双向LSTM的图模型/转换模型,著名的有Chen&Manning的神经转移依存解析器、以及Kiperwasser等人的神经图依存解析器等,都取得了接近或超越传统方法的准确率。对于成分解析,采用递归神经网络或Chart-based的神经模型(如结合自注意力机制)也取得了显著效果。预训练模型BERT的加入进一步提升了解析性能,目前最好的解析器往往是在自定义的解析架构中融合了BERT的词表示。在中文处理中,一般还需要先进行中文分词和词性标注作为句法分析的前置步骤,以确定词边界和词类信息,否则直接对汉字序列做句法树会非常复杂。
句法分析结果常用于支撑更高层的NLP任务。例如机器翻译中,源语言句法树的信息有助于生成更符合目标语言语法的译文;问答系统中,解析用户问题句可以帮助确定问句的关注焦点和回答型式;信息抽取中,依存关系有助于识别主语、谓语、宾语,从句子中提取“谁对谁做了什么”的事实。在对话系统里,语法解析可以用于理解复杂查询意图。甚至在文本生成(如智能写作辅助)中,解析树也可以用于保证生成句子的结构合理。
概括来说,句法分析赋予计算机初步的“读句子”的能力——不仅逐词读取,还能读出句子的层次结构和修饰依存。现代NLP系统往往内置句法分析的模块或者受益于预训练模型已经掌握的隐含句法知识。当我们希望深入理解文本,句法分析几乎是一个绕不开的步骤:它为我们提供了通向语义理解的大门。
语义理解
如果说句法分析解决的是句子“怎么说”的问题,那么语义理解解决的就是句子“说了什么”的问题,即理解语言表达的意义。语义理解是让计算机真正“读懂”人类语言的关键环节,涉及多个方面的内容,包括词汇层面的含义、句子层面的逻辑,以及更深层的隐含意义。
词汇语义是语义理解的基础层面。例如,同义词(意思相同或相近的词),反义词(意思相反的词),上位词和下位词关系(如“动物”是“狗”的上位词)等。这方面有著名的人工构建的词汇语义知识库,如WordNet,将英文中的名词、动词等按照同义词集合成同义词集,并标注它们之间的上下位、部件等关系。这为计算机提供了词与词之间语义关联的知识。对于中文,也有类似知网(HowNet)这样的知识库,给出了词汇的义原表示和语义关系。词汇层面的任务还包括词义消歧(WSD),即当一个词有多个含义时(如“苹果”可以是水果或公司),根据上下文判定此处取哪种含义。这通常需要结合上下文信息以及可能的外部知识。
句子语义超越了单个词,关注整句话表达的意义。这包括理解句子所传达的事实、意图、疑问等。例如语义角色标注(Semantic Role Labeling, SRL)就是一项典型任务:找出句子中的谓语动词,以及与其相关的各个论元,以及论元所承担的语义角色(如施事、受事、时间、地点等)。例如在句子“[小明]在[公园]踢[足球]”,SRL会识别出动词“踢”,并标注“小明”是施事(做动作的人),“足球”是受事(动作作用的对象),“在公园”是地点。在理解句子语义时,我们还常需要进行指代消解,即搞清楚代词等指代了文中的哪个实体(例如“张三告诉李四他通过了考试”,这里“他”究竟指谁?),这对正确理解句意非常重要。
语义解析(Semantic Parsing)更进一步,试图把自然语言转换为机器可以直接操作的逻辑表示或可执行操作。比如将一句问句解析成数据库查询语言SQL,或者解析成一条逻辑表达式(如一阶逻辑公式)表示其中蕴含的命题。这在问答系统和对话系统中很有用,比如用户问“2010年巴西世界杯的冠军是谁?”,语义解析可以将其转成查询数据库的语句,从知识库中找出答案。又如在智能家居中,“把客厅的灯调暗”需要被解析成相应的控制指令。语义解析往往需要构建特定领域的语义标记体系和数据来训练模型,如语义解析比赛GeoQuery把地理问句解析成Prolog查询等等。这方面由于需要标注逻辑形式数据,成本较高,但成功解析后结果可以直接用于推理和操作,因而价值很大。
除了上述内容,知识图谱也是语义层的重要组成部分。知识图谱以图结构存储现实世界中的实体及其关系,例如存储“巴黎-属于-法国”、“巴拉克·奥巴马-出生地-夏威夷”这样的事实三元组。有了知识图谱,NLP系统在理解文本时就有了外部背景常识的支持。比如句子“奥巴马访问了巴黎”,解析出其中实体“奥巴马”和“巴黎”后,可以链接到知识图谱中对应的节点,获得“奥巴马是美国前总统”、“巴黎是法国首都”等背景知识,从而更好地理解这句话的含义甚至潜在意图。将知识图谱与NLP结合,可以提升任务的效果,如更精准的问答、关系抽取以及对话理解等。
语义理解的难点在于自然语言的歧义和复杂性。单词层面有多义词,句子层面还有结构歧义(一句话可能有多种解析,例如经典的句子“我看到那个男孩使用望远镜”,是“我用望远镜看到男孩”还是“我看到拿着望远镜的男孩”?),还有言外之意和常识。例如句子“顾客:这鱼太咸了。服务员:我去给您拿杯水。” 服务员的回复看似不直接,其实是理解了顾客话里的抱怨语气并提供解决方案。这种语用学层面的理解,包括理解说话者意图(语言行为)、讽刺和反语、隐喻等,更是语义理解中最具挑战的部分。目前的大模型(如ChatGPT等)在这方面有所突破,因为它们通过海量语料学习到了很多隐含模式和常识,在语义理解上表现出惊人的能力。
语义理解相关的任务还有很多,例如:
- 文本蕴含识别:判断一句话是否能从另一句话推理得到,或者二者是否矛盾。这要求深入理解句意并进行逻辑推断。
- 情感倾向分析:虽属于文本分类范畴,但需要对主观语义进行分析,之前章节已讨论。
- 语义相似度计算:判定两段文本在语义上的相似程度,常用于信息检索、复述识别等。
- 对话理解:理解多轮对话中的上下文语义,识别用户真正意图。例如在任务型对话系统中,可能需要解析用户的话语对应哪个意图(订票?查天气?)以及相关槽位信息。
- 摘要生成:需要理解原文语义后抽取或生成高度概括的表述。
- 机器翻译:广义上也涉及语义,好的翻译需要准确传达原文语义。
需要指出的是,现代NLP模型(尤其是大型预训练模型)往往将句法和语义的知识融合在一起。例如BERT的训练让它在多层注意力中学到了不少语义模式,这使得在很多下游任务中,即便我们不显式做句法/语义分析,模型也能取得不错效果。然而,在有些专业场景下(如法律、医疗),或者需要高度精确理解/推理的任务上,显式的语义分析过程仍然有意义:它能够提供可解释的中间表示,让我们确定机器对语言的理解是符合人类常识和逻辑的。
总的来说,语义理解让机器从“看到了什么单词”上升到“明白这句话想表达什么”。这是一条从词汇、句法,到语义网络和逻辑的渐进之路。虽然完全的人机等价的语言理解尚未实现,但随着知识获取、更强大的模型和推理机制的发展,计算机对自然语言的理解深度也在不断逼近人类。语义理解既是NLP中最迷人的课题之一,也是实现真正智能语言交互的必经之路。
(值得一提的是,在实际系统中,语义理解的诸多组件常被合称为自然语言理解(NLU),与自然语言生成(NLG)相对,分别对应计算机理解人说的话和计算机产生自己的语言输出。)
常见NLP任务与方法
文本分类
文本分类是指将给定的文本(句子、段落或文档)自动归入预先定义的类别标签的过程。它是NLP中最基础也最重要的任务之一。想象图书馆的管理员需要根据书的主题将其归类上架,文本分类就相当于教会计算机阅读文本内容并作出正确的分类决定。应用场景非常广泛,例如:垃圾邮件过滤(判定一封邮件是正常还是垃圾)、新闻分类(识别新闻是体育、财经、科技等类型)、法律文书分类、社区问答帖子归档、甚至对对话或用户反馈做意图识别等。文本分类可以是单标签分类(每个文本归属于一个类别)或者多标签分类(一个文本可同时属于多个类别)。
基本流程:文本分类一般遵循之前所述的标准NLP建模流程。从数据来看,需要准备标注了类别的训练文本(如垃圾邮件过滤需要有标记好的垃圾和正常邮件样本)。预处理时,将文本进行清洗和分词,并根据需要去除停用词、提取词干等。然后进行特征表示,如使用Bag of Words或TF-IDF将文本向量化。这些传统表示方法简单直观但存在忽略词序和上下文等缺点。因此在现代文本分类中,通常会用预训练的词向量来表示文本中的词,再将它们组合(如取平均、用CNN/RNN编码)得到文本向量;或者干脆使用BERT等预训练模型直接输出整个句子的向量表示。接下来选择分类模型:早期常用朴素贝叶斯、支持向量机、逻辑回归等传统机器学习方法。它们速度快,效果在中小规模数据上也不错,但需要人工挑选特征。深度学习方法则包括卷积神经网络(CNN)、循环神经网络(RNN/LSTM),以及最新的Transformer架构(如BERT微调)等。深度模型能自动从词序和语义中提取高级特征,在大数据集上表现更佳。
训练模型时,一般使用交叉熵损失作为目标函数,通过梯度下降优化参数。训练完成后,在测试集上评估模型性能,指标通常用准确率(正确分类的比例)。在某些不均衡数据情况下,也会看精确率/召回率/F1等。一个好的文本分类器应该在看不见的数据上同样有高准确度,即具备良好的泛化能力。
举例来说,如果我们训练一个新闻分类模型,它学会了根据文章的词语分布来判断类别。假如“球队”、“进球”、“主场”这些词频繁出现,模型就倾向预测类别为体育新闻;而如果出现“股价”、“公司”、“交易”则多半是财经新闻。传统模型可能通过人工设计的关键词特征来实现这种效果,而深度模型则能自动从样本学习到这些区别模式。例如使用LSTM的模型,可以“阅读”整篇文章,记忆哪些词出现,最终在输出层做出预测。另外,现代做法中还可以直接使用预训练模型fine-tune来分类:比如取一个预训练好的BERT,在其最后加上一层分类层,用我们手头的分类数据对BERT稍微训练一下,使其适应该分类任务。这往往能取得极好的效果,因为BERT已经掌握了大量语言知识,只需学会如何将这些知识应用到具体分类决策上。
高级主题:在一些场景下,文本分类可能面对极端不均衡的问题,如垃圾邮件中正常邮件占大头、垃圾邮件占小头,或者多分类任务中某些类别样本特别少。对此可以采用调整损失函数权重、过采样/欠采样等手段。还有在线文本分类需求,要求模型快速更新适应新主题,涉及到增量学习。对于多标签分类(如一篇文章可能属于科技和教育两个板块),通常使用Sigmoid输出多标签并针对每个标签独立判别。值得注意的是,分类模型往往很依赖训练数据领域,如果拿金融新闻分类模型去分类体育新闻,效果会很差。因此实际应用中,要确保模型训练语料和应用场景的一致性,或使用领域自适应技术。最后,模型可解释性在文本分类中也很重要,特别当用于医疗、法律等领域时,人们希望知道模型是根据哪些依据判定了某类别。这时可以采用一些可解释方法,如提取最能影响模型决策的n个词显示给用户参考等。
情感分析
情感分析(Sentiment Analysis,又称意见挖掘)是一种特殊的文本分类任务,目标是识别文本所表达的主观情感极性,判断其态度是正面、负面还是中性。情感分析应用极为广泛,尤其在商业和社会领域:例如分析产品评论以了解用户满意度,分析社交媒体上的讨论以监测舆情,在电影评论、饭店点评中自动打分等。对企业来说,情感分析可以帮助及时获取公众反馈;对个人用户,也可用于过滤社交平台上的消极内容等。
情感分析可以在不同粒度上进行:文档级情感分析将整篇文档视为一个整体判断情感倾向,句子级情感分析则针对单一句子进行判断,更细的是方面级情感分析(Aspect-level),在一段文本中找出针对不同方面(如一款手机的屏幕、续航、价格等)的评价分别是好是坏。例如一句评价:“这手机屏幕很清晰,但电池太不耐用了”,整体可能不好说是正还是负,但针对“屏幕”的情感是正面的,针对“电池”的情感是负面的。情感分类的类型也可以不仅局限于二分类(正/负)或三分类(正/中/负),有时会定义更细的情绪类别(愤怒、喜悦、悲伤等)或情感强度(如给出1-5的星级评分)。
情感分析的方法主要分为两大类:基于词典的规则方法和基于机器学习的统计方法。
- 基于情感词典的方法:这是最传统的情感分析技术,不需要训练数据,依赖预先构建的情感词典。情感词典汇集了带有主观情感倾向的词语,并标注其极性(正向或负向)和强度。例如英文的SentiWordNet、VADER,中文的知网情感词典、大连理工情感本体等。分析时,算法扫描文本中的词,如果在积极词典中则加分,在消极词典中则减分,最后根据综合得分判断情感极性。同时要考虑否定词和程度副词的影响:如遇到否定词“不是”,通常反转后面情感;遇到“非常/有点”之类程度词,可以放大或缩小情感强度。词典法的优点是直观可解释,实现简单,对领域依赖小(只要有该领域的情感词典即可)。而且因为不用训练,不存在模型过拟合,计算效率也很高。但是它的缺点也很明显:首先难以处理复杂的语言现象,如讽刺和反语(“这真是太棒了”(语气讽刺)实际上是负面),词典方法往往误判。其次,效果高度依赖词典的覆盖度和质量,陌生的新兴词、俚语网络用语往往不在词典中导致漏判。再次,它无法考虑上下文,比如“这部电影一点也不恐怖”,词典看到“恐怖”可能判断为负面,但整句实际上是否定了“恐怖”。这些局限使得纯粹基于词典的方法在复杂任务中表现有限。
- 基于机器学习的方法:这包括传统机器学习和深度学习方法。对于机器学习,情感分析被当作一个有监督的文本分类问题,需要准备大量带情感标注的文本(如已知正面评论和负面评论),然后训练分类模型。前文提到的文本分类技术都适用:先抽取特征(例如使用BOW、TF-IDF表示,同时也可以结合一些情感词典特征作为辅助),然后训练一个分类器(如SVM、逻辑回归等)。这些方法在特定领域通常比词典法精度高,因为模型可以结合多种线索而非单词面值。例如可以捕捉到上下文消极模式,如“根本不 <积极词>”,学会这种组合其实表达的是负面。随着深度学习兴起,人们开始用神经网络解决情感分析。CNN可以从局部n-gram中学习情感特征,RNN能捕捉长距离依赖,如句首的否定作用于句尾的情感词。注意力机制也常用于情感分析,让模型学会关注句子中最能体现情感的关键片段。如今最先进的是使用预训练模型(如BERT)进行微调,让模型利用其丰富的语言知识来判断情感倾向。
机器学习方法的优点在于性能往往优于简单规则法,而且可随数据自动学习,不需要人工维护词典规则。不过它需要足够多的标注数据,训练过程也可能较耗时,模型结果的可解释性相对差一些(可以部分用SHAP、LIME之类解释)。在实际应用中,两类方法有时也结合使用:比如先用词典方法快速初筛,再用机器学习精细分类;或把情感词典匹配结果作为特征供模型使用。
情感分析的挑战除了讽刺、隐喻这类语言现象外,还有领域适应性的问题。同样一句话在不同领域可能表达不同倾向,如“这游戏真难”,在游戏测评中可能是褒义(有挑战性),但在别的场景通常是贬义。模型需要能够识别这种差别。为此可以通过领域自适应训练或者加入领域背景知识来改进。另一个挑战是细粒度情感如方面级分析,需要模型不仅判断整体情感,还要识别句中涉及哪些对象和属性(这实际上跟信息抽取结合了)。例如解析“这家酒店环境不错,但是服务太差”,需要提取出环境=正面,服务=负面。通常通过与命名实体识别或专有名词词典配合可以达到目的。
总的来说,情感分析让我们能够大规模地、自动地抽取文本中的主观倾向,为企业和个人提供有价值的洞察。在社交媒体时代,情感分析已成为了解民意、品牌监测、舆情管控的利器。而随着深度学习模型性能提升,我们也在迈向对更复杂情感(如讽刺幽默)和更细粒度情感理解的征途。
命名实体识别
命名实体识别(Named Entity Recognition, NER)是指从文本中识别出具有特定意义的实体,并将其归类到预定义的类别中。通俗地说,就是让计算机像用荧光笔“高亮”文本中的特殊名词——比如人名、地名、组织机构名、时间日期、货币金额、专业术语等等。这些“命名实体”通常是文本中最能携带信息的词,识别它们是很多上层应用(信息抽取、问答系统、知识图谱构建等)的基础。
举例:一句话“比尔·盖茨于1975年在新墨西哥州创立了微软公司”,命名实体识别应当找出“比尔·盖茨”(Person人名)、“1975年”(Time时间)、“新墨西哥州”(Location地名)和“微软公司”(Organization组织)。有了这些标注,我们就清楚句子涉及的人物、时间、地点和组织,这对于后续理解该句在讲什么很有帮助。
NER的应用非常广。例如从新闻中自动提取关键人物、地点和事件信息,供下游生成新闻摘要或建立知识库;搜索引擎借助NER更好地理解查询意图(如识别查询中的电影名、歌手名等)从而提供更精确结果;客服系统中分析用户咨询,提取其中产品名、地名等以匹配答案;医药领域识别医学文献中的蛋白质名称、基因名称、疾病名称等助力信息检索。总之,凡是涉及信息提取的场景,几乎都会用到命名实体识别技术。
技术实现方面,NER通常被建模为一个序列标注(Sequence Labeling)问题:对输入序列中的每个词(或字)打上一个标签,标签表示该词是否是某类实体的开始、中间、结束或非实体。例如最常用的是BIO标注方案:B-PER表示人名开头,I-PER表示人名内部,O表示不属于任何命名实体,等等。通过对每个词打标签,就可以框出实体并分类。整个序列标注可以通过多种方法实现:
- 基于规则和词典:早期NER大量依赖人工定义的模式和词典。如使用正则表达式匹配人名(中文可以基于常见姓氏+名字长度模式),地名有地名后缀(“省”、“市”等)模式,时间则匹配日期格式等等。这种方法对特定领域(如生物医学)仍然有效,因为有现成的词典和规范。但纯规则法的覆盖率有限且维护困难,遇到未登记的新词就无能为力。
- 统计机器学习:这类方法将NER视作序列标注任务,使用传统模型如隐马尔可夫模型(HMM)、条件随机场(CRF)等。在CRF中,我们定义状态集合(如各类标签),从训练语料学习状态转移概率和观测概率,以最大化标注序列的概率。CRF特别适合序列标注,因为能考虑上下文信息和标签间依赖,被广泛用于NER。配合CRF的需要人工设计特征:如当前词、本词的词形(是否大写)、上下文窗口的词、词是否在某实体词典中、词缀前缀等等。好的特征对模型效果很关键。早期著名的NER系统(如1990s的MITRE系统)都使用CRF或最大熵模型,结合了几十上百种特征。统计模型相比规则鲁棒性更好,可以推广到未见过的文本,缺点是需要人工特征工程且性能受限于特征表达能力。
- 深度学习:近年来NER几乎被神经网络方法主导。典型架构是双向LSTM + CRF:前面用BiLSTM读取句子,将每个词编码成隐状态向量,后面接一层CRF作为判别层。这种模型不需要人工特征,词的表示由词向量(可预训练)加上LSTM自动学习的上下文特征构成,末层CRF保证输出的标签序列合法(如不会出现I-PER前面不是B-PER之类的不合理标签序列)。BiLSTM-CRF在CoNLL2003英文NER比赛等任务上一度达到SOTA。后来引入预训练语言模型BERT后,效果更上一层楼:做法通常是用BERT对句子编码,然后在每个字/词的BERT向量上接一个分类层或CRF层,直接输出标签。由于BERT已经包含丰富的语言知识和一定的实体知识,微调后往往能取得极高的F1值。目前很多NER任务的最佳结果都是这种BERT+CRF架构。深度学习方法的优点是不需要手工特征、可端到端训练,性能高;缺点是需要大量标注数据,且模型本身缺乏可解释性。
NER模型评估指标一般使用精准率(Precision)、召回率(Recall)和F1-score。因为NER关注每个实体的边界和类别,一个实体识别对了才算真正正确。Precision表示模型识别出的实体中有多少是正确的,Recall表示文本中真实的实体有多少被模型找到了。F1则是二者的调和平均。例如测试集有100个人名,模型标出了90个,其中80个正确,那么Precision=80/90≈88.9%,Recall=80/100=80%,F1≈84%。在NER任务中,通常比较关注F1,因为既希望找全又不想有太多误报。在实际应用里,不同应用对Precision/Recall偏好不同:有的系统宁可漏掉一些也要确保输出准确(高Precision),有的则宁可多输出保证不漏(高Recall)。可以通过调整模型置信度阈值或对输出规则后处理来满足需求。
NER面临的一些挑战包括:实体边界识别,有时不容易判断实体从哪到哪,比如英文“New York Times”是一个整体的组织名而非“New York”地名加上普通词“Times”;实体歧义,如前述“苹果”是公司还是水果,需要通过上下文和大写等信息判断;以及领域迁移问题,不同行业有特定的实体类型和写法,如生物医学领域充满了缩写和拉丁术语,通用NER模型拿过去效果不好,需要加入专业词典或通过迁移学习在少量专业标注数据上微调。为解决这些问题,常用方法有:利用上下文提高判别,比如通过前后词来推测当前词的实体类型;利用预训练模型或多任务学习引入外部知识,使模型识别更专业的术语;以及规则和模型结合,例如对于确定格式的实体用规则辅助标注。实际系统有时会对NER输出再做后处理,例如实体链接(把识别出的实体链接到知识库中的唯一标识符)等等。
总而言之,命名实体识别为文本加上了结构化的信息标签,让计算机从杂乱的文字中提炼出“名词”信息。它就像给文章加了人物表、地名表,使后续处理可以像处理数据库那样方便地索引和利用这些元素。随着深度学习和预训练的加持,NER技术已经相当成熟,在新闻、社交媒体等常见领域准确率很高。不过在开放域网页文本、OCR识别结果等嘈杂环境,以及新词层出不穷的场景下,NER仍有提升空间。可以预见,结合更强大的语言模型和知识库,NER会越来越“聪明”,不仅能认出实体,还能即时获取其含义,实现从“识别”走向“理解”。
其他重要任务
除了上述任务,自然语言处理还有许多其他分支领域和任务类型:
- 机器翻译(Machine Translation):将一种语言的文本自动翻译成另一种语言。经典方法采用**序列到序列(Seq2Seq)**模型,早期用Encoder-Decoder架构的RNN配合注意力机制实现,如今几乎全部采用Transformer架构实现高质量翻译。机器翻译要求模型同时掌握源语言和目标语言的语法和语义,对长距离依赖和词语歧义的处理尤为重要。
- 自动摘要(Text Summarization):自动为一段长文本生成简明扼要的摘要。分为抽取式摘要(从原文中挑选重要句子组合)和生成式摘要(用自己的话表述要点)。这通常需要模型有一定的语义理解与生成能力,生成式摘要常用Seq2Seq模型,训练时以人类写的摘要作为监督信号。
- 问答系统(Question Answering):输入一个问题,系统从知识库或给定文章中找到答案。包含开放域问答(如从Wikipedia找答案)和机器阅读理解(给一段文章,问其中细节)。后者常用BERT类模型做阅读理解,在SQuAD等数据集上已超过人类表现。问答需要综合搜索、信息抽取和推理等能力。
- 对话系统(Dialogue Systems):分为任务型对话(完成特定任务,如订票)和开放域闲聊对话。任务型对话系统典型模块包括自然语言理解(NLU,识别用户意图和槽值)、对话状态管理(根据上下文决定下一步动作)、对话策略和自然语言生成(NLG,生成回复)。如今端到端训练的对话模型也在兴起,尤其是大型生成模型(如ChatGPT)在多轮对话上展示了惊人效果。对话系统需要考虑上下文记忆、对话管理、多轮共指等复杂问题。
- 自然语言推理(NLI)/文本蕴含:判断两个句子之间的推理关系,是“蕴含”、“矛盾”还是“无关”。这需要模型真正理解句意并进行逻辑推断。如“A比B矮”和“B比A高”是一致的蕴含关系,而“A吃了蛋糕”和“A烤了蛋糕”并不蕴含。预训练模型提升下,NLI任务上也有很高表现,但模型是否真正推理仍是研究热点。
- 信息抽取(Information Extraction):从文本中结构化地提取出知识三元组或事件。例如关系抽取(抽出实体之间的特定关系,如“出生地(爱因斯坦,德国)”)、事件抽取(识别事件的触发词和各要素,如某次地震的时间地点震级伤亡等)。信息抽取往往以NER和句法依存为基础,再加上分类或规则来完成。
- 风格/语气分析:除了情感之外,还有其他文本属性的分析,例如文本的意图(询问、命令、陈述等语气分类)、文本的作者属性检测(年龄、性别、心理倾向分析),或者垃圾语言检测(侮辱辱骂识别)。这些可以看作特殊的分类任务或序列标注任务,随着社交媒体内容增多而受到重视。
可以看到,NLP任务丰富多彩,但万变不离其宗——无论是翻译、摘要还是问答、对话,其核心都涉及对自然语言的理解和生成。在不同任务中,前面讨论的基本技术(语言模型、词向量、句法语义分析等)或显或隐地发挥作用。例如机器翻译需要强大的序列生成模型、问答需要信息抽取和推理、对话系统则综合了意图分类、槽值识别、语言生成等多个子任务。掌握了基础,往往触类旁通,可以融会贯通去应对新的挑战。
Transformers 与 PyTorch建模
近年来,NLP领域最引人瞩目的进展之一就是Transformer模型的引入及其引发的预训练浪潮。与此同时,PyTorch作为深度学习框架的代表,已成为NLP研究和开发的主力工具。本节我们介绍Transformer架构的要点,以及如何结合PyTorch/Transformers库来进行模型构建与应用。
Transformer架构简介
Transformer是由Vaswani等人在2017年提出的一种全新神经网络架构。它完全基于注意力机制(Attention)来建模序列,不再使用传统的RNN循环结构,从而极大地提高了并行计算效率。Transformer最初用于机器翻译的编码器-解码器架构,如今已扩展为各种变体,成为NLP模型的标准基石。
Transformer的基本结构包括编码器和解码器两部分。编码器将源序列编码成隐表示,解码器根据编码表示生成目标序列。二者都是由多个层(Layer)堆叠而成,每一层内部主要包含两个子层:一是多头自注意力(Multi-Head Self-Attention)机制,二是前向反馈网络(Feed-Forward Network),每个子层外还加了残差连接和层归一化以确保深层训练的稳定。在输入阶段,Transformer会先将词用词向量表示,再加上**位置编码(Positional Encoding)**以注入序列位置信息,因为纯注意力模型缺少对序列顺序的建模。位置编码有固定的三角函数设计,也可以像BERT那样使用可学习的位置向量。
自注意力是Transformer的核心:它让序列中的每个位置对自身序列进行注意力计算,获取全局依赖。相对于RNN需要按顺序递归地处理序列,注意力机制可以直接在一次计算中关联序列中任意两个位置的词,这使得长程依赖问题得到缓解,也让并行化成为可能(因为各位置的注意力可以同时计算)。在Transformer里,每个编码器层的自注意力通过Query-Key-Value机制实现:对于输入序列的表示$\mathbf{H}$,首先通过仿射变换映射出查询矩阵$Q=\mathbf{H}W^Q$、键矩阵$K=\mathbf{H}W^K$、值矩阵$V=\mathbf{H}W^V$。然后计算注意力权重矩阵 $A = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)$,并用它加权值向量$V$得到注意力输出$O = A V$。这里$A$的每一行对应序列中一个词对所有词(包括它自己)的注意力分布。缩放因子$\sqrt{d_k}$用于稳定梯度。通过自注意力,模型可以针对每个词,自适应地从整句中提取与之相关的信息。例如在翻译中,注意力可以帮模型找到当前要翻译的词应当对应源句中的哪个词。
多头注意力(Multi-Head Attention)是对单一注意力的并行拓展。Transformer的每个注意力层并非只有一套$W^Q, W^K, W^V$,而是引入$h$个注意力头,各自独立地执行注意力计算。这样做有两个好处:一是不同的头可以学习不同类型的相关性(例如一个头关注语法关系,另一个头关注词的同义替换等);二是每个头的维度缩小(将总维度划分给各头),单头计算量小且并行头数可以增加模型表示能力。多个头的输出会在最后concat起来,经线性变换恢复维度。在实际模型中,Transformer编码器的自注意力就是多头注意力(典型如8头或12头)。类似地,解码器中有自注意力和编码-解码注意力(后者的Q来自解码器当前层,K,V来自编码器输出),用以在翻译时让解码端获取源句信息。多头注意力的成功表明,**注意力即一切(Attention is All You Need)**并非戏言。Transformer通过堆叠多层注意力和前馈网络,实现了对序列建模的新高度。
Transformer相比前序模型有几个显著优势:并行度高(没有RNN的时序依赖,每个层的所有位置可同时计算),长程依赖捕获能力强(自注意力直接全局连接,无需像RNN那样经过长链传递信息),此外表示能力更强(多头机制赋予模型从不同子空间关注信息的能力)。Transformer也更方便利用GPU/TPU进行加速,因为矩阵乘法等操作很适合并行。
Transformer的这些优点很快体现在各种任务的性能提升上。以机器翻译为起点,Transformer后来被用于语言模型预训练(GPT、BERT)、文本摘要、对话、生物序列建模、甚至迁移到计算机视觉领域(Vision Transformer)。可以说,目前NLP领域性能最好的模型几乎都是基于Transformer架构构建的。
PyTorch与Transformers库
PyTorch是当今深度学习研究中使用最广泛的开源框架之一。由Meta (Facebook) AI研究院开发,于2017年发布,凭借动态计算图、友好的 Python 接口和强大的GPU加速能力,很快赢得了研究社区的青睐。与TensorFlow的静态图不同,PyTorch允许我们用普通Python代码定义模型结构,网络中的张量计算会在运行时动态构建计算图。这意味着调试和开发更加直观灵活(可以随时打印、中断、使用普通控制流),非常适合学术探索和原型实现。此外,PyTorch提供了丰富的神经网络库(torch.nn)、优化器(torch.optim)、数据加载工具(torch.utils.data)等,使开发者能方便地实现几乎所有流行模型。
在NLP领域,PyTorch几乎成了标配框架。无论是自己实现一个LSTM语言模型,还是Fine-tune一个BERT分类器,PyTorch都提供了简洁的接口。例如定义一个两层的前馈网络,只需继承nn.Module并在forward方法中组合层计算即可。下面是一个使用PyTorch定义并运行简单模型的示例:
import torch
import torch.nn as nn
# 定义一个简单的前馈神经网络模型
class SimpleFFN(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim):
super(SimpleFFN, self).__init__()
self.linear1 = nn.Linear(input_dim, hidden_dim)
self.relu = nn.ReLU()
self.linear2 = nn.Linear(hidden_dim, output_dim)
def forward(self, x):
# 前向传播: 全连接 -> ReLU -> 全连接
out = self.linear1(x)
out = self.relu(out)
out = self.linear2(out)
return out
# 创建模型实例,输入维度10,隐藏层维度20,输出维度2
model = SimpleFFN(input_dim=10, hidden_dim=20, output_dim=2)
# 生成一个随机输入张量(batch_size=5,每个样本10维)
x = torch.randn(5, 10)
# 前向计算输出
logits = model(x)
print(logits.shape) # 输出张量形状为 (5, 2)
上例中,我们定义了一个简单的两层全连接网络,并对一个batch的随机数据进行了前向计算,输出是每个样本2维的实数向量(例如可以看作二分类的logits)。在PyTorch中,几乎所有神经网络结构(CNN、RNN、Transformer等)都可以用类似方式搭建,然后通过定义损失函数和使用优化器在训练数据上迭代优化即可。手工实现训练循环涉及:将数据分成小批, for循环每个batch,清空梯度optimizer.zero_grad(),前向计算得到loss,loss.backward()反传计算梯度,再optimizer.step()更新参数。PyTorch不会帮你隐藏这些细节,但也因此给了开发者完全的控制权去定制训练过程。对于有一定深度学习基础的初学者,这种方式能帮助理解模型训练的细节。对于复杂模型和大数据训练,也可以考虑使用PyTorch-Lightning等更高级封装来减少模板化代码。
Transformers库(指Hugging Face的Transformers库)是在PyTorch之上构建的高级库,专门用于提供预训练模型的便捷使用和微调。它集成了数百种预训练的NLP模型,包括BERT、GPT系列、RoBERTa、T5、XLNet、Albert、ELECTRA等各大厂模型,以及多语言、多任务的变种。借助Transformers库,我们只需寥寥数行代码,就可以下载一个预训练模型并在我们的数据上进行推理或微调,而不必从头实现复杂的模型结构和加载预训练参数。
例如,我们想使用中文预训练模型BERT来做二分类任务(比如情感分类),可以使用Transformers库如下:
from transformers import BertTokenizer, BertForSequenceClassification
import torch
# 加载预训练的中文BERT模型和对应的分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertForSequenceClassification.from_pretrained('bert-base-chinese', num_labels=2)
# 将中文文本编码为模型输入格式
text = "这个产品的质量很不错!"
inputs = tokenizer(text, return_tensors='pt') # 自动添加特殊符号并转换为张量
# 前向传播获取模型输出
outputs = model(**inputs)
logits = outputs.logits
# 得到预测的类别(0或1)
predicted_class = torch.argmax(logits, dim=1).item()
print(predicted_class)
上述代码首先加载了bert-base-chinese预训练模型(12层中文BERT)以及对应的字词分词器,然后对示例文本进行分词和索引转换,得到PyTorch张量形式的输入。接着将输入张量喂给BertForSequenceClassification模型(它是在BERT模型顶部加了一层线性层用于分类的模型),得到输出的logits。取argmax即可得到预测的类别(0或1)。这样,我们无需关心模型内部结构细节,就完成了一次推理。若要进行训练,只需额外准备带标签的数据,编写训练循环或使用Transformers提供的Trainer接口即可对模型进行微调。
需要注意的是,像BertForSequenceClassification这样的模型在from_pretrained时如果指定了num_labels=2,它会初始化一个随机的分类层权重,因此直接用它推理并没有意义(模型尚未针对具体任务训练)。实际应用中,我们应该用已有微调好的模型(如Hugging Face模型库中提供了一些现成fine-tuned模型)或者先调用model.train()模式在标注数据上训练若干epoch,再用训练后的模型去预测。在本例中,因缺少微调,输出类别只是随机猜测。不过这个流程演示了Transformers库使用的便利性。对于很多下游任务,我们甚至可以利用Transformers的Pipeline接口更快上手:例如使用情感分析pipeline,只需指定任务和模型名称,便可直接对输入字符串给出情感标签和置信度:
from transformers import pipeline
classifier = pipeline("sentiment-analysis", model="uer/roberta-base-finetuned-jd-binary-chinese")
result = classifier("这个产品真让人失望")
print(result) # 输出类似: [{'label': 'NEGATIVE', 'score': 0.9876}]
上面代码中,我们用了一个在京东商品评论上微调过的RoBERTa模型,立即就能对中文情感进行判断。这种高级封装对于快速验证想法、做demo非常有用。
综上,Transformers库+PyTorch的组合让NLP开发变得前所未有的高效和强大。研究人员可以专注于任务本身,而无需重复造轮子实现模型;工程师可以方便地将最新的预训练模型应用到产品中。同时,PyTorch保留的底层灵活性也允许用户在需要时深入修改模型、定制训练过程。例如我们可以取出Transformers模型的内部层输出,将其与自己写的网络模块结合,形成新的架构。
综合实例:完整NLP模型流程
为了把以上内容串联起来,我们以文本分类任务为例,简述用PyTorch和Transformers完成从训练到部署的要点步骤:
- 数据准备:准备标注好的文本数据集,划分训练集、验证集、测试集。确保分布一致,类别平衡。如果数据不够,可考虑数据增强或迁移学习。对中文数据,可视需要进行分词;对于Transformers模型,可交由自带的Tokenizer处理。转换文本为模型输入所需格式(如ID序列)。
- 模型选择:确定使用预训练模型还是简单模型。如果数据量很大也可以训练一个从头开始的模型,否则一般使用预训练模型微调最有效。比如选择
BERT-base中文模型用于分类。在PyTorch中,可直接通过Transformers加载模型;也可以用更低级的方式,自己定义一个embedding+BiLSTM+Linear的网络。 - 训练设置:定义损失函数(分类常用CrossEntropyLoss),优化器(如AdamW),以及训练超参数(batch大小、学习率、训练轮数等)。如果使用Transformers库,可以使用它的
Trainer类简化训练过程,自动处理训练循环、评估和保存模型等。如果手写训练循环,要注意对梯度裁剪、学习率调度等进行设置以确保收敛稳定。 - 模型训练:迭代训练模型,每个epoch跑完在验证集上评估性能,保存当前最优模型。观察loss曲线和验证集指标,防止过拟合(可以使用早停)。利用GPU加速训练,必要时多GPU分布式训练。对于预训练模型微调,通常用较小的学习率,可能冻结部分层只训练顶层,根据验证效果调整策略。
- 模型评估:在测试集上评估最终模型,报告主要指标。若有错误分析需求,可将模型对测试样本的预测与真实标签对比,找出错误案例,分析原因(例如模型可能对长文本效果差、混淆某些类别等)。这有助于我们回顾模型是否充分理解了任务,以及下一步改进的方向。
- 推理部署:将训练好的模型用于实际数据预测。可以使用Transformers提供的pipeline直接部署一个API服务,或者导出为TorchScript/ONNX格式以在生产环境高效运行。部署时要注意输入输出的处理,与训练时保持一致。例如确保分词方法、文本预处理在上线时完全复现训练过程。同时考虑性能优化,如批量推理、GPU/CPU负载,以及多线程并发处理。如果模型较大需要压缩加速,可以考虑蒸馏、小模型替换等方法。
整个过程中,我们运用了前面章节讨论的知识:用了预训练语言模型BERT作为起点(连接了语言模型的概念),用了词向量(BERT内置字向量)来表示文本,模型结构上BERT其实已经implicitly包含了很多句法和语义知识(我们没显式做句法树,但是BERT的注意力机制学到了一些句法模式),最后完成的文本分类模型解决的是一个NLP下游任务。而这一切,在PyTorch和Transformers的帮助下,只需很少的代码就能实现。这也是现代NLP的一个缩影:以预训练模型为基础、结合少量特定任务数据、快速微调得到可用模型,极大地降低了构建NLP应用的门槛。
总结与展望
通过这份笔记,我们系统回顾了自然语言处理的基础知识、主要理论以及建模流程。从语言模型、词向量这样的底层表示方法,到句法分析、语义理解这样的中层技术,再到文本分类、情感分析、命名实体识别等具体应用任务,我们可以看到NLP技术体系的全貌。同时,我们也介绍了当今NLP领域的旗舰架构Transformer,以及PyTorch和Transformers库在模型实现中的作用。理论和实践相结合,使我们既理解了NLP模型背后的原理,又掌握了使用现代工具进行开发的要点。
自然语言处理是一个发展迅猛的领域。近年来预训练大模型的出现,使得NLP进入了一个新的范式:掌握通用语言知识的超大模型加上针对特定任务的微调,往往比以往任何专门设计的模型都要强大。随着计算力和数据的增长,我们有理由相信模型对语言的掌握会越来越深刻。不过,语言毕竟是复杂的、人类智慧的结晶,现有模型距离真正“理解”还有一些距离,例如对隐含语义、常识推理、多步推理、事实一致性等方面依然存在挑战。在未来的研究中,如何让模型融入更多常识和世界知识、更好地进行可解释的推理,将是重要方向。同时,NLP也在和计算机视觉、语音等融合,迈向多模态的智能系统,比如图文对照的模型、带视觉感知的对话机器人等。
对学习者来说,掌握NLP的基础理论并勤加实践,是应对飞速变化的技术的最佳方式。有了坚实的基础,无论模型架构怎么改朝换代,都能触类旁通,理解背后的核心思想。而通过实践编程掌握工具和框架,则能将想法快速变为现实,在这个“大模型+小数据”的时代乘风而上。希望这份学习笔记能帮助初中级的NLP爱好者理清思路,夯实基础,在日后的学习和工作中更进一步。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)