Я обнаружил, что вычисленные градиенты зависят от взаимодействия декораторов tf.function следующим образом.
Сначала я создаю некоторые синтетические c данные для двоичной классификации
tf.random.set_seed(42)
np.random.seed(42)
x=tf.random.normal((2,1))
y=tf.constant(np.random.choice([0,1],2))
Затем я определяю две функции потерь, которые различаются только декоратором tf.function
weights=tf.constant([1.,.1])[tf.newaxis,...]
def customloss1(y_true,y_pred,sample_weight=None):
y_true_one_hot=tf.one_hot(tf.cast(y_true,tf.uint8),2)
y_true_scale=tf.multiply(weights,y_true_one_hot)
return tf.reduce_mean(tf.keras.losses.categorical_crossentropy(y_true_scale,y_pred))
@tf.function
def customloss2(y_true,y_pred,sample_weight=None):
y_true_one_hot=tf.one_hot(tf.cast(y_true,tf.uint8),2)
y_true_scale=tf.multiply(weights,y_true_one_hot)
return tf.reduce_mean(tf.keras.losses.categorical_crossentropy(y_true_scale,y_pred))
Затем я создаю очень простую модель регрессии logisti c с удалением всех наворотов для простоты
tf.random.set_seed(42)
np.random.seed(42)
model=tf.keras.Sequential([
tf.keras.layers.Dense(2,use_bias=False,activation='softmax',input_shape=[1,])
])
и, наконец, определите две функции для вычисления градиентов вышеупомянутых функций потерь, одна из которых украшена tf.function, а другая не украшена им.
def get_gradients1(x,y):
with tf.GradientTape() as tape1:
p1=model(x)
l1=customloss1(y,p1)
with tf.GradientTape() as tape2:
p2=model(x)
l2=customloss2(y,p2)
gradients1=tape1.gradient(l1,model.trainable_variables)
gradients2=tape2.gradient(l2,model.trainable_variables)
return gradients1, gradients2
@tf.function
def get_gradients2(x,y):
with tf.GradientTape() as tape1:
p1=model(x)
l1=customloss1(y,p1)
with tf.GradientTape() as tape2:
p2=model(x)
l2=customloss2(y,p2)
gradients1=tape1.gradient(l1,model.trainable_variables)
gradients2=tape2.gradient(l2,model.trainable_variables)
return gradients1, gradients2
Теперь, когда я запускаю
get_gradients1(x,y)
Я получаю
([<tf.Tensor: shape=(1, 2), dtype=float32, numpy=array([[ 0.11473544, -0.11473544]], dtype=float32)>],
[<tf.Tensor: shape=(1, 2), dtype=float32, numpy=array([[ 0.11473544, -0.11473544]], dtype=float32)>])
, и градиенты такие же, как и ожидалось. Однако, когда я запускаю
get_gradients2(x,y)
, я получаю
([<tf.Tensor: shape=(1, 2), dtype=float32, numpy=array([[ 0.02213785, -0.5065186 ]], dtype=float32)>],
[<tf.Tensor: shape=(1, 2), dtype=float32, numpy=array([[ 0.11473544, -0.11473544]], dtype=float32)>])
, где правильный только второй ответ. Таким образом, когда моя внешняя функция оформлена, я получаю правильный ответ только от внутренней функции, которая также оформлена. У меня сложилось впечатление, что достаточно украсить внешний (который является тренировочным l oop во многих приложениях), но здесь мы видим его нет. Я хочу понять, почему, а также насколько глубоко нужно go, чтобы украсить используемые функции?
Добавлена некоторая отладочная информация
Я добавил некоторую отладочную информацию и показываю код только для customloss2 (другой идентичен)
@tf.function
def customloss2(y_true,y_pred,sample_weight=None):
y_true_one_hot=tf.one_hot(tf.cast(y_true,tf.uint8),2)
y_true_scale=tf.multiply(weights,y_true_one_hot)
tf.print('customloss2',type(y_true_scale),type(y_pred))
tf.print('y_true_scale','\n',y_true_scale)
tf.print('y_pred','\n',y_pred)
return tf.reduce_mean(tf.keras.losses.categorical_crossentropy(y_true_scale,y_pred))
и при запуске get_gradients1 я получаю
customloss1 <type 'EagerTensor'> <type 'EagerTensor'>
y_true_scale
[[1 0]
[0 0.1]]
y_pred
[[0.510775387 0.489224613]
[0.529191136 0.470808864]]
customloss2 <class 'tensorflow.python.framework.ops.Tensor'> <class 'tensorflow.python.framework.ops.Tensor'>
y_true_scale
[[1 0]
[0 0.1]]
y_pred
[[0.510775387 0.489224613]
[0.529191136 0.470808864]]
, мы видим, что тензоры для customloss1 Eager, но для customloss2 - Tensor, и все же мы получаем то же значение для градиентов.
С другой стороны, когда я запускаю его на get_gradients2
customloss1 <class 'tensorflow.python.framework.ops.Tensor'> <class 'tensorflow.python.framework.ops.Tensor'>
y_true_scale
[[1 0]
[0 0.1]]
y_pred
[[0.510775387 0.489224613]
[0.529191136 0.470808864]]
customloss2 <class 'tensorflow.python.framework.ops.Tensor'> <class 'tensorflow.python.framework.ops.Tensor'>
y_true_scale
[[1 0]
[0 0.1]]
y_pred
[[0.510775387 0.489224613]
[0.529191136 0.470808864]]
, мы видим, что все идентично, без тензоров, которые стремятся, и все же я получаю разные градиенты!