深度学习pytorch——RNN从表示到原理、应用、发展、实战详细讲解(终结)

时间序列的表示

题外话:在自然界中,我们接触到的信号都是模拟信号,在手机、电脑等电子设备中存储的信号是数字信号。我们通常对模拟信号进行等间隔取样得到数字信号,这个数字信号也被称为序列。将模拟信号转换为数据信号的过程称为数字信号处理,现已成为一门独立的学科,其分支数字图像处理在人工智能领域发挥着重要的作用。

序列的规范化表示方法:

[ seq_len, feature_len ] 用来表示序列,其中seq_len为序列的长度、feature_len为表示序列中每个元素的维度。序列可以是数字化后的语音信号、可以是数字化后的图片、也可以是一句话等等。

GloVe编码方式的引入:

我们都知道pytorch中是不存在字符串类型的,但是文字信息又是我们长处理的数据,我们如何表示这种数据,答案是编码。其中最简单的编码方式one-hot,我们在处理手写数字识别的时候用的就是这种编码方式,建立一个10维的向量,每个位置表示一个数字,如果是这个数字则相应位置为1,否则为0。

0 :[ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]

1 :[ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 ]

……

9 :[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ]

将相应的数字换为单词就可以表达每个单词。

one-hot的编码方式存在稀疏高维特点。稀疏:每一个向量只能用来表达一个单词;高维:一个向量的维度等于这个句子的单词数量,你也许感觉还可以,如果是一篇英语文章呢?这种编码方式代表了巨大的数据量

由于one-hot的高度不实用性,GloVe编码方式随之诞生。GloVe表示了单词的相似性,具体怎么表示的我们看如下图:

左边的表示了与king的相似性,右边的表示了与queen的相似性。我们可以看到每个单词的后面都跟着一个数值,这个数值表示了与值对应单词的相似度,就比如kings后面的数值0.897245,便是了kings与king的形似度为0.897245。这个相似度是如何计算的呢?

使用两个单词之间的cos值表示,将每个单词看作一个向量,然后计算两向量之间的cos<w1, w2>如果你还不是很明白,就去看高中数学吧,或者看下图:

不知道这张图是否唤起了你沉睡的记忆?

接着介绍在序列在我们今天的主题RNN中是如何表示的,RNN里面提供了两种方法来表示序列:

第一种:[word num, b, word vec],其中word num表示单词的数量,b表示句子的数量,word vec表示单词的维度。

第二种:[b, word num, word vec],其中b表示句子的数量,word num表示单词的数量,word vec表示单词的维度。

这两种表示方法中的每个维度含义都是相同的,只是两者的顺序发生了改变,可以根据理解图片向量的含义来理解整体的含义。

GloVe实例:

import torch
import torchnlp  # 自然语言处理模块,如果没有torchnlp 模块,用pip install pytorch-nlp 安装
import torch.nn as nn
from torchnlp import word_to_vector
from torchnlp.word_to_vector import GloVe

################ nn.Embedding ################

word_to_ix = {"hello": 0, "world": 1}  # 编一个索引号
# 查表 Word vector lookup table,已经提前编好存储在pytorch中
lookup_tensor = torch.tensor([word_to_ix["hello"]], dtype=torch.long)  # 得到一个索引idx
embeds = nn.Embedding(2, 5)  # 实例化一个词向量表,其中2表示:一个有2个单词,5表示每个单词的词向量的维度或长度。
hello_embeds = embeds(lookup_tensor)  # 根据索引查表,得到索引对应的feature,也就是word vector 的 representation
print(hello_embeds)

# tensor([[ 0.7305, -1.1242, -0.1810, -0.5458, -1.0004]],
#        grad_fn=<EmbeddingBackward>)

# 这里没有初始化,nn.embedding这张表是随机生成的,得到的vector,非常random。
# 且通过查表方式得到的vector没有办法算梯度,不能优化。
# 怎样初始化,使用现成的 word2vec 或者GloVe

##########  word2vec / GloVe ##########
# vectors=GloVe()     # 查表操作,下载太慢了
vectors = GloVe('6B')  # 840条词向量,每个词向量的维度是300个维度

vector = vectors["hello"]
print(vector.shape)
# torch.Size([300])
print(vector)

什么是RNN?

RNN全称为Recurrent Neural Network,中文名字“循环神经网络”。具体是什么,往下看:

当我们去做一个情感分析的时候,会尝试去评价一个句子是积极的还是消极的,先让我们以CNN的角度来看:

现在我们要分析I hate this boring movies这句话是消极的,还是积极的,我们将此句话使用一个[5,  100]的序列来表示,其中5表示一共有5个单词,100表示每个单词使用100个维度来表示。我们将每个单词都送进CNN,也就是将[100]进行线性运算,但是每个单词的weight和bias都是不一样的,这就意味着CNN是将每个单词单个处理的,丧失了语义,并且每个单词都会拥有2个参数,这意味着巨大的参数量。我们希望可以减少参数,并且将所有的单词联系起来,放在一个语境下去分析,由此权值共享的概念和长期记忆的概念被提出。

权值共享,简单明了,让所有的单词都共享同意对参数,如下图所示:

长期记忆,有一个单元来保存所有的信息,将每个单词串联起来,放在同一个语境下:

这个被用来保存所有信息为h0,每个单元的输出为h,请注意看红色箭头的指向,具体红色箭头指哪里由你自己决定。

一般我们将h0初始化为[0, 0, 0, ...],每次输入一个单词,比如序列为[5, 3, 100],送入的就是      [3,  100]。

这个模型就被称为RNN。

RNN背后的数学原理:

 RNN的使用

前提:再来看这张图,我先解释一下这个图中每个数据的特征,其中整体的数据x:[seq len, batch, feature len],其中seq len表示单词的个数、batch表示句子的个数、feature len表示一个单词使用feature len个维度来表达;一个单词xt:[batch, feature len];Wxh:[hidden len, feature len]其中hidden len表示记忆单元的维度;ht:[batch, hidden len];Whh:[hidden len, hidden len]。可以使用来表示紫色框中的公式,最后的结果就是[ batch, hidden len]。

代码实例,可以直接运行试试看,也可以改变一下feature len和hidden len:

from torch import nn

rnn = nn.RNN(100, 10)  # RNN(feature len, hidden len)
print(rnn._parameters.keys())
# odict_keys(['weight_ih_l0', 'weight_hh_l0', 'bias_ih_l0', 'bias_hh_l0'])
print(rnn.weight_hh_l0.shape, rnn.weight_ih_l0.shape)
# torch.Size([10, 10]) torch.Size([10, 100])
print(rnn.bias_hh_l0.shape, rnn.bias_ih_l0.shape)
# torch.Size([10]) torch.Size([10])

在进行下面实例之前先明确两个概念:

out 是最后输出的集合,表示为[h0,h1,…,hi]的组合叠加
h 则是每一层最后隐藏单元的集合

一层RNN:

import torch
from torch import nn

# [input dim(feature dim/word vector),hidden dim,num layers(default Settings is 1)]
    rnn = nn.RNN(input_size=100, hidden_size=20, num_layers=1)
    print(rnn)
    # RNN(100, 20)

    # x:[seq len, batch, word vector]
    x = torch.randn(10, 3, 100)
    # out:[seq len,batch,hidden dim] [10,3,20]
    # h0/ht:[num layers,batch,hidden dim] [1,3,20]
    out, h = rnn(x, torch.zeros(1, 3, 20))
    print(out.shape, h.shape)
    # torch.Size([10, 3, 20]) torch.Size([1, 3, 20])

一层RNN-Cell:

import torch
from torch import nn

# 1-layer
cell1 = nn.RNNCell(100, 20)  # [word vec, h_dim]
h1 = torch.zeros(3, 20)  # [b,h_dim]
for xt in x:    # xt:每个数据,x:所有数据
    h1 = cell1(xt, h1)
print(h1.shape)
# torch.Size([3, 20])

多层:

先来看一下处理后维度的变化: 

from torch import nn

rnn = nn.RNN(100, 10, num_layers=2)  # RNN(feature len, hidden len, num_layers)
print(rnn._parameters.keys())
# odict_keys(['weight_ih_l0', 'weight_hh_l0', 'bias_ih_l0', 'bias_hh_l0', 'weight_ih_l1', 'weight_hh_l1', 'bias_ih_l1', 'bias_hh_l1'])
print(rnn.weight_hh_l0.shape, rnn.weight_ih_l0.shape)
# torch.Size([10, 10]) torch.Size([10, 100])
print(rnn.weight_hh_l1.shape, rnn.weight_ih_l1.shape)
# torch.Size([10, 10]) torch.Size([10, 10])

 多层RNN:

import torch
from torch import nn

rnn = nn.RNN(input_size=100, hidden_size=20, num_layers=4)
print(rnn)
# RNN(100, 20, num_layers=4)

x = torch.randn(10, 3, 100)
# out:[seq len,batch,hidden dim] [10,3,20]
# h0/ht:[num layers,batch,hidden dim] [4,3,20]
out, h = rnn(x, torch.zeros(4, 3, 20))  # rnn(所有的数据, h0)
# out, h = rnn(x)  #与上一行等价,这里没有传h0进来
print(out.shape, h.shape)
# torch.Size([10, 3, 20]) torch.Size([4, 3, 20])

多层RNN-Cell:

import torch
from torch import nn

cell1 = nn.RNNCell(100, 30)  # [word vec,h_dim],100 dim reduce to 30
cell2 = nn.RNNCell(30, 20)  # [word vec,h_dim],30 dim reduce to 20
h1 = torch.zeros(3, 30)
h2 = torch.zeros(3, 20)
for xt in x:
    h1 = cell1(xt, h1)    # 本层的输出h1作为了下一层的输入
    h2 = cell2(h1, h2)
print(h2.shape)
# torch.Size([3, 20])

时间序列的预测

############# 正弦函数预测(1 layer RNN) #############
# (1)构建RNN网络
# (2)training:构建优化器,迭代,加载正弦函数数据,定义loss,输出
# (3)testing
# (4)画图


import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from matplotlib import pyplot as plt

num_time_steps = 50
input_size = 1
hidden_size = 16
output_size = 1
lr = 0.01

########构建RNN网络#######

class Net(nn.Module):

    def __init__(self, ):
        super(Net, self).__init__()

        self.rnn = nn.RNN(
            input_size=input_size,
            hidden_size=hidden_size,
            num_layers=1,
            batch_first=True,  # [batch,seq len ,word vector/feature dim]
        )

        # 正态分布的权值初始化
        for p in self.rnn.parameters():  # parameters:包含所有模型的迭代器.
            nn.init.normal_(p, mean=0.0, std=0.001)  # normal_:正态分布

        # 连接层,对数据做处理 [49,16]→[49,1]
        self.linear = nn.Linear(hidden_size, output_size)  # 对输入数据做线性变换y=Ax+b

    # 前向传播函数
    def forward(self, x, hidden_prev):
        out, hidden_prev = self.rnn(x, hidden_prev)  # 前向网络参数:输入,h0

        # out:[b, seq, h],torch.Size([1, 49, 16])
        # ht:[b,num layers,hidden dim],torch.Size([1, 1, 16])
        out = out.view(-1, hidden_size)  # [1, seq, h]=>[seq, h]  torch.Size([49, 16])
        out = self.linear(out)  # torch.Size([49, 1]), [seq, h] => [seq, 1]
        out = out.unsqueeze(dim=0)  # torch.Size([1, 49, 1]),要个 y做一个MSE,所以增加的维度要一致 [seq, 1] => [1, seq, 1]
        return out, hidden_prev  # torch.Size([1, 49, 1]),torch.Size([1, 1, 16])

########## 开始训练 ##############

model = Net()
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr)

hidden_prev = torch.zeros(1, 1, hidden_size)  # h0:[b,num layers,hidden dim],[1,1,16]

for iter in range(6000):
    # np.random.randint(low, high=None, size=None)
    # 作用返回一个随机整型数,范围从低(包括)到高(不包括),即[low, high)。如果没有写参数high的值,则返回[0,low)的值。
    # 65-74行慵懒生成数据
    start = np.random.randint(3, size=1)[0]  # 返回[0,3)的随机整型数,size=1表示维度为1,[0]取第0维的数
    # print(start)  # scalar

    time_steps = np.linspace(start, start + 10, num_time_steps)  # 等分数列,生成[start,start+10] 区间的50个等分数列
    # print(time_steps)  #(50,)
    data = np.sin(time_steps)  # 取正弦函数  (50,)
    data = data.reshape(num_time_steps, 1)  # (50, 1)
    x = torch.tensor(data[:-1]).float().view(1, num_time_steps - 1, 1)  # 第0-48的数字 x:(1,49,1)
    y = torch.tensor(data[1:]).float().view(1, num_time_steps - 1, 1)  # 第1-49的数字 y:(1,49,1)
    # 相当于用第0-48的数字预测第1-49的数字,往后预测1位

    # 正式开始训练
    output, hidden_prev = model(x, hidden_prev)  # 将x和h0送进模型,得到output和ht

    # tensor.detach()
    # 返回一个新的tensor,从当前计算图中分离下来的,但是仍指向原变量的存放位置,
    # 不同之处只是requires_grad为false,得到的这个tensor永远不需要计算其梯度,不具有grad
    hidden_prev = hidden_prev.detach()

    loss = criterion(output, y)  # compute MSE of output and y
    model.zero_grad()
    loss.backward()


    optimizer.step()

    ########### 进行测试 #########

    if iter % 100 == 0:  # 每100次输出结果
        print("Iteration: {} loss {}".format(iter, loss.item()))

# 数据定义送进来
start = np.random.randint(3, size=1)[0]
time_steps = np.linspace(start, start + 10, num_time_steps)
data = np.sin(time_steps)
data = data.reshape(num_time_steps, 1)
x = torch.tensor(data[:-1]).float().view(1, num_time_steps - 1, 1)  # 数据
y = torch.tensor(data[1:]).float().view(1, num_time_steps - 1, 1)   # 预测值

# 测试
predictions = []  # 先把预测的东西做一个空的数组
input = x[:, 0, :]  # x从[1,49,1]=>[1,1]
for _ in range(x.shape[1]):  # '_' 是一个循环标志
    input = input.view(1, 1, 1)  # x从[1,1]=>[1,1,1],即把seq变为1,曲线一个点一个点的动
    (pred, hidden_prev) = model(input, hidden_prev)
    input = pred  # 接收刚才输出的pred,再次送入网络,seq为1,就可以实现一个点一个点的预测下去了。
    predictions.append(pred.detach().numpy().ravel()[0])  # append 用于在列表末尾添加新的对象, ravel展平, [0]取数值
    # 也就是说,将得到的预测点一个一个展示出来

# 画图
x = x.data.numpy().ravel()  # 展平
y = y.data.numpy()
plt.scatter(time_steps[:-1], x.ravel(), s=90)  # 原始X散点图 0-48, x,size=90
plt.plot(time_steps[:-1], x.ravel())  # 原始X曲线图 0-48, x

plt.scatter(time_steps[1:], predictions)  # 预测曲线图 1-49, predictions,default size=20
plt.show()

预测结果:

RNN训练难题

从CNN模型的经验,RNN如果进行简单的叠加也会导致很差的结果,这个很差的结果主要有两种,梯度爆炸(Gradient Exploding)梯度离散(Gradient Vanishing)。为什么会导致这种原因的发生?我们一起来看一下RNN的梯度推导公式:

如果还不明白,看下面,从小到大的心灵鸡汤: 

梯度爆炸解决:

将w.grad限制在一个范围内,就是我们给定一个预值threshold then,如果梯度超过预值了我们就对梯度进行一个缩放,缩放规则为 w.grad = threshold / ||w.grad|| * w.grad。具体的代码表现形式:

loss = criterion(output, y)
model.zero_grad()
loss.backward()
for p in model.parameters():
    print(p.grad.norm())  #打印出现在的W.grad,经验来讲,如果大于等于50就有可能梯度爆炸,如果等于10左右,就ok
    torch.nn.utils.clip_grad_norm_(p, 10)  #采用clip 将梯度降成<10
optimizer.step()

梯度离散解决——LSTM:

解决梯度离散的方法就是LSTM,但是为什么叫做LSTM?LSTM是 long-short-term-memory 的简称,下面来介绍一下具体为什么要叫 long-short-term-memory ,进行RNN中的时候我们定义了一个h0,用来贯穿语境,想象很美好,现实很骨感,没错h0只能记住最近的几个,前面的早忘到十万八千里了,这种现象称作 short-term-memory(短期记忆)。而让它脑子变得好使的就加了一个long,即 long-short-term-memory

先来回顾一下 short-term-memory

我们完全可以将上述模型换成如下形式:

 再来看一下LSTM,观察一下两者的区别:

蓝色对应公式:

,其中Wf表示决定记住多少。

以下两个公式表示绿色的部分,这一步决定了 我们将要存储多少信息在细胞门里面。

 Wi表示获取多少现在的信息。

橙色对应公式,更新hidden里面的值:

第一个框:通过忘记之前决定要忘记的Ft乘于旧的Ct-1。

第二个框:决定更新多少的值it乘于新的状态值Ct(上面有个~)。

紫色的对应输出,这一步我们需要决定我们想要输出什么:

我们要输出的部分:

最终我们要输出的部分:

总的来说就是:

表达成公式就是如下样子:

 极值条件下(这个很像真值表,如果你学过《离散数学》或《数字电路与逻辑设计》的话,应该知道):

输入门输出门表现行为
01记住先前的值
11加入先前的值
00清除值
10覆盖原来的值

但是这种计算方法怎么解决了梯度离散的现象呢?让我们从数学公式的角度来分析一下原因:

原来的累乘变成了累加,累加就有可能加正数和负数,这种形式的梯度很自然的就解决了梯度离散的问题。

LSTM的使用

由于LSTM如此强大,pytorch也提供了相关的类——nn.LSTM

nn.LSTM:

1、__init__

参数:input_size:feature len;hidden_size:h0的特征维度;num_layers:循环的层数,如果不填的话默认为1。

2、LSTM.forward()

参数:out, (ht, ct) = lstm(x, [ht_0, ct_0])

x: [seq, b , vec]; h/c: [num_layer, b, h]; out: [seq, b, h]

3、代码演示:

lstm = nn.LSTM(input_size=100, hidden_size=20, num_layers=4)
print(lstm)
# LSTM(100, 20, num_layers=4)
x = torch.randn(10, 3, 100)
out, (h, c) = lstm(x)
print(out.shape, h.shape, c.shape)  # out:[seq, b, h] h/c:[num_layer, b, h]
# torch.Size([10, 3, 20]) torch.Size([4, 3, 20]) torch.Size([4, 3, 20])

nn.LSTMCell:

1、__init__

参数:input_size:feature len;hidden_size:h0的特征维度;num_layers:循环的层数,如果不填的话默认为1。

2、LSTMCell.forward()

ht, ct = lstmcell(xt, [ht_1, ct_1]) 

xt: [b, word vec]

ht/ct: [b, h]

3、代码演示:

1-layer:

cell = nn.LSTMCell(input_size=100, hidden_size=20)
h = torch.zeros(3, 20)
c = torch.zeros(3, 20)
for xt in x:
    h, c = cell(xt, [h, c])
print(h.shape, c.shape)
# torch.Size([3, 20]) torch.Size([3, 20])

2-layer:

cell1 = nn.LSTMCell(input_size=100, hidden_size=30)
cell2 = nn.LSTMCell(input_size=30, hidden_size=20)
h1 = torch.zeros(3, 30)
c1 = torch.zeros(3, 30)
h2 = torch.zeros(3, 20)
c2 = torch.zeros(3, 20)
for xt in x:
    h1, c1 = cell1(xt, [h1, c1])
    h2, c2 = cell2(h1, [h2, c2])
print(h2.shape, c2.shape)
# torch.Size([3, 20]) torch.Size([3, 20])

情感分类实战

Google CoLab:

在Google Colab中你可以获得连续12小时的使用、获得免费的K80 for GPU,Google Colab一般用来学习机器学习,是一个非常好用的工具,前提是你必须可以科学上网,可以使用Google。


########### 情感分类实战(基于IMDB数据集)#############
# 1 加载数据:
# 1.1 分割训练集测试集
# 1.2 创建vocabulary
# 1.3 创建 iterations
# 2 定义模型(class RNN):
# 2.1 __init__: 定义模型
# 2.2 forward:前向传播
# 2.3 使用预训练过的embedding 来替换随机初始化
# 3 训练模型 :
# 3.1 首先定义优化器和损失函数
# 3.2 定义一个函数用于计算准确率  def binary_acc
# 3.3 进入训练模式
# 4 评估模型(test)
# 5 运行
# 6 预测输出样例



import torch
import torchtext  #(version:0.6.0)
from torch import nn, optim
from torchtext import data, datasets
import spacy  #(version:3.3.0)
# nlp=spacy.load('en_core_web_sm')


print('GPU:', torch.cuda.is_available())

# 为CPU设置随机种子
torch.manual_seed(123)



############ 1 加载数据 ###########
######### 1.1 分割训练集测试集 ###########

# 两个Field对象定义字段的处理方法(文本字段、标签字段)
#Spacy 3.x以上的版本:tokenizer_language='en_core_web_sm',否则默认是tokenizer_language='en'
#TEXT = data.Field(tokenize='spacy')
TEXT = data.Field(tokenize='spacy',tokenizer_language='en_core_web_sm') #文本
LABEL = data.LabelField(dtype=torch.float)  #标签
train_data, test_data = datasets.IMDB.splits(TEXT, LABEL)

print('len of train data:', len(train_data))  # 25000
print('len of test data:', len(test_data))    # 25000

# torchtext.data.Example : 用来表示一个样本,数据+标签
print(train_data.examples[15].text)  # 文本:句子的单词列表
print(train_data.examples[15].label) # 标签: 积极pos

#当我们把句子传进模型的时候,是按照一个个batch传进去的,而且每个batch中的句子必须是相同的长度。
#为了确保句子的长度相同,TorchText会把短的句子 pad到和最长的句子 等长。

########## 1.2 创建vocabulary ############

# vocabulary把每个单词一一映射到一个数字。
# 使用10k个单词来构建单词表(用max_size这个参数可以设定),所有其他的单词都用来表示。

# word2vec, glove
TEXT.build_vocab(train_data, max_size=10000, vectors='glove.6B.100d')
LABEL.build_vocab(train_data)

# 词典中应当有10002个单词,且有两个label,
# 可以通过TEXT.vocab和TEXT.label查询,
# 可以直接用stoi(stringtoint) 或者 itos(inttostring) 来查看单词表。

# print(len(TEXT.vocab))             # 10002
# print(TEXT.vocab.itos[:12])        # ['', '', 'the', ',', '.', 'and', 'a', 'of', 'to', 'is', 'in', 'I']
# print(TEXT.vocab.stoi['and'])      # 5
# print(LABEL.vocab.stoi)            # defaultdict(None, {'neg': 0, 'pos': 1})


############### 1.3 创建 iterations ############

# 每个iterator 迭代器 中各有两部分:词(.text)和标签(.label),其中 text 全部转换成数字了。
# BucketIterator会把长度差不多的句子放到同一个batch中,确保每个batch中不出现太多的padding。

batchsz = 30
device = torch.device('cuda:0')
train_iterator, test_iterator = data.BucketIterator.splits(
    (train_data, test_data),
    batch_size = batchsz,
    device=device
)

################### 2 定义模型 ##########################


#这里的RNN是一个通称,应该叫Network
class RNN(nn.Module):
    
    def __init__(self, vocab_size, embedding_dim, hidden_dim):
        super(RNN, self).__init__()
        
        # [0-10001] => [100]
        # 参数1:embedding个数(单词数), 参数2:embedding的维度(词向量维度)
        self.embedding = nn.Embedding(vocab_size, embedding_dim)  #[10002,100]
        # [100] => [256]
        # bidirectional=True 双向LSTM,所以下面FC层使用 hidden_dim*2
        self.rnn = nn.LSTM(embedding_dim, hidden_dim, num_layers=2,   #[100,256]
                           bidirectional=True, dropout=0.5)
        # [256*2] => [1]
        self.fc = nn.Linear(hidden_dim*2, 1)
        self.dropout = nn.Dropout(0.5)
        
        
    def forward(self, x):
        """
        x: [seq_len, b] vs [b, 3, 28, 28]
        """
        # [seq, b, 1] => [seq, b, 100]
        embedding = self.dropout(self.embedding(x))
        
        # output: [seq, b, hid_dim*2]
        # hidden/h: [num_layers*2, b, hid_dim]
        # cell/c: [num_layers*2, b, hid_di]
        output, (hidden, cell) = self.rnn(embedding)
        
        # [num_layers*2, b, hid_dim] => 2 of [b, hid_dim] => [b, hid_dim*2]
        # 双向LSTM,所以要把最后两个输出连接
        hidden = torch.cat([hidden[-2], hidden[-1]], dim=1)
        
        # [b, hid_dim*2] => [b, 1]
        hidden = self.dropout(hidden)
        out = self.fc(hidden)
        
        return out

####### 使用预训练过的embedding 来替换随机初始化  ###############


rnn = RNN(len(TEXT.vocab), 100, 256)  #词个数 10002 ,词嵌入维度 100 ,输出维度 256

#使用预训练过的embedding 来替换随机初始化
pretrained_embedding = TEXT.vocab.vectors
print('pretrained_embedding:', pretrained_embedding.shape) # torch.Size([10002, 100])
rnn.embedding.weight.data.copy_(pretrained_embedding)   # .copy_() 这种带着下划线的函数均代表替换inplace
print('embedding layer inited.')

######### 3 训练模型 ##########

######### 3.1首先定义优化器和损失函数  #########

optimizer = optim.Adam(rnn.parameters(), lr=1e-3)
criteon = nn.BCEWithLogitsLoss().to(device)  # BCEWithLogitsLoss是针对二分类的CrossEntropy
rnn.to(device)

# RNN(
#   (embedding): Embedding(10002, 100)
#   (rnn): LSTM(100, 256, num_layers=2, dropout=0.5, bidirectional=True)
#   (fc): Linear(in_features=512, out_features=1, bias=True)
#   (dropout): Dropout(p=0.5, inplace=False)
# )

######### 3.2 定义一个函数用于计算准确率  #########

import numpy as np

def binary_acc(preds, y):
    """
    get accuracy
    """
    preds = torch.round(torch.sigmoid(preds))
    correct = torch.eq(preds, y).float() #correct is a tensor
    acc = correct.sum() / len(correct)
    return acc

######### 3.3 进入训练模式  #########


def train(rnn, iterator, optimizer, criteon):
    
    avg_acc = []
    rnn.train()  # 表示进入训练模式
    
    for i, batch in enumerate(iterator):

        # batch.text 就是上面forward函数的参数text,压缩维度是为了和batch.label维度一致
        pred = rnn(batch.text).squeeze(1) # [seq, b] => [b, 1] => [b]
        loss = criteon(pred, batch.label)
        # 计算每个batch的准确率
        acc = binary_acc(pred, batch.label).item()
        avg_acc.append(acc)
        
        optimizer.zero_grad() # 清零梯度准备计算
        loss.backward() # 反向传播
        optimizer.step() # 更新训练参数
        
        if i%100 == 0:
            print(i, acc)
        
    avg_acc = np.array(avg_acc).mean()
    print('avg acc:', avg_acc)
    
################# 4 评估模型 ######################

#定义一个评估函数,和训练函数高度重合


def eval(rnn, iterator, criteon):
    
    avg_acc = []
    
    rnn.eval() # 表示进入测试模式
    
    with torch.no_grad():
        for batch in iterator:

            # [b, 1] => [b]
            pred = rnn(batch.text).squeeze(1)
            loss = criteon(pred, batch.label)
            acc = binary_acc(pred, batch.label).item()
            avg_acc.append(acc)
    avg_acc = np.array(avg_acc).mean()
    print('>>test:', avg_acc)

#############  5 运行 ###########

for epoch in range(10):
    train(rnn, train_iterator, optimizer, criteon)   # 训练模型  
    eval(rnn, test_iterator, criteon)  # 评估模型 


# 跑的时候发现模型过拟合了

############## 6 预测输出样例  ##########

# for batch in test_iterator:
#     # batch_size个预测
#     preds = rnn(batch.text).squeeze(1)
#     # preds = predice_test(preds) #?
#     print(preds)
#
#
#     i = 0
#     for text in batch.text:
#         # 遍历一句话里的每个单词
#         for word in text:
#             print(TEXT.vocab.itos[word], end=' ')
#
#         print('')
#         # 输出3句话
#         if i == 3:
#             break
#         i = i + 1
#
#     i = 0
#     for pred in preds:
#         idx = int(pred.item())
#         print(idx, LABEL.vocab.itos[idx])
#         # 输出3个结果(标签)
#         if i == 3:
#             break
#         i = i + 1
#     break

我是pytorch:2.1.2,torchtext:0.16.0+cpu,跑这段代码的时候会报错AttributeError: module 'torchtext.data' has no attribute 'Field',原因是因为torchtext0.12.0之后不存在Filed了,解决办法就是下载旧的版本,我是通过在命令行提示符执行

pip install torchtext==0.6.0

成功解决了问题。 

总结:

        至此我的pytorch已经学习完毕,在这个过程中我通过写博客来督促自己学习,通过输出的过程中我解决了在听课时遇到的难点,并且思考了如何清晰的表达我的语言,才可以让阅读我博客的人觉得pytorch是如此轻松,并且使我更加对pytorch的知识点更加清晰,也明白了自己的很多不足,并且严重的意识到实践才是巩固知识的唯一途径。

        在以后,我会着重于深度学习的应用,将所学知识用到AI安全方面,并且也会学习机器学习的知识以此加强对深度学习的理解。同样,为了加强我学习的动力,我将会继续采用blog记录的方式,将会开展AI-安全系列、机器学习系列,如果有对AI-安全感兴趣的小伙伴,请好好学习,然后发blog,也让我学习学习,我期待中国的AI-安全领域出现更多开放的学习资料,中国网络安全领域繁荣。

        最后的最后,请允许我将我这段时间阅读中记下的至理名言放在这里:

啊,在我们化作灰烬之前,

充分享用人生。

黄泉之下,

可没有美酒,

没有琴弦,

没有歌妓,

没有明天。 

谁说啊呦不可爱? 

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

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

相关文章

C# wpf 嵌入外部程序

WPF Hwnd窗口互操作系列 第一章 嵌入Hwnd窗口 第二章 嵌入WinForm控件 第三章 嵌入WPF控件 第四章 嵌入外部程序&#xff08;本章&#xff09; 第五章 底部嵌入HwndHost 文章目录 WPF Hwnd窗口互操作系列前言一、如何实现&#xff1f;1、定义属性2、进程嵌入&#xff08;1&…

Windows11配置VUE开发环境

目录 一、按照nodejs二、命令安装npm cache clean --forcenpm install -g vue/clinpm install npm -gnpm install webpacknpm install vue-cli -g与npm install -g vue/cli区别npm install -g cnpm --registryhttps://registry.npm.taobao.orgnpm i yarn -g --verbosenpm i -g …

全面解析十七种数据分析方法,具象数据分析思维

一、介绍 在当今数据驱动的商业环境中&#xff0c;数据分析已经成为了企业获取竞争优势的关键工具。无论是为了优化运营效率&#xff0c;提高客户满意度&#xff0c;还是推动产品创新&#xff0c;企业都需要通过分析大量数据来做出明智的决策。数据分析方法多种多样&#xff0c…

计算机网络-文件传输及IP协议——沐雨先生

实验内容 编写请求文件的客户Java应用程序编写响应文件请求的服务器Java应用程序利用Wireshark查看和分析IP包 基本要求 使用Java语言建立请求文件的客户应用程序使用Java语言建立响应文件请求的服务器应用程序了解IP协议的工作过程了解IP包首部各字段及含义 对Java应用程序…

机器学习——模型融合:平均法

机器学习——模型融合&#xff1a;平均法 在机器学习领域&#xff0c;模型融合是一种通过结合多个基本模型的预测结果来提高整体模型性能的技术。模型融合技术通常能够降低预测的方差&#xff0c;提高模型的鲁棒性&#xff0c;并在一定程度上提高预测的准确性。本文将重点介绍…

数据结构初阶:栈和队列

栈 栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。 栈中的数据元素遵守后进先出 LIFO &#xff08; Last In First Out &#xff09;的原则。…

腾讯云4核8G服务器12M带宽646元1年零3个月,4C8G使用场景说明

腾讯云4核8G服务器多少钱&#xff1f;腾讯云4核8G轻量应用服务器12M带宽租用价格646元15个月&#xff0c;活动页面 txybk.com/go/txy 活动链接打开如下图所示&#xff1a; 腾讯云4核8G服务器优惠价格 这台4核8G服务器是轻量应用服务器&#xff0c;详细配置为&#xff1a;轻量4核…

hive图形化客户端工具

hive准备 创建测试数据 以root用户登录&#xff0c;使用hive命令启动hive。 创建库 create database testhivedb; 创建表 create table testhivedb.testhivetable( id int ,name string ); 插入数据 insert into testhivedb.testhivetable values (1,cc); insert into…

《债务与国家的崛起》西方民主制度的金融起源 - 三余书屋 3ysw.net

债务与国家的崛起&#xff1a;西方民主制度的金融起源 你好&#xff0c;今天我们来聊聊由英国知名经济与金融历史学家詹姆斯麦克唐纳所著的《债务与国家的崛起》这本书。19世纪世界历史上发生了一次巨变&#xff0c;即“大分流”。当时西方通过工业革命实现了科技和经济的飞速…

为什么苹果 Mac 电脑需要使用清理软件?

尽管 Apple Mac 电脑因其卓越的性能、简洁高效的 macOS 操作系统及独特的美学设计备受全球用户青睐&#xff0c;但任何电子设备在长期使用后都难以避免面临系统资源日渐累积的问题。其中一个重要维护需求在于&#xff0c;随着使用时间的增长&#xff0c;Mac电脑可能会由于系统垃…

xilinx 7系列fpga上电配置

一、前言 Xilinx FPGA通过加载比特流到内部存储单元来进行配置。 Xilinx FPGA存在两种数据配置路径&#xff0c;一种是满足最小引脚需求的串行路径&#xff0c;一种是可用8位、16位或32位来连接到行业的高性能通用接口&#xff0c;如处理器&#xff0c;8位或者16位并行的闪存…

测试开发:从0到1学习如何测试API网关

日常工作中&#xff0c;难免会遇到临危受命的情况&#xff0c;虽然没有这么夸张&#xff0c;但是也可能会接到一个陌生的任务&#xff0c;也许只是对这个概念有所耳闻。也许这个时候会感到一丝的焦虑&#xff0c;生怕没法完成领导交给的测试任务。其实也没有必要那么紧张&#…

【微服务】面试题(一)

最近进行了一些面试&#xff0c;这几个问题分享给大家 一、分别介绍一下微服务、分布式以及两者的区别 微服务&#xff08;Microservices&#xff09;和分布式系统&#xff08;Distributed Systems&#xff09;是两种不同的软件架构风格&#xff0c;虽然它们之间有些重叠&#…

南京观海微电子---Vitis HLS设计流程(实例演示)——Vitis HLS教程

1. 前言 课时2我们介绍了Vitis HLS的设计流程&#xff0c;如下图所示&#xff1a; 算法或软件的设计和仿真都基于C/C&#xff0c;通过HLS平台导出打包好的IP RTL代码&#xff0c;最后将该打包的IP加入到主工程使用。 本课时&#xff0c;我们通过一个具体的实例&#xff0c;演示…

Kibana管理ES生命周期

希望通过Kibana界面管理ES索引的生命周期 版本&#xff1a;7.15.2 创建索引模板 创建索引模板方便匹配索引&#xff0c;对匹配到的一批索引采用同一套生命周期管理策略&#xff0c;例如开发环境的所有索引以dev-开头&#xff0c;可以创建样式为dev-*的索引模板以匹配开发环境…

【服务器uwsgi + flask + nginx的搭建】

目录 服务器uwsgi flask nginx的搭建1. 安装必要的软件2. 启动nginx服务3. 测试Nginx4. 配置uwsgi和flask5. 配置nginx 服务器uwsgi flask nginx的搭建 1. 安装必要的软件 安装Python、uWSGI、Flask 和 Nginx。 # Ubuntu 安装命令 sudo apt-get update sudo apt-get ins…

海外媒体宣发,穿透与世界的交流 - “保姆级”教程 - 大舍传媒

1. 引言 在当今高度信息化的世界&#xff0c;境外媒体宣发已经成为企业、品牌和政府机构推广自身形象、扩大影响力的重要手段。如何在国际舞台上有效传播信息&#xff0c;提高国际知名度&#xff0c;成为了许多组织面临的重要课题。大舍传媒凭借多年的境外媒体宣发经验&#x…

第六篇: 3.5 性能效果 (Performance)- IAB/MRC及《增强现实广告效果测量指南1.0》

​​​​​​​ 翻译计划 第一篇概述—IAB与MRC及《增强现实广告效果测量指南》之目录、适用范围及术语第二篇 广告效果测量定义和其他矩阵之- 3.1 广告印象&#xff08;AD Impression&#xff09;第三篇 广告效果测量定义和其他矩阵之- 3.2 可见性 &#xff08;Viewability…

Goingpub国自然基金-免费查询

可进行年份、学部、项目类别等检索&#xff0c;支持生成主题词汇总分析报告。 最最最关键&#xff0c;免费&#xff0c;只需要你注册登录一下&#xff0c;防止被爬虫侵扰。 界面简单&#xff0c;实用&#xff0c;支持模糊搜索&#xff0c;包含最新2023年数据&#xff0c;共56…

34-4 CSRF漏洞 - CSRF跨站点请求伪造

一、漏洞定义 CSRF(跨站请求伪造)是一种客户端攻击,又称为“一键式攻击”。该漏洞利用了Web应用程序与受害用户之间的信任关系,通过滥用同源策略,使受害者在不知情的情况下代表攻击者执行操作。与XSS攻击不同,XSS利用用户对特定网站的信任,而CSRF则利用了网站对用户网页…