深度学习神经网络 综合程序解析+参数优化(指数下降学习率+滑动平均+正则化)+问题分析

首先我们给出生成训练集的函数。目前我们使用的训练集的数据都是自己造的。我们把在半径为根2的圆内的点对标红。

#coding:utf-8
#0导入模块,生成模拟数据集
import numpy as np
import matplotlib.pyplot as plt
data_num = 1200
seed = 2
def generateds():
    #基于seed产生随机数
    rdm = np.random.RandomState(seed)
    #随机数返回300行2列的矩阵,表示300组坐标点(x0,x1)作为输入数据集
    X = rdm.randn(data_num,2)
    #从X这个300行2列的矩阵中取出一行,判断如果两个坐标的平方和小于2,给Y赋值1,其余赋值为0
    Y_ = [int(x0*x0+x1*x1<2) for (x0,x1) in X]
    #遍历Y中的每个元素,1赋值'red',其余赋值'blue',这样可视化显示时人可以直观区分
    Y_c = [['red'if y else 'blue'] for y in Y_]
    #对数据集X和标签Y进行形状整理,第一个元素为-1表示跟随第二列计算,第二个元素表示多少列,可见X为两列,Y为1列
    X = np.vstack(X).reshape(-1,2)
    Y_ = np.vstack(Y_).reshape(-1,1)

    #print(X)
    #print(Y_)
    #print(Y_c)
    # 用plt.scatter画出数据集X各行中第0列元素和第1列元素的点即各行的(x0,x1),用各行Y_c对应的值表示颜色(c是color的缩写).
    #plt.scatter(X[:, 0], X[:, 1], c=np.squeeze(Y_c))
    #plt.show()

    return X,Y_,Y_c

然后是构造前向传播的程序:

#coding:utf-8
#0导入模块,生成模拟数据集
import tensorflow as tf

#定义神经网络的输入,参数和输出,定义前向传播过程
def get_weight(shape,regularizer):
    w = tf.Variable(tf.random_normal(shape),dtype=tf.float32)
    tf.add_to_collection('losses',tf.contrib.layers.l2_regularizer(regularizer)(w))
    return w

def get_bias(shape):
    b = tf.Variable(tf.constant(0.01,shape=shape))
    return b

def forward(x,regularizer):
    w1 = get_weight([2,16],regularizer)
    b1 = get_bias([16])
    y1 = tf.nn.relu(tf.matmul(x,w1)+b1)

    w2 = get_weight([16, 1], regularizer)
    b2 = get_bias([1])
    y = tf.nn.relu(tf.matmul(y1, w2) + b2)

    return y

定义反向传播结构。方向传播其实就是降低损失函数用的。下一节会重点介绍一下反向传播的数学原理。

#coding:utf-8
#0导入模块,生成模拟数据集
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf

import generated
import forward

STEPS = 40000
BATCH_SIZE = 120
LEARNING_RATE_BASE = 0.001
LEARNING_RATE_DECAY = 0.999
REGULARIZER = 0.01
MOVINT_AVERAGE_DECAY = 0.99

def backward():
    x = tf.placeholder(tf.float32,shape=(None,2))
    y_ = tf.placeholder(tf.float32,shape=(None,1))

    X,Y_,Y_c = generated.generateds()

    y = forward.forward(x,REGULARIZER)

    global_step = tf.Variable(0,trainable=False)

    learning_rate = tf.train.exponential_decay(
        LEARNING_RATE_BASE,
        global_step,
        generated.data_num/BATCH_SIZE,
        LEARNING_RATE_DECAY,
        staircase=True)

    #定义损失函数
    loss_mse = tf.reduce_mean(tf.square(y - y_))
    loss_total = loss_mse + tf.add_n(tf.get_collection('losses'))  # add_n:把losses里面的值相加。

    # 定义反向传播方法:包含正则化
    train_step = tf.train.AdamOptimizer(learning_rate).minimize(loss_total)

    """" """
    # 实例化滑动平均类,给删减率为0.99,当前轮数为global_step
    ema = tf.train.ExponentialMovingAverage(MOVINT_AVERAGE_DECAY, global_step)
    # ema.apply后的括号里是更新列表,每次运行sess.fun(ema_op)时,对更新列表中的元素求滑动平均值
    # 在实际应用中会使用tf.trainable_variable()自动将所有待训练的参数汇总为列表
    # 这里因为只有一个参数,所以直接用apply([w1])也是可以的
    # ema_op = ema.apply([w1])
    ema_op = ema.apply(tf.trainable_variables())
    # 反向传播更新参数之后,再更新每一个参数的滑动平均值,用下面的代码可以一次完成这两个操作
    with tf.control_dependencies([train_step, ema_op]):
        train_op = tf.no_op(name="train")
    #设置完使用滑动平均模型之后,只需要在每次使用反向传播的时候改为使用run.(train_op)就可以正常执行了。


    with tf.Session() as sess:
        init_op = tf.global_variables_initializer()
        sess.run(init_op)
        for i in range(STEPS):
            start = (i * BATCH_SIZE) % generated.data_num
            end = start + BATCH_SIZE
            sess.run(train_op, feed_dict={x: X[start:end], y_: Y_[start:end]})
            if i % 2000 == 0:
                loss_total_v = sess.run(loss_total, feed_dict={x: X, y_: Y_})
                print("After " + str(i) + " steps, loss is: " + str(loss_total_v))
        # xx在-3到3之间以步长为0.01,yy在-3到3之间以步长为0.01,生成二维网格坐标点
        xx, yy = np.mgrid[-3:3:.01, -3:3:.01]
        # 将xx,yy拉直,并合并成一个2列的矩阵,得到一个网格坐标点的集合
        grid = np.c_[xx.ravel(), yy.ravel()]
        # 将网格坐标点喂入神经网络,probs为输出
        probs = sess.run(y, feed_dict={x: grid})
        # probs的shape调整成xx的样子
        probs = probs.reshape(xx.shape)
        #print("w1: " + str(sess.run(w1)))
        #print("b1: " + str(sess.run(b1)))
        #print("w2: " + str(sess.run(w2)))
        #print("b2: " + str(sess.run(b1)))

    plt.scatter(X[:, 0], X[:, 1], c=np.squeeze(Y_c))
    plt.contour(xx, yy, probs, levels=[.5])
    plt.show()


backward()

在训练的过程中,一般是正常的,如下所示:

但是有时候我们训练中损失函数可能会被卡在某个地方(参数不变),就像这样:

而这种问题出现的时候,一般一开始损失函数值都比较大,可能是指数衰减的学习率导致进入了一个局部最小解。

我们把学习率改成不变的,设置为0.001,仍然会被卡在相同的损失函数值下。我猜测是因为一开始的损失函数太大的时候,参数都集中在了别的某个位置,没法达到正常情况。

我们在把这些网格点喂进训练好的神经网络以后不但输出最后的等高线图,还把数据打印出来(这里我们的网格只用横纵坐标都是-1到1之间的数,这样生成的结果理想情况应该都是大于0.5才对):

[[0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 ...
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]]

结果都是0。竟然都是0了!难道说,所有的参数训练中都变成了0??

无奈中,我把函数重新对照了一遍,然后发现了问题:前向传播的函数我不小心写成了relu。难怪会变成这样。

因为只要数据计算结果小于0,relu就会把它变成0。改成直接矩阵相乘,问题解决。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dezeming

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值