1 // Copyright (C) 2012  Davis E. King (davis@dlib.net)
2 // License: Boost Software License   See LICENSE.txt for the full license.
3 #ifndef DLIB_ACTIVE_LEARnING_Hh_
4 #define DLIB_ACTIVE_LEARnING_Hh_
5 
6 #include "active_learning_abstract.h"
7 
8 #include "svm_c_linear_dcd_trainer.h"
9 #include <vector>
10 
11 namespace dlib
12 {
13 
14     enum active_learning_mode
15     {
16         max_min_margin,
17         ratio_margin
18     };
19 
20 // ----------------------------------------------------------------------------------------
21 
22     template <
23         typename kernel_type,
24         typename in_sample_vector_type,
25         typename in_scalar_vector_type,
26         typename in_sample_vector_type2
27         >
impl_rank_unlabeled_training_samples(const svm_c_linear_dcd_trainer<kernel_type> & trainer,const in_sample_vector_type & samples,const in_scalar_vector_type & labels,const in_sample_vector_type2 & unlabeled_samples,const active_learning_mode mode)28     std::vector<unsigned long> impl_rank_unlabeled_training_samples (
29         const svm_c_linear_dcd_trainer<kernel_type>& trainer,
30         const in_sample_vector_type& samples,
31         const in_scalar_vector_type& labels,
32         const in_sample_vector_type2& unlabeled_samples,
33         const active_learning_mode mode
34     )
35     {
36         DLIB_ASSERT(is_vector(unlabeled_samples) &&
37                      (samples.size() == 0 || is_learning_problem(samples, labels)) ,
38                 "\t std::vector<unsigned long> rank_unlabeled_training_samples()"
39                 << "\n\t Invalid inputs were given to this function"
40                 << "\n\t is_vector(unlabeled_samples):         " << is_vector(unlabeled_samples)
41                 << "\n\t is_learning_problem(samples, labels): " << is_learning_problem(samples, labels)
42                 << "\n\t samples.size(): " << samples.size()
43                 << "\n\t labels.size():  " << labels.size()
44                 );
45 
46         // If there aren't any training samples then all unlabeled_samples are equally good.
47         // So just report an arbitrary ordering.
48         if (samples.size() == 0 || unlabeled_samples.size() == 0)
49         {
50             std::vector<unsigned long> ret(unlabeled_samples.size());
51             for (unsigned long i = 0; i < ret.size(); ++i)
52                 ret[i] = i;
53 
54             return ret;
55         }
56 
57         // We are going to score each unlabeled sample and put the score and index into
58         // results.  Then at the end of this function we just sort it and return the indices.
59         std::vector<std::pair<double, unsigned long> > results;
60         results.resize(unlabeled_samples.size());
61 
62         // make sure we use this trainer's ability to warm start itself since that will make
63         // this whole function run a lot faster.  But first, we need to find out what the state
64         // we will be warm starting from is.
65         typedef typename svm_c_linear_dcd_trainer<kernel_type>::optimizer_state optimizer_state;
66         optimizer_state state;
67         trainer.train(samples, labels, state); // call train() just to get state
68 
69         decision_function<kernel_type> df;
70 
71         std::vector<typename kernel_type::sample_type> temp_samples;
72         std::vector<typename kernel_type::scalar_type> temp_labels;
73         temp_samples.reserve(samples.size()+1);
74         temp_labels.reserve(labels.size()+1);
75         temp_samples.assign(samples.begin(), samples.end());
76         temp_labels.assign(labels.begin(), labels.end());
77         temp_samples.resize(temp_samples.size()+1);
78         temp_labels.resize(temp_labels.size()+1);
79 
80 
81         for (long i = 0; i < unlabeled_samples.size(); ++i)
82         {
83             temp_samples.back() = unlabeled_samples(i);
84             // figure out the margin for each possible labeling of this sample.
85 
86             optimizer_state temp(state);
87             temp_labels.back() = +1;
88             df = trainer.train(temp_samples, temp_labels, temp);
89             const double margin_p = temp_labels.back()*df(temp_samples.back());
90 
91             temp = state;
92             temp_labels.back() = -1;
93             df = trainer.train(temp_samples, temp_labels, temp);
94             const double margin_n = temp_labels.back()*df(temp_samples.back());
95 
96             if (mode == max_min_margin)
97             {
98                 // The score for this sample is its min possible margin over possible labels.
99                 // Therefore, this score measures how much flexibility we have to label this
100                 // sample however we want.  The intuition being that the most useful points to
101                 // label are the ones that are still free to obtain either label.
102                 results[i] = std::make_pair(std::min(margin_p, margin_n), i);
103             }
104             else
105             {
106                 // In this case, the score for the sample is a ratio that tells how close the
107                 // two margin values are to each other.  The closer they are the better.  So in
108                 // this case we are saying we are looking for samples that have the same
109                 // preference for either class label.
110                 if (std::abs(margin_p) >= std::abs(margin_n))
111                 {
112                     if (margin_p != 0)
113                         results[i] = std::make_pair(margin_n/margin_p, i);
114                     else // if both are == 0 then say 0/0 == 1
115                         results[i] = std::make_pair(1, i);
116                 }
117                 else
118                 {
119                     results[i] = std::make_pair(margin_p/margin_n, i);
120                 }
121             }
122         }
123 
124         // sort the results so the highest scoring samples come first.
125         std::sort(results.rbegin(), results.rend());
126 
127         // transfer results into a vector with just sample indices so we can return it.
128         std::vector<unsigned long> ret(results.size());
129         for (unsigned long i = 0; i < ret.size(); ++i)
130             ret[i] = results[i].second;
131         return ret;
132     }
133 
134 // ----------------------------------------------------------------------------------------
135 
136     template <
137         typename kernel_type,
138         typename in_sample_vector_type,
139         typename in_scalar_vector_type,
140         typename in_sample_vector_type2
141         >
142     std::vector<unsigned long> rank_unlabeled_training_samples (
143         const svm_c_linear_dcd_trainer<kernel_type>& trainer,
144         const in_sample_vector_type& samples,
145         const in_scalar_vector_type& labels,
146         const in_sample_vector_type2& unlabeled_samples,
147         const active_learning_mode mode = max_min_margin
148     )
149     {
150         return impl_rank_unlabeled_training_samples(trainer,
151                                                     mat(samples),
152                                                     mat(labels),
153                                                     mat(unlabeled_samples),
154                                                     mode);
155     }
156 
157 // ----------------------------------------------------------------------------------------
158 
159 }
160 
161 #endif // DLIB_ACTIVE_LEARnING_Hh_
162 
163