tensorflow笔记之二十八——带掩码的损失函数

一个应用场景

在多标签问题中,有时候需要一些特殊的处理,比如一些样本只对部分class进行更新,而另一些样本对另外一些class进行更新。

举个例子,我们预测一个用户喜欢什么类型的电影,假如我们知道用户对哪些电影有过观看行为,这是正样本,负样本一般来说可以随机采样构造,构造也有两种方式,一个是固定用户随机采样电影,一个是固定电影随机采样用户,两种方式其实是等价的,不过考虑到随机采样用户可以覆盖到完全没有观影记录的用户,我们倾向于用后者(看电影的用户和不看电影的用户可能本身就有很大的不同了,如果train domain局限在看电影的用户,infer的时候如果遇到一个不看电影的用户可能就搞不定了)。一个用户可以喜欢多款电影,也可以一个都不喜欢,所以这个应该是一个多标签问题。

假如有5类电影,用户A观看过第2类,那么这个用户的label可以表示为[0,1,0,0,0],对这条正样本构造一个负样本的话,用户随机采样为用户B,负样本label应该是什么?可以是 [0,0,0,0,0]还是[1,0,1,1,1]吗?都不行。因为我们只知道用户A看过第2类,但并不能说用户A对其他类型就不喜欢,同理随机负采样的用户B,我们可以认为对第2类给标记为0,但其他类是0还是1其实也不好说。

常用的方式就是对于这条样本,只更新第2类相关的参数,其余的类别不更新。实现这个目的的方式就是在计算loss的时候进行掩码,比如上面例子中这条样本的掩码就应该是[0,1,0,0,0],掩码中1对应的位置计算loss,0对应的位置不算。

代码示例

下面以sigmoid(二分类/多标签)交叉熵损失为例,看两种loss掩码的方式,一种是tensorflow自带weights的损失函数来实现掩码,一种是我们自己写的掩码损失函数,二者完全等价。注意,tf.nn.sigmoid_cross_entropy_with_logits的结果loss维度跟输入的label和pred是一样的,而我们通常在日志里打印出来的loss=xxxx其实是经过聚合(reduction)之后的,聚合的方式有很多种,这里我们用的reduction是SUM_OVER_NONZERO_WEIGHTS,也就是对非0的loss元素求平均。

import tensorflow as tf
import numpy as np

label = tf.constant([[1.0,1.0,0.0,0.0,0.0],[0.0,1.0,1.0,1.0,0.0],[0.0,0.0,0.0,0.0,1.0],[1.0,1.0,1.0,1.0,1.0]])
pred = tf.constant([[0.2,0.2,0.2,0,0.8],[0,0.5,0.5,0.2,0],[0,0.5,0.1,0.0,0.9],[0.9,0.9,0.7,0.1,0.0]])
mask = tf.constant([[1,0,0,0,0],[0,0,1,1,0],[0,0,1,0,1],[0,1,1,1,1]])

def loss1(a, b, weights):
	# tensorflow自带,内部也是tf.nn.sigmoid_cross_entropy_with_logits,实现了带weights的loss reduction。
    loss = tf.compat.v1.losses.sigmoid_cross_entropy(
                    multi_class_labels=a,
                    logits=b,
                    weights=weights,
                    reduction=tf.compat.v1.losses.Reduction.SUM_OVER_NONZERO_WEIGHTS)
    return loss

def loss2(a, b, weights):
    # 计算loss,维度跟labels一致
    cross_entropy = tf.nn.sigmoid_cross_entropy_with_logits(
                    labels=a, logits=b)
    # mask中1对应的位置矩阵
    mask_idx = tf.where(weights > 0)
    # 根据mask进行聚合,聚合方式是对非0的loss元素求平均,跟SUM_OVER_NONZERO_WEIGHTS一样
    loss = tf.cond(tf.equal(tf.size(mask_idx), 0),
                   lambda: tf.constant(0.0),
                   lambda: tf.reduce_mean(tf.gather_nd(cross_entropy, mask_idx)))
    return loss

with tf.Session() as sess:
    result1 = sess.run(loss1(label,pred,mask))
    print(result1)
    result2 = sess.run(loss2(label,pred,mask))
    print(result2)

输出结果可以看到二者是一样的:

0.53753215
0.5375321
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
由于CIOU损失函数不是TensorFlow自损失函数,需要自定义实现。以下是在Mask RCNN中使用CIOU损失函数的代码: ```python import tensorflow as tf import numpy as np def compute_ciou(box1, box2): """ 计算CIOU """ def cal_area(box): """ 计算框的面积 """ return (box[..., 2] - box[..., 0]) * (box[..., 3] - box[..., 1]) # 计算两个框的面积 box1_area = cal_area(box1) box2_area = cal_area(box2) # 计算相交矩形的坐标 lt = tf.maximum(box1[..., :2], box2[..., :2]) rb = tf.minimum(box1[..., 2:], box2[..., 2:]) # 计算相交矩形的面积 wh = tf.maximum(0.0, rb - lt) inter_area = wh[..., 0] * wh[..., 1] # 计算并集矩形的面积 union_area = box1_area + box2_area - inter_area # 计算CIOU iou = inter_area / union_area v = tf.pow((1 - iou), 2) alpha = v / (1 - iou + v) center_distance = tf.reduce_sum(tf.square((box1[..., :2] + box1[..., 2:]) / 2 - (box2[..., :2] + box2[..., 2:]) / 2), axis=-1) diagonal_distance = tf.reduce_sum(tf.square(box1[..., 2:] - box1[..., :2]) + tf.square(box2[..., 2:] - box2[..., :2]), axis=-1) ciou = iou - (center_distance + alpha * diagonal_distance) / union_area return ciou def ciou_loss(y_true, y_pred): """ 计算CIOU损失函数 """ box_true, class_true, mask_true = y_true box_pred, class_pred, mask_pred = y_pred # 计算CIOU ciou = compute_ciou(box_true, box_pred) # 计算类别损失 class_loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(labels=class_true, logits=class_pred)) # 计算损失 mask_loss = tf.reduce_mean(tf.boolean_mask(tf.nn.sigmoid_cross_entropy_with_logits(labels=mask_true, logits=mask_pred), mask_true)) # 计算总损失 loss = class_loss + ciou + mask_loss return loss # 在Mask RCNN中使用CIOU损失函数 model.compile(optimizer=optimizer, loss=[ciou_loss, rpn_class_loss, rpn_bbox_loss, mrcnn_class_loss, mrcnn_bbox_loss, mrcnn_mask_loss]) ``` 在上面的代码中,`compute_ciou`函数用于计算CIOU,`ciou_loss`函数用于计算CIOU损失函数,`model.compile`函数中将CIOU损失函数作为第一个损失函数。由于Mask RCNN的损失函数需要分为多个部分,因此在`ciou_loss`函数中,还需要计算类别损失损失,并将三者相加得到总损失
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值