Pytorch的LSTM的理解
class torch.nn.LSTM(*args, **kwargs)
参数列表
- input_size:x的特征维度
- hidden_size:隐藏层的特征维度
- num_layers:lstm隐层的层数,默认为1
- bias:False则bih=0和bhh=0. 默认为True
- batch_first:True则输入输出的数据格式为 (batch, seq, feature)
- dropout:除最后一层,每一层的输出都进行dropout,默认为: 0
- bidirectional:True则为双向lstm默认为False
- 输入:input, (h0, c0)
- 输出:output, (hn,cn)
输入数据格式:
input(seq_len, batch, input_size)
h0(num_layers * num_directions, batch, hidden_size)
c0(num_layers * num_directions, batch, hidden_size)
输出数据格式:
output(seq_len, batch, hidden_size * num_directions)
hn(num_layers * num_directions, batch, hidden_size)
cn(num_layers * num_directions, batch, hidden_size)
Pytorch里的LSTM单元接受的输入都必须是3维的张量(Tensors).每一维代表的意思不能弄错。
第一维体现的是序列(sequence)结构,也就是序列的个数,用文章来说,就是每个句子的长度,因为是喂给网络模型,一般都设定为确定的长度,也就是我们喂给LSTM神经元的每个句子的长度,当然,如果是其他的带有带有序列形式的数据,则表示一个明确分割单位长度,
例如是如果是股票数据内,这表示特定时间单位内,有多少条数据。这个参数也就是明确这个层中有多少个确定的单元来处理输入的数据。
第二维度体现的是batch_size,也就是一次性喂给网络多少条句子,或者股票数据中的,一次性喂给模型多少是个时间单位的数据,具体到每个时刻,也就是一次性喂给特定时刻处理的单元的单词数或者该时刻应该喂给的股票数据的条数
第三位体现的是输入的元素(elements of input),也就是,每个具体的单词用多少维向量来表示,或者股票数据中 每一个具体的时刻的采集多少具体的值,比如最低价,最高价,均价,5日均价,10均价,等等
H0-Hn是什么意思呢?就是每个时刻中间神经元应该保存的这一时刻的根据输入和上一课的时候的中间状态值应该产生的本时刻的状态值,
这个数据单元是起的作用就是记录这一时刻之前考虑到所有之前输入的状态值,形状应该是和特定时刻的输出一致
c0-cn就是开关,决定每个神经元的隐藏状态值是否会影响的下一时刻的神经元的处理,形状应该和h0-hn一致。
当然如果是双向,和多隐藏层还应该考虑方向和隐藏层的层数。
注:上式中的 q后面跟一个单词,表示该单词的一定维度的向量表示,该维度即是LSTM接受的张量中的第三个维度。
记住这里存在一个尺寸为1的第二维度。此外,如果你希望一次在网络中走完整个序列,你可以将第一个维度的尺寸也设为1。
下面我简单看一下例子:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
torch.manual_seed(1)
lstm = nn.LSTM(3, 3) # 输入单词用一个维度为3的向量表示, 隐藏层的一个维度3,仅有一层的神经元,
#记住就是神经元,这个时候神经层的详细结构还没确定,仅仅是说这个网络可以接受[seq_len,batch_size,3]的数据输入
print(lstm.all_weights)
inputs = [torch.randn(1, 3) for _ in range(5)]
# 构造一个由5个单单词组成的句子 构造出来的形状是 [5,1,3]也就是明确告诉网络结构我一个句子由5个单词组成,
#每个单词由一个1X3的向量组成,就是这个样子[1,2,3]
#同时确定了网络结构,每个批次只输入一个句子,其中第二维的batch_size很容易迷惑人
#对整个这层来说,是一个批次输入多少个句子,具体但每个神经元,就是一次性喂给神经元多少个单词。
print('Inputs:',inputs)
# 初始化隐藏状态
hidden = (torch.randn(1, 1, 3),
torch.randn(1, 1, 3))
print('Hidden:',hidden)
for i in inputs:
# 将序列的元素逐个输入到LSTM,这里的View是把输入放到第三维,看起来有点古怪,
#回头看看上面的关于LSTM输入的描述,这是固定的格式,以后无论你什么形式的数据,
#都必须放到这个维度。就是在原Tensor的基础之上增加一个序列维和MiniBatch维,
#这里可能还会有迷惑,前面的1是什么意思啊,就是一次把这个输入处理完,
#在输入的过程中不会输出中间结果,这里注意输入的数据的形状一定要和LSTM定义的输入形状一致。
# 经过每步操作,hidden 的值包含了隐藏状态的信息
out, hidden = lstm(i.view(1, 1, -1), hidden)
print('out1:',out)
print('hidden2:',hidden)
# 另外, 我们还可以一次对整个序列进行训练. LSTM 返回的第一个值表示所有时刻的隐状态值,
# 第二个值表示最近的隐状态值 (因此下面的 "out"的最后一个值和 "hidden" 的值是一样的).
# 之所以这样设计, 是为了通过 "out" 的值来获取所有的隐状态值, 而用 "hidden" 的值来
# 进行序列的反向传播运算, 具体方式就是将它作为参数传入后面的 LSTM 网络.
# 增加额外的第二个维度
inputs = torch.cat(inputs).view(len(inputs), 1, -1)
hidden = (torch.randn(1, 1, 3), torch.randn(1, 1, 3)) # clean out hidden state
out, hidden = lstm(inputs, hidden)
print('out2',out)
print('hidden3',hidden)
运行输出:
out2 tensor([[[-0.0187, 0.1713, -0.2944]],
[[-0.3521, 0.1026, -0.2971]],
[[-0.3191, 0.0781, -0.1957]],
[[-0.1634, 0.0941, -0.1637]],
[[-0.3368, 0.0959, -0.0538]]])
hidden3 (tensor([[[-0.3368, 0.0959, -0.0538]]]), tensor([[[-0.9825, 0.4715, -0.0633]]]))
接下来我们要做一件事,
就是训练网络帮我我们标注词性,当然实际的自然语言处理我们有很多成功的算法,但是应对新词总会有点麻烦,我们想啊,既然神经网络可以帮我们做了很多神奇的事,那么我们可不可以训练一个网路模型来帮我我们自动的标注词性呢,显然这个思路靠谱,使用神经网络的套路:
- 准备训练数据,这一步最是头大的,最好的办法就是找各大机构提供的标准的标注库,实在找不到,自己处理,国内外很多的分词标准库和工具可以用,jieba分词标注是一个不错的选择,使用起来也简单。
- 读取数据文件
- 分词
- 把词语和标注分别放在两个数组里面
- 构建词汇表、构建标注表
- 把分词结果转换成对应词汇表和标签表中的序号。
- 构建网络模型,这里使用Word2Vec预处理一下输入文本
- 训练网络
- 分析结果
下面按照这个套路上源码:
import jieba.posseg
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
# import sys
import gensim
torch.manual_seed(2)
# sys.stdout = open('1.log', 'a')
sent='明天是荣耀运营十周年纪念日。' \
'荣耀从两周年纪念日开始,' \
'在每年的纪念日这天凌晨零点会开放一个新区。' \
'第十版账号卡的销售从三个月前就已经开始。' \
'在老区玩的不顺心的老玩家、准备进入荣耀的新手,都已经准备好了新区账号对这个日子翘首以盼。' \
'陈果坐到了叶修旁边的机器,随手登录了她的逐烟霞。' \
'其他九大区的玩家人气并没有因为第十区的新开而降低多少,' \
'越老的区越是如此,实在是因为荣耀的一个账号想经营起来并不容易。' \
'陈果的逐烟霞用了五年时间才在普通玩家中算是翘楚,哪舍得轻易抛弃。' \
'更何况到最后大家都会冲着十大区的共同地图神之领域去。'
words=jieba.posseg.cut(sent,HMM=True) #分词
processword=[]
tagword=[]
for w in words:
processword.append(w.word)
tagword.append(w.flag)
#词语和对应的词性做一一对应
texts=[(processword,tagword)]
#使用gensim构建本例的词汇表
id2word=gensim.corpora.Dictionary([texts[0][0]])
#每个词分配一个独特的ID
word2id=id2word.token2id
#使用gensim构建本例的词性表
id2tag=gensim.corpora.Dictionary([texts[0][1]])
#为每个词性分配ID
tag2id=id2tag.token2id
def sen2id(inputs):
return [word2id[word] for word in inputs]
def tags2id(inputs):
return [tag2id[word] for word in inputs]
#根据词汇表把文本输入转换成对应的词汇表的序号张量
def formart_input(inputs):
return torch.tensor(sen2id(inputs),dtype=torch.long)
#根据词性表把文本标注输入转换成对应的词汇标注的张量
def formart_tag(inputs):
return torch.tensor(tags2id(inputs),dtype=torch.long)
#定义网络结构
class LSTMTagger(torch.nn.Module):
def __init__(self,embedding_dim,hidden_dim,voacb_size,target_size):
super(LSTMTagger,self).__init__()
self.embedding_dim=embedding_dim
self.hidden_dim=hidden_dim
self.voacb_size=voacb_size
self.target_size=target_size
# 使用Word2Vec预处理一下输入文本
self.embedding=nn.Embedding(self.voacb_size,self.embedding_dim)
# LSTM 以 word_embeddings 作为输入, 输出维度为 hidden_dim 的隐状态值
self.lstm=nn.LSTM(self.embedding_dim,self.hidden_dim)
## 线性层将隐状态空间映射到标注空间
self.out2tag=nn.Linear(self.hidden_dim,self.target_size)
self.hidden = self.init_hidden()
def init_hidden(self):
# 开始时刻, 没有隐状态
# 关于维度设置的详情,请参考 Pytorch 文档
# 各个维度的含义是 (Seguence, minibatch_size, hidden_dim)
return (torch.zeros(1, 1, self.hidden_dim),
torch.zeros(1, 1, self.hidden_dim))
def forward(self,inputs):
# 预处理文本转成稠密向量
embeds=self.embedding((inputs))
#根据文本的稠密向量训练网络
out,self.hidden=self.lstm(embeds.view(len(inputs),1,-1),self.hidden)
#做出预测
tag_space=self.out2tag(out.view(len(inputs),-1))
tags=F.log_softmax(tag_space,dim=1)
return tags
model=LSTMTagger(10,10,len(word2id),len(tag2id))
loss_function=nn.NLLLoss()
optimizer=optim.SGD(model.parameters(),lr=0.1)
#看看随机初始化网络的分析结果
with torch.no_grad():
input_s=formart_input(texts[0][0])
print(input_s)
print(processword)
tag_s=model(input_s)
for i in range(tag_s.shape[0]):
print(tag_s[i])
# print(tag_s)
for epoch in range(300):
# 再说明下, 实际情况下你不会训练300个周期, 此例中我们只是构造了一些假数据
for p ,t in texts:
# Step 1. 请记住 Pytorch 会累加梯度
# 每次训练前需要清空梯度值
model.zero_grad()
# 此外还需要清空 LSTM 的隐状态
# 将其从上个实例的历史中分离出来
# 重新初始化隐藏层数据,避免受之前运行代码的干扰,如果不重新初始化,会有报错。
model.hidden = model.init_hidden()
# Step 2. 准备网络输入, 将其变为词索引的Tensor 类型数据
sentence_in=formart_input(p)
tags_in=formart_tag(t)
# Step 3. 前向传播
tag_s=model(sentence_in)
# Step 4. 计算损失和梯度值, 通过调用 optimizer.step() 来更新梯度
loss=loss_function(tag_s,tags_in)
loss.backward()
print('Loss:',loss.item())
optimizer.step()
#看看训练后的结果
with torch.no_grad():
input_s=formart_input(texts[0][0])
tag_s=model(input_s)
for i in range(tag_s.shape[0]):
print(tag_s[i])
Pytorch的LSTM的理解的更多相关文章
- [PyTorch] rnn,lstm,gru中输入输出维度
本文中的RNN泛指LSTM,GRU等等 CNN中和RNN中batchSize的默认位置是不同的. CNN中:batchsize的位置是position 0. RNN中:batchsize的位置是pos ...
- pytorch nn.LSTM()参数详解
输入数据格式:input(seq_len, batch, input_size)h0(num_layers * num_directions, batch, hidden_size)c0(num_la ...
- 【pytorch】pytorch-backward()的理解
pytorch-backword函数的理解 函数:\(tensor.backward(params)\) 这个params的维度一定要和tensor的一致,因为tensor如果是一个向量y = [y1 ...
- pytorch之LSTM
from:http://pytorch-cn.readthedocs.io/zh/latest/package_references/torch-nn/#recurrent-layers class ...
- Pytorch的gather用法理解
先放一张表,可以看成是二维数组 行(列)索引 索引0 索引1 索引2 索引3 索引0 0 1 2 3 索引1 4 5 6 7 索引2 8 9 10 11 索引3 12 13 14 15 看一下下面例子 ...
- pytorch 中LSTM模型获取最后一层的输出结果,单向或双向
单向LSTM import torch.nn as nn import torch seq_len = 20 batch_size = 64 embedding_dim = 100 num_embed ...
- Pytorch 中张量的理解
张量是一棵树 长久以来,张量和其中维度的概念把我搞的晕头转向. 一维的张量是数组,二维的张量是矩阵,这也很有道理. 但是给一个二维张量,让我算出它每一行的和,应该用 sum(dim=0) 还是 sum ...
- 牛刀小试之用pytorch实现LSTM
https://www.itcodemonkey.com/article/9008.html 要看一看
- pytorch之张量的理解
张量==容器 张量是现代机器学习的基础,他的核心是一个容器,多数情况下,它包含数字,因此可以将它看成一个数字的水桶. 张量有很多中形式,首先让我们来看最基本的形式.从0维到5维的形式 0维张量/标量: ...
随机推荐
- java 日期合法
try { String date_str = "5555-22-33"; SimpleDateFormat format=new SimpleDateFormat("y ...
- Leetcode695.Max Area of Island岛屿的最大面积
给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合.你可以假设二维矩阵的四个边缘都被水包围着. 找到给定的二维数组中 ...
- 【水滴石穿】github_popular
项目不难,就是文件摆放位置跟别的不一样 https://github.com/chenji336/github_popular //定义入口是app.js ///** @format */ impor ...
- C++中数字转换成字符串
头文件:<string> 转换函数:to_string(); 例如:int n=10; string str=to_string(n) ;
- Apache Camel继承Spring Boot 实现文件远程复制和转移
pom.xml <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-f ...
- 面向视频的全新AI架构 —— 阿里云智能视觉技术全解
我们都知道,AI技术正在以可见的速度被应用于各行各业,然而绝大部分业务场景想应用AI技术,都需要算法工程师根据自身业务的标注数据,来进行单独训练,才能打磨出合适的AI模型.如此一来,如何以最低的门槛和 ...
- PLAY2.6-SCALA(三) 数据的返回与保存
1.修改默认的Content-Type 自动设置内容类型为text/plain val textResult = Ok("Hello World!") 自动设置内容类型为appli ...
- redis 如何查看版本
./redis-cli -h 127.0.0.1 info | grep 'redis_version' redis-server -v
- GCD使用经验与技巧浅谈
前言 GCD(Grand Central Dispatch)可以说是Mac.iOS开发中的一大“利器”,本文就总结一些有关使用GCD的经验与技巧. dispatch_once_t必须是全局或stati ...
- Cmakelists.txt中配置glew
在cmakelists.txt中添加: add_library(glew_static STATIC IMPORTED) set_target_properties(glew_static PROPE ...