手写识别优化
前言
之前实现的神经网络还有很多可以优化的地方。本文主要正对其进行优化。
初始化
在训练神经网络之前参数初始化。这里的参数就是w,b。
w,b的初始化。
权重w初始化基于正态分布模型(高斯分布):
以0为均值,1为方差然后除以当前神经元的个数的算数平方根。
偏执b初始化基于高斯模型:
以0为均值,1为方差。
公式为:
为什么?
首先观察一下高斯分布的函数图像
这个应该很熟悉了,就是每个神经元真实标签值和预测值之间的gap。(这里只是讨论待定系数,还是忽略激励函数的存在)。
我们姑且把这个损失函数成为C0
加了正则项之后损失函数变形:
正则化后的损失函数
我们很容易看出损失函数其实就是多了后面一项。
image.png
其中的amda不是学习率是我们手动设置的一个参数。
还是老规矩,我们对这个COST求偏�w
image.png
其中sgn(x)是一个 函数。也称做符号函数sign函数。就是改变输入的符号。正号变成负号,负号变成正号。
函数表达式为:
image.png
函数图像为:
image.png
那我们的权重更新也发生了变化。
image.png
式子中的前面两项已经很熟悉了,就是梯度下降。关键是后面加了一个东西。这个作用是什么样子的呢?
还记得一元凸函数的样子
image.png
我们最终目的就是要让cost滚到谷底。
第一种情况:
当w是个负数的时候,并且COST在山谷的左边的时候!w应该往右边滚动。
由梯度下降的知识可以知道前面两项就是可以让其向右边挪动。
因为第二项偏导数会变成一个负数,正数减去一个负数自然会增大!!!
我们看后面那一项:
因为w是一个负数,sgn(w)变成了-1。
那么这里就是减去一个负数。相当于w往下滚的越快了。
这是怎么回事,那么会不会滚来滚去导致无法收敛呢?
别急当w在这个点的时候。
w也会向右边滚动(同理),但是我们现在sgn(x)就是一个正数了。那么现在往右边挪就不那么顺畅了,这个第三项那个正数会狠狠的拉它一把。让它滚向右边的步伐缩小。
还不明白?看第二种情况!
第二种情况:
当w在山谷的右边的时候,应该往左边滚动。
由梯度下降的知识就知道,因为偏导数是个正数自然会向左边挪动。这里的sgn(x)也是个正数。
那么w会加速往左边滚。
总结一下这个sgn(x)就是为了让w为负数的时候迅速增大,w为正数的时候迅速见效。让w收敛的同时尽可能的等于0。
以前梯度下降很单纯往谷底走就好了
现在 谷底和原点就两个磁铁一样吸引着我们的w
image.png
w会找到一个比较合适的中间值停下来,从而在一定程度上面避免过拟合!!!
以上是一元凸函数而且只有一个参数,我们上升一下维度(盗个图)
image.png
其中彩色的波浪叫做损失等高线(借助地理的概念)。就是说在w在一个圈上面产生的cost是一样的。那么我们的w应该尽量靠近紫色的圈圈。这个时候损失会最小。
但是这里加了一个正方形也就是w的绝对值围城的正放形。同样的它想要w尽可能的靠近它的圆心。于是就出现了上述的情况。w为了满足这两个条件就在黑点的地方停下来了。正则项成功的限制住了w。让其一定程度上避免过拟合的发生。
L2正则
与L1正则是一样的。也是为了避免过拟合。
看公式
后面不再是绝对值。而是w的平方。前面的2n纯属为了求导方便。
我的理解就是
L2正则就是为了方便计算
其余的跟L1正则没有任何区别!!!
那就看看计算
1、求偏导
image.png
2、设置更新函数
image.png
是不是基本一毛一样!大家可以根据L1正则和梯度下降的法则分析一下过程。其实L2正则也是限制W的。(又偷个图)
image.png
这一次再是个圆。其余一样的!!
<h1>代码部分</h1>
说了这么其实多代码其实超级简单,在我们之前实现的代码基础上加一行即可,就是是w的更新公式改改!!!
class Network(object):
def __init__(self, sizes):
# 网络层数
self.num_layers = len(sizes)
# 网络每层神经元个数
self.sizes = sizes
self.default_weight_initializer()
def default_weight_initializer(self):
# 初始化每层的偏置
self.biases = [np.random.randn(y, 1) for y in self.sizes[1:]]
# 初始化每层的权重
self.weights = [np.random.randn(y, x)/np.sqrt(x)
for x, y in zip(self.sizes[:-1], self.sizes[1:])]
# 随机梯度下降
def SGD(self, training_data, epochs, mini_batch_size, eta,lmbda=0.0,
test_data=None):
if test_data: n_test = len(test_data)
# 训练数据总个数
n = len(training_data)
# 开始训练 循环每一个epochs
for j in xrange(epochs):
# 洗牌 打乱训练数据
random.shuffle(training_data)
# mini_batch
mini_batches = [training_data[k:k + mini_batch_size]
for k in range(0, n, mini_batch_size)]
# 训练mini_batch
for mini_batch in mini_batches:
self.update_mini_batch(mini_batch, eta,lmbda,n)
if test_data:
print "Epoch {0}: {1} / {2}".format(
j, self.evaluate(test_data), n_test)
print "Epoch {0} complete".format(j)
# 更新mini_batch
def update_mini_batch(self, mini_batch, eta,lmbda,n):
# 保存每层偏倒
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
# 训练每一个mini_batch
for x, y in mini_batch:
delta_nable_b, delta_nabla_w = self.update(x, y)
# 保存一次训练网络中每层的偏倒
nabla_b = [nb + dnb for nb, dnb in zip(nabla_b, delta_nable_b)]
nabla_w = [nw + dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
# 更新权重和偏置 Wn+1 = wn - eta * nw 我们只改了这里
self.weights = [w -eta*(lmbda/n)*w-(eta / len(mini_batch)) * nw
for w, nw in zip(self.weights, nabla_w)]
self.biases = [b - (eta / len(mini_batch)) * nb
for b, nb in zip(self.biases, nabla_b)]
# 前向传播
def update(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
# 保存每一层的激励值a=sigmoid(z)
activations = [x]
# 保存每一层的z=wx+b
zs = []
# 前向传播
for b, w in zip(self.biases, self.weights):
# 计算每层的z
z = np.dot(w, activation) + b
# 保存每层的z
zs.append(z)
# 计算每层的a
activation = sigmoid(z)
# 保存每一层的a
activations.append(activation)
# 反向更新了
# 计算最后一层的误差
delta = self.cost_derivative(activations[-1], y) * sigmoid_prime(zs[-1])
# 最后一层权重和偏置的倒数
nabla_b[-1] = delta
nabla_w[-1] = np.dot(delta, activations[-2].transpose())
# 倒数第二层一直到第一层 权重和偏置的倒数
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)
注意!这代码是不能允许的啊。只是结合之前的做一下优化,看一下修改的地方即可!
网友评论