Если результаты Keras не воспроизводятся, как лучше всего сравнивать модели и выбирать гиперпараметры?
ОБНОВЛЕНИЕ: этот вопрос был для Tensorflow 1.x. Я обновился до 2.0, и (по крайней мере, в простом коде ниже) проблема воспроизводимости кажется исправленной в 2.0. Так что это решает мою проблему; но мне все еще любопытно, какие "лучшие практики" использовались для решения этой проблемы в 1.x.
Обучение одной и той же модели / параметров / данных на keras/tenorflow не дает воспроизводимых результатов, и потери значительно отличаются каждый раз, когда вы тренируете модель. По этому поводу есть много вопросов о stackru (например, как получить воспроизводимые результаты в keras), но рекомендуемые обходные пути, похоже, не работают для меня или многих других людей в Stackru. Хорошо, это то, что есть.
Но с учетом этого ограничения невоспроизводимости с keras на тензорном потоке - что лучше всего для сравнения моделей и выбора гиперпараметров? Я тестирую разные архитектуры и активации, но поскольку оценка потерь каждый раз разная, я никогда не уверен, что одна модель лучше другой. Есть ли лучший способ справиться с этим?
Я не думаю, что проблема связана с моим кодом, но на всякий случай это помогает; вот пример программы:
import os
#stackru says turning off the GPU helps reproducibility, but it doesn't help for me
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"] = ""
os.environ['PYTHONHASHSEED']=str(1)
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers
import random
import pandas as pd
import numpy as np
#Stackru says this is needed for reproducibility but it doesn't help for me
from tensorflow.keras import backend as K
config = tf.ConfigProto(intra_op_parallelism_threads=1,inter_op_parallelism_threads=1)
sess = tf.Session(graph=tf.get_default_graph(), config=config)
K.set_session(sess)
#make some random data
NUM_ROWS = 1000
NUM_FEATURES = 10
random_data = np.random.normal(size=(NUM_ROWS, NUM_FEATURES))
df = pd.DataFrame(data=random_data, columns=['x_' + str(ii) for ii in range(NUM_FEATURES)])
y = df.sum(axis=1) + np.random.normal(size=(NUM_ROWS))
def run(x, y):
#Stackru says you have to set the seeds but it doesn't help for me
tf.set_random_seed(1)
np.random.seed(1)
random.seed(1)
os.environ['PYTHONHASHSEED']=str(1)
model = keras.Sequential([
keras.layers.Dense(40, input_dim=df.shape[1], activation='relu'),
keras.layers.Dense(20, activation='relu'),
keras.layers.Dense(10, activation='relu'),
keras.layers.Dense(1, activation='linear')
])
NUM_EPOCHS = 500
model.compile(optimizer='adam', loss='mean_squared_error')
model.fit(x, y, epochs=NUM_EPOCHS, verbose=0)
predictions = model.predict(x).flatten()
loss = model.evaluate(x, y) #This prints out the loss by side-effect
#Each time we run it gives a wildly different loss. :-(
run(df, y)
run(df, y)
run(df, y)
Учитывая невоспроизводимость, как я могу оценить, помогают ли изменения в моих гиперпараметрах и архитектуре?
3 ответа
Проблема вроде бы решена в Tensorflow 2.0 (по крайней мере, на простых моделях)! Вот фрагмент кода, который, кажется, дает повторяемые результаты.
import os
####*IMPORANT*: Have to do this line *before* importing tensorflow
os.environ['PYTHONHASHSEED']=str(1)
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers
import random
import pandas as pd
import numpy as np
def reset_random_seeds():
os.environ['PYTHONHASHSEED']=str(1)
tf.random.set_seed(1)
np.random.seed(1)
random.seed(1)
#make some random data
reset_random_seeds()
NUM_ROWS = 1000
NUM_FEATURES = 10
random_data = np.random.normal(size=(NUM_ROWS, NUM_FEATURES))
df = pd.DataFrame(data=random_data, columns=['x_' + str(ii) for ii in range(NUM_FEATURES)])
y = df.sum(axis=1) + np.random.normal(size=(NUM_ROWS))
def run(x, y):
reset_random_seeds()
model = keras.Sequential([
keras.layers.Dense(40, input_dim=df.shape[1], activation='relu'),
keras.layers.Dense(20, activation='relu'),
keras.layers.Dense(10, activation='relu'),
keras.layers.Dense(1, activation='linear')
])
NUM_EPOCHS = 500
model.compile(optimizer='adam', loss='mean_squared_error')
model.fit(x, y, epochs=NUM_EPOCHS, verbose=0)
predictions = model.predict(x).flatten()
loss = model.evaluate(x, y) #This prints out the loss by side-effect
#With Tensorflow 2.0 this is now reproducible!
run(df, y)
run(df, y)
run(df, y)
Это хитро, но в вашем коде на самом деле нет шага для лучшей воспроизводимости: сброса графиков Keras и TensorFlow перед каждым запуском. Без этогоtf.set_random_seed()
не будет работать должным образом - см. правильный подход ниже.
Я бы исчерпал все варианты, прежде чем бросить полотенце на невоспроизводимость; в настоящее время мне известен только один такой случай, и, скорее всего, это ошибка. Тем не менее, возможно, вы получите заметно различающиеся результаты, даже если выполните все шаги - в этом случае см. "Если ничего не работает", но каждый из них явно не очень продуктивен, поэтому лучше всего сосредоточиться на достижении воспроизводимости:
Окончательные улучшения:
- Использовать
reset_seeds(K)
ниже - Увеличьте числовую точность:
K.set_floatx('float64')
- Установлен
PYTHONHASHSEED
перед запуском ядра Python - например, с терминала - Обновление до TF 2, которое включает в себя некоторые исправления ошибок воспроизводимости, но с производительностью
- Запустить процессор в одном потоке (мучительно медленно)
- Вы не импортировать из
tf.python.keras
- см. здесь - Убедитесь, что весь импорт согласован (т. Е. Не
from keras.layers import ...
а такжеfrom tensorflow.keras.optimizers import ...
) - Используйте превосходный процессор - например, Google Colab, даже при использовании графического процессора, намного более устойчив к неточности чисел - см. Этот SO
Также см. Соответствующий SO по воспроизводимости
Если ничего не работает:
- Повторить X раз с точно такими же гиперпараметрами и семенами, средние результаты
- K-Fold Cross-Validation с точно такими же гиперпараметрами и начальными значениями, средние результаты - лучший вариант, но требуется больше работы
Правильный метод сброса:
def reset_seeds(reset_graph_with_backend=None):
if reset_graph_with_backend is not None:
K = reset_graph_with_backend
K.clear_session()
tf.compat.v1.reset_default_graph()
print("KERAS AND TENSORFLOW GRAPHS RESET") # optional
np.random.seed(1)
random.seed(2)
tf.compat.v1.set_random_seed(3)
print("RANDOM SEEDS RESET") # optional
Запуск TF на одном потоке ЦП: (код только для TF1)
session_conf = tf.ConfigProto(
intra_op_parallelism_threads=1,
inter_op_parallelism_threads=1)
sess = tf.Session(config=session_conf)
Положив только код ниже, он работает. КЛЮЧ в вопросе, ОЧЕНЬ ВАЖНО, - вызывать функцию reset_seeds() каждый раз перед запуском модели. Поступая так, вы получите воспроизводимые результаты, как я проверял в Google Collab.
import numpy as np
import tensorflow as tf
import random as python_random
def reset_seeds():
np.random.seed(123)
python_random.seed(123)
tf.random.set_seed(1234)
reset_seeds()
У вас есть пара вариантов для стабилизации производительности...
1) Установите начальное число для ваших инициализаторов, чтобы они всегда инициализировались одинаковыми значениями.
2) Больше данных обычно приводит к более стабильной сходимости.
3) Более низкая скорость обучения и большие размеры пакетов также хороши для более предсказуемого обучения.
4) Обучение на основе фиксированного количества эпох вместо использования обратных вызовов для изменения гиперпарам во время обучения.
5) K-кратная проверка для обучения на разных подмножествах. Среднее значение этих складок должно привести к довольно предсказуемой метрике.
6) Также у вас есть возможность просто тренироваться несколько раз и брать среднее значение.