循环神经网络
用来处理序列数据的神经网络,被称为循环神经网络(RNNS)。
在传统的神经网络模型中,层与层之间是全连接的,每层的节点之间是无连接的,因此无法高效处理序列问题。
处理序列数据的神经网络之所以称为循环神经网络,是因为一个序列当前的输出与前面的输出有关。具体的表现形式为:网络会对前面的信息进行记忆,并应用与当前输出的计算中,即隐藏层之间的节点不再是无连接的,而是有连接的,并且隐藏层的输入不仅包括输入层的输出,还包括上一时刻隐藏层的输出。
用于训练前馈神经网络的主要技术是反向传播,使用计算错误的数据来更新网络权重。在循环神经网络中,因为神经元的重复连接或循环连接,反向传播不适用,不能很好地完成网络权重的更新,这需要时间反向传播技术(BPTT)来解决循环神经网络的反向传播问题。
当反向传播用于非常深的神经网络和循环神经网络时,为了更新权重而需要计算的梯度变得不稳定,它们可能变成非常大的数值(梯度爆炸)或者非常小的数值(梯度消失)。使用这些数据来更新网络中的权重,使训练变得不稳定,并且使神经网络生成的模型不可靠。
在多层感知器神经网络中,通过使用激活函数使这个问题得到缓解,甚至使用无监督的预训练层来环节这个问题;在循环神经网络架构中,使用长短期记忆网络(LSTM)可以缓解这个问题。
LSTM已被证明,对处理从自然语言翻译到各样图像和视频自动字幕等序列问题具有非常良好的结果。目前被广泛应用在自然语言翻译、控制机器人、图像分析、文档摘要、语音识别、图像识别、手写识别、聊天机器人、预测疾病、点击率、股票、合成音乐等领域。
序列问题分类
一对多:序列输出,用于图像字幕。
多对一:序列输入,用于情感分类。
多对多:序列输入和输出,用于机器翻译。
同步多对多:同步序列输入和输出,用于视频分类。
多层感知器的时间序列预测
先编写一个简单的函数将单列数据转换为两列数据集,第一列包含当月(t)的旅客数,第二列包含下个月(t
+1)的旅客数。
窗口方法:使用多个时间项来进行下一个时间项的预测。
import numpy as np
from pandas import read_csv
from matplotlib import pyplot as plt
import math
from keras.models import Sequential
from keras.layers import Dense
#多层感知器
seed = 7
batch_size = 2
epochs = 200
filename = 'international-airline-passengers.csv'
footer = 3
look_back = 1
# look_back = 3 使用窗口方法的多层感知器,即t-2、t-1、t
def create_dataset(dataset):
dataX,dataY = [],[]
for i in range(len(dataset) - look_back -1):
x = dataset[i:i+look_back, 0]
dataX.append(x)
y = dataset[i+look_back, 0]
dataY.append(y)
print('X: %s, Y: %s' % (x,y))
return np.array(dataX),np.array(dataY)
def build_model():
model = Sequential()
# model.add(Dense(units=12,input_dim=look_back,activation='relu')) 窗口方法
model.add(Dense(units=8,input_dim=look_back,activation='relu'))
model.add(Dense(units=1))
model.compile(loss='mean_squared_error',optimizer='adam')
return model
if __name__ == '__main__':
np.random.seed(seed)
data = read_csv(filename,usecols=[1],engine='python',skipfooter=footer)
dataset = data.values.astype('float32')
train_size = int(len(dataset)*0.67)
validation_size = len(dataset) - train_size
train,validation = dataset[0:train_size,:],dataset[train_size:len(dataset),:]
X_train,y_train = create_dataset(train)
X_validation,y_validation = create_dataset(validation)
model = build_model()
model.fit(X_train,y_train,epochs=epochs,batch_size=batch_size,verbose=0)
train_score = model.evaluate(X_train,y_train,verbose=0)
print('Train Score: %.2f MSE (%.2f RMSE)' % (train_score,math.sqrt(train_score)))
validation_score = model.evaluate(X_validation,y_validation,verbose=0)
print('Validation Score: %.2f MSE (%.2f RMSE)' % (validation_score,math.sqrt(validation_score)))
predict_train = model.predict(X_train)
predict_validation = model.predict(X_validation)
predict_train_plot = np.empty_like(dataset)
predict_train_plot[:,:] = np.nan
predict_train_plot[look_back:len(predict_train)+look_back,:] = predict_train
predict_validation_plot = np.empty_like(dataset)
predict_validation_plot[:,:] = np.nan
predict_validation_plot[len(predict_train)+look_back*2+1:len(dataset)-1,:] = predict_validation
plt.plot(dataset,color='blue')
plt.plot(predict_train_plot,color='green')
plt.plot(predict_validation_plot,color='red')
plt.show()
LSTM方法
使用时间步长的LSTM回归
在为LSTM神经网络准备数据时,数据中包括时间步长,并设置为相同的数值。但是在一些序列问题中,可能每个样本都有不同的时间步长。(如对机器故障进行测量,每个观测到的故障点都是一个样本,观察到的结果是故障间隔的时间步长,观察到的变量是特征。)
时间步长提供了另一种方式来描述时间序列问题,与上面的窗口方法相同,将时间序列中先前的时间步长作为输入,来预测下一个时间步长的输出,而不是将过去的观察结果作为单独的输入特征。
这种将时间不长作为输入特征的方法,的确能够提高时间序列问题预测的精确度。
在对数据进行reshape处理时,将列设置为时间步长,并将其设置为1.
LSTM的批次间记忆
通常情况下,每个训练批次之后,或者调用model.predict()或者model.evaluate()函数之后,网络的状态都会重新设置。在Keras中,通过设置LSTM层的stateful为True,来保存LSTM层的内部状态,从而获得更好的控制。
为了保持状态,要求在训练神经网络模型时不忽略数据。这需要每个epoch训练结束后,通过调用model.reset_states()函数,显式地充值网络状态,即创建外循环epoch,并在每个epoch中调用model.fit()和model.reset_state()函数。
import numpy as np
from matplotlib import pyplot as plt
from pandas import read_csv
import math
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
seed = 7
batch_size = 1
epochs = 100
filename = 'international-airline-passengers.csv'
footer = 3
look_back = 1
# look_back = 3 窗口方法
def create_dataset(dataset):
dataX,dataY = [],[]
for i in range(len(dataset)-look_back-1):
x = dataset[i:i+look_back,0]
dataX.append(x)
y = dataset[i+look_back,0]
dataY.append(y)
print('X: %s, Y: %s' % (x,y))
return np.array(dataX),np.array(dataY)
def build_model():
model = Sequential()
model.add(LSTM(units=4,input_shape=(1,look_back)))
# 批次间记忆
# model.add(LSTM(units=4,batch_input_shape=(batch_size,look_back,1),stateful=True))
# 堆叠批次间记忆
# model.add(LSTM(units=4,batch_input_shape=(batch_size,look_back,1),stateful=True,return_sequences=True))
# model.add(LSTM(units=4,batch_input_shape=(batch_size,look_back,1),stateful=True)))
model.add(Dense(units=1))
model.compile(loss='mean_squared_error',optimizer='adam')
return model
if __name__ == '__main__':
np.random.seed(seed)
data = read_csv(filename,usecols=[1],engine='python',skipfooter=footer)
dataset = data.values.astype('float32')
scaler = MinMaxScaler()
dataset = scaler.fit_transform(dataset)
train_size = int(len(dataset)*0.67)
validation_size = len(dataset) - train_size
train,validation = dataset[0:train_size,:],dataset[train_size:len(dataset),:]
X_train,y_train = create_dataset(train)
X_validation,y_validation = create_dataset(validation)
X_train = np.reshape(X_train,(X_train.shape[0],1,X_train.shape[1]))
X_validation = np.reshape(X_validation,(X_validation.shape[0],1,X_validation.shape[1]))
# 使用时间步长/批次间记忆
# X_train = np.reshape(X_train,(X_train.shape[0],X_train.shape[1],1))
# X_validation = np.reshape(X_validation,(X_validation.shape[0],X_validation.shape[1],1))
model = build_model()
model.fit(X_train,y_train,epochs=epochs,batch_size=batch_size,verbose=2)
predict_train = model.predict(X_train)
predict_validation = model.predict(X_validation)
'''
批次间记忆
for i in range(epochs):
history = model.fit(X_train,y_train,epochs=epochs,batch_size=batch_size,verbose=0,shuffle=False)
mean_loss = np.mean(history.history['loss'])
print('mean loss %.5f for loop %s' % (mean_loss,str(i)))
model.reset_states()
predict_train = model.predict(X_train,batch_size=batch_size)
model.reset_states()
predict_validation = model.predict(X_validation,batch_size=batch_size)
'''
predict_train = scaler.inverse_transform(predict_train)
y_train = scaler.inverse_transform([y_train])
predict_validation = scaler.inverse_transform(predict_validation)
y_validation = scaler.inverse_transform([y_validation])
train_score = math.sqrt(mean_squared_error(y_train[0],predict_train[:,0]))
print('Train Scores: %.2f RMSE' % train_score)
validation_score = math.sqrt(mean_squared_error(y_validation[0],predict_validation[:,0]))
print('Validation Score: %.2f RMSE' % validation_score)
predict_train_plot = np.empty_like(dataset)
predict_train_plot[:,:] = np.nan
predict_train_plot[look_back:len(predict_train)+look_back,:] = predict_train
predict_validation_plot = np.empty_like(dataset)
predict_validation_plot[:,:] = np.nan
predict_validation_plot[len(predict_train)+look_back*2+1:len(dataset)-1,:] = predict_validation
dataset = scaler.inverse_transform(dataset)
plt.plot(dataset,color='blue')
plt.plot(predict_train_plot,color='green')
plt.plot(predict_validation_plot,color='red')
plt.show()
混合使用LSTM和CNN
卷积神经网络对稀疏结构的数据处理非常有效。IMDB影评数据确实在评价的单词序列中具有一维稀疏空间结构,CNN能够挑选出不良情绪的不变特征。通过CNN学习后的空间特征,可以被LSTM层学习为序列。在词嵌入层之后,可以通过添加一维CNN和最大池化层,将合并的特征提供给LSTM。
from keras.datasets import imdb
from keras.preprocessing import sequence
from keras.layers.embeddings import Embedding
from keras.layers import Dense
from keras.models import Sequential
from keras.layers import LSTM,Dropout
from keras.layers.convolutional import Conv1D,MaxPooling1D
import numpy as np
seed = 7
top_words = 5000
max_words = 500
out_dimension = 32
batch_size = 128
epochs = 2
dropout_rate = 0.2
def build_model():
model = Sequential()
model.add(Embedding(top_words,out_dimension,input_length=max_words))
model.add(Conv1D(filters=32,kernel_size=3,padding='same',activation='relu'))
model.add(MaxPooling1D(pool_size=2))
# model.add(Dropout(dropout_rate))
model.add(LSTM(units=100))
# model.add(Dropout(dropout_rate))
model.add(Dense(units=1,activation='sigmoid'))
model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])
model.summary()
return model
if __name__ == '__main__':
np.random.seed(seed=seed)
(x_train,y_train),(x_validation,y_validation) = imdb.load_data(num_words=top_words)
x_train = sequence.pad_sequences(x_train,maxlen=max_words)
x_validation = sequence.pad_sequences(x_validation,maxlen=max_words)
model = build_model()
model.fit(x_train,y_train,batch_size=batch_size,epochs=epochs,verbose=2)
scores = model.evaluate(x_validation,y_validation,verbose=2)
print('Accuracy: %.2f%%' % (scores[1]*100))
(来自19年草稿箱)