BP神经网络——从二次代价函数(Quadratic cost)到交叉熵(cross-entropy cost)代价函数

通过下文的阐述我们可以获得以下信息:

  1. 反向传播(back propagation)算法是一个计算框架(或者计算流程)

    既然是一个计算框架,便与代价函数的具体形式(无论是二次代价还是交叉熵代价函数,只要能将预测的误差映射为一个标量,当然这一映射要满足特定的物理意义)无关。正因为如此,我们可将任何满足特定条件的代价函数embedded反向传播的计算过程。

  2. 不同代价函数在计算上的不同

    不同代价函数的不同就在于step 2,计算单样本的预测误差对最后一层各神经元的输入( z )的微分(导数)形式不同,也即δL不同。

我们首先来回顾BP神经网络反向传播过程:

I:前向计算各层各个神经元的输入与输出,并记录之

根据权重和偏置的初始化值前向计算(feedforward)各个layer的各个neuron的输入(又叫得分,score,对应于 z )与输出(又叫激活值,activation,简记为a),下面给出向量的形式:

z=wa1+ba=σ(z)

II:反向传播的起点,计算最后一层的 δL

首先在当前网络状态( (w,b) 给定)下,根据feedforward( a=σ(wa+b) ),预测当前样本 x label值,再根据代价函数(此时不指定具体形式,C=(a,y) a=σ(z) a 是activation激活值的缩写),计算对于神经网络的最后一层,代价函数关于最后一层的输入z的导数:

δL=C(a,y)zL

L 表示整个神经网络拓扑结构的层数,在代价函数为二次代价(Quadratic cost)的情况下:

C(a,y)=12ay2=12σ(zL)y2δL=C(a,y)zL=(σ(zL)y)σ(zL)

代价函数为交叉熵(cross-entropy)的情形( a=σ(zL) ):

C(a,y)δL=(yln(a)+(1y)ln(1a))=C(a,y)zL=(yσ(zL)1y1σ(zL))σ(zL)

因为 σ(z)=11+expz ,经过简单求导其关于 z 的微分为:σ(z)=σ(z)(1σ(z)),进一步可将上式化简为:
δL=(y(1σ(zL))(1y)σ(zL))=aLy

对比两种代价函数下的 δL ,我们发现 σ(zL) 这一项神奇地消失了,有没有什么特殊的意义呢?
我们来观察 σ(z)=11+exp(z) 这一著名的sigmoid型函数:


这里写图片描述

我们看到在 σ(z) 逼近0或者1时,变化率( σ(z) )越来越小,呈现一种饱和状态( saturation),当 σ(z) 变化率减慢,也就意味着 δL 变化率减慢,也就意味着学习率在下降(当然是在同等 learningrate:η 的前提下)。

所以,将代价函数换成交叉熵,便避免了因 σ(z) 饱和而带来的学习率的下降的情况,当然这种替换仅对sigmoid型的激励函数有效

III:反向传播,更新各层之间的权重与偏置

Cw=Czzlw=δx1

这里需要注意的是向量 δx1 (这里为行向量)以及权值更新矩阵 Cw size的问题, (Cw)j×i=(δ)j×1×(x1)1×i ,编程时要特别注意。

反向传播算法的核心公式即是:

δ=Cz=Cz+1z+1aaz=(δ+1w+1)σ(z)

这里矩阵和向量的size分别为: (δ)j=((w+1)Tk×j×(δ+1)k×1)jσ(z)j×1 表示同size的矩阵或者向量进行按位相乘,计算十分简单,对应于matlab中的.*numpy.ndarray*,注意两种语言的差异。

而我们在step 2,已经计算过最末一层的 δL ,由此从后向前便可计算各层的权值与偏置。

难能可贵的一点是:我们在步骤3并未看到任何与代价函数有关的记号,它已全部隐式的embedded在 δ 中了

下面给出先关的代码:

def sigmoid(z):
    return 1./(1.+np.exp(-z))
def sigmoid_prime(z):
    return sigmoid(z)*(1-sigmoid(z))

class QuadraticCost(object):
    @staticmethod
    def fn(a, y):
        return 1/2*np.linalg.norm(a-y)**2
    @staicmethod
    def delta(z, a, y):
        return (a-y)*sigmid_prime(z)

class CrossEntroyCost(object):
    @staticmethod
    def fn(a, y):
        # 为了数值稳定性,有可能出现(y=1,a=1)的情况,0*log(0)=nan
        # nan+任何值=nan,nan破坏最后的内积运算
        return -(np.dot(y.transpose(), np.nan_to_num(np.log(a)))+
                np.dot((1-y).transpose(), np.nan_to_num(np.log(1-a))))
    @staticmethod
    def delta(z, a, y):            # 保持接口一致
        return a-y
# 作为Network类的成员函数出现:
def backprog(self, x, y):
    nabla_b = [np.zeros(b.shape) for b in self.biases] 
    nabla_w = [np.zeros(w.shape) for w in self.weights]

    activation = x
    activations = [x]
    zs = []
    # step 1
    for w, b in zip(self.weights, self.biases):
        z = np.dot(w, activation) + b
        zs.append(z)
        activation = sigmoid(z)
        activations.append(activation)

    # step 2
    # self.cost 作为构造时的一个参数,默认为CrossEntropyCost
    # 这里再次体现了我在本文开始说的,不同代价函数唯一的不同在于计算最后一层的$\delta^\ell$
    delta = (self.cost).delta(zs[-1], activations[-1], y)
    nabla_b[-1] = delta
    nabla_w[-1] = np.dot(delta, activations[-2].transpose())

    # step 3
    for l in range(2, self.num_layers):
        z = zs[-l]
        sp = sigmoid_prime(z)
        delta = np.dot(self.weights[-l+1].transpose(), delta)*sp
        nabla_b[-l] = delta
        nabla_w[-l] = np.dot(delta, activations[-l-1].transpose()]
    return nabla_b, nabla_w
  • 10
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: 可以定义一个函数,输入三个参数a、b、c,分别表示一元二次方程的系数,返回方程的实根。 具体实现可以使用求根公式,即: 若b²-4ac>,则方程有两个实根,分别为: x1 = (-b + √(b²-4ac)) / 2a x2 = (-b - √(b²-4ac)) / 2a 若b²-4ac=,则方程有一个实根,为: x = -b / 2a 若b²-4ac<,则方程无实根。 代码实现如下: def solve_quadratic_equation(a, b, c): delta = b**2 - 4*a*c if delta > : x1 = (-b + delta**.5) / (2*a) x2 = (-b - delta**.5) / (2*a) return x1, x2 elif delta == : x = -b / (2*a) return x else: return None ### 回答2: 一元二次方程是指形如 $ax^2+bx+c=0$ 的方程,其中 $a\neq 0$。求解一元二次方程的实根可以使用求根公式: $$ x=\frac{-b\pm\sqrt{b^2-4ac}}{2a} $$ 其中 $b^2-4ac$ 称为判别式。如果判别式大于0,则方程有两个不相等的实根;如果判别式等于0,则方程有一个重根;如果判别式小于0,则方程无实根。 根据这个公式,可以定义一个函数来求解一元二次方程的实根: def solve_quadratic_equation(a, b, c): delta = b**2 - 4*a*c if delta > 0: x1 = (-b + math.sqrt(delta)) / (2*a) x2 = (-b - math.sqrt(delta)) / (2*a) return x1, x2 elif delta == 0: x = -b / (2*a) return x else: return "No real roots" 这个函数接受三个参数 a、b 和 c,分别表示方程 $ax^2+bx+c=0$ 中的系数。根据判别式的值,函数返回两个实根、一个重根或者提示没有实根。当判别式小于0时,即方程无实根时,函数返回字符串 "No real roots"。 这个函数还需要导入 math 模块,以使用 sqrt() 函数计算平方根。可以使用下面的语句导入 math 模块: import math 使用这个函数可以求解任意一元二次方程的实根,例如: >>> solve_quadratic_equation(1, -5, 6) (3.0, 2.0) 这表示方程 $x^2-5x+6=0$ 的两个实根分别为 3 和 2。 需要注意的是,这个函数只能求解一元二次方程的实根,不能求解复数解。如果方程存在复数解,则需要使用复数数学库来进行求解。 ### 回答3: 一元二次方程的一般形式为:$ax^2+bx+c=0$,其中$a\neq0$。要求解这个方程的实根,可以使用公式:$$x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}$$ 这个公式包含了两个根:$x_1=\frac{-b+\sqrt{b^2-4ac}}{2a}$和$x_2=\frac{-b-\sqrt{b^2-4ac}}{2a}$。实际上,只有当$b^2-4ac\geq0$时,这个方程才有实数解。 现在,我们可以利用这个公式,写出一个求解一元二次方程实根的函数,可以用来解决任意方程的问题。代码如下: ```python import math def solve_quadratic_equation(a, b, c): # 计算判别式 delta = b**2 - 4 * a * c # 如果判别式小于0,则方程无实数解 if delta < 0: return None # 如果判别式等于0,则方程有一个实数解 if delta == 0: return -b / (2 * a) # 如果判别式大于0,则方程有两个实数解 x1 = (-b + math.sqrt(delta)) / (2 * a) x2 = (-b - math.sqrt(delta)) / (2 * a) return (x1, x2) ``` 这个函数接受三个参数$a$、$b$和$c$,分别代表一元二次方程的系数。它首先计算判别式,判断方程是否有实数解;然后根据判别式的大小,计算出方程的解,并返回一个元组,包含这个方程的两个实数根(如果有的话)。 这个函数可以用于解决许多实际问题,例如计算抛物线的焦点、求解二次方程组等等。需要注意的是,由于计算机的精度限制,当判别式非常小但不为0时,可能存在误差。此时,应该使用一些特殊的算法来解决这个问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

五道口纳什

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

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

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

打赏作者

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

抵扣说明:

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

余额充值