昇思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.rhkb.cn/news/370805.html

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

相关文章

常见的Java运行时异常

常见的Java运行时异常 1、ArithmeticException&#xff08;算术异常&#xff09;2、ClassCastException &#xff08;类转换异常&#xff09;3、IllegalArgumentException &#xff08;非法参数异常&#xff09;4、IndexOutOfBoundsException &#xff08;下标越界异常&#xf…

dell Vostro 3690安装win11 23h2 方法

下载rufus-4.5.exe刻U盘去除限制 https://www.dell.com/support/home/zh-cn/product-support/product/vostro-3690-desktop/drivers dell官网下载驱动解压到U盘 https://dl.dell.com/FOLDER09572293M/2/Intel-Rapid-Storage-Technology-Driver_88DM9_WIN64_18.7.6.1010_A00_01…

ros1仿真导航机器人 navigation

仅为学习记录和一些自己的思考&#xff0c;不具有参考意义。 1navigation导航框架 2导航设置过程 &#xff08;1&#xff09;启动仿真环境 roslaunch why_simulation why_robocup.launch &#xff08;2&#xff09;启动move_base导航、amcl定位 roslaunch why_simulation nav…

基于Maximin的异常检测方法(MATLAB)

异常存在于各个应用领域之中&#xff0c;往往比正常所携带的信息更多也更为重要。例如医疗系统中疾病模式&#xff0c;信用卡消费中的欺诈行为&#xff0c;数据库中数据泄露&#xff0c;大型机器故障&#xff0c;网络入侵行为等。大数据技术体系的快速兴起与发展&#xff0c;加…

使用Spring Boot和自定义缓存注解优化应用性能

在现代应用开发中&#xff0c;缓存是提高系统性能和响应速度的关键技术之一。Spring Boot提供了强大的缓存支持&#xff0c;但有时我们需要更灵活的缓存控制。本文将介绍如何使用Spring Boot和自定义缓存注解来优化应用性能。 1. 为什么需要自定义缓存注解&#xff1f; Sprin…

【ue5】虚幻5同时开多个项目

正常开ue5项目我是直接在桌面点击快捷方式进入 只会打开一个项目 如果再想打开一个项目需要进入epic 再点击启动就可以再开一个项目了

步进电机改伺服电机

步进电机&#xff1a; 42&#xff1a;轴径5mm 57&#xff1a;轴径8mm 86&#xff1a;轴径14mm 【86CME120闭环】// 12牛米 伺服电机&#xff1a; 40&#xff1a; 60&#xff1a; 80&#xff1a; 86&#xff1a; ECMA——C 1 0910 R S 4.25A 轴径…

分享大厂对于缓存操作的封装

hello&#xff0c;伙伴们好久不见&#xff0c;我是shigen。发现有两周没有更新我的文章了。也是因为最近比较忙&#xff0c;基本是993了。 缓存大家再熟悉不过了&#xff0c;几乎是现在任何系统的标配&#xff0c;并引申出来很多的问题&#xff1a;缓存穿透、缓存击穿、缓存雪崩…

UEC++ 虚幻5第三人称射击游戏(二)

UEC++ 虚幻5第三人称射击游戏(二) 派生榴弹类武器 新建一个继承自Weapon的子类作为派生榴弹类武器 将Weapon类中的Fire函数添加virtual关键字变为虚函数让榴弹类继承重写 在ProjectileWeapon中重写Fire函数,新建生成投射物的模版变量 Fire函数重写逻辑 代码//生成的投射物U…

如何计算弧线弹道的落地位置

1&#xff09;如何计算弧线弹道的落地位置 2&#xff09;Unity 2021 IL2CPP下使用Protobuf-net序列化报异常 3&#xff09;编译问题&#xff0c;用Mono可以&#xff0c;但用IL2CPP就报错 4&#xff09;Wwise的Bank在安卓上LoadBank之后&#xff0c;播放没有声音 这是第393篇UWA…

DP:背包问题----0/1背包问题

文章目录 &#x1f497;背包问题&#x1f49b;背包问题的变体&#x1f9e1;0/1 背包问题的数学定义&#x1f49a;解决背包问题的方法&#x1f499;例子 &#x1f497;解决背包问题的一般步骤&#xff1f;&#x1f497;例题&#x1f497;总结 ❤️❤️❤️❤️❤️博客主页&…

毕业季有感

本文介绍一些刚毕业、即将入职前的随想与心得。 毕业和上班无缝衔接。要离开北京了&#xff0c;来到天津。 我一向很喜欢探索新环境&#xff0c;每一次要到新的学校、新的城市、新的单位都会很激动&#xff1b;这一次也是一样&#xff0c;在一开始几乎只有对新环境的憧憬。但是…

rocketmq-console可视化界面功能说明

rocketmq-console可视化界面功能说明 登录界面OPS(运维)Dashboard(驾驶舱)Cluster(集群)Topic(主题)Consumer(消费者)Producer(生产者)Message(消息)MessageTrace(消息轨迹) rocketmq-console是rocketmq的一款可视化工具&#xff0c;提供了mq的使用详情等功能。 本章针对于rock…

【机器学习】机器学习重塑广告营销:精准触达,高效转化的未来之路

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀目录 &#x1f4d2;1. 引言&#x1f4d9;2. 机器学习基础与广告营销的结合&#x1f9e9;机器学习在广告营销中的核心应用领域&#x1f339;用…

CSRF靶场通关合集

目录 前言 CSRF漏洞总结 1.PiKachu靶场 1.1CSRF(get) 1.2 CSRF(post)请求 1.3 CSRF Token 2.DVWA靶场 难度低 难度中 难度高 前言 最近系统的将从web渗透到内网渗透的知识点做一个回顾,同时结合一些实战的案例来演示,下面是对刚开始学习时对靶场的一个总结. CSRF漏洞…

LLM - 循环神经网络(RNN)

1. RNN的关键点&#xff1a;即在处理序列数据时会有顺序的记忆。比如&#xff0c;RNN在处理一个字符串时&#xff0c;在对字母表顺序有记忆的前提下&#xff0c;处理这个字符串会更容易。就像人一样&#xff0c;读取下面第一个字符串会更容易&#xff0c;因为人对字母出现的顺序…

LabVIEW汽车转向器测试系统

绍了一种基于LabVIEW的汽车转向器测试系统。该系统集成了数据采集、控制和分析功能&#xff0c;能够对转向器进行高效、准确的测试。通过LabVIEW平台&#xff0c;实现了对转向器性能参数的实时监测和分析&#xff0c;提升了测试效率和数据精度&#xff0c;为汽车转向器的研发和…

《Windows API 每日一练》8.4 edit控件

编辑类是最简单的预定义窗口类&#xff0c;而另一方面却又是最复杂的。当你用“edit”作为类名创建子窗口时&#xff0c;可以基于CreateWindow调用的x坐标、y坐标、宽度和高度参数定义一个矩形。这个矩形包含可编辑的文本。一旦子窗口控件获得输入焦点&#xff0c;你就可以输入…

(南京观海微电子)——MOS管原理及应用区别

MOS管&#xff1a; 全称为金属氧化物半导体场效应管&#xff08;Metal Oxide Semiconductor Field Effect Transistor&#xff09;&#xff0c;也被称为MOSFET&#xff08;Metal-Oxide-Semiconductor Field-Effect Transistor&#xff09;。它是一种半导体器件&#xff0c;常用…

【PB案例学习笔记】-27制作一个控制任务栏显示与隐藏的小程序

写在前面 这是PB案例学习笔记系列文章的第27篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…