TL;博士
结果不同的原因是两个模型的初始权重不同。其中一个表现(显著)优于另一个的事实纯属偶然,正如@today所提到的,他们获得的结果大致相似。
作为
tf.set_random_seed
解释说,随机操作使用两个种子
和
操作特定种子
;
tf.set_随机_种子
依赖随机种子的操作实际上从两个种子派生:图形级种子和操作级种子。这将设置图形级别的种子。
让我们来看看
Dense
我们看到了
the default kernel initializer
是
'glorot_uniform'
(让我们只考虑这里的内核初始化器,但对于偏见初始化器也一样。)进一步浏览源代码,我们最终会发现它获取了
GlorotUniform
使用默认参数。特别是
random number generator seed
操作(即重量初始化)设置为
None
. 现在如果我们检查这个种子在哪里使用,我们会发现它被传递给
random_ops.truncated_normal
二
种子,一个为图形级种子,另一个为操作特定种子:
seed1, seed2 = random_seed.get_seed(seed)
. 我们可以检查
get_seed
op_seed = ops.get_default_graph()._last_id
. 系统的相应部分
tf.set_随机_种子
-
如果设置了图形级种子,但未设置操作种子:系统将确定地选择操作种子和图形级种子,以便获得唯一的随机序列。
现在回到原来的问题,如果
input_shape
定义与否。再看一点源代码,我们发现
Sequential.add
只有
输入波形
被指定;否则,它只存储层列表(
model._layers
); 比较
model.inputs, model.outputs
calling the layers directly
派往
Layer.__call__
. 这个包装器构建层,设置层的输入和输出,并向输出添加一些元数据;它还使用
ops.name_scope
分组操作。我们可以从
Tensorboard
Input -> Dense -> Dropout -> Dense
):
现在我们没有具体说明
输入波形
compile
模型实际上是
not compiled
(仅设置优化器等属性)。相反,当第一次将数据传递到模型时,它是“动态”编译的。这种情况发生在
model._standardize_weights
:模型输出通过以下方式获得:
self.call(dummy_input_values, training=training)
builds the layers
(请注意,模型尚未构建)然后
computes the output incrementally
Layer.call
(不是
__call__
展开这两个图,我们会发现它们包含相同的操作,分组方式不同。然而,这样做的效果是
keras.backend.get_session().graph._last_id
这两种定义不同,因此导致随机操作的种子不同:
# With `input_shape`:
>>> keras.backend.get_session().graph._last_id
303
# Without `input_shape`:
>>> keras.backend.get_session().graph._last_id
7
绩效结果
为了实现类似的随机操作,我对OP的代码进行了一些修改:
-
添加了描述的步骤
here
为确保随机化方面的再现性,
-
设置随机种子
稠密的
和
Dropout
变量初始化,
-
远离的
validation_split
因为拆分发生在“动态”编译模型之前,没有
-
shuffle = False
export PYTHONHASHSEED=0
在运行脚本之前):
from collections import deque
from functools import partial
import math
import random
import sys
import numpy as np
import tensorflow as tf
from tensorflow import keras
seed = int(sys.argv[1])
np.random.seed(1)
tf.set_random_seed(seed)
random.seed(1)
session_conf = tf.ConfigProto(intra_op_parallelism_threads=1,
inter_op_parallelism_threads=1)
sess = tf.Session(graph=tf.get_default_graph(), config=session_conf)
keras.backend.set_session(sess)
def func(x):
return math.sin(x)*5 + math.sin(x*1.8)*4 + math.sin(x/4)*5
def get_data():
x = 0
dx = 0.1
q = deque()
r = 0
data = np.zeros((100000, 1002), np.float32)
while True:
x = x + dx
sig = func(x)
q.append(sig)
if len(q) < 1000:
continue
arr = np.array(q, np.float32)
for k in range(10):
xx = random.uniform(0.1, 9.9)
data[r, :1000] = arr[:1000]
data[r, 1000] = 5*xx #scale for easier fitting
data[r, 1001] = func(x + xx)
r = r + 1
if r >= data.shape[0]:
break
if r >= data.shape[0]:
break
q.popleft()
inputs = data[:, :1001]
outputs = data[:, 1001]
return (inputs, outputs)
Dense = partial(keras.layers.Dense, kernel_initializer=keras.initializers.glorot_uniform(seed=1))
Dropout = partial(keras.layers.Dropout, seed=1)
model = keras.Sequential()
model.add(Dense(64, activation=tf.nn.relu,
# input_shape=(1001,)
))
model.add(Dropout(0.05))
model.add(Dense(64, activation=tf.nn.relu))
model.add(Dropout(0.05))
model.add(Dense(64, activation=tf.nn.relu))
model.add(Dropout(0.05))
model.add(Dense(64, activation=tf.nn.relu))
model.add(Dropout(0.05))
model.add(Dense(1))
model.compile(
loss = 'mse',
optimizer = tf.train.RMSPropOptimizer(0.0005)
)
inputs, outputs = get_data()
shuffled = np.arange(len(inputs))
np.random.shuffle(shuffled)
inputs = inputs[shuffled]
outputs = outputs[shuffled]
hist = model.fit(inputs, outputs[:, None], epochs=10, shuffle=False)
np.save('without.{:d}.loss.npy'.format(seed), hist.history['loss'])
for i in $(seq 1 10)
do
python run.py $i
done
绘制平均损失+/-1标准偏差:
我验证了两个版本的初始权重和初始预测(拟合前)是相同的:
inputs, outputs = get_data()
mode = 'without'
pred = model.predict(inputs)
np.save(f'{mode}.prediction.npy', pred)
for i, layer in enumerate(model.layers):
if isinstance(layer, keras.layers.Dense):
w, b = layer.get_weights()
np.save(f'{mode}.{i:d}.kernel.npy', w)
np.save(f'{mode}.{i:d}.bias.npy', b)
和
for i in 0 2 4 8
do
for data in bias kernel
do
diff -q "with.$i.$data.npy" "without.$i.$data.npy"
done
done
[ ! ]
我在删除所有文件后检查了性能
辍学者
在这种情况下,性能实际上是相等的。因此,关键似乎在于辍学层。实际上,没有退出层的模型的性能与模型的性能相同
具有
退出层,但
没有
指定
输入波形
输入波形
基本上,这两个版本的区别在于
__召唤__
call
输入波形
未指定。这可能是由于
training=False
指定
输入波形
:
未指定
:
switch
核实
training
kwarg让我们来定义子类
辍学者
:
class Dropout(keras.layers.Dropout):
def __init__(self, rate, noise_shape=None, seed=None, **kwargs):
super().__init__(rate, noise_shape=noise_shape, seed=1, **kwargs)
def __call__(self, inputs, *args, **kwargs):
training = kwargs.get('training')
if training is None:
training = keras.backend.learning_phase()
print('[__call__] training: {}'.format(training))
return super().__call__(inputs, *args, **kwargs)
def call(self, inputs, training=None):
if training is None:
training = keras.backend.learning_phase()
print('[call] training: {}'.format(training))
return super().call(inputs, training)
我获得了两个版本的类似输出,但是调用
__召唤__
你什么时候失踪了
未指定:
[__call__] training: Tensor("keras_learning_phase:0", shape=(), dtype=bool)
[call] training: Tensor("keras_learning_phase:0", shape=(), dtype=bool)
[__call__] training: Tensor("keras_learning_phase:0", shape=(), dtype=bool)
[call] training: Tensor("keras_learning_phase:0", shape=(), dtype=bool)
[__call__] training: Tensor("keras_learning_phase:0", shape=(), dtype=bool)
[call] training: Tensor("keras_learning_phase:0", shape=(), dtype=bool)
[__call__] training: Tensor("keras_learning_phase:0", shape=(), dtype=bool)
[call] training: Tensor("keras_learning_phase:0", shape=(), dtype=bool)
所以我怀疑问题出在你的内心深处
__召唤__
系统
我使用的是Ubuntu 16.04、Python 3.6.7和Tensorflow 1.12.0
conda
(不支持GPU):
$ uname -a
Linux MyPC 4.4.0-141-generic #167-Ubuntu SMP Wed Dec 5 10:40:15 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
$ python --version
Python 3.6.7 :: Anaconda, Inc.
$ conda list | grep tensorflow
tensorflow 1.12.0 mkl_py36h69b6ba0_0
tensorflow-base 1.12.0 mkl_py36h3c3e929_0
我也有
keras
和
keras-base
安装(
keras-applications
和
keras-preprocessing
tensorflow
):
$ conda list | grep keras
keras 2.2.4 0
keras-applications 1.0.6 py36_0
keras-base 2.2.4 py36_0
keras-preprocessing 1.0.5 py36_0
在移除所有,
keras*
和
tensorflow*
,然后重新安装
张量流
,差异消失了。即使在重新安装之后
凯拉斯
pip
; 这里也没有差异。现在我再也无法重现这种差异了。一定是tensorflow的装置坏了。