一:基本概念

入门理解:深度学习并不是去尝试定义到底什么是⼀只猫,⽽是通过⼤量的数据和⼤量的投票器,把⽹络⾥⾯的开关训练成“猫通路”“狗通路”。对数据量的需求远远胜过对具体“猫特征”定义的需求。在这里插入图片描述

深度学习的机理:它是⽤⼀串⼀串的函数,也就是层,堆叠起来,作⽤于输⼊数据,进⾏从原始数据到分类结果的过滤与提纯。这些层通过权重来参数化,通过损失函数来判断当前⽹络的效能,然后通过优化器来调整权重,寻找从输⼊到输出的最佳函数。
学习:就是为神经⽹络的每个层中的每个神经元寻找最佳的权重。
知识:就是学到的权重。

二:正向传播和反向传播(理解,不用自己写代码)

概念

正向传播:预测过程

比喻:工厂流水线
原材料(输入数据)
    ↓
工序1(第1层神经网络):粗加工 → 提取简单特征
    ↓
工序2(第2层):精加工 → 组合成复杂特征
    ↓
工序3(第3层):组装 → 形成最终判断
    ↓
成品(预测结果)

具体例子:识别图片是不是猫

输入:一张图片的像素值 [224×224×3个数字]
    ↓
第1层(卷积):发现边缘、颜色块 → "这里有毛茸茸的纹理"
    ↓
第2层(卷积):组合边缘 → "这是耳朵形状""这是胡须"
    ↓
第3层(全连接):组合部件 → "耳朵+胡须+圆眼睛=猫"
    ↓
输出:0.92(92%概率是猫)

正向传播就是:数据从左到右流一遍,每层用当前权重算一遍,得到预测。

反向传播:
比喻:质检反馈调流水线

预测结果:92%是猫 ← 但实际是狗(标签错了)
    ↓
问题追溯:最后一层把"尖耳朵"权重给太高了
    ↓
倒推责任:
    - 第3层:猫耳朵权重 ↑ 太多,要↓
    - 第2层:胡须检测器太敏感,要调
    - 第1层:边缘检测方向偏了,要修正
    ↓
调整各工序参数(权重更新)

核心机制:链式求导(误差层层倒推)

步骤做什么人话解释
算损失预测vs真实差距“这次猜错多少”
求梯度每个权重对损失的贡献“怪谁?怪多少?”
更新权重往减少损失的方向调“谁的责任大,谁改得多”

正向传播是数据流过网络做预测,反向传播是从误差倒推每层责任,用梯度下降调整权重。就像工厂先生产看质量,再根据投诉追溯哪道工序问题大,针对性改进。

数学原理极简版

x → h₁ → h₂ → ŷ → L
    ↑    ↑    ↑
   w₁x  w₂h₁ w₃h₂  (w藏在里面,不显示)

正向传播:复合函数

输入 x → 层1计算 → 层2计算 → ... → 输出 ŷ

数学:ŷ = f₃(f₂(f₁(x)))
     每层 = 权重 × 输入 + 偏置,再套激活函数

反向传播:链式法则
以下是给本人给数学不好的自己的例子,求各层梯度就是在说我这层参数对loss有多少影响,就恰好是偏导的定义(单参数而言,一层多参数则是参数偏导组成向量),我知道了我自己参数对Loss影响,我就学习。然后我本层还可以拿到我前一层的输入,就能偏导求出前一层对loss的影响并且传递给前一层,前一层知道了自己层对loss影响,就进一步得到自己层参数对loss影响。

输入: x = 2

层1: h₁ = w₁ × x = 3 × 2 = 6      (w₁ = 3)
层2: h₂ = w₂ × h₁ = 4 × 6 = 24     (w₂ = 4)  
层3: ŷ = w₃ × h₂ = 5 × 24 = 120    (w₃ = 5)

输出: ŷ = 120
真实: y = 130
损失: L = (ŷ - y)² = (120 - 130)² = 100

在这里插入图片描述
在这里插入图片描述

反向传播时算w和算h是交替的,这个逻辑一定别混了。再看一遍图
x → h₁ → h₂ → ŷ → L
    ↑    ↑    ↑
   w₁x  w₂h₁ w₃h₂  (w藏在里面,不显示)

梯度下降中正向传播和反向传播的细节,⼤家理解即可,因为其代码实现早就封装在Keras或者TensorFlow这样的框架之中了。
然⽽,优化器的选择和设置、激活函数和损失函数的选择,以及评估指标的选择,则是需要我们动⼿配置、调试的内容。

三:梯度下降优化器

优化器的引⼊和神经⽹络梯度下降的特点有关。损失函数对于整个神经⽹络的参数来说并不总是凸函数,⽽是⾮常复杂的函数。
在这里插入图片描述
在局部最低点和鞍点上,导数没有任何⽅向感,参数也不知道应该往哪⾥“⾛”。

解决方法:①尝试各种初始起点值②优化器

3.1批量梯度下降BGD

Batch Gradient Descent
深度学习模型不会同时处理整个数据集,⽽是将数据拆分成⼩批量,通过向量化计算⽅式进⾏处理。

istory	=	model.fit(X_train,	y_train,	#	指定训练集
					epochs=30,						#	指定训练的轮次
					batch_size=128,	#	指定数据批量
					validation_data=(X_test,	y_test))	#	指定验证集

上百个样本同时并⾏处理,BGD提升了效率,但并没有解决局部最低点的问题。

3.2随机梯度下降SGD

Stochastic Gradient Descent
每次只随机选择⼀个样本来更新模型参数。因此,这种⽅法每轮次的学习速度⾮常快,但是所需更新的轮次也特别多。随机梯度下降中参数更新的⽅向不如批量梯度下降精确,每次也并不⼀定是向着最低点进⾏。但这种波动反⽽有⼀个好处:在有很多局部最低点的盆地区域中,随机地波动可能会使得优化的⽅向从当前的局部最低点跳到另⼀个更好的局部最低点,最终收敛于⼀个较好的点,甚⾄是全局最低点。

ann.compile(loss=keras.losses.categorical_crossentropy,	
			optimizer=keras.optimizers.SGD())	#	指定SGD优化器

3.3⼩批量随机梯度下降MBGD

每次参数更新时,从训练集中随机选择m个样本进⾏学习。选择合理的批量⼤⼩后,MBGD既可以通过随机扰动产⽣跳出局部最低点的效果,⼜⽐SGD性能稳定。

keras.optimizers.SGD(lr=0.02,	 	#设定优化器中的学习速率(默认值为0.01) 	
					decay=0.1)	#设定优化器中的衰减率

3.4动量SGD

更新参数w时不仅要考虑当前的梯度(当前的加速度),还要考虑上⼀次的参数更新(当前的在Keras的SGD优化器中,可以通过momentum选项设定动量:

optimizer=keras.optimizers.SGD(lr=0.01,	#	在优化器中设定学习速率
				momentum=0.9))	#	在优化器中设定动量⼤⼩

3.5上坡时减少动量——NAG

在下坡时、增加动量之后,在越过局部最低点后的上坡过程中计算参数的梯度时⼜减去了动量项。

optimizer=keras.optimizers.SGD(lr=0.01,		#	在优化器中设定学习速率	
					momentum=0.9,	#	在优化器中设定动量⼤⼩			
					nesterov=True))	#设定涅斯捷罗夫梯度加速

3.6Adagrad⾃适应梯度(adaptive gradient)

它根据前⼏轮迭代时的历史梯度值来调整学习速率。对于数据集中的稀疏特征,速率较⼤,梯度下降步幅将较⼤;对⾮稀疏特征,则使⽤较⼩的速率更新。因
此,这个优化算法适合处理含稀疏特征的数据集。⽐如,在⽂本处理的词向量
(word embedding)训练过程中,对频繁出现的单词赋予较⼩的更新,对不经常出现的单词则赋予较⼤的更新。

keras.optimizers.adagrad()	#	学习速率⾃适应

3.7 加权平均值计算⼆阶动量——RMSProp

RMSProp抑制衰减的⽅法不同于普通的动量,它是采⽤窗⼝滑动加权平均值计算⼆阶动量,同时它也有保存Adagrad中每个参数⾃适应不同的学习速率的优点。

keras.optimizers.RMSprop()	#	RMSprop优化器

3.8多种优化思路的集⼤成者——Adam

Adam全称为Adaptive Moment Estimation,相当于Adaptive + Momentum。它集成了SGD的⼀阶动量和RMSProp的⼆阶动量,⽽且也是⼀种不同参数⾃适应不同学习速率的⽅法,与
AdaDelta和RMSProp的区别在于,它计算历史梯度衰减的⽅式类似动量,⽽不是使⽤平⽅衰减。

keras.optimizers.Adam(learning_rate=0.001,	#	学习速率
					beta_1=0.9,		#	⼀阶动量指数衰减速率
					beta_2=0.999,	#	⼆阶动量指数衰减速率,	对于稀疏矩阵值应接近1
					amsgrad=False)

Adam是最常⻅的,⽬前也是⼝碑⽐较好的优化器。

四:激活函数

回想:在逻辑回归中,输⼊的特征通过加权、求和后,还将通过⼀个Sigmoid逻辑函数将线性回归值压缩⾄[0,1]区间,以体现分类概率值。这个逻辑函数在神经⽹络中被称为激活函数。
现在:在神经⽹络中,不仅最后的分类输出层需要激活函数,⽽且每⼀层都需要进⾏激活,然后向下⼀层输⼊被激活之后的值。不过神经⽹络中间层的输出值,没有必要位于[0,1]区间,因为中间层只负责⾮线性激活,并不负责输出分类概率和预测结果。

为什么神经网络的每一层都需要激活?
没有激活函数,多层网络退化为单层线性变换,无论多少层都等价于单层
层1: h₁ = w₁x
层2: h₂ = w₂h₁ = w₂w₁x = w'x  
层3: ŷ = w₃h₂ = w₃w₂w₁x = w''x
没有激活:网络只是"加权求和",再深也是线性组合。
有激活:每层把数据"掰弯"一次,多层叠加能拟合任意复杂形状。
函数公式特点
Sigmoid1/(1+e⁻ˣ)输出(0,1),曾用于分类,现在少用
Tanh(eˣ-e⁻ˣ)/(eˣ+e⁻ˣ)输出(-1,1),零中心化
ReLUmax(0,x)计算快、缓解梯度消失,现在最常用
Leaky ReLUmax(0.01x, x)解决ReLU死亡问题

4.1Sigmoid函数和梯度消失

在这里插入图片描述

在这里插入图片描述
Sigmoid函数应⽤于深度神经⽹络中时有⽐较致命的缺点——会出现梯度消失(gradient vanishing)的情况。梯度消失可以这样简单地理解:反向传播求误差时,需要对激活函数进⾏求导,将来⾃输出损失的反馈信号传播到更远的层。如果需要经过很多层,那么信号可能会变得⾮常微弱,甚⾄完全丢失,⽹络最终变得⽆法训练。

4.2Tanh函数

在这里插入图片描述

在这里插入图片描述
Tanh函数是⼀个以0为中⼼的分布函数,它的速度⽐Sigmoid函数快,然⽽并没有解决梯度消失问题。

4.3 ReLU函数

ReLU函数的特点是单侧抑制,输⼊信号⼩于等于0时,输出是0;输⼊信号⼤于0时,输出等于输⼊。ReLU对于随机梯度下降的收敛很迅速
f(z)=max(0,z)
在这里插入图片描述
ReLU既能够进⾏神经⽹络的激活,收敛速度快,⼜不存在梯度消失现象。因此,⽬前的神经⽹络中已经很少有⼈在使⽤Sigmoid函数了,ReLU基本上算是主流。
ReLU函数也有缺点,它训练的时候⽐较脆弱,容易“死掉”。⽽且不可逆,“死”了就“活”不过来了。⽐如,⼀个⾮常⼤的梯度流过⼀个ReLU神经元,参数更新之后,这个神经元可能再也不会对任何输⼊进⾏激活反映。所以⽤ReLU的时候,学习速率绝对不能设得太⼤,因为那样会“杀死”⽹络中的很多神经元。
(因为加了激活函数后,反向传播还要对激活函数求一次导)

激活函数导数最大值问题
Sigmoidσ(x)(1-σ(x))0.25反向传播时梯度×0.25,越乘越小
Tanh1-tanh²(x)1实际大多<1,仍衰减
ReLU1 (x>0) / 0 (x<0)1不压缩梯度,直接传回去

4.4Leaky ReLU和PReLU

f(z)=max(εz,z)
在这里插入图片描述
ε是很⼩的负数梯度值,⽐如0. 01。这样做的⽬的是使负轴信息不会全部丢失,解决了ReLU神经元“死掉”的问题。因为它不会出现零斜率部分,⽽且它的训练速度更快。Leaky ReLU有⼀个问题,就是在接收很⼤负值的情况下,Leaky ReLU会导致神经元饱和,从⽽基本上处于⾮活动状态。

eLU函数
在这里插入图片描述
———————————————————————————————————————————————————
这么多的激活函数,都是针对神经⽹络内部的神经元⽽⾔的,⽬的是将线性变换转换为层与层之间的⾮线性变换。
但是神经⽹络中的最后⼀层,输出层,二分类问题仍然用sigmod。多分类问题常用Softmax
在这里插入图片描述

五:损失函数

激活函数是属于层的参数,每⼀层都可以指定⾃⼰的激活函数,⽽损失函数是属于整个神经⽹络的参数。
神经⽹络中损失函数的选择是根据问题类型⽽定的,指导原则如下。

对于连续值向量的回归问题,使⽤我们⾮常熟悉的均⽅误差损失函数:

ann.compile(optimizer='adam',	
			loss='mse')	#	均⽅误差损失函数
对于⼆分类问题,使⽤同样熟悉的⼆元交叉熵损失函数:

ann.compile(optimizer='adam',														
			loss='binary_crossentropy',			#	⼆元交叉熵损失函数											
			metrics=['accuracy'])
对于多分类问题,如果输出是one-hot编码,则⽤分类交叉熵损失函数:

ann.compile(optimizer='adam',	
			loss='categorical_crossentropy',	#	分类交叉熵损失函数
			metrics=['accuracy'])
对于多分类问题,如果输出是整数数值,则使⽤稀疏分类交叉熵损失函数:
ann.compile(optimizer='adam',	
			loss='sparse_categorical_crossentropy',	#	稀疏分类交叉熵损失函数												metrics=['accuracy'])

对于序列问题,如语⾳识别等,则可以⽤时序分类(Connectionist Temporal Classification,CTC)等损失函数。

六:神经网络正则化DROPOUT

正则化在干什么
核心目的:防止过拟合(模型死记硬背训练数据,泛化能力差)

正则化方法怎么做效果
L2正则化损失函数 + λ∑w²惩罚大权重,让权重变小变分散
L1正则化损失函数 + λ∑|w|产生稀疏权重,部分变0(特征选择)
Dropout训练时随机丢弃部分神经元防止神经元共适应,强制冗余学习
早停(Early Stop)验证集不提升就停止训练防止训练过度

一句话:正则化 = 给模型加约束,让它学"规律"而不是"噪音",提高泛化能力。

Dropout也是神经⽹络中的层组件之⼀。它的原理⾮常奇特:在某⼀层之后添加Dropout层,意思就是随机将该层的⼀部分神经元的输出特征丢掉(设为0),相当于随机消灭⼀部分神经元。

from	keras.layers	import	Dropout	#	导⼊Dropout
ann	=	Sequential()	#	创建⼀个序贯ANN模型
ann.add(Dense(units=12,	input_dim=12,	activation	=	'relu'))	#	添加输⼊层ann.add(Dense(units=24,	activation	=	'relu'))	#	添加隐层
ann.add(Dropout(0.5))	#	添加Dropout层
ann.add(Dense(units=48,	activation	=	'relu'))	#	添加隐层
ann.add(Dropout(0.5))	#	添加Dropout层
ann.add(Dense(units=96,	activation	=	'relu'))	#	添加隐层
ann.add(Dropout(0.5))	#	添加Dropout层
ann.add(Dense(units=192,	activation	=	'relu'))	#	添加隐层
ann.add(Dropout(0.5))	#	添加Dropout层
ann.add(Dense(units=1,	activation	=	'sigmoid'))	#	添加输出层
ann.compile(optimizer	=	'adam',	#	优化器
			loss	=	'binary_crossentropy',	#损失函数	
			metrics	=	['acc'])	#	评估指标

七:深度神经⽹络的调试及性能优化

回调,就是在训练进⾏过程中,根据⼀些预设的指示对训练进⾏控制。下⾯是⼏个常⽤的回调函数。
ModelCheckpoint:在训练过程中的不同时间点保存模型,也就是保存当前⽹络的所有权重。
EarlyStopping:如果验证损失不再改善,则中断训练。这个回调函数常与ModelCheckpoint结合使⽤,以保存最佳模型。
ReduceLROnPlateau:在训练过程中动态调节某些参数值,⽐如优化器的学习速率,从⽽跳出训练过程中的⾼原区。
TensorBoard:将模型训练过程可视化。

#	导⼊回调功能
from	keras.callbacks	import	ModelCheckpoint
from	keras.callbacks	import	EarlyStopping
from	keras.callbacks	import	ReduceLROnPlateau
#	设定要回调的功能
earlystop	=	EarlyStopping(monitor='val_acc',	patience=20,	
				verbose=1,	restore_best_weights=True)
reducelr	=	ReduceLROnPlateau(monitor='val_acc',	factor=0.5,	
				patience=3,	verbose=1,	min_lr=1e-7)
modelckpt	=	ModelCheckpoint(filepath='ann.h5',	monitor='val_acc',																							verbose=1,	save_best_only=True,	mode='max')callbacks	=	[earlystop,	reducelr,	modelckpt]	#	设定回调
history	=	ann.fit(X_train,	y_train,	#	指定训练集
					batch_size=128,		#	指定批量⼤⼩
					validation_data	=	(X_test,	y_test),	#	指定验证集
					epochs=100,			#	指定轮次
					callbacks=callbacks)	#	指定回调功能

八:反向传播三个问题

8.1梯度消失

核心原因:反向传播是连乘,如果每乘的数都<1,越乘越接近0。

h₁ = σ(z₁) = σ(w₁x)           z₁ = w₁x
h₂ = σ(z₂) = σ(w₂h₁)          z₂ = w₂h₁  
ŷ  = σ(z₃) = σ(w₃h₂)          z₃ = w₃h₂
L  = (ŷ - y)²


∂L/∂w₁ = ∂L/∂ŷ × σ'(z₃) × w₃ × σ'(z₂) × w₂ × σ'(z₁) × x
          ↑___________↑___________↑___________↑
                多个<1的数连乘

sigmoid的σ’(x) ≤ 0.25,3层网络就乘了3次0.25,梯度只剩1/64。
结果:前面层梯度≈0,学不到东西。
解决:ReLU(导数=1)、残差连接(跳层)。

真正解决梯度消失的“武器”是残差连接(residual connection)结构。
它的基本思想是:在⼤型深度⽹络中(⾄少10层以上),让前⾯某层的输出跨越多层直接输⼊⾄较靠后的层,形成神经⽹络中的捷径(shortcut)。
这样,就不必担⼼过⼤的⽹络中梯度逐渐消失的问题了。
残差连接结构在最新的深度神经⽹络结构中⼏乎都有出现,因为它对解决梯度消失问题⾮常有效。

8.2梯度爆炸

核心原因:如果权重w>1,连乘会指数增长

∂L/∂w₁ = ... × w₃ × w₂ × ...

假设w₂=w₃=2,10层网络:2^10 = 1024倍放大

结果:梯度→∞,权重疯狂更新,Loss变NaN。
解决:梯度裁剪(设上限)、好的初始化(Xavier/He)。,权重正则化

8.3收敛困难

核心原因:梯度方向不对,或学习率不合适。
解决:Adam优化器(自适应学习率)、学习率衰减、早停。

问题一句话
梯度消失乘小数乘没了,前面层学不动
梯度爆炸乘大数乘爆了,模型崩掉
收敛困难步子不对,用Adam调

更多推荐