TheAlgorithms/C++ 1.0.0
All the algorithms implemented in C++
Loading...
Searching...
No Matches
adaline_learning.cpp
Go to the documentation of this file.
1
29#include <array>
30#include <cassert>
31#include <climits>
32#include <cmath>
33#include <cstdlib>
34#include <ctime>
35#include <iostream>
36#include <numeric>
37#include <vector>
38
40constexpr int MAX_ITER = 500; // INT_MAX
41
45namespace machine_learning {
46class adaline {
47 public:
55 explicit adaline(int num_features, const double eta = 0.01f,
56 const double accuracy = 1e-5)
58 if (eta <= 0) {
59 std::cerr << "learning rate should be positive and nonzero"
60 << std::endl;
61 std::exit(EXIT_FAILURE);
62 }
63
64 weights = std::vector<double>(
65 num_features +
66 1); // additional weight is for the constant bias term
67
68 // initialize with random weights in the range [-50, 49]
69 for (double &weight : weights) weight = 1.f;
70 // weights[i] = (static_cast<double>(std::rand() % 100) - 50);
71 }
72
76 friend std::ostream &operator<<(std::ostream &out, const adaline &ada) {
77 out << "<";
78 for (int i = 0; i < ada.weights.size(); i++) {
79 out << ada.weights[i];
80 if (i < ada.weights.size() - 1) {
81 out << ", ";
82 }
83 }
84 out << ">";
85 return out;
86 }
87
95 int predict(const std::vector<double> &x, double *out = nullptr) {
96 if (!check_size_match(x)) {
97 return 0;
98 }
99
100 double y = weights.back(); // assign bias value
101
102 // for (int i = 0; i < x.size(); i++) y += x[i] * weights[i];
103 y = std::inner_product(x.begin(), x.end(), weights.begin(), y);
104
105 if (out != nullptr) { // if out variable is provided
106 *out = y;
107 }
108
109 return activation(y); // quantizer: apply ADALINE threshold function
110 }
111
119 double fit(const std::vector<double> &x, const int &y) {
120 if (!check_size_match(x)) {
121 return 0;
122 }
123
124 /* output of the model with current weights */
125 int p = predict(x);
126 int prediction_error = y - p; // error in estimation
127 double correction_factor = eta * prediction_error;
128
129 /* update each weight, the last weight is the bias term */
130 for (int i = 0; i < x.size(); i++) {
131 weights[i] += correction_factor * x[i];
132 }
133 weights[x.size()] += correction_factor; // update bias
134
135 return correction_factor;
136 }
137
144 template <size_t N>
145 void fit(std::array<std::vector<double>, N> const &X,
146 std::array<int, N> const &Y) {
147 double avg_pred_error = 1.f;
148
149 int iter = 0;
150 for (iter = 0; (iter < MAX_ITER) && (avg_pred_error > accuracy);
151 iter++) {
152 avg_pred_error = 0.f;
153
154 // perform fit for each sample
155 for (int i = 0; i < N; i++) {
156 double err = fit(X[i], Y[i]);
157 avg_pred_error += std::abs(err);
158 }
159 avg_pred_error /= N;
160
161 // Print updates every 200th iteration
162 // if (iter % 100 == 0)
163 std::cout << "\tIter " << iter << ": Training weights: " << *this
164 << "\tAvg error: " << avg_pred_error << std::endl;
165 }
166
167 if (iter < MAX_ITER) {
168 std::cout << "Converged after " << iter << " iterations."
169 << std::endl;
170 } else {
171 std::cout << "Did not converge after " << iter << " iterations."
172 << std::endl;
173 }
174 }
175
186 int activation(double x) { return x > 0 ? 1 : -1; }
187
188 private:
196 bool check_size_match(const std::vector<double> &x) {
197 if (x.size() != (weights.size() - 1)) {
198 std::cerr << __func__ << ": "
199 << "Number of features in x does not match the feature "
200 "dimension in model!"
201 << std::endl;
202 return false;
203 }
204 return true;
205 }
206
207 const double eta;
208 const double accuracy;
209 std::vector<double> weights;
210};
211
212} // namespace machine_learning
213
215
224void test1(double eta = 0.01) {
225 adaline ada(2, eta); // 2 features
226
227 const int N = 10; // number of sample points
228
229 std::array<std::vector<double>, N> X = {
230 std::vector<double>({0, 1}), std::vector<double>({1, -2}),
231 std::vector<double>({2, 3}), std::vector<double>({3, -1}),
232 std::vector<double>({4, 1}), std::vector<double>({6, -5}),
233 std::vector<double>({-7, -3}), std::vector<double>({-8, 5}),
234 std::vector<double>({-9, 2}), std::vector<double>({-10, -15})};
235 std::array<int, N> y = {1, -1, 1, -1, -1,
236 -1, 1, 1, 1, -1}; // corresponding y-values
237
238 std::cout << "------- Test 1 -------" << std::endl;
239 std::cout << "Model before fit: " << ada << std::endl;
240
241 ada.fit<N>(X, y);
242 std::cout << "Model after fit: " << ada << std::endl;
243
244 int predict = ada.predict({5, -3});
245 std::cout << "Predict for x=(5,-3): " << predict;
246 assert(predict == -1);
247 std::cout << " ...passed" << std::endl;
248
249 predict = ada.predict({5, 8});
250 std::cout << "Predict for x=(5,8): " << predict;
251 assert(predict == 1);
252 std::cout << " ...passed" << std::endl;
253}
254
262void test2(double eta = 0.01) {
263 adaline ada(2, eta); // 2 features
264
265 const int N = 50; // number of sample points
266
267 std::array<std::vector<double>, N> X;
268 std::array<int, N> Y{}; // corresponding y-values
269
270 // generate sample points in the interval
271 // [-range2/100 , (range2-1)/100]
272 int range = 500; // sample points full-range
273 int range2 = range >> 1; // sample points half-range
274 for (int i = 0; i < N; i++) {
275 double x0 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
276 double x1 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
277 X[i] = std::vector<double>({x0, x1});
278 Y[i] = (x0 + 3. * x1) > -1 ? 1 : -1;
279 }
280
281 std::cout << "------- Test 2 -------" << std::endl;
282 std::cout << "Model before fit: " << ada << std::endl;
283
284 ada.fit(X, Y);
285 std::cout << "Model after fit: " << ada << std::endl;
286
287 int N_test_cases = 5;
288 for (int i = 0; i < N_test_cases; i++) {
289 double x0 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
290 double x1 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
291
292 int predict = ada.predict({x0, x1});
293
294 std::cout << "Predict for x=(" << x0 << "," << x1 << "): " << predict;
295
296 int expected_val = (x0 + 3. * x1) > -1 ? 1 : -1;
297 assert(predict == expected_val);
298 std::cout << " ...passed" << std::endl;
299 }
300}
301
313void test3(double eta = 0.01) {
314 adaline ada(6, eta); // 2 features
315
316 const int N = 100; // number of sample points
317
318 std::array<std::vector<double>, N> X;
319 std::array<int, N> Y{}; // corresponding y-values
320
321 // generate sample points in the interval
322 // [-range2/100 , (range2-1)/100]
323 int range = 200; // sample points full-range
324 int range2 = range >> 1; // sample points half-range
325 for (int i = 0; i < N; i++) {
326 double x0 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
327 double x1 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
328 double x2 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
329 X[i] = std::vector<double>({x0, x1, x2, x0 * x0, x1 * x1, x2 * x2});
330 Y[i] = ((x0 * x0) + (x1 * x1) + (x2 * x2)) <= 1.f ? 1 : -1;
331 }
332
333 std::cout << "------- Test 3 -------" << std::endl;
334 std::cout << "Model before fit: " << ada << std::endl;
335
336 ada.fit(X, Y);
337 std::cout << "Model after fit: " << ada << std::endl;
338
339 int N_test_cases = 5;
340 for (int i = 0; i < N_test_cases; i++) {
341 double x0 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
342 double x1 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
343 double x2 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
344
345 int predict = ada.predict({x0, x1, x2, x0 * x0, x1 * x1, x2 * x2});
346
347 std::cout << "Predict for x=(" << x0 << "," << x1 << "," << x2
348 << "): " << predict;
349
350 int expected_val = ((x0 * x0) + (x1 * x1) + (x2 * x2)) <= 1.f ? 1 : -1;
351 assert(predict == expected_val);
352 std::cout << " ...passed" << std::endl;
353 }
354}
355
357int main(int argc, char **argv) {
358 std::srand(std::time(nullptr)); // initialize random number generator
359
360 double eta = 0.1; // default value of eta
361 if (argc == 2) { // read eta value from commandline argument if present
362 eta = strtof(argv[1], nullptr);
363 }
364
365 test1(eta);
366
367 std::cout << "Press ENTER to continue..." << std::endl;
368 std::cin.get();
369
370 test2(eta);
371
372 std::cout << "Press ENTER to continue..." << std::endl;
373 std::cin.get();
374
375 test3(eta);
376
377 return 0;
378}
adaline(int num_features, const double eta=0.01f, const double accuracy=1e-5)
const double eta
learning rate of the algorithm
std::vector< double > weights
weights of the neural network
double fit(const std::vector< double > &x, const int &y)
void fit(std::array< std::vector< double >, N > const &X, std::array< int, N > const &Y)
const double accuracy
model fit convergence accuracy
int predict(const std::vector< double > &x, double *out=nullptr)
bool check_size_match(const std::vector< double > &x)
friend std::ostream & operator<<(std::ostream &out, const adaline &ada)
static void test2()
Self-implementations, 2nd test.
static void test1()
Self-test implementations, 1st test.
int main()
Main function.
constexpr int MAX_ITER
static void test3()
A* search algorithm