神经网络前向传播和反向传播算法推导
一、目标
推导具有单隐层的神经网络的前向传播和反向传播算法,并进行编程(可以使用sklearn
中的神经网络)。
探讨10,30,100,300,1000,不同隐藏节点数对网络性能的影响。
探讨不同学习率和迭代次数对网络性能的影响。
改变数据的标准化方法,探讨对训练的影响。
查阅资料说明什么是Hebb
学习规则
二、推导单隐层神经网络的前向传播和反向传播算法 参考资料:https://blog.csdn.net/Lucky_Go/article/details/89738286
三、算法实现 参考资料:https://blog.csdn.net/zsx17/article/details/89342506
因为网上神经网络的代码基本都是用tensorflow
实现的,这里是直接调库。在完成了作业的基本要求之后我也尝试了自己实现单隐层神经网络的代码(在实验报告的后部分)。
1. 载入数据 import numpy as npimport tensorflow as tfimport tensorflow.examples.tutorials.mnist.input_data as input_datamnist = input_data.read_data_sets('MNIST_data/' , one_hot=True )
2. 建立模型 x = tf.placeholder(tf.float32, [None , 784 ], name='X' ) y = tf.placeholder(tf.float32, [None , 10 ], name='Y' ) H1_NN = 256 W1 = tf.Variable(tf.random_normal([784 , H1_NN])) b1 = tf.Variable(tf.zeros([H1_NN])) Y1 = tf.nn.relu(tf.matmul(x, W1) + b1) W2 = tf.Variable(tf.random_normal([H1_NN, 10 ])) b2 = tf.Variable(tf.zeros([10 ])) forward = tf.matmul(Y1, W2) + b2 pred = tf.nn.softmax(forward)
3. 训练模型 loss_function = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=forward, labels=y)) train_epochs = 40 batch_size = 50 total_batch = int (mnist.train.num_examples / batch_size) display_step = 1 learning_rate = 0.01 optimizer = tf.train.AdamOptimizer(learning_rate).minimize(loss_function) correct_prediction = tf.equal(tf.argmax(y, 1 ), tf.argmax(pred, 1 )) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) from time import timestartTime = time() sess = tf.Session() sess.run(tf.global_variables_initializer()) for epoch in range (train_epochs): for batch in range (total_batch): xs, ys = mnist.train.next_batch(batch_size) sess.run(optimizer, feed_dict={x: xs, y: ys}) loss, acc = sess.run([loss_function, accuracy], feed_dict={x: mnist.validation.images, y: mnist.validation.labels}) if (epoch + 1 ) % display_step == 0 : print ('训练轮次:' , '%02d' % (epoch + 1 ), '损失:' , '{:.9f}' .format (loss), '准确率:' , '{:.4f}' .format (acc)) print ('训练结束' )duration = time() - startTime print ("总运行时间为:" , "{:.2f}" .format (duration))
4. 模型评估 accu_test = sess.run(accuracy, feed_dict={x: mnist.test.images, y: mnist.test.labels}) print ('测试集准确率:' , accu_test)
5. 应用模型 prediction_result = sess.run(tf.argmax(pred, 1 ), feed_dict={x: mnist.test.images}) print ("前10项的结果:" , prediction_result[0 :10 ])compare_lists = prediction_result == np.argmax(mnist.test.labels, 1 ) print (compare_lists)err_lists = [i for i in range (len (compare_lists)) if compare_lists[i] == False ] print ('预测错误的图片:' , err_lists)print ('预测错误图片的总数:' , len (err_lists))import numpy as npdef print_predict_errs (labels, prediction ): count = 0 compare_lists = (prediction == np.argmax(labels, 1 )) err_lists = [i for i in range (len (compare_lists)) if compare_lists[i] == False ] for x in err_lists: print ('index=' + str (x) + '标签值=' , np.argmax(labels[x]), '预测值=' , prediction[x]) count = count + 1 print ("总计:" + str (count)) print_predict_errs(labels=mnist.test.labels, prediction=prediction_result) import matplotlib.pyplot as pltdef plot_images_labels_prediction (images, labels, predication, index, num=10 ): fig = plt.gcf() fig.set_size_inches(10 , 12 ) if num > 25 : num = 25 for i in range (0 , num): ax = plt.subplot(5 , 5 , i + 1 ) ax.imshow(np.reshape(images[index], (28 , 28 )), cmap='binary' ) title = 'label=' + str (np.argmax(labels[index])) if len (predication) > 0 : title += ",predict=" + str (predication[index]) ax.set_title(title, fontsize=10 ) ax.set_xticks([]) ax.set_yticks([]) index += 1 plt.show() plot_images_labels_prediction(mnist.test.images, mnist.test.labels, prediction_result, 10 , 25 ) plot_images_labels_prediction(mnist.test.images, mnist.test.labels, prediction_result, 610 , 20 )
6. 结果展示 上面的代码中隐层节点个数为256个,学习率为0.01,迭代次数为40次。训练结果如下:
部分分类图像如下所示:
四、算法调优 在上面的模型中隐层结点数为256,学习率为0.01,迭代次数为40次。
下面分别从隐层节点数、学习率和迭代次数三个角度进行调优。
1. 隐层节点数 将隐层节点数设为10,得到的结果如下图所示:
将隐层节点设为30,100,300,1000的效果不再具体展示,效果如下所示:
隐层节点个数
总运行时间/s
预测错误的图片数
准确率
10
46.29
736
0.9264
30
43.46
528
0.9472
100
59.06
343
0.9657
256
84.48
249
0.9751
300
76.64
269
0.9731
1000
302.27
240
0.976
由表可知,准确率随着隐层节点个数的增加而增加,增加速率逐步减少。
2. 学习率 学习率分别为0.005,0.01, 0.02, 0.1,隐层节点数选择256,迭代次数选择40。分类结果如下:
学习率
总运行时间/s
预测错误的图片数
准确率
0.005
78.81
231
0.9769
0.01
84.48
249
0.9751
0.02
69.72
446
0.9554
0.1
73.87
2561
0.7439
由表可知,准确率随着学习率的增加而降低。在学习率低于0.01时,图片分类的准确率提升的速率较小。
3. 迭代次数 迭代次数分别为20,40,100,隐层节点数选择256,学习率选择0.01。分类结果如下:
迭代次数
总运行时间/s
预测错误的图片数
准确率
20
37.12
307
0.9693
40
84.48
249
0.9751
100
184.39
239
0.9761
由表可知,迭代次数对总运行时间的影响率很大,准确率随着迭代次数的增加而增加,但对准确率起决定因素的还是隐层的节点个数以及学习率。
4. 改变数据标准化方法 最大-最小规范化 Z-score
规范化五、Hebb
学习规则 参考资料:https://baike.baidu.com/item/Hebb%E5%AD%A6%E4%B9%A0%E8%A7%84%E5%88%99/3061563?fr=aladdin
Hebb
学习规则是一个无监督学习规则,这种学习的结果是使网络能够提取训练集的统计特性,从而把输入信息按照它们的相似性程度划分为若干类。这一点与人类观察和认识世界的过程非常吻合,人类观察和认识世界在相当程度上就是在根据事物的统计特征进行分类。Hebb
学习规则只根据神经元连接间的激活水平改变权值,因此这种方法又称为相关学习或并联学习。
无监督学习规则 唐纳德·赫布(1904-1985)是加拿大著名生理心理学家。Hebb
学习规则与“条件反射”机理一致,并且已经得到了神经细胞学说的证实。 巴甫洛夫的条件反射实验:每次给狗喂食前都先响铃,时间一长,狗就会将铃声和食物联系起来。以后如果响铃但是不给食物,狗也会流口水。 受该实验的启发,Hebb的理论认为在同一时间被激发的神经元间的联系会被强化。比如,铃声响时一个神经元被激发,在同一时间食物的出现会激发附近的另一个神经元,那么这两个神经元间的联系就会强化,从而记住这两个事物之间存在着联系。相反,如果两个神经元总是不能同步激发,那么它们间的联系将会越来越弱。 Hebb
学习律可表示为: $W{ij}(t+1)=W {ij}(t)+a⋅yi⋅y_j$ $W {ij}(t+1)=W_{ij}(t)+a⋅y_i⋅y_j$
其中$W{ij}$表示神经元$j$到神经元$i$的连接权,$y_i$与$y_j$表示两个神经元的输出,$a$是表示学习速率的常数,如果$y_i$与$y_j$同时被激活,即$y_i$与$y_j$同时为正,那么$W {ij}$将增大。如果$yi$被激活,而$y_j$处于抑制状态,即$y_i$为正$y_j$为负,那么$W {ij}$将变小。
六、 自己实现单隐层神经网络 参考资料:https://blog.csdn.net/hellozhxy/article/details/81055391
网络结构的函数定义:
def layer_sizes (X, Y ): n_x = X.shape[0 ] n_h = 4 n_y = Y.shape[0 ] return (n_x, n_h, n_y)
参数初始化函数:
def initialize_parameters (n_x, n_h, n_y ): W1 = np.random.randn(n_h, n_x)*0.01 b1 = np.zeros((n_h, 1 )) W2 = np.random.randn(n_y, n_h)*0.01 b2 = np.zeros((n_y, 1 )) assert (W1.shape == (n_h, n_x)) assert (b1.shape == (n_h, 1 )) assert (W2.shape == (n_y, n_h)) assert (b2.shape == (n_y, 1 )) parameters = {"W1" : W1, "b1" : b1, "W2" : W2, "b2" : b2} return parameters
前向传播计算函数:
def forward_propagation (X, parameters ): W1 = parameters['W1' ] b1 = parameters['b1' ] W2 = parameters['W2' ] b2 = parameters['b2' ] Z1 = np.dot(W1, X) + b1 A1 = np.tanh(Z1) Z2 = np.dot(W2, Z1) + b2 A2 = sigmoid(Z2) assert (A2.shape == (1 , X.shape[1 ])) cache = {"Z1" : Z1, "A1" : A1, "Z2" : Z2, "A2" : A2} return A2, cache
计算损失函数:
def compute_cost (A2, Y, parameters ): m = Y.shape[1 ] logprobs = np.multiply(np.log(A2),Y) + np.multiply(np.log(1 -A2), 1 -Y) cost = -1 /m * np.sum (logprobs) cost = np.squeeze(cost) assert (isinstance (cost, float )) return cost
反向传播函数:
def backward_propagation (parameters, cache, X, Y ): m = X.shape[1 ] W1 = parameters['W1' ] W2 = parameters['W2' ] A1 = cache['A1' ] A2 = cache['A2' ] dZ2 = A2-Y dW2 = 1 /m * np.dot(dZ2, A1.T) db2 = 1 /m * np.sum (dZ2, axis=1 , keepdims=True ) dZ1 = np.dot(W2.T, dZ2)*(1 -np.power(A1, 2 )) dW1 = 1 /m * np.dot(dZ1, X.T) db1 = 1 /m * np.sum (dZ1, axis=1 , keepdims=True ) grads = {"dW1" : dW1, "db1" : db1, "dW2" : dW2, "db2" : db2} return grads
权值更新函数:
def update_parameters (parameters, grads, learning_rate = 1.2 ): W1 = parameters['W1' ] b1 = parameters['b1' ] W2 = parameters['W2' ] b2 = parameters['b2' ] dW1 = grads['dW1' ] db1 = grads['db1' ] dW2 = grads['dW2' ] db2 = grads['db2' ] W1 -= dW1 * learning_rate b1 -= db1 * learning_rate W2 -= dW2 * learning_rate b2 -= db2 * learning_rate parameters = {"W1" : W1, "b1" : b1, "W2" : W2, "b2" : b2} return parameters
最终的神经网络模型:
def nn_model (X, Y, n_h, num_iterations = 10000 , print_cost=False ): np.random.seed(3 ) n_x = layer_sizes(X, Y)[0 ] n_y = layer_sizes(X, Y)[2 ] parameters = initialize_parameters(n_x, n_h, n_y) W1 = parameters['W1' ] b1 = parameters['b1' ] W2 = parameters['W2' ] b2 = parameters['b2' ] for i in range (0 , num_iterations): A2, cache = forward_propagation(X, parameters) cost = compute_cost(A2, Y, parameters) grads = backward_propagation(parameters, cache, X, Y) parameters = update_parameters(parameters, grads, learning_rate=1.2 ) if print_cost and i % 1000 == 0 : print ("Cost after iteration %i: %f" %(i, cost)) return parameters