Как получить стабильные результаты с TensorFlow, установив случайное начальное число
Я пытаюсь запустить нейронную сеть несколько раз с разными параметрами, чтобы откалибровать параметры сети (вероятности отсева, скорость обучения). Однако у меня проблема в том, что работа сети при сохранении неизменных параметров все равно дает мне другое решение, когда я запускаю сеть в цикле следующим образом:
filename = create_results_file()
for i in range(3):
g = tf.Graph()
with g.as_default():
accuracy_result, average_error = network.train_network(
parameters, inputHeight, inputWidth, inputChannels, outputClasses)
f, w = get_csv_writer(filename)
w.writerow([accuracy_result, "did run %d" % i, average_error])
f.close()
Я использую следующий код в начале моей функции train_network перед настройкой уровней и функции ошибок в моей сети:
np.random.seed(1)
tf.set_random_seed(1)
Я также пытался добавить этот код до создания графа TensorFlow, но я продолжаю получать разные решения в выводе результатов.
Я использую AdamOptimizer и инициализирую вес сети, используя tf.truncated_normal
, Кроме того, я использую np.random.permutation
перетасовать входящие изображения для каждой эпохи.
8 ответов
Установка текущего случайного начального числа TensorFlow влияет только на текущий график по умолчанию. Так как вы создаете новый график для вашего обучения и устанавливаете его по умолчанию (with g.as_default():
), вы должны установить случайное семя в рамках этого with
блок.
Например, ваш цикл должен выглядеть следующим образом:
for i in range(3):
g = tf.Graph()
with g.as_default():
tf.set_random_seed(1)
accuracy_result, average_error = network.train_network(
parameters, inputHeight, inputWidth, inputChannels, outputClasses)
Обратите внимание, что это будет использовать одно и то же случайное начальное число для каждой итерации внешнего for
петля. Если вы хотите использовать разные - но все еще детерминированные - начальные числа в каждой итерации, вы можете использовать tf.set_random_seed(i + 1)
,
Настройка бэкэнда:
cuda:10.1
,
cudnn: 7
,
tensorflow-gpu: 2.1.0
,
keras: 2.2.4-tf
, и
vgg19
индивидуальная модель
Изучив проблему нестабильных результатов для бэкэнда тензорного потока с обучением графического процессора и большими моделями нейронных сетей на основе keras, я наконец смог получить воспроизводимые (стабильные) результаты следующим образом:
- Импортируйте только те библиотеки, которые потребуются для установки начального числа и инициализации начального значения.
import tensorflow as tf
import os
import numpy as np
import random
SEED = 0
- Функция инициализации начального числа для всех библиотек, которые могут иметь стохастическое поведение
def set_seeds(seed=SEED):
os.environ['PYTHONHASHSEED'] = str(seed)
random.seed(seed)
tf.random.set_seed(seed)
np.random.seed(seed)
- Активировать
Tensorflow
детерминированное поведение
def set_global_determinism(seed=SEED):
set_seeds(seed=seed)
os.environ['TF_DETERMINISTIC_OPS'] = '1'
os.environ['TF_CUDNN_DETERMINISTIC'] = '1'
tf.config.threading.set_inter_op_parallelism_threads(1)
tf.config.threading.set_intra_op_parallelism_threads(1)
# Call the above function with seed value
set_global_determinism(seed=SEED)
Важные заметки:
- Пожалуйста, вызовите приведенный выше код перед выполнением любого другого кода
- Обучение модели может стать медленнее, поскольку код детерминирован, поэтому есть компромисс.
- Я экспериментировал несколько раз с разным количеством эпох и разными настройками (включая model.fit () с shuffle = True), и приведенный выше код дает мне воспроизводимые результаты.
Рекомендации:
- https://suneeta-mall.github.io/2019/12/22/Reproducible-ml-tensorflow.html
- https://keras.io/getting_started/faq/#how-can-i-obtain-reproducible-results-using-keras-during-development
- https://www.tensorflow.org/api_docs/python/tf/config/threading/set_inter_op_parallelism_threads
- https://www.tensorflow.org/api_docs/python/tf/random/set_seed?version=nightly
Детерминированное поведение может быть получено либо путем предоставления начального уровня на уровне графов, либо на уровне операций. Оба работали на меня. Начальное число на уровне графа может быть размещено с помощью tf.set_random_seed. Начальное число уровня операции может быть помещено, например, в переменный инициализатор, как в:
myvar = tf.Variable(tf.truncated_normal(((10,10)), stddev=0.1, seed=0))
Ответ, совместимый с Tensorflow 2.0: для версии Tensorflow выше 2.0, если мы хотим установить Global Random Seed, используется командаtf.random.set_seed
.
Если мы переходим с Tensorflow Version 1.x to 2.x
, мы можем использовать команду,
tf.compat.v2.random.set_seed
.
Обратите внимание, что tf.function
в этом случае действует как повторный запуск программы.
Чтобы установить начальное значение уровня операции (как указано выше), мы можем использовать команду, tf.random.uniform([1], seed=1)
.
Для получения дополнительной информации обратитесь к этой странице Tensorflow.
Пожалуйста, добавьте все функции случайного семени перед вашим кодом:
tf.reset_default_graph()
tf.random.set_seed(0)
random.seed(0)
np.random.seed(0)
Я думаю, что некоторые модели в TensorFlow используют numpy или случайную функцию python.
Кажется, что ни один из этих ответов не будет работать из-за основных проблем реализации в CuDNN.
Вы можете получить немного больше детерминизма, добавив дополнительный флаг
os.environ['PYTHONHASHSEED']=str(SEED)
os.environ['TF_CUDNN_DETERMINISTIC'] = '1' # new flag present in tf 2.0+
random.seed(SEED)
np.random.seed(SEED)
tf.set_random_seed(SEED)
Но это все равно не будет полностью детерминированным. Для того, чтобы получить еще более точное решение, необходимо использовать процедуру, описанную в этом NVidia репо.
Я использую TensorFlow 2 (2.2.0) и запускаю код в JupyterLab. Я тестировал это в macOS Catalina и в Google Colab с теми же результатами. Я добавлю кое-что в ответ службы поддержки Tensorflow.
Когда я тренируюсь с использованием метода model.fit(), я делаю это в ячейке. Я занимаюсь другими делами в других камерах. Это код, который я запускаю в указанной ячейке:
# To have same results always place this on top of the cell
tf.random.set_seed(1)
(x_train, y_train), (x_test, y_test) = load_data_onehot_grayscale()
model = get_mlp_model_compiled() # Creates the model, compiles it and returns it
history = model.fit(x=x_train, y=y_train,
epochs=30,
callbacks=get_mlp_model_callbacks(),
validation_split=.1,
)
Вот что я понимаю:
- В TensorFlow есть несколько случайных процессов, которые происходят на разных этапах (инициализация, перемешивание, ...), каждый раз, когда эти процессы происходят, TensorFlow использует случайную функцию. Когда вы устанавливаете семя, используя
tf.random.set_seed(1)
вы заставляете эти процессы использовать его, и если начальное значение установлено, а процессы не меняются, результаты будут такими же. - Теперь, в приведенном выше коде, если я изменю
tf.random.set_seed(1)
идти ниже линииmodel = get_mlp_model_compiled()
мои результаты меняются, я думаю, это потому, чтоget_mlp_model_compiled()
использует случайность и не использует нужное мне семя. - Предостережение по поводу пункта 2: если я запускаю ячейку 3 раза подряд, я получаю те же результаты. Я считаю, что это происходит потому, что в прогоне №1
get_mlp_model_compiled()
не использует внутренний счетчик TensorFlow с моим семенем. В прогоне №2 он будет использовать семя, и во всех последующих прогонах он будет также использовать семя, поэтому после прогона №2 результаты будут такими же.
У меня может быть какая-то информация неверна, поэтому не стесняйтесь поправлять меня.
Чтобы понять, что происходит, вам следует прочитать документы, они не такие длинные и их легко понять.
Этот ответ является дополнением к ответу Люка и для TF v2.2.0
import numpy as np
import os
import random
import tensorflow as tf # 2.2.0
SEED = 42
os.environ['PYTHONHASHSEED']=str(SEED)
os.environ['TF_CUDNN_DETERMINISTIC'] = '1' # TF 2.1+
random.seed(SEED)
np.random.seed(SEED)
tf.random.set_seed(SEED)