昇思MindSpore学习笔记5-01生成式--LSTM+CRF序列标注

摘要:

        记录昇思MindSpore AI框架使用LSTM+CRF模型分词标注的步骤和方法。包括环境准备、score计算、Normalizer计算、Viterbi算法、CRF组合,以及改进的双向LSTM+CRF模型。

一、

1.序列标注

标注标签输入序列中的每个Token

用于抽取文本信息

        分词(Word Segmentation)

        词性标注(Position Tagging)

        命名实体识别(Named Entity Recognition, NER)

例如:

输入序列

输出标注

B

I

I

I

O

O

O

O

O

B

I

清华大学 和 北京是地名,标签后便于识别实体

“BIOE”标注法:实体(Entity)的开头标注为B,其他部分标注为I,非实体标注为O

2.条件随机场(Conditional Random Field, CRF)

标注序列

        标签预测序列中每个Token,

        简单的多分类问题

        相邻Token直接有关联关系

输入序列

输出标注

B

I

I

I

输出标注

O

I

I

I

×

正确实体中的Token有依赖关系

        I前必须是B或I

        错误标注O违背了依赖

引入学习关联关系的算法----条件随机场概率图模型保证依赖正确性。

条件随机场

        定义

        参数化

序列标注问题的线性序列

        选用条件随机场特指线性链条件随机场(Linear Chain CRF)

3.公式

x={x0,...,xn}                 输入序列

y={y0,...,yn},yY      输出标注序列

n                                  序列最大长度

Y                 ​​​​​​​        ​​​​​​​         x对应的所有可能的输出序列集合

输出序列y的概率为:

P(y|x)=\frac{exp(Score(x,y))}{\sum _{y'\epsilon Y}exp(Score(x,y'))}                     (1)

x_i          序列第i个Token

y_i           x_i对应的标签

Score    捕获相邻标签y_{i-1}y_i之间的关系

定义概率函数:

1. 发射概率函数ΨEMIT :表示x_i y_i的概率。

2. 转移概率函数ΨTRANS:表示y_{i-1}y_i的概率。

Score计算公式:

         

T         标签集合

P         构造大小为|T|*|T|的矩阵

            存储标签间转移概率

ℎ         编码层(Dense、LSTM等)输出的隐状态

            发射概率

Score计算公式转化为:

实现CRF参数化形式

CRF层前向训练部分

CRF和损失函数合并

选择负对数似然函数(Negative Log Likelihood, NLL)

Loss=-log(P(y\mid x))                (4)

代入公式(1)

公式(5),被减数Normalizer,减数Score,分别实现后相减得Loss。

二、环境准备

%%capture captured_output
# 实验环境已经预装了mindspore==2.2.14,如需更换mindspore版本,可更改下面mindspore的版本号
!pip uninstall mindspore -y
!pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore==2.2.14
# 查看当前 mindspore 版本
!pip show mindspore

输出:

Name: mindspore
Version: 2.2.14
Summary: MindSpore is a new open source deep learning training/inference framework that could be used for mobile, edge and cloud scenarios.
Home-page: https://www.mindspore.cn
Author: The MindSpore Authors
Author-email: contact@mindspore.cn
License: Apache 2.0
Location: /home/nginx/miniconda/envs/jupyter/lib/python3.9/site-packages
Requires: asttokens, astunparse, numpy, packaging, pillow, protobuf, psutil, scipy
Required-by: 

三、Score计算

公式(3)计算正确标签序列所对应的得分,

转移概率矩阵P

两个大小为|T|的向量

        序列开始时的转移概率

        序列结束时的转移概率

mask         掩码矩阵忽略打包序列时的填充值

Score        仅计算有效Token

def compute_score(emissions, tags, seq_ends, mask, trans, start_trans, end_trans):# emissions: (seq_length, batch_size, num_tags)# tags: (seq_length, batch_size)# mask: (seq_length, batch_size)
​seq_length, batch_size = tags.shapemask = mask.astype(emissions.dtype)
​# 将score设置为初始转移概率# shape: (batch_size,)score = start_trans[tags[0]]# score += 第一次发射概率# shape: (batch_size,)score += emissions[0, mnp.arange(batch_size), tags[0]]
​for i in range(1, seq_length):# 标签由i-1转移至i的转移概率(当mask == 1时有效)# shape: (batch_size,)score += trans[tags[i - 1], tags[i]] * mask[i]
​# 预测tags[i]的发射概率(当mask == 1时有效)# shape: (batch_size,)score += emissions[i, mnp.arange(batch_size), tags[i]] * mask[i]
​# 结束转移# shape: (batch_size,)last_tags = tags[seq_ends, mnp.arange(batch_size)]# score += 结束转移概率# shape: (batch_size,)score += end_trans[last_tags]
​return score

四、Normalizer计算

公式(5)中被减数Normalizer的计算

        x对应的所有可能输出序列的Score的对数指数和(Log-Sum-Exp)

        穷举法计算

                计算每个可能的输出序列Score

                共有|T|个结果

        动态规划算法

                复用计算结果提高效率

        计算Score_i

        先计算Score_{i-1}

Normalizer简化为:

h_i         第i个Token发射概率

P         转移矩阵。

h_i和P与y序列计算无关,可提出得:

由公式(7),Normalizer的实现如下:

def compute_normalizer(emissions, mask, trans, start_trans, end_trans):# emissions: (seq_length, batch_size, num_tags)# mask: (seq_length, batch_size)
​seq_length = emissions.shape[0]
​# 将score设置为初始转移概率,并加上第一次发射概率# shape: (batch_size, num_tags)score = start_trans + emissions[0]
​for i in range(1, seq_length):# 扩展score的维度用于总score的计算# shape: (batch_size, num_tags, 1)broadcast_score = score.expand_dims(2)
​# 扩展emission的维度用于总score的计算# shape: (batch_size, 1, num_tags)broadcast_emissions = emissions[i].expand_dims(1)
​# 根据公式(7),计算score_i# 此时broadcast_score是由第0个到当前Token所有可能路径# 对应score的log_sum_exp# shape: (batch_size, num_tags, num_tags)next_score = broadcast_score + trans + broadcast_emissions
​# 对score_i做log_sum_exp运算,用于下一个Token的score计算# shape: (batch_size, num_tags)next_score = ops.logsumexp(next_score, axis=1)
​# 当mask == 1时,score才会变化# shape: (batch_size, num_tags)score = mnp.where(mask[i].expand_dims(1), next_score, score)
​# 最后加结束转移概率# shape: (batch_size, num_tags)score += end_trans# 对所有可能的路径得分求log_sum_exp# shape: (batch_size,)return ops.logsumexp(score, axis=1)

五、Viterbi算法

实现解码部分

        选择适合求解序列最优路径的Viterbi算法

        动态规划求解所有可能的预测序列得分

        保存第i个Token对应的score取值最大的标签

                Viterbi算法求解最优预测序列要用

最大概率得分Score

每个Token对应的标签历史History

根据Viterbi算法得到公式:P_{0, i}=max(P_{0, i-1})+P_{i-1, i}

逆序求解每一个概率最大的标签,构成最佳的预测序列。

def viterbi_decode(emissions, mask, trans, start_trans, end_trans):# emissions: (seq_length, batch_size, num_tags)# mask: (seq_length, batch_size)
​seq_length = mask.shape[0]
​score = start_trans + emissions[0]history = ()
​for i in range(1, seq_length):broadcast_score = score.expand_dims(2)broadcast_emission = emissions[i].expand_dims(1)next_score = broadcast_score + trans + broadcast_emission
​# 求当前Token对应score取值最大的标签,并保存indices = next_score.argmax(axis=1)history += (indices,)
​next_score = next_score.max(axis=1)score = mnp.where(mask[i].expand_dims(1), next_score, score)
​score += end_trans
​return score, history
​
def post_decode(score, history, seq_length):# 使用Score和History计算最佳预测序列batch_size = seq_length.shape[0]seq_ends = seq_length - 1# shape: (batch_size,)best_tags_list = []
​# 依次对一个Batch中每个样例进行解码for idx in range(batch_size):# 查找使最后一个Token对应的预测概率最大的标签,# 并将其添加至最佳预测序列存储的列表中best_last_tag = score[idx].argmax(axis=0)best_tags = [int(best_last_tag.asnumpy())]
​# 重复查找每个Token对应的预测概率最大的标签,加入列表for hist in reversed(history[:seq_ends[idx]]):best_last_tag = hist[idx][best_tags[-1]]best_tags.append(int(best_last_tag.asnumpy()))
​# 将逆序求解的序列标签重置为正序best_tags.reverse()best_tags_list.append(best_tags)
​return best_tags_list

六、CRF层

组装CRF层

        输入序列可能存在Padding

        输入序列的真实长度,seq_length参数

        生成mask矩阵的sequence_mask方法。

        nn.Cell封装

CRF层代码:

import mindspore as ms
import mindspore.nn as nn
import mindspore.ops as ops
import mindspore.numpy as mnp
from mindspore.common.initializer import initializer, Uniform
​
def sequence_mask(seq_length, max_length, batch_first=False):"""根据序列实际长度和最大长度生成mask矩阵"""range_vector = mnp.arange(0, max_length, 1, seq_length.dtype)result = range_vector < seq_length.view(seq_length.shape + (1,))if batch_first:return result.astype(ms.int64)return result.astype(ms.int64).swapaxes(0, 1)
​
class CRF(nn.Cell):def __init__(self, num_tags: int, batch_first: bool = False, reduction: str = 'sum') -> None:if num_tags <= 0:raise ValueError(f'invalid number of tags: {num_tags}')super().__init__()if reduction not in ('none', 'sum', 'mean', 'token_mean'):raise ValueError(f'invalid reduction: {reduction}')self.num_tags = num_tagsself.batch_first = batch_firstself.reduction = reductionself.start_transitions = ms.Parameter(initializer(Uniform(0.1), (num_tags,)), name='start_transitions')self.end_transitions = ms.Parameter(initializer(Uniform(0.1), (num_tags,)), name='end_transitions')self.transitions = ms.Parameter(initializer(Uniform(0.1), (num_tags, num_tags)), name='transitions')
​def construct(self, emissions, tags=None, seq_length=None):if tags is None:return self._decode(emissions, seq_length)return self._forward(emissions, tags, seq_length)
​def _forward(self, emissions, tags=None, seq_length=None):if self.batch_first:batch_size, max_length = tags.shapeemissions = emissions.swapaxes(0, 1)tags = tags.swapaxes(0, 1)else:max_length, batch_size = tags.shape
​if seq_length is None:seq_length = mnp.full((batch_size,), max_length, ms.int64)
​mask = sequence_mask(seq_length, max_length)
​# shape: (batch_size,)numerator = compute_score(emissions, tags, seq_length-1, mask, self.transitions, self.start_transitions, self.end_transitions)# shape: (batch_size,)denominator = compute_normalizer(emissions, mask, self.transitions, self.start_transitions, self.end_transitions)# shape: (batch_size,)llh = denominator - numerator
​if self.reduction == 'none':return llhif self.reduction == 'sum':return llh.sum()if self.reduction == 'mean':return llh.mean()return llh.sum() / mask.astype(emissions.dtype).sum()
​def _decode(self, emissions, seq_length=None):if self.batch_first:batch_size, max_length = emissions.shape[:2]emissions = emissions.swapaxes(0, 1)else:batch_size, max_length = emissions.shape[:2]
​if seq_length is None:seq_length = mnp.full((batch_size,), max_length, ms.int64)
​mask = sequence_mask(seq_length, max_length)
​return viterbi_decode(emissions, mask, self.transitions, self.start_transitions, self.end_transitions)

七、BiLSTM+CRF模型

双向LSTM+CRF模型训练命名实体识别任务。

模型结构如下:

        nn.Embedding -> nn.LSTM -> nn.Dense -> CRF

LSTM         提取序列特征

Dense层     变换获得发射概率矩阵

CRF层

具体代码:

class BiLSTM_CRF(nn.Cell):def __init__(self, vocab_size, embedding_dim, hidden_dim, num_tags, padding_idx=0):super().__init__()self.embedding = nn.Embedding(vocab_size, embedding_dim, padding_idx=padding_idx)self.lstm = nn.LSTM(embedding_dim, hidden_dim // 2, bidirectional=True, batch_first=True)self.hidden2tag = nn.Dense(hidden_dim, num_tags, 'he_uniform')self.crf = CRF(num_tags, batch_first=True)
​def construct(self, inputs, seq_length, tags=None):embeds = self.embedding(inputs)outputs, _ = self.lstm(embeds, seq_length=seq_length)feats = self.hidden2tag(outputs)
​crf_outs = self.crf(feats, tags, seq_length)return crf_outs

生成两句示例和对应的标签

构造词表和标签表

embedding_dim = 16
hidden_dim = 32
​
training_data = [("清 华 大 学 坐 落 于 首 都 北 京".split(),"B I I I O O O O O B I".split()
), ("重 庆 是 一 个 魔 幻 城 市".split(),"B I O O O O O O O".split()
)]
​
word_to_idx = {}
word_to_idx['<pad>'] = 0
for sentence, tags in training_data:for word in sentence:if word not in word_to_idx:word_to_idx[word] = len(word_to_idx)
​
tag_to_idx = {"B": 0, "I": 1, "O": 2}
len(word_to_idx)

输出:

21

实例化模型

选择优化器

Wrapper封装模型和优化器

model = BiLSTM_CRF(len(word_to_idx), embedding_dim, hidden_dim, len(tag_to_idx))
optimizer = nn.SGD(model.trainable_params(), learning_rate=0.01, weight_decay=1e-4)
grad_fn = ms.value_and_grad(model, None, optimizer.parameters)
​
def train_step(data, seq_length, label):loss, grads = grad_fn(data, seq_length, label)optimizer(grads)return loss

生成数据打包成Batch

按序列最大长度,填充长度不足的序列,

返回Tensor

        输入序列

        输出标签

        序列长度

def prepare_sequence(seqs, word_to_idx, tag_to_idx):seq_outputs, label_outputs, seq_length = [], [], []max_len = max([len(i[0]) for i in seqs])
​for seq, tag in seqs:seq_length.append(len(seq))idxs = [word_to_idx[w] for w in seq]labels = [tag_to_idx[t] for t in tag]idxs.extend([word_to_idx['<pad>'] for i in range(max_len - len(seq))])labels.extend([tag_to_idx['O'] for i in range(max_len - len(seq))])seq_outputs.append(idxs)label_outputs.append(labels)
​return ms.Tensor(seq_outputs, ms.int64), \ms.Tensor(label_outputs, ms.int64), \ms.Tensor(seq_length, ms.int64)

输出:

data, label, seq_length = prepare_sequence(training_data, word_to_idx, tag_to_idx)
data.shape, label.shape, seq_length.shape
((2, 11), (2, 11), (2,))

预编译模型

训练500step

训练流程可视化依赖tqdm库

        安装pip install tqdm

from tqdm import tqdm
​
steps = 500
with tqdm(total=steps) as t:for i in range(steps):loss = train_step(data, seq_length, label)t.set_postfix(loss=loss)t.update(1)

输出:

  0%|          | 0/500 [00:00<?, ?it/s]
/
100%|██████████| 500/500 [04:33<00:00,  1.83it/s, loss=0.33540726] 
score, history = model(data, seq_length)
score
\

输出:

Tensor(shape=[2, 3], dtype=Float32, value=
[[ 3.26808167e+01,  3.74181900e+01,  3.23572197e+01],[ 2.94694691e+01,  2.79099541e+01,  3.44803162e+01]])

预测得分后处理

predict = post_decode(score, history, seq_length)
predict

输出:

[[0, 1, 1, 1, 2, 2, 2, 2, 2, 0, 1], [0, 1, 2, 2, 2, 2, 2, 2, 2]]

转换预测的index序列为标签序列

idx_to_tag = {idx: tag for tag, idx in tag_to_idx.items()}
​
def sequence_to_tag(sequences, idx_to_tag):outputs = []for seq in sequences:outputs.append([idx_to_tag[i] for i in seq])return outputs
sequence_to_tag(predict, idx_to_tag)

输出:

[['B', 'I', 'I', 'I', 'O', 'O', 'O', 'O', 'O', 'B', 'I'],['B', 'I', 'O', 'O', 'O', 'O', 'O', 'O', 'O']]

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

InetAddress.getLocalHost().getHostAddress()阻塞导致整个微服务崩溃

InetAddress.getLocalHost().getHostAddress()阻塞导致整个微服务崩溃 import java.net.InetAddress;public class GetHostIp {public static void main(String[] args) {try {long start System.currentTimeMillis();String ipAddress InetAddress.getLocalHost().getHostA…

【计算机网络】物理层(作业)

1、若信道在无噪声情况下的极限数据传输速率不小于信噪比为30dB 条件下的极限数据传输速率&#xff0c;则信号状态数至少是&#xff08;D&#xff09;。 A. 4B. 16C. 8D. 32 解析&#xff1a;可用奈奎斯特采样定理计算无噪声情况下的极限数据传输速率&#xff0c;用香农第二定…

Docker 容器网络及其配置说明

Docker 容器网络及其配置说明 docker容器网络docker的4种网络模式bridge 模式container模式host 模式none 模式应用场景 docker 容器网络配置Linux 内核实现名称空间的创建创建 Network Namespace操作 Network Namespace 转移设备veth pair创建 veth pair实现 Network Namespac…

三、docker配置阿里云镜像仓库并配置docker代理

一、配置阿里云镜像仓库 1. 登录阿里云官网&#xff0c;并登录 https://www.aliyun.com/ 2. 点击产品 - 容器 - 容器与镜像服务ACR - 管理控制台 - 镜像工具 - 镜像加速器 二、配置docker代理 #1. 创建docker相关的systemd文件 mkdir -p /etc/systemd/system/docker.servic…

SQLite 嵌入式数据库

目录&#xff1a; 一、SQLite 简介二、SQLite 数据库安装1、安装方式一&#xff1a;2、安装方式二&#xff1a; 三、SQLite 的命令用法1、创建、打开、退出数据库&#xff1a;2、编辑数据库&#xff1a; 四、SQLite 的编程操作1、打开 / 创建数据库的 C 接口&#xff1a;2、操作…

Qt/C++音视频开发78-获取本地摄像头支持的分辨率/帧率/格式等信息/mjpeg/yuyv/h264

一、前言 上一篇文章讲到用ffmpeg命令方式执行打印到日志输出&#xff0c;可以拿到本地摄像头设备信息&#xff0c;顺藤摸瓜&#xff0c;发现可以通过执行 ffmpeg -f dshow -list_options true -i video“Webcam” 命令获取指定摄像头设备的分辨率帧率格式等信息&#xff0c;会…

基于springboot+vue+uniapp的高校宿舍信息管理系统小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

【数据结构/操作系统 堆和栈】区别及应用场景、底层原理图解

堆和栈 比较有趣的是&#xff0c;计算机网络、操作系统中都会对堆栈有不同方面比较详细的描述&#xff0c;而使用的地方通常对这些底层的细节表现得没有那么明显。 但如果你能了解堆栈在计算机网络和操作系统中的表现形式&#xff0c;在你写代码时就会有不一样的认识&#xff…

Nordic 52832作为HID 键盘连接配对电视/投影后控制没反应问题的分析和解决

问题现象&#xff1a;我们的一款HID键盘硬件一直都工作的很好&#xff0c;连接配对后使用起来和原装键盘效果差不多&#xff0c;但是后面陆续有用户反馈家里的电视等蓝牙设备配对连接我们的键盘后&#xff0c;虽然显示已连接&#xff0c;但实际上控制不了。设备涉及到了好些品牌…

Sentinel-1 Level 1数据处理的详细算法定义(一)

《Sentinel-1 Level 1数据处理的详细算法定义》文档定义和描述了Sentinel-1实现的Level 1处理算法和方程&#xff0c;以便生成Level 1产品。这些算法适用于Sentinel-1的Stripmap、Interferometric Wide-swath (IW)、Extra-wide-swath (EW)和Wave模式。 今天介绍的内容如下&…

linux软链接和硬链接的区别

1 创建软链接和硬链接 如下图所示&#xff0c;一开始有两个文件soft和hard。使用 ln -s soft soft1创建软链接&#xff0c;soft1是soft的软链接&#xff1b;使用ln hard hard1创建硬链接&#xff0c;hard1是hard的硬链接。可以看到软链接的文件类型和其它3个文件的文件类型是不…

【JVM系列】Full GC(完全垃圾回收)的原因及分析

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

使用Python实现CartPole游戏

在深度强化学习内容的介绍中&#xff0c;提出了CartPole游戏进行深度强化学习&#xff0c;现在提供一种用Python简单实现Cart Pole游戏的方法。 1. 游戏介绍 CartPole 游戏是一个经典的强化学习问题&#xff0c;其中有一个小车&#xff08;cart&#xff09;和一个杆&#xff…

用网络编程完成windows和linux跨平台之间的通信(服务器)

服务器代码逻辑&#xff1a; 服务器功能 创建 Socket&#xff1a; 服务器首先创建一个 Socket 对象&#xff0c;用于进行网络通信。通常使用 socket() 函数创建。 绑定&#xff08;Bind&#xff09;&#xff1a; 服务器将 Socket 绑定到一个特定的 IP 地址和端口号上。这是通过…

昇思25天学习打卡营第19天 | RNN实现情感分类

RNN实现情感分类 概述 情感分类是自然语言处理中的经典任务&#xff0c;是典型的分类问题。本节使用MindSpore实现一个基于RNN网络的情感分类模型&#xff0c;实现如下的效果&#xff1a; 输入: This film is terrible 正确标签: Negative 预测标签: Negative输入: This fil…

Go 中的类型推断

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

【三级等保】等保整体建设方案(Word原件)

建设要点目录&#xff1a; 1、系统定级与安全域 2、实施方案设计 3、安全防护体系建设规划 软件全文档&#xff0c;全方案获取方式&#xff1a;本文末个人名片直接获取。

【Python】基于KMeans的航空公司客户数据聚类分析

&#x1f490;大家好&#xff01;我是码银~&#xff0c;欢迎关注&#x1f490;&#xff1a; CSDN&#xff1a;码银 公众号&#xff1a;码银学编程 实验目的和要求 会用Python创建Kmeans聚类分析模型使用KMeans模型对航空公司客户价值进行聚类分析会对聚类结果进行分析评价 实…

Python酷库之旅-第三方库Pandas(008)

目录 一、用法精讲 16、pandas.DataFrame.to_json函数 16-1、语法 16-2、参数 16-3、功能 16-4、返回值 16-5、说明 16-6、用法 16-6-1、数据准备 16-6-2、代码示例 16-6-3、结果输出 17、pandas.read_html函数 17-1、语法 17-2、参数 17-3、功能 17-4、返回值…

介绍东芝TB62262FTAG芯片:高性能两相双极步进电机驱动器

在当今快速发展的科技领域&#xff0c;高性能的电机驱动器对于许多工程项目来说至关重要。东芝的TB62262FTAG这款两相双极步进电机驱动器采用PWM斩波技术&#xff0c;集成了多个先进功能&#xff0c;适用于各种工业和消费类应用。本文将详细介绍TB62262FTAG的参数、性能、优势及…