/* ----------------------------------------------------------------------- *//** * * @file linear_svm_igd.cpp * * @brief Linear Support Vector Machine functions * *//* ----------------------------------------------------------------------- */ #include #include "linear_svm_igd.hpp" #include "task/linear_svm.hpp" #include "task/l1.hpp" #include "task/l2.hpp" #include "algo/igd.hpp" #include "algo/loss.hpp" #include "algo/gradient.hpp" #include "type/tuple.hpp" #include "type/model.hpp" #include "type/state.hpp" namespace madlib { namespace modules { namespace convex { // This 2 classes contain public static methods that can be called typedef IGD >, GLMIGDState >, LinearSVM > LinearSVMIGDAlgorithm; typedef Loss >, GLMIGDState >, LinearSVM > LinearSVMLossAlgorithm; typedef Gradient >, GLMIGDState >, LinearSVM > LinearSVMGradientAlgorithm; /** * @brief Perform the linear support vector machine transition step * * Called for each tuple. */ AnyType linear_svm_igd_transition::run(AnyType &args) { // The real state. // For the first tuple: args[0] is nothing more than a marker that // indicates that we should do some initial operations. // For other tuples: args[0] holds the computation state until last tuple GLMIGDState > state = args[0]; // initialize the state if first tuple if (state.algo.numRows == 0) { LinearSVM::epsilon = args[9].getAs();; LinearSVM::is_svc = args[10].getAs();; if (!args[3].isNull()) { GLMIGDState > previousState = args[3]; state.allocate(*this, previousState.task.dimension); state = previousState; } else { // configuration parameters uint32_t dimension = args[4].getAs(); state.allocate(*this, dimension); // with zeros } // resetting in either case state.reset(); state.task.stepsize = args[5].getAs(); const double lambda = args[6].getAs(); const bool isL2 = args[7].getAs(); const int nTuples = args[8].getAs(); L1::n_tuples = nTuples; L2::n_tuples = nTuples; if (isL2) L2::lambda = lambda; else L1::lambda = lambda; } // Skip the current record if args[1] (features) contains NULL values, // or args[2] is NULL try { args[1].getAs(); } catch (const ArrayWithNullException &e) { return args[0]; } if (args[2].isNull()) return args[0]; // tuple using madlib::dbal::eigen_integration::MappedColumnVector; // each tuple can be weighted - this can be combination of the sample weight // and the class weight. Calling function is responsible for combining the two // into a single tuple weight. The default value for this parameter is 1, set // into the definition of "tuple". // The weight is used to increase the value of a particular tuple for the online // learning. The weight is not used for the loss computation. GLMTuple tuple; tuple.indVar.rebind(args[1].getAs().memoryHandle(), state.task.dimension); tuple.depVar = args[2].getAs(); tuple.weight = args[11].getAs(); // Now do the transition step // apply IGD with regularization L2::scaling(state.algo.incrModel, state.task.stepsize); LinearSVMIGDAlgorithm::transition(state, tuple); L1::clipping(state.algo.incrModel, state.task.stepsize); // evaluate objective function and its gradient // at the old model - state.task.model LinearSVMLossAlgorithm::transition(state, tuple); LinearSVMGradientAlgorithm::transition(state, tuple); state.algo.numRows ++; return state; } /** * @brief Perform the preliminary aggregation function: Merge transition states */ AnyType linear_svm_igd_merge::run(AnyType &args) { GLMIGDState > stateLeft = args[0]; GLMIGDState > stateRight = args[1]; // We first handle the trivial case where this function is called with one // of the states being the initial state if (stateLeft.algo.numRows == 0) { return stateRight; } else if (stateRight.algo.numRows == 0) { return stateLeft; } // Merge states together LinearSVMIGDAlgorithm::merge(stateLeft, stateRight); LinearSVMLossAlgorithm::merge(stateLeft, stateRight); LinearSVMGradientAlgorithm::merge(stateLeft, stateRight); // The following numRows update, cannot be put above, because the model // averaging depends on their original values stateLeft.algo.numRows += stateRight.algo.numRows; return stateLeft; } /** * @brief Perform the linear support vector machine final step */ AnyType linear_svm_igd_final::run(AnyType &args) { // We request a mutable object. Depending on the backend, this might perform // a deep copy. GLMIGDState > state = args[0]; // Aggregates that haven't seen any data just return Null. if (state.algo.numRows == 0) { return Null(); } state.algo.loss += L2::loss(state.task.model); state.algo.loss += L1::loss(state.task.model); L2::gradient(state.task.model, state.algo.gradient); L1::gradient(state.task.model, state.algo.gradient); // finalizing LinearSVMIGDAlgorithm::final(state); elog(NOTICE, "loss = %e, |gradient| = %e, |model| = %e\n", (double) state.algo.loss, state.algo.gradient.norm(), state.task.model.norm()); return state; } /** * @brief Return the difference in RMSE between two states */ AnyType internal_linear_svm_igd_distance::run(AnyType &args) { GLMIGDState > stateLeft = args[0]; GLMIGDState > stateRight = args[1]; return std::abs((stateLeft.algo.loss - stateRight.algo.loss) / stateLeft.algo.loss); } /** * @brief Return the coefficients and diagnostic statistics of the state */ AnyType internal_linear_svm_igd_result::run(AnyType &args) { GLMIGDState > state = args[0]; AnyType tuple; tuple << state.task.model << static_cast(state.algo.loss / state.algo.numRows) << state.algo.gradient.norm() << static_cast(state.algo.numRows); return tuple; } } // namespace convex } // namespace modules } // namespace madlib