写在前面:关于权重和偏置更新
权重W更新采用+或-的两种情况
- 可以很明显看到,关于权重更新存在2种情况,本质没什么区别,只不过在自己写代码手动更新权重和偏置时,这点就很容易混淆出错。本帖第一个单层感知机示例,采用第一种方式;第二个BPN示例,采用第二种方式;
- 通常情况下,推荐采用第二种。因为一般数学推导中都是写成W=W-dW,这样就和以前的帖子一致<线性回归、逻辑回归与正则化>,避免以后自我矛盾
- 关于偏置b更新,一般没有数学公式。可以简单看做b=w0,因而不予额外讲述。在本帖第二个代码示例中,b的更新就是采用b=b-db形式。第一个示例中没有b,仅设置了W
- 我仔细翻看了以往的代码示例,以前的代码都是采用了TF自带的优化器,我们只需设置W和b占位符、赋予Y和Yhat相应值即可,其他的由优化器自我完成。至于设置loss时,对于函数逼近问题,到现在一直只用了平方差(Y-Yhat还是Yhat-Y不重要);对于分类问题,交叉熵求解的内部细节我们不参与,因此以前这个问题不突出。而此帖,error和权重、偏置更新都由自己手动撸码而成,因此顺序、加减问题就很明显
- 养成良好习惯,一律采用图示第二种方式
TensorFlow实现单层感知机详解
简单感知机是一个单层神经网络。它使用阈值激活函数,正如 Marvin Minsky 在论文中所证明的,它只能解决线性可分的问题(线性可分的定义参见我的别的帖子)。虽然这限制了单层感知机只能应用于线性可分问题,但它具有学习能力已经很好了。当感知机使用阈值激活函数时,不能使用TensorFlow优化器来更新权重(根据上一帖可知,阈值激活函数并不可微,自然不能使用TF中定义的优化器,需要自己编写规则更新权重)。我们将不得不使用权重更新规则:
η 是学习率。为了简化编程,当输入固定为 +1 时,偏置可以作为一个额外的权重。那么,上面的公式可以用来同时更新权重和偏置。
- 仔细研究下面程序,搞清楚为什么这个式子可以用来更新权重。(核心原因就是,Y-Yhat<0,即Yhat过大,则dW为负,W=W+dW变小,促使Yhat缩小;反之亦反)
- 研读这个程序和MNIST计算过程(mnist.data每一行是一个图片样本,shape(W)=(m,n)=(784,10), shape(B)=(n,1)=(10,1)),再结合神经元图,就可以很明晰一个结论:输入X中,每一行就是一个样本,X的行数M就是第零层输入层(虚拟节点)和第一层神经元的个数M,第一层中一个神经元用来承接一个样本。每个样本有m个输入的元素即[x1,x2...xm],相应和m个权重数wi相乘,故W有m行。若有n个输出,接着再和n个偏置数bi相加,故B有n行。
- 对于回归问题,只有一个输出n=1,故B常为一个数;对于分类问题,会有多个输出结果,如MNIST10个分类n=10,故B为n行1列。(注意为n行1列,而非1行n列,是因为加减法法则缘故,X*W所得大小(55000,10)的张量可以和大小(10,1)的B张量相加减,具体见短句汇总帖)
- 程序中只创建了一个W和b,故第一层所有神经元所用的W和b张量都是一样的。后续层也是这样吗?估计是的,查一查
- 综上所述:X大小(M,m),表M个样本,每个样本m个元素,第一层网络有M个神经元;W大小(m,n),表每个神经元有m个权重数wi,n个分类,计算X*W后和n个偏置数bi相加
# tensorflow实现单层感知机,是一个单层神经网络。
# 它使用阈值激活函数,只能解决线性可分的问题,不能使用TensorFlow优化器来更新权重
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
# from Activation_Function_Threshold import threshold
def threshold(x):
# tf.less返回两个张量各元素比较(x<y)得到的真假值组成的张量,如[False True],参见语句汇总
cond = tf.less(x, tf.zeros(tf.shape(x), dtype=x.dtype))
# tf.where有三个参数,根据第一个条件是否成立,当为True的时候选择第二个参数中的值,否则使用第三个参数中的值。参见语句汇总
out = tf.where(cond, tf.zeros(tf.shape(x)), tf.ones(tf.shape(x)))
return out
# 1.定义要使用的超参数:
# hyper parameters
learn_rate = 0.4
min_err = 1e-3 # minmum accepted error
max_epochs = 100 # maximum epochs
# 2.指定训练数据。在这个例子中,取三个输入神经元(A,B,C)并训练它学习逻辑 AB+BC:
# traning data Y=AB+BC, sum of two linear functions
# 疑问,AB算是线性函数?线性X线性=线性? 好像多项式都算线性?
T, F = 1.0, 0.0
X_in = [[T, T, T, T],
[T, T, F, T],
[T, F, T, T],
[T, F, F, T],
[F, T, T, T],
[F, T, F, T],
[F, F, T, T],
[F, F, F, T]]
Y = [[T],
[T],
[F],
[F],
[T],
[F],
[F],
[F]]
# 3.定义要用到的变量和用于计算更新的计算图,最后执行计算图
W = tf.Variable(tf.random_normal([4, 1], stddev=2, seed=0))
h = tf.matmul(X_in, W)
Y_hat = threshold(h)
error = Y - Y_hat
mean_error = tf.reduce_mean(tf.square(error))
# 用mean_err作为判断标准,但不用来修订dW
dW = learn_rate * tf.matmul(X_in, error, transpose_a=True)
# shape(dW)=[4,1]=倒置[8,4]*[8,1]=[4,8]*[8,1]
train = tf.assign(W, W + dW)
init = tf.global_variables_initializer()
err = 1
epoch = 0
with tf.Session() as sess:
sess.run(init)
while err > min_err and epoch < max_epochs:
epoch += 1
err, _ = sess.run([mean_error, train])
# print(np.shape(error))
print('epoch:{0} mean error: {1}'.format(epoch, err))
print('Training complete')
那么,如果使用 Sigmoid 激活函数,而不是阈值激活函数,会发生什么?你猜对了,首先,可以使用 TensorFlow 优化器来更新权重。其次,网络将表现得像逻辑回归。
TensorFlow实现反向传播算法详解
反向传播(BPN)算法是神经网络中研究最多、使用最多的算法之一,它用于将输出层中的误差传播到隐藏层的神经元,然后用于更新权重。学习 BPN 算法可以分成以下两个过程:
- 正向传播:输入被馈送到网络,信号从输入层通过隐藏层传播到输出层。在输出层,计算误差和损失函数。
- 反向传播:在反向传播中,首先计算输出层神经元损失函数的梯度,然后计算隐藏层神经元损失函数的梯度。接下来用梯度更新权重。
这两个过程重复迭代直到收敛。
实现公式
首先给网络提供 M 个训练对(X,Y),X 为输入,Y 为期望的输出。输入通过激活函数 g(h) 和隐藏层传播到输出层。输出 Yhat是网络的输出,得到 error=Y-Yhat。其损失函数 J(W) 为
其中,i 取遍所有输出层的神经元(1 到 N)。然后可以使用 J(W) 的梯度并使用链式法则求导,来计算连接第 i 个输出层神经元到第 j 个隐藏层神经元的权重 Wij的变化
这里,Oj是隐藏层神经元的输出,h 表示隐藏层的输入值。这很容易理解,但现在怎么更新连接第 n 个隐藏层的神经元 k 到第 n+1 个隐藏层的神经元 j 的权值 Wjk?过程是相同的:将使用损失函数的梯度和链式法则求导,但这次计算 Wjk:
现在已经有方程了,看看如何在TensorFlow中做到这一点。在这里,还是使用 MNIST 数据集(http://yann.lecun.com/exdb/MNIST/)。
具体实现过程
对照标记理解程序即可。一个疑问是,程序中并不存在除以M求平均,难道多阶张量自动体现这个意味?
此外,偏置b的更新梯度数学式是怎么推导的?查一下。这里根据程序总结出更新公式,有意思的是更新b的程序中体现了平均之意
# MNIST数据集是分类问题的标准。本例用TensorFlow实现反向传播算法
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets(
'A:/software/Anaconda3/Lib/site-packages/tensorflow/examples/tutorials/mnist/MNIST_data', one_hot=True)
# 1. 定义超参数和其他常量。
# 每个手写数字的尺寸是 28×28=784 像素。数据集被分为 10 类,以 0 到 9 之间的数字表示。这两点是固定的。
# 学习率、最大迭代周期数、每次批量训练的批量大小以及隐藏层中的神经元数量都是超参数。
# 可以通过调整这些超参数,看看它们是如何影响网络表现的:
n_input = 784
n_classes = 10
max_epochs = 1000
learn_rate = 0.5
batch_size = 10
seed = 0
n_hidden = 30 # Number of neurons in the hidden layer
# 2. 定义Sigmoid函数s(x)的导数来进行权重更新,其导数为s(x)*(1-s(x))
def sigmaprime(x):
return tf.multiply(tf.sigmoid(x), tf.subtract(tf.constant(1.0), tf.sigmoid(x)))
# 3. 为训练数据创建占位符
x_in = tf.placeholder(tf.float32, [None, n_input])
y = tf.placeholder(tf.float32, [None, n_classes])
# 4. 创建模型
def multilayer_perceptron(x, weights, biases):
# Hidder layer with RELU activation
h_layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['h1'])
out_layer_1 = tf.sigmoid(h_layer_1)
# output layer with linear activation
h_out = tf.add(tf.matmul(out_layer_1, weights['out']), biases['out'])
return tf.sigmoid(h_out), h_out, out_layer_1, h_layer_1
# 5. 定义权重和偏置变量
weights = {'h1': tf.Variable(tf.random_normal([n_input, n_hidden], seed=seed)),
'out': tf.Variable(tf.random_normal([n_hidden, n_classes], seed=seed))}
biases = {'h1': tf.Variable(tf.random_normal([1, n_hidden], seed=seed)),
'out': tf.Variable(tf.random_normal([1, n_classes], seed=seed))}
# 6. 为正向传播、误差、梯度和更新计算创建计算图:
# Forward pass
y_hat, h_2, o_1, h_1 = multilayer_perceptron(x_in, weights, biases)
# error
err = y_hat - y
# backward pass
delta_2 = tf.multiply(err, sigmaprime(h_2))
delta_w_2 = tf.matmul(tf.transpose(o_1), delta_2)
wtd_error = tf.matmul(delta_2, tf.transpose(weights['out']))
delta_1 = tf.multiply(wtd_error, sigmaprime(h_1))
delta_w_1 = tf.matmul(tf.transpose(x_in), delta_1)
eta = tf.constant(learn_rate)
# 7.更新权重update weights
# 注意为什么这里是 W=W-dW,而不是W=W+dW。因为当err=y_hat-y,就是W=W-dW,当err=y-y_hat,则W=W+dW
step = [tf.assign(weights['h1'], tf.subtract(weights['h1'], tf.multiply(eta, delta_w_1))),
tf.assign(biases['h1'], tf.subtract(biases['h1'], tf.multiply(eta, tf.reduce_mean(delta_1, axis=[0])))),
tf.assign(weights['out'], tf.subtract(weights['out'], tf.multiply(eta, delta_w_2))),
tf.assign(biases['out'], tf.subtract(biases['out'], tf.multiply(eta, tf.reduce_mean(delta_2, axis=[0])))),]
# 8.定义计算精度accuracy和初始化
acc_mat = tf.equal(tf.argmax(y_hat, 1), tf.argmax(y, 1))
accuracy = tf.reduce_sum(tf.cast(acc_mat, tf.float32))
init = tf.global_variables_initializer()
# 9.执行
with tf.Session() as sess:
sess.run(init)
for epoch in range(max_epochs):
batch_xs, batch_ys = mnist.train.next_batch(batch_size)
sess.run(step, feed_dict={x_in: batch_xs, y: batch_ys})
if epoch % 100 == 0:
acc_test = sess.run(accuracy, feed_dict={x_in: mnist.test.images, y: mnist.test.labels})
acc_train = sess.run(accuracy, feed_dict={x_in: mnist.train.images, y: mnist.train.labels})
# 训练集和测试集样本大小分别为55000和10000,这里所求精度即向满分55000和10000靠齐,除以550和100就可获得相对百分数
# 也有写600的,认为训练集为60000,但是我验证了,是train=55000, validation=5000
print('Epoch:{0} accuracy train%:{1} accuracy test%: {2}'.format(epoch, acc_train / 550, (acc_test / 100)))
解读分析
在这里,训练网络时的批量大小为batchsize=10,如果增加批量的值,网络性能就会下降。(为什么会下降?不理解)另外,需要在测试数据集上检测训练好的网络的精度,这里测试数据集的大小是 10000。
单隐藏层多层感知机在训练数据集上的准确率为 84.45,在测试数据集上的准确率为 92.1。这是好的,但不够好。MNIST 数据集被用作机器学习中分类问题的基准。接下来,看一下如何使用 TensorFlow 的内置优化器影响网络性能。
推荐阅读
- MNIST 数据集:http://yann.lecun.com/exdb/mnist/
- 反向传播算法的简化讲解:http://neuralnetworksanddeeplearning.com/chap2.html
- 反向传播算法的另一个直观解释:http://cs231n.github.io/optimization-2/
- 反向传播算法的详细信息,以及如何将它应用于不同的网络:https://page.mi.fu-berlin.de/rojas/neural/chapter/K7.pdf
|
|