吴恩达第五集 神经网络反向传播算法推导

其中有很多内容参考博客https://blog.csdn.net/qq_32865355/article/details/80260212

算法简介

误差反向传播算法简称反向传播算法(即BP算法)。使用反向传播算法的多层感知器又称为BP神经网络。BP算法是一个迭代算法,它的基本思想为:(1)先计算每一层的状态和激活值,直到最后一层(即信号是前向传播的);(2)计算每一层的误差,误差的计算过程是从最后一层向前推进的(这就是反向传播算法名字的由来);(3)更新参数(目标是误差变小)。迭代前面两个步骤,直到满足停止准则(比如相邻两次迭代的误差的差别很小)。

记号说明

 L表示总共的层数,比如下面这个例子当中L=3

 例子详解

下面以三层感知器(即只含有一个隐藏层的多层感知器)为例介绍“反向传播算法(BP 算法)”。

信息向前传播公式推导 

误差反向推导 

误差一般公式

同理可得

误差递归公式

 

 偏置参数更新

以上总结的四个论文

BP算法执行步骤

 python代码

"""
BP神经网络
"""
import numpy as np
import math


# 激励函数及相应导数,后续可添加
def sigmoid(x):
    return 1.0 / (1 + np.exp(-x))


def diff_sigmoid(x):
    fval = sigmoid(x)
    return fval * (1 - fval)


def linear(x):
    return x


def diff_linear(x):
    return np.ones_like(x)#返回一个和x结构一样的矩阵,但是里面所有的元素都为1


class BP:
    def __init__(self, n_hidden=None, f_hidden='sigmoid', f_output='sigmoid',
                 epsilon=1e-3, maxstep=1000, eta=0.1, alpha=0.0):
        self.n_input = None  # 输入层神经元数目
        self.n_hidden = n_hidden  # 隐藏层神经元数目
        self.n_output = None # 输出结果数量,也就是输出层神经元数量
        self.f_hidden = f_hidden
        self.f_output = f_output
        self.epsilon = epsilon#总误差临界点
        self.maxstep = maxstep
        self.eta = eta  # 学习率,是运用梯度下降算法时,W系数的变化率
        self.alpha = alpha  # 动量因子,是运用梯度下降算法时,b系数的变化率

        self.wih = None  # 输入层到隐藏层权值矩阵
        self.who = None  # 隐藏层到输出层权值矩阵
        self.bih = None  # 输入层到隐藏层阈值,就是偏执量
        self.bho = None  # 隐藏层到输出层阈值,同样是偏执量
        self.N = None    # 样本数量

    def init_param(self, X_data, y_data):
        # 初始化
        if len(X_data.shape) == 1:  # 若输入数据为一维数组,则进行转置为n维数组
            X_data = np.transpose([X_data])
        self.N = X_data.shape[0]
        # normalizer = np.linalg.norm(X_data, axis=0)
        # X_data = X_data / normalizer
        if len(y_data.shape) == 1:
            y_data = np.transpose([y_data])
        self.n_input = X_data.shape[1]#将输入层神经元数量赋值给变量
        self.n_output = y_data.shape[1]#将输出层神经元数量赋值给变量
        #如果没有确定的隐藏层个数,可以用公式确定一个大概量,并且不断调整隐藏层个数,但是这里只通过公式确定,并没有不断调整的过程
        if self.n_hidden is None:
            self.n_hidden = int(math.ceil(math.sqrt(self.n_input + self.n_output)) + 2)
        self.wih = np.random.rand(self.n_input, self.n_hidden)  # i*h,创建一个self.n_input行,self.n_hidden列的随机矩阵,并且随机数在(0-1)之间
        self.who = np.random.rand(self.n_hidden, self.n_output)  # h*o
        self.bih = np.random.rand(self.n_hidden)  # h,创建一个self.n_hidden行1列的随机矩阵
        self.bho = np.random.rand(self.n_output)  # o
        return X_data, y_data

    def inspirit(self, name):
        # 获取相应的激励函数
        if name == 'sigmoid':
            return sigmoid
        elif name == 'linear':
            return linear
        else:
            raise ValueError('the function is not supported now')

    def diff_inspirit(self, name):
        # 获取相应的激励函数的导数
        if name == 'sigmoid':
            return diff_sigmoid
        elif name == 'linear':
            return diff_linear#返回的都是函数
        else:
            raise ValueError('the function is not supported now')

    def forward(self, X_data):
        # 前向传播
        x_hidden_in = X_data @ self.wih + self.bih  # n*h,@代表矩阵的乘法,X_data @ self.wih等价于dot(X_data,self.wih)
        x_hidden_out = self.inspirit(self.f_hidden)(x_hidden_in)  # n*h
        x_output_in = x_hidden_out @ self.who + self.bho  # n*o
        x_output_out = self.inspirit(self.f_output)(x_output_in)  # n*o
        #x_hidden_in,x_output_in是z,x_hidden_out,x_output_out是a,只不过分别是隐藏层和输出层的a和z
        return x_output_out, x_output_in, x_hidden_out, x_hidden_in

    def fit(self, X_data, y_data):
        # 训练主函数
        X_data, y_data = self.init_param(X_data, y_data)
        step = 0
        # 初始化动量项
        delta_wih = np.zeros_like(self.wih)
        delta_who = np.zeros_like(self.who)
        delta_bih = np.zeros_like(self.bih)
        delta_bho = np.zeros_like(self.bho)
        while step < self.maxstep:
            step += 1
            # 向前传播
            x_output_out, x_output_in, x_hidden_out, x_hidden_in = self.forward(X_data)
            if np.sum(abs(x_output_out - y_data)) < self.epsilon:
                break#当总误差小于一个规定值时也会退出循环
            # 误差反向传播,依据权值逐层计算当层误差
            err_output = y_data - x_output_out  # n*o, 输出层上,每个神经元上的误差
            delta_ho = -err_output * self.diff_inspirit(self.f_output)(x_output_in)  # n*o,输出层的δ
            err_hidden = delta_ho @ self.who.T  # n*h, 隐藏层(相当于输入层的输出),每个神经元上的误差,应用递归得到δ
            # 隐藏层到输出层权值及阈值更新
            delta_bho = np.sum(self.eta * delta_ho + self.alpha * delta_bho, axis=0) / self.N
            self.bho -= delta_bho
            delta_who = self.eta * x_hidden_out.T @ delta_ho + self.alpha * delta_who
            self.who -= delta_who
            # 输入层到隐藏层权值及阈值的更新
            delta_ih = err_hidden * self.diff_inspirit(self.f_hidden)(x_hidden_in)  # n*h
            delta_bih = np.sum(self.eta * delta_ih + self.alpha * delta_bih, axis=0) / self.N
            self.bih -= delta_bih
            delta_wih = self.eta * X_data.T @ delta_ih + self.alpha * delta_wih
            self.wih -= delta_wih
        return

    def predict(self, X):
        # 预测
        res = self.forward(X)
        return res[0]


if __name__ == '__main__':
    import matplotlib.pyplot as plt

    N = 100
    X_data = np.linspace(-1, 1, N)
    X_data = np.transpose([X_data])
    y_data = np.exp(-X_data) * np.sin(2 * X_data)
    bp = BP(f_output='linear', maxstep=2000, eta=0.01, alpha=0.1)  # 注意学习率若过大,将导致不能收敛
    #bp = BP(maxstep=2000, eta=0.01, alpha=0.1)  # 注意学习率若过大,将导致不能收敛
    bp.fit(X_data, y_data)
    plt.plot(X_data, y_data)
    pred = bp.predict(X_data)
    plt.scatter(X_data, pred, color='r')
    plt.show()

这个BP神经网络中并没有严格按照上面的推导过程来,其中有许多改进,具体可以参考https://blog.csdn.net/slx_share/article/details/80236070

其中隐藏层的个数选择可以按照如下方公式选择:

è¿éåå¾çæè¿°

h为隐含层节点的数目,m和n分别是输入层和输出层节点的数目,a为1~10之间的调节常数; 

隐藏层神经元的个数大大影响最后的预测效果,最好的方式根据经验公式选择一个参考值,然后再不断调整。 

  • 16
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值