Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
dlib
Commits
347863c3
"vscode:/vscode.git/clone" did not exist on "3e664e8d954aa3234fd8ca5f32c966beaab4b879"
Commit
347863c3
authored
Sep 15, 2017
by
Davis King
Browse files
Added loss_ranking_ layer
parent
b6dc0d8e
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
276 additions
and
0 deletions
+276
-0
dlib/dnn/loss.h
dlib/dnn/loss.h
+142
-0
dlib/dnn/loss_abstract.h
dlib/dnn/loss_abstract.h
+88
-0
dlib/test/ranking.cpp
dlib/test/ranking.cpp
+46
-0
No files found.
dlib/dnn/loss.h
View file @
347863c3
...
...
@@ -10,6 +10,7 @@
#include "../geometry.h"
#include "../image_processing/box_overlap_testing.h"
#include "../image_processing/full_object_detection.h"
#include "../svm/ranking_tools.h"
#include <sstream>
namespace
dlib
...
...
@@ -1418,6 +1419,147 @@ namespace dlib
template
<
typename
SUBNET
>
using
loss_metric
=
add_loss_layer
<
loss_metric_
,
SUBNET
>
;
// ----------------------------------------------------------------------------------------
class
loss_ranking_
{
public:
typedef
float
training_label_type
;
// nominally +1/-1
typedef
float
output_label_type
;
// ranking score
template
<
typename
SUB_TYPE
,
typename
label_iterator
>
void
to_label
(
const
tensor
&
input_tensor
,
const
SUB_TYPE
&
sub
,
label_iterator
iter
)
const
{
DLIB_CASSERT
(
sub
.
sample_expansion_factor
()
==
1
);
const
tensor
&
output_tensor
=
sub
.
get_output
();
DLIB_CASSERT
(
output_tensor
.
nr
()
==
1
&&
output_tensor
.
nc
()
==
1
&&
output_tensor
.
k
()
==
1
);
DLIB_CASSERT
(
input_tensor
.
num_samples
()
==
output_tensor
.
num_samples
());
const
float
*
out_data
=
output_tensor
.
host
();
for
(
long
i
=
0
;
i
<
output_tensor
.
num_samples
();
++
i
)
{
*
iter
++
=
out_data
[
i
];
}
}
template
<
typename
const_label_iterator
,
typename
SUBNET
>
double
compute_loss_value_and_gradient
(
const
tensor
&
input_tensor
,
const_label_iterator
truth
,
SUBNET
&
sub
)
const
{
const
tensor
&
output_tensor
=
sub
.
get_output
();
tensor
&
grad
=
sub
.
get_gradient_input
();
DLIB_CASSERT
(
sub
.
sample_expansion_factor
()
==
1
);
DLIB_CASSERT
(
input_tensor
.
num_samples
()
!=
0
);
DLIB_CASSERT
(
input_tensor
.
num_samples
()
%
sub
.
sample_expansion_factor
()
==
0
);
DLIB_CASSERT
(
input_tensor
.
num_samples
()
==
grad
.
num_samples
());
DLIB_CASSERT
(
input_tensor
.
num_samples
()
==
output_tensor
.
num_samples
());
DLIB_CASSERT
(
output_tensor
.
nr
()
==
1
&&
output_tensor
.
nc
()
==
1
&&
output_tensor
.
k
()
==
1
);
DLIB_CASSERT
(
grad
.
nr
()
==
1
&&
grad
.
nc
()
==
1
&&
grad
.
k
()
==
1
);
std
::
vector
<
double
>
rel_scores
;
std
::
vector
<
double
>
nonrel_scores
;
std
::
vector
<
long
>
rel_idx
,
nonrel_idx
;
const
float
*
out_data
=
output_tensor
.
host
();
float
*
g
=
grad
.
host_write_only
();
for
(
long
i
=
0
;
i
<
output_tensor
.
num_samples
();
++
i
)
{
const
float
y
=
*
truth
++
;
if
(
y
>
0
)
{
rel_scores
.
push_back
(
out_data
[
i
]
-
y
);
rel_idx
.
push_back
(
i
);
}
else
if
(
y
<
0
)
{
nonrel_scores
.
push_back
(
out_data
[
i
]
-
y
);
nonrel_idx
.
push_back
(
i
);
}
else
{
g
[
i
]
=
0
;
}
}
std
::
vector
<
unsigned
long
>
rel_counts
;
std
::
vector
<
unsigned
long
>
nonrel_counts
;
count_ranking_inversions
(
rel_scores
,
nonrel_scores
,
rel_counts
,
nonrel_counts
);
const
unsigned
long
total_pairs
=
rel_scores
.
size
()
*
nonrel_scores
.
size
();
DLIB_CASSERT
(
total_pairs
>
0
,
"You can't give a ranking mini-batch that contains only one class. Both classes must be represented."
);
const
double
scale
=
1.0
/
total_pairs
;
double
loss
=
0
;
for
(
unsigned
long
k
=
0
;
k
<
rel_counts
.
size
();
++
k
)
{
loss
-=
rel_counts
[
k
]
*
rel_scores
[
k
];
g
[
rel_idx
[
k
]]
=
-
1.0
*
rel_counts
[
k
]
*
scale
;
}
for
(
unsigned
long
k
=
0
;
k
<
nonrel_counts
.
size
();
++
k
)
{
loss
+=
nonrel_counts
[
k
]
*
nonrel_scores
[
k
];
g
[
nonrel_idx
[
k
]]
=
nonrel_counts
[
k
]
*
scale
;
}
return
loss
*
scale
;
}
friend
void
serialize
(
const
loss_ranking_
&
,
std
::
ostream
&
out
)
{
serialize
(
"loss_ranking_"
,
out
);
}
friend
void
deserialize
(
loss_ranking_
&
,
std
::
istream
&
in
)
{
std
::
string
version
;
deserialize
(
version
,
in
);
if
(
version
!=
"loss_ranking_"
)
throw
serialization_error
(
"Unexpected version found while deserializing dlib::loss_ranking_."
);
}
friend
std
::
ostream
&
operator
<<
(
std
::
ostream
&
out
,
const
loss_ranking_
&
)
{
out
<<
"loss_ranking"
;
return
out
;
}
friend
void
to_xml
(
const
loss_ranking_
&
/*item*/
,
std
::
ostream
&
out
)
{
out
<<
"<loss_ranking/>"
;
}
};
template
<
typename
SUBNET
>
using
loss_ranking
=
add_loss_layer
<
loss_ranking_
,
SUBNET
>
;
// ----------------------------------------------------------------------------------------
class
loss_mean_squared_
...
...
dlib/dnn/loss_abstract.h
View file @
347863c3
...
...
@@ -691,6 +691,94 @@ namespace dlib
template
<
typename
SUBNET
>
using
loss_metric
=
add_loss_layer
<
loss_metric_
,
SUBNET
>
;
// ----------------------------------------------------------------------------------------
class
loss_ranking_
{
/*!
WHAT THIS OBJECT REPRESENTS
This object implements the loss layer interface defined above by
EXAMPLE_LOSS_LAYER_. In particular, it implements the pairwise ranking
loss described in the paper:
Optimizing Search Engines using Clickthrough Data by Thorsten Joachims
This is the same loss function used by the dlib::svm_rank_trainer object.
Therefore, it is generally appropriate when you have a two class problem
and you want to learn a function that ranks one class before the other.
So for example, suppose you have two classes of data. Objects of type A
and objects of type B. Moreover, suppose that you want to sort the objects
so that A objects always come before B objects. This loss will help you
learn a function that assigns a real number to each object such that A
objects get a larger number assigned to them than B objects. This lets you
then sort the objects according to the output of the neural network and
obtain the desired result of having A objects come before B objects.
The training labels should be positive values for objects you want to get
high scores and negative for objects that should get small scores. So
relative to our A/B example, you would give A objects labels of +1 and B
objects labels of -1. This should cause the learned network to give A
objects large positive values and B objects negative values.
Finally, the specific loss function is:
For all pairs of positive vs negative training examples A_i and B_j respectively:
sum_ij: max(0, B_i - A_j + margin_ij)
where margin_ij = the label for A_j minus the label for B_i. If you
always use +1 and -1 labels then the margin is always 2. However, this
formulation allows you to give certain training samples different weight by
adjusting the training labels appropriately.
!*/
public:
typedef
float
training_label_type
;
typedef
float
output_label_type
;
template
<
typename
SUB_TYPE
,
typename
label_iterator
>
void
to_label
(
const
tensor
&
input_tensor
,
const
SUB_TYPE
&
sub
,
label_iterator
iter
)
const
;
/*!
This function has the same interface as EXAMPLE_LOSS_LAYER_::to_label() except
it has the additional calling requirements that:
- sub.get_output().nr() == 1
- sub.get_output().nc() == 1
- sub.get_output().k() == 1
- sub.get_output().num_samples() == input_tensor.num_samples()
- sub.sample_expansion_factor() == 1
and the output label is the predicted ranking score.
!*/
template
<
typename
const_label_iterator
,
typename
SUBNET
>
double
compute_loss_value_and_gradient
(
const
tensor
&
input_tensor
,
const_label_iterator
truth
,
SUBNET
&
sub
)
const
;
/*!
This function has the same interface as EXAMPLE_LOSS_LAYER_::compute_loss_value_and_gradient()
except it has the additional calling requirements that:
- sub.get_output().nr() == 1
- sub.get_output().nc() == 1
- sub.get_output().k() == 1
- sub.get_output().num_samples() == input_tensor.num_samples()
- sub.sample_expansion_factor() == 1
!*/
};
template
<
typename
SUBNET
>
using
loss_ranking
=
add_loss_layer
<
loss_ranking_
,
SUBNET
>
;
// ----------------------------------------------------------------------------------------
class
loss_mean_squared_
...
...
dlib/test/ranking.cpp
View file @
347863c3
...
...
@@ -2,6 +2,7 @@
// License: Boost Software License See LICENSE.txt for the full license.
#include <dlib/svm.h>
#include <dlib/rand.h>
#include <dlib/dnn.h>
#include <sstream>
#include <string>
#include <cstdlib>
...
...
@@ -403,6 +404,50 @@ namespace
DLIB_TEST
(
std
::
abs
(
abs
(
df
.
b
-
df2
.
b
))
<
1e-8
);
}
// ----------------------------------------------------------------------------------------
void
test_dnn_ranking_loss
()
{
print_spinner
();
typedef
matrix
<
double
,
2
,
1
>
sample_type
;
ranking_pair
<
sample_type
>
data
;
sample_type
samp
;
// Make one relevant example.
samp
=
1
,
0
;
data
.
relevant
.
push_back
(
samp
);
// Now make a non-relevant example.
samp
=
0
,
1
;
data
.
nonrelevant
.
push_back
(
samp
);
using
net_type
=
loss_ranking
<
fc_no_bias
<
1
,
input
<
matrix
<
float
,
2
,
1
>>>>
;
net_type
net
;
dnn_trainer
<
net_type
>
trainer
(
net
,
sgd
(
1.0
,
0.9
));
std
::
vector
<
matrix
<
float
,
2
,
1
>>
x
;
std
::
vector
<
float
>
y
;
x
.
push_back
(
matrix_cast
<
float
>
(
data
.
relevant
[
0
]));
y
.
push_back
(
1
);
x
.
push_back
(
matrix_cast
<
float
>
(
data
.
nonrelevant
[
0
]));
y
.
push_back
(
-
1
);
//trainer.be_verbose();
trainer
.
set_learning_rate_schedule
(
logspace
(
-
1
,
-
7
,
2000
));
trainer
.
train
(
x
,
y
);
matrix
<
float
>
params
=
mat
(
net
.
subnet
().
layer_details
().
get_layer_params
());
dlog
<<
LINFO
<<
"params: "
<<
params
;
dlog
<<
LINFO
<<
"relevant output score: "
<<
net
(
x
[
0
]);
dlog
<<
LINFO
<<
"nonrelevant output score: "
<<
net
(
x
[
1
]);
DLIB_TEST
(
std
::
abs
(
params
(
0
)
-
1
)
<
0.0001
);
DLIB_TEST
(
std
::
abs
(
params
(
1
)
+
1
)
<
0.0001
);
DLIB_TEST
(
std
::
abs
(
net
(
x
[
0
])
-
1
)
<
0.001
);
DLIB_TEST
(
std
::
abs
(
net
(
x
[
1
])
+
1
)
<
0.001
);
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
...
...
@@ -427,6 +472,7 @@ namespace
test_svmrank_weight_force_dense
<
false
>
();
run_prior_test
();
run_prior_sparse_test
();
test_dnn_ranking_loss
();
}
}
a
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment