Эквивалентны ли эти модели?
Главный вопрос: я определяю одну и ту же модель двумя разными способами. Почему я получаю разные результаты? Кажется, это одна и та же модель.
Дополнительный вопрос (ответ ниже). Если я снова запущу код, я снова получу другие результаты. Я установил семя в начале, чтобы исправить случайность. Почему так происходит?
import numpy as np
np.random.seed(1)
from keras.models import Model, Sequential
from keras.layers import Input, Dense
model1= Sequential([
Dense(20, activation='sigmoid',kernel_initializer='glorot_normal',
input_shape=(2,)),
Dense(2, activation='linear', kernel_initializer='glorot_normal'),
])
model1.compile(optimizer='adam', loss='mean_squared_error')
ipt = Input(shape=(2,))
x = Dense(20, activation='sigmoid', kernel_initializer='glorot_normal')(ipt)
out = Dense(2, activation='linear', kernel_initializer='glorot_normal')(x)
model2 = Model(ipt, out)
model2.compile(optimizer='adam', loss='mean_squared_error')
x_train=np.array([[1,2],[3,4],[3,4]])
model1.fit(x_train, x_train,epochs=2, validation_split=0.1, shuffle=False)
model2.fit(x_train, x_train,epochs=2, validation_split=0.1, shuffle=False)
В первый раз вывод:
2/2 [==============================] - 0s 68ms/step - loss: 14.4394 - val_loss: 21.5747
Epoch 2/2
2/2 [==============================] - 0s 502us/step - loss: 14.3199 - val_loss: 21.4163
Train on 2 samples, validate on 1 samples
Epoch 1/2
2/2 [==============================] - 0s 72ms/step - loss: 11.0523 - val_loss: 17.7059
Epoch 2/2
2/2 [==============================] - 0s 491us/step - loss: 10.9833 - val_loss: 17.5785
Во второй раз вывод:
2/2 [==============================] - 0s 80ms/step - loss: 14.4394 - val_loss: 21.5747
Epoch 2/2
2/2 [==============================] - 0s 501us/step - loss: 14.3199 - val_loss: 21.4163
Train on 2 samples, validate on 1 samples
Epoch 1/2
2/2 [==============================] - 0s 72ms/step - loss: 11.0523 - val_loss: 17.6733
Epoch 2/2
2/2 [==============================] - 0s 485us/step - loss: 10.9597 - val_loss: 17.5459
Обновите после прочтения ответа: ответом ниже на один из моих вопросов был дан ответ. Я изменил начало своего кода на:
import numpy as np
np.random.seed(1)
import random
random.seed(2)
import tensorflow as tf
tf.set_random_seed(3)
И теперь я получаю те же цифры, что и раньше. Так что стабильно. Но мой главный вопрос остался без ответа. Почему каждый раз две эквивалентные модели дают разные результаты?
Вот результат, который я получаю каждый раз:
результаты 1:
Epoch 1/2
2/2 [==============================] - 0s 66ms/sample - loss: 11.9794 - val_loss: 18.9925
Epoch 2/2
2/2 [==============================] - 0s 268us/sample - loss: 11.8813 - val_loss: 18.8572
результаты 2:
Epoch 1/2
2/2 [==============================] - 0s 67ms/sample - loss: 5.4743 - val_loss: 9.3471
Epoch 2/2
2/2 [==============================] - 0s 3ms/sample - loss: 5.4108 - val_loss: 9.2497
1 ответ
Проблема коренится в ожидаемом и фактическом поведении определения модели и случайности. Чтобы увидеть, что происходит, мы должны понять, как работает "ГСЧ":
- "Генератор случайных чисел" (ГСЧ) на самом деле является функцией, которая производит числа, которые отображаются на распределение вероятностей "в долгосрочной перспективе".
- Когда функция ГСЧ, например
RNG()
вызывается, он возвращает "случайное" значение и увеличивает свой внутренний счетчик на 1. Назовите этот счетчикn
- тогда:random_value = RNG(n)
- Когда вы устанавливаете SEED, вы устанавливаете
n
в соответствии со значением этого семени (но не для этого семени); мы можем представить эту разницу через+ c
в стойке c
будет константой, созданной нелинейной, но детерминированной функцией начального числа:f(seed)
import numpy as np
np.random.seed(4) # internal counter = 0 + c
print(np.random.random()) # internal counter = 1 + c
print(np.random.random()) # internal counter = 2 + c
print(np.random.random()) # internal counter = 3 + c
np.random.seed(4) # internal counter = 0 + c
print(np.random.random()) # internal counter = 1 + c
print(np.random.random()) # internal counter = 2 + c
print(np.random.random()) # internal counter = 3 + c
0.9670298390136767
0.5472322491757223
0.9726843599648843
0.9670298390136767
0.5472322491757223
0.9726843599648843
Предположим model1
имеет 100 весов, и вы устанавливаете семя (n = 0 + c
). Послеmodel1
построен, ваш счетчик на 100 + c
. Если вы не сбросите семя, даже если вы построитеmodel2
с одним и тем же кодом модели будут отличаться - посколькуmodel2
веса инициализируются на n
из 100 + c
к 200 + c
.
Дополнительная информация:
Есть три семени, чтобы гарантировать лучшую случайность:
import numpy as np
np.random.seed(1) # for Numpy ops
import random
random.seed(2) # for Python ops
import tensorflow as tf
tf.set_random_seed(3) # for tensorfow ops - e.g. Dropout masks
Это даст неплохую воспроизводимость, но не идеальную, если вы используете графический процессор - из-за параллелизма операций; это видео хорошо объясняет. Для еще лучшей воспроизводимости настройтеPYHTONHASHSEED
- эту и другую информацию можно найти в официальном FAQ по Keras.
"Идеальная" воспроизводимость довольно избыточна, так как ваши результаты должны совпадать в пределах 0,1% в большинстве случаев - но если вам это действительно нужно, вероятно, единственный способ в настоящее время - переключиться на ЦП и прекратить использование CUDA - но это замедлит обучение колоссально (на х10+).
Источники случайности:
- Инициализация веса (каждый инициализатор Keras по умолчанию использует случайность)
- Слои шума (Dropout, GaussianNoise и т. Д.)
- Хеширование для операций на основе хэша, например, порядок элементов в наборе или dict
- Параллелизм GPU (см. Связанное видео)
Демонстрация случайности модели:
import numpy as np
np.random.seed(4)
model1_init_weights = [np.random.random(), np.random.random(), np.random.random()]
model2_init_weights = [np.random.random(), np.random.random(), np.random.random()]
print("model1_init_weights:", model1_init_weights)
print("model2_init_weights:", model2_init_weights)
model1_init_weights: [0.9670298390136767, 0.5472322491757223, 0.9726843599648843]
model2_init_weights: [0.7148159936743647, 0.6977288245972708, 0.21608949558037638]
Перезагрузите ядро. Теперь запустите это:
import numpy as np
np.random.seed(4)
model2_init_weights = [np.random.random(), np.random.random(), np.random.random()]
model1_init_weights = [np.random.random(), np.random.random(), np.random.random()]
print("model1_init_weights:", model1_init_weights)
print("model2_init_weights:", model2_init_weights)
model1_init_weights: [0.7148159936743647, 0.6977288245972708, 0.21608949558037638]
model2_init_weights: [0.9670298390136767, 0.5472322491757223, 0.9726843599648843]
Таким образом, переворачивая порядок model1
а также model2
в вашем коде также переворачивает убытки. Это связано с тем, что начальное значение не сбрасывается между определениями двух моделей, поэтому ваши инициализации веса совершенно разные.
Если вы хотите, чтобы они были одинаковыми, сбросьте начальное значение перед определением КАЖДОЙ МОДЕЛИ и перед УСТАНОВКОЙ каждой модели - и используйте удобную функцию, как показано ниже. Но лучше всего перезапустить ядро и работать отдельно.py
файлы.
def reset_seeds():
np.random.seed(1)
random.seed(2)
tf.set_random_seed(3)
print("RANDOM SEEDS RESET")