深度学习之神经网络核心原理与算法-学习率

学习率

学习率 一塔 就是每次挪动中的步长。一塔通常来说给一个比较小的值会更好一些。

mark

步子太大会导致迈过谷底。

其他超参数

而由于偏导数方向的改变。你再次挪动又会向着谷底的方向挪动。

mark

只是由于步子还是很大,还是会迈过谷底。这样就会像上图一样来回折返。

到底设置为多少,是要根据自己的项目进行判断的。但是小一点的值总是要好一点的。

收敛的比较慢,但是可以使loss的值下到谷底。

Dropout(克服过拟合)

每次训练随机丢弃一些神经元,就相当于整个网络结构发生变化

减少过拟合风险

mark

在某些层上临时关闭一些节点,让他们不输入也不输出。原则上选择关闭哪些节点都是随机性的。

在分类阶段,将所有的节点都置于有效的状态

mark

就可以把训练中得到的子网络并联起来使用。

交叉熵( Cross Entropy)

学习慢

有时候我们使用相同的学习率,初始化不同的w和b,开始学习的变化率会很慢。

网络在开始学习的时候,整个loss下降的很慢。

举例: 使用梯度下降法来计算w和b, 来看看它是如何学习的

w=0.6 b=0.9 x=1.0 y=0.82 学习率=0.15

mark

简化后的网络。

mark

我们的w和b在不断的变化,我们的cost在不断的下降。

举例: 一个不好的初始化

w=2.0 b=2.0 x=1.0 y=0.2 学习率=0.15

mark

可以看到网络的训练开始学习的很慢。w和b的变化很慢。

复杂的神经网络学习很慢。

原因: 其实就是因为偏导很小造成的。

mark

mark

也就是图中某一点的斜率几乎水平了。

为什么偏导很小,导致学习很慢。

回顾一下之前的网络更新方程。

mark

最后一层的偏loss/偏b 等于 预测出来的结果减去网络的标签。点乘于sigmoid的导数。

这里的y0 yi都是一个定值。sigmoid在Z 小于-4 或大于4.水平。斜率为0.

mark

想要让网络学习快一点,也就是偏导大一些。我们就要增大sigmoid函数的导数值。

定义

mark

之前我们都是使用二次cost来定义我们网络的损失函数。这里我们可以使用交叉熵来定义我们网络的损失函数。

我们可以重新推导一遍偏loss偏w和偏loss偏b的值。

mark

如果使用交叉熵函数,而不使用二次损失函数。

mark

可以看到最终的公式里就没有sigmoid的导数这一项了。同样的方法我们也可以推导出偏loss/偏b。(避免使用sigmoid的导数)

mark

这里的x,n,y都是定制。sigmoid(z)是网络的预测结果。如果网络的预测结果和真实结果接近的话,整个网络的loss值就会减小。

如果偏差比较大的话,loss的值也会因为偏导较大而减小。

mark

可以看到情况1中loss一直随着训练轮数增加而下降。

mark

情况2不再出现学习很慢的情况。

交叉熵编码实现

如何在代码里面添加交叉熵(Cross Entropy)

Network类的初始化时我们可以定义一个损失函数。

def __init__(self, sizes, cost=CrossEntropyCost):        # 损失函数        self.cost = cost

增加一个cost参数。

定义一个二次cost的类

class QuadraticCost(object):@staticmethod    def fn(a, y):       return 0.5 * np.linalg.norm(a -y) ** 2@staticmethod    def delta(z, a, y):        return (a - y) * sigmoid_prime(z)

通过staticmethod装饰器,可以直接通过类名.方法名调用(不要实例化:QuadraticCost.fn)

fn里面a是网络预测结果,y是真实的标签。我们返回二次cost函数。

1/2 乘以 (预测值-真实值)的二范数 的平方。

np.linalg.norm

https://blog.csdn.net/lanchunhui/article/details/51004387

再定义另一个delta方法,输入参数为z,预测值a,真实值y

返回(误差) 乘以 sigmoid(z)

这时候再定义交叉熵的类。

class CrossEntropyCost(object):    '''
>>>import numpy as np
>>> a = np.array([[np.nan,np.inf],
... [-np.nan,-np.inf]])
>>> a
array([[ nan, inf],
[ nan, -inf]])
>>> np.nan_to_num(a)
array([[ 0.00000000e+000, 1.79769313e+308],
[ 0.00000000e+000, -1.79769313e+308]])
'''@staticmethod def fn(a, y): return np.sum(np.nan_to_num(-y * np.log(a) - (1 - y) * np.log(1 - a)))@staticmethod def delta(z, a, y): return (a - y)

交叉熵的方程:

mark

因为在计算出来的数中可能存在无限大和nan值。所以我们通过nan_to_num方法将其进行处理。

网络初始化时,我们可以默认使用CrossEntropyCost这个类

接着我们要将反向更新的代码进行修改:

# 反向更新了        # 计算最后一层的误差        delta = (self.cost).delta(zs[-1], activations[-1], y)

使用sel
f.cost函数来替换掉我们之前写死的二次损失函数。

发表评论

电子邮件地址不会被公开。 必填项已用*标注