1 RNN和LSTM
RNN和LSTM的基础内容,就不细讲了,可参考内容:Deep Learning-卷积神经网络
RNN相比前向网络,主要是增加了时间参数共享的先验知识,LSTM针对RNN梯度消失和爆炸的缺点,增加了三个门,对记忆进行了控制。
2 神经网络相关参数说明
名词 | 解释 |
---|---|
Epoch | 使用训练集的全部数据对模型进行一次完整的训练,称为“一代训练” |
Batch | 使用训练集中的一小部分样本对模型权重进行一次反向传播的参数更新。这一小部分数据被称为“一批数据”(通常设为2的n次幂,网络较小选用256,较大选用64) |
Iteration | 使用一个Batch数据对模型进行一次参数更新的过程,被称为“一次训练” 。 (就是batch的个数,训练一次:一次前向传播+一次方向传播) |
换算关系
N
u
m
b
e
r
o
f
B
a
t
c
h
e
s
=
T
r
a
i
n
i
n
g
S
e
t
S
i
z
e
B
a
t
c
h
S
i
z
e
Number of Batches =\frac{Training Set Size}{Batch Size}
NumberofBatches=BatchSizeTrainingSetSize
梯度下降
梯度下降方式 | Training Set Size | Batch Size | Number of Batches |
---|---|---|---|
SGD | N | 1 | N |
BGD | N | N | 1 |
Mini-Batch | N | B | N/B(未整除需加1) |
案例:假设现在训练集大小为90000,测试集为10000。现在选择 Batch Size = 256 对模型进行训练。
每个 Epoch 要训练的图片数量:90000
训练集具有的 Batch 个数:90000/256=351+1=352
每个 Epoch 需要完成的 Batch 个数:352
每个 Epoch 具有的 Iteration 个数:352
每个 Epoch 中发生模型权重更新的次数:352
训练 10 代后,模型权重更新的次数:352*10=3520
不同代的训练,其实用的是同一个训练集的数据。第 1 代和第 10 代虽然用的都是训练集的9万数据,但是对模型的权重更新值却是完全不同的。因为不同代的模型处于代价函数空间上的不同位置,模型的训练代越靠后,越接近谷底,其代价越小。
3 keras中的stateful
使用有状态的RNN是为了把每批样本的最后状态作为下一批样本的初始状态。
当使用有状态 RNN 时,假定:
- 所有的批次都有相同数量的样本
- 如果 x1 和 x2 是连续批次的样本,则 x2[i] 是 x1[i] 的后续序列,对于每个 i。
要在 RNN 中使用状态,你需要:
- 通过将 batch_size 参数传递给模型的第一层来显式指定你正在使用的批大小。例如,对于 10 个时间步长的 32 样本的 batch,每个时间步长具有 16 个特征,batch_size = 32。
- 在 RNN 层中设置 stateful = True。
- 在调用 fit() 时指定 shuffle = False。
重置累积状态:
- 使用 model.reset_states() 来重置模型中所有层的状态
- 使用 layer.reset_states() 来重置指定有状态 RNN 层的状态
使用场景:
假设文章一共1000句话,我们想预测出第1001句是什么,不想丢弃前1000句里的一些时序性特征(stateless时这1000句训练时会被打乱,时序性特征丢失)。那么,stateful LSTM就可以做到。
在stateful = True 时,我们要在fit中手动使得shuffle = False。随后,在X[i](表示输入矩阵中第i个sample)这个小序列训练完之后,Keras会将将训练完的记忆参数传递给X[i+bs](表示第i+bs个sample),作为其初始的记忆参数。bs = batch_size。这样一来,我们的记忆参数就能顺利地在sample和sample之间传递,X[i+n*bs]也能知道X[i]的信息。
4 模型可视化
使用netron对Keras生成的模型进行可视化,netron支持多种深度学习框架。netron即可以使用网页版也可以使用python版,这里使用python版。
下载
pip install netron
使用
import netron
netron.start(filename)
效果:
参考资料:
用Netron实现可视化
netron使用效果
基于历史数据集预测销售额
sales-of-shampoo-over-a-three-ye.csvs数据
“Month”,“Sales of shampoo over a three year period”
“1-01”,266.0
“1-02”,145.9
“1-03”,183.1
“1-04”,119.3
“1-05”,180.3
“1-06”,168.5
“1-07”,231.8
“1-08”,224.5
“1-09”,192.8
“1-10”,122.9
“1-11”,336.5
“1-12”,185.9
“2-01”,194.3
“2-02”,149.5
“2-03”,210.1
“2-04”,273.3
“2-05”,191.4
“2-06”,287.0
“2-07”,226.0
“2-08”,303.6
“2-09”,289.9
“2-10”,421.6
“2-11”,264.5
“2-12”,342.3
“3-01”,339.7
“3-02”,440.4
“3-03”,315.9
“3-04”,439.3
“3-05”,401.3
“3-06”,437.4
“3-07”,575.5
“3-08”,407.6
“3-09”,682.0
“3-10”,475.3
“3-11”,581.3
“3-12”,646.9
加载数据,绘制销售额图像
# load and plot dataset
from pandas import read_csv
from pandas import datetime
import matplotlib.pyplot as plt
import pandas as pd
def parser(x):
return datetime.strptime('200'+x,'%Y-%m')
series = read_csv('./data/sales-of-shampoo-over-a-three-ye.csv',header=0,parse_dates=[0],index_col=0,squeeze=True,date_parser=parser)
series.tail()
# line plot
series.plot()
plt.show()
RNN 预测代码
from pandas import DataFrame
from pandas import datetime
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense,SimpleRNN
from math import sqrt
import matplotlib
import numpy as np
# be able to save images on server
matplotlib.use('Agg')
# 将时间序列转换为监督值
def timeseries_to_supervised(data, lag=1):
df = pd.DataFrame(data)
columns = [df.shift(i) for i in range(1,lag+1)]
columns.append(df)
df = pd.concat(columns,axis=1)
return df
# create a differenced series
def difference(dataset, interval=1):
diff = list()
for i in range(interval, len(dataset)):
value = dataset[i] - dataset[i - interval] # 每个值减去前一个值,注意i是从interval开始
diff.append(value)
return Series(diff)
# invert differenced value 间隔默认为1
def inverse_difference(history, yhat, interval=1):
return yhat+history[-interval]
# 缩放 train 和 test 数据到 [-1,1]
def scale(train, test):
# 将属性缩放到一个指定的最大和最小值,对于方差非常小的属性可以增强其稳定性;维持稀疏矩阵中为0的条目
scaler = MinMaxScaler(feature_range=(-1,1))
scaler = scaler.fit(train)
# transform train
train = train.reshape(train.shape[0], train.shape[1])
trian_scaled = scaler.transform(train)
# transform test
test = test.reshape(test.shape[0],train.shape[1])
test_scaled = scaler.transform(test)
return scaler, trian_scaled, test_scaled
# inverse scaling for a forecasted value
def invert_scale(scaler,X,yhat):
new_row = [x for x in X] + [yhat]
array = np.array(new_row)
array = array.reshape(1,len(array))
inverted = scaler.inverse_transform(array)
return inverted[0,-1]
# fit an LSTM network to training data
def fit_rnn(train, n_batch, nb_epoch, n_neurons):
X, y = train[:, 0:-1], train[:, -1]
# 调整大小是因为后面训练需要记忆样本间的参数
X = X.reshape(X.shape[0], 1, X.shape[1])
model = Sequential()
# stateful:True批次中索引i处的每个样品的最后状态将用作下一批次中索引i样品的初始状态。让参数在sample之间传递
# SimpleRNN:全连接的RNN batch_input_shape:固定批次大小
model.add(SimpleRNN(n_neurons, batch_input_shape=(n_batch, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
for i in range(nb_epoch):
# epochs:整个数据的迭代 batch_size:每个梯度更新的样本数
# verbose:日志显示 0 不在标准输出流输出日志信息
model.fit(X, y, epochs=1, batch_size=n_batch, verbose=0, shuffle=False)
# clear hidden states
model.reset_states()
return model
def run_rnn(series, n_lag, n_repeats, n_epochs, n_batch, n_neurons):
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, n_lag)
supervised_values = supervised.values[n_lag:, :]
# split data into train and test-sets
train, test = supervised_values[0:-12], supervised_values[-12:]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# run experiment
error_scores = list()
for r in range(n_repeats):
# fit the model
train_trimmed = train_scaled[2:, :]
rnn_model = fit_rnn(train_trimmed, n_batch, n_epochs, n_neurons)
# forecast test dataset
test_reshaped = test_scaled[:, 0:-1]
test_reshaped = test_reshaped.reshape(len(test_reshaped), 1, 1)
output = rnn_model.predict(test_reshaped, batch_size=n_batch)
predictions = list()
for i in range(len(output)):
yhat = output[i, 0]
X = test_scaled[i, 0:-1]
# invert scaling
yhat = invert_scale(scaler, X, yhat)
# invert differencing
yhat = inverse_difference(raw_values, yhat, len(test_scaled) + 1 - i)
# store forecast
predictions.append(yhat)
# report performance
rmse = sqrt(mean_squared_error(raw_values[-12:], predictions))
print('%d) Test RMSE: %.3f' % (r + 1, rmse))
error_scores.append(rmse)
return error_scores
def run():
# load dataset
series = pd.read_csv('./data/sales-of-shampoo-over-a-three-ye.csv', header=0, parse_dates=[0], index_col=0, squeeze=True,
date_parser=parser)
# configure the experiment
n_lag = 1
n_repeats = 30 # 迭代次数
n_epochs = 1000 # 完整训练次数
n_batch = 4 # 小样本数
n_neurons = 3 # 神经元个数
# run the experiment
results = pd.DataFrame()
results['results'] = run_rnn(series, n_lag, n_repeats, n_epochs, n_batch, n_neurons)
results.plot(title="RNN RMSE Iteration")
plt.show()
# summarize results
print(results.describe())
# save boxplot
plt.savefig('./imgs/plot_rnn_rmse.png')
run()
LSTM预测字母序列
from keras.models import Sequential
from keras.layers import Dense, LSTM
import pandas as pd
import numpy as np
# 将char转换为长度为91的数组中的独热编码
def encode(pattern, n_unique):
encoded = list()
for value in pattern:
row = [0.0 for x in range(n_unique)]
index = ord(value)
row[ord(value)] = 1.0
encoded.append(row)
return encoded
# 将序列分出X和y
def to_xy_pairs(encoded):
X, y = list(), list()
for i in range(1,len(encoded)):
X.append(encoded[i-1])
y.append(encoded[i])
return X,y
# 将X, y 转换为LSTM可以理解的三维矩阵
def to_lstm_dataset(sequence, n_unique):
# 独热编码
encoded = encode(sequence, n_unique)
X,y = to_xy_pairs(encoded)
dfX, dfy = pd.DataFrame(X), pd.DataFrame(y)
lstmX = dfX.values
lstmX = lstmX.reshape(lstmX.shape[0],1,lstmX.shape[1])
lstmy = dfy.values
return lstmX, lstmy
seq1 = ['A','B','C','D','A']
seq2 = ['Z','B','C','D','Z']
n_unique = ord('Z') + 1
seq1X, seq1Y = to_lstm_dataset(seq1, n_unique) # seq1X:A B C D seq1Y:B C D A
seq2X, seq2Y = to_lstm_dataset(seq2, n_unique)
# 参数设置
n_neurons = 200
n_batch = 1
n_epoch = 1000
n_features = n_unique
# create LSTM
model = Sequential()
model.add(LSTM(n_neurons, batch_input_shape=(n_batch, 1, n_features), stateful=True))
model.add(Dense(n_unique, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam')
# train LSTM
for i in range(n_epoch):
model.fit(seq1X, seq1Y, epochs=1, batch_size=n_batch, verbose=1, shuffle=False)
model.reset_states()
model.fit(seq2X, seq2Y, epochs=1, batch_size=n_batch, verbose=0, shuffle=False)
model.reset_states()
# test LSTM on sequence 1
print('Sequence 1')
result = model.predict_classes(seq1X, batch_size=n_batch, verbose=0)
model.reset_states()
for i in range(len(result)):
print('X=%s y=%s, yhat=%s' % (seq1[i], seq1[i+1], chr(result[i])))
# test LSTM on sequence 2
print('Sequence 2')
result = model.predict_classes(seq2X, batch_size=n_batch, verbose=0)
model.reset_states()
for i in range(len(result)):
print('X=%s y=%s, yhat=%s' % (seq2[i], seq2[i+1], chr(result[i])))
结果图