// Copyright (C) 2018 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_AUTO_LEARnING_CPP_ #define DLIB_AUTO_LEARnING_CPP_ #include "auto.h" #include "../global_optimization.h" #include "svm_c_trainer.h" #include <iostream> #include <thread> namespace dlib { normalized_function<decision_function<radial_basis_kernel<matrix<double,0,1>>>> auto_train_rbf_classifier ( std::vector<matrix<double,0,1>> x, std::vector<double> y, const std::chrono::nanoseconds max_runtime, bool be_verbose ) { const auto num_positive_training_samples = sum(mat(y)>0); const auto num_negative_training_samples = sum(mat(y)<0); DLIB_CASSERT(num_positive_training_samples >= 6 && num_negative_training_samples >= 6, "You must provide at least 6 examples of each class to this training routine."); // make sure requires clause is not broken DLIB_CASSERT(is_binary_classification_problem(x,y) == true, "\tdecision_function svm_c_trainer::train(x,y)" << "\n\t invalid inputs were given to this function" << "\n\t x.size(): " << x.size() << "\n\t y.size(): " << y.size() << "\n\t is_binary_classification_problem(x,y): " << is_binary_classification_problem(x,y) ); randomize_samples(x,y); using kernel_type = radial_basis_kernel<matrix<double,0,1>>; normalized_function<decision_function<kernel_type>> df; // let the normalizer learn the mean and standard deviation of the samples df.normalizer.train(x); for (auto& samp : x) samp = df.normalizer(samp); std::mutex m; auto cross_validation_score = [&](const double gamma, const double c1, const double c2) { svm_c_trainer<kernel_type> trainer; trainer.set_kernel(kernel_type(gamma)); trainer.set_c_class1(c1); trainer.set_c_class2(c2); // Finally, perform 6-fold cross validation and then print and return the results. matrix<double> result = cross_validate_trainer(trainer, x, y, 6); if (be_verbose) { std::lock_guard<std::mutex> lock(m); std::cout << "gamma: " << std::setw(11) << gamma << " c1: " << std::setw(11) << c1 << " c2: " << std::setw(11) << c2 << " cross validation accuracy: " << result << std::flush; } // return the f1 score plus a penalty for picking large parameter settings // since those are, a priori less likely to generalize. return 2*prod(result)/sum(result) - std::max(c1,c2)/1e12 - gamma/1e8; }; if (be_verbose) std::cout << "Searching for best RBF-SVM training parameters..." << std::endl; auto result = find_max_global( default_thread_pool(), cross_validation_score, {1e-5, 1e-5, 1e-5}, // lower bound constraints on gamma, c1, and c2, respectively {100, 1e6, 1e6}, // upper bound constraints on gamma, c1, and c2, respectively max_runtime); double best_gamma = result.x(0); double best_c1 = result.x(1); double best_c2 = result.x(2); if (be_verbose) { std::cout << " best cross-validation score: " << result.y << std::endl; std::cout << " best gamma: " << best_gamma << " best c1: " << best_c1 << " best c2: "<< best_c2 << std::endl; } svm_c_trainer<kernel_type> trainer; trainer.set_kernel(kernel_type(best_gamma)); trainer.set_c_class1(best_c1); trainer.set_c_class2(best_c2); if (be_verbose) std::cout << "Training final classifier with best parameters..." << std::endl; df.function = trainer.train(x,y); return df; } // ---------------------------------------------------------------------------------------- normalized_function<multiclass_linear_decision_function<linear_kernel<matrix<double,0,1>>, unsigned long>> auto_train_multiclass_svm_linear_classifier ( std::vector<matrix<double,0,1>> x, std::vector<unsigned long> y, const std::chrono::nanoseconds max_runtime, bool be_verbose ) { const auto labels = select_all_distinct_labels(y); for (const auto label : labels) { const auto num_samples = sum(mat(y) == label); DLIB_CASSERT(num_samples >= 3, "You must provide at least 3 examples of each class to this training routine, however, label " << label << " has only " << num_samples << " examples."); } DLIB_ASSERT(is_learning_problem(x,y) == true); randomize_samples(x, y); using kernel_type = linear_kernel<matrix<double,0,1>>; normalized_function<multiclass_linear_decision_function<kernel_type, unsigned long>> df; // let the normalizer learn the mean and standard deviation of the samples df.normalizer.train(x); for (auto& samp : x) samp = df.normalizer(samp); auto cross_validation_score = [&](const double c) { svm_multiclass_linear_trainer<kernel_type, unsigned long> trainer; trainer.set_c(c); trainer.set_epsilon(0.01); trainer.set_max_iterations(100); trainer.set_num_threads(std::thread::hardware_concurrency()); // Finally, perform 3-fold cross validation and then print and return the confusion matrix. const auto cm = cross_validate_multiclass_trainer(trainer, x, y, 3); const double accuracy = sum(diag(cm)) / sum(cm); if (be_verbose) { std::cout << "C: " << c << " cross validation accuracy: " << accuracy << '\n'; std::cout << cm << std::endl; } return accuracy; }; if (be_verbose) std::cout << "Searching for best Multiclass linear SVM training parameters..." << std::endl; const auto result = find_max_global(cross_validation_score, 1e-3, 1000, max_runtime); const double best_c = result.x(0); if (be_verbose) { std::cout << " best cross-validation score: " << result.y << std::endl; std::cout << " best C: " << best_c << std::endl; } svm_multiclass_linear_trainer<kernel_type, unsigned long> trainer; trainer.set_num_threads(std::thread::hardware_concurrency()); trainer.set_c(best_c); if (be_verbose) std::cout << "Training final classifier with best parameters..." << std::endl; df.function = trainer.train(x, y); return df; } normalized_function<multiclass_linear_decision_function<linear_kernel<matrix<float,0,1>>, unsigned long>> auto_train_multiclass_svm_linear_classifier ( const std::vector<matrix<float,0,1>>& x, std::vector<unsigned long> y, const std::chrono::nanoseconds max_runtime, bool be_verbose ) { std::vector<matrix<double,0,1>> samples; for (const auto& samp : x) samples.push_back(matrix_cast<double>(samp)); const auto temp = auto_train_multiclass_svm_linear_classifier(samples, y, max_runtime, be_verbose); normalized_function<multiclass_linear_decision_function<linear_kernel<matrix<float,0,1>>, unsigned long>> df; df.normalizer.train(x); df.function.labels = temp.function.labels; df.function.weights = matrix_cast<float>(temp.function.weights); df.function.b = matrix_cast<float>(temp.function.b); return df; } } #endif // DLIB_AUTO_LEARnING_CPP_