Commit bd6f9183 authored by Davis King's avatar Davis King

merged

parents cabad6d5 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_
......
......@@ -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_
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment