简单感知机是一个单层神经网络。它使用阈值激活函数,正如 Marvin Minsky 在论文中所证明的,它只能解决线性可分的问题(线性可分的定义参见我的别的帖子)。虽然这限制了单层感知机只能应用于线性可分问题,但它具有学习能力已经很好了。当感知机使用阈值激活函数时,不能使用TensorFlow优化器来更新权重(根据上一帖可知,阈值激活函数并不可微,自然不能使用TF中定义的优化器,需要自己编写规则更新权重)。我们将不得不使用权重更新规则:
# tensorflow实现单层感知机,是一个单层神经网络。
# 它使用阈值激活函数,只能解决线性可分的问题,不能使用TensorFlow优化器来更新权重
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt# from Activation_Function_Threshold import thresholddef threshold(x):# tf.less返回两个张量各元素比较(x
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
那么,如果使用 Sigmoid 激活函数,而不是阈值激活函数,会发生什么?你猜对了,首先,可以使用 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/)。
具体实现过程
# 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 activationh_layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['h1'])out_layer_1 = tf.sigmoid(h_layer_1)# output layer with linear activationh_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=5000print('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 的内置优化器影响网络性能。
推荐阅读