在有条件的keras中实现自定义损失函数
Implementing custom loss function in keras with condition
我需要一些关于 keras 损失函数的帮助。我一直在使用 Tensorflow 后端在 keras 上实现自定义损失函数。
我已经在numpy中实现了自定义损失函数,但如果能将其转化为keras损失函数就更好了。损失函数采用数据框和一系列用户 ID。如果 user_id 不同,则相同 user_id 的欧氏距离为正和负。函数 returns 总结了数据帧的标量距离。
def custom_loss_numpy (encodings, user_id):
# user_id: a pandas series of users
# encodings: a pandas dataframe of encodings
batch_dist = 0
for i in range(len(user_id)):
first_row = encodings.iloc[i,:].values
first_user = user_id[i]
for j in range(i+1, len(user_id)):
second_user = user_id[j]
second_row = encodings.iloc[j,:].values
# compute distance: if the users are same then Euclidean distance is positive otherwise negative.
if first_user == second_user:
tmp_dist = np.linalg.norm(first_row - second_row)
else:
tmp_dist = -np.linalg.norm(first_row - second_row)
batch_dist += tmp_dist
return batch_dist
我尝试在keras损失函数中实现。我从 y_true 和 y_pred 张量对象中提取了 numpy 数组。
def custom_loss_keras(y_true, y_pred):
# session of my program
sess = tf_session.TF_Session().get()
with sess.as_default():
array_pred = y_pred.eval()
print(array_pred)
但是我收到以下错误。
tensorflow.python.framework.errors_impl.InvalidArgumentError: You must feed a value for placeholder tensor 'dense_1_input' with dtype float and shape [?,102]
[[Node: dense_1_input = Placeholder[dtype=DT_FLOAT, shape=[?,102], _device="/job:localhost/replica:0/task:0/device:CPU:0"]()]]
非常感谢任何形式的帮助。
在 Keras 中实现参数化自定义损失函数有两个步骤。首先,为coefficient/metric写一个方法。其次,编写一个包装函数来按照 Keras 需要的方式格式化事物。
对于像DICE这样简单的自定义损失函数,直接使用Keras后端而不是tensorflow实际上会更干净一些。这是以这种方式实现的系数示例:
import keras.backend as K
def dice_coef(y_true, y_pred, smooth, thresh):
y_pred = y_pred > thresh
y_true_f = K.flatten(y_true)
y_pred_f = K.flatten(y_pred)
intersection = K.sum(y_true_f * y_pred_f)
return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)
现在是棘手的部分。 Keras 损失函数只能将 (y_true, y_pred)
作为参数。所以我们需要一个单独的函数 returns 另一个函数:
def dice_loss(smooth, thresh):
def dice(y_true, y_pred)
return -dice_coef(y_true, y_pred, smooth, thresh)
return dice
终于可以在Keras中如下使用了compile
:
# build model
model = my_model()
# get the loss function
model_dice = dice_loss(smooth=1e-5, thresh=0.5)
# compile model
model.compile(loss=model_dice)
首先,在Keras损失函数中是不可能"extract numpy array from y_true
and y_pred
"的。你必须用 Keras 后端函数(或 TF 函数)操作张量来计算损失。
换句话说,最好考虑一种"vectorized"计算损失的方法,而不使用if-else和循环。
您的损失函数可以按以下步骤计算:
- 在
encodings
. 中的所有向量对之间生成成对欧氏距离矩阵
- 生成一个矩阵
I
,其元素I_ij
如果user_i == user_j
为1,如果user_i != user_j
. 则为-1
- 将两个矩阵逐元素相乘,并将元素相加得到最终的损失。
这是一个实现:
def custom_loss_keras(user_id, encodings):
# calculate pairwise Euclidean distance matrix
pairwise_diff = K.expand_dims(encodings, 0) - K.expand_dims(encodings, 1)
pairwise_squared_distance = K.sum(K.square(pairwise_diff), axis=-1)
# add a small number before taking K.sqrt for numerical safety
# (K.sqrt(0) sometimes becomes nan)
pairwise_distance = K.sqrt(pairwise_squared_distance + K.epsilon())
# this will be a pairwise matrix of True and False, with shape (batch_size, batch_size)
pairwise_equal = K.equal(K.expand_dims(user_id, 0), K.expand_dims(user_id, 1))
# convert True and False to 1 and -1
pos_neg = K.cast(pairwise_equal, K.floatx()) * 2 - 1
# divide by 2 to match the output of `custom_loss_numpy`, but it's not really necessary
return K.sum(pairwise_distance * pos_neg, axis=-1) / 2
我假设 user_id
在上面的代码中是整数。这里的技巧是使用 K.expand_dims
来实现成对操作。乍一看可能有点难以理解,但它非常有用。
它应该给出与custom_loss_numpy
大致相同的损失值(因为K.epsilon()
会有一点不同):
encodings = np.random.rand(32, 10)
user_id = np.random.randint(10, size=32)
print(K.eval(custom_loss_keras(K.variable(user_id), K.variable(encodings))).sum())
-478.4245
print(custom_loss_numpy(pd.DataFrame(encodings), pd.Series(user_id)))
-478.42953553795815
我在损失函数中犯了一个错误。
在训练中使用该函数时,由于Keras自动将y_true
改为至少2D,参数user_id
不再是1D张量。它的形状将是 (batch_size, 1)
.
为了使用这个功能,必须删除多余的轴:
def custom_loss_keras(user_id, encodings):
pairwise_diff = K.expand_dims(encodings, 0) - K.expand_dims(encodings, 1)
pairwise_squared_distance = K.sum(K.square(pairwise_diff), axis=-1)
pairwise_distance = K.sqrt(pairwise_squared_distance + K.epsilon())
user_id = K.squeeze(user_id, axis=1) # remove the axis added by Keras
pairwise_equal = K.equal(K.expand_dims(user_id, 0), K.expand_dims(user_id, 1))
pos_neg = K.cast(pairwise_equal, K.floatx()) * 2 - 1
return K.sum(pairwise_distance * pos_neg, axis=-1) / 2
我需要一些关于 keras 损失函数的帮助。我一直在使用 Tensorflow 后端在 keras 上实现自定义损失函数。
我已经在numpy中实现了自定义损失函数,但如果能将其转化为keras损失函数就更好了。损失函数采用数据框和一系列用户 ID。如果 user_id 不同,则相同 user_id 的欧氏距离为正和负。函数 returns 总结了数据帧的标量距离。
def custom_loss_numpy (encodings, user_id):
# user_id: a pandas series of users
# encodings: a pandas dataframe of encodings
batch_dist = 0
for i in range(len(user_id)):
first_row = encodings.iloc[i,:].values
first_user = user_id[i]
for j in range(i+1, len(user_id)):
second_user = user_id[j]
second_row = encodings.iloc[j,:].values
# compute distance: if the users are same then Euclidean distance is positive otherwise negative.
if first_user == second_user:
tmp_dist = np.linalg.norm(first_row - second_row)
else:
tmp_dist = -np.linalg.norm(first_row - second_row)
batch_dist += tmp_dist
return batch_dist
我尝试在keras损失函数中实现。我从 y_true 和 y_pred 张量对象中提取了 numpy 数组。
def custom_loss_keras(y_true, y_pred):
# session of my program
sess = tf_session.TF_Session().get()
with sess.as_default():
array_pred = y_pred.eval()
print(array_pred)
但是我收到以下错误。
tensorflow.python.framework.errors_impl.InvalidArgumentError: You must feed a value for placeholder tensor 'dense_1_input' with dtype float and shape [?,102]
[[Node: dense_1_input = Placeholder[dtype=DT_FLOAT, shape=[?,102], _device="/job:localhost/replica:0/task:0/device:CPU:0"]()]]
非常感谢任何形式的帮助。
在 Keras 中实现参数化自定义损失函数有两个步骤。首先,为coefficient/metric写一个方法。其次,编写一个包装函数来按照 Keras 需要的方式格式化事物。
对于像DICE这样简单的自定义损失函数,直接使用Keras后端而不是tensorflow实际上会更干净一些。这是以这种方式实现的系数示例:
import keras.backend as K def dice_coef(y_true, y_pred, smooth, thresh): y_pred = y_pred > thresh y_true_f = K.flatten(y_true) y_pred_f = K.flatten(y_pred) intersection = K.sum(y_true_f * y_pred_f) return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)
现在是棘手的部分。 Keras 损失函数只能将
(y_true, y_pred)
作为参数。所以我们需要一个单独的函数 returns 另一个函数:def dice_loss(smooth, thresh): def dice(y_true, y_pred) return -dice_coef(y_true, y_pred, smooth, thresh) return dice
终于可以在Keras中如下使用了compile
:
# build model
model = my_model()
# get the loss function
model_dice = dice_loss(smooth=1e-5, thresh=0.5)
# compile model
model.compile(loss=model_dice)
首先,在Keras损失函数中是不可能"extract numpy array from y_true
and y_pred
"的。你必须用 Keras 后端函数(或 TF 函数)操作张量来计算损失。
换句话说,最好考虑一种"vectorized"计算损失的方法,而不使用if-else和循环。
您的损失函数可以按以下步骤计算:
- 在
encodings
. 中的所有向量对之间生成成对欧氏距离矩阵
- 生成一个矩阵
I
,其元素I_ij
如果user_i == user_j
为1,如果user_i != user_j
. 则为-1
- 将两个矩阵逐元素相乘,并将元素相加得到最终的损失。
这是一个实现:
def custom_loss_keras(user_id, encodings):
# calculate pairwise Euclidean distance matrix
pairwise_diff = K.expand_dims(encodings, 0) - K.expand_dims(encodings, 1)
pairwise_squared_distance = K.sum(K.square(pairwise_diff), axis=-1)
# add a small number before taking K.sqrt for numerical safety
# (K.sqrt(0) sometimes becomes nan)
pairwise_distance = K.sqrt(pairwise_squared_distance + K.epsilon())
# this will be a pairwise matrix of True and False, with shape (batch_size, batch_size)
pairwise_equal = K.equal(K.expand_dims(user_id, 0), K.expand_dims(user_id, 1))
# convert True and False to 1 and -1
pos_neg = K.cast(pairwise_equal, K.floatx()) * 2 - 1
# divide by 2 to match the output of `custom_loss_numpy`, but it's not really necessary
return K.sum(pairwise_distance * pos_neg, axis=-1) / 2
我假设 user_id
在上面的代码中是整数。这里的技巧是使用 K.expand_dims
来实现成对操作。乍一看可能有点难以理解,但它非常有用。
它应该给出与custom_loss_numpy
大致相同的损失值(因为K.epsilon()
会有一点不同):
encodings = np.random.rand(32, 10)
user_id = np.random.randint(10, size=32)
print(K.eval(custom_loss_keras(K.variable(user_id), K.variable(encodings))).sum())
-478.4245
print(custom_loss_numpy(pd.DataFrame(encodings), pd.Series(user_id)))
-478.42953553795815
我在损失函数中犯了一个错误。
在训练中使用该函数时,由于Keras自动将y_true
改为至少2D,参数user_id
不再是1D张量。它的形状将是 (batch_size, 1)
.
为了使用这个功能,必须删除多余的轴:
def custom_loss_keras(user_id, encodings):
pairwise_diff = K.expand_dims(encodings, 0) - K.expand_dims(encodings, 1)
pairwise_squared_distance = K.sum(K.square(pairwise_diff), axis=-1)
pairwise_distance = K.sqrt(pairwise_squared_distance + K.epsilon())
user_id = K.squeeze(user_id, axis=1) # remove the axis added by Keras
pairwise_equal = K.equal(K.expand_dims(user_id, 0), K.expand_dims(user_id, 1))
pos_neg = K.cast(pairwise_equal, K.floatx()) * 2 - 1
return K.sum(pairwise_distance * pos_neg, axis=-1) / 2