在第一门课中已经学习了如何建立一个神经网络,或者浅层的,或者深度的。而这第二门课,我们将着重讨论和研究如何优化神经网络模型,例如调整超参数,提高算法运行速度等等。

Week1:深度学习的实践层面(Practical of Deep Learning)

1.1 训练,验证,测试集(Train / Dev / Test sets)

  1. 模型创建的迭代过程

    • 在构建一个神经网络的时候,我们需要设置许多参数,而且很对是超参数,例如:神经网络的层数、每个隐藏层包含的神经元个数、学习因子(学习速率)、激活函数的选择等等。很难在第一次设置的时候就选择到这些最佳的参数,而是需要通过不断地迭代更新来获得。

    • 循环迭代的过程是这样的:

      • 先有个想法Idea,先选择初始的参数值,构建神经网络模型结构;
      • 然后通过代码Code的形式,实现这个神经网络;
      • 最后,通过实验Experiment验证初始参数对应的神经网络的表现性能。

      根据验证结果,我们对参数进行适当的调整优化,再进行下一次的Idea->Code->Experiment循环。通过很多次的循环,不断调整参数,选定最佳的参数值,从而让神经网络性能最优化。

    • 最适合某个领域的深度学习网络往往不能直接应用在其它领域上。

      • 解决不同问题的最佳选择是根据样本数量、输入特征数量和电脑配置信息(GPU或者CPU)等,来选择最合适的模型。即使是最有经验的深度学习专家也很难第一次就找到最合适的参数。
      • 深度学习模型的选择是一个反复迭代的过程,需要通过反复多次的循环训练得到最优化参数。
      • 决定整个训练过程快慢的关键在于单次循环所花费的时间,单次循环越快,训练过程越快。而设置合适的Train/Dev/Test sets数量,能有效提高训练效率。
  2. Train/Dev/Test sets数量的选取
    • 相关定义:

      • Train sets:训练你的算法模型;
      • Dev sets:验证不同算法的表现情况,从中选择最好的算法模型;
      • Test sets:测试最好算法的实际表现,作为该算法的无偏估计。
    • 选取方法:
      • 小数据时代:

        • Train sets:Test sets的比例为:70%和30%。
        • Train:Dev:Test sets的比例为:60%:20%:20%。
      • 大数据时代:
        • 有100万条数据,其中1万条作为验证集,1万条作为测试集,即:训练集占98%,验证集和测试集各占1%。
        • 对于数据量过百万时,训练集可以占到99.5%,验证和测试集各占0.25%,或者验证集占0.4%,测试集占0.1%。
  3. Train/Dev/Test sets数据无偏差性
    • 训练样本和测试样本分布要匹配,即:训练样本和测试样本来要同分布。

      假设你开发一个手机app,可以让用户上传图片,然后app识别出猫的图片。在app识别算法中,你的训练样本可能来自网络下载,而你的验证和测试样本可能来自不同用户的上传。
      从网络下载的图片一般像素较高而且比较正规,而用户上传的图片往往像素不稳定,且图片质量不一。因此,训练样本和验证/测试样本可能来自不同的分布。解决这一问题的比较科学的办法是尽量保证Dev sets和Test sets来自于同一分布。

    • 训练样本非常重要,当数据量不够时,可以将现有的训练样本做一些处理,例如:图片的翻转、假如随机噪声等,来扩大训练样本的数量,从而让该模型更加强大。即使Train sets和Dev/Test sets不来自同一分布,使用这些技巧也能提高模型性能。

1.2 偏差,方差(Bias /Variance)

  1. 定义
    偏差(Bias)和方差(Variance)是机器学习领域非常重要的两个概念和需要解决的问题。

    据上图,高偏差(bias)意味着欠拟合,高方差(variance)意味着过拟合\color{red}高偏差(bias)意味着欠拟合,高方差(variance)意味着过拟合高偏差(bias)意味着欠拟合,高方差(variance)意味着过拟合。

    • 在传统的机器学习算法中,Bias和Variance是对立的,分别对应着欠拟合和过拟合,我们常常需要在Bias和Variance之间进行权衡。
    • 而在深度学习中,我们可以同时减小Bias和Variance,构建最佳神经网络模型。
  2. 数据适度拟合(just right)

    • 以猫识别为例子,输入是一幅图像,其特征维度很大。这种情况下,我们可以通过两个数值训练集误差(Train set error)和验证集误差(Dev set error)来理解bias和variance。

      • 假定训练集误差是 1%,验证集误差是 11%,可以看出训练集设置得非常好,而验证集结果相对较差, 我们可能过度拟合了训练集,在某种程度上,验证集并没有充分利用交叉验证集的作用, 像这种情况, 我们称之为“高方差(variance)”。
      • 假设训练集误差是 15%,验证集误差是 16%,假设该案例中人的错误率几乎为 0%,人们浏览这些图片,分辨出是不是猫。

        算法并没有在训练集中得到很好训练,如果训练数据的拟合度不高,就是数据欠拟合\color{red}数据欠拟合数据欠拟合,就可以说这种算法偏差(bias)比较高\color{red}偏差(bias)比较高偏差(bias)比较高。
        相反,它对于验证集产生的结果却是合理的,验证集中的错误率只比训练集的多了 1%,所以这种算法偏差高,因为它甚至不能拟合训练集。

      • 假设训练集误差是 15%,偏差相当高,但是,验证集的评估结果更糟糕,错误率达到 30%,在这种情况下,我会认为这种算法偏差高,因为它在训练集上结果不理想,而且方差也很高,这是方差偏差也很糟糕的情况。
      • 假设训练集误差是 0.5%,验证集误差是 1%,用户看到这样的结果会很开心,偏差和方差都很低

      以上分析的假设条件:
      这些分析都是基于假设预测的,假设人眼辨别的错误率接近 0%,一般来说,最优误差也被称为贝叶斯误差\color{red}贝叶斯误差贝叶斯误差,所以,最优误差接近 0%。如果最优误差或贝叶斯误差非常高,比如 15%。我们再看看这个分类器(训练误差 15%,验证误差 16%), 15%的错误率对训练集来说也是非常合理的,偏差不高,方差也非常低。

    • high bias and high variance的模型:

      模型既存在high bias也存在high variance,可以理解成某段区域是欠拟合的,某段区域是过拟合的。


1.3 机器学习基础(Basic Recipe for Machine Learning)

机器学习和深度学习的目标就是:避免出现highbias和highvariance\color{red}避免出现high\;bias和high\;variance避免出现highbias和highvariance。

  • 减少high bias的方法

    • 增加神经网络的隐藏层个数、神经元个数;
    • 训练时间延长;
    • 选择其它更复杂的NN模型(这个要重复尝试,不一定一次就能有效)。
    • 注\color{red}注注:在base error不高的情况下,一般都能通过这些方式有效降低和避免high bias,至少在训练集上表现良好。一旦偏差降低到可以接受的数值,检查一下方差有没有问题
  • 减少high variance的方法
    • 增加训练样本数据;
    • 进行正则化Regularization;
    • 选择其他更复杂的NN(The neural network) 模型(这个要重复尝试,不一定一次就能有效)。
  • 注意
    • 第一,解决high bias和high variance的方法是不同的。如果算法存在高偏差问题,准备更多训练数据用处不大。实际应用中通过Train set error和Dev set error判断是否出现了high bias或者high variance,然后再选择针对性的方法解决问题。
    • 第二,Bias和Variance的折中tradeoff。传统机器学习算法中,Bias和Variance通常是对立的,减小Bias会增加Variance,减小Variance会增加Bias。而在现在的深度学习中,通过使用更复杂的神经网络和海量的训练样本,一般能够同时有效减小Bias和Variance。这也是深度学习之所以如此强大的原因之一。

1.4 正则化(Regularization)

深度学习可能存在过拟合问题——高方差,有两个解决方法:一个是正则化;另一个是准备更多的数据(这是非常可靠的方法,但你可能无法时时刻刻准备足够多的训练数据或者获取更多数据的成本很高)。

  • Logistic regression(逻辑回归)

    • 采用 L2L_2L2​ regularization
      其表达式为:
      J(w,b)=1m∑i=1mL(y^(i),y(i))+λ2m∣∣w∣∣22其中∣∣w∣∣22=∑j=1nxwj2=wTw\begin{array}{l}J(w,b)=\frac1m\sum_{i=1}^mL(\hat y^{(i)},y^{(i)})+\frac{\lambda}{2m}||w||_2^2\\ \text{其中}||w||_2^2=\sum_{j=1}^{n_x}w_j^2=w^Tw\end{array}J(w,b)=m1​∑i=1m​L(y^​(i),y(i))+2mλ​∣∣w∣∣22​其中∣∣w∣∣22​=∑j=1nx​​wj2​=wTw​

      为什么只对w进行正则化而不对b进行正则化呢?
      其实也可以对b进行正则化。但是一般w的维度很大,而b只是一个常数。相比较来说,参数的变化程度上基本由w决定,改变b值对整体模型参数的变化影响较小。所以,一般为了简便,就忽略对b的正则化了。

    • 采用 L1L_1L1​ regularization
      J(w,b)=1m∑i=1mL(y^(i),y(i))+λ2m∣∣w∣∣1∣∣w∣∣1=∑j=1nx∣wj∣\begin{array}{l}J(w,b)=\frac1m\sum_{i=1}^mL(\hat y^{(i)},y^{(i)})+\frac{\lambda}{2m}||w||_1\\ ||w||_1=\sum_{j=1}^{n_x}|w_j|\end{array}J(w,b)=m1​∑i=1m​L(y^​(i),y(i))+2mλ​∣∣w∣∣1​∣∣w∣∣1​=∑j=1nx​​∣wj​∣​
      与L2L_2L2​ regularization相比,L1L_1L1​ regularization得到的w更加稀疏,即很多www为零值。其优点是节约存储空间,因为大部分www为0。

      实际上L1L_1L1​ regularization在解决high variance方面比L2L_2L2​ regularization并不更具优势。而且,L1L_1L1​的在微分求导方面比较复杂(L1L_1L1​范式有的时候会改用L21L_{21}L21​范式)。所以,一般L2L_2L2​ regularization更加常用。

    • L1L_1L1​、L2L_2L2​ regularization中的λ\lambdaλ就是正则化参数(超参数的一种)。可以设置λ\lambdaλ为不同的值,在Dev set中进行验证,选择最佳的λ\lambdaλ。

      在python中,由于lambda是保留字,所以为了避免冲突,我们使用lambd来表示λ\lambdaλ。

  • 深度学习
    • L2L_2L2​ regularization:
      表达式为:
      J(w[1],b[1],⋯,w[L],b[L])=1m∑i=1mL(y^(i),y(i))+λ2m∑l=1L∣∣w[l]∣∣2其中∣∣w[l]∣∣2=∑i=1n[l]∑j=1n[l−1](wij[l])2\begin{array}{l}J(w^{[1]},b^{[1]},\cdots,w^{[L]},b^{[L]})=\frac1m\sum_{i=1}^mL(\hat y^{(i)},y^{(i)})+\frac{\lambda}{2m}\sum_{l=1}^L||w^{[l]}||^2\\ \text{其中}||w^{[l]}||^2=\sum_{i=1}^{n^{[l]}}\sum_{j=1}^{n^{[l-1]}}(w_{ij}^{[l]})^2\end{array}J(w[1],b[1],⋯,w[L],b[L])=m1​∑i=1m​L(y^​(i),y(i))+2mλ​∑l=1L​∣∣w[l]∣∣2其中∣∣w[l]∣∣2=∑i=1n[l]​∑j=1n[l−1]​(wij[l]​)2​

      通常,我们把∣∣w[l]∣∣2||w^{[l]}||^2∣∣w[l]∣∣2称为Frobenius范数,记为∣∣w[l]∣∣F2||w^{[l]}||_F^2∣∣w[l]∣∣F2​。一个矩阵的Frobenius范数就是计算所有元素平方和再开方,如下所示:
      ∣∣A∣∣F=∑i=1m∑j=1n∣aij∣2||A||_F=\sqrt {\sum_{i=1}^m\sum_{j=1}^n|a_{ij}|^2}∣∣A∣∣F​=i=1∑m​j=1∑n​∣aij​∣2​

    • 梯度下降算法中的dw[l]dw^{[l]}dw[l]计算表达式需要做如下修改:
      dw[l]=dwbefore[l]+λmw[l]w[l]:=w[l]−α⋅dw[l]\begin{array}{l}dw^{[l]}=dw^{[l]}_{before}+\frac{\lambda}{m}w^{[l]}\\ w^{[l]}:=w^{[l]}-\alpha\cdot dw^{[l]}\end{array}dw[l]=dwbefore[l]​+mλ​w[l]w[l]:=w[l]−α⋅dw[l]​

    • L2L_2L2​ regularization也被称做weightdecayweight\; decayweightdecay(权重衰减)
      如下公式,由于加上了正则项,dw[l]dw^{[l]}dw[l]有个增量,在更新w[l]w^{[l]}w[l]的时候,会多减去这个增量,使得w[l]w^{[l]}w[l]比没有正则项的值要小一些。不断迭代更新,不断地减小:
      w[l]:=w[l]−α⋅dw[l]=w∣l∣−α⋅(dwbefore[l]+λmw[l])=(1−αλm)w[l]−α⋅dwbefore[l]\begin{aligned} w^{[l]} &:=w^{[l]}-\alpha \cdot d w^{[l]} \\ &=w^{|l|}-\alpha \cdot\left(d w_{b e f o r e}^{[l]}+\frac{\lambda}{m} w^{[l]}\right) \\ &=\left(1-\alpha \frac{\lambda}{m}\right) w^{[l]}-\alpha \cdot d w_{b e f o r e}^{[l]} \end{aligned}w[l]​:=w[l]−α⋅dw[l]=w∣l∣−α⋅(dwbefore[l]​+mλ​w[l])=(1−αmλ​)w[l]−α⋅dwbefore[l]​​
      其中,(1−αλm)<1(1-\alpha\frac{\lambda}{m})<1(1−αmλ​)<1。


1.5 为什么正则化有利于预防过拟合呢?(Why regularization reduces overfitting?)

可以从三个方面来理解:为什么正则化有利于预防过拟合:

  1. 从上一讲最后一小节的公式进行理解:
    w[l]:=w[l]−α⋅dw[l]=w∣l∣−α⋅(dwbefore[l]+λmw[l])=(1−αλm)w[l]−α⋅dwbefore[l]\begin{aligned} w^{[l]} &:=w^{[l]}-\alpha \cdot d w^{[l]} \\ &=w^{|l|}-\alpha \cdot\left(d w_{b e f o r e}^{[l]}+\frac{\lambda}{m} w^{[l]}\right) \\ &=\left(1-\alpha \frac{\lambda}{m}\right) w^{[l]}-\alpha \cdot d w_{b e f o r e}^{[l]} \end{aligned}w[l]​:=w[l]−α⋅dw[l]=w∣l∣−α⋅(dwbefore[l]​+mλ​w[l])=(1−αmλ​)w[l]−α⋅dwbefore[l]​​

    • 其中,(1−αλm)<1(1-\alpha\frac{\lambda}{m})<1(1−αmλ​)<1。当λ\lambdaλ较大时,w[l]≈0w^{[l]}\approx0w[l]≈0,意味着该神经网络模型中的某些神经元实际的作用很小,可以忽略。
      从效果上来看,就是将原本过于复杂的神经网络模型就变得不那么复杂了,而变得非常简单化了。
  2. 从如下例子进行理解,如下图:

    上图从左到右,分别表示:欠拟合,恰好拟合,过拟合三种情况。

    • 假如我们选择了非常复杂的神经网络模型,在未使用正则化的情况下,得到的 分类超平面(不一定是二维) 可能是类似上图右侧的过拟合。
    • 当λ\lambdaλ很大时,w[l]≈0w^{[l]}\approx0w[l]≈0。w[l]w^{[l]}w[l]近似为零,意味着该神经网络模型中的某些神经元实际的作用很小,可以忽略。从效果上来看,其实是将某些神经元给忽略掉了,类似于上图中左边的线性回归。
      如下图所示,整个简化的神经网络模型变成了一个逻辑回归模型,问题就从high variance变成了high bias了。
    • 当选择合适大小的λ\lambdaλ值,就能够同时避免high bias和high variance,得到最佳模型。
  3. 从激活函数的角度来看:
    假设激活函数是tanhtanhtanh函数。tanhtanhtanh函数的特点是在zzz接近零的区域,函数近似是线性的。

    当∣z∣|z|∣z∣很大的时候,函数非线性且变化缓慢。当使用正则化:

    • λ\lambdaλ较大,即对权重w[l]w^{[l]}w[l]的惩罚较大,w[l]w^{[l]}w[l]的值相较无λ\lambdaλ时,变得更小。
    • 因为z[l]=w[l]a[l]+b[l]z^{[l]}=w^{[l]}a^{[l]}+b^{[l]}z[l]=w[l]a[l]+b[l]。当w[l]w^{[l]}w[l]减小的时候,z[l]z^{[l]}z[l]也会减小。则此时的z[l]z^{[l]}z[l]分布在tanh函数的近似线性区域。那么这个神经元起的作用就相当于是linear regression。
    • 如果每个神经元对应的权重w[l]w^{[l]}w[l]都比较小,那么整个神经网络模型相当于是多个linear regression的组合,即可看成一个linear network。得到的分类超平面就会比较简单,不会出现过拟合现象。

1.6 dropout 正则化(Dropout Regularization)

除了上面章节说的,利用正则化进行预防过拟合外,还有另外一种防止过拟合的有效方法:Dropout。

  1. Dropout出现的原因
    训练深度神经网络的时候,总是会遇到两大缺点:

    • 容易过拟合
    • 费时
  2. 什么是Dropout
    Dropout是指在深度学习网络的训练过程中,对于每层的神经元,按照一定的概率将其暂时从网络中丢弃。也就是说,每次训练时,每一层都有部分神经元不工作,起到简化复杂网络模型的效果,从而避免发生过拟合

    如上图所示,让某个神经元的激活值以一定的概率ppp停止工作,这样可以使模型泛化性更强。
    接下来介绍一种常用的方法:Inverteddropout\color{red}Inverted\; dropoutInverteddropout。
  3. Dropout工作流程
    假设我们要训练一个神经网络,如下图:

    输入是xxx输出是yyy,正常的流程是:我们首先把xxx通过网络前向传播,然后把误差反向传播以决定如何更新参数让网络进行学习。使用Dropout之后,过程变成如下:

    • 首先随机(临时)删掉网络中一半的隐藏神经元,输入输出神经元保持不变(下图中虚线为部分临时被删除的神经元)
    • 把输入xxx通过修改后的网络前向传播,然后把得到的损失结果通过修改的网络反向传播。训练样本执行完这个过程后,按照随机梯度下降法更新对应的参数(w,b)(w,b)(w,b)。
    • 继续重复这一过程:
      • 恢复被删掉的神经元(此时被删除的神经元保持原样,而没有被删除的神经元已经有所更新)
      • 从隐藏层神经元中随机选择一个keep_pro比例的子集临时删除掉(备份被删除神经元的参数)。
      • 对训练样本,先前向传播然后反向传播损失并根据随机梯度下降法更新参数(w,b)(w,b)(w,b)(没有被删除的那一部分参数得到更新,删除的神经元参数保持被删除前的结果)。
    • 直到达到停止条件。
  4. Dropout的数学形式
    • 在训练模型阶段

      • 没有Dropout的网络计算公式:
        zi(l+1)=wi(l+1)yl+bi(l+1)yi(l+1)=f(zi(l+1))\begin{array}{l} z_{i}^{(l+1)}=\mathbf{w}_{i}^{(l+1)} \mathbf{y}^{l}+b_{i}^{(l+1)} \\ y_{i}^{(l+1)}=f\left(z_{i}^{(l+1)}\right) \end{array}zi(l+1)​=wi(l+1)​yl+bi(l+1)​yi(l+1)​=f(zi(l+1)​)​
      • 采用Dropout的网络计算公式:
        rj(l)∼Bernulli⁡(p)y~(l)=r(l)∗y(l)zi(l+1)=wi(l+1)y~l+bi(l+1)yi(l+1)=f(zi(l+1))\begin{aligned} r_{j}^{(l)} & \sim \operatorname{Bernulli}(p) \\ \widetilde{\mathbf{y}}^{(l)} &=\mathbf{r}^{(l)} * \mathbf{y}^{(l)} \\ z_{i}^{(l+1)} &=\mathbf{w}_{i}^{(l+1)} \widetilde{\mathbf{y}}^{l}+b_{i}^{(l+1)} \\ y_{i}^{(l+1)} &=f\left(z_{i}^{(l+1)}\right) \end{aligned}rj(l)​y​(l)zi(l+1)​yi(l+1)​​∼Bernulli(p)=r(l)∗y(l)=wi(l+1)​y​l+bi(l+1)​=f(zi(l+1)​)​
        其中Bernoulli函数是为了生成概率rrr向量,也就是随机生成一个0(False),1(True)0(False),1(True)0(False),1(True)的向量。
      • 注意\color{red}注意注意: 经过上面屏蔽掉某些神经元,使其激活值为000以后,我们还需要对向量进行缩放,也就是乘以1/(1−p)1/(1-p)1/(1−p)。如果你在训练的时候,经过置000后,没有对进行缩放(rescale),那么在测试的时候,就需要对权重进行缩放,操作如下。
    • 在测试模型阶段
      如果在训练时,没有乘以概率ppp;预测模型的时候,每一个神经单元的权重参数要乘以概率ppp。

      即:Wtest(l)=pW(l)\mathbf{W}^{(l)}_{test} = p\mathbf{W}^{(l)}Wtest(l)​=pW(l)
  5. Python语言描述
    • 假设对于第lll层神经元,设定保留神经元比例概率keep_prob=0.8,即该层有20%的神经元停止工作。dldldl为dropout向量,设置dldldl为随机vector,其中80%的元素为1,20%的元素为0。在python中可以使用如下语句生成dropout vector:
      dl = np.random.rand(al.shape[0],al.shape[1])<keep_prob
    • 然后,第lll层经过dropout,随机删减20%的神经元,只保留80%的神经元,其输出为:
      al = np.multiply(al,dl)
    • 最后,还要对alal进行scale up处理,即:
      al /= keep_prob

      以上就是Inverted dropout的方法。之所以要对alalal进行scale up是为了保证在经过dropout后,alalal作为下一层神经元的输入值尽量保持不变。假设第lll层有50个神经元,经过dropout后,有10个神经元停止工作,这样只有40神经元有作用。那么得到的ala_lal​只相当于原来的80%。scale up后,能够尽可能保持ala_lal​的期望值相比之前没有大的变化。

    • 注意\color{red}注意注意:
      • Inverted dropout的另外一个好处就是在对该dropout后的神经网络进行测试时能够减少scaling问题。因为在训练时,使用scale up保证alal的期望值没有大的变化,测试时就不需要再对样本数据进行类似的尺度伸缩操作了。
      • 使用dropout训练结束后(如果做了scale up),在测试和实际应用模型时,不需要进行dropout和随机删减神经元,所有的神经元都在工作。

1.7 理解 dropout(Understanding Dropout)

  1. 为什么dropout可以避免过拟合呢?

    • 从机器学习的角度
      Dropout通过每次迭代训练时,随机选择不同的神经元,相当于每次都在不同的神经网络上进行训练,类似机器学习中Bagging的方法(三个臭皮匠,赛过诸葛亮),能够防止过拟合。
    • 从权重www的角度
      对于某个神经元来说,某次训练时,它的某些输入在dropout的作用被过滤了。而在下一次训练时,又有不同的某些输入被过滤。经过多次训练后,某些输入被过滤,某些输入被保留。
      这样,该神经元就不会受某个输入非常大的影响,影响被均匀化了。也就是说,对应的权重www不会很大。这从从效果上来说,与L2 regularization是类似的,都是对权重www进行“惩罚”,减小了www的值。
    • 从神经元之间的复杂角度
      因为dropout程序导致两个神经元不一定每次都在一个dropout网络中出现。迫使网络去学习更加鲁棒的特征 ,这些特征在其它的神经元的随机子集中也存在。使神经网络更加能学习到与其他神经元之间的更加健壮robust的特征。
  2. 使用dropout的时,需要注意:
    • 不同隐藏层的dropout系数keep_prob可以不同。一般来说,神经元越多的隐藏层,keep_out可以设置得小一些.,例如0.5;神经元越少的隐藏层,keep_out可以设置的大一些,例如0.8,设置是1。越容易出现overfitting的隐藏层,其keep_prob就设置的相对小一些。没有准确固定的做法,通常可以根据validation进行选择。
    • Dropout在电脑视觉CV领域应用比较广泛,因为输入层维度较大,而且没有足够多的样本数量。值得注意的是dropout是一种regularization技巧,用来防止过拟合的,最好只在需要regularization的时候使用dropout。
    • 使用dropout的时候,可以通过绘制cost function来进行debug,看看dropout是否正确执行。一般做法是,先将所有层的keep_prob全设置为1,绘制cost function,即涵盖所有神经元,看J是否单调下降。然后,在下一次迭代训练时,再将keep_prob设置为其它值。

1.8 其他正则化方法(Other regularization methods)

除了L2 regularization和dropout regularization之外,还有其它减少过拟合的方法。

  1. 增加训练样本数量
    通常成本较高,难以获得额外的训练样本。但是,我们可以对已有的训练样本进行一些处理来“制造”出更多的样本,称为data augmentation。

    • 图片识别中
      可以对已有的图片进行水平翻转、垂直翻转、任意角度旋转、缩放或扩大等等。如下图所示,这些处理都能“制造”出新的训练样本。虽然这些是基于原有样本的,但是对增大训练样本数量还是有很有帮助的,不需要增加额外成本,却能起到防止过拟合的效果。
    • 数字识别中:
      可以将原有的数字图片进行任意旋转或者扭曲,或者增加一些noise。
  2. early stopping
    一个神经网络模型随着迭代训练次数增加,train set error一般是单调减小的,而dev set error 先减小,之后又增大。也就是说训练次数过多时,模型会对训练样本拟合的越来越好,但是对验证集拟合效果逐渐变差,即发生了过拟合。

    • Def:迭代训练次数不是越多越好,可以通过train set error和dev set error随着迭代次数的变化趋势,选择合适的迭代次数,即early stopping。如下图:
    • 机器学习过程包括几个步骤:
      • 选择算法来优化代价函数JJJ,方法有:梯度下降、MomentumRMSpropAdam等;
      • 选好优化函数后,找工具过拟合,工具有:正则化,扩增数据等。
      • 上面的1和2时可以交替进行,相互独立,互不影响,把它叫做“正交化(orthogonalization)\color{red}正交化(orthogonalization)正交化(orthogonalization)”。
    • early stopping的主要缺点:
      使用early stopping,不能正交化(orthogonalization)\color{red}正交化(orthogonalization)正交化(orthogonalization)地处理优化代价函数和找工具防止过拟合。

      Early stopping的做法通过减少得带训练次数来防止过拟合,这样J就不会足够小。也就是说,early stopping将上述两个目标融合在一起,同时优化,但可能没有“分而治之”(正交化(orthogonalization)\color{red}正交化(orthogonalization)正交化(orthogonalization))的效果好。

    • L2 regularization可以实现“分而治之”(正交化(orthogonalization)\color{red}正交化(orthogonalization)正交化(orthogonalization))的效果:迭代训练足够多,减小JJJ,而且也能有效防止过拟合。而L2 regularization的缺点之一是最优的正则化参数λ\lambdaλ的选择比较复杂。对这一点来说,early stopping比较简单。总的来说,L2 regularization更加常用一些。

1.9 归一化输入(Normalizing inputs)

在训练神经网络时,标准化输入可以提高训练的速度。

  1. 标准化输入
    μ=1m∑i=1mX(i)σ2=1m∑i=1m(X(i))2X′:=X−μσ2\begin{array}{l}\mu=\frac1m\sum_{i=1}^mX^{(i)}\\ \sigma^2=\frac1m\sum_{i=1}^m(X^{(i)})^2\\ X':=\frac{X-\mu}{\sigma^2}\end{array}μ=m1​∑i=1m​X(i)σ2=m1​∑i=1m​(X(i))2X′:=σ2X−μ​​
    其中μ\muμ是均值,σ\sigmaσ是方差,XXX和X′X'X′是输入和归一化后的输入。下图可见:

    Tip\color{red}TipTip:
    由于训练集进行了标准化处理,那么对于测试集或在实际应用时,应该使用同样的μ\color{red}\muμ和σ2\color{red}\sigma^2σ2对其进行标准化处理。这样保证了训练集合测试集的标准化操作一致
  2. 为什么要进行标准化输入
    主要是为了让所有输入归一化同样的尺度上,方便进行梯度下降算法时能够更快更准确地找到全局最优解。
    假如输入特征是二维的,且x1x_1x1​的范围是[1,1000],x2x_2x2​的范围是[0,1]。

    • 不进行标准化处理
      x1x_1x1​与x2x_2x2​之间分布极不平衡,训练得到的w1w_1w1​和w2w_2w2​也会在数量级上差别很大。这样导致的结果是costfunctioncost\;functioncostfunction与www和bbb的关系可能是一个非常细长的椭圆形碗。对其进行梯度下降算法时,由于w1w_1w1​和w2w_2w2​数值差异很大,只能选择很小的学习因子α\alphaα,避免JJJ振荡过大。一旦α\alphaα较大,必然发生振荡,JJJ不再收敛。如下左图所示。
    • 进行了标准化操作
      x1x_1x1​与x2x_2x2​分布均匀,w1w_1w1​和w2w_2w2​数值差别不大,得到的costfunctioncost\;functioncostfunction与www和bbb的关系是类似圆形碗。对其进行梯度下降算法时,α\alphaα可以选择相对大一些,且JJJ一般不会发生振荡,保证JJJ是收敛的。如下右图所示。
  3. 输入特征之间的范围较接近
    如果输入特征之间的范围本来就比较接近,那么不进行标准化操作也是没有太大影响的。但是,标准化处理在大多数场合下还是值得推荐的。
    Tip\color{red}TipTip:
    再次强调:由于训练集进行了标准化处理,那么对于测试集或在实际应用时,应该使用同样的μ\color{red}\muμ和σ2\color{red}\sigma^2σ2对其进行标准化处理。这样保证了训练集合测试集的标准化操作一致

1.10 梯度消失/梯度爆炸(Vanishing / Exploding gradients)

在深度神经网络中可能存在这样一个问题:梯度消失和梯度爆炸。意思是:当训练一个 层数非常多的神经网络时,计算得到的梯度随着层数的增加,近似成指数级别的减小或增大。

  • 例子
    假设一个多层的每层只包含两个神经元的深度神经网络模型,如下图所示:

    1. 简化处理

      1. 令各层的激活函数为线性函数,即g(Z)=Zg(Z)=Zg(Z)=Z;
      2. 令各层常数项b全部为零;
    2. 该网络的预测输出Y^\hat YY^为:
      Y^=W[L]W[L−1]W[L−2]⋯W[3]W[2]W[1]X\hat Y=W^{[L]}W^{[L-1]}W^{[L-2]}\cdots W^{[3]}W^{[2]}W^{[1]}XY^=W[L]W[L−1]W[L−2]⋯W[3]W[2]W[1]X

      • 假设每个权重矩阵W[l]=[1.5001.5],l=1,...,L−1W^{[l]} = \begin{bmatrix} 1.5 & 0 \\0 & 1.5 \\\end{bmatrix},\;l=1,...,L-1W[l]=[1.50​01.5​],l=1,...,L−1,最后W[L]=[1.51.5]W^{[L]}= [1.5\; 1.5]W[L]=[1.51.5],则:
        Y^=W[L][1.5001.5](L−1)X\hat Y= W^{[L]}\begin{bmatrix} 1.5 & 0 \\ 0 & 1.5 \\\end{bmatrix}^{(L -1)}XY^=W[L][1.50​01.5​](L−1)X
        因此,LLL越大,Y^\hat YY^越大,且呈指数型增长。我们称之为数值爆炸
      • 假设每个权重矩阵W[l]=[0.5000.5],l=1,...,L−1W^{[l]} = \begin{bmatrix} 0.5& 0 \\ 0 & 0.5 \\ \end{bmatrix},\;l=1,...,L-1W[l]=[0.50​00.5​],l=1,...,L−1,则:
        Y^=W[L][0.5000.5](L−1)X\hat Y= W^{[L]}\begin{bmatrix} 0.5 & 0 \\ 0 & 0.5 \\\end{bmatrix}^{(L - 1)}XY^=W[L][0.50​00.5​](L−1)X
        忽略W[L]W^{[L]}W[L],因此每个矩阵都小于1,网络层数LLL越多,Y^\hat YY^呈指数型减小。我们称之为数值消失
    3. 结论
      • 如果各层权重W[l]W^{[l]}W[l]都大于111或者都小于111,那么各层激活函数的输出将随着层数l的增加,呈指数型增大或减小。当层数很大时,出现数值爆炸消失
      • 如果梯度(导数)呈现同样的指数型增大或减小的变化。LLL非常大时,则梯度会非常大或非常小,引起每次更新的步进长度过大或者过小,使训练过程十分困难。

1.11 神经网络的权重初始化(Weight Initialization for Deep NetworksVanishing / Exploding gradients)

改善梯度消失/梯度爆炸(Vanishing and Exploding gradients)这类问题:

  • 权重www初始化处理:

    • 以单个神经元为例,激活函数为线性函数,lll层的输入个数为nnn,其输出为:
      z=w1x1+w2x2+⋯+wnxny=a=g(z)\begin{array}{l}z=w_1x_1+w_2x_2+\cdots+w_nx_n\\ y= a = g(z)\end{array}z=w1​x1​+w2​x2​+⋯+wn​xn​y=a=g(z)​

      为了防止zzz值过大或过小,随着nnn越大,希望wiw_iwi​越小,因为zzz是wixiw_i x_iwi​xi​的和,希望每项值更小。因此可以设置 wi=1nw_i=\frac{1}{n}wi​=n1​,其中nnn表示神经元的输入特征数量。
    • Python语言处理:
      设置w[l]w^{[l]}w[l]层权重

      w[l] = np.random.randn(n[l],n[l-1])*np.sqrt(1/n[l-1])
      

      其中n[l−1]n^{[l - 1]}n[l−1]是第l−1l-1l−1层神经元数量。

  • 不同的激活函数,不同的权重处理方法:
    • 激活函数是tanh:其方差为1n\frac1nn1​

      w[l] = np.random.randn(n[l],n[l-1])*np.sqrt(1/n[l-1])
      
    • 激活函数是ReLU:其方差为2n\frac2nn2​
      w[l] = np.random.randn(n[l],n[l-1])*np.sqrt(2/n[l-1])
      
    • Yoshua Bengio提出了另外一种初始化www的方法,令其方差为2n[l−1]n[l]\frac{2}{n^{[l-1]}n^{[l]}}n[l−1]n[l]2​:
      w[l] = np.random.randn(n[l],n[l-1])*np.sqrt(2/n[l-1]*n[l])
      

      至于选择哪种初始化方法因人而异,可以根据不同的激活函数选择不同方法。

  • Tip\color{red}TipTip:
    如果想添加方差,方差参数则是另一个你需要调整的超级参数,虽然调优该参数能起到一定作用,但考虑到相比调优,其它超级参数的重要性,通常把方差的超参数的优先级放的比较低。

1.12 梯度的数值逼近(Numerical approximation of gradients)

Back Propagation神经网络有一项重要的测试是梯度检查(gradient checking),是检查验证反向传播过程中梯度下降算法是否正确。

  • 求近似梯度值:

    f(θ)f\left( \theta \right)f(θ)为因变量,θ\thetaθ为自变量,在θ\thetaθ 右侧,设置一个θ+ε\theta +\varepsilonθ+ε,在θ\thetaθ左侧,设置θ−ε\theta -\varepsilonθ−ε。根据积分公式:
    f′(θ)=f(θ+ε)−f(θ−ε)2εf'(\theta)=\frac {f\left( \theta + \varepsilon \right) - f(\theta -\varepsilon)}{2\varepsilon}f′(θ)=2εf(θ+ε)−f(θ−ε)​
    其中ε>0\varepsilon > 0ε>0,且足够小。上式左右两边逼近误差可以达到O(ε2)O(\varepsilon^{2})O(ε2)。

1.13 梯度检验(Gradient checking)

梯度检验的核心:
∂J∂θ=lim⁡ε→0J(θ+ε)−J(θ−ε)2ε\frac{\partial J}{\partial \theta}=\lim _{\varepsilon \rightarrow 0} \frac{J(\theta+\varepsilon)-J(\theta-\varepsilon)}{2 \varepsilon}∂θ∂J​=ε→0lim​2εJ(θ+ε)−J(θ−ε)​

  1. 对于1层神经网络的梯度检验

    1. 对于正向传播
      将W[1],b[1]W^{[1]},b^{[1]}W[1],b[1]这些矩阵/向量先"拉长"构造成一维向量,然后将这些一维向量组合起来构成一个更大的一维向量θ\color{red}\thetaθ。则cost function可以写成:
      J(W[1],b[1])=J(θ)J(W^{[1]},b^{[1]})=J(\theta)J(W[1],b[1])=J(θ)
    2. 使用导数的定义计算出“gradapprox”:
      1. θ+=θ+ε\theta^{+}=\theta+\varepsilonθ+=θ+ε
      2. θ−=θ−ε\theta^{-}=\theta-\varepsilonθ−=θ−ε
      3. J+=J(θ+)J^{+}=J\left(\theta^{+}\right)J+=J(θ+)
      4. J−=J(θ−)J^{-}=J\left(\theta^{-}\right)J−=J(θ−)
      5. gradapprox=J+−J−2ε\text{gradapprox} =\frac{J^{+}-J^{-}}{2 \varepsilon}gradapprox=2εJ+−J−​
    3. 使用下面的公式计算“gradapprox”和“grad”之间的差值:
      difference=∥grad−gradapprox∥2∥grad∥2+∥gradapprox∥2\text {difference}=\frac{\| \text {grad}-\text {gradapprox} \|_{2}}{\| \text {grad}\left\|_{2}+\right\| \text {gradapprox} \|_{2}}difference=∥grad∥2​+∥gradapprox∥2​∥grad−gradapprox∥2​​
      当difference小于特定值时,表示算法没有错误。
  2. 对于多层神经网络的梯度检验

    1. 将W[1],b[1],⋯,W[L],b[L]W^{[1]},b^{[1]},\cdots,W^{[L]},b^{[L]}W[1],b[1],⋯,W[L],b[L]中矩阵/向量先"拉长"构造成一维向量,然后和同层的向量构成一个更大的向量。然后将这些一维向量组合起来构成一个更大的一维向量θ=[θ1,...θL]\color{red}\mathbb\theta = [\theta_1, ...\theta_L]θ=[θ1​,...θL​]。则cost function可以写成:
      J(W[1],b[1],⋯,W[L],b[L])=J(θ1,...θL)J(W^{[1]},b^{[1]},\cdots,W^{[L]},b^{[L]})=J(\theta_1, ...\theta_L)J(W[1],b[1],⋯,W[L],b[L])=J(θ1​,...θL​)

    2. 将Back Propagation通过梯度下降算法得到的dW[1],db[1],⋯,dW[L],db[L]dW^{[1]},db^{[1]},\cdots,dW^{[L]},db^{[L]}dW[1],db[1],⋯,dW[L],db[L]按照第一步的顺序构造成一个一维向量dθ=[dθ1,...dθL]\color{red}d\mathbb\theta = [d\theta_1, ...d\theta_L]dθ=[dθ1​,...dθL​]。dθd\thetadθ的维度与θ\thetaθ一致。

    3. 利用J(θ)\color{red}J(\theta)J(θ)对每个θi\theta_iθi​计算近似梯度,其值与反向传播算法得到的dθi\color{red}d\theta_idθi​相比较,检查是否一致。对于第iii个元素,近似梯度为:
      dθapprox[i]≈J(θ1,θ2,⋯,θi+ε,⋯)−J(θ1,θ2,⋯,θi−ε,⋯)2εd\theta_{approx}[i]\approx \frac{J(\theta_1,\theta_2,\cdots,\theta_i+\varepsilon,\cdots)-J(\theta_1,\theta_2,\cdots,\theta_i-\varepsilon,\cdots)}{2\varepsilon}dθapprox​[i]≈2εJ(θ1​,θ2​,⋯,θi​+ε,⋯)−J(θ1​,θ2​,⋯,θi​−ε,⋯)​
      参数 θ\thetaθ不再是一个标量,而是一个字典。我们需要实现一个字典转向量和向量转字典的函数,以用于梯度检验算法中。

    4. 计算:
      ∣∣dθapprox−dθ∣∣2∣∣dθapprox∣∣2+∣∣dθ∣∣2\frac{||d\theta_{approx}-d\theta||_2}{||d\theta_{approx}||_2+||d\theta||_2}∣∣dθapprox​∣∣2​+∣∣dθ∣∣2​∣∣dθapprox​−dθ∣∣2​​

      • 如果欧氏距离越小,例如10−710^{-7}10−7,甚至更小,则表明dθapproxd\theta_{approx}dθapprox​与dθd\thetadθ越接近,即反向梯度计算是正确的,没有bugs。
      • 如果欧氏距离较大,例如10−510^{-5}10−5,则表明梯度计算可能出现问题,需要再次检查是否有bugs存在。
      • 如果欧氏距离很大,例如10−310^{-3}10−3,甚至更大,则表明dθapproxd\theta_{approx}dθapprox​与dθd\thetadθ差别很大,梯度下降计算过程有bugs,需要仔细检查。

1.14 梯度检验应用的注意事项(Gradient Checking Implementation Notes)

在进行梯度检查的过程中有几点需要注意的地方:

  • 不要在整个训练过程中都进行梯度检查,仅仅作为debug使用。
  • 如果梯度检查出现错误,找到对应出错的梯度,检查其推导是否出现错误。
  • 注意不要忽略正则化项,计算近似梯度的时候要包括进去。
  • 梯度检查时关闭dropout,检查完毕后再打开dropout。
  • 随机初始化时运行梯度检查,经过一些训练后再进行梯度检查(不常用)。

本章总结

  1. 数据分配,方差和偏差

    • 上图左边,经典机器学习和深度学习模型所需要的样本数有非常大的差别,深度学习的样本数是经典 ML 的成千上万倍。因 此训练集、开发集和测试集的分配也有很大的区别,当然我们假设这些不同的数据集都服从同分布。

    • 上图右边,偏差与方差问题同样是机器学习模型中常见的挑战,上图依次展示了由高偏差带来的欠拟合和由高方差带来的过拟合。一般而言,解决高偏差的问题是选择更复杂的网络或不同的神经网络架构,而解决高方差的问题可以添加正则化、减少模型冗余或使用更多的数据进行训练。

      当然,机器学习模型需要注意的问题远不止这些,但在配置我们的 ML 应用中,它们是最基础和最重要的部分。其它如数据预处理、数据归一化、超参数的选择等都在后面的信息图中有所体现。

  2. 正则化
    正则化是解决高方差或模型过拟合的主要手段,过去数年,研究者提出和开发了多种适合机器学习算法的正则化方法,如数据增强、L2 正则化(权重衰减)、L1 正则化、Dropout、Drop Connect、随机池化提前终止等。

    • 上图左列,L1L_1L1​和 L2L_2L2​ 正则化也是是机器学习中使用最广泛的正则化方法。L1L_1L1​正则化向目标函数添加正则化项,以减少参数的绝对值总和;而 L2L_2L2​ 正则化中,添加正则化项的目的在于减少参数平方的总和。根据之前的研究,L1L_1L1​ 正则化中的很多参数向量是稀疏向量,因为很多模型导致参数趋近于 0,因此它常用于特征选择设置中。此外,参数范数惩罚L2L_2L2​ 正则化能让深度学习算法「感知」到具有较高方差的输入xxx,因此与输出目标的协方差较小(相对增加方差)的特征权重将会收缩。

    • 中间列,上图展示了 Dropout 技术,即暂时丢弃一部分神经元及其连接的方法。随机丢弃神经元可以防止过拟合,同时指数级、高效地连接不同网络架构。一般使用了 Dropout 技术的神经网络会设定一个保留率 p,然后每一个神经元在一个批量的训练中以概率 1-p 随机选择是否去掉。在最后进行推断时所有神经元都需要保留,因而有更高的准确度。

      Bagging 是通过结合多个模型降低泛化误差的技术,主要的做法是分别训练几个不同的模型,然后让所有模型表决测试样例的输出。而 Dropout 可以被认为是集成了大量深层神经网络的 Bagging 方法,因此它提供了一种廉价的 Bagging 集成近似方法,能够训练和评估值数据数量的神经网络。

    • 右边列,上图还描述了数据增强与提前终止等正则化方法。数据增强通过向训练数据添加转换或扰动来人工增加训练数据集。数据增强技术如水平或垂直翻转图像、裁剪、色彩变换、扩展和旋转通常应用在视觉表象和图像分类中。而提前终止通常用于防止训练中过度表达的模型泛化性能差。如果迭代次数太少,算法容易欠拟合(方差较小,偏差较大),而迭代次数太多,算法容易过拟合(方差较大,偏差较小)。因此,提前终止通过确定迭代次数解决这个问题。

  3. 最优化-1
    最优化是机器学习模型中非常非常重要的模块,它不仅主导了整个训练过程,同时还决定了最后模型性能的好坏和收敛需要的时长。以下展示了最优化方法需要关注的知识点,包括最优化的预备和具体的最优化方法,以下是第一部分。

    • 最左边,归一化输入数据,而且开发集与测试集归一化的常数(均值与方差)与训练集是相同的。上图也展示了归一化的原因,因为如果特征之间的量级相差太大,那么损失函数的表面就是一张狭长的椭圆形,而梯度下降或最速下降法会因为「锯齿」现象而很难收敛,因此归一化为圆形有助于减少下降方向的震荡。

    • 中间列,梯度消失与梯度爆炸问题也是十分常见的现象。「梯度消失」指的是随着网络深度增加,参数的梯度范数指数式减小的现象。梯度很小,意味着参数的变化很缓慢,从而使得学习过程停滞。梯度爆炸指神经网络训练过程中大的误差梯度不断累积,导致模型权重出现很大的更新,在极端情况下,权重的值变得非常大以至于出现 NaN 值。

    • 右边列,梯度检验现在可能用的比较少,因为我们在 TensorFlow 或其它框架上执行最优化算法只需要调用优化器就行。梯度检验一般是使用数值的方法计算近似的导数并传播,因此它能检验我们基于解析式算出来的梯度是否正确。

吴恩达-deep learning 02.改善深层神经网络:超参数调试、正则化以及优化Week1相关推荐

  1. 吴恩达深度学习 | (12) 改善深层神经网络专项课程第三周学习笔记

    课程视频 第三周PPT汇总 吴恩达深度学习专项课程共分为五个部分,本篇博客将介绍第二部分改善深层神经网络专项的第三周课程:超参数调试.Batch Normalization和深度学习框架. 目录 1. ...

  2. 吴恩达深度学习的改善深层神经网络编程作业:优化Optimization

  3. Andrew Ng(吴恩达) deep learning 课程 (coursera)

    引言 前段时间 Andrew Ng(吴恩达)在 Coursera 开设了深度学习的课程,正如 Andrew 在 Coursera 上的机器学习课程一样,Andrew 总是面向零基础的工程师来授课,而不 ...

  4. python下的橡皮线_python下载吴恩达deep learning编程习题

    在cousera上注册deep learning后,可下载课后习题. 1.进入编程环境后,单击左上角的file-open,进入文件管理模式 2. 点击红圈文件夹进入根目录 3. 在根目录处右上角new ...

  5. 吴恩达Deep learning笔记(一)

    Welcome to Deep learning Specialization 介绍了深度学习目前的发展状况以及未来的前景 Introdcution to Deep Learning what'is ...

  6. 吴恩达作业3:利用深层神经网络实现小猫的分类

    利用4层神经网络实现小猫的分类,小猫训练样本是(209,64*64*3=12288),故输入节点是12288个,隐藏层节点依次为20,7,5,输出层为1. 首先看文件路径,dnn_utils_v2.p ...

  7. 2020-6-9 吴恩达-改善深层NN-w3 超参数调试(3.3 超参数训练的实践:Pandas(资源少,一次一个模型) VS Caviar(资源多,一次多个模型))

    1.视频网站:mooc慕课https://mooc.study.163.com/university/deeplearning_ai#/c 2.详细笔记网站(中文):http://www.ai-sta ...

  8. 吴恩达深度学习 —— 4.1 深层神经网络

    什么是深度学习网络,我们已经学过了逻辑回归,并且见过了单隐层神经网络,下图是一个双隐层神经网络,以及一个五隐层的神经网络的例子. 我们说逻辑回归是一个浅层模型,浅层或是深层是一个程度的问题,要记住当我 ...

  9. 【cs230】吴恩达Deep Learning-2/3

    [cs230]吴恩达Deep Learning-2/3 0. 课程计划 5. AI + Healthcare 5.1 AI可以解决哪些高影响力问题 5.1.1 Levels 5.1.2 学习范式的变化 ...

  10. 吴恩达.深度学习系列-C4卷积神经网络-W2深度卷积模型案例

    吴恩达.深度学习系列-C4卷积神经网络-W2深度卷积模型案例 (本笔记部分内容直接引用redstone的笔记http://redstonewill.com/1240/.原文整理的非常好,引入并添加我自 ...

最新文章

  1. k8s停止服务_使用 K8S 几年后,这些技术专家有话要说
  2. threejs对象拾取
  3. linux telnet 权限,允许telnet 通过root用户进行访问
  4. 计算机控制实时的概念,计算机控制系统复习资料..doc
  5. 非标机械设计老工程师的经验总结,还不赶紧收藏
  6. oracle regexp
  7. 人物连连看 python课程设计 pygame学习
  8. css 背景重复渐变_CSS3重复渐变[CSS3提示]
  9. linux强制安装deb,ubuntu强行修改deb安装包依赖
  10. Preface Numbering序言页码
  11. Πολιτική απορρήτου
  12. oracle 正序 倒序 排列
  13. android计算器开发
  14. RTX腾讯通基本功能
  15. 全球首发护舒宝敏感肌系列 小豆子李子璇也Pick的姨妈CP
  16. 【武忠祥高等数学基础课笔记】反常积分
  17. 简历模板(毕业学生)
  18. 【ESP 保姆级教程】疯狂传感器篇 —— 案例:ESP8266 + MQ7一氧化碳传感器 + 串口输出
  19. ESP32 Secure Boot和Flash加密
  20. 世界杯 叮当 机器人 树莓派_人工智能参与世界杯狂欢 腾讯叮当与央视网联合开展巅峰对决直播答题活动...

热门文章

  1. C++规范编码引涉语法点之(8)trivial和non-trivial构造/析构/复制/赋值函数 及POD类型
  2. android 显示和风天气字体图标
  3. 解读神经网络十大误解,再也不会弄错它的工作原理
  4. CSScript 使用纪要
  5. 战狼2影评-20170807
  6. 《当程序员的那些狗日日子》(四十)繁杂的需求
  7. meta是什么意思中文_Meta 的意思是什么?
  8. 用计算机术语写毕业寄语,大学毕业寄语(精选50句)
  9. springboot项目打包报错
  10. 极光一键登录集成手册 | Android