基于BiLSTM-CRF对清华语料文本进行分类

 安装TorchCRF

!pip install TorchCRF==1.0.6

构建BiLSTM-CRF

# encoding:utf-8

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

from torch.utils.data import Dataset
from sklearn.model_selection import train_test_split
import numpy as np

import torch
import torch.nn as nn
import torch.optim as op
from torch.utils.data import DataLoader

import torch
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
from sklearn.metrics import classification_report

# 命名体识别数据


class NERDataset(Dataset):
    def __init__(self, X, Y, *args, **kwargs):
        self.data = [{'x': X[i], 'y': Y[i]} for i in range(X.shape[0])]

    def __getitem__(self, index):
        return self.data[index]

    def __len__(self):
        return len(self.data)

# LSTM_CRF模型
class NERLSTM_CRF(nn.Module):
    def __init__(self, config):
        super(NERLSTM_CRF, self).__init__()

        self.embedding_dim = config.embedding_dim
        self.hidden_dim = config.hidden_dim
        self.vocab_size = config.vocab_size
        self.num_tags = config.num_tags

        self.embeds = nn.Embedding(self.vocab_size, self.embedding_dim)
        self.dropout = nn.Dropout(config.dropout)

        self.lstm = nn.LSTM(
            self.embedding_dim,
            self.hidden_dim // 2,
            num_layers=1,
            bidirectional=True,
            batch_first=True,  # 该属性设置后,需要特别注意数据的形状
        )

        self.linear = nn.Linear(self.hidden_dim, self.num_tags)

        # CRF 层
        self.crf = CRF(self.num_tags)

    def forward(self, x, mask):
        embeddings = self.embeds(x)
        feats, hidden = self.lstm(embeddings)
        emissions = self.linear(self.dropout(feats))
        outputs = self.crf.viterbi_decode(emissions, mask)
        return outputs

    def log_likelihood(self, x, labels, mask):
        embeddings = self.embeds(x)
        feats, hidden = self.lstm(embeddings)
        emissions = self.linear(self.dropout(feats))
        loss = -self.crf.forward(emissions, labels, mask)
        return torch.sum(loss)

# ner chinese

加载数据

tag2id = {"PAD":0, "B-LOC":1,"I-LOC":2, "B-PER":3, "I-PER":4, "B-ORG":5, "I-ORG":6, "O":7}
id2tag = {v:k for k, v in tag2id.items()}
tlp ="/kaggle/input/ner-bilstm-crf/data_txt/{}"


def load_text(name, word2id=None):
    lines = []
    if not word2id:
        word2id, flag = {"pad":0, "unk":1}, True
    else:
        flag = False

    with open(tlp.format(name), encoding="utf-8") as fp:
        buff = []
        for line in fp:
            if not line.strip():
                lines.append(buff)
                buff = []
            else:
                buff.append(line.strip())
    x_train, y_train, maxlen= [],[], 60
    for line in lines:
        x, y = [], []
        for v in line:
            w,t = v.split("\t")
            if w not in word2id and flag:
                word2id[w]=len(word2id)
            x.append(word2id.get(w, 1))
            y.append(tag2id[t])
        if len(x)<maxlen:
            x = x+[0]*(maxlen-len(x))
            y = y+[0]*(maxlen-len(y))
        x = x[:maxlen]
        y = y[:maxlen]
        x_train.append(x)
        y_train.append(y)
    return np.array(x_train), np.array(y_train), word2id

    # return np.array(x_train[:10000]), np.array(y_train[:10000]), word2id


def load_data():

    x_train, y_train, word2id = load_text("train.txt")
    x_train, x_valid, y_train, y_valid = train_test_split(x_train, y_train, test_size=0.1)
    print("train:", x_train.shape, y_train.shape)
    print("valid:", x_valid.shape, y_valid.shape)
    x_test, y_test, _ = load_text("test.txt", word2id)

    print("test len:", len(x_test))
    print(id2tag)
    return word2id, tag2id, x_train, x_test, x_valid, y_train, y_test, y_valid, id2tag


def main():
    word = load_data()
    print(len(word))


if __name__ == '__main__':
    main()

实体类型解码

#from read_file_pkl import load_data

word2id, tag2id, x_train, x_test, x_valid, y_train, y_test, y_valid, id2tag = load_data()


# 用于将实体类别解码,单字组合成单词
def parse_tags(text, path):
    tags = [id2tag[idx] for idx in path]

    begin = 0
    end = 0

    res = []
    for idx, tag in enumerate(tags):
        # 将连续的 同类型 的字连接起来
        if tag.startswith("B"):
            begin = idx
        elif tag.startswith("E"):
            end = idx
            word = text[begin:end + 1]
            label = tag[2:]
            res.append((word, label))
        elif tag=='O':
            res.append((text[idx], tag))
    return res


class Config:
    embedding_dim = 100
    hidden_dim = 200
    vocab_size = len(word2id)
    num_tags = len(tag2id)

    dropout = 0.2
    lr = 0.001
    weight_decay = 1e-5


def utils_to_train():
    device = torch.device('cpu')
    max_epoch = 1
    batch_size = 32
    num_workers = 4

    train_dataset = NERDataset(x_train, y_train)
    valid_dataset = NERDataset(x_valid, y_valid)
    test_dataset = NERDataset(x_test, y_test)

    train_data_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers)
    valid_data_loader = DataLoader(valid_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers)
    test_data_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers)

    config = Config()
    model = NERLSTM_CRF(config).to(device)
    criterion = nn.CrossEntropyLoss(ignore_index=0)
    optimizer = op.Adam(model.parameters(), lr=config.lr, weight_decay=config.weight_decay)

    return max_epoch, device, train_data_loader, valid_data_loader, test_data_loader, optimizer, model

BiSTM-CRF训练

word2id = load_data()[0]
max_epoch, device, train_data_loader, valid_data_loader, test_data_loader, optimizer, model = utils_to_train()

# 中文命名体识别
class ChineseNER(object):
    def train(self):
        for epoch in range(max_epoch):

            # 训练模式
            model.train()

            for index, batch in enumerate(train_data_loader):
                # 梯度归零
                optimizer.zero_grad()

                # 训练数据-->gpu
                x = batch['x'].to(device)
                mask = (x > 0).to(device)
                y = batch['y'].to(device)

                # 前向计算计算损失
                loss = model.log_likelihood(x, y, mask)

                # 反向传播
                loss.backward()

                # 梯度裁剪
                torch.nn.utils.clip_grad_norm_(parameters=model.parameters(),
                                               max_norm=10)

                # 更新参数
                optimizer.step()
                if index % 200 == 0:
                    print('epoch:%5d,------------loss:%f' %
                          (epoch, loss.item()))

            # 验证损失和精度
            aver_loss = 0
            preds, labels = [], []
            for index, batch in enumerate(valid_data_loader):

                # 验证模式
                model.eval()

                # 验证数据-->gpu
                val_x, val_y = batch['x'].to(device), batch['y'].to(device)
                val_mask = (val_x > 0).to(device)
                predict = model(val_x, val_mask)

                # 前向计算损失
                loss = model.log_likelihood(val_x, val_y, val_mask)
                aver_loss += loss.item()

                # 统计非0的,也就是真实标签的长度
                leng = []
                res = val_y.cpu()
                for i in val_y.cpu():
                    tmp = []
                    for j in i:
                        if j.item() > 0:
                            tmp.append(j.item())
                    leng.append(tmp)

                for index, i in enumerate(predict):
                    preds += i[:len(leng[index])]

                for index, i in enumerate(val_y.tolist()):
                    labels += i[:len(leng[index])]

            # 损失值与评测指标
            aver_loss /= (len(valid_data_loader) * 64)
            precision = precision_score(labels, preds, average='macro')
            recall = recall_score(labels, preds, average='macro')
            f1 = f1_score(labels, preds, average='macro')
            report = classification_report(labels, preds)
            print(report)
            torch.save(model.state_dict(), 'params1.data_target_pkl')

    # 预测,输入为单句,输出为对应的单词和标签
    def predict(self, input_str=""):
        model.load_state_dict(torch.load("../models/ner/params1.data_target_pkl"))
        model.eval()
        if not input_str:
            input_str = input("请输入文本: ")

        input_vec = []
        for char in input_str:
            if char not in word2id:
                input_vec.append(word2id['[unknown]'])
            else:
                input_vec.append(word2id[char])

        # convert to tensor
        sentences = torch.tensor(input_vec).view(1, -1).to(device)
        mask = sentences > 0
        paths = model(sentences, mask)

        res = parse_tags(input_str, paths[0])
        return res

    # 在测试集上评判性能
    def test(self, test_dataloader):
        model.load_state_dict(torch.load("../models/ner/params1.data_target_pkl"))

        aver_loss = 0
        preds, labels = [], []
        for index, batch in enumerate(test_dataloader):

            # 验证模式
            model.eval()

            # 验证数据-->gpu
            val_x, val_y = batch['x'].to(device), batch['y'].to(device)
            val_mask = (val_x > 0).to(device)
            predict = model(val_x, val_mask)

            # 前向计算损失
            loss = model.log_likelihood(val_x, val_y, val_mask)
            aver_loss += loss.item()

            # 统计非0的,也就是真实标签的长度
            leng = []
            for i in val_y.cpu():
                tmp = []
                for j in i:
                    if j.item() > 0:
                        tmp.append(j.item())
                leng.append(tmp)

            for index, i in enumerate(predict):
                preds += i[:len(leng[index])]

            for index, i in enumerate(val_y.tolist()):
                labels += i[:len(leng[index])]

        # 损失值与评测指标
        aver_loss /= len(test_dataloader)
        precision = precision_score(labels, preds, average='macro')
        recall = recall_score(labels, preds, average='macro')
        f1 = f1_score(labels, preds, average='macro')
        report = classification_report(labels, preds)
        print(report)


if __name__ == '__main__':
    cn = ChineseNER()
    cn.train()

环境

TPU VM v3-8

Precision

recall

F1-score

support

1

0.86

0.77

0.82

3209

2

0.82

0.76

0.79

4417

3

0.90

0.77

0.83

1591

4

0.90

0.80

0.85

3052

5

0.83

0.68

0.75

1896

6

0.84

0.79

0.82

7566

7

0.98

0.99

0.99

167824

Accuracy

0.97

189555

Macro avg

0.88

0.80

0.83

189555

Weighted avg

0.97

0.97

0.97

189555

表1 BiLSTM-CRF训练效果 

总体上,模型的Accuracy(整体正确率)为0.97,说明模型的预测效果较好。Macro avg和Weighted avg是分别对各个类别进行平均后的指标,Macro avg对每个类别的指标进行平均,Weighted avg对每个类别的指标进行加权平均。考虑了样本数的影响,类别7的Precision、recall和F1-score都较高,说明该类别的样本预测效果最好;而类别5的指标相对较低,说明该类别的样本预测效果最差。

操作异常问题与解决方案

1、ModuleNotFoundError: No module named “TorchCRF”

解决办法:pip install TorchCRF

2、 IndexError: tensors used as indices must be long, byte or bool tensors

解决办法:先卸载TorchCRF然后安装适配版本的TorchCRF

pip uninstall TorchCRF

pip install TorchCRF==1.0.6

3、IndexError:CUDA kernel errors might be asynchronously reported at some other API call,so the stacktrace below m

解决办法:添加以下代码,指定GPU运行

import os

os.environ['CUDA_LAUNCH_BLOCKING'] = '1'

6、BrokenPipeError: [Errno 32] Broken pipe

解决办法显存爆满,应该换用更大显存的GPU,因此选择使用kaggle运行

7、RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cpu

解决办法:换用TPU进行运行

总结

本实验使用BiLSTM-CRF对文本进行分类,最终分类结果达到97%,分类效果较好。LSTM是一种能够对序列数据进行建模的循环神经网络,能够捕捉输入文本的上下文信息。而双向LSTM则是同时考虑正向和反向的上下文信息,进一步提高了模型的上下文信息捕捉能力。RF(Conditional Random Field)是一种无向图模型,能够对序列标注结果进行后验推断,从而更好地处理序列标注问题。

在文本分类中,可以将每个单词的特征作为节点,利用CRF进行动态规划解码,找出最优的分类结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/358167.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python-自动化篇-运维-语音识别

文章目录 理论文本转换为语音使用 pyttsx使用 SAPI使用 SpeechLib 语音转换为文本 代码和效果01使用pyttsx实现文本_语音02使用SAPI实现文本_语音03使用SpeechLib实现文本_语音04使用PocketSphinx实现语音转换文本 理论 语音识别技术&#xff0c;也被称为自动语音识别&#xf…

Threejs 展示——点击模型指定部分添加高亮显示

文章目录 需求分析需求 如下图所示,点击模型指定部分添加高亮显示 分析 绘制一个 canvas将该 canvas 将渲染器挂载到dom<template><canvas id="three" /> </template><scr

基于C#制作一个俄罗斯方块小游戏

目录 引言游戏背景介绍游戏规则游戏设计与实现开发环境与工具游戏界面设计游戏逻辑实现游戏优化和测试性能优化测试工具和流程说明引言 俄罗斯方块是一款经典的益智游戏,深受玩家喜爱。本文将介绍如何使用C#编程语言制作一个简单的俄罗斯方块小游戏,并探讨其设计与实现过程。…

从公有云对象存储迁移到回私有化 MinIO需要了解的所有信息

我们上一篇文章《如何从 AWS S3 遣返到 MinIO》的反响非常出色 - 我们已经接到了数十个企业的电话&#xff0c;要求我们提供遣返建议。我们已将这些回复汇总到这篇新文章中&#xff0c;其中我们更深入地研究了与遣返相关的成本和节省&#xff0c;以便您更轻松地进行自己的分析。…

递归、分治

递归 Recursion 函数自身调用自身通过函数体来进行的循环以自相似的方法重复进行的过程递归的三个关键 定义需要递归的问题&#xff08;重叠子问题&#xff09;- 数学归纳法思维确定递归边界保护与还原现场 递归形式时间复杂度规模问题举例指数型 2 n 2^n 2n子集排列型 n ! …

基于BERT模型实现文本相似度计算

配置所需的包 !pip install transformers2.10.0 -i https://pypi.tuna.tsinghua.edu.cn/simple !pip install HanziConv -i https://pypi.tuna.tsinghua.edu.cn/simple 数据预处理 # -*- coding: utf-8 -*-from torch.utils.data import Dataset from hanziconv import Han…

统计学-R语言-7.3

文章目录 前言总体方差的检验一个总体方差的检验两个总体方差比的检验 非参数检验总体分布的检验正态性检验的图示法Shapiro-Wilk和K-S正态性检验总体位置参数的检验 练习 前言 本篇文章继续对总体方差的检验进行介绍。 总体方差的检验 一个总体方差的检验 在生产和生活的许多…

Hypermesh中模型抽取中面的方法

一、自动抽取中面 二、手动抽取中面 offsetplanessweeps会记录所抽取的中面由哪两个面形成的 planes&#xff1a;所识别的对面是两个平面&#xff0c;就会在两平面中间区域插入一个中面 sweeps&#xff1a;所识别的对面是两个曲面时&#xff0c;就会在两个曲面中间区域插入一个…

esp32 操作DS1307时钟芯片

电气参数摘要 有VCC供电&#xff0c;IIC活动状态是1.5mA&#xff0c;待机状态200μA&#xff0c;电池电流5nA(MAX50nA&#xff09;无VCC供电的时候&#xff0c;电池电流&#xff0c;300nA&#xff08;时钟运行&#xff09;&#xff0c;10nA&#xff08;时钟停止&#xff09;供…

机器人顶会IROS,ICRA论文模板下载及投稿须知,以及机器人各大会议查询

一、背景 机器人方向&#xff0c;不止期刊TRO&#xff0c;TASE&#xff0c;RAM&#xff0c;RAL上的成果被认可&#xff0c;机器人顶会上的成果也是非常好的。当决定要写一篇IROS论文时&#xff0c;结果IROS论文模板和投稿须知找了半天才找到&#xff0c;且意外发现了一个特别好…

Java 面试题之 IO(一)

字节流 文章目录 字节流InputStream&#xff08;字节输入流&#xff09;OutputStream&#xff08;字节输出流&#xff09; 文章来自Java Guide 用于学习如有侵权&#xff0c;立即删除 InputStream&#xff08;字节输入流&#xff09; InputStream用于从源头&#xff08;通常是…

最全音频总线汇总

本文介绍了常见的音频总线&#xff0c;主要有I2S, AC’97&#xff08;Audio Codec 97&#xff09;, SPI&#xff08;Serial Peripheral Interface&#xff09;,USB Audio, Bluetooth Audio,HDMI, A2B&#xff08;Analog to Digital to Analog&#xff09;, Ethernet Audio I2S…

ros2配合yolov8具体实现

效果图 用yolov8实时检测物体,包括物体的类别,置信度和坐标通过ros2发布出去自定义消息 int64 xmin int64 ymin int64 xmax int64 ymax float32 conf string name发布端代码 from ultralytics import YOLO import cv2 import rclpy from yolo_interfaces.msg import Msgyo…

永宏 plc怎样实现远程调试、远程上下载程序?

准备工作 一台可联网操作的电脑一台单网口的远程透传网关及博达远程透传配置工具网线一条&#xff0c;用于实现网络连接和连接PLC一台永宏 PLC及其编程软件一张4G卡或WIFI天线实现通讯(使用4G联网则插入4G SIM卡&#xff0c;WIFI联网则将WIFI天线插入USB口&#xff09; 第一步…

不会使用这个工具的网工,都不能称为高手

你们好&#xff0c;我的网工朋友。 点开之前&#xff0c;你脑子里闪出来的工具是什么&#xff1f;ping&#xff1f;又或是arp、tracert、route……&#xff1f; 今天要给你分享的是非常经典的Linux网络抓包工具Tcpdump。 它允许用户拦截和显示发送或收到过网络连接到该计算机…

Pytest中doctests的测试方法应用!

在 Python 的测试生态中&#xff0c;Pytest 提供了多种灵活且强大的测试工具。其中&#xff0c;doctests 是一种独特而直观的测试方法&#xff0c;通过直接从文档注释中提取和执行测试用例&#xff0c;确保代码示例的正确性。本文将深入介绍 Pytest 中 doctests 的测试方法&…

幻兽帕鲁服务器视频教程,3分钟快速搭建,新手0基础也可以!

幻兽帕鲁服务器搭建教程&#xff0c;基于阿里云计算巢新手3分钟0基础搞定&#xff0c;详细参考 aliyunbaike.com/go/palworld 视频教程 1分钟部署幻兽帕鲁搭建&#xff0c;新手成功创建&#xff01; 大家也可以参考阿里云百科下方的教程&#xff1a; 如何自建幻兽帕鲁服务器&a…

二极管漏电流对单片机ad采样偏差的影响

1&#xff0c;下图是常规的单片机采集电压电路&#xff0c;被测量电压经过电阻分压&#xff0c;给到mcu采集&#xff0c;反向二极管起到钳位作用&#xff0c;避免高压打坏mcu。 2&#xff0c;该电路存在的问题 二极管存在漏电流&#xff0c;会在100k电阻上产生叠加电压&#x…

pve宿主机更改网络导致没网,pve更改ip

一、问题描述 快过年了&#xff0c;我把那台一直在用的小型服务器&#xff0c;带回去了&#xff0c;导致网络发生了变更&#xff0c;需要对网络进行调整&#xff0c;否则连不上网&#xff0c;我这里改的是宿主机&#xff0c;不是pve虚拟机中的系统。 二、解决方法 pve用的是…

07.领域驱动设计:3种常见微服务架构模型的对比和分析

目录 1、概述 2、整洁架构 3、六边形架构 4、三种微服务架构模型的对比和分析 5、从三种架构模型看中台和微服务设计 5.1 中台建设要聚焦领域模型 5.2 微服务要有合理的架构分层 5.2.1 项目级微服务 5.2.2 企业级中台微服务 5.3 应用和资源的解耦与适配 6、总结 1、概…
最新文章