知识图谱(BILSTM_CRF项目完整实现)【第七章】

一、BILSTM、BILSTM_CRF介绍

BiLSTM 和 BiLSTM-CRF 都是 NLP(自然语言处理)里处理序列数据的经典模型,主要用来做序列标注(给句子里的每个词打标签)。

1.1BiLSTM 模型(双向长短期记忆网络)

作用: 提取上下文特征,理解词语在句子中的语义。

结构:由两个 LSTM组成

前向 LSTM:从左到右读句子,捕捉过去的信息

后向 LSTM:从右到左读句子,捕捉未来的信息

输出:把两个方向的结果拼接,得到完整上下文表示


核心优势

解决传统 RNN 的梯度消失问题,能记住长距离依赖

双向理解,能判断歧义(如 "苹果" 是水果还是公司)
缺点
只能独立预测每个词的标签,不考虑标签之间的逻辑约束

容易输出非法序列(如:人名中间突然接地名开头)

1.2BiLSTM-CRF 模型(BiLSTM + 条件随机场)

作用: 在 BiLSTM 基础上,保证标签序列的合法性,是序列标注(NER)的黄金标准。

结构

Embedding 层:词→向量

BiLSTM 层:提取上下文特征

全连接层:输出每个词对应各标签的发射分数

CRF 层:

学习标签转移矩阵(如:B-PER → I-PER 概率高,B-PER → B-LOC 概率低)

用Viterbi 算法找出全局最优、合法的标签序列

核心优势

BiLSTM:强大的上下文语义提取

CRF:强制标签符合语法规则,杜绝非法序列

比纯 BiLSTM准确率更高、结果更合理

1.3两者对比

BiLSTM:只看单个词,不管前后标签是否合理

BiLSTM-CRF:全局看整个句子,保证标签连贯合法

1.4典型应用场景

命名实体识别(NER):识别人名、地名、机构名

例:B-PERI-PEROB-LOCI-LOC

词性标注(POS):给每个词标词性(名词、动词等)

分词 / 语义角色标注

总结:BiLSTM 是基础特征提取器,BiLSTM-CRF 是工业界最常用、效果最好的序列标注模型。这个项目我们使用BILSTM作为基线模型,BILSTM_CRF作为项目使用的序列标注模型。

二、CRF介绍

第六章中我们详细介绍了BILSTM模型,这里我详细介绍一下CRF模型

线性层的输出:(D*num_laycrs,seq_len,tag_size)

CRF接收的数据类型分为训练、测试

  • 训练时候的输入:线性层的输出、mask、真实标签
  • 测试时候的输入:线性层的输出、mask

mask的作用:因为神经网络同一批次的数据要求长度相同,所以需要使用mask把补齐序列使用的无效字符转换为0,避免影响到预测结果。

训练CRF模型的时候最重要的是计算损失(先使用CRF得出预测标签,再和真实标签计算损失)

CRF的输出:(D*num_layces,seq_len)+预测的标签序列

三、BiLSTM模型预测序列

BiLSTM_CRF.py

复制代码
pip install TorchCRF

import torch
import torch.nn as nn
from TorchCRF import CRF

from P03_NER.LSTM_CRF.config import Config
from P03_NER.LSTM_CRF.utils.data_loader import word2id, get_data


class NERLSTM_CRF(nn.Module):
    def __init__(self, embedding_dim, hidden_dim, tag2id, word2id, dropout=0.2):
        '''
        模型初始化
        :param embedding_dim: 嵌入层维度 300
        :param hidden_dim: BiLSTM输出时的维度
        :param tag2id: tag2id字典
        :param word2id: word2id字典
        :param dropout: 随机失活比例
        '''
        super(NERLSTM_CRF, self).__init__()
        self.name = "BiLSTM_CRF"
        self.embedding_dim = embedding_dim
        self.hidden_dim = hidden_dim
        self.tag_size = len(tag2id)
        self.vocab_size = len(word2id)

        # 词嵌入层
        self.embedding = nn.Embedding(self.vocab_size, self.embedding_dim)
        # BiLSTM层
        self.lstm = nn.LSTM(input_size=self.embedding_dim, hidden_size=self.hidden_dim//2, bidirectional=True, batch_first=True)
        # 随机失活
        self.dropout = nn.Dropout(dropout)
        # 线性层
        self.hidden2tag = nn.Linear(self.hidden_dim, self.tag_size)
        # CRF层
        self.crf = CRF(self.tag_size)

    def get_emission_score(self, input_ids, attention_mask):
        # print(f'input_ids: {input_ids.shape}')
        # print(f'attention_mask: {attention_mask.shape}')
        # 送入词嵌入层
        word_embeds = self.embedding(input_ids)
        # 送入BiLSTM层
        lstm_out, _ = self.lstm(word_embeds)
        # 送入随机失活层
        dropout_out = self.dropout(lstm_out)
        # print(f'dropout_out: {dropout_out.shape}')
        # 对位相乘,只保留有效位置的输出结果,将pad的部分置成0
        attention_mask = attention_mask.unsqueeze(-1)
        multi_out = dropout_out * attention_mask  # 自动广播并相乘
        # print(f'multi_out: {multi_out.shape}')
        # 送入线性层
        final_out = self.hidden2tag(multi_out)
        return final_out

    # 使用forward方法实现预测
    def forward(self, input_ids, attention_mask):
        # 获取发射得分
        emission_score = self.get_emission_score(input_ids, attention_mask)
        # 使用viterbi解码获取标签路径
        predict_result = self.crf.viterbi_decode(emission_score, attention_mask)
        return predict_result

    # 计算损失的函数
    def log_likelihood(self, input_ids, labels, attention_mask):
        # 获取发射得分
        emission_score = self.get_emission_score(input_ids, attention_mask)
        # 计算损失【将发射分数、真实标签、attention_mask直接输入给模型,就可以得到对数似然损失,加上负号就可以得到真正的需求求解的损失函数】
        loss = - self.crf(emission_score, labels, attention_mask.bool())
        return loss


if __name__ == '__main__':
    conf = Config()
    model = NERLSTM_CRF(conf.embedding_dim, conf.hidden_dim, conf.tag2id, word2id, conf.dropout)
    print(model)

    train_dataloader, val_dataloader = get_data()
    for input_ids, labels, attention_mask in train_dataloader:
        result = model(input_ids, attention_mask)
        print(f'result: {result}')
        # 计算损失
        loss = model.log_likelihood(input_ids, labels, attention_mask)
        print(f'loss: {loss}')
        break

执行结果:每个字符预测的标签对应的id;

可以很明显的发现预测的序列长度是不一样的,这是因为mask把之前补齐的长度变成了无效长度,帮助BILSTM只预测有效的标签。

相关推荐
孟郎郎3 分钟前
AI 辅助开发编程敏感信息保护安全规范
人工智能·安全·ai·风险·隐患
财迅通Ai7 分钟前
南华期货半年净利预期高增,稀缺自主清算壁垒驱动跨境业务规模放量
大数据·人工智能·区块链·南华期货
今夕资源网8 分钟前
AI声音克隆软件 CosyVoice今夕一键整合包解压即用 阿里巴巴通义实验室开源 github斩获22K星标
人工智能·github·多国语言·声音克隆·零样本语音克隆·感情·ai语音克隆
CIO_Alliance22 分钟前
iPaaS概念原理(2)| iPaaS的架构模型是怎样的?
人工智能·低代码·架构·ipaas·系统集成
Tom·Ge34 分钟前
【Spring Boot 4】Spring Boot 4.0 AI Agent实战:Skills Agent + MCP协议集成
人工智能·spring boot·microsoft
风途科技~1 小时前
手持式雷达测速仪:不限道路厂区,可设置限速阈值实时监测车辆速度
大数据·人工智能
GEO_ai_zhijian1 小时前
饮料生产线质量检测系统哪家好
大数据·人工智能·python
Hello-FPGA1 小时前
GPU与图像采集卡编程实战 eGrabber + CUDA 图像采集:三种内存模式技术解析
人工智能·深度学习·计算机视觉
xd1855785551 小时前
标题炼金术-电商标题优化的HarmonyOS开发实践
人工智能·华为·harmonyos·鸿蒙
Luminbox紫创测控1 小时前
大面积太阳模拟器设计原理与高均匀度辐照实现方法
人工智能·测试工具·安全性测试·测试标准