小言_互联网的博客

【Pytorch项目实战】之自然语言处理:RNN、LSTM、GRU、Transformer

322人阅读  评论(0)

自然语言处理

算法一:循环神经网络(Recurrent Natural Network,RNN

RNN与CNN的区别?CNN的输入图像大小固定,而在语音识别中,每句话的长度都是不一样的,且一句话的前后也是有关系的。

应用:自然语言处理(Neuro-Linguistic Programming,NLP)、语音识别、机器翻译等时序问题中。
难题:网络结构的加深使得模型忘记了先前学习的信息,即只能实现短时记忆
原因
(1)RNN相邻时间步是连接在一起的,,故(以横向网络看)每个权重都会朝相同方向迭代更新。
(2)随着层数的增加,梯度小则越小,最后趋近于0(梯度消失);梯度大则越大,最后非常大(梯度爆炸)。
(3)最终导致网络无法训练,无法实现长时记忆。

RNN网络的结构图

RNN由输入层、隐含层、输出层组成。其中,U是输入x到隐含层的权重矩阵;W是状态s到隐含层的权重矩阵;V是隐含层到输出层o的权重矩阵。
RNN的参数共享:即各个时间节点x(t-1)、x(t)、x(t+1)对应的W、U、V权重矩阵都是不变的。
RNN的最大特点:隐层状态。可以捕获一个序列的信息。

隐含层的详细结构图

假设:输入x为n维向量,隐含层的神经元个数为m,输出层的神经元个数为r。则U=n*mW=m*mV=m*r。x(t)、a(t)、o(t)都是向量。

  • x(t)是时刻t的输入向量;例如:一句话。
  • a(t)是时刻t的隐层状态;可以捕获之前时刻发生的信息。
    • a(t)=f(U*x(t)+W*a(t-1))。其中,x(t)表示当前时刻的输入;a(t-1)表示前一时刻的状态;f是非线性函数(如:tanh、ReLU)。
  • o(t)是时刻t的输出向量;例如:想要预测句子的下一个词,它将会是词汇表中的概率向量。o(t)=softmax(W*a(t))

RNN既可以像CNN一样横向发展(增加时间步或序列长度),也可以纵向扩展为多层RNN。

为加深对RNN结构图的理解,举例说明。

详细过程如下图(RNN前向传播的计算过程)

上述案例的代码实现如下:

import numpy as np

X = [1,2]
state = [0.0, 0.0]
w_cell_state = np.asarray([[0.1, 0.2], [0.3, 0.4],[0.5, 0.6]])
b_cell = np.asarray([0.1, -0.1])
w_output = np.asarray([[1.0], [2.0]])
b_output = 0.1

for i in range(len(X)):
    state=np.append(state,X[i])
    before_activation = np.dot(state, w_cell_state) + b_cell
    state = np.tanh(before_activation)
    final_output = np.dot(state, w_output) + b_output
    print("状态值_%i: "%i, state)
    print("输出值_%i: "%i, final_output)

 

RNN的反向传播算法
称为随时间反向传播(Backpropagation Through Time,BPTT),原理与CNN的反向传播一样。区别:CNN按照层进行反向传播,而BPTT按照时间 t 进行反向传播。

算法二:长短时记忆神经网络(Long Short-Term Memory,LSTM)

时期:1997年提出,通过" 门 "的结构控制信息的增加或去除。
优点能够有效解决信息的短时记忆,避免梯度消失或爆炸。
缺点:模型不能并行学习,只能左右学习,导致面对大语料库(NLP)时训练效率非常低。
最大特点: 通过三个门控制对以往信息的取舍,即循环体结构

  • (1)遗忘门(Forget Gate):决定了上一时刻的单元状态c(t-1)有多少保留到当前时刻c(t);
  • (2)输入门(Input Gate):决定了当前时刻网络的输入x(t)有多少保存到单元状态c(t);
  • (3)输出门(Output Gate):控制单元状态c(t)有多少输出到LSTM的当前输出值h(t)。

算法三:门控循环单元神经网络(Gated Recurrent Unit,GRU)

背景:2014年提出,GRU是LSTM的变种。
优点:计算效率更高,占用内存相对较少。且在实际使用中,两者差异不大,故越来越流行。
主要两大改动

  • (1)将输入门、遗忘门、输出门变为两个门:更新门z(t)、重置门r(t)
  • (2)将单元状态与输出合并为一个状态h(t)。


GRU的结构图及公式

算法四:Transformer

2017 年6月,Google团队Ashish Vaswani等人在论文 Attention is All you need 中提出了 Transformer 模型。其使用 Self-Attention 结构取代了在 NLP 任务中常用的 RNN 的顺序网络结构,使得模型可以并行化训练,而且能够充分利用训练资料的全局信息,加入Transformer的Seq2seq模型在NLP的各个任务上都有了显著的提升。

核心机制:Self-Attention。Self-Attention机制的本质来自于人类视觉注意力机制。当人视觉在感知东西时候往往会更加关注某个场景中显著性的物体,为了合理利用有限的视觉信息处理资源,人需要选择视觉区域中的特定部分,然后集中关注它。
注意力机制的主要目:对输入进行注意力权重的分配,即决定需要关注输入的哪部分,并对其分配有限的信息处理资源给重要的部分。

Transformer 模型详解
Transformer详解(附代码)
详解Transformer中Self-Attention以及Multi-Head Attention

(一)实战:基于LSTM预测股票行情(数据集:Tushare数据包)

链接:https://pan.baidu.com/s/1hfmWHjDQxIHsR9xZNJuvYg?pwd=s1qc
提取码:s1qc

import pandas as pd
import matplotlib.pyplot as plt
import datetime
import torch
import torch.nn as nn
import numpy as np
from torch.utils.data import Dataset, DataLoader
import torchvision
import torchvision.transforms as transforms

import tushare as ts    # (1)pip install tushare(2)pip install pytdx
from pandas.plotting import register_matplotlib_converters

import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'     		# "OMP: Error #15: Initializing libiomp5md.dll"
#################################################################################
def generate_data_by_n_days(series, n, index=False):
	"""生成训练数据"""
    if len(series) <= n:
        raise Exception("The Length of series is %d, while affect by (n=%d)." % (len(series), n))
    df = pd.DataFrame()
    for i in range(n):
        df['c%d' % i] = series.tolist()[i:-(n - i)]
    df['y'] = series.tolist()[n:]
    if index:
        df.index = series.index[n:]
    return df


def readData(column='high', n=30, all_too=True, index=False, train_end=-500):
	"""读取训练集"""
    df = pd.read_csv("sh300.csv", index_col=0)		# 读取csv文件
    df.index = list(map(lambda x: datetime.datetime.strptime(x, "%Y-%m-%d"), df.index))		# 以日期为索引
    df_column = df[column].copy()		# 获取每天的最高价数据(column='high')
    # 拆分为训练集和测试集
    df_column_train, df_column_test = df_column[:train_end], df_column[train_end - n:]
    # 生成训练数据
    df_generate_train = generate_data_by_n_days(df_column_train, n, index=index)
    if all_too:
        return df_generate_train, df_column, df.index
    return df_generate_train


class LSTM(nn.Module):
	"""LSTM网络模型"""
    def __init__(self, input_size):
        super(LSTM, self).__init__()
        self.LSTM = nn.LSTM(input_size=input_size, hidden_size=64, num_layers=1, batch_first=True)
        self.out = nn.Sequential(nn.Linear(64, 1))

    def forward(self, x):
        r_out, (h_n, h_c) = self.LSTM(x, None)  	# None即隐层状态用0初始化
        out = self.out(r_out)
        return out


class mytrainset(Dataset):
	"""数据与标签分离"""
    def __init__(self, data):
        self.data, self.label = data[:, :-1].float(), data[:, -1].float()

    def __getitem__(self, index):
        return self.data[index], self.label[index]

    def __len__(self):
        return len(self.data)


#################################################################################
# (1)加载数据
cons = ts.get_apis()				# (API)建立连接
df = ts.bar('000300', conn=cons, asset='INDEX', start_date='2010-01-01', end_date='')
df = df.dropna()            		# 删除有null的行		# 若报错提示: no module dropna。则删除该行。
df.to_csv('sh300.csv')				# 将df保存到当前目录下

# 若本地已下载文件:'sh300.csv'。(忽略上述代码,改调用下述代码)
# df = pd.read_csv('sh300.csv')		# 读取csv文件
# print('文件表头', df.columns)		# 打印文件表头
# df_describe = df.describe			# 查看统计信息
# 获取沪深指数(000300)的信息,包括交易日期(datetime)、开盘价(open)、收盘价(close)、最高价(high)、最低价(low)、成交量(vol)、成交金额(amount)、涨跌幅(p_change)
#################################################################################
# (2)设置超参数
n = 30		# 30天数据
LR = 0.001
EPOCH = 200
batch_size = 20
train_end = -600
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
#################################################################################
# (3)装载数据
# 获取训练数据、最高价数据、最高价数据的索引(以日期为索引)
df, df_all, df_index = readData('high', n=n, train_end=train_end)
df_index = df_index.tolist()			# 最高价数据的索引转化为数组
df_all = np.array(df_all.tolist())		# 最高价数据转化为数组
df_numpy = np.array(df)					# 训练数据集转化为数组
# 归一化处理
df_numpy_mean = np.mean(df_numpy)
df_numpy_std = np.std(df_numpy)
df_numpy = (df_numpy - df_numpy_mean) / df_numpy_std
df_tensor = torch.Tensor(df_numpy)		# numpy转换为Tensor
trainset = mytrainset(df_tensor)		# 训练数据集的数据与标签拆分开
trainloader = DataLoader(trainset, batch_size=batch_size, shuffle=False)
################################################
# (4)训练模型
LSTM = LSTM(n).to(device)			# n为序列长度(即时间步长)
optimizer = torch.optim.Adam(LSTM.parameters(), lr=LR)		# 优化器(权重参数, 学习率)
loss_func = nn.MSELoss()			# 均方差损失函数
losses = []
for step in range(EPOCH):
	train_loss = 0
    for tx, ty in trainloader:
        tx = tx.to(device)
        ty = ty.to(device)
        # 在第1个维度上添加一个维度为1的维度,形状变为[batch,seq_len,input_size]
        output = LSTM(torch.unsqueeze(tx, dim=1)).to(device)
        loss = loss_func(torch.squeeze(output), ty)
        optimizer.zero_grad()		# 梯度清零
        loss.backward()				# 随时间反向传播
        optimizer.step()			# 梯度更新
        # 记录误差
        train_loss += loss.item()
    losses.append(train_loss/len(trainloader))
#################################################################################
# (5)测试模型
generate_data_train = []
generate_data_test = []
test_index = len(df_all) + train_end
df_all_normal = (df_all - df_numpy_mean) / df_numpy_std
df_all_normal_tensor = torch.Tensor(df_all_normal)
for i in range(n, len(df_all)):
    x = df_all_normal_tensor[i - n:i].to(device)
    # LSTM的输入必须是3维,故需添加两个1维的维度,最后成为[1,1,input_size]
    x = torch.unsqueeze(torch.unsqueeze(x, dim=0), dim=0)
    y = LSTM(x).to(device)
    if i < test_index:
        generate_data_train.append(torch.squeeze(y).detach().cpu().numpy() * df_numpy_std + df_numpy_mean)
    else:
        generate_data_test.append(torch.squeeze(y).detach().cpu().numpy() * df_numpy_std + df_numpy_mean)
#################################################################################
# (6)画图
plt.subplot(221), plt.plot(df_index, df_all), plt.title('high data')

plt.subplot(222), plt.plot(np.arange(len(losses)), losses), plt.xlabel('EPOCH'), plt.ylabel('Loss'), plt.title('train loss') 

plt.subplot(223) 
plt.plot(df_index[n:train_end], generate_data_train, label='generate_train')
plt.plot(df_index[train_end:], generate_data_test, label='generate_test')
plt.plot(df_index[train_end:], df_all[train_end:], label='real-data')
plt.legend()

plt.subplot(224)
plt.plot(df_index[train_end:-500], generate_data_test[-600:-500], label='test_data')
plt.plot(df_index[train_end:-500], df_all[train_end:-500], label='real_data')
plt.legend(), plt.title('predict results')
plt.show()


 

转载:https://blog.csdn.net/shinuone/article/details/128663890
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场