在这一节,我们对上一个程序(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. WPF设置软件界面背景为MediaElement并播放视频

    在我们的常见的软件界面设计中我们经常会设置软件的背景为SolidColorBrush或者LinerColorBrush.RadialGradientBrush 等一系列的颜色画刷为背景,有时我们也会使 ...

  2. python数学第八天【协方差】

  3. Delphi之TComponent类

    TComponent类 TComponent类直接由TPersistent派生.TComponent的独特特征是它的属性能够在设计期间通过ObjectInspector来控制,能够拥有其他组件.非可视 ...

  4. Git拉取项目时报错“remote: HTTP Basic: Access denied”解决方法

    问题: Git拉取项目时报错“remote: HTTP Basic: Access denied”,此问题多为本地密码与远端密码不符导致. 解决方法: 在下载地址中加上用户名和密码即可,如下: htt ...

  5. jsp页面给字体加颜色

    jsp页面给字体加颜色<span style="color:red">要加颜色的部分</span>

  6. HTC Vive 基础入门 基于Unreal Engine 4引擎

    主要以讲解介绍HTC Vive设备以及Unreal继承的Steam VR Plugin为主 使用最新的虚幻引擎与Plugin完成VR环境的搭建 然后完成一个基本的VR Games. 任务5: 04-配 ...

  7. 弹出层-layui

    type 0(信息框,默认)1(页面层)2(iframe层)3(加载层)4(tips层) 弹出层 //winIndex存储弹出层的index,以便关闭弹出层时使用 function openWindo ...

  8. TP5.x——update更新成功但是返回是0

    原因 更新的数据和表中的数据一致,这个官方文档上有说明的.所以大家使用这个语句的话需要注意 update 方法返回影响数据的条数,没修改任何数据返回 0 解决方法:我是进行了判断如何和数据库一致直接返 ...

  9. TP5调用微信JSSDK 教程 - 测试成功案例

    前提:必需了解TP5的全局配置,扩展目录,composer,依赖注入的概念: 第一步:首先配置加载官方JSSDK类 "autoload": { "files": ...

  10. python学习日记(生成器函数进阶)

    迭代器和生成器的概念 迭代器 对于list.string.tuple.dict等这些容器对象,使用for循环遍历是很方便的.在后台for语句对容器对象调用iter()函数.iter()是python内 ...