-
Notifications
You must be signed in to change notification settings - Fork 38
/
losses.py
112 lines (99 loc) · 5.14 KB
/
losses.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# -*- coding: utf-8 -*-
"""
File Name: losses
Description : 损失函数层
Author : mick.yi
date: 2019/3/13
"""
import tensorflow as tf
from keras import backend as K
def ctpn_cls_loss(predict_cls_ids, true_cls_ids, indices):
"""
ctpn分类损失
:param predict_cls_ids: 预测的anchors类别,(batch_num,anchors_num,2) fg or bg
:param true_cls_ids:实际的anchors类别,(batch_num,rpn_train_anchors,(class_id,tag))
tag 1:正样本,0:负样本,-1 padding
:param indices: 正负样本索引,(batch_num,rpn_train_anchors,(idx,tag)),
idx:指定anchor索引位置,tag 1:正样本,0:负样本,-1 padding
:return:
"""
# 去除padding
train_indices = tf.where(tf.not_equal(indices[:, :, -1], 0)) # 0为padding
train_anchor_indices = tf.gather_nd(indices[..., 0], train_indices) # 一维(batch*train_num,),每个训练anchor的索引
true_cls_ids = tf.gather_nd(true_cls_ids[..., 0], train_indices) # 一维(batch*train_num,)
# 转为onehot编码
true_cls_ids = tf.where(true_cls_ids >= 1,
tf.ones_like(true_cls_ids, dtype=tf.uint8),
tf.zeros_like(true_cls_ids, dtype=tf.uint8)) # 前景类都为1
true_cls_ids = tf.one_hot(true_cls_ids, depth=2)
# batch索引
batch_indices = train_indices[:, 0] # 训练的第一维是batch索引
# 每个训练anchor的2维索引
train_indices_2d = tf.stack([batch_indices, tf.cast(train_anchor_indices, dtype=tf.int64)], axis=1)
# 获取预测的anchors类别
predict_cls_ids = tf.gather_nd(predict_cls_ids, train_indices_2d) # (batch*train_num,2)
# 交叉熵损失函数
losses = tf.nn.softmax_cross_entropy_with_logits_v2(
labels=true_cls_ids, logits=predict_cls_ids)
return losses
def smooth_l1_loss(y_true, y_predict, sigma2=9.0):
"""
smooth L1损失函数; 0.5 * sigma2 * x^2 if |x| <1/sigma2 else |x|-0.5/sigma2; x是 diff
:param y_true: 真实值,可以是任何维度
:param y_predict: 预测值
:param sigma2
:return:
"""
abs_diff = tf.abs(y_true - y_predict, name='abs_diff')
loss = tf.where(tf.less(abs_diff, 1. / sigma2), 0.5 * sigma2 * tf.pow(abs_diff, 2), abs_diff - 0.5 / sigma2)
return loss
def ctpn_regress_loss(predict_deltas, deltas, indices):
"""
高度方向中心点偏移和高度尺寸缩放回归损失
:param predict_deltas: 预测的回归目标,(batch_num, anchors_num, 2)
:param deltas: 真实的回归目标,(batch_num, ctpn_train_anchors, 3+1), 最后一位为tag, tag=0 为padding
:param indices: 正负样本索引,(batch_num, ctpn_train_anchors, (idx,tag)),
idx:指定anchor索引位置,最后一位为tag, tag=0 为padding; 1为正样本,-1为负样本
:return:
"""
# 去除padding和负样本
positive_indices = tf.where(tf.equal(indices[:, :, -1], 1))
deltas = tf.gather_nd(deltas[..., :-2], positive_indices) # (n,(dy,dh,dx,tag))
true_positive_indices = tf.gather_nd(indices[..., 0], positive_indices) # 一维,正anchor索引
# batch索引
batch_indices = positive_indices[:, 0]
# 正样本anchor的2维索引
train_indices_2d = tf.stack([batch_indices, tf.cast(true_positive_indices, dtype=tf.int64)], axis=1)
# 正样本anchor预测的回归类型
predict_deltas = tf.gather_nd(predict_deltas, train_indices_2d, name='ctpn_regress_loss_predict_deltas')
# Smooth-L1 # 非常重要,不然报NAN
loss = K.switch(tf.size(deltas) > 0,
smooth_l1_loss(deltas, predict_deltas),
tf.constant(0.0))
loss = K.mean(loss)
return loss
def side_regress_loss(predict_deltas, deltas, indices):
"""
侧边改善回归目标
:param predict_deltas: 预测的x周偏移回归目标,(batch_num, anchors_num, 1)
:param deltas: 真实的回归目标,(batch_num, ctpn_train_anchors, 3+1), 最后一位为tag, tag=0 为padding
:param indices: 正负样本索引,(batch_num, ctpn_train_anchors, (idx,tag)),
idx:指定anchor索引位置,最后一位为tag, tag=0 为padding; 1为正样本,-1为负样本
:return:
"""
# 去除padding和负样本
positive_indices = tf.where(tf.equal(indices[:, :, -1], 1))
deltas = tf.gather_nd(deltas[..., 2:3], positive_indices) # (n,(dy,dh,dx,tag)) 取 dx
true_positive_indices = tf.gather_nd(indices[..., 0], positive_indices) # 一维,正anchor索引
# batch索引
batch_indices = positive_indices[:, 0]
# 正样本anchor的2维索引
train_indices_2d = tf.stack([batch_indices, tf.cast(true_positive_indices, dtype=tf.int64)], axis=1)
# 正样本anchor预测的回归类型
predict_deltas = tf.gather_nd(predict_deltas, train_indices_2d, name='ctpn_regress_loss_predict_side_deltas')
# Smooth-L1 # 非常重要,不然报NAN
loss = K.switch(tf.size(deltas) > 0,
smooth_l1_loss(deltas, predict_deltas),
tf.constant(0.0))
loss = K.mean(loss)
return loss