forked from nwojke/cosine_metric_learning
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlosses.py
144 lines (114 loc) · 5.2 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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# vim: expandtab:ts=4:sw=4
import tensorflow as tf
def _pdist(a, b=None):
sq_sum_a = tf.reduce_sum(tf.square(a), reduction_indices=[1])
if b is None:
return -2 * tf.matmul(a, tf.transpose(a)) + \
tf.reshape(sq_sum_a, (-1, 1)) + tf.reshape(sq_sum_a, (1, -1))
sq_sum_b = tf.reduce_sum(tf.square(b), reduction_indices=[1])
return -2 * tf.matmul(a, tf.transpose(b)) + \
tf.reshape(sq_sum_a, (-1, 1)) + tf.reshape(sq_sum_b, (1, -1))
## 正值与负值的相关距离,越大越好
def softmargin_triplet_loss(features, labels, create_summaries=True):
"""Softmargin triplet loss.
See::
Hermans, Beyer, Leibe: In Defense of the Triplet Loss for Person
Re-Identification. arXiv, 2017.
Parameters
----------
features : tf.Tensor
A matrix of shape NxM that contains the M-dimensional feature vectors
of N objects (floating type).
labels : tf.Tensor
The one-dimensional array of length N that contains for each feature
the associated class label (integer type).
create_summaries : Optional[bool]
If True, creates summaries to monitor training behavior.
Returns
-------
tf.Tensor
A scalar loss tensor.
"""
eps = tf.constant(1e-5, tf.float32)
nil = tf.constant(0., tf.float32)
almost_inf = tf.constant(1e+10, tf.float32)
squared_distance_mat = _pdist(features)
distance_mat = tf.sqrt(tf.maximum(nil, eps + squared_distance_mat))
label_mat = tf.cast(tf.equal(
tf.reshape(labels, (-1, 1)), tf.reshape(labels, (1, -1))), tf.float32)
positive_distance = tf.reduce_max(label_mat * distance_mat, axis=1)
negative_distance = tf.reduce_min(
(label_mat * almost_inf) + distance_mat, axis=1)
loss = tf.nn.softplus(positive_distance - negative_distance)
## 监视作用的输出
if create_summaries:
fraction_invalid_pdist = tf.reduce_mean(
tf.cast(tf.less_equal(squared_distance_mat, -eps), tf.float32))
tf.summary.scalar("fraction_invalid_pdist", fraction_invalid_pdist)
fraction_active_triplets = tf.reduce_mean(
tf.cast(tf.greater_equal(loss, 1e-5), tf.float32))
tf.summary.scalar("fraction_active_triplets", fraction_active_triplets)
embedding_squared_norm = tf.reduce_mean(
tf.reduce_sum(tf.square(features), axis=1))
tf.summary.scalar("mean squared feature norm", embedding_squared_norm)
mean_distance = tf.reduce_mean(distance_mat)
tf.summary.scalar("mean feature distance", mean_distance)
mean_positive_distance = tf.reduce_mean(positive_distance)
tf.summary.scalar("mean positive distance", mean_positive_distance)
mean_negative_distance = tf.reduce_mean(negative_distance)
tf.summary.scalar("mean negative distance", mean_negative_distance)
return tf.reduce_mean(loss)
## 与类平均的距离, 越小越好
def magnet_loss(features, labels, margin=1.0, unique_labels=None):
"""Simple unimodal magnet loss.
See::
Rippel, Paluri, Dollar, Bourdev: Metric Learning With Adaptive
Density Discrimination. ICLR, 2016.
Parameters
----------
features : tf.Tensor
A matrix of shape NxM that contains the M-dimensional feature vectors
of N objects (floating type).
labels : tf.Tensor
The one-dimensional array of length N that contains for each feature
the associated class label (integer type).
margin : float
A scalar margin hyperparameter.
unique_labels : Optional[tf.Tensor]
Optional tensor of unique values in `labels`. If None given, computed
from data.
Returns
-------
tf.Tensor
A scalar loss tensor.
"""
nil = tf.constant(0., tf.float32)
one = tf.constant(1., tf.float32)
minus_two = tf.constant(-2., tf.float32)
eps = tf.constant(1e-4, tf.float32)
margin = tf.constant(margin, tf.float32)
num_per_class = None
if unique_labels is None:
unique_labels, sample_to_unique_y, num_per_class = tf.unique_with_counts(labels)
num_per_class = tf.cast(num_per_class, tf.float32)
y_mat = tf.cast(tf.equal(
tf.reshape(labels, (-1, 1)), tf.reshape(unique_labels, (1, -1))),
dtype=tf.float32)
# If class_means is None, compute from batch data.
if num_per_class is None:
num_per_class = tf.reduce_sum(y_mat, reduction_indices=[0])
class_means = tf.reduce_sum(
tf.expand_dims(tf.transpose(y_mat), -1) * tf.expand_dims(features, 0),
reduction_indices=[1]) / tf.expand_dims(num_per_class, -1)
squared_distance = _pdist(features, class_means)
num_samples = tf.cast(tf.shape(labels)[0], tf.float32)
variance = tf.reduce_sum(
y_mat * squared_distance) / (num_samples - one)
const = one / (minus_two * (variance + eps))
linear = const * squared_distance - y_mat * margin
maxi = tf.reduce_max(linear, reduction_indices=[1], keepdims=True)
loss_mat = tf.exp(linear - maxi)
a = tf.reduce_sum(y_mat * loss_mat, reduction_indices=[1])
b = tf.reduce_sum((one - y_mat) * loss_mat, reduction_indices=[1])
loss = tf.maximum(nil, -tf.log(eps + a / (eps + b)))
return tf.reduce_mean(loss), class_means, variance