【问题标题】:How to write a conditonal regressional loss function based on binary cross entropy loss for Keras model如何为 Keras 模型编写基于二元交叉熵损失的条件回归损失函数
【发布时间】:2021-09-12 21:53:37
【问题描述】:

我正在构建一个人脸关键点检测系统。目标是将人脸图像输入模型,然后模型检测图像中的解剖标志(眼睛、鼻子)并输出可见标志的像素坐标。每个地标有三个目标:x、y、可见。 X 和 Y 是像素坐标,可见地标是否在图像中。该计划是首先在预测可见性和真实可见性之间进行二元交叉熵损失。然后,第二个损失是 x,y 坐标和目标之间的回归损失(我使用的是 MAPE)。然而,回归损失只会计算可见的地标。损失看起来像:

#Pseudo-code
def loss(y_true,y_pred):
    if y_true[2] == 1
       #Probability that landmark is in image
       #Compute binary cross entropy loss
       #Compute MAPE regression loss
       Total_loss = Binary_loss + MAPE_loss
       return Total_loss

    else:
       Total_loss = Binary loss
       return Total_loss

一旦编写了损失函数,我将如何在代码中实现它?我知道如何为每个问题创建模型(检查坐标,并分别检查可见性),但我不确定如何将两个头与条件损失函数结合起来。我将如何组合图层(每个头部的 Conv、Flatten、Dense)以获得所需的输出?谢谢!

编辑: 我无法上传数据,但这是它的图像。前 9 列是地标的坐标和可见性。最后一列是已展平的相应图像。 当我加载数据进行训练时,我会执行以下步骤:

###Read in data file
file = "Directory/file.csv"
train_data = pd.read_csv(file)
###Convert each coordinate column to type float64
train_data['xreye'] = train_data['xreye'].astype(np.float64)
...
###Convert image column to string type
train_data['Image'] = train_data['Image'].astype(str)

#Image is feature, other values are labels to predict later
#Image column values are strings, also some missing values, have to split
##string by space and append it and handle missing values
imag = []
for i in range(len(train_data)):
    img = train_data['Image'][i].split(' ')
    img = ['0' if x == '' else x for x in img]      
    imag.append(img) 
#Reshape and convert to float value
image_list = np.array(imag,dtype = 'uint8')
X_train = image_list.reshape(-1,256,256,1)

####Get pixel coordinates and visibility targets
training = train_data[['xreye','yreye','reyev','xleye','yleye','leyev','xtsept','ytsept','tseptv']]
y_train = []
for i in range(len(train_data)):
    y = training.iloc[i,:]
    y_train.append(y)

y_train = np.array(y_train, dtype='float')

编辑:模型代码、损失函数和拟合方法。

###Loss function
visuals_mask = [False, False, True] * 3
def loss_func(y_true, y_pred):
    visuals_true = tf.boolean_mask(y_true, visuals_mask, axis=1)
    visuals_pred = tf.boolean_mask(y_pred, visuals_mask, axis=1)
    visuals_loss = tf.keras.losses.BinaryCrossentropy(visuals_true, visuals_pred)
    visuals_loss = tf.reduce_mean(visuals_loss)

    coords_true = tf.boolean_mask(y_true, ~np.array(visuals_mask), axis=1)
    coords_pred = tf.boolean_mask(y_pred, ~np.array(visuals_mask), axis=1)
    coords_loss = tf.keras.losses.MeanAbsolutePercentageError(coords_true, coords_pred)
    coords_loss = tf.reduce_mean(coords_loss)

    return coords_loss + visuals_loss
####Model code
model = Sequential()

model.add(Conv2D(32, (3,3), activation='relu', padding='same', use_bias=False, input_shape=(256,256,1)))
model.add(BatchNormalization())
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Conv2D(64, (3,3), activation='relu', padding='same', use_bias=False))
model.add(BatchNormalization())
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Conv2D(128, (3,3), activation='relu', padding='same', use_bias=False))
model.add(BatchNormalization())
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Flatten())

model.add(Dense(128, activation='relu'))
model.add(Dense(64, activation='relu'))
model.add(Dense(32, activation='relu'))
model.add(Dense(16, activation='relu'))
model.add(Dense(8, activation='relu'))
model.add(Dense(4, activation='relu'))
model.add(Dense(9, activation='linear'))
model.summary()
model.compile(optimizer='adam', loss=loss_func)

###Model fit
checkpointer = ModelCheckpoint('C:/Users/Cloud/.spyder-py3/x_y_shift/weights/vis_coords_TEST.hdf5', monitor='val_loss', verbose=1, mode = 'min', save_best_only=True)
out = model.fit(X_train,y_train,epochs=5,batch_size=4,validation_split=0.1, verbose=1, callbacks=[checkpointer])

【问题讨论】:

  • 您能分享一下您的输入和输出数据的形状吗?这是单输出模型还是多输出模型?
  • 当然,输入是形​​状 (256,256,1) 的图像。输出将是(righteyeX,righteyeY,righteyeVis,lefteyeX,lefteyeY,lefteyeVis,noseX,noseY,noseVis),每个图像输入的输出形状(1,8)。所以我想这将是一个多输出模型。谢谢!

标签: python keras deep-learning conv-neural-network loss-function


【解决方案1】:

我不能确定,因为我没有数据来重现问题,但这些是我脑海中的步骤:

  1. 使用 boolean masking 从输出中获取 2、5 和 8. 索引:
visuals_mask_ = [False, False, True] * 3

# in the loss function
visuals_true = tf.boolean_mask(y_true, visuals_mask_, axis=-1) # do the same with preds
  1. 计算视觉损失
visuals_loss = binary_crossentropy(visuals_true, visuals_pred) # use sparse if that's the case
  1. 获取坐标的输出,就像我们为视觉效果所做的那样,但使​​用反转的visuals_mask。我相信tf.boolean_mask(y_true, tf.math.logical_not(visuals_mask_, axis=-1)) 应该可以工作。
  2. 计算其余部分的 MAPE(coords_truecoords_pred
  3. 通过tf.reduce_mean 获取两种损失的方法
  4. 获取损失总和并返回

我希望这些能提供一些见解。

编辑: 我尝试了以下方法,似乎可以正常工作:

y_true = tf.convert_to_tensor(np.random.rand(32, 9))
y_pred = tf.convert_to_tensor(np.random.rand(32, 9))

visuals_mask = [False, False, True] * 3

def loss_func(y_true, y_pred):
    visuals_true = tf.boolean_mask(y_true, visuals_mask, axis=1)
    visuals_pred = tf.boolean_mask(y_pred, visuals_mask, axis=1)
    visuals_loss = binary_crossentropy(visuals_true, visuals_pred)
    visuals_loss = tf.reduce_mean(visuals_loss)

    coords_true = tf.boolean_mask(y_true, ~np.array(visuals_mask), axis=1)
    coords_pred = tf.boolean_mask(y_pred, ~np.array(visuals_mask), axis=1)
    coords_loss = mean_absolute_percentage_error(coords_true, coords_pred)
    coords_loss = tf.reduce_mean(coords_loss)

    return coords_loss + visuals_loss

loss_func(y_true, y_pred)

我在这里假设的是:

  • 您的输出实际上长度为 9 ((batch_size, 9))。
  • 由于eager execution,此演示和实际训练中的自定义损失计算可能有所不同。

编辑 2: 我用这种模型试过了,它似乎有效:

model = Sequential()

model.add(Conv2D(4, 10, data_format='channels_last', input_shape=(256, 256, 1)))
model.add(Flatten())
model.add(Dense(9, activation='sigmoid'))

model.compile('adam', loss=loss_func)

【讨论】:

  • 您好,我尝试使用此损失函数和简单模型,但最终收到此错误:无法将 类型的对象转换为张量.我猜这是因为我没有用张量正确地塑造输入。对于 y_train,我假设我只会做 y_train = tf.convert_to_tensor(y_train),但我会为 y_pred 做什么?我不知道在训练过程中如何输入。
  • 我认为你的数据是正确的。你能显示你的代码吗?模型、fit 方法和损失函数。顺便说一句,您不需要在训练期间在损失函数中使用conver_to_tensor,因为它们已经是张量。试试我写的损失函数吧。
  • 您好,我已添加您要求的代码。我已经尝试了我的模型和您测试的简单模型,但无济于事。我仍然得到每个相同的错误。我也尝试将批量大小从 4 更改为 1,但这也不起作用。我也在使用 Python 3.8,不确定这是否会影响某些代码,但我只是想把它扔掉。再次感谢您的所有帮助!我真的很感激!
  • 我注意到我在损失函数中使用了 BinaryCrossentropy()。但是,当我将它切换到 binary_crossentropy() 时,它导致了这个错误:TypeError: Expected float32 passed to parameter 'y' of op 'Equal',得到了 'auto' of type 'str'。错误:预期的 float32,取而代之的是 'str' 类型的 'auto'。网上看了一下,推荐在model.compile()中使用loss_func()而不是loss_func。但是,当我尝试它说: TypeError: loss_func() missing 2 required positional arguments: 'y_true' 和 'y_pred'。
  • 您好,我刚刚复制了您的模型和损失函数代码并执行了这些操作,并且成功了: 1. 我删除了回调 2. 将损失函数中的行更改为 visuals_loss = binary_crossentropy(visuals_true, visuals_pred)coords_loss = mean_absolute_percentage_error(coords_true, coords_pred) 和它马上就开始训练了。
猜你喜欢
  • 2020-09-30
  • 2021-11-25
  • 2017-03-14
  • 2022-11-18
  • 2021-02-28
  • 2017-12-08
  • 2021-08-25
  • 2018-09-12
  • 2017-06-26
相关资源
最近更新 更多