| /* ----------------------------------------------------------------------- *//** |
| * |
| * @file linear_svm_igd.cpp |
| * |
| * @brief Linear Support Vector Machine functions |
| * |
| *//* ----------------------------------------------------------------------- */ |
| |
| #include <dbconnector/dbconnector.hpp> |
| |
| #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<MutableArrayHandle<double> >, |
| GLMIGDState<ArrayHandle<double> >, |
| LinearSVM<GLMModel, GLMTuple > > LinearSVMIGDAlgorithm; |
| |
| typedef Loss<GLMIGDState<MutableArrayHandle<double> >, |
| GLMIGDState<ArrayHandle<double> >, |
| LinearSVM<GLMModel, GLMTuple > > LinearSVMLossAlgorithm; |
| |
| typedef Gradient<GLMIGDState<MutableArrayHandle<double> >, |
| GLMIGDState<ArrayHandle<double> >, |
| LinearSVM<GLMModel, GLMTuple > > 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<MutableArrayHandle<double> > state = args[0]; |
| |
| // initialize the state if first tuple |
| if (state.algo.numRows == 0) { |
| LinearSVM<GLMModel, GLMTuple >::epsilon = args[9].getAs<double>();; |
| LinearSVM<GLMModel, GLMTuple >::is_svc = args[10].getAs<bool>();; |
| if (!args[3].isNull()) { |
| GLMIGDState<ArrayHandle<double> > previousState = args[3]; |
| state.allocate(*this, previousState.task.dimension); |
| state = previousState; |
| } else { |
| // configuration parameters |
| uint32_t dimension = args[4].getAs<uint32_t>(); |
| state.allocate(*this, dimension); // with zeros |
| } |
| // resetting in either case |
| state.reset(); |
| state.task.stepsize = args[5].getAs<double>(); |
| const double lambda = args[6].getAs<double>(); |
| const bool isL2 = args[7].getAs<bool>(); |
| const int nTuples = args[8].getAs<int>(); |
| L1<GLMModel>::n_tuples = nTuples; |
| L2<GLMModel>::n_tuples = nTuples; |
| if (isL2) |
| L2<GLMModel>::lambda = lambda; |
| else |
| L1<GLMModel>::lambda = lambda; |
| } |
| |
| // Skip the current record if args[1] (features) contains NULL values, |
| // or args[2] is NULL |
| try { |
| args[1].getAs<MappedColumnVector>(); |
| } 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<MappedColumnVector>().memoryHandle(), |
| state.task.dimension); |
| tuple.depVar = args[2].getAs<double>(); |
| tuple.weight = args[11].getAs<double>(); |
| |
| // Now do the transition step |
| // apply IGD with regularization |
| L2<GLMModel>::scaling(state.algo.incrModel, state.task.stepsize); |
| LinearSVMIGDAlgorithm::transition(state, tuple); |
| L1<GLMModel>::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<MutableArrayHandle<double> > stateLeft = args[0]; |
| GLMIGDState<ArrayHandle<double> > 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<MutableArrayHandle<double> > state = args[0]; |
| |
| // Aggregates that haven't seen any data just return Null. |
| if (state.algo.numRows == 0) { return Null(); } |
| |
| state.algo.loss += L2<GLMModel>::loss(state.task.model); |
| state.algo.loss += L1<GLMModel>::loss(state.task.model); |
| L2<GLMModel>::gradient(state.task.model, state.algo.gradient); |
| L1<GLMModel>::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<ArrayHandle<double> > stateLeft = args[0]; |
| GLMIGDState<ArrayHandle<double> > 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<ArrayHandle<double> > state = args[0]; |
| |
| AnyType tuple; |
| tuple << state.task.model |
| << static_cast<double>(state.algo.loss / state.algo.numRows) |
| << state.algo.gradient.norm() |
| << static_cast<int64_t>(state.algo.numRows); |
| |
| return tuple; |
| } |
| |
| } // namespace convex |
| |
| } // namespace modules |
| |
| } // namespace madlib |