tensorflow 训练权重不更新_TensorFlow学习Program1——15.权重和偏置更新要点、单层感知机、反向传播算法B ...

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 10:39   25   0

写在前面:关于权重和偏置更新

e706dc9ecbdfa8b611e1e8045ba59d20.png
权重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中定义的优化器,需要自己编写规则更新权重)。我们将不得不使用权重更新规则:

333ba32356f12b21278712b2c488c270.png
η 是学习率。为了简化编程,当输入固定为 +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')

db1f912743ac22e9fbf61c5a04015602.png

那么,如果使用 Sigmoid 激活函数,而不是阈值激活函数,会发生什么?你猜对了,首先,可以使用 TensorFlow 优化器来更新权重。其次,网络将表现得像逻辑回归。

TensorFlow实现反向传播算法详解

反向传播(BPN)算法是神经网络中研究最多、使用最多的算法之一,它用于将输出层中的误差传播到隐藏层的神经元,然后用于更新权重。学习 BPN 算法可以分成以下两个过程:

  1. 正向传播:输入被馈送到网络,信号从输入层通过隐藏层传播到输出层。在输出层,计算误差和损失函数。
  2. 反向传播:在反向传播中,首先计算输出层神经元损失函数的梯度,然后计算隐藏层神经元损失函数的梯度。接下来用梯度更新权重。

这两个过程重复迭代直到收敛。

实现公式

首先给网络提供 M 个训练对(X,Y),X 为输入,Y 为期望的输出。输入通过激活函数 g(h) 和隐藏层传播到输出层。输出 Yhat是网络的输出,得到 error=Y-Yhat。其损失函数 J(W) 为

5d5e68bcef502cd50b23ea0b3f16ab8e.png

其中,i 取遍所有输出层的神经元(1 到 N)。然后可以使用 J(W) 的梯度并使用链式法则求导,来计算连接第 i 个输出层神经元到第 j 个隐藏层神经元的权重 Wij的变化

07ca514f1ec8b70106ba756a5ba5e707.png

这里,Oj是隐藏层神经元的输出,h 表示隐藏层的输入值。这很容易理解,但现在怎么更新连接第 n 个隐藏层的神经元 k 到第 n+1 个隐藏层的神经元 j 的权值 Wjk?过程是相同的:将使用损失函数的梯度和链式法则求导,但这次计算 Wjk:

69367d5c7c41e24eaab845d92e8726cf.png

现在已经有方程了,看看如何在TensorFlow中做到这一点。在这里,还是使用 MNIST 数据集(http://yann.lecun.com/exdb/MNIST/)。

具体实现过程

6bab5cf4769021c2ee1a9327ba1d6577.png
对照标记理解程序即可。一个疑问是,程序中并不存在除以M求平均,难道多阶张量自动体现这个意味?

f53a7586424062bc7c9be2d863f52e31.png
此外,偏置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
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP