循环神经网络RNN

举报
别团等shy哥发育 发表于 2023/01/09 09:13:39 2023/01/09
【摘要】 @toc 1、卷积神经网络与循环神经网络简单对比CNN:借助卷积核(kernel)提取特征后,送入后续网络(如全连接网络 Dense) 进行分类、目标检测等操作。CNN借助卷积核从空间维度提取信息,卷积核参数空间共享。RNN: 借助循环核(cell)提取特征后,送入后续网络(如全连接网络 Dense) 进行预测等操作。RNN 借助循环核从时间维度提取信息,循环核参数时间共享。 2、详解RNN...

@toc

1、卷积神经网络与循环神经网络简单对比

  • CNN:借助卷积核(kernel)提取特征后,送入后续网络(如全连接网络 Dense) 进行分类、目标检测等操作。CNN借助卷积核从空间维度提取信息,卷积核参数空间共享。
  • RNN: 借助循环核(cell)提取特征后,送入后续网络(如全连接网络 Dense) 进行预测等操作。RNN 借助循环核从时间维度提取信息,循环核参数时间共享。

2、详解RNN

2.1 循环核

  循环核:参数时间共享,循环层提取时间信息。
在这里插入图片描述

  循环核具有记忆力,通过不同时刻的参数共享,实现了对时间序列的信息提取。每个循环核有多个记忆体,对应上图中的多个小圆柱。记忆体内存储着每个时刻的状态信息 h t h_t ,这里 h t = t a n h ( x t w x h + h t 1 w h h + b h ) h_t=tanh(x_tw_{xh}+h_{t-1}w_{hh}+bh) 。其中 w x h w_{xh} w h h w_{hh} 为权重矩阵,bh为偏置, x t x_t 为当前时刻的输入特征, h t 1 h_{t-1} 为记忆体上一时刻存储的状态信息,tanh 为激活函数。
  当前时刻循环核的输出特征 y t = s o f t m a x ( h t w h y + b y ) y_t=softmax(h_tw_{hy}+by) ,其中 w h y w_{hy} 为权重矩阵、by为偏置、softmax为激活函数,其实就相当于一层全连接层。我们可以设定记忆体的个数从而改变记忆容量,当记忆体个数被指定、输入 x t x_t 输出 y t y_t 维度被指定,周围这些待训练参数的维度也就被限定了。
  ==在前向传播时,记忆体内存储的状态信息 h t h_t 在每个时刻都被刷新,而三个参数矩阵 w x h w_{xh} w h h w_{hh} w h y w_{hy} 和两个偏置项bh、by自始至终都是固定不变的。在反向传播时,三个参数矩阵和两个偏置项由梯度下降法更新。==
在这里插入图片描述

2.2 循环核按照时间步展开

在这里插入图片描述

  将循环核按时间步展开,就是把循环核按照时间轴方向展开,可以得到如上图的形式。每个时刻记忆体状态信息 h t h_t 被刷新,记忆体周围的参数矩阵和两个偏置项是固定不变的,我们训练优化的就是这些参数矩阵。训练完成后,使用效果最好的参数矩阵执行前向传播,然后输出预测结果。其实这和我们人类的预测是一致的:我们脑中的记忆体每个时刻都根据当前的输入而更新;当前的预测推理是根据我们以往的知识积累用固化下来的“参数矩阵”进行的推理判断。
  可以看出,循环神经网络就是借助循环核实现时间特征提取后把提取到的信息送入全连接网络,从而实现连续数据的预测。

2.3 循环计算层:向输出方向生长

在这里插入图片描述
  在 RNN 中,每个循环核构成一层循环计算层,循环计算层的层数是向输出方向增长的。如上图所示,左图的网络有一个循环核,构成了一层循环计算层;中图的网络有两个循环核,构成了两层循环计算层;右图的网络有三个循环核,构成了三层循环计算层。其中,三个网络中每个循环核中记忆体的个数可以根据我们的需求任意指定。

2.4 RNN训练

  得到 RNN 的前向传播结果之后,和其他神经网络类似,我们会定义损失函数,使用反向传播梯度下降算法训练模型。RNN 唯一的区别在于:由于它每个时刻的节点都可能有一个输出,所以 RNN 的总损失为所有时刻(或部分时刻)上的损失和。

2.5 Tensorflow2描述循环计算层

tf.keras.layers.SimpleRNN(
神经元个数,
activation=‘激活函数’,(如果不写,默认使用tanh)
return_sequences=是否每个时刻输出ℎ𝑡𝑡到下一层)

  (1)神经元个数:即循环核中记忆体的个数.
  (2) return_sequences:在输出序列中,返回最后时间步的输出值 h t h_t 还是返回全部时间步的输出。False 返回最后时刻,True 返回全部时刻。当下一层依然是 RNN 层,通常为 True,反之如果后面是 Dense 层,通常为 Fasle。
在这里插入图片描述
在这里插入图片描述
  (3)输入维度:==三维张量(输入样本数, 循环核时间展开步数, 每个时间步输入特征个数)==。
在这里插入图片描述
  如上图所示,左图一共要送入 RNN 层两组数据,每组数据经过一个时间步就会得到输出结果,每个时间步送入三个数值,则输入循环层的数据维度就是 RNN 层输入维度[2, 1, 3];
  右图输入只有一组数据,分四个时间步送入循环层,每个时间步送入两个数值 ,则输入循环层的数据维度就是 [1,4, 2]。
  (4)输出维度:当return_sequenc=True三维张量(输入样本数, 循环核时间展开步数,本层的神经元个数);当return_sequenc=False二维张量(输入样本数,本层的神经元个数)
  (5) activation:‘激活函数’(不写默认使用 tanh
例:SimpleRNN(3, return_sequences=True),定义了一个具有三个记忆体的循环核,这个循环核会在每个时间步输出 h t h_t

2.6 循环计算过程1pre1

  RNN 最典型的应用就是利用历史数据预测下一时刻将发生什么,即根据以前见过的历史规律做预测。举一个简单的字母预测例子体会一下循环网络的计算过程:输入一个字母预测下一个字母—输入 a 预测出 b、输入 b 预测出 c、输入 c预测出 d、输入 d 预测出 e、输入 e 预测出 a。计算机不认识字母,只能处理数字。所以需要我们对字母进行编码。这里假设使用独热编码(实际中可使用其他编码方式),编码结果如下图所示。
在这里插入图片描述
假设使用一层 RNN 网络,记忆体的个数选取 3,则字母预测的网络如下图所示。假设输入字母 b,即输入 x t x_t 为[0,1,0,0,0],这时上一时刻的记忆体状态信息 h t 1 h_{t-1} 为 0。由上文理论知识不难得到:

h t = t a n h ( x t w x h + h t 1 w h h + b ) = t a n h ( [ 2.3 0.8 1.1 ] + 0 + [ 0.5 0.3 0.2 ] ) = t a n h [ 1.8 1.1 0.9 ] = [ 0.9 0.8 0.7 ] \begin{aligned} h_t&=tanh(x_tw_{xh}+h_{t-1}w_{hh}+b)\\ &=tanh([−2.3 \quad0.8\quad 1.1 ] + 0 + [ 0.5\quad 0.3\quad − 0.2])\\ &=tanh[−1.8 \quad1.1\quad 0.9 ]\\ &= [−0.9 \quad0.8 \quad0.7] \end{aligned}

这个过程可以理解为脑中的记忆因为当前输入的事物而更新了。

输出 y t y_t 是把提取到的时间信息通过全连接进行识别预测的过程,是整个网络的输
出层。不难知道,

y t = s o f t m a x ( h t w h y + b y ) = s o f t m a x ( [ 0.7 0.6 2.9 0.7 0.8 ] + [ 0.0 0.1 0.4 0.7 0.1 ] ) = s o f t m a x ( [ 0.7 0.5 3.3 0.0 0.7 ] ) = [ 0.02 0.02 0.91 0.03 0.02 ] \begin{aligned} y_t&=softmax(h_tw_{hy+by})\\ &=softmax([−0.7\quad − 0.6\quad 2.9\quad 0.7\quad −0.8] + [ 0.0 \quad0.1\quad 0.4 \quad− 0.7\quad 0.1])\\ &=softmax([−0.7 \quad− 0.5 \quad3.3 \quad0.0 \quad− 0.7])\\ &=[0.02 \quad0.02\quad 0.91\quad 0.03\quad 0.02 ] \end{aligned}

可见模型认为有 91%的可能性输出字母 c,所以循环网络输出了预测结果 c。
在这里插入图片描述
  用RNN实现输入一个字母,预测下一个字母(One hot 编码)

2.6.1 独热编码实现

import numpy as np
import tensorflow as tf
from tensorflow.keras.layers import Dense, SimpleRNN
import matplotlib.pyplot as plt
import os

input_word = "abcde" # 用到的字母abcde
w_to_id = {
    'a': 0,
    'b': 1,
    'c': 2,
    'd': 3,
    'e': 4
}  # 单词映射到数值id的词典
# id编码为one-hot
id_to_onehot = {0: [1., 0., 0., 0., 0.],
                1: [0., 1., 0., 0., 0.],
                2: [0., 0., 1., 0., 0.],
                3: [0., 0., 0., 1., 0.],
                4: [0., 0., 0., 0., 1.]
                }
# 生成训练用的输入特征x_train和标签y_train
# 输入特征a对应的标签是b
# 输入特征b对应的标签是c
# 输入特征c对应的标签是d
# 输入特征d对应的标签是e
# 输入特征e对应的标签是a
x_train = [id_to_onehot[w_to_id['a']],
           id_to_onehot[w_to_id['b']],
           id_to_onehot[w_to_id['c']],
           id_to_onehot[w_to_id['d']],
           id_to_onehot[w_to_id['e']]]
y_train = [w_to_id['b'],
           w_to_id['c'],
           w_to_id['d'],
           w_to_id['e'],
           w_to_id['a']]

np.random.seed(7)
np.random.shuffle(x_train)
np.random.seed(7)
np.random.shuffle(y_train)
tf.random.set_seed(7)

# 使x_train符合SimpleRNN输入要求:[送入样本数, 循环核时间展开步数, 每个时间步输入特征个数]。
# 此处整个数据集送入,送入样本数为len(x_train);输入1个字母出结果,循环核时间展开步数为1; 表示为独热码有5个输入特征,每个时间步输入特征个数为5
x_train = np.reshape(x_train, (len(x_train), 1, 5))
y_train = np.array(y_train)

# 搭建具有3个记忆体的循环层(也可自行调整)
model = tf.keras.Sequential([
    SimpleRNN(3),
    Dense(5, activation='softmax')  # 全连接层:实现输出层yt的计算,要映射到独热码,找到输出概率最大的字母,所以这里是5
])

model.compile(optimizer=tf.keras.optimizers.Adam(0.01),
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['sparse_categorical_accuracy'])

checkpoint_save_path = "./checkpoint/rnn_onehot_1pre1.ckpt"

if os.path.exists(checkpoint_save_path + '.index'):
    print('-------------load the model-----------------')
    model.load_weights(checkpoint_save_path)

cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
                                                 save_weights_only=True,
                                                 save_best_only=True,
                                                 monitor='loss')  # 由于fit没有给出测试集,不计算测试集准确率,根据loss,保存最优模型

history = model.fit(x_train, y_train, batch_size=32, epochs=100, callbacks=[cp_callback])

model.summary()

# print(model.trainable_variables)
file = open('./weights.txt', 'w')  # 参数提取
for v in model.trainable_variables:
    file.write(str(v.name) + '\n')
    file.write(str(v.shape) + '\n')
    file.write(str(v.numpy()) + '\n')
file.close()

###############################################    show   ###############################################

# 显示训练集和验证集的acc和loss曲线
acc = history.history['sparse_categorical_accuracy']
loss = history.history['loss']

plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.title('Training Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.title('Training Loss')
plt.legend()
plt.show()

############### predict #############

preNum = int(input("input the number of test alphabet:"))
for i in range(preNum):
    alphabet1 = input("input test alphabet:")
    alphabet = [id_to_onehot[w_to_id[alphabet1]]]
    # 使alphabet符合SimpleRNN输入要求:[送入样本数, 循环核时间展开步数, 每个时间步输入特征个数]。此处验证效果送入了1个样本,送入样本数为1;输入1个字母出结果,所以循环核时间展开步数为1; 表示为独热码有5个输入特征,每个时间步输入特征个数为5
    alphabet = np.reshape(alphabet, (1, 1, 5))
    result = model.predict([alphabet])
    pred = tf.argmax(result, axis=1)
    pred = int(pred)
    tf.print(alphabet1 + '->' + input_word[pred])

  测试:首先输入要执行几次预测任务;随后等待输入一个字母,将这个字母转换为独热码形式后调整为 RNN 层希望的形状;然后通过 predict 得到预测结果,选出预测结果中最大的一个即为预测结果。
在这里插入图片描述
  acc和loss曲线:
在这里插入图片描述

2.6.2 Embedding 编码方式

(1)为什么要使用Embedding?

  • 独热码:数据量大、过于稀疏,映射之间是独立的,没有表现出关联性。
  • Embedding:是一种单词编码方法,用低维向量实现了编码。这种编码通过神经网络训练优化,能表达出单词间的相关性。

(2)Tensorflow2中的词向量空间编码层:

tf.keras.layers.Embedding(词汇表大小,编码维度)

词汇表大小:编码一共要表示多少个单词;
编码维度:用几个数字表达一个单词;
输入维度:二维张量[送入样本数,循环核时间展开步数]
输出维度:三维张量[送入样本数,循环核时间展开步数,编码维度]
例 :tf.keras.layers.Embedding(100, 3)。对数字 1-100 进行编码,词汇表大小就是 100 ;每个自然数用三个数字表示,编码维度就是 3,所以 Embedding层的参数是 100 和 3。比如数字[4] embedding 为 [0.25, 0.1, 0.11]。

(3)Embedding 实现字母预测

import numpy as np
import tensorflow as tf
from tensorflow.keras.layers import Dense, SimpleRNN, Embedding
import matplotlib.pyplot as plt
import os

input_word = "abcde"
w_to_id = {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4}  # 单词映射到数值id的词典

x_train = [w_to_id['a'], w_to_id['b'], w_to_id['c'], w_to_id['d'], w_to_id['e']]
y_train = [w_to_id['b'], w_to_id['c'], w_to_id['d'], w_to_id['e'], w_to_id['a']]

np.random.seed(7)
np.random.shuffle(x_train)
np.random.seed(7)
np.random.shuffle(y_train)
tf.random.set_seed(7)

# 使x_train符合Embedding输入要求:[送入样本数, 循环核时间展开步数] ,
# 此处整个数据集送入所以送入,送入样本数为len(x_train);输入1个字母出结果,循环核时间展开步数为1。
x_train = np.reshape(x_train, (len(x_train), 1))
y_train = np.array(y_train)

model = tf.keras.Sequential([
    Embedding(5, 2),    # 先对输入数据进行编码,这一层生成5行2列的可训练参数矩阵,实现编码可训练
    SimpleRNN(3),   # 3个记忆体的循环层
    Dense(5, activation='softmax')  # 全连接层实现输出y的计算
])

model.compile(optimizer=tf.keras.optimizers.Adam(0.01),
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['sparse_categorical_accuracy'])

checkpoint_save_path = "./checkpoint/run_embedding_1pre1.ckpt"

if os.path.exists(checkpoint_save_path + '.index'):
    print('-------------load the model-----------------')
    model.load_weights(checkpoint_save_path)

cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
                                                 save_weights_only=True,
                                                 save_best_only=True,
                                                 monitor='loss')  # 由于fit没有给出测试集,不计算测试集准确率,根据loss,保存最优模型

history = model.fit(x_train, y_train, batch_size=32, epochs=100, callbacks=[cp_callback])

model.summary()

# print(model.trainable_variables)
file = open('./weights.txt', 'w')  # 参数提取
for v in model.trainable_variables:
    file.write(str(v.name) + '\n')
    file.write(str(v.shape) + '\n')
    file.write(str(v.numpy()) + '\n')
file.close()

###############################################    show   ###############################################

# 显示训练集和验证集的acc和loss曲线
acc = history.history['sparse_categorical_accuracy']
loss = history.history['loss']

plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.title('Training Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.title('Training Loss')
plt.legend()
plt.show()

############### predict #############

preNum = int(input("input the number of test alphabet:"))
for i in range(preNum):
    alphabet1 = input("input test alphabet:")
    alphabet = [w_to_id[alphabet1]]
    # 使alphabet符合Embedding输入要求:[送入样本数, 循环核时间展开步数]。
    # 此处验证效果送入了1个样本,送入样本数为1;输入1个字母出结果,循环核时间展开步数为1。
    alphabet = np.reshape(alphabet, (1, 1))
    result = model.predict(alphabet)
    pred = tf.argmax(result, axis=1)
    pred = int(pred)
    tf.print(alphabet1 + '->' + input_word[pred])

在这里插入图片描述
  acc和loss曲线:
在这里插入图片描述

2.7 循环计算过程之4pre1

使用RNN实现连续输入四个字母,预测下一个字母
  1pre1 是输入一个字母预测下一个字母的例子,4pre1 是连续输入多(这里取4)个字母预测下一个字母的例子。这里仍然使用三个记忆体,初始时刻记忆体内的记忆是 0。接下来用一套训练好的参数矩阵感受循环计算的前向传播过程,在这个过程中的每个时刻参数矩阵是固定的,记忆体会在每个时刻被更新。下面以输入 bcde 预测 a 为例:
在这里插入图片描述
  在第一个时刻,b 的独热码[0,1,0,0,0]输入,记忆体根据如下更新公式刷新

h t = t a n h ( x t w x h + h t 1 w h h + b h ) = t a n h ( [ 1.5 0.2 0.3 ] + [ 0.0 0.0 0.0 ] + [ 0.2 0.0 0.1 ] ) = t a n h ( [ 1.3 0.2 0.2 ] ) = [ 0.9 0.2 0.2 ] \begin{aligned} h_t&=tanh(x_tw_{xh}+h_{t-1}w_{hh}+bh)\\ &=tanh([-1.5\quad0.2\quad0.3]+[0.0 \quad0.0\quad0.0]+[0.2\quad0.0\quad -0.1])\\ &=tanh([-1.3\quad0.2\quad0.2])\\ &=[-0.9\quad0.2\quad0.2] \end{aligned}

  在第二个时刻,c 的独热码[0,0,1,0,0]输入,记忆体根据更新公式刷新

h t = t a n h ( x t w x h + h t 1 w h h + b h ) = t a n h ( [ 0.3 1.7 0.7 ] + [ 1.1 1.1 0.5 ] + [ 0.2 0.0 0.1 ] ) = t a n h ( [ 1.0 2.8 1.1 ] ) = [ 0.8 1.0 0.8 ] \begin{aligned} h_t&=tanh(x_tw_{xh}+h_{t-1}w_{hh}+bh)\\ &=tanh([-0.3\quad1.7\quad0.7]+[1.1 \quad1.1\quad0.5]+[0.2\quad0.0\quad -0.1])\\ &=tanh([1.0\quad2.8\quad1.1])\\ &=[0.8\quad1.0\quad0.8] \end{aligned}

  在第三个时刻,d 的独热码[0,0,0,1,0]输入,记忆体根据更新公式刷新

h t = t a n h ( x t w x h + h t 1 w h h + b h ) = t a n h ( [ 0.1 0.1 0.1 ] + [ 0.6 0.4 2.2 ] + [ 0.2 0.0 0.1 ] ) = t a n h ( [ 0.7 0.5 2.4 ] ) = [ 0.6 0.5 1.0 ] \begin{aligned} h_t&=tanh(x_tw_{xh}+h_{t-1}w_{hh}+bh)\\ &=tanh([-0.1\quad0.1\quad-0.1]+[0.6 \quad0.4\quad-2.2]+[0.2\quad0.0\quad -0.1])\\ &=tanh([0.7\quad0.5\quad-2.4])\\ &=[0.6\quad0.5\quad-1.0] \end{aligned}

  在第四个时刻,e 的独热码[0,0,0,0,1]输入,记忆体根据更新公式刷新

h t = t a n h ( x t w x h + h t 1 w h h + b h ) = t a n h ( [ 1.2 1.5 0.3 ] + [ 1.3 0.4 0.8 ] + [ 0.2 0.0 0.1 ] ) = t a n h ( [ 2.3 1.9 1.0 ] ) = [ 1.0 1.0 0.8 ] \begin{aligned} h_t&=tanh(x_tw_{xh}+h_{t-1}w_{hh}+bh)\\ &=tanh([-1.2\quad-1.5\quad0.3]+[-1.3 \quad-0.4\quad0.8]+[0.2\quad0.0\quad -0.1])\\ &=tanh([-2.3\quad-1.9\quad1.0])\\ &=[-1.0\quad-1.0\quad0.8] \end{aligned}

  输出预测通过全连接完成,由下式求得最终输出:

y t = s o f t m a x ( h t w h y + b y ) = s o f r m a x ( [ 3.3 1.2 0.9 0.3 3.1 ] + [ 0.3 0.2 0.1 0.1 0.3 ] ) = s o f t m a x ( [ 3.0 1.4 1.0 0.4 3.4 ] ) = [ 0.71 0.14 0.10 0.05 0.00 ] \begin{aligned} y_t&=softmax(h_tw_{hy}+by)\\ &=sofrmax([3.3\quad1.2\quad0.9\quad0.3\quad-3.1]+[−0.3\quad 0.2\quad 0.1\quad 0.1\quad − 0.3])\\ &=softmax([3.0\quad 1.4 \quad1.0 \quad0.4 \quad− 3.4])\\ &=[0.71 \quad0.14\quad 0.10 \quad0.05 \quad0.00 ] \end{aligned}

  ==说明有 71%的可能是字母 a。观察输出结果,模型不仅成功预测出了下一个字母是 a,还可以从神经网络输出的概率发现:因为输入序列的最后一个字母是e,所以模型理应也确实认为下一个字母还是 e 的可能性最小,可能性最大的是a,其次分别是 b、c、d 。==

2.7.1 独热编码方式实现

import numpy as np
import tensorflow as tf
from tensorflow.keras.layers import Dense, SimpleRNN
import matplotlib.pyplot as plt
import os

input_word = "abcde"
w_to_id = {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4}  # 单词映射到数值id的词典
id_to_onehot = {0: [1., 0., 0., 0., 0.],
                1: [0., 1., 0., 0., 0.],
                2: [0., 0., 1., 0., 0.],
                3: [0., 0., 0., 1., 0.],
                4: [0., 0., 0., 0., 1.]}  # id编码为one-hot

x_train = [
    [id_to_onehot[w_to_id['a']], id_to_onehot[w_to_id['b']], id_to_onehot[w_to_id['c']], id_to_onehot[w_to_id['d']]],
    [id_to_onehot[w_to_id['b']], id_to_onehot[w_to_id['c']], id_to_onehot[w_to_id['d']], id_to_onehot[w_to_id['e']]],
    [id_to_onehot[w_to_id['c']], id_to_onehot[w_to_id['d']], id_to_onehot[w_to_id['e']], id_to_onehot[w_to_id['a']]],
    [id_to_onehot[w_to_id['d']], id_to_onehot[w_to_id['e']], id_to_onehot[w_to_id['a']], id_to_onehot[w_to_id['b']]],
    [id_to_onehot[w_to_id['e']], id_to_onehot[w_to_id['a']], id_to_onehot[w_to_id['b']], id_to_onehot[w_to_id['c']]],
]
y_train = [w_to_id['e'], w_to_id['a'], w_to_id['b'], w_to_id['c'], w_to_id['d']]

np.random.seed(7)
np.random.shuffle(x_train)
np.random.seed(7)
np.random.shuffle(y_train)
tf.random.set_seed(7)

# 使x_train符合SimpleRNN输入要求:[送入样本数, 循环核时间展开步数, 每个时间步输入特征个数]。
# 此处整个数据集送入,送入样本数为len(x_train);输入4个字母出结果,循环核时间展开步数为4; 表示为独热码有5个输入特征,每个时间步输入特征个数为5
x_train = np.reshape(x_train, (len(x_train), 4, 5))
y_train = np.array(y_train)

model = tf.keras.Sequential([
    SimpleRNN(3),
    Dense(5, activation='softmax')
])

model.compile(optimizer=tf.keras.optimizers.Adam(0.01),
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['sparse_categorical_accuracy'])

checkpoint_save_path = "./checkpoint/rnn_onehot_4pre1.ckpt"

if os.path.exists(checkpoint_save_path + '.index'):
    print('-------------load the model-----------------')
    model.load_weights(checkpoint_save_path)

cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
                                                 save_weights_only=True,
                                                 save_best_only=True,
                                                 monitor='loss')  # 由于fit没有给出测试集,不计算测试集准确率,根据loss,保存最优模型

history = model.fit(x_train, y_train, batch_size=32, epochs=100, callbacks=[cp_callback])

model.summary()

# print(model.trainable_variables)
file = open('./weights.txt', 'w')  # 参数提取
for v in model.trainable_variables:
    file.write(str(v.name) + '\n')
    file.write(str(v.shape) + '\n')
    file.write(str(v.numpy()) + '\n')
file.close()

###############################################    show   ###############################################

# 显示训练集和验证集的acc和loss曲线
acc = history.history['sparse_categorical_accuracy']
loss = history.history['loss']

plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.title('Training Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.title('Training Loss')
plt.legend()
plt.show()

############### predict #############

preNum = int(input("input the number of test alphabet:"))
for i in range(preNum):
    alphabet1 = input("input test alphabet:")
    alphabet = [id_to_onehot[w_to_id[a]] for a in alphabet1]
    # 使alphabet符合SimpleRNN输入要求:[送入样本数, 循环核时间展开步数, 每个时间步输入特征个数]。此处验证效果送入了1个样本,送入样本数为1;输入4个字母出结果,所以循环核时间展开步数为4; 表示为独热码有5个输入特征,每个时间步输入特征个数为5
    alphabet = np.reshape(alphabet, (1, 4, 5))
    result = model.predict([alphabet])
    pred = tf.argmax(result, axis=1)
    pred = int(pred)
    tf.print(alphabet1 + '->' + input_word[pred])

在结果预测时,需要等待连续输入四个字母,然后把这四个字母转换为独热码,然后调整为 RNN 层希望的形状输入网络进行预测即可。运行结果如下图所示。
在这里插入图片描述
acc和loss曲线:
在这里插入图片描述

2.7.2 Embedding编码方式实现

  这次将词汇量扩充到 26 个(即字母从 a 到 z)。首先建立一个映射表,把字母用数字表示为 0 到 25;然后建立两个空列表,一个用于存放训练用的输入特征 x_train,另一个用于存放训练用的标签 y_train;接下来用 for 循环从数字列表中把连续 4 个数作为输入特征添加到 x_train 中,第 5 个数作为标签添加到 y_train 中,这就构建了训练用的输入特征x_train 和标签y_train。

import numpy as np
import tensorflow as tf
from tensorflow.keras.layers import Dense, SimpleRNN, Embedding
import matplotlib.pyplot as plt
import os

input_word = "abcdefghijklmnopqrstuvwxyz"
w_to_id = {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4,
           'f': 5, 'g': 6, 'h': 7, 'i': 8, 'j': 9,
           'k': 10, 'l': 11, 'm': 12, 'n': 13, 'o': 14,
           'p': 15, 'q': 16, 'r': 17, 's': 18, 't': 19,
           'u': 20, 'v': 21, 'w': 22, 'x': 23, 'y': 24, 'z': 25}  # 单词映射到数值id的词典

training_set_scaled = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                       11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
                       21, 22, 23, 24, 25]

x_train = []
y_train = []

# 连续四个数作为输入特征添加到x_train中,第5个数作为标签添加到y_train中
for i in range(4, 26):
    x_train.append(training_set_scaled[i - 4:i])
    y_train.append(training_set_scaled[i])

np.random.seed(7)
np.random.shuffle(x_train)
np.random.seed(7)
np.random.shuffle(y_train)
tf.random.set_seed(7)

# 使x_train符合Embedding输入要求:[送入样本数, 循环核时间展开步数] ,
# 此处整个数据集送入所以送入,送入样本数为len(x_train);输入4个字母出结果,循环核时间展开步数为4。
x_train = np.reshape(x_train, (len(x_train), 4))
y_train = np.array(y_train)     # y_train变为numpy格式

model = tf.keras.Sequential([
    Embedding(26, 2),   # 词汇量26 ,2表示每个单词用两个数值编码,生成一个26*2的可训练参数矩阵,实现编码可训练
    SimpleRNN(10),
    Dense(26, activation='softmax')     # 26:输出会是26个字母之一
])

model.compile(optimizer=tf.keras.optimizers.Adam(0.01),
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['sparse_categorical_accuracy'])

checkpoint_save_path = "./checkpoint/rnn_embedding_4pre1.ckpt"

if os.path.exists(checkpoint_save_path + '.index'):
    print('-------------load the model-----------------')
    model.load_weights(checkpoint_save_path)

cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
                                                 save_weights_only=True,
                                                 save_best_only=True,
                                                 monitor='loss')  # 由于fit没有给出测试集,不计算测试集准确率,根据loss,保存最优模型

history = model.fit(x_train, y_train, batch_size=32, epochs=100, callbacks=[cp_callback])

model.summary()

file = open('./weights.txt', 'w')  # 参数提取
for v in model.trainable_variables:
    file.write(str(v.name) + '\n')
    file.write(str(v.shape) + '\n')
    file.write(str(v.numpy()) + '\n')
file.close()

###############################################    show   ###############################################

# 显示训练集和验证集的acc和loss曲线
acc = history.history['sparse_categorical_accuracy']
loss = history.history['loss']

plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.title('Training Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.title('Training Loss')
plt.legend()
plt.show()

################# predict ##################

preNum = int(input("input the number of test alphabet:"))
for i in range(preNum):
    alphabet1 = input("input test alphabet:")
    alphabet = [w_to_id[a] for a in alphabet1]
    # 使alphabet符合Embedding输入要求:[送入样本数, 时间展开步数]。
    # 此处验证效果送入了1个样本,送入样本数为1;输入4个字母出结果,循环核时间展开步数为4。
    alphabet = np.reshape(alphabet, (1, 4))
    result = model.predict([alphabet])
    pred = tf.argmax(result, axis=1)    # 选出预测结果最大的一个输出
    pred = int(pred)
    tf.print(alphabet1 + '->' + input_word[pred])

  在验证环节,如下图所示,先输入要执行几次检测,随后等待连续输入四个字母,待输入结束后把它们转换为 Embedding 层希望的形状,然后输入网络进行预测,选出预测结果最大的一个
在这里插入图片描述

到此,基本的循环神经网络就介绍完了,后面的LSTM和GRU带我研究完后再发。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。