喝醉的烤面包 · “好串”求解算法优化原理与Python实现- ...· 3 月前 · |
欢乐的黄豆 · React Native 之读取JSON ...· 9 月前 · |
讲道义的匕首 · 如何在FastAPI Swagger ...· 1 年前 · |
飘逸的煎饼果子 · Data Templating ...· 1 年前 · |
闯红灯的马克杯 · Tabs 标签页 (组件) - ...· 1 年前 · |
AI研习社按:本文作者 Jason Brownlee 为澳大利亚知名机器学习专家、教育者,对时间序列预测尤有心得。原文发布于其博客。AI研习社崔静闯、朱婷编译。
神经网络算法利用了随机性,比如初始化随机权重,因此用同样的数据训练同一个网络会得到不同的结果。
初学者可能会有些懵圈,因为算法表现得不太稳定。但实际上它们就是这么设计的。随机初始化可以让网络通过学习,得到一个所学函数的很好的近似。
然而, 有时候用同样的数据训练同一个网络,你需要每次都得到完全相同的结果。例如在教学和产品上。
在这个教程中,你会学到怎样设置随机数生成器,才能每次用同样的数据训练同一网络时,都能得到同样的结果。
我们开始。
这个教程分为六部分:
如果在 Python 环境的设置方面需要帮助,请看下面这个帖子:
How to Setup a Python Environment for Machine Learning and Deep Learning with Anaconda
我发现这对神经网络和深度学习的初学者而言是个常见问题。
这种误解可能出于以下问题:
神经网络特意用随机性来保证,能通过有效学习得到问题的近似函数。采用随机性的原因是:用它的机器学习算法,要比不用它的效果更好。
在神经网络中,最常见的利用随机性的方式是网络权值的随机初始化,尽管在其他地方也能利用随机性,这有一个简短的清单:
这些甚至更多的随机性来源意味着,当你对同一数据运行同一个神经网络算法时,注定得到不同的结果。
想了解更多关于随机算法的原委,参考下面的帖子
Embrace Randomness in Machine Learning
我们可以用一个小例子来演示神经网络的随机性.
在这一节中,我们会建立一个多层感知器模型来学习一个以 0.1 为间隔的从 0.0 到 0.9 的短序列。给出 0.0,模型必须预测出 0.1;给出 0.1,模型必须预测出 0.2;以此类推。
下面是准备数据的代码
# 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 MLPfriendly format values = 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 后端下怎样做到这点。
通常,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
你的结果应该跟我的差不多(忽略微小的精度差异)。
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 文档,尽力减少可能引入随机性的第三方库。
以上所有示例都假设代码是在一个 CPU 上运行的。
这种情况也是有可能的,就是当使用 GPU 训练模型时,可能后端设置的是使用一套复杂的 GPU 库,这些库中有些可能会引入他们自己的随机源,你可能会或者不会考虑到这个。
例如,有证据显示如果你在堆栈中使用了 Nvidia cuDNN,这可能引入额外的随机源( introduce additional sources of randomness),并且使结果不能准确再现。
由于模型的复杂性和训练的并行性,你可能会得到不可复现的结果。
这很可能是由后端库的效率造成的,或者是不能在内核中使用随机数序列。我自己没有遇到过这个,但是在一些 GitHub 问题和 StackOverflowde 问题中看到了一些案例。
如果只是缩小成因的范围的话,你可以尝试降低模型的复杂度,看这样是否影响结果的再现。
我建议您阅读一下你的后端是怎么使用随机性的,并看一下是否有任何选项向你开放。在 Theano 中,参考:
在 TensorFlow 中,参考:
Constants, Sequences, and Random Values
tf.set_random_see
另外,为了更深入地了解,考虑一下寻找拥有同样问题的其他人。一些很好的搜寻平台包括 GitHub、StackOverflow 和 CrossValidated。
在本教程中,你了解了如何在 Keras 上得到神经网络模型的可重复结果。特别是,你学习到了:
via. machine learning mastery, 崔静闯、朱婷编译
本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!