反向传播算法的简单实现

反向传播算法代码实现

代码实现

import numpy as np
import math
import tensorflow as tf
import matplotlib.pyplot as plt


def grad_sigmoid(n):
    # 求sigmoid梯度
    return np.array(tf.sigmoid(1.0 - tf.sigmoid(n)))


def g(p):
    # 目标函数值
    t = []
    index = 0
    for i in p:
        t.append(1.0 + math.sin(p[index] * np.pi/4))
        index += 1
    return t


# 采用1-2-1网络,输入为1维,隐藏层为2维,输出为一维
# 隐藏层为对数-S型层
# 输出层为线性层
# N为batch size; 训练集一批401个,输入p在[-2,2]区间以0.01间距的等距采样,有对应的目标向量t=g(p)。
# D_in 输入维度
# H为隐藏层维度
# D_out 输出维度
N, D_in, H, D_out = 401, 1, 2, 1

# 生成训练集,输入向量p和目标向量t
p = np.linspace(start=-2.0, stop=2.0, num=N)
t = g(p)
p = np.array([p])
t = np.array([t])
print("输入向量:")
print(p)
print("输入向量长度:")
print(len(p))
print("目标向量:")
print(t)
print("目标向量长度:")
print(len(t))

print("随机生成初始权值矩阵:")
# w1 = np.random.randn(H, D_in)
# w2 = np.random.randn(D_out, H)
w1 = np.array([[-0.27], [-0.41]])
w2 = np.array([[0.09, -0.17]])
print("W1:")
print(w1)
print("W2:")
print(w2)

print("随机生成初始偏置")
# b1 = np.random.randn(H, D_in)
# b2 = np.random.randn(D_out, D_out)
b1 = np.array([[-0.48], [-0.13]])
b2 = 0.48
print("b1:")
print(b1)
print("b2:")
print(b2)

# 设置学习率为0.1
alpha = 0.01

for i in range(900):
    # 前向传播计算
    n1 = w1.dot(p) + b1
    a1 = np.array(tf.sigmoid(n1))
    a2 = w2.dot(a1) + b2
    # n2==a2  输出层为线性层
    # 计算误差
    loss = np.square(a2 - t).sum()/N
    if i % 10 == 0 or i == 900:
        print(i, loss)

    # 最后层敏感度计算,最后一层为线性函数,导数为1,
    s2 = -2.0 * (t - a2)
    # w2_sub 为 w2的对角矩阵
    w2_sub = np.array([[w2[0][0], 0], [0, w2[0][1]]])
    s1 = (grad_sigmoid(n1).T.dot(w2_sub) * s2.T).T
    grad_w2 = np.array(np.zeros((D_out, H)))
    grad_w1 = np.array(np.zeros((H, D_in)))
    grad_b2 = np.array(np.zeros((D_out, D_out)))
    grad_b1 = np.array(np.zeros((H, D_in)))
    # 计算批处理的梯度
    for m in range(0, N - 1):
        grad_w2 += 1 / N * a1.T[m] * s2[0][m]
        grad_w1 += 1 / N * np.array([p[0][m] * s1.T[m]]).transpose()
        grad_b2 += 1 / N * s2[0][m]
        grad_b1 += 1 / N * np.array([s1.T[m]]).transpose()
    # 更新权重偏置,向梯度相反的方向下降
    w1 -= alpha * grad_w1
    w2 -= alpha * grad_w2
    b1 -= alpha * grad_b1
    b2 -= alpha * grad_b2
print("训练结束:")
print("w1:")
print(w1)
print("w2:")
print(w2)
print("b1:")
print(b1)
print("b2:")
print(b2)
# test = np.array([np.random.uniform(-2, 2, size=N)])
test = np.array([np.linspace(start=-2.0, stop=2.0, num=N)])
out = w2.dot(tf.sigmoid(w1.dot(test) + b1)) + b2
plt.xlabel('in')
plt.ylabel('out')
plt.xlim(xmax=2, xmin=-2)
plt.ylim(ymax=2, ymin=0)
colors1 = '#00CED1'
colors2 = '#054E9F'
area1 = np.pi * 2 ** 1  # 点面积
area2 = np.pi * 1 ** 1  # 点面积
# 画散点图
plt.scatter(p, t, s=area1, c=colors1, alpha=1, label='origin')
plt.scatter(test, out, s=area2, c=colors2, alpha=0.5, label='test')
plt.legend()
plt.show()

代码运行结果

在这里插入图片描述

总结

由于我采用的是批处理数据下降,拟合的性能指标函数为均方误差,逼近的函数为

g( p ) = 1 + sin(pi/4 * p) , -2 ≤ p ≤ 2.

训练数据为 401个处在区间[-2,2]的等间隔点,测试数据为401个处在[-2,2]的随机间隔点,当然这些测试数据是不太规范的,因为会存在测试数据与训练数据相同的点,会导致拟合效果图有些点会接近逼近的函数,这里只是简单检测拟合效果,不是太严谨。

网络采用1-2-1网络,即第一层为1个输入,第二层为2个神经元,第三层为1个输出的网络模式,其中第一层为对数-S型层,即传输函数为logsig函数,最后一层输出层为线性层。

由于采用的是批处理下降,这里的批处理下降公式为:
Wm(k+1) = Wm(k)-alpha/Q * SUMQq=1 (smq(am-1q)T)

bm(k+1) = bm(k)-alpha/Q * SUMQq=1 (smq)
其中alpha为学习率,Q为批处理数据量(一批数据的数量),smq为这批数据中第q个数据在第m层产生的敏感度,即性能指标函数对第m层净输入的偏导
可以注意到,批处理下降的拟合效果并不是很好,因为代码实现部分采用的数据是同一批数据反复训练网络中的权值和偏置,而每个数据产生的梯度都会被除以Q(批数据数量),削弱了这个数据梯度的下降,即有些数据本应该被下降更多,但是由于批处理导致削弱,而有些数据不需要下降更多,对这些数据的影响较小,受到影响较大的数据是那些本身需要梯度下降的数据,这里采用批处理并不是很合适,因为数据量偏小,而且批处理并不是所有训练模式都适用。

假设采用非批处理模式下降,即每次训练1个数据而不是一批数据,然后进行反向传播梯度下降得到的拟合效果图为:
在这里插入图片描述
可以看到拟合效果相比批处理下降效果非常好。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值