谨慎建模,小数据也可以用好深度学习

谨慎建模,小数据也可以用好深度学习

在Simply Stats,Jeff Leek 发表了一篇叫做‘当你的数据量不够大的时候,最好不要使用深度学习’的文章,这让我略有不安。首先声明,我不认为深度学习是万能的,并且同意Jeff的中心观点(稍后我会具体解释),但是我认为有几点值得更深探索。

Jeff分析了两种方法在分类手写的0和1时候的表现(使用众所周知的MNIST数据)。他对比了一个5层并使用hyperbolic tangent激活方程的神经网络和仅用10像素和最小p值的Leekasso。他惊讶地发现,当数据量小的时候,Leekasso的表现胜过神经网络。图示如下:

所以结论就是,如果你的样本量小于100,就不要使用深度学习,因为模型会过度拟合,以至于在测试数据上的表现会很差。这样的结论或许有些言之过早:深度模型的模型复杂,并且训练起来也比较棘手,所以直觉告诉我,缺乏模型收敛性或者训练难度是模型表现差的原因,而不是过度拟合。

//深度学习对比Leekasso Redux

首先要做的事情就建立一个人们可以实际使用的深度学习模型,也就是现代版的多层感知机(MLP)和卷积神经网络(CNN)。如果Jeff的结论正确,这些模型只有在样本很小的时候才会过度拟合。

让我们来建立一个简单的使用relu激活方程的MLP和一个像VGG那样的卷积模型,然后再比较它们和Leekasso性能的差异。所有代码都可以在Github查看。感谢我的暑期实习生Michael Chen,用python和Keras实现了建模。

Github链接:github.com/beamandrew/deep_learning_works/blob/master/mnist.py

MLP模型很标准,看上去的这样的:

CNN看上去也很熟悉,如果你工作中使用过这些模型的话:

 

作为参考,MLP大约有12万个参数,CNN大约有20万个参数。根据原文假设,我们似乎的确会过度拟合,当有大量的参数和小样本。

 

我们尝试着尽可能靠近原始分析—我们用了5层交叉验证,但是用了标准MNIST测试数据(有大约2000个0和1的样本)来检验模型的表现。我们将测试数据分成了两部分,第一部分用来检验合成模型过程的收敛性,第二部分用来测试样本外预测准度。我们用了默认参数。

我们尽力复制了原文中Leekasso和MLP 的Python版本,以下是每个模型的样本外预测准度:

这和原文中的分析结果很不一样!MLP在小样本使用时仍然有很差的效果,但我们的卷积神经网络在大小样本之下的表现都近乎完美。

//为什么会这样?

深度学习模型训练起来及其棘手,而知道如何调参是个很重要的技能。许多参数的调整是和所解决的问题息息相关(尤其是关于SGD的参数),如果调参不当会导致整个模型的性能大幅度下降。如果你在构建深度学习模型,那就一定要记住:模型的细节是十分重要的,一定要当心那些看上去像深度学习的黑箱操作。

  • 以下是我对原文中分析结果的猜想:
  • 激活方程的选择是很重要的,而tanh网络训练起来是困难的。这就是为什么这个领域大部分已经转向了‘relu’系列的激活方程。
  • 确保随机梯度下降是收敛的。在原始比较当中,模型只训练了20个epoch,这可能是不够的。因为当n = 10个样本时,20个epoch仅有20*10 = 200次的迭代。而遍历全部的MNIST数据集大概相当于6万次梯度更新,并且用大约1000000次迭代更新是很平常的事。如果只进行200次迭代更新,那么就需要很大的学习率,否则模型就无法收敛。h2o.deeplearning()的默认学习率是0.005,所以在少量的迭代次数下,这样的学习率很有可能太小了。我们的模型训练了200个epoch,在前50个epoch的训练中,我们看到了样本外预测准确率有很大的浮动。所以我猜想,缺乏模型收敛性是原文中模型差距的主要的原因。
  • 确保检查默认参数设置。Keras很好,因为默认参数都尽力设置成了最好表现的数值,但是你依然需要确保你选择的参数对需要解决的问题来说是最好的。
  • 不同的框架会导致很不一样的结果。我尝试使用了最原始的R代码来看看结果是否一样。然而,我总是不能通过h2o.deeplearning()方程得到好结果。我猜想这是和该方程使用的优化过程有关。该方程好像使用的是弹性均值 SGD 以计算多个结点而加速训练。我不知道当你仅有少量样本数据时会不会出现故障,但我认为可能性是很大的。
  • 幸好,Rstudio的人很友善,他们刚刚发布了Keras的R接口,因此我可以用R来重新实现我的python代码。我们之前使用的MLP在R里面的实现如下:

我把这一段代码放进了Jeff的原始R代码,重新做图。我对Leekasso也进行了一点修改。原来的代码使用了lm( )(即线性回归),而我切换成了glm()(即logistic回归)。新的图表如下:

厉害了深度学习!一个相似的现象或许可以解释Leekasso的Python和R版本之间的不同。Python版本的logistic回归使用了liblinear作为其解算器,我认为这比R的默认解算器更可靠。这一点或许会有影响,因为Leekasso选择的变量是高度共线性的(collinear)。

  • 这个建模问题太简单了以至于说明不了什么问题。我重新跑了Leekasso但是仅仅使用了最好的预测器,其结果几乎完全等同于全Leekasso。实际上,我确定我可以做出一个不使用数据并且准确度高的分类器:只需要取图的中心像素,如果是黑色,则预测1,否则就预测0,正如David Robinson所指出的那样:

David也展示了其实大部分数组都可以用一个pixel分类。因此,这个问题给我们带来任何关于真正‘小’样本的洞见是不太可能的。因此,为这个问题建模所得的结论要谨慎对待。

//关于深度学习如何有效的误解:

最终,我想回到Jeff在原文中提到的观点,尤其是以下这句话:

“问题在于只有很少的几个地方有足够量的数据来使用深度学习【…】但我一直认为使用深度模型优于简单模型的主要优势在于当你有巨量的数据来调试巨量参数的时候。”

 

这篇文章,特别是结尾处,并不是完整的。很多人将深度学习想成是一个巨大的黑箱,里面有巨量的参数来学习任何的函数,只要你有足够的数据(‘足够’的意思是大约一百万左右)。当然,神经网络的确是极其灵活的,而这种灵活性是它们如此成功的部分原因。但这不是唯一原因,对吧?

 

synthroidnews.net,机器学习和统计领域的灵活模型发展已经有了70多年的历史。我不认为神经网络一定比所有相似复杂度的算法更灵活。

 

下面我列举了一些我认为神经网络很成功的原因:

  • 一切都是偏差方差折中的练习。我认为Jeff的主要观点是关于偏差方差的折中。如果你没有大量的数据,可能用一个简单的模型(高偏差,低方差)优于用一个极度复杂的模型(低偏差,高方差)。我认为这是一个普遍适用的好建议,然而:
  • 神经网络有一个很大的技术库来克服过度拟合。神经网络会有很多的参数,正如Jeff所说,这会导致很高的方差,如果我们没有足够的数据来稳定地估计参数值。这个领域很明确这个问题的存在,也开发了大量技术来减少方差。比如dropout结合随机梯度下降导致了一个像bagging一样的过程,但是使用在参数上而不是变量上。降低方差的技术例如dropout以其他模型难以复制的方式被加进了训练程序。这使得你可以在没有太多数据的情况下真正训练大模型。
  • 深度学习可以让你轻易直接在模型中包括问题的具体约束,以致减小方差。这是我最想强调的一点,也是最容易被忽略的一点。由于其模块化,神经网络可以让你真正整合,极大降低模型方差的强约束(或者说先验)。最好的例子就是卷积神经网络。在CNN中,我们实际上把图像的属性编码进模型本身。例如,当我们指定一个3*3的过滤器时,实际上是在直接告诉网络:本地连结的像素小集群会包含有用的信息。此外,我们还可以把图像的平移和旋转不变性直接编码进模型。所有这些都将模型偏差至图像性质,大规模地减少方差以致提高模型的表现。
  • 你不需要谷歌级别的大型数据来使用深度学习。使用以上的方法,即便你只有100至1000个样本,也可以从深度学习中受益。这些技术可以帮助你缓解高方差的问题,同时受益于模型的灵活性。你甚至可以通过迁移学习来创建其他工作。

 

综上所述,我认为以上的分析解释了为什么深度学习在应用中有优势,打破了深度学习需要大量参数和数据的假设。最后,本文并非否认Jeff的观点,而是旨在提供一个不同的视角,为读者带来启发。


作者:Andrew L.Beam
来自:beamandrew.github.io/
原文:You can probably use deep learning even if your data isn't that big
翻译:王思力