在看文章时,一篇文章提到了使用elman神经网络来对癫痫病人的脑电信号与正常人的脑电信号进行区分,并且取得了较好的分类结果。于是就想自己写一个elman神经网络demo看看效果。
elman神经网络和感知机的差别通过下面的图片可以很明显的看出哪里不一样,elman在隐藏层多了一个“context units“,用来保存隐藏层的输出,并作用到下一次隐藏层的计算中,关于elman原理的说明,大家可以自己查阅一些资料,这里不再赘述。(图片来自维基百科https://en.wikipedia.org/wiki/Recurrent_neural_network)
"""
coding: utf-8
@author: zhangxiang
"""
"""
在对脑电信号进行分类的时候,发现一篇文章对健康人,癫痫患者未发作时的脑电信号和癫痫发作时的脑电信号的分类使用了基于时序的
elman_RNN 神经网络进行建模,于是想在预测麻醉深度类别及其它时序相关的分类问题上使用这一模型。
就写了一个demo
"""
import numpy as np
class ELMAN_RNN(object):
def __init__(self, input_num, hidden_num, output_num, learning_rate):
self.input_num = input_num
self.hidden_num = hidden_num
self.output_num = output_num
self.learning_rate = learning_rate
self.hidden_weights = np.random.random((self.input_num, self.hidden_num))
self.output_weights = np.random.random((self.hidden_num, self.output_num))
self.rnn_weights = np.random.random((self.hidden_num, self.hidden_num))
self.hidden_bias = np.random.rand(1)
self.output_bias = np.random.rand(1)
self.hidden_output = np.zeros((1, self.hidden_num))
def training(self, train_input, train_output):
"""training one time"""
output = self.feed_forward(train_input)
self.bptt(train_input, output, train_output)
def calculate_the_cross_entropy(self, training_set):
"""get the total error loss"""
loss = 0
for i in range(np.array(training_set).shape[0]):
x, y = training_set[i]
y = np.array(y).reshape(1,2)
result = self.feed_forward(x)
loss += self.get_the_total_error(y, result)
return loss
def get_the_total_error(self, y, result):
"""loss = -∑yi*ln(ai), y is the real label, result is the softmax result"""
loss = -np.sum(y*np.log(result))
return loss
def feed_forward(self, input):
"""calculate feed_forward value"""
self.hidden_output = self.sigmoid(np.dot(np.array(input).reshape(1,2), self.hidden_weights) + np.dot(self.hidden_output, self.rnn_weights) + self.hidden_bias)
return self.softmax(np.dot(self.hidden_output, self.output_weights) + self.output_bias)
def bptt(self,input, output, train_output):
"""update the weights of all layers"""
# claculate delta of output layers
delta_of_output_layers = [0]*self.output_num
for i in range(self.output_num):
delta_of_output_layers[i] = self.calculate_output_wrt_rawout(output[0, i], train_output[i])
# caculate delta of hidden layers
delta_of_hidden_layers = [0]*self.hidden_num
for i in range(self.hidden_num):
d_error_wrt_hidden_output = 0.0
for o in range(self.output_num):
d_error_wrt_hidden_output += delta_of_output_layers[o]*self.output_weights[i, o]
delta_of_hidden_layers[i] = d_error_wrt_hidden_output*self.calculate_output_wrt_netinput(self.hidden_output[0,i])
# get the δw of output layers and update the weights
for i in range(self.output_num):
for weight_j in range(self.output_weights.shape[0]):
delta_wrt_weight_j = delta_of_output_layers[i]*self.hidden_output[0,weight_j]
self.output_weights[weight_j, i] -= self.learning_rate*delta_wrt_weight_j
# get the δw of hidden layers and update the weights
for i in range(self.hidden_num):
for weight_j in range(self.hidden_weights.shape[0]):
delta_wrt_weight_j = delta_of_hidden_layers[i]*input[weight_j]
self.hidden_weights[weight_j, i] -= self.learning_rate*delta_wrt_weight_j
def sigmoid(self, x):
"""activation function"""
return 1.0/(1.0 + np.exp(-x))
def softmax(self, x):
"""the activation for multiple output function"""
return np.exp(x)/np.sum(np.exp(x))
def calculate_output_wrt_rawout(self, output, train_output):
"""derivative of softmax function, actually in classification train_output equal to 1"""
return (output - train_output)
def calculate_output_wrt_netinput(self, output):
"""the derivative of sigmoid function"""
return output*(1 - output)
if __name__ == "__main__":
import matplotlib.pyplot as plt
elman = ELMAN_RNN(input_num=2, hidden_num=4, output_num=2, learning_rate=0.02)
train_x = [[1,2], [1,1], [1.5, 1.5], [2,1], [-1,-1], [-0.5, -0.5], [-1, -2], [-2, -1.5]]
label_y = [[1,0], [1,0], [1,0], [1,0], [0,1], [0,1], [0,1], [0,1]]
training_sets = [[[2,2],[1,0]], [[0.2, 0.8], [1,0]], [[-0.5, -0.8], [0, 1]], [[-1.2, -0.5], [0, 1]]]
loss = []
for i in range(1000):
for x, y in zip(train_x, label_y):
elman.training(x, y)
loss.append(elman.calculate_the_cross_entropy(training_sets))
plt.figure()
plt.plot(loss)
plt.title('the loss with the training')
plt.show()
print('training finished!')
loss函数的变化如下: