在这一节,我们对上一个程序(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. StringBuilder与String有哪些区别?

    System.String具备不可修改性,在程序中这样的特性容易产生性能上的问题.针对这个问题.NET提供的StringBuilder类可以解决类似的问题. String 和 StringBuilde ...

  2. iview render bug & vue namespace bug

    iview render bug https://codepen.io/xgqfrms/pen/gyGjKP https://codepen.io/xgqfrms/full/gyGjKP bug &l ...

  3. swagger bug

    https://blog.csdn.net/u011943534/article/details/81778125 处理swagger报错Could not resolve pointer: /def ...

  4. Lambda表达式Contains方法(等价于SQL语句中的like)使用注意事项

    貌似已经半年多没写一篇帖子了,充分的说明要么老总一天折腾的让人心齐疲惫,没心情去写:要么另外一种可能就是自己不思进取,说白了就是懒.好在这种状态在今天被打破了.MoNey加油. 众所周知,想在Enti ...

  5. linux网络配置+远程工具使用

    ifconfig 网卡名 ip地址 就可以设定ip地址 只是临时改变ip地址,重启后会失效 secure crt  更改语言,用gb 和 utf8 两处修改

  6. Nginx proxy_protocol协议

    L:113

  7. Nginx 决策浏览器缓存是否有效

    expires指令是告诉浏览器过期时间 syntax:expires [modified] time; eopch | max | off; default : off context :http,s ...

  8. Civil 3D CustomDraw .NET混合项目设置

    样例文件中的CustomDraw项目使用的是COM API,但COM API不完整,某些时候需要使用.NET API,此时需要将C++项目设置成"公共语言运行时支持(/clr)"但 ...

  9. Spring 使用介绍(十一)—— Spring事件

    一.简介 spring事件是观察者设计模式的实现,主要有三个元素: 事件 spring事件由ApplicationEvent定义 监听者 由ApplicationListener定义 发布者 由App ...

  10. 18mysql3

    一.内外连接全连接,左右连接   █▓        通过两张表查找其对应的记录. 隐式 内连接 select * from a,b where a.列名 = b.列名   █▓        左连接 ...