Поскольку TF2 ориентирован на активное выполнение, концепция сеанса ушла, и все, что выполняется, превратилось в конкретные функции. Но концепция традиционного замороженного графа все еще может найти свои варианты использования, особенно для разработки и отладки.
Допустим, у вас есть очень простая модель, построенная на основе Keras.
keras = tf.keras class MyCustomLayer(keras.layers.Layer): def __init__(self): super(MyCustomLayer, self).__init__(self) self._weight = tf.Variable(initial_value=(2., 3.)) def call(self, input): output = tf.sigmoid(input) * self._weight return output model = keras.models.Sequential( [keras.layers.Input((1,2)), MyCustomLayer()])
В TF1.x вы можете получить сеанс, а затем график через
tf.keras.backend.get_session()
В TF2 вы не можете получить глобальный сеанс, но у вас есть доступ к конкретной функции.
model = tf.keras.Sequential(...) func = tf.function(model).get_concrete_function( tf.TensorSpec(model.inputs[0].shape, model.inputs[0].dtype))
С конкретной функцией вы уже можете получить определение графика через
func.graph.as_graph_def()
Однако он полон переменных и ReadVariableOp. Нам нужно преобразовать их в постоянные, потому что при выводе модели не нужно обновлять веса.
Это можно сделать с помощью волшебной функции convert_variables_to_constants_v2_as_graph в TF2.2. Раньше он назывался convert_variables_to_constants_v2 в TF2.1 IIRC.
frozen_func, graph_def = convert_variables_to_constants_v2_as_graph(func)
Но этого мало. Нам нужно больше оптимизации графа, чтобы получить чистый граф вывода. Давайте вызовем грэпплера.
input_tensors = [ tensor for tensor in frozen_func.inputs if tensor.dtype != tf.resource ] output_tensors = frozen_func.outputs graph_def = run_graph_optimizations( graph_def, input_tensors, output_tensors, config=get_grappler_config(["constfold", "function"]), graph=frozen_func.graph)
Хорошая работа! У вас должен быть чистый график вывода.
Вот полный код: