您好,欢迎光临搜索引擎优化网站!

企业网站优化,优化方法,公司网络优化方案

seo服务公司,seo排名工具、关键词优化学习

神经网络识别手写优化(一)

作者:jcmp

浏览量: 0

2021-04-06

一、手写识别优化之前实现的神经网络还有很

一、手写识别优化

之前实现的神经网络还有很多可以优化的地方。本文主要正对其进行优化。

二、初始化

在训练神经网络之前参数初始化。这里的参数就是w,b。 w,b的初始化。 权重w初始化基于正态分布模型(高斯分布): 以0为均值,1为方差然后除以当前神经元的个数的算数平方根。 偏执b初始化基于高斯模型: 以0为均值,1为方差。 公式为:

为什么? 首先观察一下高斯分布的函数图像

符合高斯分布的数据也就是说,数据的大部分点在中间“山峰”的区域。高斯分布就是说两端的很少,中间的很多。这跟人类社会的2 8法则一项。顶尖的人很少,智障也很少。大家都是中庸的。包括看客和我。

在这里w和b 会对神经元的输出产生影响。w称为权重,我们也可以理解为神经元的 重要性 (因为w越大输出越大,w越小输出越小)。 因为我们在没有训练模型之前最好是对每个神经元都不“偏心”。那么我们干脆就认为大家都差不多,差不多的那就是中庸的。那就符合高斯分布好啦。有因为权值不能过大,大了收敛很慢。而且难算,那么干脆除一个定值(当前层的节点个数)好了。

import randomimport numpy as npclass Network(object): def __init__(self, sizes): """ 基本参数初始化 :param sizes: """ # 网络层数 self.num_layers = len(sizes) # 网络每层神经元个数 self.sizes = sizes #调用权重初始化函数 self.default_weight_initializer() def default_weight_initializer(self): """ 权重和偏置的初始化 :return: """ # 初始化每层的偏置 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:])]

三、正则化

正则化的目的就是为了提高模型的泛化能力,一定基础上防止过拟合。

四、L1正则化

在前面的博文中我们定义了LOSS函数,也就是COST函数。

这个应该很熟悉了,就是每个神经元真实标签值和预测值之间的gap。(这里只是讨论待定系数,还是忽略激励函数的存在)。 我们姑且把这个损失函数成为C0 加了正则项之后损失函数变形:

我们很容易看出损失函数其实就是多了后面一项。

其中的amda不是学习率是我们手动设置的一个参数。 还是老规矩,我们对这个COST求偏�w。

其中sgn(x)是一个 函数。也称做符号函数sign函数。就是改变输入的符号。正号变成负号,负号变成正号。

函数表达式为:

函数图像为:

那我们的权重更新也发生了变化。

式子中的前面两项已经很熟悉了,就是梯度下降。关键是后面加了一个东西。这个作用是什么样子的呢?

还记得一元凸函数的样子

我们最终目的就是要让cost滚到谷底。

第一种情况: 当w是个负数的时候,并且COST在山谷的左边的时候!w应该往右边滚动。 由梯度下降的知识可以知道前面两项就是可以让其向右边挪动。 因为第二项偏导数会变成一个负数,正数减去一个负数自然会增大!!! 我们看后面那一项: 因为w是一个负数,sgn(w)变成了-1。 那么这里就是减去一个负数。相当于w往下滚的越快了。

这是怎么回事,那么会不会滚来滚去导致无法收敛呢? 别急当w在这个点的时候。

w也会向右边滚动(同理),但是我们现在sgn(x)就是一个正数了。那么现在往右边挪就不那么顺畅了,这个第三项那个正数会狠狠的拉它一把。让它滚向右边的步伐缩小。

还不明白?看第二种情况!

第二种情况: 当w在山谷的右边的时候,应该往左边滚动。 由梯度下降的知识就知道,因为偏导数是个正数自然会向左边挪动。这里的sgn(x)也是个正数。 那么w会加速往左边滚。

总结一下这个sgn(x)就是为了让w为负数的时候迅速增大,w为正数的时候迅速见效。让w收敛的同时尽可能的等于0。

以前梯度下降很单纯往谷底走就好了

现在 谷底和原点就两个磁铁一样吸引着我们的w

w会找到一个比较合适的中间值停下来,从而在一定程度上面避免过拟合!!!

以上是一元凸函数而且只有一个参数,我们上升一下维度(盗个图)

其中彩色的波浪叫做损失等高线(借助地理的概念)。就是说在w在一个圈上面产生的cost是一样的。那么我们的w应该尽量靠近紫色的圈圈。这个时候损失会最小。 但是这里加了一个正方形也就是w的绝对值围城的正放形。同样的它想要w尽可能的靠近它的圆心。于是就出现了上述的情况。w为了满足这两个条件就在黑点的地方停下来了。正则项成功的限制住了w。让其一定程度上避免过拟合的发生。

五、L2正则

与L1正则是一样的。也是为了避免过拟合。 看公式

后面不再是绝对值。而是w的平方。前面的2n纯属为了求导方便。

我的理解就是

L2正则就是为了方便计算

其余的跟L1正则没有任何区别!!!

那就看看计算

1、求偏导

2、设置更新函数

是不是基本一毛一样!大家可以根据L1正则和梯度下降的法则分析一下过程。其实L2正则也是限制W的。(又偷个图)。

这一次再是个圆。其余一样的!!

代码部分

说了这么其实多代码其实超级简单,在我们之前实现的代码基础上加一行即可,就是是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)。

注意!这代码是不能允许的啊。只是结合之前的做一下优化,看一下修改的地方即可!

进入了解更多关于SEO关键词优化的信息。