rnn/lstm 项目实战
tip:本项目用到的数据和代码在https://pan.baidu.com/s/1Cw6OSSWJevSv7T1ouk4B6Q?pwd=z6w2
1. RNN : 预测股价
任务:基于zgpa_train.csv数据,建立RNN模型,预测股价
1.完成数据预处理,将序列数据转化为可用于RNN输入的数据
2.对新数据zgpa_test.csv进行预测,可视化结果
3.存储预测结果,并观察局部预测结果
备注:模型结构:单层RNN,输出有5个神经元,每次使用前8个数据预测第9个数据
1.1 数据准备
下面是一个预测股票的一个项目实战。
任务:基于zgpa_train.csv数据,建立RNN模型,预测股价
1.完成数据预处理,将序列数据转化为可用于RNN输入的数据
2.对新数据zgpa_test.csv进行预测,可视化结果
3.存储预测结果,并观察局部预测结果
备注:模型结构:单层RNN,输出有5个神经元,每次使用前8个数据预测第9个数据。
数据内容大致如下:
开始项目,代码如下:
import pandas as pd
import numpy as np
data = pd.read_csv('zgpa_train.csv')
data.head()
# 加载收盘价
price = data.loc[:, 'close']
price.head()
# 归一化处理
price_norm = price/max(price)
print(price_norm)
# 归一化之前的数据可视化
%matplotlib inline
from matplotlib import pyplot as plt
fig1 = plt.figure(figsize=(8,5))
plt.plot(price)
plt.title('close_price') # 归一化之前的数据
plt.xlabel('time')
plt.ylabel('price')
plt.show()
%matplotlib inline的作用:
%matplotlib inline
是一个 IPython 魔法命令,用于在 Jupyter Notebook 中将 matplotlib 绘图直接嵌入到输出单元中。这样可以使绘图在 Notebook 中显示,而无需调用 plt.show()
方法。此外,它可以确保每次执行绘图代码时,图形都能在相应的输出单元显示,而不是在外部窗口中弹出。
inline
:表示图像“在线”显示,意思是在 Jupyter Notebook 的输出单元中直接显示图像,而不是在单独的窗口中弹出。
# 对 X 与 y 进行赋值
# 定义方法去提取 X 与 y
def extract_data(data, time_step):
X = [] # list
y = [] # list
# 例如,10个样本;time_step=8;
# 第一组样本,0,1,2,...7
# 第二组样本,1,2,3,...8
# 第三组样本,2,3,4,...9
# 共有2组样本可供观测
for i in range(len(data)-time_step):
X.append([a for a in data[i:i+time_step]])
y.append(data[i+time_step])
X = np.array(X)
X = X.reshape(X.shape[0], X.shape[1], 1)
return X, y
上面是设计数组X、y的函数,即X是因,y是果,用X的一个数组得到y的一个对应值;这里把X处理成了最后一个维度只有一个元素的维度,即X.shape =(*,*,...,1)。
# 定义 X 与 y
time_step = 8
X, y = extract_data(price_norm, time_step)
print(X.shape) # 723个样本,每个样本有8个数据,每个样本对应1个单独数值,(723,8,1)
print(X[0, :, :]) # 第一个样本数据
这里使用刚才的函数,提供data和time_step,创建出X,y。
jupyter里可以不需要用那么多print,直接在单元格输入X.shape,X[0]就能得到相应的结果。
---------------------------------------------------------------------------------------------------------------------------
1.2 模型建立
下面是建立模型的主要代码,这段代码使用 Keras 构建了一个简单的 循环神经网络(RNN)模型,它适合用于 回归任务。以下是代码中各个步骤的解释::
from keras.models import Sequential
from keras.layers import Dense, SimpleRNN # 引入层相关,输出层与 RNN 层
model = Sequential() # 创建实例
model.add(SimpleRNN(units=5, input_shape=(time_step, 1), activation='relu')) # RNN 层
model.add(Dense(units=1, activation='linear')) # 输出层,回归任务直接使用 linear 激活函数
model.compile(optimizer='adam', loss='mean_squared_error') # 回归使用mse评测
model.summary()
逐步解析代码:
1. 导入模块
from keras.models import Sequential
from keras.layers import Dense, SimpleRNN
Sequential
:用于构建 Keras 的顺序模型,可以按顺序堆叠各层。Dense
和SimpleRNN
:分别代表全连接层和简单循环神经网络(RNN)层。
2. 创建模型实例
model = Sequential()
使用 Sequential()
创建一个顺序模型实例 model
,可以层层堆叠神经网络层。
3. 添加 SimpleRNN 层
model.add(SimpleRNN(units=5, input_shape=(time_step, 1), activation='relu'))
- SimpleRNN:一种简单的循环神经网络层。
units=5
:表示 RNN 层有 5 个隐藏单元(Hidden Units)。input_shape=(time_step, 1)
:输入的形状。假设时间步为time_step
,每步有一个特征。activation='relu'
:使用 ReLU 激活函数。
1. 这一步就对应理论部分的由x到h,这里 units=5
表示 RNN 层有 5 个隐藏单元h,每个单元可以理解为负责捕捉某一方面的特征,这些单元共同作用,从输入数据中提取序列信息,并将提取到的特征传递到下一层或输出层。在这个RNN 模型中,每个时间步都会产生 5 个隐藏单元的输出。
隐藏单元越多,模型能够捕捉的特征就越多,模型也会更复杂,适合处理高维和复杂数据。
2. input_shape=(time_step, 1)
:说明输入序列的长度和每个时间步的特征数量,用来告诉模型输入数据的形状。这里的特征指的是,描述了每个时间步的输入信息量,比如传感器数据流中每个时间步可能有多个传感器特征(例如温度、湿度、压力等)。这个模型只用到了一个特征,即close收盘价,对应之前数据准备只取了那一列。
3. activation='relu'
:指定了隐藏状态计算中的激活函数,即计算由x到h的激活函数。
4. 添加 Dense 输出层
model.add(Dense(units=1, activation='linear'))
-
是在给模型添加一个 全连接层(Dense 层),该层有如下配置:
-
units=1:表示该层包含一个神经元,输出为一个值。对于回归任务(例如预测一个连续值),输出层通常只需一个神经元,因为只需输出一个预测结果。
-
activation='linear':激活函数设置为
linear
,即线性激活函数。线性激活函数的作用是直接输出该层计算的值,不做任何非线性变换,这非常适合回归任务,因为回归问题中预测的目标值是连续的,需要直接输出真实值的估计。
即上面的隐藏状态h到这里的y,一个时间步对应只有一个y的输出,具体来说就是一个时间步里,刚才得到的5个隐藏状态h到输出的y,用到的激活函数是 linear。
5. 编译模型
model.compile(optimizer='adam', loss='mean_squared_error')
optimizer='adam'
:使用 Adam 优化器,适合多种神经网络优化。loss='mean_squared_error'
:使用均方误差(MSE)作为损失函数,MSE 常用于回归问题评估模型预测的误差。
Adam 是一种自适应优化算法,具有以下特点:
- 自适应学习率:Adam 会为每个参数动态调整学习率,使得每个参数都能有合适的更新步长。
- 结合动量和 RMSProp 的优势:Adam 使用动量(Momentum)来平滑梯度的更新,同时利用 RMSProp 的方法来控制梯度的变化幅度,从而加速收敛。
6. 模型结构总结
model.summary()
输出模型的层次结构、参数数量等信息,便于了解模型的总体结构和参数情况。
总结
- 该模型是一个用于回归任务的简单 RNN 模型,包含一个 RNN 层和一个全连接输出层。
- 使用 ReLU 激活函数在隐藏层,线性激活函数在输出层,以适应回归任务。
--------------------------------------------------------------------------------------------------------------------------------
1.3 模型层数介绍
在深度学习模型中,层数概念指的是网络结构中的不同计算层,它们共同完成数据的逐步抽象和特征提取。一般来说,一个模型的层可以分为 输入层、隐藏层 和 输出层。这些层共同构成了模型的数据流动路径和特征提取结构。
(1). 输入层
- 作用:接收输入数据并将其传递给模型的第一层。输入层本身不进行任何计算操作,仅定义输入数据的形状。
- 表示方式:在 Keras 中,输入层是通过指定第一层的
input_shape
参数来隐式定义的,不会单独显示在model.summary()
中。 - 例子:在你的模型中,
input_shape=(time_step, 1)
就定义了输入层,表示输入数据是一个具有time_step
个时间步且每个时间步有 1 个特征的序列。
(2). 隐藏层
- 作用:模型中真正进行特征提取、模式识别和学习的层。隐藏层可以是各种类型的神经网络层,例如全连接层(Dense)、卷积层(Conv2D)、循环层(SimpleRNN、LSTM)等。
- 数量和类型:一个模型可以有多个隐藏层,这些层负责将输入数据逐步转换为更高层次的特征。
- 例子:在你的模型中,
SimpleRNN
层就是一个隐藏层。它接受输入数据的时间序列,并通过 5 个隐藏单元来提取时序特征。
(3). 输出层
- 作用:生成最终的模型输出。输出层的形状和激活函数通常根据具体任务(如分类或回归)进行设置。
- 设置:输出层通常是一个全连接层(Dense),其单元数量和激活函数会根据任务调整。例如,分类任务会使用
softmax
激活函数,而回归任务则使用linear
激活函数。 - 例子:在你的模型中,
Dense(units=1, activation='linear')
是输出层,用于回归任务,输出一个连续值。
在深度学习模型中,层数包括输入层(逻辑上存在)、隐藏层(特征提取层)和输出层(生成预测结果)。在大多数框架中,输入层是隐式的,所以模型的“层数”通常只统计显式的隐藏层和输出层。
Dense 层可以同时作为 隐藏层 和 输出层,它的角色取决于其在模型中的位置和任务。
-
作为隐藏层:如果 Dense 层在模型的中间部分,用于进一步处理和转换特征,那么它就是隐藏层。例如,在一个深层神经网络中,前几层 Dense 层可以作为隐藏层,以提取输入数据的特征。
-
作为输出层:如果 Dense 层是模型的最后一层,用于生成最终的输出(如分类概率或回归值),那么它就是输出层。例如,在分类任务中,最后一个 Dense 层通常会使用
softmax
激活函数作为输出层;在回归任务中,通常使用linear
激活函数作为输出层。
上面提到的模型有 2 个显式的计算层(SimpleRNN
和 Dense
):
- 输入层(隐式):指定输入的形状
(time_step, 1)
,传入 SimpleRNN 层。 - SimpleRNN 层:接收输入数据并生成 5 个隐藏状态的输出,作为时序特征。
- Dense 输出层:接收 5 维的隐藏状态,将它转化为一个数值输出,用于回归任务。
--------------------------------------------------------------------------------------------------------------------------
1.4 训练模型
# 训练模型
model.fit(X, np.array(y), batch_size = 30, epochs = 200)
这一步使用 fit()
方法来训练模型,参数说明如下:
- X:训练数据的输入特征。
- y:训练数据的标签(目标值),这里先转换为 NumPy 数组
np.array(y)
。 - batch_size=30:批量大小,每次更新模型权重时使用 30 个样本的数据,这样可以加快训练速度。
- epochs=200:训练的轮数,将整个数据集训练 200 次,模型会在每个 epoch 后更新权重以逐步优化性能。
在训练过程中,模型会通过前向传播计算预测结果,通过反向传播调整权重,逐步减小预测值与真实值之间的误差。
在训练模型时,使用批量大小(batch_size=30
)而不是全部样本进行权重更新,主要是为了在效率和性能上做出平衡。选择 batch_size=30
,可以加快训练速度、减少内存占用,同时还可以引入一定的随机性,使得训练过程更稳定,减少陷入局部最优的风险。
------------------------------------------------------------------------------------------------------------------------------
1.5 基于训练数据做预测
# 基于训练数据做预测
y_train_predict = model.predict(X)*max(price) # 预测结果:去除归一化
y_train = [i * max(price) for i in y] # 训练数据真实结果
这部分代码进行基于训练数据的预测,并将预测结果“去归一化”,即将预测值和真实值还原到原始价格范围。详细说明如下:
-
y_train_predict:使用
model.predict(X)
基于训练数据X
进行预测,得到归一化状态下的预测结果。之后通过* max(price)
将预测结果恢复到原始数据范围,因为在数据预处理时可能将price
归一化过,所以这里乘以max(price)
还原到真实价格范围。 -
y_train:真实的训练数据
y
也被还原到原始数据范围。代码[i * max(price) for i in y]
中,将归一化状态下的y
乘以max(price)
,以得到真实的训练数据价格。
tip:model.predict(X)
的详细过程
-
前向传播:
predict()
方法会将输入数据X
逐层传递,通过网络结构中的每一层(如 RNN 层、Dense 层),并应用层中的权重和激活函数计算输出。 -
生成预测值:经过前向传播后,
predict()
会输出每个输入样本的预测结果。这些结果的形状取决于模型的输出层结构。在你的模型中,输出层是一个Dense
层,有一个神经元,所以predict()
将返回每个输入样本的单一预测值。
--------------------------------------------------------------------------------------------------------------------------------
1.6 展示真实值和预测值
fig2 = plt.figure(figsize=(8, 5))
plt.plot(y_train, label='real price')
plt.plot(y_train_predict, label='predict price')
plt.title('close_price') # 归一化之前的数据
plt.xlabel('time')
plt.ylabel('price')
plt.legend()
plt.show()
1.7 对测试数据进行预测
最后把测试数据放到刚才训练的模型,得到相应的预测值。
现预处理数据
# 对测试数据进行预测
data_test = pd.read_csv('zgpa_test.csv')
data_test.head()
price_test = data_test.loc[:, 'close']
price_test.head()
# extract X_test and y_test
price_test_norm = price_test/max(price)
X_test_norm, y_test_norm = extract_data(price_test_norm, time_step)
print(X_test_norm.shape, len(y_test_norm))
拿训练好的模型,做预测,并画图做对比,比较预测值和真实值:
# make prediction based on the test data
y_test_predict = model.predict(X_test_norm)*max(price)
y_test = [i*max(price) for i in y_test_norm]
fig3 = plt.figure(figsize=(8, 5))
plt.plot(y_test, label='real price_test')
plt.plot(y_test_predict, label='predict price_test')
plt.title('close_price') # 归一化之前的数据
plt.xlabel('time')
plt.ylabel('price')
plt.legend() # 展示图例
plt.show()
把预测的数据存储起来:
# 存储数据
result_y_test = np.array(y_test).reshape(-1, 1) # 若干行,1列
result_y_test_predict = y_test_predict
print(result_y_test.shape, result_y_test_predict.shape)
result = np.concatenate((result_y_test, result_y_test_predict), axis=1)
print(result.shape)
result = pd.DataFrame(result, columns=['real_price_test', 'predict_price_test'])
result.to_csv('zgpa_predict_test.csv')
--------------------------------------------------------------------------------------------------------------------------------
1.8 pandas的一点知识点
(1) -1在reshape的应用
这里提到了reshape(-1),那就讲一下其作用。在 reshape
中只能有一个 -1
。如果有多个 -1
,NumPy 无法确定数组的形状。当使用 -1
作为 reshape()
参数中的某个维度时,NumPy 会根据数组的总元素数量和指定的其他维度,自动计算 -1
所在维度的大小。举几个例子:
单独一个-1,将数组展平为一维数组。不论原数组的形状是什么,reshape(-1)
会将其拉平成一个一维向量。
arr = np.array([[1, 2, 3], [4, 5, 6]])
arr_reshaped = arr.reshape(-1)
print(arr_reshaped)
[1 2 3 4 5 6]
下面将数组转换为二维数组,其中有多行(具体行数由原数组长度决定),每行只有 1 列。-1
表示行数由系统自动计算,而 1
则指定了列数为 1。
arr = np.array([1, 2, 3, 4, 5, 6])
arr_reshaped = arr.reshape(-1, 1)
print(arr_reshaped)
[[1][2][3][4][5][6]]
reshape(3, -1)
告诉 NumPy 重新将数组的形状调整为 3 行,而列数(-1
)则由 NumPy 自动计算为 4,以确保元素总数(3x4=12)不变。
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
arr_reshaped = arr.reshape(3, -1)
print(arr_reshaped)
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
(2) numpy中concatenate的应用
把两个长度为n的一维array合并成一个二维shape为(n,2)的array,可以用以下两个方法:
1. 先把一维array用方法reshape(-1,1),转化成[[1][2][3][4][5][6]],这种格式,然后指定axis=1,在里面那层进行合并。
# 将一维数组转换为二维数组,每个数组为一列
# a = np.array([1,2,3,4,5])
a1 = a.reshape(-1, 1)
b1 = b.reshape(-1, 1)
# 按列方向(axis=1)合并成一个二维数组
result = np.concatenate((a1, b1), axis=1)
2. 或者直接使用np.column_stack()。
result2 = np.column_stack((a,b))
1.9 总结
这个股票预测rnn模型的局限性:预测结果比实际结果趋势变化较慢。
如上图所示,实际数据已经开始上升了,而预测的数据还在下降,并在下一个时间点上才开始上升。
2. LSTM:预测序列文字
任务:基于 flare 文本数据,建立 LSTM 模型,预测序列文字
1.完成数据预处理,将文字序列数据转化为可用于LSTM输入的数据
2.查看文字数据预处理后的数据结构,并进行数据分离操作
3.针对字符串输入(" flare is a teacher in ai industry. He obtained his phd in Australia."),预测其对应的后续字符
备注:模型结构:单层LSTM,输出有20个神经元:每次使用前20个字符预测第21个字符
2.1 数据预处理
加载文本数据,把换行符和制表符替换成空格。
# load the data
data = open('flare').read()
data = data.replace('\n','').replace('\r', '') # 替换换行符
print(data)
用set函数对字符数据进行处理,得到去重后的字符。
# 字符去重处理
letters = list(set(data))
print(letters)
num_letters = len(letters)
print(num_letters) # 进行独热数值编码,23行1列的数组
#['A', 'o', 'c', 'h', 'l', 'm', 'p', '.', 's', 'S', ' ', 't', 'n', 'H', 'e', 'b', 'd', 'a', 'u', 'f', 'y', 'r', 'i']
23
利用for循环加上enumerate函数配合得到由序号、字符组成的字典。
# 建立字典
# int to char
int_to_char = {a:b for a,b in enumerate(letters)}
print(int_to_char)
# char to int
char_to_int = {b:a for a,b in enumerate(letters)}
print(char_to_int)
{0: 'A', 1: 'o', 2: 'c', 3: 'h', 4: 'l', 5: 'm', 6: 'p', 7: '.', 8: 's', 9: 'S', 10: ' ', 11: 't', 12: 'n', 13: 'H', 14: 'e', 15: 'b', 16: 'd', 17: 'a', 18: 'u', 19: 'f', 20: 'y', 21: 'r', 22: 'i'}
{'A': 0, 'o': 1, 'c': 2, 'h': 3, 'l': 4, 'm': 5, 'p': 6, '.': 7, 's': 8, 'S': 9, ' ': 10, 't': 11, 'n': 12, 'H': 13, 'e': 14, 'b': 15, 'd': 16, 'a': 17, 'u': 18, 'f': 19, 'y': 20, 'r': 21, 'i': 22}
设置time_step = 20,即用连续的20个字符预测第21个字符。
2.1.1 构建数据处理函数
然后构建能得到训练数据X、y的函数。
# time_step
time_step = 20
# 数据预处理
import numpy as np
from tensorflow.keras.utils import to_categorical # 库发生了迁移
# 滑动窗口提取数据
def extract_data(data, slide):
x = []
y = []
for i in range(len(data) - slide):
x.append([a for a in data[i : i + slide]])
y.append(data[i+slide])
return x,y
# 字符到数字的批量转化
def char_to_int_Data(x, y, chat_to_int):
x_to_int = []
y_to_int = []
for i in range(len(x)):
x_to_int.append([char_to_int[char] for char in x[i]])
y_to_int.append([char_to_int[char] for char in y[i]])
return x_to_int, y_to_int
# 实现输入字符文章的批量处理,输入整个字符,滑动窗口大小,转化字典
def data_preprocessing(data, slide, num_letters, char_to_int):
char_Data = extract_data(data, slide)
int_Data = char_to_int_Data(char_Data[0], char_Data[1], char_to_int)
Input = int_Data[0]
Output = list(np.array(int_Data[1]).flatten())
Input_RESHAPED = np.array(Input).reshape(len(Input), slide)
new = np.random.randint(0, 10, size=[Input_RESHAPED.shape[0], Input_RESHAPED.shape[1], num_letters])
for i in range(Input_RESHAPED.shape[0]):
for j in range(Input_RESHAPED.shape[1]):
new[i, j, :] = to_categorical(Input_RESHAPED[i, j], num_classes = num_letters)
return new, Output
这里的函数比较复杂,最终data_preprocessing函数嵌套了之前构建的函数,最后要求传入文本数据,time_step(用多少个字符预测下一个), num_letters(文本数据中不重复字符个数), char_to_int(文本-序号字典)。
2.1.2 One-hot 编码
这里从tensorflow.keras.utils
模块中导入 to_categorical
函数。to_categorical
是一个工具函数,主要用于将整数编码的类别标签转换为 One-hot 编码 格式。
One-hot 编码是一种常用的编码方式,主要用于将类别数据转换为数值数据,使其可以用于机器学习或深度学习模型中。它的核心思想是使用一个向量表示每一个类别,其中只有一个位置是 1
,其余位置是 0
。这个唯一的 1
表示该类别的位置。
假设我们有 4 个类别:苹果
、香蕉
、橘子
和 葡萄
。我们可以为这 4 个类别进行 One-hot 编码:
类别 | One-hot 编码 |
---|---|
苹果 | [1, 0, 0, 0] |
香蕉 | [0, 1, 0, 0] |
橘子 | [0, 0, 1, 0] |
葡萄 | [0, 0, 0, 1] |
--------------------------------------------------------------------------------------------------------------------------------
2.1.3 得到训练集、测试集
下面传入相应的参数,得到转化成数字的数据X,y。
X、y都只能是数字,其中X转化成了One-hot 编码,y转化成了字符-数字字典里字符对应的数字。
# extract X and y from text data
X, y = data_preprocessing(data, time_step, num_letters, char_to_int) # X 已经被独热编码,y 稍后处理
试着了解下得到的数据是什么样的。
print(X) # 独热格式
print(X.shape) # 23 个映射
[[[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]
...
[0 0 0 ... 0 1 0]
[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 1]]
[[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]
[0 0 0 ... 0 1 0]
...
(44962, 20, 23)
print(len(y))
44962
首先看shape,这个文本有44962+20个字符,得到44962个组,后面对应的y是一个有44962个元素的列表,和X相对应。后面的(20,23)表示用20个字符来预测下一个字符,这20个字符每一个字符都是用的One-hot 编码来表示的。
大概是这个意思:X[0]得到的20*23二维矩阵,根据One-hot 编码中23个字符对应的位置,得到对应的字符,再用20个字符预测得到y[0],即y的第一个数字,对应的字符。(图里的字符只是举个例子。)
把刚才处理得到的44962组转化成数字类型数据,根据0.1的比例,分90%的数据给训练集,10%的数据给测试集。
# split the data
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=10)
print(X_train.shape, len(y_train))
(40465, 20, 23) 40465
把刚才的y也转化成One-hot 编码。
y_train_category = to_categorical(y_train, num_letters)
print(y_train_category)
tip:刚才rnn也分了训练集和测试集,只不过lstm这个项目用一个文件按比例(9:1)拆成训练集和测试集,而rnn模型则是分别用两个文件当成训练集和测试集。
2.2 构造模型
和之前构建rnn模型类似,相似的内容不再赘述。
# set up the model
from keras.models import Sequential
from keras.layers import Dense, LSTM
model = Sequential()
model.add(LSTM(units=20, input_shape=(X_train.shape[1], X_train.shape[2]), activation='relu'))
model.add(Dense(units=num_letters, activation='softmax'))
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
model.summary()
这里指定的损失函数为categorical_crossentropy
,这是多分类任务中常用的损失函数,用于 One-hot 编码格式的标签。
metrics
指定评估指标,评估指标用于在训练和评估时衡量模型的表现。accuracy
准确率是最常用的分类评估指标之一,表示模型预测正确的样本占总样本的比例。在训练和验证过程中,模型会输出 accuracy
指标值,以便观察模型的分类性能。
2.3 训练模型
# train the model
model.fit(X_train, y_train_category, batch_size = 1000, epochs = 10)
模型在每次迭代时会从训练数据中随机抽取 1000 个样本进行训练,暂时先训练轮数为10。
2.4 利用模型进行预测(训练值)
# make prediction based on the training data
predict_x = model.predict(X_train) # 笔者可能与视频使用的keras不是一个版本,故而有所更改
y_train_predict = np.argmax(predict_x, axis=1)
print(y_train_predict)
这里得到的predict_x是二维数组:
根据argmax函数,配合axis=1,找出第二层每个中括号中最大值的索引(从0开始)。 得到一维数组y_train_predict(40465):
把数字数组根据之前的字典转化回字符:
# transform the int to letters
y_train_predict_char = [int_to_char[i] for i in y_train_predict]
print(y_train_predict_char)
计算下训练集的真实值和预测值的准确度:
from sklearn.metrics import accuracy_score
accuracy_train = accuracy_score(y_train, y_train_predict)
print(accuracy_train)
2.5 测试集的预测
方法和上面差不多,最后得到测试集的真实值和预测值的准确度。
predict_x = model.predict(X_test) # 笔者可能与视频使用的keras不是一个版本,故而有所更改
y_test_predict = np.argmax(predict_x, axis=1)
accuracy_test = accuracy_score(y_test, y_test_predict)
print(accuracy_test)
print(y_test_predict)
print(y_test)
2.6 用模型预测一段其他文本
拿一段另外的文本,用前二十个字符预测第21个字符,这段文本得保证里面的字符都包括在一开始得到的字符集合里。
# 预测样例
new_letters = "flare is a teacher in ai industry. He obtained his phd in Australia."
X_new, y_new = data_preprocessing(new_letters, time_step, num_letters, char_to_int)
predict_x = model.predict(X_new) # 笔者可能与视频使用的keras不是一个版本,故而有所更改
y_new_predict = np.argmax(predict_x, axis=1)
print(y_new_predict)
# transform the int to letters
y_new_predict_char = [int_to_char[i] for i in y_new_predict]
print(y_new_predict_char)
还是像之前的操作:用数据处理函数先得到X_new三维数组,用模型预测得到二位数组predict_x,然后用argmax配合axis=1得到一维数组y_new_predict,最后把用序号-字符字典把数组转化成字符组,得到预测值。
可以配合下面代码,返回的内容使得预测可视化更加合理:
for i in range(0, X_new.shape[0]-20):
print(new_letters[i:i+20], '--predict next letter is--', y_new_predict_char[i])
得到结果如下:
如图,可以看到预测的结果不是很理想。可以再次训练模型,然后重复刚才2.6的操作:
# train the model
model.fit(X_train, y_train_category, batch_size = 1000, epochs = 10)
# 预测样例
new_letters = "flare is a teacher in ai industry. He obtained his phd in Australia."
X_new, y_new = data_preprocessing(new_letters, time_step, num_letters, char_to_int)
predict_x = model.predict(X_new) # 笔者可能与视频使用的keras不是一个版本,故而有所更改
y_new_predict = np.argmax(predict_x, axis=1)
print(y_new_predict)
# transform the int to letters
y_new_predict_char = [int_to_char[i] for i in y_new_predict]
print(y_new_predict_char)
for i in range(0, X_new.shape[0]-20):
print(new_letters[i:i+20], '--predict next letter is--', y_new_predict_char[i])
这次得到的结果就更加满意了:
2.7 总结
总结一下rnn和lstm两个项目中,训练和预测用到的数据:
rnn:用一个文件做训练集,对模型进行训练;用另外一个文件直接拿来做预测。
lstm:将一个文件按9:1分成训练集和测试集;用另外一个文本拿来做预测。