Эти модели эквивалентны? - PullRequest
       26

Эти модели эквивалентны?

4 голосов
/ 03 октября 2019

Основной вопрос: Я определяю одну и ту же модель двумя разными способами. Почему я получаю разные результаты? Они кажутся одной и той же моделью.

Дополнительный вопрос (ответ приведен ниже). Если я снова запускаю код, я снова получаю другие результаты. Я установил семя в начале, чтобы исправить случайность. Почему это происходит?

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 Ответ

2 голосов
/ 03 октября 2019

Проблема коренится в ожидаемом против фактического поведении определения модели и случайности. Чтобы увидеть, что происходит, мы должны понять, как работает «ГСЧ»:

  • «Генератор случайных чисел» (ГСЧ) на самом деле является функцией, которая генерирует числа так, что они отображаются на распределение вероятностей 'вдолгосрочный период '
  • Когда вызывается функция ГСЧ, например, 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

Это даст довольно хорошую воспроизводимость, но не идеально, если вы используете GPU - из-за параллелизма операций; это видео объясняет это хорошо. Для еще лучшей воспроизводимости установите PYHTONHASHSEED - эту и другую информацию в официальном Keras FAQ .

«Идеальная» воспроизводимость довольно избыточна, так как ваши результаты должны согласовываться в течение 0,1% времени - но если вам это действительно нужно, вероятно, единственный способ в настоящее время - это переключиться на ЦП и прекратить использование CUDA -но это сильно замедлит обучение (в x10 +).


Источники случайности :

  • Весовые инициализации (каждый инициализатор Keras по умолчанию использует случайность)
  • Слои шума (Dropout, GaussianNoise и т. Д.)
  • Хеширование для операций на основе хеша, например, порядок элементов в наборе или в формате
  • 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")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...