在这一节,我们对上一个程序(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. websocket协议握手详解

    最近使用tornado做长链接想着怎么着也要试试websocket协议吧.所以说干就干. 首先要知道websocket是基于http协议的,为什么这么说?因为从协议来说,websocket是借用了一部 ...

  2. PHP金额工具类之将阿利伯数字转换为大写中文数字

    1.将阿拉伯数字转换为中文大写数字 <?php namespace core\components; class PriceHelper extends \yii\base\Component{ ...

  3. flask 下载本地文件

    下载本地文件就是找到文件路径  调用flask自带的send_file(路径)下载, 并返回 flask: # 下载文件 from flask import send_file@task_mgm.ro ...

  4. Spring 使用介绍(六)—— AOP(二)

    一.切入点语法 1)通配符 AOP支持的通配符: *:匹配任何数量字符 ..:匹配任何数量字符的重复,在类型模式中匹配任何数量子包,在方法参数模式中匹配任何数量参数 +:匹配指定类型的子类型,仅能作为 ...

  5. 【XSY2754】求和 莫比乌斯反演 杜教筛

    题目描述 给你\(n,p\),求 \[ \sum_{i=1}^n\sum_{j=1}^i\sum_{k=1}^i\gcd(i,j,k)\mod p \] \(n\leq {10}^9\) 题解 \[ ...

  6. 【XSY2730】Ball 多项式exp 多项式ln 多项式开根 常系数线性递推 DP

    题目大意 一行有\(n\)个球,现在将这些球分成\(k\) 组,每组可以有一个球或相邻两个球.一个球只能在至多一个组中(可以不在任何组中).求对于\(1\leq k\leq m\)的所有\(k\)分别 ...

  7. 【题解】 AtCoder ARC 076 F - Exhausted? (霍尔定理+线段树)

    题面 题目大意: 给你\(m\)张椅子,排成一行,告诉你\(n\)个人,每个人可以坐的座位为\([1,l]\bigcup[r,m]\),为了让所有人坐下,问至少还要加多少张椅子. Solution: ...

  8. 【Linux命令】linux一次性解压多个.gz或者.tar.gz文件

    原文:linux一次性解压多个.gz或者.tar.gz文件 解压多个压缩包 对于解压多个.gz文件的,用此命令: for gz in *.gz; do gunzip $gz; done 对于解压多个. ...

  9. 【转】分享两个基于MDK IDE的调试输出技巧

    我们在STM32开发调试过程中,常常需要做些直观的输出,如果手头没有相关的设备或仪器,我们可以使用 IDE自带的工具.这里分享两个基于MDK  IDE的调试输出技巧. 一.使用其自带的逻辑分析仪查看波 ...

  10. Codeforces 1079D Barcelonian Distance(计算几何)

    题目链接:Barcelonian Distance 题意:给定方格坐标,方格坐标上有两个点A,B和一条直线.规定:直线上沿直线走,否则沿方格走.求A到B的最短距离. 题解:通过直线到达的:A.B两点都 ...