From 69454d9487cdf09eac49f39ad7f64b965cd1504a Mon Sep 17 00:00:00 2001 From: Henry Gouk Date: Tue, 7 Aug 2018 10:06:42 +1200 Subject: [PATCH] Implementation of hinge loss for binary classification (#3477) --- amalgamation/xgboost-all0.cc | 1 + doc/parameter.rst | 1 + src/objective/hinge.cc | 71 +++++++++++++++++++++++++++++++ src/objective/objective.cc | 1 + tests/cpp/objective/test_hinge.cc | 20 +++++++++ 5 files changed, 94 insertions(+) create mode 100644 src/objective/hinge.cc create mode 100644 tests/cpp/objective/test_hinge.cc diff --git a/amalgamation/xgboost-all0.cc b/amalgamation/xgboost-all0.cc index ee09f0e85..26fbd1b7a 100644 --- a/amalgamation/xgboost-all0.cc +++ b/amalgamation/xgboost-all0.cc @@ -20,6 +20,7 @@ #include "../src/objective/regression_obj.cc" #include "../src/objective/multiclass_obj.cc" #include "../src/objective/rank_obj.cc" +#include "../src/objective/hinge.cc" // gbms #include "../src/gbm/gbm.cc" diff --git a/doc/parameter.rst b/doc/parameter.rst index 01b724fac..d6ce40aed 100644 --- a/doc/parameter.rst +++ b/doc/parameter.rst @@ -248,6 +248,7 @@ Specify the learning task and the corresponding learning objective. The objectiv - ``reg:logistic``: logistic regression - ``binary:logistic``: logistic regression for binary classification, output probability - ``binary:logitraw``: logistic regression for binary classification, output score before logistic transformation + - ``binary:hinge``: hinge loss for binary classification. This makes predictions of 0 or 1, rather than producing probabilities. - ``gpu:reg:linear``, ``gpu:reg:logistic``, ``gpu:binary:logistic``, ``gpu:binary:logitraw``: versions of the corresponding objective functions evaluated on the GPU; note that like the GPU histogram algorithm, they can only be used when the entire training session uses the same dataset diff --git a/src/objective/hinge.cc b/src/objective/hinge.cc new file mode 100644 index 000000000..5b04d215a --- /dev/null +++ b/src/objective/hinge.cc @@ -0,0 +1,71 @@ +/*! + * Copyright 2018 by Contributors + * \file hinge.cc + * \brief Provides an implementation of the hinge loss function + * \author Henry Gouk + */ +#include +#include "../common/math.h" + +namespace xgboost { +namespace obj { + +DMLC_REGISTRY_FILE_TAG(hinge); + +class HingeObj : public ObjFunction { + public: + HingeObj() = default; + + void Configure( + const std::vector > &args) override { + // This objective does not take any parameters + } + + void GetGradient(HostDeviceVector *preds, + const MetaInfo &info, + int iter, + HostDeviceVector *out_gpair) override { + CHECK_NE(info.labels_.size(), 0U) << "label set cannot be empty"; + CHECK_EQ(preds->Size(), info.labels_.size()) + << "labels are not correctly provided" + << "preds.size=" << preds->Size() + << ", label.size=" << info.labels_.size(); + auto& preds_h = preds->HostVector(); + + out_gpair->Resize(preds_h.size()); + auto& gpair = out_gpair->HostVector(); + + for (size_t i = 0; i < preds_h.size(); ++i) { + auto y = info.labels_[i] * 2.0 - 1.0; + bst_float p = preds_h[i]; + bst_float w = info.GetWeight(i); + bst_float g, h; + if (p * y < 1.0) { + g = -y * w; + h = w; + } else { + g = 0.0; + h = std::numeric_limits::min(); + } + gpair[i] = GradientPair(g, h); + } + } + + void PredTransform(HostDeviceVector *io_preds) override { + std::vector &preds = io_preds->HostVector(); + for (auto& p : preds) { + p = p > 0.0 ? 1.0 : 0.0; + } + } + + const char* DefaultEvalMetric() const override { + return "error"; + } +}; + +XGBOOST_REGISTER_OBJECTIVE(HingeObj, "binary:hinge") +.describe("Hinge loss. Expects labels to be in [0,1f]") +.set_body([]() { return new HingeObj(); }); + +} // namespace obj +} // namespace xgboost diff --git a/src/objective/objective.cc b/src/objective/objective.cc index bf860a480..ebc68ebca 100644 --- a/src/objective/objective.cc +++ b/src/objective/objective.cc @@ -36,5 +36,6 @@ DMLC_REGISTRY_LINK_TAG(regression_obj); #endif DMLC_REGISTRY_LINK_TAG(multiclass_obj); DMLC_REGISTRY_LINK_TAG(rank_obj); +DMLC_REGISTRY_LINK_TAG(hinge); } // namespace obj } // namespace xgboost diff --git a/tests/cpp/objective/test_hinge.cc b/tests/cpp/objective/test_hinge.cc new file mode 100644 index 000000000..fe3005bca --- /dev/null +++ b/tests/cpp/objective/test_hinge.cc @@ -0,0 +1,20 @@ +// Copyright by Contributors +#include +#include + +#include "../helpers.h" + +TEST(Objective, HingeObj) { + xgboost::ObjFunction * obj = xgboost::ObjFunction::Create("binary:hinge"); + std::vector > args; + obj->Configure(args); + xgboost::bst_float eps = std::numeric_limits::min(); + CheckObjFunction(obj, + {-1.0f, -0.5f, 0.5f, 1.0f, -1.0f, -0.5f, 0.5f, 1.0f}, + { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f}, + { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}, + { 0.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 0.0f}, + { eps, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, eps }); + + ASSERT_NO_THROW(obj->DefaultEvalMetric()); +}