ai学习笔记(九)
这次分享一下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)中解决序列标注任务的经典且强大的模型架构。
这个模型巧妙地结合了两种技术的优势:
- BiLSTM (双向长短期记忆网络):擅长提取上下文特征,解决“长距离依赖”问题。
- 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] |
分别输入 SentA 和 SentB |
| 交互机制 | 句子间有 Self-Attention 交互 | 句子间无交互,独立编码 |
| 输出形式 | 分类标签/相似度分数 | 固定维度的句子向量 (Embedding) |
| 计算复杂度 | O(N2) (两两组合) | O(N) (先算向量,再快速比对) |
| 主要用途 | 高精度重排序 (Reranking)、问答匹配 | 语义搜索、聚类、大规模去重 |
| 速度 | 慢 (不适合大规模检索) | 极快 (适合实时检索) |
4.3 应用场景
语义搜索:用户搜“怎么减肥”,能匹配到“减重方法”的文章。
聚类分析:将海量评论自动归类为“价格投诉”、“质量投诉”等。
去重:识别内容重复的新闻或帖子。
RAG (检索增强生成):在大模型应用中,快速从知识库中检索相关片段。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)