• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //
18 // This file contains the MulticlassPA class which implements a simple
19 // linear multi-class classifier based on the multi-prototype version of
20 // passive aggressive.
21 
22 #include "native/multiclass_pa.h"
23 
24 using std::vector;
25 using std::pair;
26 
27 namespace learningfw {
28 
RandFloat()29 float RandFloat() {
30   return static_cast<float>(rand()) / RAND_MAX;
31 }
32 
MulticlassPA(int num_classes,int num_dimensions,float aggressiveness)33 MulticlassPA::MulticlassPA(int num_classes,
34                            int num_dimensions,
35                            float aggressiveness)
36     : num_classes_(num_classes),
37       num_dimensions_(num_dimensions),
38       aggressiveness_(aggressiveness) {
39   InitializeParameters();
40 }
41 
~MulticlassPA()42 MulticlassPA::~MulticlassPA() {
43 }
44 
InitializeParameters()45 void MulticlassPA::InitializeParameters() {
46   parameters_.resize(num_classes_);
47   for (int i = 0; i < num_classes_; ++i) {
48     parameters_[i].resize(num_dimensions_);
49     for (int j = 0; j < num_dimensions_; ++j) {
50       parameters_[i][j] = 0.0;
51     }
52   }
53 }
54 
PickAClassExcept(int target)55 int MulticlassPA::PickAClassExcept(int target) {
56   int picked;
57   do {
58     picked = static_cast<int>(RandFloat() * num_classes_);
59     //    picked = static_cast<int>(random_.RandFloat() * num_classes_);
60   } while (target == picked);
61   return picked;
62 }
63 
PickAnExample(int num_examples)64 int MulticlassPA::PickAnExample(int num_examples) {
65   return static_cast<int>(RandFloat() * num_examples);
66 }
67 
Score(const vector<float> & inputs,const vector<float> & parameters) const68 float MulticlassPA::Score(const vector<float>& inputs,
69                           const vector<float>& parameters) const {
70   // CHECK_EQ(inputs.size(), parameters.size());
71   float result = 0.0;
72   for (int i = 0; i < static_cast<int>(inputs.size()); ++i) {
73     result += inputs[i] * parameters[i];
74   }
75   return result;
76 }
77 
SparseScore(const vector<pair<int,float>> & inputs,const vector<float> & parameters) const78 float MulticlassPA::SparseScore(const vector<pair<int, float> >& inputs,
79                                 const vector<float>& parameters) const {
80   float result = 0.0;
81   for (int i = 0; i < static_cast<int>(inputs.size()); ++i) {
82     //DCHECK_GE(inputs[i].first, 0);
83     //DCHECK_LT(inputs[i].first, parameters.size());
84     result += inputs[i].second * parameters[inputs[i].first];
85   }
86   return result;
87 }
88 
L2NormSquare(const vector<float> & inputs) const89 float MulticlassPA::L2NormSquare(const vector<float>& inputs) const {
90   float norm = 0;
91   for (int i = 0; i < static_cast<int>(inputs.size()); ++i) {
92     norm += inputs[i] * inputs[i];
93   }
94   return norm;
95 }
96 
SparseL2NormSquare(const vector<pair<int,float>> & inputs) const97 float MulticlassPA::SparseL2NormSquare(
98     const vector<pair<int, float> >& inputs) const {
99   float norm = 0;
100   for (int i = 0; i < static_cast<int>(inputs.size()); ++i) {
101     norm += inputs[i].second * inputs[i].second;
102   }
103   return norm;
104 }
105 
TrainOneExample(const vector<float> & inputs,int target)106 float MulticlassPA::TrainOneExample(const vector<float>& inputs, int target) {
107   //CHECK_GE(target, 0);
108   //CHECK_LT(target, num_classes_);
109   float target_class_score = Score(inputs, parameters_[target]);
110   //  VLOG(1) << "target class " << target << " score " << target_class_score;
111   int other_class = PickAClassExcept(target);
112   float other_class_score = Score(inputs, parameters_[other_class]);
113   //  VLOG(1) << "other class " << other_class << " score " << other_class_score;
114   float loss = 1.0 - target_class_score + other_class_score;
115   if (loss > 0.0) {
116     // Compute the learning rate according to PA-I.
117     float twice_norm_square = L2NormSquare(inputs) * 2.0;
118     if (twice_norm_square == 0.0) {
119       twice_norm_square = kEpsilon;
120     }
121     float rate = loss / twice_norm_square;
122     if (rate > aggressiveness_) {
123       rate = aggressiveness_;
124     }
125     //    VLOG(1) << "loss = " << loss << " rate = " << rate;
126     // Modify the parameter vectors of the correct and wrong classes
127     for (int i = 0; i < static_cast<int>(inputs.size()); ++i) {
128       // First modify the parameter value of the correct class
129       parameters_[target][i] += rate * inputs[i];
130       // Then modify the parameter value of the wrong class
131       parameters_[other_class][i] -= rate * inputs[i];
132     }
133     return loss;
134   }
135   return 0.0;
136 }
137 
SparseTrainOneExample(const vector<pair<int,float>> & inputs,int target)138 float MulticlassPA::SparseTrainOneExample(
139     const vector<pair<int, float> >& inputs, int target) {
140   // CHECK_GE(target, 0);
141   // CHECK_LT(target, num_classes_);
142   float target_class_score = SparseScore(inputs, parameters_[target]);
143   //  VLOG(1) << "target class " << target << " score " << target_class_score;
144   int other_class = PickAClassExcept(target);
145   float other_class_score = SparseScore(inputs, parameters_[other_class]);
146   //  VLOG(1) << "other class " << other_class << " score " << other_class_score;
147   float loss = 1.0 - target_class_score + other_class_score;
148   if (loss > 0.0) {
149     // Compute the learning rate according to PA-I.
150     float twice_norm_square = SparseL2NormSquare(inputs) * 2.0;
151     if (twice_norm_square == 0.0) {
152       twice_norm_square = kEpsilon;
153     }
154     float rate = loss / twice_norm_square;
155     if (rate > aggressiveness_) {
156       rate = aggressiveness_;
157     }
158     //    VLOG(1) << "loss = " << loss << " rate = " << rate;
159     // Modify the parameter vectors of the correct and wrong classes
160     for (int i = 0; i < static_cast<int>(inputs.size()); ++i) {
161       // First modify the parameter value of the correct class
162       parameters_[target][inputs[i].first] += rate * inputs[i].second;
163       // Then modify the parameter value of the wrong class
164       parameters_[other_class][inputs[i].first] -= rate * inputs[i].second;
165     }
166     return loss;
167   }
168   return 0.0;
169 }
170 
Train(const vector<pair<vector<float>,int>> & data,int num_iterations)171 float MulticlassPA::Train(const vector<pair<vector<float>, int> >& data,
172                           int num_iterations) {
173   int num_examples = data.size();
174   float total_loss = 0.0;
175   for (int t = 0; t < num_iterations; ++t) {
176     int index = PickAnExample(num_examples);
177     float loss_t = TrainOneExample(data[index].first, data[index].second);
178     total_loss += loss_t;
179   }
180   return total_loss / static_cast<float>(num_iterations);
181 }
182 
SparseTrain(const vector<pair<vector<pair<int,float>>,int>> & data,int num_iterations)183 float MulticlassPA::SparseTrain(
184     const vector<pair<vector<pair<int, float> >, int> >& data,
185     int num_iterations) {
186   int num_examples = data.size();
187   float total_loss = 0.0;
188   for (int t = 0; t < num_iterations; ++t) {
189     int index = PickAnExample(num_examples);
190     float loss_t = SparseTrainOneExample(data[index].first, data[index].second);
191     total_loss += loss_t;
192   }
193   return total_loss / static_cast<float>(num_iterations);
194 }
195 
GetClass(const vector<float> & inputs)196 int MulticlassPA::GetClass(const vector<float>& inputs) {
197   int best_class = -1;
198   float best_score = -10000.0;
199   // float best_score = -MathLimits<float>::kMax;
200   for (int i = 0; i < num_classes_; ++i) {
201     float score_i = Score(inputs, parameters_[i]);
202     if (score_i > best_score) {
203       best_score = score_i;
204       best_class = i;
205     }
206   }
207   return best_class;
208 }
209 
SparseGetClass(const vector<pair<int,float>> & inputs)210 int MulticlassPA::SparseGetClass(const vector<pair<int, float> >& inputs) {
211   int best_class = -1;
212   float best_score = -10000.0;
213   //float best_score = -MathLimits<float>::kMax;
214   for (int i = 0; i < num_classes_; ++i) {
215     float score_i = SparseScore(inputs, parameters_[i]);
216     if (score_i > best_score) {
217       best_score = score_i;
218       best_class = i;
219     }
220   }
221   return best_class;
222 }
223 
Test(const vector<pair<vector<float>,int>> & data)224 float MulticlassPA::Test(const vector<pair<vector<float>, int> >& data) {
225   int num_examples = data.size();
226   float total_error = 0.0;
227   for (int t = 0; t < num_examples; ++t) {
228     int best_class = GetClass(data[t].first);
229     if (best_class != data[t].second) {
230       ++total_error;
231     }
232   }
233   return total_error / num_examples;
234 }
235 
SparseTest(const vector<pair<vector<pair<int,float>>,int>> & data)236 float MulticlassPA::SparseTest(
237     const vector<pair<vector<pair<int, float> >, int> >& data) {
238   int num_examples = data.size();
239   float total_error = 0.0;
240   for (int t = 0; t < num_examples; ++t) {
241     int best_class = SparseGetClass(data[t].first);
242     if (best_class != data[t].second) {
243       ++total_error;
244     }
245   }
246   return total_error / num_examples;
247 }
248 }  // namespace learningfw
249