持续创造,加快生长!这是我参加「日新计划 6 月更文挑战」的第3天,点击检查活动概况
- 作者:韩信子@ShowMeAI
- 教程地址:www.showmeai.tech/tutorials/3…
- 本文地址:www.showmeai.tech/article-det…
- 声明:版权一切,转载请联络平台与作者并注明出处
- 保藏ShowMeAI检查更多精彩内容
本系列为 斯坦福CS231n 《深度学习与核算机视觉(Deep Learning for Computer Vision)》的全套学习笔记,对应的课程视频能够在 这儿 检查。更多资料获取办法见文末。
引言
经过ShowMeAI前序文章 深度学习与CV教程(3) | 丢掉函数与最优化 ,深度学习与CV教程(4) | 神经网络与反向传达,深度学习与CV教程(5) | 卷积神经网络 咱们现已学习把握了以下内容:
- 核算图:核算前向传达、反向传达
- 神经网络:神经网络的层结构、非线性函数、丢掉函数
- 优化战略:梯度下降使丢掉最小
- 批梯度下降:小批量梯度下降,每次迭代只用练习数据中的一个小批量核算丢掉和梯度
- 卷积神经网络:多个滤波器与原图画独立卷积得到多个独立的激活图
【本篇】和【下篇】 ShowMeAI解说练习神经网络的中心办法与关键点,首要包括:
- 初始化:激活函数挑选、数据预处理、权重初始化、正则化、梯度检查
- 练习动态:监控学习进程、参数更新、超参数优化
- 模型评估:模型集成(model ensembles)
本篇重点
- 激活函数
- 数据预处理
- 权重初始化
- 批量归一化
- 监控学习进程
- 超参数调优
1.激活函数
关于激活函数的详细常识也能够参考阅览ShowMeAI的 深度学习教程 | 吴恩达专项课程 全套笔记解读 中的文章 浅层神经网络 里【激活函数】板块内容。
在全衔接层或许卷积层,输入数据与权重相乘后累加的成果送给一个非线性函数,即激活函数(activation function)。每个激活函数的输入都是一个数字,然后对其进行某种固定的数学操作。
下面是在实践中或许遇到的几种激活函数:
1.1 Sigmoid函数
数学公式:(x)=1/(1+e−x)\sigma(x) = 1 / (1 + e^{-x})
求导公式:d(x)dx=(1−(x))(x)\frac{d\sigma(x)}{dx} = \left( 1 – \sigma(x) \right) \sigma(x) (不小于 00 )
特色:把输入值「挤压」到 00 到 11 规模内。Sigmoid 函数把输入的实数值「挤压」到 00 到 11 规模内,很大的负数变成 00,很大的正数变成 11,在前史神经网络中,Sigmoid 函数很常用,由于它对神经元的激活频率有杰出的解释:从完全不激活(00)到假定最大频率处的完全饱满(saturated)的激活(11) 。
可是现在 Sigmoid 函数现已很少运用了,由于它有三个首要缺陷:
缺陷①:Sigmoid 函数饱满时使梯度消失
- 当神经元的激活在挨近 00 或 11 处时(即门单元的输入过或过大时)会饱满:在这些区域,梯度简直为 00。
- 在反向传达的时分,这个部分梯度要与丢掉函数关于这个门单元输出的梯度相乘。因而,假如部分梯度十分小,那么相乘的成果也会挨近零,这会「杀死」梯度,简直就有没有信号经过神经元传到权重再到数据了。
- 还有,为了防止饱满,有必要关于权重矩阵初始化特别留意。比方,假如初始权重过大,那么大多数神经元将会饱满,导致网络就简直不学习了。
缺陷②:Sigmoid 函数的输出不是零中心的
- 这个性质会导致神经网络后边层中的神经元得到的数据不是零中心的。
- 这一状况将影响梯度下降的运作,由于假如输入神经元的数据总是正数(比方在 (∑iwixi+b)\sigma(\sum_{i}w_ix_i+b) )中每个输入 xx 都有 x>0x > 0),那么关于 ww 的梯度在反向传达的进程中,将会要么全部是正数,要么全部是负数(依据该 Sigmoid 门单元的回传梯度来定,回传梯度可正可负,而 ddW=XT⋅′\frac{d\sigma}{dW}=X^T \cdot\sigma’ 在 XX 为正时恒为非负数)。
- 这将会导致梯度下降权重更新时呈现 zz 字型的下降。该问题相关于上面的神经元饱满问题来说仅仅个小麻烦,没有那么严峻。
缺陷③: 指数型核算量比较大。
1.2 tanh函数
数学公式:tanh(x)=2(2x)−1\tanh(x) = 2 \sigma(2x) -1
特色:将实数值压缩到 [−1,1][-1,1] 之间
和 SigmoidSigmoid 神经元相同,它也存在饱满问题,可是和 SigmoidSigmoid 神经元不同的是,它的输出是零中心的。因而,在实践操作中,tanhtanh 非线性函数比 SigmoidSigmoid 非线性函数更受欢迎。留意 tanhtanh 神经元是一个简略扩大的 SigmoidSigmoid 神经元。
1.3 ReLU 函数
数学公式:f(x)=max(0,x)f(x) = \max(0, x)
特色:一个关于 00 的阈值
长处:
- ReLU 只需负半轴会饱满;节约核算资源,不含指数运算,只对一个矩阵进行阈值核算;更契合生物学观念;加快随机梯度下降的收敛。
- Krizhevsky 论文指出比 Sigmoid 和 tanh 函数快6倍之多,据称这是由它的线性,非饱满的公式导致的。
缺陷:
- 仍有一半会饱满;非零中心;
- 练习时,ReLU 单元比较脆弱并且或许「死掉」。
- 举例来说,当一个很大的梯度流过 ReLU 的神经元的时分,由于梯度下降,或许会导致权重更新到一种特别的状态(比方大多数的 ww 都小于 00 ),在这种状态下神经元将无法被其他任何数据点再次激活。假如这种状况产生,那么从此一切流过这个神经元的梯度将都变成 00,也便是说,这个 ReLU 单元在练习中将不可逆转的逝世,由于这导致了数据多样化的丢掉。
- 例如,假如学习率设置得太高(本来大多数大于 00 的 ww 更新后都小于 00 了),或许会发现网络中40%的神经元都会死掉(在整个练习会集这些神经元都不会被激活)。
- 经过合理设置学习率,这种状况的产生概率会下降。
1.4 Leaky ReLU
公式:f(x)=1(x<0)(x)+1(x>=0)(x)f(x) = \mathbb{1}(x < 0) (\alpha x) + \mathbb{1}(x>=0) (x),\alpha 是小常量
特色:处理「 ReLU 逝世」问题,x<0x<0 时给出一个很小的梯度值,比方 0.010.01。
Leaky ReLU 修正了 x<0x<0 时 ReLU 的问题,有研究指出这个激活函数表现很不错,可是其作用并不是很安稳。Kaiming He等人在2015年发布的论文 Delving Deep into Rectifiers 中介绍了一种新办法 PReLU,把负区间上的斜率作为每个神经元中的一个参数,可是无法确定该激活函数在不同使命中均有好处。
1.5 指数线性单元(Exponential Linear Units,ELU)
公式:f(x)={xifx>0(exp(x)−1)otherwisef(x)=\begin{cases} x & if \space\space x>0 \\ \alpha(exp(x)-1) & otherwise \end{cases}
特色:介于 ReLU 和Leaky ReLU 之间
具有 ReLU 的一切长处,可是不包括核算量;介于 ReLU 和 Leaky ReLU 之间,有负饱满的问题,可是对噪声有较强的鲁棒性。
1.6 Maxout
公式:max(w1Tx+b1,w2Tx+b2)max(w_1^Tx+b_1, w_2^Tx + b_2)
特色:是对 ReLU 和 leaky ReLU 的一般化归纳
关于权重和数据的内积成果不再运用非线性函数,直接比较两个线性函数。ReLU 和 Leaky ReLU 都是这个公式的特殊状况,比方 ReLU 便是当 w1=1w_1=1,b1=0b_1=0 的时分。
Maxout 拥有 ReLU 单元的一切长处(线性操作和不饱满),而没有它的缺陷(逝世的 ReLU 单元)。可是和 ReLU 比照,它每个神经元的参数数量增加了一倍,这就导致整体参数量激增。
实践运用Tips :
- 用 ReLU 函数。留意设置好学习率,你能够监控你的网络中逝世的神经元占的份额。
- 假如单元逝世问题困扰你,就试试Leaky ReLU 或许 Maxout,不要再用 Sigmoid 了。也能够试试 tanh,可是其作用应该不如 ReLU 或许 Maxout。
2.数据预处理
关于深度学习数据预处理的常识也能够比照阅览ShowMeAI的深度学习教程 | 吴恩达专项课程 全套笔记解读中的文章深度学习的有用层面里【规范化输入】板块内容。
关于数据预处理有 3 个常用的符号,数据矩阵 XX,假定其尺度是 [ND][N \times D](NN 是数据样本的数量,DD 是数据的维度)。
2.1 减均值(Mean Subtraction)
减均值法是数据预处理最常用的办法。它对数据中每个独立特征减去均匀值,在每个维度上都将数据的中心都迁移到原点。
在 numpy 中,该操作能够经过代码 X -= np.mean(X, axis=0)
完成。而关于图画,更常用的是对一切像素都减去一个值,能够用 X -= np.mean(X)
完成,也能够在 3 个颜色通道上别离操作。
详细来讲,假如练习数据是 5000050000 张 3232332 \times 32 \times 3 的图片:
- 第一种做法是减去均值图画,行将每张图片拉生长为 30723072 的向量,50000307250000 \times 3072 的矩阵按列求均匀,得到一个含有 30723072 个数的均值图画,练习集测验集验证集都要减去这个均值,AlexNet 是这种办法;
- 第二种做法是依照通道求均匀,RGB三个通道每个通道一个均值,即每张图片的 30723072 个数中,RGB各有 323232 \times 32 个数,要在 50000323250000 \times 32 \times 32 个数中求一个通道的均值,终究的均值有 33 个数字,然后一切图片每个通道都要减去对应的通道均值,VGGNet是这种办法。
之所以履行减均值操作,是由于处理输入数据大多数都是正或许负的问题。尽管经过这种操作,数据变成零中心的,可是仍然只能第一层处理 Sigmoid 非零均值的问题,后边会有更严峻的问题。
2.2 归一化(Normalization)
归一化是指将数据的一切维度都归一化,使其数值规模都近似相等。
有两种常用办法能够完成归一化。
- 第一种是先对数据做零中心化(zero-centered)处理,然后每个维度都除以其规范差,完成代码为
X /= np.std(X, axis=0)
。 - 第二种是对每个维度都做归一化,使得每个维度的最大和最小值是 11 和 −1-1。这个预处理操作只需在确信不同的输入特征有不同的数值规模(或计量单位)时才有意义,但要留意预处理操作的重要性简直等同于学习算法本身。
在图画处理中,由于像素的数值规模简直是共同的(都在0-255之间),所以进行这个额定的预处理进程并不是很必要。
- 左边:原始的 2 维输入数据。
- 中心:在每个维度上都减去均匀值后得到零中心化数据,现在数据云是以原点为中心的。
- 右边:每个维度都除以其规范差来调整其数值规模,赤色的线指出了数据各维度的数值规模。
在中心的零中心化数据的数值规模不同,但在右边归一化数据中数值规模相同。
2.3 主成分剖析(PCA)
这是另一种机器学习中比较常用的预处理办法,但在图画处理中根本不必。在这种处理中,先对数据进行零中心化处理,然后核算协方差矩阵,它展示了数据中的相关性结构。
# 假定输入数据矩阵X的尺度为[N x D]
X -= np.mean(X, axis = 0) # 对数据进行零中心化(重要)
cov = np.dot(X.T, X) / X.shape[0] # 得到数据的协方差矩阵,DxD
数据协方差矩阵的第 (i,j)(i, j) 个元素是数据第 ii 个和第 jj 个维度的协方差。详细来说,该矩阵的对角线上的元素是方差。还有,协方差矩阵是对称和半正定的。咱们能够对数据协方差矩阵进行 SVD(奇异值分化)运算。
U,S,V = np.linalg.svd(cov)
UU 的列是特征向量,SS 是装有奇异值的1维数组(由于 cov 是对称且半正定的,所以S中元素是特征值的平方)。为了去除数据相关性,将现已零中心化处理过的原始数据投影到特征基准上:
Xrot = np.dot(X,U) # 对数据去相关性
np.linalg.svd
的一个杰出性质是在它的回来值U中,特征向量是依照特征值的巨细排列的。咱们能够运用这个性质来对数据降维,只需运用前面的小部分特征向量,丢弃掉那些包括的数据没有方差的维度,这个操作也被称为 主成分剖析(Principal Component Analysis 简称PCA) 降维:
Xrot_reduced = np.dot(X, U[:,:100]) # Xrot_reduced 变成 [N x 100]
经过上面的操作,将原始的数据集的巨细由 [ND][N \times D] 降到了 [N100][N \times 100],留下了数据中包括最大方差的的 100 个维度。一般运用 PCA 降维过的数据练习线性分类器和神经网络会抵达十分好的性能作用,同时还能节约时刻和存储器空间。
有一问题是为什么运用协方差矩阵进行 SVD 分化而不是运用原 XX 矩阵进行?
其实都是能够的,只对数据 XX(能够不是方阵)进行 SVD 分化,做 PCA 降维(避免了求协方差矩阵)的话一般用到的是右奇异向量 VV,即 VV 的前几列是需求的特征向量(留意
np.linalg.svd
回来的是V.T
)。XX 是NDN \times D,则 UU 是 NNN \times N,VV 是 DDD \times D;而对协方差矩阵(DDD \times D)做 SVD 分化用于 PCA 降维的话,能够随意取左右奇异向量UU、VV(都是 DDD \times D)之一,由于两个向量是相同的。
2.4 白化(Whitening)
终究一个在实践中会看见的变换是白化(whitening)。白化操作的输入是特征基准上的数据,然后对每个维度除以其特征值来对数值规模进行归一化。
白化变换的几许解释是:假如数据服从多变量的高斯散布,那么经过白化后,数据的散布将会是一个均值为零,且协方差相等的矩阵。
该操作的代码如下:
# 对数据进行白化操作:
# 除以特征值
Xwhite = Xrot / np.sqrt(S + 1e-5)
留意分母中增加了 1e-5
(或一个更小的常量)来防止分母为 00,该变换的一个缺陷是在变换的进程中或许会夸大数据中的噪声,这是由于它将一切维度都拉伸到相同的数值规模,这些维度中也包括了那些只需很少差异性(方差小)而大多是噪声的维度。
在实践操作中,这个问题能够用更强的滑润来处理(例如:选用比 1e-5
更大的值)。
下图为 CIFAR-10 数据集上的 PCA、白化等操作成果可视化。
从左往右4张子图:
- 第1张:一个用于演示的图片集合,含 49 张图片。
- 第2张:3072 个特征向量中的前 144 个。靠前面的特征向量解释了数据中大部分的方差。
-
第3张:49 张经过了PCA降维处理的图片,只运用这儿展示的这 144 个特征向量。为了让图片能够正常显现,需求将 144 维度从头变成依据像素基准的 3072 个数值。由于U是一个旋转,能够经过乘以
U.transpose()[:144,:]
来完成,然后将得到的 3072 个数值可视化。能够看见图画变得有点含糊了,可是,大多数信息仍是保留了下来。 -
第4张:将「白化」后的数据进行显现。其间 144个 维度中的方差都被压缩到了相同的数值规模。然后 144 个白化后的数值经过乘以
U.transpose()[:144,:]
转换到图画像素基准上。
2.5 实践运用
实践上在卷积神经网络中并不会选用PCA和白化,对数据进行零中心化操作仍是十分重要的,对每个像素进行归一化也很常见。
补充阐明:
进行预处理很重要的一点是:任何预处理战略(比方数据均值)都只能在练习集数据上进行核算,然后再运用到验证集或许测验集上。
- 一个常见的过错做法是先核算整个数据集图画的均匀值然后每张图片都减去均匀值,终究将整个数据集分红练习/验证/测验集。正确的做法是先分红练习/验证/测验集,仅仅从练习会集求图片均匀值,然后各个集(练习/验证/测验集)中的图画再减去这个均匀值。
3.权重初始化
关于神经网络权重初始化的常识也能够比照阅览ShowMeAI的深度学习教程 | 吴恩达专项课程 全套笔记解读中的文章深度学习的有用层面里【权重初始化缓解梯度消失和爆破】板块内容。
初始化网络参数是练习神经网络里十分重要的一步,有不同的初始化办法,咱们来看看他们各自的特色。
3.1 全零初始化
对一个两层的全衔接网络,假如输入给网络的一切参数初始化为 00 会怎样?
这种做法是过错的。 由于假如网络中的每个神经元都核算出相同的输出,然后它们就会在反向传达中核算出相同的梯度,然后进行相同的参数更新。换句话说,假如权重被初始化为相同的值,神经元之间就失去了不对称性的源头。
3.2 小随机数初始化
现在权重初始值要十分挨近 00 又不能等于 00,处理办法便是将权重初始化为很小的数值,以此来打破对称性。
其思路是:假如神经元刚开端的时分是随机且不相等的,那么它们将核算出不同的更新,并将自身变成整个网络的不同部分。
完成办法是:W = 0.01 * np.random.randn(D,H)
。其间 randn
函数是依据零均值和规范差的一个高斯散布来生成随机数的。
小随机数初始化在简略的网络中作用比较好,可是网络结构比较深的状况纷歧定会得到好的成果。比方一个 10 层的全衔接网络,每层 500 个神经元,运用 tanhtanh 激活函数,用小随机数初始化。
代码与输出图画如下:
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
# 假定一些高斯散布单元
D = np.random.randn(1000, 500)
hidden_layer_sizes = [500]*10 # 躲藏层尺度都是500,10层
nonlinearities = ['tanh']*len(hidden_layer_sizes) # 非线性函数都是用tanh函数
act = {'relu': lambda x: np.maximum(0, x), 'tanh': lambda x: np.tanh(x)}
Hs = {}
for i in range(len(hidden_layer_sizes)):
X = D if i == 0 else Hs[i-1] # 当时躲藏层的输入
fan_in = X.shape[1]
fan_out = hidden_layer_sizes[i]
W = np.random.randn(fan_in, fan_out) * 0.01 # 权重初始化
H = np.dot(X, W) # 得到当时层输出
H = act[nonlinearities[i]](H) # 激活函数
Hs[i] = H # 保存当时层的成果并作为下层的输入
# 观察每一层的散布
print('输入层的均值:%f 方差:%f'% (np.mean(D), np.std(D)))
layer_means = [np.mean(H) for i,H in Hs.items()]
layer_stds = [np.std(H) for i,H in Hs.items()]
for i,H in Hs.items():
print('躲藏层%d的均值:%f 方差:%f' % (i+1, layer_means[i], layer_stds[i]))
# 画图
plt.figure()
plt.subplot(121)
plt.plot(list(Hs.keys()), layer_means, 'ob-')
plt.title('layer mean')
plt.subplot(122)
plt.plot(Hs.keys(), layer_stds, 'or-')
plt.title('layer std')
# 绘制散布图
plt.figure()
for i,H in Hs.items():
plt.subplot(1, len(Hs), i+1)
plt.hist(H.ravel(), 30, range=(-1,1))
plt.show()
能够看到只需第一层的输出均值方差比较好,输出挨近高斯散布,后边几层均值方差根本为 00,这样导致的成果是正向传达的激活值根本为 00,反向传达时就会核算出十分小的梯度(因权重的梯度便是层的输入,输入挨近 00,梯度挨近 00 ),参数根本不会更新。
假如上面的比如不必小随机数,即 W = np.random.randn(fan_in, fan_out) * 1
,此刻会怎样呢?
此刻,由于权重较大并且运用的 tanh 函数,一切神经元都会饱满,输出为 +1+1 或 −1-1,梯度为 00,如下图所示,均值在 00 邻近动摇,方差较大在 0.980.98 邻近动摇,神经元输出大多为 +1+1 或 −1-1。
3.3 Xavier/He初始化(校准方差)
上述剖析能够看出,权重过小或许会导致网络崩溃,权重过大或许会导致网络饱满,所以都在研究出一种合理的初始化办法。一种很好的经历是运用Xavier初始化:
W = np.random.randn(fan_in, fan_out) / np.sqrt(fan_in)
这是Glorot等在2010年发表的 论文。这样就保证了网络中一切神经元起始时有近似相同的输出散布。实践经历证明,这样做能够进步收敛的速度。
原理:假定神经元的权重 ww 与输入 xx 的内积为 s=∑inwixis = \sum_i^n w_i x_i,这是还没有进行非线性激活函数运算之前的原始数值。此刻 ss 的方差:
前三步运用的是方差的性质(累加性、独立变量相乘);
第三步中,假定输入和权重的均值都是 00,即 E[xi]=E[wi]=0E[x_i] = E[w_i] = 0,可是 ReLU 函数中均值应该是正数。在终究一步,咱们假定一切的 wi,xiw_i,x_i 都服从相同的散布。从这个推导进程咱们能够看见,假如想要 ss 有和输入 xx 相同的方差,那么在初始化的时分有必要保证每个权重 ww 的方差是1/n1/n 。
又由于关于一个随机变量 XX 和标量 aa,有 Var(aX)=a2Var(X)\text{Var}(aX) = a^2\text{Var}(X),这就阐明能够让 ww 依据规范高斯散布(方差为1)取样,然后乘以 a=1/na = \sqrt{1/n},即 Var(1/n⋅w)=1/nVar(w)=1/n\text{Var}( \sqrt{1/n}\cdot w) = 1/n\text{Var}(w)=1/n,此刻就能保证 Var(s)=Var(x)\text{Var}(s) =\text{Var}(x)。
代码为:W = np.random.randn(fan_in, fan_out) / np.sqrt(fan_in)
,其间fan_in
便是上文的 nn。
不过作者在论文中引荐的是:
W = np.random.randn(fan_in, fan_out) / np.sqrt(fan_in + fan_out)
,使 Var(w)=2/(nin+nout)\text{Var}(w) = 2/(n_{in} + n_{out}),其间 nin,noutn_{in}, n_{out} 是前一层和后一层中单元的个数,这是依据妥协和对反向传达中梯度的剖析得出的定论)
输出成果为:
图上能够看出,后边几层的输入输出散布很挨近高斯散布。
可是运用 ReLU 函数这种联系会被打破,相同 ww 运用单位高斯并且校准方差,可是运用 ReLU 函数后每层会消除一半的神经元(置 00 ),成果会使方差每次减半,会有越来越多的神经元失活,输出为 00 的神经元越来越多。如下图所示:
处理办法是 W = np.random.randn(fan_in, fan_out) / np.sqrt(fan_in/2)
。由于每次有一半的神经元失活,校准时除2即可,这样得到的成果会比较好。
这是2015年何凯明的论文 Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification 提到的办法,这个办法是神经网络算法运用 ReLU 神经元时的当时最佳引荐。成果如下:
3.4 稀少初始化
另一个处理非标定方差的办法是将一切权重矩阵设为 00,可是为了打破对称性,每个神经元都同下一层固定数目的神经元随机衔接(其权重数值由一个小的高斯散布生成)。一个比较典型的衔接数目是10个。
偏置项(biases)的初始化:一般将偏置初始化为 00。
3.5 实践运用
合适的初始化设置仍然是现在比较活跃的研究领域,经典的论文有:
- Understanding the difficulty of training deep feedforward neural networks by Glorot and Bengio, 2010
- Exact solutions to the nonlinear dynamics of learning in deep linear neural networks by Saxe et al, 2013
- Random walk initialization for training very deep feedforward networks by Sussillo and Abbott, 2014
- Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification by He et al., 2015
- Data-dependent Initializations of Convolutional Neural Networks by Krhenbhl et al., 2015
- [All you need is a good init](arxiv.org/abs/1511.06…) by Mishkin and Matas, 2015
当时的引荐是运用 ReLU 激活函数,并且运用 w = np.random.randn(n) * sqrt(2.0/n)
来进行权重初始化,n
是上一层神经元的个数,这是何凯明的论文得出的定论,也称作 He初始化。
4.批量归一化(Batch Normalization)
关于Batch Normalization的详细图示解说也能够比照阅览ShowMeAI的深度学习教程 | 吴恩达专项课程 全套笔记解读中的文章网络优化:超参数调优、正则化、批归一化和程序结构里【Batch Normalization】板块内容。
4.1 概述
批量归一化 是 loffe 和 Szegedy 最近才提出的办法,该办法一定程度处理了怎么合理初始化神经网络这个棘手问题,其做法是让激活数据在练习开端前经过一个网络,网络处理数据使其服从规范高斯散布。
归一化是一个简略可求导的操作,所以上述思路是可行的。在完成层面,运用这个技巧一般意味着全衔接层(或许是卷积层,后续会讲)与激活函数之间增加一个BatchNorm层。在神经网络中运用批量归一化现已变得十分常见,在实践中运用了批量归一化的网络关于欠好的初始值有更强的鲁棒性。
4.2 原理
详细来说,咱们希望每一层网络的输入都近似契合规范高斯散布,考虑有 NN 个激活数据的小批量输入,每个输入 xx 有 DD 维,即 x=(x(1)⋯x(d))x = (x^{(1)} \cdots x^{(d)}),那么对这个小批量数据的每个维度进行归一化,使契合单位高斯散布,运用下面的公式:
- 其间的均值和方差是依据整个练习集核算出来的;
- 这个公式其实便是随机变量转化为规范高斯散布的公式,是可微的;
- 前向传达与反向传达也是运用小批量梯度下降(SGD),也能够运用这个小批量进行归一化;
- 在练习开端前进行归一化,而不是在初始化时;
- 卷积层每个激活图都有一个均值和方差;
- 对每个神经元别离进行批量归一化。
批量归一化会把输入约束在非线性函数的线性区域,有时分咱们并不想没有一点饱满,所以希望能操控饱满程度,即在归一化完成后,咱们鄙人一步增加两个参数去缩放平和移归一化后的激活数据:
这两个参数能够在网络中学习,并且能完成咱们想要的作用。的确,经过设置:(k)=Var[x(k)]\gamma ^{(k)}=\sqrt{\text{Var}[x^{(k)}]},(k)=E[x(k)]\beta ^{(k)}=\text{E}[x^{(k)}] 能够康复原始激活数据,假如这样做的确最优的话。现在网络有了为了让网络抵达较好的练习作用而去学习操控让 tanh 具有更高或更低饱满程度的才能。
当运用随机优化时,咱们不能依据整个练习集去核算。咱们会做一个简化:由于咱们在 SGD 中运用小批量,每个小批量都能够得到激活数据的均值和方差的估量。这样,用于归一化的数据完全能够参加梯度反向传达。
批量归一化的思维:考虑一个尺度为 mm 的小批量B。由于归一化被独登时运用于激活数据 xx 的每个维度,因而让咱们关注特定激活数据维度 x(k)x(k) 并且为了清楚起见省略 kk。在小批量中共有 mm 个这种激活数据维度 x(k)x(k):B=x1⋯m\text{B} ={x_{1 \cdots m}}
归一化后的值为:x1⋯m\hat{x}_{1 \cdots m}
线性转化后的值为:y1⋯my_{1 \cdots m}
这种线性转化是批量归一化转化:BN,:x1⋯m→y1⋯m\text{BN}_{\gamma, \beta} : x_{1 \cdots m} → y_{1 \cdots m}
于是,咱们的小批量激活数据 B=x1⋯m\text{B} ={x_{1 \cdots m}} 经过BN层,有两个参数需求学习:\gamma,\beta (\varepsilon 是为了保持数值安稳在小批量方差上增加的小常数)。
该BN层的输出为:yi=BN,(xi),i=1⋯m{y_i=\text{BN}_{\gamma, \beta}(x_i)},i=1 \cdots m,该层的核算有:
-
小批量均值:B←1m∑i=1mxi\mu _B\leftarrow \frac{1}{m} \sum_{i=1}^m x_i
-
小批量方差:B2←1m∑i=1m(xi−B)2\sigma^2 _B\leftarrow \frac{1}{m} \sum_{i=1}^m (x_i-\mu _B)^2
-
归一化:xi←xi−BB2+\hat{x} _i\leftarrow \frac{x_i-\mu _B}{\sqrt{\sigma^2 _B+\varepsilon } }
-
缩放平和移:yi←xi+≡BN,(xi)y_i\leftarrow \gamma \hat{x} _i+\beta \equiv \text{BN}_{\gamma,\beta }(x_i)
4.3 优势
- 改善经过网络的梯度流
- 具有更高的鲁棒性:允许更大的学习速率规模、减少对初始化的依靠
- 加快学习速率衰减,更容易练习
- 能够看作是一种正则办法,在原始输入 XX 上抖动
- 能够不运用Dropout,加快练习
补充阐明:测验时不运用小批量中核算的均值和方差,相反,运用练习期间激活数据的一个固定的经历均值,例如能够运用在练习期间的均匀值作为估量。
总结:批量归一化能够理解为在网络的每一层之前都做预处理,将输入数据转化为单位高斯数据或许进行平移伸缩,仅仅这种操作以另一种办法与网络集成在了一同。
5.层归一化(Layer Normalization)
事实证明,批量归一化能使网络更容易练习,可是对批量的巨细有依靠性,批量太小作用欠好,批量太大又遭到硬件的约束。所以在对输入批量巨细具有上限的复杂网络中不太有用。
现在现已提出了几种批量归一化的替代计划来缓解这个问题,其间一个便是层归一化。咱们不再对这个小批量进行归一化,而是对特征向量进行归一化。换句话说,当运用层归一化时,依据该特征向量内的一切项的总和来归一化对应于单个数据点。
层归一化测验与练习的行为相同,都是核算每个样本的归一。可用于循环神经网络。
6.卷积神经网络中归一化
空间批量归一化(Spatial Batch Normalization)是对深度进行归一化。
- 全衔接网络中的批量归一化输入尺度为 (N,D)(N,D) 输出是 (N,D)(N,D),其间咱们在小批量维度 NN 上核算核算数据用于归一化 NN 个特征点。
- 卷积层输入的数据,批量归一化的输入尺度是 (N,C,H,W)(N,C,H,W) 并产生尺度为 (N,C,H,W)(N,C,H,W) 的输出,其间N是小批量巨细,(H,W)(H,W) 是输出特征图的空间巨细。
- 假如运用卷积生成特征图,咱们希望每个特征通道的核算在不同图画和同一图画内的不同方位之间相对共同。因而,空间批量归一化经过核算小批量维度N和空间维度 HH 和 WW 的核算量来核算每个 CC 特征通道的均值和方差。
卷积神经网络中的层归一化是对每张图片进行归一化。
- 可是在卷积神经网络中,层归一化作用欠好。由于关于全衔接层,层中的一切躲藏单元倾向于对终究预测做出相似的奉献,并且对层的求和输入从头定中心和从头缩放作用很好;而关于卷积神经网络,奉献相似的假定不再适用。其感受野坐落图画鸿沟邻近的大量躲藏单元很少打开,因而与同一层内其他躲藏单元的核算数据十分不同(图片中心的方位奉献比较大,边际的方位或许是布景或噪声)。
实例归一化既对图片又对数据进行归一化;
组归一化(Group Normalization)2018年何凯明的论文 Group Normalization 提出了一种中心技能。
- 与层归一化在每个数据点的整个特征上进行规范化比较,主张将每个数据点特征拆分为相同的 GG 组,然后对每个数据点的每个数据组的规范化(简略来说,相关于层归一化将整张图片归一,这个将整张图片裁成 GG 组,然后对每个组进行归一)。
- 这样就能够假定每个组仍然做出相同的奉献,由于分组便是依据视觉辨认的特征。比方将传统核算机视觉中的许多高性能人为特征在一同。其间一个定向梯度直方图便是在核算每个空间部分块的直方图之后,每个直方图块在被衔接在一同构成终究特征向量之前被归一化。
7.监控学习进程
7.1 监控学习进程的进程
1) 数据预处理,减均值
2) 挑选网络结构
两层神经网络,一个躲藏层有 50 个神经元,输入图画是 3072 维的向量,输出层有 10 个神经元,代表10种分类。
3) 合理性(Sanity)检查
运用小参数进行初始化,使正则丢掉为 00,保证得到的丢掉值与希望共同。
例如,输入数据集为CIFAR-10的图画分类
- 关于Softmax分类器,一般希望它的初始丢掉值是 2.3022.302,这是由于初始时估计每个类别的概率是 0.10.1(由于有10个类别),然后Softmax丢掉值正确分类的负对数概率 −ln(0.1)=2.302-ln(0.1) = 2.302。
- 关于多类 SVM,假定一切的鸿沟都被跳过(由于一切的分值都近似为零),所以丢掉值是9(由于关于每个过错分类,鸿沟值是1)。
- 假如没看到这些丢掉值,那么初始化中就或许有问题。
进步正则化强度,丢掉值会变大。
def init_two_layer_model(input_size, hidden_size, output_size):
model = {}
model["W1"] = 0.0001 * np.random.randn(input_size, hidden_size)
model['b1'] = np.zeros(hidden_size)
model['W2'] = 0.0001 * np.random.randn(hidden_size, output_size)
model['b2'] = np.zeros(output_size)
return model
model = init_two_layer_model(32*32*3, 50, 10)
loss, grad = two_layer_net(X_train, model, y_train, 0) # 0没有正则丢掉
print(loss)
对小数据子集过拟合。
- 这一步很重要,在整个数据集进行练习之前,测验在一个很小的数据集上进行练习(比方20个数据),然后保证能抵达0的丢掉值。此刻让正则化强度为0,不然它会阻挠得到0的丢掉。除非能经过这一个正常性检查,不然进行整个数据集练习是没有意义的。
- 可是留意,能对小数据集进行过拟合仍然有或许存在不正确的完成。比方,由于某些过错,数据点的特征是随机的,这样算法也或许对小数据进行过拟合,可是在整个数据集上跑算法的时分,就没有任何泛化才能。
model = init_two_layer_model(32*32*3, 50, 10)
trainer = ClassifierTrainer()
X_tiny = X_train[:20] # 选前20个作为样本
y_tiny = y_train[:20]
best_model, stats = trainer.train(X_tiny, y_tiny, X_tiny, y_tiny,
model, two_layer_net, verbose=True,
num_epochs=200, reg=0.0, update='sgd',
learning_rate=1e-3, learning_rate_decay=1,
sample_batchs=False)
4) 梯度检查(Gradient Checks)
理论大将进行梯度检查很简略,便是简略地把解析梯度和数值核算梯度进行比较。可是从实践操作层面上来说,这个进程愈加复杂且容易出错。下面是一些常用的技巧:
① 运用中心化公式。
在运用有限差值近似来核算数值梯度的时分,常见的公式是:df(x)dx=f(x+h)−f(x)h\frac{df(x)}{dx} = \frac{f(x + h) – f(x)}{h} 其间 hh 是一个很小的数字,在实践中近似为 1e-5
。可是在实践中证明,运用中心化公式作用更好:df(x)dx=f(x+h)−f(x−h)2h\frac{df(x)}{dx} = \frac{f(x + h) – f(x – h)}{2h} 该公式在检查梯度的每个维度的时分,会要求核算两次丢掉函数(所以核算资源的消耗也是两倍),可是梯度的近似值会准确许多。
② 运用相对误差来比较。
数值梯度 fn′f’_n 和解析梯度 fa′f’_a 的绝对误差并不能准确的标明二者的距离,应当运用相对误差。∣fa′−fn′∣max(∣fa′∣,∣fn′∣)\frac{\mid f’_a – f’_n \mid}{\max(\mid f’_a \mid, \mid f’_n \mid)} 在实践中:相对误差大于 1e-2
一般就意味着梯度或许出错;小于 1e-7
才是比较好的成果。可是网络的深度越深,相对误差就越高。所以关于一个10层网络,1e-2
的相对误差值或许就行,由于误差一直在累积。相反,假如一个可微函数的相对误差值是 1e-2
,那么一般阐明梯度完成不正确。
③ 运用双精度。
一个常见的过错是运用单精度浮点数来进行梯度检查,这样会导致即使梯度完成正确,相对误差值也会很高(比方1e-2
)。保持在浮点数的有用规模。把原始的解析梯度和数值梯度数据打印出来,保证用来比较的数字的值不是过小。
④ 留意方针函数的不可导点(kinks) 。
在进行梯度检查时,一个导致不准确的原因是不可导点问题。不可导点是指方针函数不可导的部分,由 ReLU 函数、SVM丢掉、Maxout神经元等引入。考虑当 x=-1e-6
时,对 ReLU 函数进行梯度检查。由于 x<0x<0,所以解析梯度在该点的梯度为0。可是,在这儿数值梯度会忽然核算出一个非零的梯度值,由于 f(x+h)f(x+h) 或许跳过了不可导点(例如:假如 h>1e-6
),导致了一个非零的成果。处理这个问题的有用办法是运用少数数据点。这样不可导点会减少,并且假如梯度检查对2-3个数据点都有用,那么根本上对整个批量数据也是没问题的。
⑤ 慎重设置h。
并不是越小越好,假如无法进行梯度检查,能够试试试试将 hh 调到 1e-4
或许 1e-6
。
在操作的特性模式中梯度检查。为了安全起见,最好让网络学习(「预热」)一小段时刻,比及丢掉函数开端下降的之后再进行梯度检查。在第一次迭代就进行梯度检查的危险就在于,此刻或许正处在不正常的鸿沟状况,然后掩盖了梯度没有正确完成的事实。
⑥ 封闭正则丢掉。
引荐先关掉正则化对数据丢掉做独自检查,然后对正则化做独自检查,防止正则化丢掉吞没掉数据丢掉。
5) 正式练习,数值盯梢,特征可视化。
设置一个较小的正则强度,找到使丢掉下降的学习率。
best_model, stats = trainer.train(X_tiny, y_tiny, X_tiny, y_tiny,
model, two_layer_net, verbose=True,
num_epochs=10, reg=0.000001, update='sgd',
learning_rate=1e-6, learning_rate_decay=1,
sample_batchs=False)
学习率为 10−610^{-6} 时,丢掉下降缓慢,阐明学习速率过小。
假如把学习率设为另一个极端:10610^{6},如下图所示,会产生丢掉爆破:
NaN一般意味着学习率过高,导致丢掉过大。设为 10−310^{-3} 时仍然爆破,一个比较合理的规模是 [10−5,10−3][10^{-5}, 10^{-3}]。
7.2 练习进程中的数值盯梢
1) 盯梢丢掉函数
练习期间第一个要盯梢的数值便是丢掉值,它在前向传达时对每个独立的批数据进行核算。
鄙人面的图表中,xx 轴一般都是表示周期(epochs)单位,该单位衡量了在练习中每个样本数据都被观察过的次数的希望(一个 epoch 意味着每个样本数据都被观察过了一次)。相较于迭代次数(iterations) ,一般更倾向盯梢 epoch,这是由于迭代次数与数据的批尺度(batchsize)有关,而批尺度的设置又能够是任意的。
比方一共有 1000个 练习样本,每次 SGD 运用的小批量是 10 个样本,一次迭代指的是用这 10 个样本练习一次,而1000个样本都被运用过一次才是一次 epoch,即这 1000 个样本全部被练习过一次需求 100 次 iterations,一次 epoch。
下图展示的是丢掉值随时刻的改变,曲线形状会给出学习率设置的状况:
左图展示了不同的学习率的作用。过低的学习率导致算法的改善是线性的。高一些的学习率会看起来呈几许指数下降,更高的学习率会让丢掉值很快下降,可是接着就停在一个欠好的丢掉值上(绿线)。这是由于最优化的「能量」太大,参数随机震动,不能最优化到一个很好的点上。过高的学习率又会导致丢掉爆破。
右图显现了一个典型的随时刻改变的丢掉函数值,在CIFAR-10数据集上面练习了一个小的网络,这个丢掉函数值曲线看起来比较合理(尽管或许学习率有点小,可是很难说),并且指出了批数据的数量或许有点太小(由于丢掉值的噪音很大)。丢掉值的震动程度和批尺度(batch size)有关,当批尺度为1,震动会相对较大。当批尺度便是整个数据集时震动就会最小,由于每个梯度更新都是单调地优化丢掉函数(除非学习率设置得过高)。
下图这种开端丢掉不变,然后开端学习的状况,阐明初始值设置的不合理。
2) 盯梢练习集和验证集准确率
在练习分类器的时分,需求盯梢的第二重要的数值是验证集和练习集的准确率。这个图表能够展示知道模型过拟合的程度:
练习集准确率和验证集准确率间的间距指明了模型过拟合的程度。在图中,蓝色的验证集曲线比练习集准确率低了许多,这就阐明模型有很强的过拟合。遇到这种状况,就应该增大正则化强度(更强的L2权重赏罚,更多的随机失活等)或搜集更多的数据。另一种或许便是验证集曲线和练习集曲线很挨近,这种状况阐明模型容量还不够大:应该经过增加参数数量让模型容量更大些。
3) 盯梢权重更新份额
终究一个应该盯梢的量是权重中更新值的数量和全部值的数量之间的份额**。留意**:是更新的,而不是原始梯度(比方,在一般sgd中便是梯度乘以学习率)。需求对每个参数集的更新份额进行独自的核算和盯梢。一个经历性的定论是这个份额应该在 1e-3
左右。假如更低,阐明学习率或许太小,假如更高,阐明学习率或许太高。下面是详细比如:
# 假定参数向量为W,其梯度向量为dW
param_scale = np.linalg.norm(W.ravel()) # ravel将多维数组转化成一维;
# np.linalg.norm默认求L2范式
update = -learning_rate*dW # 简略SGD更新
update_scale = np.linalg.norm(update.ravel())
W += update # 实践更新
print update_scale / param_scale # 要得到1e-3左右
4) 第一层可视化
假如数据是图画像素数据,那么把第一层特征可视化会有帮助:
左图: 特征充满了噪音,这暗示了网络或许呈现了问题:网络没有收敛,学习率设置不恰当,正则化赏罚的权重过低。
右图: 特征不错,滑润,干净并且品种繁多,阐明练习进程进行杰出。
8.超参数调优
关于超参数调优的解说也能够比照阅览ShowMeAI的深度学习教程 | 吴恩达专项课程 全套笔记解读中的文章网络优化:超参数调优、正则化、批归一化和程序结构里【超参数调优】板块内容。
怎么进行超参数调优呢?常需求设置的超参数有三个:
- 学习率
- 学习率衰减办法(例如一个衰减常量)
- 正则化强度(L2 赏罚,随机失活强度)
下面介绍几个常用的战略:
1) 比起交叉验证最好运用一个验证集
在大多数状况下,一个尺度合理的验证集能够让代码更简略,不需求用几个数据集来交叉验证。
2) 涣散初值,几回周期(epoch)
挑选几个十分涣散的数值,然后运用几回 epoch(完好数据集练习一轮是1个epoch)去学习。经过几回 epoch,根本就能发现哪些数值较好哪些欠好。比方很快就 nan(往往超过初始丢掉 3 倍就能够认为是 nan,就能够完毕练习。),或许没有反应,然后进行调整。
3) 进程查找:从粗到细
发现比较好的区间后,就能够精密查找,epoch 次数更多,运行时刻更长。比方之前的网络,每次进行 5 次 epoch,对较好的区间进行查找,找到准确率比较高的值,然后进一步准确查找。留意,需求在对数尺度上进行超参数查找。
也便是说,咱们从规范散布中随机生成了一个实数,然后让它成为 10 的次数。关于正则化强度,能够选用相同的战略。直观地说,这是由于学习率和正则化强度都关于练习的动态进程有乘的作用。
例如:当学习率是 0.001 的时分,假如对其固定地增加 0.01,那么关于学习进程会有很大影响。可是当学习率是 10 的时分,影响就微乎其微了。这便是由于学习率乘以了核算出的梯度。
比起加上或许减少某些值,考虑学习率的规模是乘以或许除以某些值愈加自然。可是有一些参数(比方随机失活)仍是在原始尺度上进行查找。
max_count = 100
for count in range(max_count):
reg = 10**uniform(-5, 5) # random模块的函数uniform,会在-5~5规模内随机挑选一个实数
# reg在10^-5~10^5之间取值,指数函数
lr = 10**uniform(-3, -6)
model = init_two_layer_model(32 * 32 * 3, 50, 10)
trainer = ClassifierTrainer()
best_model, stats = trainer.train(X_tiny, y_tiny, X_tiny, y_tiny,
model, two_layer_net, verbose=False,
num_epochs=5, reg=reg, update='momentum',
learning_rate=lr, learning_rate_decay=0.9,
sample_batchs=True, batch_size=100)
比较好的成果在红框中,学习率在 10e-4
左右,正则强度在 10e-4~10e-1
左右,需求进一步精密查找。修正代码:
max_count = 100
for count in range(max_count):
reg = 10**uniform(-4, 0)
lr = 10**uniform(-3, -4)
有一个相对较好的准确率:53%53\%。可是这儿却有一个问题,这些比较高的准确率都是学习率在 10e-4
邻近,也便是说都在咱们设置的区间边际,或许 10e-5
或 10e-6
有更好的成果。所以在设置区间的时分,要把较好的值放在区间中心,而不是区间边际。
随机查找优于网格查找。Bergstra 和 Bengio 在文章 Random Search for Hyper-Parameter Optimization 中说「随机挑选比网格化的挑选愈加有用」,并且在实践中也更容易完成。一般,有些超参数比其他的更重要,经过随机查找,而不是网格化的查找,能够让你更准确地发现那些比较重要的超参数的好数值。
上图中绿色函数部分是比较重要的参数影响,黄色是不重要的参数影响,相同取9个点,假如选用均匀采样就会错失许多重要的点,随机查找就不会。
下一篇 深度学习与CV教程(7) | 神经网络练习技巧 (下) 会讲到的学习率衰减计划、更新类型、正则化、以及网络结构(深度、尺度)等都需求超参数调优。
9.拓宽学习
能够点击 B站 检查视频的【双语字幕】版本
- 【课程学习指南】斯坦福CS231n | 深度学习与核算机视觉
- 【字幕+资料下载】斯坦福CS231n | 深度学习与核算机视觉 (2017全16讲)
- 【CS231n进阶课】密歇根EECS498 | 深度学习与核算机视觉
- 【深度学习教程】吴恩达专项课程 全套笔记解读
- 【Stanford官网】CS231n: Deep Learning for Computer Vision
10.要点总结
- 激活函数挑选折叶函数
- 数据预处理选用减均值
- 权重初始化选用 Xavier 或 He 初始化
- 运用批量归一化
- 梯度检查;合理性检查;盯梢丢掉函数、准确率、更新份额等
- 超参数调优选用随机查找,对数间隔,不断细化规模,增加 epoch
ShowMeAI 斯坦福 CS231n 全套解读
- 深度学习与核算机视觉教程(1) | CV引言与根底 @CS231n
- 深度学习与核算机视觉教程(2) | 图画分类与机器学习根底 @CS231n
- 深度学习与核算机视觉教程(3) | 丢掉函数与最优化 @CS231n
- 深度学习与核算机视觉教程(4) | 神经网络与反向传达 @CS231n
- 深度学习与核算机视觉教程(5) | 卷积神经网络 @CS231n
- 深度学习与核算机视觉教程(6) | 神经网络练习技巧 (上) @CS231n
- 深度学习与核算机视觉教程(7) | 神经网络练习技巧 (下) @CS231n
- 深度学习与核算机视觉教程(8) | 常见深度学习结构介绍 @CS231n
- 深度学习与核算机视觉教程(9) | 典型CNN架构 (Alexnet, VGG, Googlenet, Restnet等) @CS231n
- 深度学习与核算机视觉教程(10) | 轻量化CNN架构 (SqueezeNet, ShuffleNet, MobileNet等) @CS231n
- 深度学习与核算机视觉教程(11) | 循环神经网络及视觉运用 @CS231n
- 深度学习与核算机视觉教程(12) | 方针检测 (两阶段, R-CNN系列) @CS231n
- 深度学习与核算机视觉教程(13) | 方针检测 (SSD, YOLO系列) @CS231n
- 深度学习与核算机视觉教程(14) | 图画切割 (FCN, SegNet, U-Net, PSPNet, DeepLab, RefineNet) @CS231n
- 深度学习与核算机视觉教程(15) | 视觉模型可视化与可解释性 @CS231n
- 深度学习与核算机视觉教程(16) | 生成模型 (PixelRNN, PixelCNN, VAE, GAN) @CS231n
- 深度学习与核算机视觉教程(17) | 深度强化学习 (马尔可夫决策进程, Q-Learning, DQN) @CS231n
- 深度学习与核算机视觉教程(18) | 深度强化学习 (梯度战略, Actor-Critic, DDPG, A3C) @CS231n
ShowMeAI 系列教程引荐
- 大厂技能完成:引荐与广告核算处理计划
- 大厂技能完成:核算机视觉处理计划
- 大厂技能完成:自然语言处理行业处理计划
- 图解Python编程:从入门到通晓系列教程
- 图解数据剖析:从入门到通晓系列教程
- 图解AI数学根底:从入门到通晓系列教程
- 图解大数据技能:从入门到通晓系列教程
- 图解机器学习算法:从入门到通晓系列教程
- 机器学习实战:手把手教你玩转机器学习系列
- 深度学习教程:吴恩达专项课程 全套笔记解读
- 自然语言处理教程:斯坦福CS224n课程 课程带学与全套笔记解读
- 深度学习与核算机视觉教程:斯坦福CS231n 全套笔记解读