See More

/*++ Revision History: Date: Jun 28, 2024. Author: Rajas Chavadekar. Desc: Created. --*/ #include "SVCClassifier.h" #include #include #include "GlobalData.h" SVCClassifier::SVCClassifier(BaseVectorizer* pvec) { pVec = pvec; } SVCClassifier::~SVCClassifier() { delete pVec; } double SVCClassifier::predict_margin(const std::vector& features) const { double margin = bias; for (ml_size_t i = 0; i < features.size(); ++i) { margin += weights[i] * features[i]; } return margin; } void SVCClassifier::setHyperparameters(std::string hyperparameters) { std::string token; std::istringstream tokenStream(hyperparameters); // "bias=0.0,epochs=15,learning_rate=0.01,l1_regularization_param=0.005,l2_regularization_param=0.0" bias = 0.0; epochs = 15; learning_rate = 0.01; l1_regularization_param = 0.005; l2_regularization_param = 0.0; pVec->ngrams = 1; while (std::getline(tokenStream, token, ',')) { std::istringstream pairStream(token); std::string key; double value; if (std::getline(pairStream, key, '=') && pairStream >> value) { cout << key << " = " << value << endl; if (key == "ngrams") { pVec->ngrams = value; } else if (key == "minfrequency") { minfrequency = value; } else if (key == "bias") { bias = value; } else if (key == "epochs") { epochs = value; } else if (key == "learning_rate") { learning_rate = value; } else if (key == "l1_regularization_param") { l1_regularization_param = value; } else if (key == "l2_regularization_param") { l2_regularization_param = value; } } } } void SVCClassifier::fit(std::string abs_filepath_to_features, std::string abs_filepath_to_labels) { if (minfrequency > 0) { pVec->scanForSparseHistogram(abs_filepath_to_features, minfrequency); } pVec->fit(abs_filepath_to_features, abs_filepath_to_labels); ml_size_t num_features = pVec->word_array.size(); weights.assign(num_features, 0.0); std::vector<:shared_ptr>> sentences = pVec->sentences; std::vector labels(sentences.size()); std::ifstream label_file(abs_filepath_to_labels); std::string label; for (ml_size_t i = 0; i < labels.size(); ++i) { label_file >> labels[i]; // Convert labels to +1 or -1 for SVM labels[i] = labels[i] == 1 ? 1 : -1; } label_file.close(); for (int epoch = 0; epoch < epochs; ++epoch) { for (ml_size_t i = 0; i < sentences.size(); ++i) { std::vector features; const auto& sentence_map = sentences[i]->sentence_map; features = pVec->getFrequencies(sentence_map); double y_true = labels[i]; double margin = predict_margin(features); if (y_true * margin < 1) { for (ml_size_t j = 0; j < features.size(); ++j) { weights[j] += learning_rate * (y_true * features[j] - l1_regularization_param * (weights[j] > 0 ? 1 : -1) - 2 * l2_regularization_param * weights[j]); } bias += learning_rate * y_true; } else { for (ml_size_t j = 0; j < features.size(); ++j) { weights[j] += learning_rate * (-l1_regularization_param * (weights[j] > 0 ? 1 : -1) - 2 * l2_regularization_param * weights[j]); } } } } } Prediction SVCClassifier::predict(std::string sentence, bool preprocess) { GlobalData vars; Prediction result; std::vector processed_input = pVec->buildSentenceVector(sentence, preprocess); std::vector feature_vector = pVec->getSentenceFeatures(processed_input); double margin = predict_margin(feature_vector); result.probability = 1.0 / (1.0 + std::exp(-margin)); if (margin > 0) { result.label = vars.POS; } else { result.label = vars.NEG; } return result; } void SVCClassifier::predict(std::string abs_filepath_to_features, std::string abs_filepath_to_labels, bool preprocess) { std::ifstream in(abs_filepath_to_features); std::ofstream out(abs_filepath_to_labels); std::string feature_input; if (!in) { std::cerr << "ERROR: Cannot open features file.\n"; return; } if (!out) { std::cerr << "ERROR: Cannot open labels file.\n"; return; } #ifdef BENCHMARK double sumduration = 0.0; double sumstrlen = 0.0; ml_size_t num_rows = 0; #endif while (getline(in, feature_input)) { #ifdef BENCHMARK auto start = std::chrono::high_resolution_clock::now(); #endif Prediction result = predict(feature_input, preprocess); #ifdef BENCHMARK auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration duration = end - start; double milliseconds = duration.count(); sumduration += milliseconds; sumstrlen += feature_input.length(); num_rows++; #endif out << result.label << "," << result.probability << std::endl; } #ifdef BENCHMARK double avgduration = sumduration / num_rows; cout << "Average Time per Text = " << avgduration << " ms" << endl; double avgstrlen = sumstrlen / num_rows; cout << "Average Length of Text (chars) = " << avgstrlen << endl; #endif in.close(); out.close(); } void SVCClassifier::save(const std::string& filename) const { std::ofstream outFile(filename, std::ios::binary); if (!outFile.is_open()) { std::cerr << "Failed to open file for writing." << std::endl; return; } pVec->save(outFile); ml_size_t weight_size = weights.size(); outFile.write(reinterpret_cast(&weight_size), sizeof(weight_size)); outFile.write(reinterpret_cast(weights.data()), weight_size * sizeof(double)); outFile.write(reinterpret_cast(&bias), sizeof(bias)); outFile.close(); } void SVCClassifier::load(const std::string& filename) { std::ifstream inFile(filename, std::ios::binary); if (!inFile.is_open()) { std::cerr << "Failed to open file for reading." << std::endl; return; } pVec->load(inFile); ml_size_t weight_size; inFile.read(reinterpret_cast(&weight_size), sizeof(weight_size)); weights.resize(weight_size); inFile.read(reinterpret_cast(weights.data()), weight_size * sizeof(double)); inFile.read(reinterpret_cast(&bias), sizeof(bias)); inFile.close(); }