Как создать ансамбль в тензор потоке?
Я пытаюсь создать ансамбль из многих обученных моделей. Все модели имеют одинаковый график и отличаются только по весу. Я создаю граф модели, используя tf.get_variable
, У меня есть несколько разных контрольных точек (с разными весами) для одной и той же графовой архитектуры, и я хочу сделать одну модель экземпляра для каждой контрольной точки.
Как я могу загрузить много контрольных точек без перезаписи предыдущих загруженных весов?
Как я создал свои графики с tf.get_variable
Единственный способ, которым я могу создать несколько графиков, это передать аргумент reuse = True
, Теперь, если я попытаюсь изменить имена переменных моего графа, включающих метод сборки, в новую область видимости (чтобы они стали недоступными для других созданных графиков) перед загрузкой, то это не сработает, поскольку новые имена будут отличаться от сохраненных веса и я не смогу его загрузить.
1 ответ
Это требует нескольких взломов. Давайте сохраним несколько простых моделей
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import argparse
import tensorflow as tf
def build_graph(init_val=0.0):
x = tf.placeholder(tf.float32)
w = tf.get_variable('w', initializer=init_val)
y = x + w
return x, y
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--init', help='dummy string', type=float)
parser.add_argument('--path', help='dummy string', type=str)
args = parser.parse_args()
x1, y1 = build_graph(args.init)
saver = tf.train.Saver()
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(y1, {x1: 10})) # outputs: 10 + i
save_path = saver.save(sess, args.path)
print("Model saved in path: %s" % save_path)
# python ensemble.py --init 1 --path ./models/model1.chpt
# python ensemble.py --init 2 --path ./models/model2.chpt
# python ensemble.py --init 3 --path ./models/model3.chpt
Эти модели выводят значения "10 + i", где i=1, 2, 3. Обратите внимание, что этот скрипт создает, запускает и сохраняет несколько раз одну и ту же граф-структуру. Загрузка этих значений и восстановление каждого графика в отдельности фольклорная и может быть сделана
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import argparse
import tensorflow as tf
def build_graph(init_val=0.0):
x = tf.placeholder(tf.float32)
w = tf.get_variable('w', initializer=init_val)
y = x + w
return x, y
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--path', help='dummy string', type=str)
args = parser.parse_args()
x1, y1 = build_graph(-5.)
saver = tf.train.Saver()
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
saver.restore(sess, args.path)
print("Model loaded from path: %s" % args.path)
print(sess.run(y1, {x1: 10}))
# python ensemble_load.py --path ./models/model1.chpt # gives 11
# python ensemble_load.py --path ./models/model2.chpt # gives 12
# python ensemble_load.py --path ./models/model3.chpt # gives 13
Они производят снова результаты 11,12,13, как ожидалось. Теперь хитрость заключается в том, чтобы создать для каждой модели из ансамбля свою собственную область видимости, например
def build_graph(x, init_val=0.0):
w = tf.get_variable('w', initializer=init_val)
y = x + w
return x, y
if __name__ == '__main__':
models = ['./models/model1.chpt', './models/model2.chpt', './models/model3.chpt']
x = tf.placeholder(tf.float32)
outputs = []
for k, path in enumerate(models):
# THE VARIABLE SCOPE IS IMPORTANT
with tf.variable_scope('model_%03i' % (k + 1)):
outputs.append(build_graph(x, -100 * np.random.rand())[1])
Следовательно, каждая модель живет под своей переменной- областью, т.е. у нас есть переменные "model_001/w:0, model_002/w:0, model_003/w:0", хотя они имеют похожий (не одинаковый) подграф, эти переменные действительно являются разными объектами. Теперь уловка состоит в том, чтобы управлять двумя наборами переменных (те из графика в текущей области и те из контрольной точки):
def restore_collection(path, scopename, sess):
# retrieve all variables under scope
variables = {v.name: v for v in tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scopename)}
# retrieves all variables in checkpoint
for var_name, _ in tf.contrib.framework.list_variables(path):
# get the value of the variable
var_value = tf.contrib.framework.load_variable(path, var_name)
# construct expected variablename under new scope
target_var_name = '%s/%s:0' % (scopename, var_name)
# reference to variable-tensor
target_variable = variables[target_var_name]
# assign old value from checkpoint to new variable
sess.run(target_variable.assign(var_value))
Полное решение будет
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import numpy as np
import tensorflow as tf
def restore_collection(path, scopename, sess):
# retrieve all variables under scope
variables = {v.name: v for v in tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scopename)}
# retrieves all variables in checkpoint
for var_name, _ in tf.contrib.framework.list_variables(path):
# get the value of the variable
var_value = tf.contrib.framework.load_variable(path, var_name)
# construct expected variablename under new scope
target_var_name = '%s/%s:0' % (scopename, var_name)
# reference to variable-tensor
target_variable = variables[target_var_name]
# assign old value from checkpoint to new variable
sess.run(target_variable.assign(var_value))
def build_graph(x, init_val=0.0):
w = tf.get_variable('w', initializer=init_val)
y = x + w
return x, y
if __name__ == '__main__':
models = ['./models/model1.chpt', './models/model2.chpt', './models/model3.chpt']
x = tf.placeholder(tf.float32)
outputs = []
for k, path in enumerate(models):
with tf.variable_scope('model_%03i' % (k + 1)):
outputs.append(build_graph(x, -100 * np.random.rand())[1])
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(outputs[0], {x: 10})) # random output -82.4929
print(sess.run(outputs[1], {x: 10})) # random output -63.65792
print(sess.run(outputs[2], {x: 10})) # random output -19.888203
print(sess.run(W[0])) # randomly initialize value -92.4929
print(sess.run(W[1])) # randomly initialize value -73.65792
print(sess.run(W[2])) # randomly initialize value -29.888203
restore_collection(models[0], 'model_001', sess) # restore all variables from different checkpoints
restore_collection(models[1], 'model_002', sess) # restore all variables from different checkpoints
restore_collection(models[2], 'model_003', sess) # restore all variables from different checkpoints
print(sess.run(W[0])) # old values from different checkpoints: 1.0
print(sess.run(W[1])) # old values from different checkpoints: 2.0
print(sess.run(W[2])) # old values from different checkpoints: 3.0
print(sess.run(outputs[0], {x: 10})) # what we expect: 11.0
print(sess.run(outputs[1], {x: 10})) # what we expect: 12.0
print(sess.run(outputs[2], {x: 10})) # what we expect: 13.0
# python ensemble_load_all.py
Теперь, имея список выходных данных, вы можете усреднить эти значения в TensorFlow или сделать некоторые другие ансамблевые прогнозы.
редактировать:
- Намного проще сохранить модель в виде пустого словаря с помощью NumPy (npz) и загрузить эти значения, как в моем ответе здесь: /questions/30918007/tensorflow-kak-initsializirovat-vse-globalnyie-peremennyie-iz-numpy/30918017#30918017
- Код выше просто иллюстрирует решение. Он не поддерживает проверку работоспособности (как, если переменная действительно существует). Попробуй поймать может помочь.
По этой теме есть несколько вопросов и множество возможных ответов / способов сделать это. Здесь я хочу показать, как я пришел к самому элегантному и чистому способу создания ансамбля изN
модели, где N
произвольно. Это решение было протестировано с tf 1.12.0, python 2.7
Следующий фрагмент кода - это то, что вы ищете (комментарии ниже):
import tensorflow as tf
import numpy as np
num_of_ensembles = N
savers = list()
palceholders = list()
inference_ops = list()
for i in xrange(num_of_ensembles):
with tf.name_scope('model_{}'.format(i)):
savers.append(tf.train.import_meta_graph('saved_model.ckpt.meta'))
graph = tf.get_default_graph()
for i in xrange(num_of_ensembles):
placeholders.append(graph.get_operation_by_name('model_{}/input_ph'.format(i)).outputs[0])
inference_ops.append(graph.get_operation_by_name('model_{}/last_operation_in_the_network'.format(i)).outputs[0])
with tf.Session() as sess:
for i in xrange(num_of_ensembles):
savers[i].restore(sess, 'saved_model.ckpt')
prediction = sess.run(inference_ops[i], feed_dict={placeholders[i]: np.random.rand(your_input.shape)})
Итак, первое, что нужно сделать, - это импортировать мета-график каждой модели. Как было предложено в комментариях выше, ключ состоит в том, чтобы создать для каждой модели из ансамбля свою собственную область видимости, чтобы добавить префикс типа model_001/, model_002/ ... к каждой области действия переменной. Это позволит вам восстановитьN
разные модели со своими независимыми переменными.
Все эти графики будут жить в текущем графике по умолчанию. Теперь, когда вы загружаете модель, вам нужно извлечь входы, выходы и операции, которые вы хотите использовать из графика, в новые переменные. Для этого вам нужно знать имена этих тензоров из старой модели. Вы можете проверить все сохраненные операции с помощью команды:ops = graph.get_operations()
. В приведенном выше примере первая операция - это присвоение заполнителя /input_ph, в то время как последняя операция называлась / last_operation_in_the_network (обычно, если автор сети не указывает полеname
для каждого слоя вы найдете что-то вроде /density_3, /conv2d_1 и т. д.). Обратите внимание, что это должна быть точная последняя операция вашей модели, а также вы должны указать тензор, который является значением.outputs[0]
самой операции.
Наконец, вы можете запустить сеанс с правильной операцией вывода и заполнителем, получить прогноз в виде массива numpy и делать все, что захотите (усреднение, голосование большинством и т. Д.)
Полезные ссылки, которые вы можете проверить: