再使用BiLSTM+CRF 做NER 序列标注笔记
https://blog.csdn.net/zhang2010hao/article/details/85284312
https://blog.csdn.net/zhang2010hao/article/details/85284312#21_Emission_scorehttpsblogcsdnnetzhang2010haoarticledetails85284687_57
https://blog.csdn.net/zhang2010hao/article/details/85317411?spm=1001.2101.3001.10796
配合代码和通义的解释:
非常好的问题!我们来**详细讲解这行代码:**
```python
loss = -self.crf(emissions, tags, mask=mask)
```
---
## 🧠 一、整体含义
这行代码的作用是:
> **计算当前 batch 的负对数似然损失(Negative Log Likelihood Loss),用于训练 BiLSTM-CRF 模型。**
- `self.crf` 是 PyTorch 的 `CRF` 层(来自 `torchcrf.CRF` 或自定义的 CRF 实现)
- `emissions` 是 BiLSTM 输出的每个 token 的标签得分(logits)
- `tags` 是每个 token 的真实标签
- `mask` 是掩码,表示哪些位置是 padding(需要忽略)
---
## 📦 二、输入参数详解
### 1. `emissions`: 标签得分(logits)
形状:`(batch_size, seq_len, num_tags)`
- 每个 token 有 `num_tags` 个得分,表示它属于每个标签的可能性。
- 例如:`emissions[i, j, k]` 表示第 `i` 个句子的第 `j` 个 token 属于第 `k` 个标签的得分。
### 2. `tags`: 真实标签
形状:`(batch_size, seq_len)`
- 每个元素是一个整数,表示该 token 的真实标签索引。
- 例如:`tags[i, j] = 2` 表示第 `i` 个句子的第 `j` 个 token 的真实标签是索引为 2 的标签(如 `B-PER`)。
### 3. `mask`: 掩码
形状:`(batch_size, seq_len)`
- 值为 `True` 表示该位置是有效 token(不是 padding)
- 值为 `False` 表示该位置是 padding(需要忽略)
---
## 🧮 三、CRF 的计算原理详解
CRF 的核心思想是:**建模标签之间的转移关系**,使得预测的标签序列更合理。
### 1. 转移矩阵(Transition Matrix)
CRF 内部维护一个 `(num_tags + 2, num_tags + 2)` 的转移矩阵 `transitions`,其中:
- `transitions[i, j]` 表示从标签 `i` 转移到标签 `j` 的分数。
- 为什么要加 2?因为引入了起始(START)和结束(STOP)标签。
### 2. 正向算法(Forward Algorithm)
CRF 使用 **Forward Algorithm** 计算所有可能标签序列的总概率,记作 `Z(x)`。
### 3. 正确标签序列的得分
计算给定标签序列 `y` 的得分,记作 `score(y, x)`。
### 4. 损失函数(负对数似然)
最终损失为:
$$
\text{loss} = -\log \frac{\exp(score(y, x))}{\sum_{y'} \exp(score(y', x))} = -\log P(y|x)
$$
也就是:
```python
loss = -self.crf(...) # 返回的是负对数似然
```
---
## 🧩 四、举个例子说明
### 假设:
- `batch_size = 1`
- `seq_len = 3`
- `num_tags = 4`(标签有:O, B-PER, I-PER, B-LOC)
### 输入数据:
```python
emissions = torch.tensor([
[
[2.0, 1.0, 0.1, 0.5], # token 0 的得分
[1.0, 3.0, 0.2, 0.1], # token 1 的得分
[0.5, 2.0, 4.0, 0.3], # token 2 的得分
]
]) # shape: (1, 3, 4)
tags = torch.tensor([[1, 2, 0]]) # shape: (1, 3) → 标签序列 [B-PER, I-PER, O]
mask = torch.tensor([[True, True, True]]) # 全部有效,无 padding
```
### 转移矩阵(示例):
```python
# transitions[i][j] 表示从标签 i 到标签 j 的转移得分
transitions = [
[ -1, 1, -2, -1], # O
[-2, -1, 2, -1], # B-PER
[-2, 2, -1, -1], # I-PER
[-1, -1, -1, -1] # B-LOC
]
```
(这些数值是训练过程中学习到的)
---
## 🧮 五、CRF 的计算过程(简化版)
### Step 1: 计算正确标签序列的得分
```python
score = transitions[START -> B-PER] +
emissions[0][B-PER] +
transitions[B-PER -> I-PER] +
emissions[1][I-PER] +
transitions[I-PER -> O] +
emissions[2][O] +
transitions[O -> STOP]
```
### Step 2: 计算所有可能标签序列的总得分(使用 Forward 算法)
这个过程非常复杂,但本质是动态规划,计算所有路径的总得分。
### Step 3: 计算损失
```python
loss = -log(score / total_score)
```
PyTorch 的 `CRF` 层会自动完成这些计算。
---
## 📌 六、为什么取负号?
```python
loss = -self.crf(...)
```
因为 `self.crf(...)` 返回的是对数概率(log probability),是一个负数。
取负后,变成正的损失值,这样我们就可以用梯度下降来优化模型参数。
---
## ✅ 七、总结
| 步骤 | 含义 | 作用 |
|------|------|------|
| `emissions` | 每个 token 的标签得分 | 表示模型对每个 token 的预测 |
| `tags` | 真实标签 | 用于计算正确路径的得分 |
| `mask` | 掩码 | 忽略 padding 位置 |
| `CRF` | 建模标签间依赖 | 保证预测结果符合标签规则 |
| `loss = -CRF(...)` | 负对数似然 | 用于训练模型,最小化损失 |
---
## 📚 延伸建议
如果你感兴趣,我可以继续帮你解释:
- ✅ CRF 的 Forward Algorithm 是如何实现的?
- ✅ 如何可视化 CRF 的转移矩阵?
- ✅ CRF 与 Softmax + CrossEntropy 的区别?
- ✅ 如何在训练时加入字符级特征?
---
是否需要我帮你写一个脚本,打印 CRF 的转移矩阵?或者画一个 CRF 的流程图?是否需要?

浙公网安备 33010602011771号