【深度学习--RNN 循环神经网络--附LSTM情感文本分类】

deep learning 系列 --RNN 循环神经网络

什么是序列模型

包括了RNN LSTM GRU等网络模型,主要用途是自然语言处理、语音识别等方面,比如生成乐曲,音频转换为文字,文本情感分类,机器翻译等等

标准模型的缺陷

以往的标准模型比如CNN,每次的输入不影响下次的输出,也就是说每次输入的图片都是独立的,没有任何关联,但是很多情况下,我们建立的模型与前项甚至后项的输入是相关的。举个例子,我们要从这两句话中识别出人名:

President Teddy was …
Teddy bear was…

这其中都有一个关键且同样的词Teddy,但是这个Teddy可以是个人名,也可以是泰迪,究竟是哪个呢?通常的识别方法是:

1.从之前学习过的人名识别

我们之前的位置可能提取过Teddy是人名, 但CNN网络,并不能共享从不同位置提取到到的特征,因此不可行

2.从本次输入的上下文出发

比如这里下文有 bear,President,但是CNN也不具备这种序列特性,因此也不可行

另外,CNN的网络对于输入的模型的数据长度都是固定的,但是不同的句子长度并不一致,当然我们可以padding,但是不那么好。
因此才有了循环神经网络架构,它可以克服以上的这些问题

循环神经网络

架构

在这里插入图片描述
图示是循环神经网络架构, 是有循环的,这个图形是最长用于展示的,但实际并不好理解,这个环实际上是多次的输入和输出,下面这种展开的方式更容易理解
在这里插入图片描述
RNN的结构就包括三类输入的权重参数
1.激活值的参数,水平方向的值,每个时间步的激活参数是相同的
2.输入到隐藏层的参数,也就是x到A方向,这个也是每个时间步相同
3.用来预测输出的参数,A到h的方向
在这里插入图片描述
其中y的输出可以用如下表示:
在这里插入图片描述

在这里插入图片描述
预测值y<t>包括了激活值a<t>,而a<t>包括了x<t-1>(注意此处t表示时间步),也就是说每一次的预测输出不仅包括了本次x<t>也包括了上个时间步x<t-1>,依次类推,此次时间步的输出包括了之前所有输入

BPTT 反向传播

实际上在工具中,反向传播都是自动进行的,原理跟普通的反向传播一致
输入序列后,假设预测的值是0.9,但实际是1,产生损失,这个损失可以用交叉熵损失函数来估量
RNN中的反向传播时将之前所有的箭头都反过来,计算出合适的变量,通过导数相关的计算,利用梯度下降算法更新参数,也就是图示:
在这里插入图片描述
这个传播过程中最重要的就是水平方向时间步的反向,因此又叫做穿越时间的反向传播Back Propagate Through time

不同类型的循环神经网络

  • 多对多
    比如机器翻译,输入是多个,输出也是多个,且并不对等,此时经常使用的是encode-decoder,encoder编码器获取输入,并输出,而decoder则使用encode编码的输出,执行decoder,这样输入x和输出y的长度就可以不相同了
    在这里插入图片描述
    当然输入和输出对等的情况也很多
    比如在句子中寻找人名,预测输出y就可以是每个x的对应的每个位置输出y(0表示非人名,1表示是人名)

  • 多对一
    在这里插入图片描述
    比如进行文本的情感分类,这样输入就有可能是一段文本,而输出我们只需要最后一层最后一个时间步的输出即可,一个0和1就足以标识这段文本是positive还是negative

  • 一对多
    在这里插入图片描述
    比如生成类的,输入一个音符或者不输入,就可以产生多个输出

RNN的缺陷 梯度消失和梯度爆炸

  • 现象:

    实际上深度比较大的网络都可能梯度消失或者爆炸,这种现象在在RNN中更加明显
    当我们输入的序列为1000的时候,拿最简单的模型举例 y = wx 经过1000次的传播,y1000 的变化
    在这里插入图片描述
    w仅仅变化一点,经过1000次的传播,变化非常的大

  • 原因:

    发生这样的根本原因是RNN中每一次的输出都将被前面的数据彻底的清洗,而经过长时间的传输,很前面步的影响都后面的影响已经很微弱了,损失的反向调整同样也是,经过长距离的调整,差错已经很难反馈很多个时间步之后了

  • 解决办法:

    梯度爆炸:进行梯度裁剪即可,比如我们发现输出有很多超大的值的时候,进行裁剪
    梯度消失:它很难察觉,也在标准的RNN结构中,可以用GRU或者LSTM解决,而解决梯度消失的实质是通过保留一些前期输入的记忆

LSTM

标准的RNN结构是这样的:
在这里插入图片描述
而标准的LSTM加入了四个门控单元
在这里插入图片描述
这四个门单元分别是:
it, ft,gt, ot 分别是输入门、遗忘门、cell(记忆)门,以及输出门
他们控制 是否输入,对输入的遗忘和记忆,也控制是否输出,从而控制重要信息的传递,不重要信息遗忘

GRU

它比LSTM诞生更晚,是LSTM的变形版本,由于门单元更少,计算简单些,因此训练时间更短一些。
在这里插入图片描述

LSTM实例

本实例以IMDB数据集为例,代码篇幅过长,本文仅列示其中LSTM使用相关的重点,后续会有专门的博客详细解析代码。

  1. 预处理数据
    读取IMDB数据集
def read_imdb(datafolder ='train', dataroot=imdb_zip_path):data=[]for label in ['pos', 'neg']:filepath = os.path.join(imdb_zip_path, datafolder, label)for file in tqdm(os.listdir(filepath)):with open(os.path.join(filepath,file), 'rb') as f:content = f.read().decode('utf-8').replace('\n', ' ').lower()data.append([content, 1 if label == 'pos' else 0])random.shuffle(data)return data

IMDB中的数据分词

def get_tokenized(data):def tokenizer(text):filters = ['!', '"', '#', '$', '%', '&', '\(', '\)', '\*', '\+', ',', '-', '\.', '/', ':', ';', '<', '=', '>','\?', '@', '\[', '\\', '\]', '^', '_', '`', '\{', '\|', '\}', '~', '\t', '\n', '\x97', '\x96', '”', '“', ]text = re.sub("<.*?>", " ", text, flags=re.S)text = re.sub("|".join(filters), " ", text, flags=re.S)return [i.strip().lower() for i in text.split()]return [tokenizer(context) for context, _ in data]

创建分词后的词典

def get_vocab(data):counter = collections.Counter(_flatten(data))return vocab.vocab(counter)

封装dataloader

class ImdbLoader(object):def __init__(self, set_name='train', batch_size='64'):super(ImdbLoader, self).__init__()self.data_set = set_nameself.batch_size = batch_sizedef get_data_loader(self):# train_data = [['"dick tracy" is one of our"', 1],#               ['arguably this is a  the )', 1],#               ["i don't  just to warn anyone ", 0]]train_data = read_imdb(self.data_set)data = preprocess(train_data)#print(data)data_set = Data.TensorDataset(*data)data_loader = Data.DataLoader(data_set, self.batch_size, shuffle=True)return data_loader
  1. 创建模型
    此处创建了一个模型,包括双向的LSTM层和一个全连接层
 class BiRNN(nn.Module):def __init__(self, vocabulary, embed_len, hidden_len, num_layer):super(BiRNN, self).__init__()self.embedding = nn.Embedding(len(vocabulary), embed_len)self.encoder = nn.LSTM(input_size=embed_len,hidden_size=hidden_len,num_layers=num_layer,bidirectional=True,dropout = 0.3)# 本次使用起始和最终时间步的隐藏状态座位全连接层的输入self.decoder = nn.Linear(2*2*hidden_len, 2)def forward(self, inputs):#print('rnn model py: input_shape: ', inputs.shape)embeddings = self.embedding(inputs)glove_vab = getGlove()net.embedding.weight.data.copy_(load_pretrained_embedding(vo.get_itos(), glove_vab))net.embedding.weight.requires_grad = False#print('after embed input shape:', embeddings.shape)embeddings = embeddings.permute(1, 0, 2)output_sequence, _ = self.encoder(embeddings)concat_out = torch.cat((output_sequence[0], output_sequence[-1]), -1)outputs = self.decoder(concat_out)return outputs

3.模型训练

   def train(epoch, imdb_model, lr, train_batch_size):imdb_model_device = imdb_model.to(device)# 过滤掉不需要计算梯度的embedding的参数optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, imdb_model_device.parameters()), lr=lr)loader = ImdbLoader('train', train_batch_size)data_loader = loader.get_data_loader()for i in range(epoch):for idx, (inputs, target) in enumerate(data_loader):target = target.to(device)inputs = inputs.to(device)#print('train.py input shape:', inputs.shape)optimizer.zero_grad()output = imdb_model(inputs)#print('ouput.shape', output.shape)criterion = nn.CrossEntropyLoss()loss = criterion(output, target)loss.backward()optimizer.step()if idx % 10 == 0:predict = torch.max(output, dim=-1, keepdim=False)[-1]acc = predict.eq(target.data).cpu().numpy().mean() * 100print('train Epoch:{} processed:[{} / {} ({:.0f}%) Loss: {:.6f}, ACC: {:.6f}]'.format(i,idx * len(inputs),len(data_loader.dataset),100. * idx / len(data_loader),loss.item(),acc))torch.save(imdb_model.state_dict(), '../../resources/model_save/imdb_net.pkl')torch.save(optimizer.state_dict(), '../../resources/model_save/imdb_optimizer.pkl')

4.模型评估

 def test(imdb_model, test_batch_size):imdb_model.eval()imdb_model = imdb_model.to(device)loader = ImdbLoader('test', test_batch_size)data_loader = loader.get_data_loader()with torch.no_grad():for idx, (inputs, target) in enumerate(data_loader):target = target.to(device)inputs = inputs.to(device)#print(inputs)output = imdb_model(inputs)criterion = nn.CrossEntropyLoss()loss = criterion(output, target)predict = torch.max(output, dim=-1, keepdim=False)[-1]correct = predict.eq(target.data).sum()acc = 100. * predict.eq(target.data).cpu().numpy().mean()print('idx: {} loss : {}, accurate: {}/{} {:.2f}'.format(idx,  loss, correct, target.size(0), acc))

最终效果,当我们执行4个epoch后,准确率基本稳定在80%以上

train Epoch:3 processed:[23680 / 25000 (95%) Loss: 0.325240, ACC: 84.375000]
train Epoch:3 processed:[24320 / 25000 (97%) Loss: 0.449456, ACC: 75.000000]
train Epoch:3 processed:[15600 / 25000 (100%) Loss: 0.438567, ACC: 80.000000]
train Epoch:4 processed:[0 / 25000 (0%) Loss: 0.353131, ACC: 85.937500]
train Epoch:4 processed:[640 / 25000 (3%) Loss: 0.345814, ACC: 89.062500]
train Epoch:4 processed:[1280 / 25000 (5%) Loss: 0.195520, ACC: 93.750000]
train Epoch:4 processed:[1920 / 25000 (8%) Loss: 0.269773, ACC: 87.500000]
train Epoch:4 processed:[2560 / 25000 (10%) Loss: 0.287010, ACC: 85.937500]
train Epoch:4 processed:[3200 / 25000 (13%) Loss: 0.291449, ACC: 90.625000]

参考

colah https://colah.github.io/posts/2015-08-Understanding-LSTMs/
吴恩达 deep learning

https://pytorch.org/docs/stable/generated/torch.nn.LSTM.html#torch.nn.LSTM

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

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

相关文章

jenkins pipeline方式一键部署github项目

上篇&#xff1a;jenkins一键部署github项目 该篇使用jenkins pipeline-script一键部署&#xff0c;且介绍pipeline-scm jenkins环境配置 前言&#xff1a;按照上篇创建pipeline任务&#xff0c;结果报mvn&#xff0c;jdk环境不存在&#xff0c;就很疑惑&#xff0c;然后配置全…

bigemap如何添加mapbox地图?

第一步 打开浏览器&#xff0c;找到你要访问的地图的URL地址&#xff0c;并且确认可以正常在浏览器中访问&#xff1b;浏览器中不能访问&#xff0c;同样也不能在软件中访问。 以下为常用地图源地址&#xff1a; 天地图&#xff1a; http://map.tianditu.gov.cn 包含&…

代码pytorch-adda-master跑通记录

前言 最近在学习迁移学习&#xff0c;ADDA算法&#xff0c;由于嫌自己写麻烦&#xff0c;准备先跑通别人的代码。 代码名称&#xff1a;pytorch-adda-master 博客&#xff1a;https://www.cnblogs.com/BlairGrowing/p/17020378.html github地址&#xff1a;https://github.com…

vite打包配置以及性能优化

vite打包配置以及性能优化 安装插件 首先该安装的插件&#xff0c;你要安装一下吧 这三个是基本的插件&#xff0c;其他优化的插件下面会介绍到 "vite": "4.4.6","vite-plugin-html": "^3.2.0","vitejs/plugin-vue": &qu…

比ChatGPT更强的星火大模型V2版本发布!

初体验 测试PPT生成 结果&#xff1a; 达到了我的预期&#xff0c;只需要微调就可以直接交付&#xff0c;这点比ChatGPT要强很多. 测试文档问答 结果&#xff1a; 这点很新颖&#xff0c;现在类似这种文档问答的AI平台收费都贵的离谱&#xff0c;星火不但免费支持而且效果也…

Vue实现动态遍历生成el-input

实现效果: el-input的label是measureName, el-input绑定的值是formDatat.measureCode 接口返回的数据格式如下 处理过的formData的格式如下

AIGC绘画:kaggle部署stable diffusion项目绘画

文章目录 kaggle介绍项目部署edit my copy链接显示 结果展示 kaggle介绍 Kaggle成立于2010年&#xff0c;是一个进行数据发掘和预测竞赛的在线平台。从公司的角度来讲&#xff0c;可以提供一些数据&#xff0c;进而提出一个实际需要解决的问题&#xff1b;从参赛者的角度来讲&…

【Spring系列篇--关于IOC的详解】

目录 面试经典题目&#xff1a; 1. 什么是spring&#xff1f;你对Spring的理解&#xff1f;简单来说&#xff0c;Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。 2.什么是IoC&#xff1f;你对IoC的理解&#xff1f;IoC的重要性?将实例化对象的权利从程序员…

MybatisPlus整合p6spy组件SQL分析

目录 p6spy java为什么需要 如何使用 其他配置 p6spy p6spy是一个开源项目&#xff0c;通常使用它来跟踪数据库操作&#xff0c;查看程序运行过程中执行的sql语句。 p6spy将应用的数据源给劫持了&#xff0c;应用操作数据库其实在调用p6spy的数据源&#xff0c;p6spy劫持到…

Leetcode-每日一题【剑指 Offer 32 - II. 从上到下打印二叉树 II】

题目 从上到下按层打印二叉树&#xff0c;同一层的节点按从左到右的顺序打印&#xff0c;每一层打印到一行。 例如: 给定二叉树: [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7 返回其层次遍历结果&#xff1a; [ [3], [9,20], [15,7] ] 提示&#xff…

JVS开源基础框架:平台基本信息介绍

JVS是面向软件开发团队可以快速实现应用的基础开发脚手架&#xff0c;主要定位于企业信息化通用底座&#xff0c;采用微服务分布式框架&#xff0c;提供丰富的基础功能&#xff0c;集成众多业务引擎&#xff0c;它灵活性强&#xff0c;界面化配置对开发者友好&#xff0c;底层容…

一种多策略下RabbitMQ的延时队列实现

1.为什么会用到延时队列? 场景: 最近在开发一款系统中遇到这样一个场景,A系统开通套餐需要把套餐信息以邮件的形式发送给相关工作人员,经过人工审核通过后,在B系统里面开通,A系统会调B系统套餐列表接口查询套餐是否开通成功,开通成功则从A系统去完成订单,假如超过设定时间未开…