439 lines
14 KiB
C++
439 lines
14 KiB
C++
// Copyright (C) 2012 Davis E. King (davis@dlib.net)
|
|
// License: Boost Software License See LICENSE.txt for the full license.
|
|
#include <dlib/svm.h>
|
|
#include <dlib/rand.h>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
#include <map>
|
|
|
|
#include "tester.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
using namespace test;
|
|
using namespace dlib;
|
|
using namespace std;
|
|
|
|
|
|
logger dlog("test.ranking");
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename T>
|
|
void brute_force_count_ranking_inversions (
|
|
const std::vector<T>& x,
|
|
const std::vector<T>& y,
|
|
std::vector<unsigned long>& x_count,
|
|
std::vector<unsigned long>& y_count
|
|
)
|
|
{
|
|
x_count.assign(x.size(),0);
|
|
y_count.assign(y.size(),0);
|
|
|
|
for (unsigned long i = 0; i < x.size(); ++i)
|
|
{
|
|
for (unsigned long j = 0; j < y.size(); ++j)
|
|
{
|
|
if (x[i] <= y[j])
|
|
{
|
|
x_count[i]++;
|
|
y_count[j]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
void test_count_ranking_inversions()
|
|
{
|
|
print_spinner();
|
|
dlog << LINFO << "in test_count_ranking_inversions()";
|
|
|
|
dlib::rand rnd;
|
|
std::vector<int> x, y;
|
|
std::vector<unsigned long> x_count, y_count;
|
|
std::vector<unsigned long> x_count2, y_count2;
|
|
for (int iter = 0; iter < 5000; ++iter)
|
|
{
|
|
x.resize(rnd.get_random_32bit_number()%10);
|
|
y.resize(rnd.get_random_32bit_number()%10);
|
|
for (unsigned long i = 0; i < x.size(); ++i)
|
|
x[i] = ((int)rnd.get_random_32bit_number()%10) - 5;
|
|
for (unsigned long i = 0; i < y.size(); ++i)
|
|
y[i] = ((int)rnd.get_random_32bit_number()%10) - 5;
|
|
|
|
count_ranking_inversions(x, y, x_count, y_count);
|
|
brute_force_count_ranking_inversions(x, y, x_count2, y_count2);
|
|
|
|
DLIB_TEST(mat(x_count) == mat(x_count2));
|
|
DLIB_TEST(mat(y_count) == mat(y_count2));
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
void run_prior_test()
|
|
{
|
|
print_spinner();
|
|
typedef matrix<double,3,1> sample_type;
|
|
typedef linear_kernel<sample_type> kernel_type;
|
|
|
|
svm_rank_trainer<kernel_type> trainer;
|
|
|
|
ranking_pair<sample_type> data;
|
|
|
|
sample_type samp;
|
|
samp = 0, 0, 1; data.relevant.push_back(samp);
|
|
samp = 0, 1, 0; data.nonrelevant.push_back(samp);
|
|
|
|
trainer.set_c(10);
|
|
decision_function<kernel_type> df = trainer.train(data);
|
|
|
|
trainer.set_prior(df);
|
|
|
|
data.relevant.clear();
|
|
data.nonrelevant.clear();
|
|
samp = 1, 0, 0; data.relevant.push_back(samp);
|
|
samp = 0, 1, 0; data.nonrelevant.push_back(samp);
|
|
|
|
df = trainer.train(data);
|
|
|
|
dlog << LINFO << trans(df.basis_vectors(0));
|
|
DLIB_TEST(df.basis_vectors(0)(0) > 0);
|
|
DLIB_TEST(df.basis_vectors(0)(1) < 0);
|
|
DLIB_TEST(df.basis_vectors(0)(2) > 0);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
void run_prior_sparse_test()
|
|
{
|
|
print_spinner();
|
|
typedef std::map<unsigned long,double> sample_type;
|
|
typedef sparse_linear_kernel<sample_type> kernel_type;
|
|
|
|
svm_rank_trainer<kernel_type> trainer;
|
|
|
|
ranking_pair<sample_type> data;
|
|
|
|
sample_type samp;
|
|
samp[0] = 1; data.relevant.push_back(samp); samp.clear();
|
|
samp[1] = 1; data.nonrelevant.push_back(samp); samp.clear();
|
|
|
|
trainer.set_c(10);
|
|
decision_function<kernel_type> df = trainer.train(data);
|
|
|
|
trainer.set_prior(df);
|
|
|
|
data.relevant.clear();
|
|
data.nonrelevant.clear();
|
|
samp[2] = 1; data.relevant.push_back(samp); samp.clear();
|
|
samp[1] = 1; data.nonrelevant.push_back(samp); samp.clear();
|
|
|
|
df = trainer.train(data);
|
|
|
|
matrix<double,0,1> w = sparse_to_dense(df.basis_vectors(0));
|
|
dlog << LINFO << trans(w);
|
|
DLIB_TEST(w(0) > 0.1);
|
|
DLIB_TEST(w(1) < -0.1);
|
|
DLIB_TEST(w(2) > 0.1);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
void dotest1()
|
|
{
|
|
print_spinner();
|
|
dlog << LINFO << "in dotest1()";
|
|
|
|
typedef matrix<double,4,1> sample_type;
|
|
|
|
typedef linear_kernel<sample_type> kernel_type;
|
|
|
|
svm_rank_trainer<kernel_type> trainer;
|
|
|
|
|
|
std::vector<ranking_pair<sample_type> > samples;
|
|
|
|
ranking_pair<sample_type> p;
|
|
sample_type samp;
|
|
|
|
samp = 0, 0, 0, 1; p.relevant.push_back(samp);
|
|
samp = 1, 0, 0, 0; p.nonrelevant.push_back(samp);
|
|
samples.push_back(p);
|
|
|
|
samp = 0, 0, 1, 0; p.relevant.push_back(samp);
|
|
samp = 1, 0, 0, 0; p.nonrelevant.push_back(samp);
|
|
samp = 0, 1, 0, 0; p.nonrelevant.push_back(samp);
|
|
samp = 0, 1, 0, 0; p.nonrelevant.push_back(samp);
|
|
samples.push_back(p);
|
|
|
|
|
|
trainer.set_c(10);
|
|
|
|
decision_function<kernel_type> df = trainer.train(samples);
|
|
|
|
dlog << LINFO << "accuracy: "<< test_ranking_function(df, samples);
|
|
matrix<double,1,2> res;
|
|
res = 1,1;
|
|
DLIB_TEST(equal(test_ranking_function(df, samples), res));
|
|
|
|
DLIB_TEST(equal(test_ranking_function(trainer.train(samples[1]), samples), res));
|
|
|
|
trainer.set_epsilon(1e-13);
|
|
df = trainer.train(samples);
|
|
|
|
dlog << LINFO << df.basis_vectors(0);
|
|
sample_type truew;
|
|
truew = -0.5, -0.5, 0.5, 0.5;
|
|
DLIB_TEST(length(truew - df.basis_vectors(0)) < 1e-10);
|
|
|
|
dlog << LINFO << "accuracy: "<< test_ranking_function(df, samples);
|
|
DLIB_TEST(equal(test_ranking_function(df, samples), res));
|
|
|
|
dlog << LINFO << "cv-accuracy: "<< cross_validate_ranking_trainer(trainer, samples,2);
|
|
DLIB_TEST(std::abs(cross_validate_ranking_trainer(trainer, samples,2)(0) - 0.7777777778) < 0.0001);
|
|
|
|
trainer.set_learns_nonnegative_weights(true);
|
|
df = trainer.train(samples);
|
|
truew = 0, 0, 1.0, 1.0;
|
|
dlog << LINFO << df.basis_vectors(0);
|
|
DLIB_TEST(length(truew - df.basis_vectors(0)) < 1e-10);
|
|
dlog << LINFO << "accuracy: "<< test_ranking_function(df, samples);
|
|
DLIB_TEST(equal(test_ranking_function(df, samples), res));
|
|
|
|
|
|
samples.clear();
|
|
samples.push_back(p);
|
|
samples.push_back(p);
|
|
samples.push_back(p);
|
|
samples.push_back(p);
|
|
dlog << LINFO << "cv-accuracy: "<< cross_validate_ranking_trainer(trainer, samples,4);
|
|
DLIB_TEST(equal(cross_validate_ranking_trainer(trainer, samples,4) , res));
|
|
|
|
df.basis_vectors(0) = 0;
|
|
dlog << LINFO << "BAD RANKING:" << test_ranking_function(df, samples);
|
|
DLIB_TEST(test_ranking_function(df, samples)(1) < 0.5);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
void dotest_sparse_vectors()
|
|
{
|
|
print_spinner();
|
|
dlog << LINFO << "in dotest_sparse_vectors()";
|
|
|
|
typedef std::map<unsigned long,double> sample_type;
|
|
|
|
typedef sparse_linear_kernel<sample_type> kernel_type;
|
|
|
|
svm_rank_trainer<kernel_type> trainer;
|
|
|
|
|
|
std::vector<ranking_pair<sample_type> > samples;
|
|
|
|
ranking_pair<sample_type> p;
|
|
sample_type samp;
|
|
|
|
samp[3] = 1; p.relevant.push_back(samp); samp.clear();
|
|
samp[0] = 1; p.nonrelevant.push_back(samp); samp.clear();
|
|
samples.push_back(p);
|
|
|
|
samp[2] = 1; p.relevant.push_back(samp); samp.clear();
|
|
samp[0] = 1; p.nonrelevant.push_back(samp); samp.clear();
|
|
samp[1] = 1; p.nonrelevant.push_back(samp); samp.clear();
|
|
samp[1] = 1; p.nonrelevant.push_back(samp); samp.clear();
|
|
samples.push_back(p);
|
|
|
|
|
|
trainer.set_c(10);
|
|
|
|
decision_function<kernel_type> df = trainer.train(samples);
|
|
|
|
matrix<double,1,2> res;
|
|
res = 1,1;
|
|
|
|
dlog << LINFO << "accuracy: "<< test_ranking_function(df, samples);
|
|
DLIB_TEST(equal(test_ranking_function(df, samples), res));
|
|
|
|
DLIB_TEST(equal(test_ranking_function(trainer.train(samples[1]), samples), res));
|
|
|
|
trainer.set_epsilon(1e-13);
|
|
df = trainer.train(samples);
|
|
|
|
dlog << LINFO << sparse_to_dense(df.basis_vectors(0));
|
|
sample_type truew;
|
|
truew[0] = -0.5;
|
|
truew[1] = -0.5;
|
|
truew[2] = 0.5;
|
|
truew[3] = 0.5;
|
|
DLIB_TEST(length(subtract(truew , df.basis_vectors(0))) < 1e-10);
|
|
|
|
dlog << LINFO << "accuracy: "<< test_ranking_function(df, samples);
|
|
DLIB_TEST(equal(test_ranking_function(df, samples), res));
|
|
|
|
dlog << LINFO << "cv-accuracy: "<< cross_validate_ranking_trainer(trainer, samples,2);
|
|
DLIB_TEST(std::abs(cross_validate_ranking_trainer(trainer, samples,2)(0) - 0.7777777778) < 0.0001);
|
|
|
|
trainer.set_learns_nonnegative_weights(true);
|
|
df = trainer.train(samples);
|
|
truew[0] = 0.0;
|
|
truew[1] = 0.0;
|
|
truew[2] = 1.0;
|
|
truew[3] = 1.0;
|
|
dlog << LINFO << sparse_to_dense(df.basis_vectors(0));
|
|
DLIB_TEST(length(subtract(truew , df.basis_vectors(0))) < 1e-10);
|
|
dlog << LINFO << "accuracy: "<< test_ranking_function(df, samples);
|
|
DLIB_TEST(equal(test_ranking_function(df, samples), res));
|
|
|
|
|
|
samples.clear();
|
|
samples.push_back(p);
|
|
samples.push_back(p);
|
|
samples.push_back(p);
|
|
samples.push_back(p);
|
|
dlog << LINFO << "cv-accuracy: "<< cross_validate_ranking_trainer(trainer, samples,4);
|
|
DLIB_TEST(equal(cross_validate_ranking_trainer(trainer, samples,4) , res) );
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename K, bool use_dcd_trainer>
|
|
class simple_rank_trainer
|
|
{
|
|
public:
|
|
template <typename T>
|
|
decision_function<K> train (
|
|
const ranking_pair<T>& pair
|
|
) const
|
|
{
|
|
typedef matrix<double,10,1> sample_type;
|
|
|
|
std::vector<sample_type> relevant = pair.relevant;
|
|
std::vector<sample_type> nonrelevant = pair.nonrelevant;
|
|
|
|
std::vector<sample_type> samples;
|
|
std::vector<double> labels;
|
|
for (unsigned long i = 0; i < relevant.size(); ++i)
|
|
{
|
|
for (unsigned long j = 0; j < nonrelevant.size(); ++j)
|
|
{
|
|
samples.push_back(relevant[i] - nonrelevant[j]);
|
|
labels.push_back(+1);
|
|
samples.push_back(nonrelevant[i] - relevant[j]);
|
|
labels.push_back(-1);
|
|
}
|
|
}
|
|
|
|
if (use_dcd_trainer)
|
|
{
|
|
svm_c_linear_dcd_trainer<K> trainer;
|
|
trainer.set_c(1.0/samples.size());
|
|
trainer.set_epsilon(1e-10);
|
|
trainer.force_last_weight_to_1(true);
|
|
//trainer.be_verbose();
|
|
return trainer.train(samples, labels);
|
|
}
|
|
else
|
|
{
|
|
svm_c_linear_trainer<K> trainer;
|
|
trainer.set_c(1.0);
|
|
trainer.set_epsilon(1e-13);
|
|
trainer.force_last_weight_to_1(true);
|
|
//trainer.be_verbose();
|
|
decision_function<K> df = trainer.train(samples, labels);
|
|
DLIB_TEST_MSG(df.b == 0, df.b);
|
|
return df;
|
|
}
|
|
}
|
|
};
|
|
|
|
template <bool use_dcd_trainer>
|
|
void test_svmrank_weight_force_dense()
|
|
{
|
|
print_spinner();
|
|
dlog << LINFO << "use_dcd_trainer: "<< use_dcd_trainer;
|
|
|
|
typedef matrix<double,10,1> sample_type;
|
|
typedef linear_kernel<sample_type> kernel_type;
|
|
|
|
ranking_pair<sample_type> pair;
|
|
|
|
for (int i = 0; i < 20; ++i)
|
|
{
|
|
pair.relevant.push_back(abs(gaussian_randm(10,1,i)));
|
|
}
|
|
|
|
for (int i = 0; i < 20; ++i)
|
|
{
|
|
pair.nonrelevant.push_back(-abs(gaussian_randm(10,1,i+10000)));
|
|
pair.nonrelevant.back()(9) += 1;
|
|
}
|
|
|
|
|
|
svm_rank_trainer<kernel_type> trainer;
|
|
trainer.force_last_weight_to_1(true);
|
|
trainer.set_epsilon(1e-13);
|
|
//trainer.be_verbose();
|
|
decision_function<kernel_type> df;
|
|
df = trainer.train(pair);
|
|
|
|
matrix<double,1,2> res;
|
|
res = 1,1;
|
|
dlog << LINFO << "weights: "<< trans(df.basis_vectors(0));
|
|
const matrix<double,1,2> acc1 = test_ranking_function(df, pair);
|
|
dlog << LINFO << "ranking accuracy: " << acc1;
|
|
DLIB_TEST(equal(acc1,res));
|
|
|
|
simple_rank_trainer<kernel_type,use_dcd_trainer> strainer;
|
|
decision_function<kernel_type> df2;
|
|
df2 = strainer.train(pair);
|
|
dlog << LINFO << "weights: "<< trans(df2.basis_vectors(0));
|
|
const matrix<double,1,2> acc2 = test_ranking_function(df2, pair);
|
|
dlog << LINFO << "ranking accuracy: " << acc2;
|
|
DLIB_TEST(equal(acc2,res));
|
|
|
|
dlog << LINFO << "w error: " << max(abs(df.basis_vectors(0) - df2.basis_vectors(0)));
|
|
dlog << LINFO << "b error: " << abs(df.b - df2.b);
|
|
DLIB_TEST(std::abs(max(abs(df.basis_vectors(0) - df2.basis_vectors(0)))) < 1e-8);
|
|
DLIB_TEST(std::abs(abs(df.b - df2.b)) < 1e-8);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
class test_ranking_tools : public tester
|
|
{
|
|
public:
|
|
test_ranking_tools (
|
|
) :
|
|
tester ("test_ranking",
|
|
"Runs tests on the ranking tools.")
|
|
{}
|
|
|
|
|
|
void perform_test (
|
|
)
|
|
{
|
|
test_count_ranking_inversions();
|
|
dotest1();
|
|
dotest_sparse_vectors();
|
|
test_svmrank_weight_force_dense<true>();
|
|
test_svmrank_weight_force_dense<false>();
|
|
run_prior_test();
|
|
run_prior_sparse_test();
|
|
|
|
}
|
|
} a;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|