在这一节,我们对上一个程序(Network1.py)进行了优化

3.改进神经网络的学习方法

(1)交叉熵代价函数的引入

Network1程序采用了S型神经元,S型神经元存在一个问题,当输出层神经元的输出接近0,或者1的时候,sigmoid函数曲线相当平导致此时sigmoid函数的导数很小,当选择二次代价函数时,输出误差δL=(aL-y)σ‘(zL),∂C/∂ωL,∂C/∂bL就会非常小,使得神经网络学习变得缓慢。

因此我们引入了交叉熵代价函数

当选择交叉熵代价函数时,δL= aL-y。解决了输出层学习缓慢的问题,但是没有解决隐藏层的神经元在σ(z)接近1或者0的时候饱和的问题。

(2)柔性最大值的引入

有时候我们想把输出层第j个神经元的输出看做一种概率估计,因此引入了柔性最大值(softmax),第j个神经元的激活值是:

并定义对数代价函数

其中y为训练输入x对应的目标输出,aL为神经网络输出。如果我们训练的是MNIST图像,输入为7的图像,那么对应的对数代价就是-lna7L,当神经网络输出就是7的时候,他会估计一个对应的概率a7L和1很接近,所以代价就会很小,反之,神经网络表现的很糟,改良版a7L就变的很小,代价就随之增大,所以对数代价函数也是满足我们期望的代价函数的条件的。

(3)过度拟合和规范化

过度拟合可以理解为模型对已有的数据会表现的很好,但是对新的数据很难泛化,对一个模型真正的测验就是他对没有见过的场景的预测能力。

为了缓解过度拟合,我们主要采取以下措施:

  • L1.规范化
  • L2规范化
  • L1规范化
  • 弃权
  • 人为增加训练样本

这里主要讲解以下L2规范化技术。二次代价函数,以及交叉熵代价函数,柔性最大值规范化后的形式如下:

其中C0为原始代价函数。第二项加入的就是所有权重(每个元素)的平方和,λ成为规范化参数,规范化可以当做一种寻找小的权重和最小原始代价函数之间的折中,λ越小,就越偏向于最小化原始代价函数,反之倾向于小的权重。

此时权重学习就变成了

(4)权重初始化

由于Network1程序中中我们在隐藏层和输出层激活函数选用的都是sigmoid函数,我们刚才考虑采用交叉熵代价函数,解决了输出层学习缓慢的问题,但是隐藏层的神经元在σ(z)接近1或者0的时候,也会存在饱和的问题,会导致学习缓慢。

在Network1中我们随机初始化权重和偏置为标准正态分布。假设我们使用一个有大量输入神经元的网络,比如有10000个,我们假设训练输入x,其中一半神经元的输入神经值为1,另一半输入为0。让我们考虑隐藏神经元输入的带权和 z = Σwjxj+b,所以z服从μ(0,501)正态分布,z是一个有非常宽的高斯分布,z>>1或者z<<-1的概率会很大,这样神经元的输出σ(z)会接近1或者0,这样我们的隐藏神经元会饱和,所以当出现这样的情况时,在权重中进⾏微⼩的调整仅仅会给隐藏神经元的激活值带来极其微弱的改变。⽽这种微弱的改变也会影响⽹络中剩下的神经元,然后会带来相应的代价函数的改变。结果就是,这些权重在我们进⾏梯度下降算法时会学习得⾮常缓慢。这其实和我们在前⾯所说的问题差不多,前⾯的情况是输出神经元在错误的值上饱和导致学习的下降。我们之前通过代价函数的选择解决了前⾯的问题。不幸的是,尽管那种⽅式在输出神经元上有效,但对于隐藏神经元的饱和却⼀点作⽤都没有。

假设我们有一个有nin个输入权重的神经元,因此我们可以通过初始化权重和偏置分布为μ(0,1/nin)解决这个问题。

(5)神经网络超参数的选择

(6)其他技术

(1)随机梯度下降的变化形式

主要介绍了以下Hessian技术和momentum技术。

(2)人工神经元的其他模型

双曲正切神经元(tanh),以及修正现行神经元(ReLU)

Network2.py程序较之前程序有了一些改进:

1.代价函数中加入了规范化项,缓解过度拟合
2.权重,偏置初始化进行了优化,服从N(0,1/nin)分布,缓解了隐藏层神经元饱和,加快训练速度

代码如下:

# -*- coding: utf- -*-
"""
Created on Tue Mar :: @author: Administrator
""" '''
书籍:神经网络与深度学习
第三章:改进版的神经网络
与Network.py文件中有如下改进
.代价函数中加入了规范化项,缓解过度拟合
.权重,偏置初始化进行了优化,服从N(,/nin)分布,缓解了隐藏层神经元饱和,加快训练速度
''' import numpy as np
import json
import random import sys '''
定义二次代价函数和交叉熵代价函数的类
'''
class QuadraticCost(object):
#定义静态方法
@staticmethod
def fn(a,y):
'''
计算一个样本的二次代价函数 C0 = 0.5∑j(yj - aj)^
a:神经网络输出层值
y:目标值
'''
return 0.5*np.linalg.norm(a-y)** @staticmethod
def delta(z,a,y):
'''
计算输出层误差δL
z:输出层带权输入
a:神经网络输出层值
y:目标值
'''
return (a-y)*sigmod_prime(z) class CrossEntropyCost(object):
#定义静态方法
@staticmethod
def fn(a,y):
'''
计算一个样本的交叉熵代价函数 C0 = -∑j[yjln(ajL) + (-yj)ln( - ajL)]
a:神经网络输出层值
y:目标值
'''
#np.nan_to_num:Replace nan with zero and inf with finite numbers.
#把np.nan(非常小的数字,接近0)用0取代
#np.inf,或者-np.inf(正负无穷大的数字)用有限数替代
return np.sum(-*np.nan_to_num( y*np.log(a) + ( - y)*np.log( - a))) @staticmethod
def delta(z,a,y):
'''
计算输出层误差δL
z:输出层带权输入
a:神经网络输出层值
y:目标值
'''
return (a-y) '''
定义神经网络的类
'''
class Network(object):
def __init__(self,sizes,cost = CrossEntropyCost):
'''
sizes:list类型 每个元素对应神经网络中每一层的神经元个数
cost:CrossEntropyCost或者QuadraticCost 指定代价函数 默认采用交叉熵代价函数
'''
self.num_layers = len(sizes)
self.sizes = sizes
self.default_weight_initializer()
self.cost = cost def default_weight_initializer(self):
'''
默认权重初始化函数,改进之后的,w,b服从N(,/nin)正态分布
'''
self.biases = [np.random.randn(y,) for y in self.sizes[:]]
self.weights = [np.random.randn(y,x)/np.sqrt(x) for x,y in zip(self.sizes[:-],self.sizes[:])] def large_weight_initializer(self):
'''
权重初始化函数,w,b服从N(,)正态分布
weights:权重,随机初始化,服从(,)高斯分布 weights[i]:是一个连接着第i层和第i+1层神经元权重的numpy矩阵 i=,...
biases:偏置,随机初始化,服从(,)高斯分布 biases[i]:是第i+1层神经元偏置向量 i=,....
'''
self.biases = [np.random.randn(y,) for y in self.sizes[:]]
self.weights = [np.random.randn(y,x) for x,y in zip(self.sizes[:-],self.sizes[:])] def feedforward(self,a):
'''
前向反馈函数,对于网络给定一个输入向量a,返回对应的输出
a:神经网络的输入
'''
for b,w in zip(self.biases,self.weights):
#dot矩阵乘法 元素乘法使用*
a = sigmod(np.dot(w,a) + b)
return a def SGD(self,training_data,epochs,mini_batch_size,eta,lamda,evaluation_data=None,
monitor_evaluation_cost = False,
monitor_evaluation_accuracy = False,
monitor_training_cost = False,
monitor_training_accuracy = False):
'''
随机梯度下降算法:使用小批量训练样本来计算梯度(计算随机选取的小批量数据的梯度来估计整体的梯度)
training_data:元素为(x,y)元组的列表 (x,y):表示训练输入以及对应的输出类别 这里的输出类别是二值化后的10*1维向量
epochs:迭代期数量 即迭代次数
mini_batch:小批量数据的大小
eta:学习率
lamda:规范化lamda参数
evaluation_data:评估数据集 validation_data或者test_data
monitor_evaluation_cost:标志位 打印每次迭代评估数据集的代价
monitor_evaluation_accuracy:标志位 打印每次迭代评估数据集的预测准确的个数
monitor_training_cost 打印每次迭代训练数据集的代价
monitor_training_accuracy 打印每次迭代训练数据集的预测准确的个数
'''
if evaluation_data:
#计算评估集样本个数
n_data = len(evaluation_data)
#计算训练集样本个数
n = len(training_data)
evaluation_cost = []
evaluation_accuracy = []
training_cost = []
training_accuracy = []
#进行迭代
for j in range(epochs):
#将训练集数据打乱,然后将它分成多个适当大小的小批量数据
random.shuffle(training_data)
mini_batches = [training_data[k:k+mini_batch_size] for k in range(,n,mini_batch_size)]
#训练神经网络
for mini_batch in mini_batches:
self.update_mini_batch(mini_batch,eta,lamda,n) print('Epoch {0} training complete'.format(j)) #每一次迭代后 输出相应的测试信息,预测准确个数和代价
if monitor_evaluation_cost:
cost = self.total_cost(evaluation_data,lamda,convert=True)
evaluation_cost.append(cost)
print('Cost on evaluation data:{0}'.format(cost)) if monitor_evaluation_accuracy:
accuracy = self.accuracy(evaluation_data)
evaluation_accuracy.append(accuracy)
print('Accuracy on evaluation data:{0}/{1}'.format(accuracy,n_data)) if monitor_training_cost:
cost = self.total_cost(training_data,lamda)
training_cost.append(cost)
print('Cost on training data:{0}'.format(cost)) if monitor_training_accuracy:
accuracy = self.accuracy(training_data,convert=True)
training_accuracy.append(accuracy)
print('Accuracy on training data:{0}/{1}'.format(accuracy,n)) #返回的均为list 包含每次迭代后对应的代价值或者预测准确个数
return evaluation_cost,evaluation_accuracy,training_cost,training_accuracy def update_mini_batch(self,mini_batch,eta,lamda,n):
'''
mini_batch:小批量数据 元素为(x,y)元组的列表 (x,y)
eta:学习率
lamda:规范化lamda参数
n:训练集数据总长度
对每一个mini_batch应用梯度下降,更新权重和偏置
'''
#初始化为0
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
#依次对每一个样本求梯度,并求和
for x,y in mini_batch:
#计算每一个样本代价函数的梯度
delta_nabla_b,delta_nabla_w = self.backprop(x,y)
#梯度分量求和 Σ∂Cx/∂ω
nabla_b = [nb + dnb for nb,dnb in zip(nabla_b,delta_nabla_b)]
#梯度分量求和 Σ∂Cx/∂b
nabla_w = [nw + dnw for nw,dnw in zip(nabla_w,delta_nabla_w)]
#更新权重 w = w - η/m*Σ∂Cx/∂ω
self.weights = [( - eta*lamda/n)*w - (eta/len(mini_batch))*nw for w,nw in zip(self.weights,nabla_w)]
#更新偏置 b = b - η/m*Σ∂Cx/∂b
self.biases = [b - (eta/len(mini_batch))*nb for b,nb in zip(self.biases,nabla_b)] def backprop(self,x,y):
'''
计算给定一个样本代价函数的梯度 单独训练样本x的二次代价函数
返回一个元组(nabla_b,nabla_w) = (∂Cx/∂ω,∂Cx/∂b) :和权重weights,偏置biases维数相同的numpy数组
'''
#初始化与self.baises,self.weights维数一样的两个数组 用于存放每个训练样本偏导数的累积和
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
#前向反馈
activation = x
#保存除了输入层外所有层的σ(z)的值
activations = [x]
#保存除了输入层外所有层的z的值
zs = []
#计算除了输入层外每一层z和σ(z)的值
for b,w in zip(self.biases,self.weights):
z = np.dot(w,activation) + b
zs.append(z)
activation = sigmod(z)
activations.append(activation) #反向传播 计算输出层误差δL,以及∂Cx/∂bL,∂Cx/∂ωL
delta = self.cost.delta(zs[-],activations[-],y)
nabla_b[-] = delta
nabla_w[-] = np.dot(delta,activations[-].transpose()) #计算隐藏层误差δl,以及 ∂Cx/∂bl,∂Cx/∂ωl
     for l in range(,self.num_layers):
z = zs[-l]
sp = sigmod_prime(z)
delta = np.dot(self.weights[-l+].transpose(),delta)*sp
nabla_b[-l] = delta
nabla_w[-l] = np.dot(delta,activations[-l-].transpose())
return (nabla_b,nabla_w) def accuracy(self,data,convert=False):
'''
返回用神经网络预测data数据集中样本输出正确的个数
data:数据集[(x,y),...]
convert:判断data中每个样本的输出是否是二值化后的向量?如果是True,否则False 这个与data数据格式有关
'''
if convert:
results = [(np.argmax(self.feedforward(x)),np.argmax(y)) for x,y in data]
else:
results = [(np.argmax(self.feedforward(x)),y) for x,y in data]
return sum(int(x==y) for x,y in results) def total_cost(self,data,lamda,convert=False):
'''
返回用神经网络预测data数据集后的代价值
data:数据集[(x,y),...]
lamda:规范化lamda参数
convert:判断data中每个样本的输出是否是二值化后的向量?如果是False,否则True 这个与data数据格式有关
'''
cost = 0.0
for x,y in data:
if convert:
#二值化
y = vectorized_result(y)
cost += self.cost.fn(self.feedforward(x),y)
cost /= len(data)
#加入规范化项
cost += 0.5*lamda/len(data)*sum(np.linalg.norm(w)** for w in self.weights)
return cost def save(self,filename):
'''
把神经网络的参数序列化后写到指定文件中
'''
data = {'sizes':self.sizes,
'weights':self.weights,
'biased':self.biases,
'cost':str(self.cost.__name__)
}
with open(filename,'w') as f:
#写入序列化后的值
json.dump(data,f) def load(filename):
'''
从文件中读取字符串并反序列化,创建一个神经网络类对象,并恢复权重,偏置参数
'''
with open(filename,'r') as f:
data = json.load(f)
#从当前模块中找到名称data['cost']的对象,并赋值给cost
cost = getattr(sys.modules[__name__],data['cost'])
#创建神经网络
net = Network(data['sizes'],cost=cost)
#初始化权重和偏置
net.weights = data['weights']
net.biases = data['biases']
return net def vectorized_result(j):
"""
- 二值化为 *
Return a -dimensional unit vector with a 1.0 in the jth
position and zeroes elsewhere. This is used to convert a digit
(...) into a corresponding desired output from the neural
network.
"""
e = np.zeros((, ))
e[j] = 1.0
return e def sigmod(z):
'''
定义S型函数
当输入z是一个list,tuple或者numpy数组时,numpy自动地按元素应用sigmod函数,即以向量形式
z:list,tuple或者numpy数组
'''
return 1.0/(1.0+np.exp(-z)) def sigmod_prime(z):
'''
定义S型函数的导数
'''
return sigmod(z)*(-sigmod(z)) def  network2_baseline():
    #traning_data:[(784*1,10*1),...],50000个元素
    #validation_data[(784*1,1*1),....],10000个元素
    #test_data[(784*1,1*1),....],10000个元素
    training_data,validation_data,test_data = mnist_loader.load_data_wrapper()
    print('训练集数据长度',len(training_data))
    print(training_data[0][0].shape)      #训练集每一个样本的特征维数   (784,1)
    print(training_data[0][1].shape)      #训练集每一个样本对应的输出维数  (10,1)
    
    print('测试集数据长度',len(test_data))
    print(test_data[0][0].shape)         #测试机每一个样本的特征维数,1,1   (784,1)
    #print(test_data[0][1].shape)         #测试机每一个样本对应的输出维数   () 这里与训练集的输出略有不同,这里输出是一个数 并不是二指化后的10*1维向量
    print(test_data[0][1])               #7
       
    #测试
    net = Network2.Network([784,30,10])
    '''
    print(net.num_layers)      #3
    print(net.sizes)
    print(net.weights)
    print(net.biases)
    '''
    
    net.SGD(training_data,30,10,0.5,lamda=5,evaluation_data=validation_data,
            monitor_evaluation_cost = True,
            monitor_evaluation_accuracy=True,
            monitor_training_cost=True,
            monitor_training_accuracy=True) #开始测试
network2_baseline()

参考文献

[1]深度神经网络(DNN)损失函数和激活函数的选择

[2]深度神经网络(DNN)的正则化

第五节,Neural Networks and Deep Learning 一书小节(中)的更多相关文章

  1. 第四节,Neural Networks and Deep Learning 一书小节(上)

    最近花了半个多月把Mchiael Nielsen所写的Neural Networks and Deep Learning这本书看了一遍,受益匪浅. 该书英文原版地址地址:http://neuralne ...

  2. 第六节,Neural Networks and Deep Learning 一书小节(下)

    4.神经网络可以计算任何函数的可视化证明 神经网络拥有一定的普遍性,即包含一个隐藏层的神经网络可以被用来按照任意给定的精度来近似任何连续函数. 这一章使用一个实例来阐述神经网络是如何来近似一个一元函数 ...

  3. 【DeepLearning学习笔记】Coursera课程《Neural Networks and Deep Learning》——Week2 Neural Networks Basics课堂笔记

    Coursera课程<Neural Networks and Deep Learning> deeplearning.ai Week2 Neural Networks Basics 2.1 ...

  4. 【DeepLearning学习笔记】Coursera课程《Neural Networks and Deep Learning》——Week1 Introduction to deep learning课堂笔记

    Coursera课程<Neural Networks and Deep Learning> deeplearning.ai Week1 Introduction to deep learn ...

  5. Neural Networks and Deep Learning学习笔记ch1 - 神经网络

    近期開始看一些深度学习的资料.想学习一下深度学习的基础知识.找到了一个比較好的tutorial,Neural Networks and Deep Learning,认真看完了之后觉得收获还是非常多的. ...

  6. Neural Networks and Deep Learning

    Neural Networks and Deep Learning This is the first course of the deep learning specialization at Co ...

  7. [C3] Andrew Ng - Neural Networks and Deep Learning

    About this Course If you want to break into cutting-edge AI, this course will help you do so. Deep l ...

  8. 《Neural Networks and Deep Learning》课程笔记

    Lesson 1 Neural Network and Deep Learning 这篇文章其实是 Coursera 上吴恩达老师的深度学习专业课程的第一门课程的课程笔记. 参考了其他人的笔记继续归纳 ...

  9. [C1W1] Neural Networks and Deep Learning - Introduction to Deep Learning

    第一周:深度学习引言(Introduction to Deep Learning) 欢迎(Welcome) 深度学习改变了传统互联网业务,例如如网络搜索和广告.但是深度学习同时也使得许多新产品和企业以 ...

随机推荐

  1. lombok标签之@Data @AllArgsConstructor @@NoArgsConstructor -如何去除get,set方法。@Data注解和如何使用,lombok

    在代码中我们可以只加上标签@Data 而不用get,set方法: val : 和 scala 中 val 同名, 可以在运行时确定类型; @NonNull : 注解在参数上, 如果该类参数为 null ...

  2. Nginx 滑动窗口与缓冲区

    L:125

  3. Row_Number() over()

    分页 ROW_NUMBER() OVER (order by ID) 是先把ID列排序,再为排序以后的每条ID记录返回一个序号.

  4. LVS负载均衡群集

    概述 群集的类型:无论是哪种服务器,都至少包括两台节点服务器,而对外表现为一个整体,只提供一个访问入口(域名或IP地址),相当于一台大型计算机.根据群集所针对的目标差异,可以分为以下三个类型: 1.负 ...

  5. 【XSY2767】朋友 广义后缀自动机 网络流

    题目描述 懒得写了...直接贴题面 $\sum n\leq5000,1\leq S_{i,j}\leq k\leq 1000 $ 题解 先建出广义sam. 可以发现朋友的出现位置的定义符合后缀自动机的 ...

  6. pytorch CNN 手写数字识别

    一个被放弃的入门级的例子终于被我实现了,虽然还不太完美,但还是想记录下 1.预处理 相比较从库里下载数据集(关键是经常失败,格式也看不懂),更喜欢直接拿图片,从网上找了半天,最后从CSDN上下载了一个 ...

  7. 【HDU - 4345 】Permutation(DP)

    BUPT2017 wintertraining(15) #8F 题意 1到n的排列,经过几次置换(也是一个排列)回到原来的排列,就是循环了. 现在给n(<=1000),求循环周期的所有可能数. ...

  8. ansible 开源批量管理服务器工具

    Ansible 是一款基于 Python 开发的自动化运维工具,可以进行配置管理.批量部署等功能.对于机器较多的场景,可以使用 Ansible 来免去重复敲命令的烦恼. 安装ansibleyum -y ...

  9. UOJ #207. 共价大爷游长沙(LCT + 异或哈希)

    题目 维护一颗动态树,并维护一个点对集合 \(S\) . 动态查询一条边,是否被集合中所有点对构成的路径包含. \(n \le 100000, m \le 300000\) 题解 orz 前辈 毛爷爷 ...

  10. genymotion ddms查看data等文件目录

    使用ADB shell 命令: 打开 Cmd  ,输入  ADB shell 命令后,回车(前提是你已经配置好了adb 的环境变量,跟配置Java的环境变量一样); 输入su回车,获取超级管理员权限 ...