После прочтения ответа на этот вопрос я немного запутался, когда именно TensorFlow инициализирует переменные веса и смещения. Согласно ответам, Compile определяет функцию потерь, оптимизатор и метрики. Вот и все.
Поскольку метод compile()
не инициализирует его, можно предположить, что это происходит во время выполнения метода fit()
.
Однако проблема в том, что в случае загрузки моделей или загрузки весов, как fit()
узнать, что представленные веса действительно полезны и не должны быть выброшены, а затем назначены случайные значения вместо них.
Мы передаем тип инициализатора в аргументе kernel_initializer
при объявлении слоя. Например:
dense02 = tf.keras.layers.Dense(units=10,
kernel_initializer='glorot_uniform',
bias_initializer='zeros')
Таким образом, возникает очевидный вопрос: инициализируются ли веса слой за слоем во время прямого прохода первой эпохи или это происходит для всех слоев до первой эпохи.
(Я пытаюсь сказать, что если в модели имеется 5 плотных слоев, то происходит ли инициализация, скажем, слоя за раз, т.е. первый плотный слой инициализируется, затем для этого слоя происходит прямой проход, затем второй слой инициализируется, и происходит прямой проход для второго плотного слоя и так далее)
Другой аспект касается переноса обучения: при наложении пользовательских слоев поверх обученной модели слои обученной модели имеют веса, в то время как добавленные мной слои не будут иметь полезных слоев. Итак, откуда TensorFlow знать, что нужно только инициализировать переменные слоев, которые я добавил, а не испортить слои переданной модели (при условии, что у меня их нет trainable=False
)
Как TensorFlow или Keras справляются с инициализацией веса?
Веса инициализируются при создании модели (когда инициализируется каждый слой в модели), то есть перед compile()
и fit()
:
import tensorflow as tf
from tensorflow.keras import models, layers
inputs = layers.Input((3, ))
outputs = layers.Dense(units=10,
kernel_initializer='glorot_uniform',
bias_initializer='zeros')(inputs)
model = models.Model(inputs=inputs, outputs=outputs)
for layer in model.layers:
print("Config:\n{}\nWeights:\n{}\n".format(layer.get_config(), layer.get_weights()))
Выходы:
Config:
{'batch_input_shape': (None, 3), 'dtype': 'float32', 'sparse': False, 'ragged': False, 'name': 'input_1'}
Weights:
[]
Config:
{'name': 'dense', 'trainable': True, 'dtype': 'float32', 'units': 10, 'activation': 'linear', 'use_bias': True, 'kernel_initializer': {'class_name': 'GlorotUniform', 'config': {'seed': None}}, 'bias_initializer': {'class_name': 'Zeros', 'config': {}}, 'kernel_regularizer': None, 'bias_regularizer': None, 'activity_regularizer': None, 'kernel_constraint': None, 'bias_constraint': None}
Weights:
[array([[-0.60352975, 0.08275259, -0.6521113 , -0.5860774 , -0.42276743,
-0.3142944 , -0.28118378, 0.07770532, -0.5644444 , -0.47069687],
[ 0.4611913 , 0.35170448, -0.62191975, 0.5837332 , -0.3390234 ,
-0.4033073 , 0.03493106, -0.06078851, -0.53159714, 0.49872506],
[ 0.43685734, 0.6160207 , 0.01610583, -0.3673877 , -0.14144647,
-0.3792309 , 0.05478126, 0.602067 , -0.47438127, 0.36463356]],
dtype=float32), array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)]
Это в основном то же самое, за исключением того, что веса загрузки базовой модели из файла после инициализации модели или загрузки всей модели из файла
Проведя небольшое исследование, несмотря на то, что Ответ Mr. For Example верен, давайте немного углубимся в то, как работает инициализация в TensorFlow Keras.
Согласно tf.keras.layers.Layer Doc, мы можем создавать переменные следующими двумя способами:
__init__(self, ...)
: определяет пользовательские атрибуты слоя и создает переменную состояния слоя, которая не зависит от входных форм, с помощью add_weight()
build(self, input_shape)
: этот метод можно использовать для создания весов, которые зависят от формы (форм) входных данных, используя add_weight()
кбВ приведенном ниже коде показан пример базового слоя с двумя переменными, которые выполняют вычисления: y = w . x + b
:
class SimpleDense(Layer):
def __init__(self, units=32):
super(SimpleDense, self).__init__()
self.units = units
def build(self, input_shape): # Create the state of the layer (weights)
w_init = tf.random_normal_initializer()
self.w = tf.Variable(
initial_value=w_init(shape=(input_shape[-1], self.units),
dtype='float32'),
trainable=True)
b_init = tf.zeros_initializer()
self.b = tf.Variable(
initial_value=b_init(shape=(self.units,), dtype='float32'),
trainable=True)
def call(self, inputs): # Defines the computation from inputs to outputs
return tf.matmul(inputs, self.w) + self.b
# Instantiates the layer.
linear_layer = SimpleDense(4)
# This will also call `build(input_shape)` and create the weights.
y = linear_layer(tf.ones((2, 2)))
assert len(linear_layer.weights) == 2
# These weights are trainable, so they're listed in `trainable_weights`:
assert len(linear_layer.trainable_weights) == 2
Самое интересное, что следует отметить в приведенном выше коде, это когда вызывается метод сборки.
build()
вызывается, когда слою (после его инициализации) присваивается какой-то ввод, будь то фактические значения или просто заполнитель TensorFlow.
При использовании модели Keras Sequential мы добавляем слой в модель, он автоматически назначает входной заполнитель слою и одновременно инициализирует его.
Таким образом, мы видим веса перед вызовом compile()
или fit()
методов модели Кераса. (Обратите внимание, что __call__()
автоматически создаст слой (если он еще не создан), вызвав build()
)
Что касается трансферного обучения, когда мы загружаем переданную модель, мы загружаем уже построенные слои, поэтому метод сборки не вызывается снова, когда вы добавляете слои в свою собственную модель.
Другими словами, слоям переданной модели уже был назначен входной заполнитель, и метод build()
уже вызывался при обучении переданной модели.
Полезные ссылки:
Как TF обрабатывает веса переноса обучения и веса слоев, наложенных поверх него?