这次分享一下NLP进阶任务:序列标注与文本匹配、BiLSTM+CRF模型、SBERT模型。

物理世界每18个月数据总和翻倍

1. 序列标注

原理:序列标注任务旨在为文本中的每个token分配特定标签,如命名实体识别中的PER/LOC/ORG。核心挑战包括长距离依赖、标签歧义和领域适应性问题。输入是一个序列 X=(x1,x2,...,xn),输出也是一个等长的标签序列 Y=(y1,y2,...,yn)。

核心特点:

  • 一对多映射:输入一个句子,输出对应长度的标签序列。
  • 上下文依赖:当前词的标签往往依赖于它前后的词(例如,“苹果”在“吃苹果”中是物体,在“苹果公司”中是组织)。
  • 结构化输出:标签之间通常存在约束关系(例如,在BIO标注体系中,I-ORG 前面必须是 B-ORG 或 I-ORG,不能直接跟在 O 后面)

常见应用场景

2. BiLSTM+CRF模型

BiLSTM+CRF 是自然语言处理(NLP)中解决序列标注任务的经典且强大的模型架构。

这个模型巧妙地结合了两种技术的优势:

  1. BiLSTM (双向长短期记忆网络):擅长提取上下文特征,解决“长距离依赖”问题。
  2. CRF (条件随机场):擅长处理标签之间的约束关系,解决“非法标签序列”问题。

协同工作机制:BiLSTM输出每个位置的标签概率分布,CRF层计算全局序列得分。通过维特比算法解码,找到得分最高的合法序列。这种联合训练方式使模型在MSRA中文NER任务中达到92.1%的准确率。

2.1 架构详解

模型通常分为三层:输入层 -> BiLSTM 层 -> CRF 层

a. 输入层 (Embedding Layer)

将输入的单词序列 X=(x1,x2,...,xn)转换为向量序列。

  • 可以使用预训练词向量(Word2Vec, GloVe)或直接随机初始化。
  • 如果是中文,通常使用字向量。

b. BiLSTM 层 (特征提取器)

BiLSTM叫做双向长短期记忆网络,这是模型的“眼睛”,负责看懂句子。

  • 前向 LSTM:从左到右读取句子,捕捉过去的信息。
  • 后向 LSTM:从右到左读取句子,捕捉未来的信息。
  • 拼接:将两个方向的隐藏状态拼接,得到每个位置的上下文特征向量 hi。

  • 输出:对于每个位置i,BiLSTM 输出一个向量,该向量包含了该词在整个句子中的语义信息。此时,模型会通过一个全连接层映射到标签空间的分数(Emission Scores,发射分数)。

c. CRF 层 (全局解码器)

CRF叫做条件随机场,这是模型的“大脑”,负责根据规则做出最终决定。CRF 不直接分类,而是计算整个序列的全局得分确保输出合法序列

CRF 的核心要素:

路径得分计算:

即:总得分 = 所有位置的发射分数之和 + 所有相邻标签的转移分数之和

2.2 对比BERT

BERT 做序列标注(如命名实体识别 NER、词性标注 POS)的核心逻辑可以概括为:“预训练语言理解 + 任务特定分类头”

它不再像 BiLSTM 那样需要复杂的循环结构,而是利用 Transformer 的自注意力机制 直接为句子中的每个字/词生成一个包含全局上下文信息的向量,然后对每个向量单独进行分类。

BERT+softmax实现NER任务:

import torch
import torch.nn as nn
from transformers import BertModel, BertTokenizer

class BertNER(nn.Module):
    def __init__(self, num_labels, dropout=0.1):
        super(BertNER, self).__init__()
        # 1. 加载预训练 BERT 模型
        self.bert = BertModel.from_pretrained('bert-base-uncased')
        
        # 2. Dropout 防止过拟合
        self.dropout = nn.Dropout(dropout)
        
        # 3. 分类器:将 BERT 的输出维度 (768) 映射到标签数量
        self.classifier = nn.Linear(768, num_labels)
        
    def forward(self, input_ids, attention_mask, labels=None):
        # A. BERT 前向传播
        # outputs[0] 是 last_hidden_state: (batch_size, seq_len, 768)
        outputs = self.bert(input_ids=input_ids, attention_mask=attention_mask)
        sequence_output = outputs[0]
        
        # B. 应用 Dropout
        sequence_output = self.dropout(sequence_output)
        
        # C. 映射到标签空间
        logits = self.classifier(sequence_output) # (batch_size, seq_len, num_labels)
        
        loss = None
        if labels is not None:
            # D. 计算损失 (忽略 padding 和特殊子词的标签,通常设为 -100)
            loss_fct = nn.CrossEntropyLoss()
            # 调整维度以符合 CrossEntropyLoss 的要求
            loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))
            
        return (loss, logits) if loss is not None else logits

核心差异:

维度 BiLSTM + CRF BERT (通常指 BERT + Softmax 或 BERT + CRF)
特征提取能力 中等。基于循环神经网络,按顺序处理文本,能捕捉上下文,但对长距离依赖的捕捉能力有限,且无法并行计算。 极强。基于 Transformer 的自注意力机制(Self-Attention),能直接捕捉任意两个词之间的全局依赖关系,上下文理解能力远超 LSTM。
输入表示 通常是静态词向量(Word2Vec/GloVe)或简单微调的 Embedding。同一个词在不同语境下向量基本相同(除非动态微调)。 动态上下文表示。同一个词在不同句子中生成的向量完全不同(例如“苹果”在水果句和公司句中向量差异巨大)。
标签约束机制 强制包含 CRF。CRF 层显式学习标签转移矩阵,严格禁止非法路径(如 B-PER 后接 I-LOC)。 可选。
1. BERT + Softmax:无显式约束,靠模型自己学概率分布,偶尔会输出非法序列。
2. BERT + CRF:在 BERT 后接 CRF 层,兼具强大特征和强约束。
训练方式 通常从头训练或在少量数据上微调预训练词向量。 基于大规模语料预训练(Pre-training),然后在特定任务上进行微调(Fine-tuning)。

性能与效果对比:

1. 准确率 (Accuracy / F1-score)

  • BERT 胜出:在绝大多数数据集上,BERT 的 F1 值显著高于 BiLSTM+CRF。
    • 原因:BERT 的预训练知识让它对语义的理解更深刻,特别是在处理歧义实体(如“长春”是地名还是人名?“小米”是手机还是粮食?)时,BERT 的表现远超 BiLSTM。
    • 数据表现:在 CoNLL-2003 (英文 NER) 等标准测试集上,BERT 通常能将 F1 提升 3%~5% 甚至更多。

2. 泛化能力 (Generalization)

  • BERT 胜出:由于经过海量数据预训练,BERT 在小样本场景(Few-shot)或领域迁移场景下表现更好。它“见过”更多的词汇用法。
  • BiLSTM+CRF:如果训练数据较少,容易过拟合,且对未登录词(OOV)的处理能力较弱(依赖词向量覆盖)。

3. 标签合法性 (Label Consistency)

  • 纯 BERT (Softmax):可能会输出非法序列(虽然概率很低)。
  • BiLSTM+CRF:由于 CRF 的存在,绝对保证输出序列符合 BIO/BMES 规范。
  • BERT + CRF:结合了 BERT 的高准确率和 CRF 的强约束,是目前效果最好的方案。

效率与资源对比:

维度 BiLSTM + CRF BERT
推理速度 快。模型参数量小(几 MB 到几十 MB),计算量小,适合实时高并发场景。 慢。模型庞大(Base 版约 110M 参数,Large 版更大),计算复杂度高( O(N^2)),延迟较高。
显存占用 低。可以在低端 GPU 甚至 CPU 上流畅运行。 高。通常需要高性能 GPU,且 Batch Size 受限。
训练时间 短。收敛快,对算力要求低。 长。微调过程较慢,且预训练过程极其耗时(通常直接使用开源预训练模型)。
部署难度 低。易于量化、剪枝,可部署在移动端/边缘设备。 高。通常需要模型蒸馏(Distillation)、量化或剪枝才能落地到资源受限环境。

3. 文本匹配

1. 定义
文本匹配任务旨在衡量两个文本片段(句子、段落或文档)之间的相关性、相似度或逻辑关系。输入是两个文本 A和 B,输出通常是一个标量(相似度分数)或一个分类标签(如“相关/不相关”、“蕴含/矛盾/中立”)。

2. 核心特点

  • 多对一映射:输入两个序列,输出一个判断结果。
  • 交互性:核心难点在于如何有效地建模 A和 B之间的交互信息(Interaction)。
  • 语义鸿沟:需要解决字面不同但语义相同(同义词、 paraphrase)的问题。

3. 常见应用场景

  • 语义相似度计算:判断两句话意思是否一样(用于去重、问答检索)。
  • 自然语言推理 (NLI):判断前提句 A 是否能推导出假设句 B (蕴含、矛盾、中立)。
  • 问答系统 (QA):匹配用户问题与知识库中的候选答案。
  • 搜索引擎排序:计算查询 Query 与文档 Document 的相关性。
  • 抄袭检测:判断两段文本的重复率。
维度 序列标注 (Sequence Labeling) 文本匹配 (Text Matching)
输入 单个序列 ( $ X $ ) 两个序列 ( $ X_1, X_2 $ )
输出 标签序列 ( $ Y $ , 长度同 $ X $ ) 标量分数 或 分类标签 (单个值)
核心关注点 局部特征 + 全局标签约束 两个文本间的语义交互与对齐
典型损失函数 CrossEntropy (Token级), CRF Loss CrossEntropy (Sentence级), Contrastive Loss
主要难点 标签依赖关系、长距离依赖 语义歧义、词汇不匹配、长文本交互
评估指标 F1-score, Accuracy (Token级) Accuracy, AUC, MAP, NDCG

4. Sentence-BERT(SBERT)

词袋模型无法捕捉词语顺序和语义关系,仅统计词频导致信息丢失严重。例如“不错“和“错不“会被视为相同表达。

传统方法无法区分同义词和多义词,且难以捕捉超过5个词语的远距离语义关系,对复杂句子的理解能力有限。

BERT困境:直接使用BERT的【CLS】输出或词向量平均效果不佳,因为【CLS】输出缺乏语义信息,而词向量平均会丢失上下文信息。此外,BERT的计算效率低下,不适合大规模语义相似度计算任务。

效率问题:BERT需要将句子对同时输入模型进行计算,导致时间复杂度为O(n²),在大规模数据集上计算成本极高,无法满足实时性要求。

语义信息缺失:【CLS】输出主要训练用于分类任务,缺乏对句子整体语义的编码能力;简单的词向量平均会破坏BERT的双向上下文表示,导致语义信息丢失。

SBERT出现的核心就是解决原始 BERT 在计算“句子相似度”或“语义搜索”时速度太慢的问题。

4.1 原理

SBERT 结合了上述两者的优点:它通过微调,让 BERT 能够直接输出高质量的“句子向量”(Sentence Embeddings)。

  • 一旦训练好,你可以预先计算所有句子的向量并存储起来。
  • 查询时,只需计算新句子的向量,然后与库中的向量做简单的余弦相似度计算。
  • 速度提升:相比 Cross-Encoder,速度提升了约 5000 倍,而精度损失极小。

4.2 核心架构

SBERT 在原始 BERT 的基础上,增加了一个池化层 (Pooling Layer),并采用了孪生网络 (Siamese Network) 或 三元组网络 (Triplet Network) 的结构进行微调。

1. 池化策略 (Pooling Strategy)

原始 BERT 输出的是序列向量 (N,768),SBERT 需要将其压缩成单个向量 (768) 。常用的池化方法有:

  • [CLS] 池化:直接取 [CLS] 位置的向量(SBERT 论文发现这通常不是最优的)。
  • Mean Pooling (均值池化):对所有 Token 的输出向量求平均(最常用,效果最好)。通常会结合 attention_mask 忽略 padding 部分。
  • Max Pooling (最大池化):取每个维度上的最大值。

2. 训练目标 (Fine-tuning Objective)

SBERT 使用带标签的句子对数据进行微调,主要有两种网络结构:

  • 分类目标 (Classification Objective)

    • 输入:句子对 (A,B)。
    • 操作:分别通过共享权重的 BERT 得到向量 u 和 v 。
    • 拼接:构造向量 (u,v,∣u−v∣,u∗v)。
    • 输出:通过一个softmax层预测标签(如“蕴含”、“矛盾”、“中性”)。
    • 适用数据集:SNLI, MultiNLI。
  • 回归目标 (Regression Objective)

    • 输入:句子对 (A,B) 及其相似度分数(0-5分)。
    • 操作:得到向量 u 和 v 。
    • 计算:直接计算余弦相似度 cos⁡(u,v)。
    • 损失:最小化预测相似度与真实分数的均方误差 (MSE)。
    • 适用数据集:STS Benchmark。
  • 三元组目标 (Triplet Objective)

    • 输入:锚点句子 A,正例 P(意思相近),负例 N(意思不同)。
    • 目标:拉近 A和 P的距离,推远 A 和 N的距离。
    • 损失:Triplet Loss。
特性 原始 BERT (Cross-Encoder) SBERT (Bi-Encoder)
输入方式 [CLS] SentA [SEP] SentB [SEP] 分别输入 SentASentB
交互机制 句子间有 Self-Attention 交互 句子间无交互,独立编码
输出形式 分类标签/相似度分数 固定维度的句子向量 (Embedding)
计算复杂度 O(N2) (两两组合) O(N) (先算向量,再快速比对)
主要用途 高精度重排序 (Reranking)、问答匹配 语义搜索、聚类、大规模去重
速度 慢 (不适合大规模检索) 极快 (适合实时检索)

4.3 应用场景

语义搜索:用户搜“怎么减肥”,能匹配到“减重方法”的文章。

聚类分析:将海量评论自动归类为“价格投诉”、“质量投诉”等。

去重:识别内容重复的新闻或帖子。

RAG (检索增强生成):在大模型应用中,快速从知识库中检索相关片段。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐