专栏首页AI研习社用深度学习每次得到的结果都不一样,怎么办?

用深度学习每次得到的结果都不一样,怎么办?

AI研习社按:本文作者 Jason Brownlee 为澳大利亚知名机器学习专家、教育者,对时间序列预测尤有心得。原文发布于其博客。AI研习社崔静闯、朱婷编译。

神经网络算法利用了随机性,比如初始化随机权重,因此用同样的数据训练同一个网络会得到不同的结果。

初学者可能会有些懵圈,因为算法表现得不太稳定。但实际上它们就是这么设计的。随机初始化可以让网络通过学习,得到一个所学函数的很好的近似。

然而, 有时候用同样的数据训练同一个网络,你需要每次都得到完全相同的结果。例如在教学和产品上。

在这个教程中,你会学到怎样设置随机数生成器,才能每次用同样的数据训练同一网络时,都能得到同样的结果。

我们开始。

教程概览

这个教程分为六部分:

  1. 为啥我每次得到的结果都不一样?
  2. 不同结果的演示
  3. 解决方法
  4. 用 Theano 后端设置随机数种子
  5. 用 TensorFlow 后端设置随机数种子
  6. 得到的结果还是不同,咋办?

运行环境

  • 该教程需要你安装了 Python SciPy。你能用 Python2 或 3 来演示这个例子
  • 需要你安装 Keras (v2.0.3+),后台为 TensorFlow (v1.1.0+) 或 Theano (v0.9+)
  • 还需要你安装了 scikit-learn,Pandas,NumPy 以及 Matplotlib

如果在 Python 环境的设置方面需要帮助,请看下面这个帖子:

How to Setup a Python Environment for Machine Learning and Deep Learning with Anaconda

为啥我每次得到的结果都不一样?

我发现这对神经网络和深度学习的初学者而言是个常见问题。

这种误解可能出于以下问题:

  • 我如何得到稳定的结果?
  • 我如何得到可重复的结果
  • 我应该如何设置种子点

神经网络特意用随机性来保证,能通过有效学习得到问题的近似函数。采用随机性的原因是:用它的机器学习算法,要比不用它的效果更好。

在神经网络中,最常见的利用随机性的方式是网络权值的随机初始化,尽管在其他地方也能利用随机性,这有一个简短的清单:

  • 初始化的随机性,比如权值
  • 正则化的随机性,比如 dropout
  • 层的随机性,比如词嵌入
  • 最优化的随机性,比如随机优化

这些甚至更多的随机性来源意味着,当你对同一数据运行同一个神经网络算法时,注定得到不同的结果。

想了解更多关于随机算法的原委,参考下面的帖子

Embrace Randomness in Machine Learning

不同结果的演示

我们可以用一个小例子来演示神经网络的随机性.

在这一节中,我们会建立一个多层感知器模型来学习一个以 0.1 为间隔的从 0.0 到 0.9 的短序列。给出 0.0,模型必须预测出 0.1;给出 0.1,模型必须预测出 0.2;以此类推。

下面是准备数据的代码

# create sequencelength = 10sequence = [i/float(length) for i in range(length)]# create X/y pairsdf = DataFrame(sequence)

df = concat([df.shift(1), df], axis=1)

df.dropna(inplace=True)# convert to MLPfriendly formatvalues = df.values

X, y = values[:,0], values[:,1]

我们要用的网络,有 1 个输入,10 个隐层节点和 1 个输出。这个网络将采用均方差作为损失函数,用高效的 ADAM 算法来训练数据

这个网络需要约 1000 轮才能有效的解决这个问题,但我们只对它训练 100 轮。这样是为了确保我们在预测时能得到一个有误差的模型。

网络训练完之后,我们要对数据集进行预测并且输出均方差。建立网络的代码如下:

# design network
model = Sequential()
model.add(Dense(10, input_dim=1))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')# fit network
model.fit(X, y, epochs=100, batch_size=len(X), verbose=0)# forecast
yhat = model.predict(X, verbose=0)
print(mean_squared_error(y, yhat[:,0])

在这个例子中,我们要建立 10 次网络并且输出 10 个不同的网络得分。

完整的代码如下:

from pandas import DataFrame
from pandas import concat
from keras.models import Sequential
from keras.layers import Dense
from sklearn.metrics import mean_squared_error
# fit MLP to dataset and print error

def fit_model(X, y):# design network
‍    model = Sequential()
    model.add(Dense(10, input_dim=1))
    model.add(Dense(1))
    model.compile(loss='mean_squared_error', optimizer='adam')# fit network
    model.fit(X, y, epochs=100, batch_size=len(X), verbose=0)# forecast
    yhat = model.predict(X, verbose=0)
    print(mean_squared_error(y, yhat[:,0]))# create sequence

    length = 10sequence = [i/float(length) for i in range(length)]# create X/y pairs

    df = DataFrame(sequence)
    df = concat([df.shift(1), df], axis=1)
    df.dropna(inplace=True)# convert to MLP friendly format

    values = df.values
    X, y = values[:,0], values[:,1]# repeat experiment

    repeats = 10for _ in range(repeats):

    fit_model(X, y)‍

运行这个例子会在每一行输出一个不同的精确值,具体结果也都不同。

下面是一个输出的示例:

0.0282584265697 0.0457025913022 0.145698137198 0.0873461454407 0.0309397604521 0.046649185173 0.0958450337178 0.0130660263779 0.00625176026631 0.00296055161492

解决方案

下面是两个主要的解决方案。

解决方案 #1:重复实验

解决这个问题传统且切实可行的方法是多次运行网络(30+),然后运用统计学方法概括模型的性能,并与其他模型作比较。

我强烈推荐这种方法,但是由于有些模型的训练时间太长,这种方法并不总是可行的。

解决方案 #2:设置随机数字生成器的种子

另一种解决方案是为随机数字生成器使用固定的种子。

随机数由伪随机数生成器生成。一个随机生成器就是一个数学函数,该函数将生成一长串数字,这些数字对于一般目的的应用足够随机。

随机生成器需要一个种子点开启该进程,在大多数实现中,通常默认使用以毫秒为单位的当前时间。这是为了确保,默认情况下每次运行代码都会生成不同的随机数字序列。该种子点可以是指定数字,比如 “1”,来保证每次代码运行时生成相同的随机数序列。只要运行代码时指定的种子的值不变,它是什么并不重要。

设置随机数生成器的具体方法取决于后端,我们将探究下在 Theano 和 TensorFlow 后端下怎样做到这点。

用 Theano 后端设置随机数种子

通常,Keras 从 NumPy 随机数生成器中获得随机源。

大部分情况下,Theano 后端也是这样。

我们可以通过从 random 模块中调用 seed() 函数的方式,设置 NumPy 随机数生成器的种子,如下面所示:

from numpy.random import seed
seed(1)

最好在代码文件的顶部导入和调用 seed 函数。

这是最佳的实现方式(best practice),这是因为当各种各样的 Keras 或者 Theano(或者其他的) 库作为初始化的一部分被导入时,甚至在直接使用他们之前,可能会用到一些随机性。

我们可以在上面示例的顶端再加两行,并运行两次。

每次运行代码时,可以看到相同的均方差值的列表(在不同的机器上可能会有一些微小变化,这取决于机器的精度),如下面的示例所示:

0.169326527063 2.75750621228e-05 0.0183287291562 1.93553737255e-07 0.0549871087449 0.0906326807824 0.00337575114075 0.00414857518259 8.14587362008e-08 0.0522927019639

你的结果应该跟我的差不多(忽略微小的精度差异)。

用 TensorFlow 后端设置随机数种子

Keras 从 NumPy 随机生成器中获得随机源,所以不管使用 Theano 或者 TensorFlow 后端的哪一个,都必须设置种子点。

必须在其他模块的导入或者其他代码之前,文件的顶端部分通过调用 seed() 函数设置种子点。

from numpy.random import seed
seed(1)

另外,TensorFlow 有自己的随机数生成器,该生成器也必须在 NumPy 随机数生成器之后通过立马调用 set_random_seed() 函数设置种子点。

from tensorflow import set_random_seed
set_random_seed(2)

要明确的是,在代码文件的顶端,在其他之前,一定要有以下 4 行:

from numpy.random import seed
seed(1)
from tensorflow import set_random_seed
set_random_seed(2)

你可以使用两个相同或者不同的种子。我认为这不会造成多大差别,因为随机源进入了不同的进程。

在以上示例中增加这 4 行,可以使代码每次运行时都产生相同的结果。你应该看到与下面列出的相同的均方差值(也许有一些微小差别,这取决于不同机器的精度):

0.224045112999 0.00154879478823 0.00387589994044 0.0292376881968 0.00945528404353 0.013305765525 0.0206255228201 0.0359538356108 0.00441943512128 0.298706569397

你的结果应该与我的差不多(忽略精度的微小差异)。

如果我仍然得到不同的结果,怎么办?

为了重复迭代,报告结果和比较模型鲁棒性最好的做法是多次(30+)重复实验,并使用汇总统计。如果这是不可行的,你可以通过为代码使用的随机数发生器设置种子来获得 100% 可重复的结果。

如果你已经按照上面的说明去做,仍然用相同的数据从相同的算法中获得了不同的结果,怎么办?

这可能是有其他的随机源你还没有考虑到。

来自第三方库的随机性

也许你的代码使用了另外的库,该库使用不同的也必须设置种子的随机数生成器。

试着将你的代码简化到最低要求(例如,一个数据样本,一轮训练等等),并仔细阅读 API 文档,尽力减少可能引入随机性的第三方库。

使用 GPU 产生的随机性

以上所有示例都假设代码是在一个 CPU 上运行的。

这种情况也是有可能的,就是当使用 GPU 训练模型时,可能后端设置的是使用一套复杂的 GPU 库,这些库中有些可能会引入他们自己的随机源,你可能会或者不会考虑到这个。

例如,有证据显示如果你在堆栈中使用了 Nvidia cuDNN,这可能引入额外的随机源( introduce additional sources of randomness),并且使结果不能准确再现。

来自复杂模型的随机性

由于模型的复杂性和训练的并行性,你可能会得到不可复现的结果。

这很可能是由后端库的效率造成的,或者是不能在内核中使用随机数序列。我自己没有遇到过这个,但是在一些 GitHub 问题和 StackOverflowde 问题中看到了一些案例。

如果只是缩小成因的范围的话,你可以尝试降低模型的复杂度,看这样是否影响结果的再现。

我建议您阅读一下你的后端是怎么使用随机性的,并看一下是否有任何选项向你开放。在 Theano 中,参考:

  • Random Numbers
  • Friendly random numbers
  • Using Random Numbers

在 TensorFlow 中,参考:

Constants, Sequences, and Random Values
tf.set_random_see

另外,为了更深入地了解,考虑一下寻找拥有同样问题的其他人。一些很好的搜寻平台包括 GitHub、StackOverflow 和 CrossValidated。

总结

在本教程中,你了解了如何在 Keras 上得到神经网络模型的可重复结果。特别是,你学习到了:

  • 神经网络是有意设计成随机的,固定随机源可以使结果可复现。
  • 你可以为 NumPy 和 TensorFlow 的随机数生成器设置种子点,这将使大多数的 Keras 代码 100% 的可重复使用。
  • 在有些情况下存在另外的随机源,并且你知道如何找出他们,或许也是固定它们。

via. machine learning mastery, 崔静闯、朱婷编译

本文分享自微信公众号 - AI研习社(okweiwu),作者:崔静闯、朱婷

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-06-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 「元学习」解析:学习如何梯度下降与学习新的算法

    AI 研习社:本文作者 Cody Marie Wild,她是一位机器学习领域的数据科学家,在生活中还是名猫咪铲屎官,她钟爱语言和简洁优美的系统。在这篇文章中,C...

    AI研习社
  • 【AI听】Hinton:反向传播要被彻底放弃了!Unity推出Machine Learning,24分钟训练ImageNet……

    主播 | 吴璇 选题 | 徐普 程炜 Heo 本周关键词 反向传播|Machine Learning Agents ImageNet|信息瓶颈 NO/1 Ge...

    AI研习社
  • 如何在 GPU 上加速数据科学

    数据科学家需要算力。无论您是用 pandas 处理一个大数据集,还是用 Numpy 在一个大矩阵上运行一些计算,您都需要一台强大的机器,以便在合理的时间内完成这...

    AI研习社
  • Java内存模型

    Java内存模型(简称JMM)指定了JVM如何利用计算机内存(RAM)进行工作。JMM与整个计算机的模型类似,这个模型自然也包含内存模型,即Java内存模型(A...

    码代码的陈同学
  • 美情报机构投2800万美元研发高级机器学习算法

    据美国哈佛大学网站2016年1月26日报道,哈佛大学工程与应用科学学院(SEAS)、脑科学中心(CBS)、分子和细胞生物系共同获得美国高级情报研究计划局(IAR...

    人工智能快报
  • 谷歌的无人驾驶汽车业务最终将走入“死胡同”?

    谷歌无人驾驶汽车项目主管克里斯-乌尔木森(Chris Urmson)日前发表预计称,未来二到五年之内谷歌无人驾驶汽车将会正式上路运行。此前谷歌曾经放出豪言称公司...

    机器人网
  • keras系列︱Sequential与Model模型、keras基本结构功能(一)

    不得不说,这深度学习框架更新太快了尤其到了Keras2.0版本,快到Keras中文版好多都是错的,快到官方文档也有旧的没更新,前路坑太多。 到发文为止...

    素质
  • 无人驾驶汽车被追尾,人类表示怪我咯!?

    镁客网
  • 千呼万唤始出来,谷歌无人驾驶汽车或将量产

    镁客网
  • Java技术专题之JVM你的内存泄露了吗?

      关于JVM的内存泄露,让我们从下面一个例子开始吧,大家来判断一下如果按照下面这种分配方式,会不会出现内存泄露呢?

    数据饕餮

扫码关注云+社区

领取腾讯云代金券