Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expectile loss function #3760

Merged
merged 32 commits into from
Mar 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8388dd4
Added expectile loss
MoniFarsang Feb 7, 2022
189d655
Merge branch 'master' of https://github.com/VowpalWabbit/vowpal_wabbit
MoniFarsang Feb 23, 2022
4aafe86
Removed RevertingWeight method from expectile loss
MoniFarsang Feb 23, 2022
87c4fed
Modified expectile loss
MoniFarsang Mar 1, 2022
1892eda
Small fix
MoniFarsang Mar 1, 2022
c287d6b
Fixed parenthesis
MoniFarsang Mar 3, 2022
05de0f1
Merge branch 'master' into master
MoniFarsang Mar 3, 2022
0ec13e0
Consts for expectile loss and shared_data
MoniFarsang Mar 3, 2022
de67224
Added expectile loss to parse_args, removed scalar from getLoss
MoniFarsang Mar 15, 2022
f0dc662
Merge branch 'master' into master
MoniFarsang Mar 15, 2022
e2ad7d5
Added comment to loss_functions
MoniFarsang Mar 15, 2022
39e2781
No error message when expectile is used with quantile_tau
MoniFarsang Mar 15, 2022
c320b81
Defined separated inline functions of the squared loss which are call…
MoniFarsang Mar 15, 2022
ccb3b18
Created namespace for the inline squared loss functions
MoniFarsang Mar 15, 2022
0cf0ca6
Merge branch 'master' into master
MoniFarsang Mar 16, 2022
ff8d3e3
Added a unit test for expectile loss
MoniFarsang Mar 17, 2022
e8122b5
Added expectile option
MoniFarsang Mar 17, 2022
5a69362
Added expectile option to help_cbadf.stdout
MoniFarsang Mar 18, 2022
99e2174
Fixed linebreaks in test files
MoniFarsang Mar 18, 2022
5950144
Merge branch 'master' into master
MoniFarsang Mar 18, 2022
a5c3d52
Merge branch 'master' into master
MoniFarsang Mar 18, 2022
dbe28a1
Moved comments
MoniFarsang Mar 18, 2022
52bec99
Expectile methods use the ternary operator and have the same order as…
MoniFarsang Mar 18, 2022
92e2fb7
Added multiple tests for expectile loss
MoniFarsang Mar 18, 2022
6c1ee72
Merge branch 'master' into master
zwd-ms Mar 22, 2022
7a950ee
Fixed formatting issues
MoniFarsang Mar 22, 2022
c091699
Fixed formatting issues in loss_functions.cc and test file
MoniFarsang Mar 22, 2022
2c59f7f
Merge branch 'master' into master
MoniFarsang Mar 22, 2022
6dcafd3
Modified the parameter to be prefixed and private, added comment
MoniFarsang Mar 23, 2022
4f1bba5
Merge branch 'master' of https://github.com/MoniFarsang/vowpal_wabbit
MoniFarsang Mar 23, 2022
799d1c2
Update loss_functions.cc
zwd-ms Mar 23, 2022
ec45d5e
Merge branch 'master' into master
zwd-ms Mar 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions test/train-sets/ref/help.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ Example Options:
--sort_features Turn this on to disregard order in which features have been defined.
This will lead to smaller cache sizes (type: bool)
--loss_function arg Specify the loss function to be used, uses squared by default
(type: str, default: squared, choices {classic, hinge, logistic,
poisson, quantile, squared})
(type: str, default: squared, choices {classic, expectile, hinge,
logistic, poisson, quantile, squared})
--quantile_tau arg Parameter \tau associated with Quantile loss. Defaults to 0.5
(type: float, default: 0.5)
--l1 arg L_1 lambda (type: float, default: 0)
Expand Down
4 changes: 2 additions & 2 deletions test/train-sets/ref/help_cbadf.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ Example Options:
--sort_features Turn this on to disregard order in which features have been defined.
This will lead to smaller cache sizes (type: bool)
--loss_function arg Specify the loss function to be used, uses squared by default
(type: str, default: squared, choices {classic, hinge, logistic,
poisson, quantile, squared})
(type: str, default: squared, choices {classic, expectile, hinge,
logistic, poisson, quantile, squared})
--quantile_tau arg Parameter \tau associated with Quantile loss. Defaults to 0.5
(type: float, default: 0.5)
--l1 arg L_1 lambda (type: float, default: 0)
Expand Down
165 changes: 165 additions & 0 deletions test/unit_test/loss_functions_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,168 @@ BOOST_AUTO_TEST_CASE(squared_loss_test)

VW::finish(vw);
}

BOOST_AUTO_TEST_CASE(expectile_loss_label_is_greater_than_prediction_test)
{
auto& vw = *VW::initialize("--quiet");
const std::string loss_type("expectile");
constexpr float parameter(0.4f);

auto loss = getLossFunction(vw, loss_type, parameter);
shared_data sd;
sd.min_label = 0.0f;
sd.max_label = 1.0f;
constexpr float eta = 0.1f; // learning rate
constexpr float weight = 1.0f; // example weight

constexpr float label = 0.5f;
constexpr float prediction = 0.4f;
constexpr float update_scale = eta * weight;
constexpr float pred_per_update = 1.0f; // Use dummy value here, see gd.cc for details.

BOOST_CHECK_EQUAL(loss_type, loss->getType());
BOOST_CHECK_CLOSE(parameter, loss->getParameter(), FLOAT_TOL);

BOOST_CHECK_CLOSE(0.004f, loss->getLoss(&sd, prediction, label), FLOAT_TOL);
BOOST_CHECK_CLOSE(0.007688365f, loss->getUpdate(prediction, label, update_scale, pred_per_update), FLOAT_TOL);
BOOST_CHECK_CLOSE(0.008f, loss->getUnsafeUpdate(prediction, label, update_scale), FLOAT_TOL);

BOOST_CHECK_CLOSE(0.0064f, loss->getSquareGrad(prediction, label), FLOAT_TOL);
BOOST_CHECK_CLOSE(-0.08f, loss->first_derivative(&sd, prediction, label), FLOAT_TOL);
BOOST_CHECK_CLOSE(0.8f, loss->second_derivative(&sd, prediction, label), FLOAT_TOL);

VW::finish(vw);
}

BOOST_AUTO_TEST_CASE(expectile_loss_prediction_is_greater_than_label_test)
{
auto& vw = *VW::initialize("--quiet");
const std::string loss_type("expectile");
constexpr float parameter(0.4f);

auto loss = getLossFunction(vw, loss_type, parameter);
shared_data sd;
sd.min_label = 0.0f;
sd.max_label = 1.0f;
constexpr float eta = 0.1f; // learning rate
constexpr float weight = 1.0f; // example weight

constexpr float label = 0.4f;
constexpr float prediction = 0.5f;
constexpr float update_scale = eta * weight;
constexpr float pred_per_update = 1.0f; // Use dummy value here, see gd.cc for details.

BOOST_CHECK_EQUAL(loss_type, loss->getType());
BOOST_CHECK_CLOSE(parameter, loss->getParameter(), FLOAT_TOL);

BOOST_CHECK_CLOSE(0.006f, loss->getLoss(&sd, prediction, label), FLOAT_TOL);
BOOST_CHECK_CLOSE(-0.011307956f, loss->getUpdate(prediction, label, update_scale, pred_per_update), FLOAT_TOL);
BOOST_CHECK_CLOSE(-0.012f, loss->getUnsafeUpdate(prediction, label, update_scale), FLOAT_TOL);

BOOST_CHECK_CLOSE(0.0144f, loss->getSquareGrad(prediction, label), FLOAT_TOL);
BOOST_CHECK_CLOSE(0.12f, loss->first_derivative(&sd, prediction, label), FLOAT_TOL);
BOOST_CHECK_CLOSE(1.2f, loss->second_derivative(&sd, prediction, label), FLOAT_TOL);

VW::finish(vw);
}

BOOST_AUTO_TEST_CASE(expectile_loss_parameter_equals_zero_test)
{
auto& vw = *VW::initialize("--quiet");
const std::string loss_type("expectile");
constexpr float parameter(0.0f);

auto loss = getLossFunction(vw, loss_type, parameter);
shared_data sd;
sd.min_label = 0.0f;
sd.max_label = 1.0f;
constexpr float eta = 0.1f; // learning rate
constexpr float weight = 1.0f; // example weight

constexpr float label = 0.5f;
constexpr float prediction = 0.4f;
constexpr float update_scale = eta * weight;
constexpr float pred_per_update = 1.0f; // Use dummy value here, see gd.cc for details.

BOOST_CHECK_EQUAL(loss_type, loss->getType());
BOOST_CHECK_CLOSE(parameter, loss->getParameter(), FLOAT_TOL);

BOOST_CHECK_CLOSE(0.0f, loss->getLoss(&sd, prediction, label), FLOAT_TOL);
BOOST_CHECK_CLOSE(0.0f, loss->getUpdate(prediction, label, update_scale, pred_per_update), FLOAT_TOL);
BOOST_CHECK_CLOSE(0.0f, loss->getUnsafeUpdate(prediction, label, update_scale), FLOAT_TOL);

BOOST_CHECK_CLOSE(0.0f, loss->getSquareGrad(prediction, label), FLOAT_TOL);
BOOST_CHECK_CLOSE(0.0f, loss->first_derivative(&sd, prediction, label), FLOAT_TOL);
BOOST_CHECK_CLOSE(0.0f, loss->second_derivative(&sd, prediction, label), FLOAT_TOL);

VW::finish(vw);
}

BOOST_AUTO_TEST_CASE(expectile_loss_parameter_equals_one_test)
{
auto& vw = *VW::initialize("--quiet");
const std::string loss_type("expectile");
constexpr float parameter(1.0f);

auto loss = getLossFunction(vw, loss_type, parameter);
shared_data sd;
sd.min_label = 0.0f;
sd.max_label = 1.0f;
constexpr float eta = 0.1f; // learning rate
constexpr float weight = 1.0f; // example weight

constexpr float label = 0.5f;
constexpr float prediction = 0.4f;
constexpr float update_scale = eta * weight;
constexpr float pred_per_update = 1.0f; // Use dummy value here, see gd.cc for details.

BOOST_CHECK_EQUAL(loss_type, loss->getType());
BOOST_CHECK_CLOSE(parameter, loss->getParameter(), FLOAT_TOL);

BOOST_CHECK_CLOSE(0.01f, loss->getLoss(&sd, prediction, label), FLOAT_TOL);
BOOST_CHECK_CLOSE(0.01812692f, loss->getUpdate(prediction, label, update_scale, pred_per_update), FLOAT_TOL);
BOOST_CHECK_CLOSE(0.02f, loss->getUnsafeUpdate(prediction, label, update_scale), FLOAT_TOL);

BOOST_CHECK_CLOSE(0.04f, loss->getSquareGrad(prediction, label), FLOAT_TOL);
BOOST_CHECK_CLOSE(-0.2f, loss->first_derivative(&sd, prediction, label), FLOAT_TOL);
BOOST_CHECK_CLOSE(2.0f, loss->second_derivative(&sd, prediction, label), FLOAT_TOL);

VW::finish(vw);
}

BOOST_AUTO_TEST_CASE(compare_expectile_loss_with_squared_loss_test)
{
auto& vw = *VW::initialize("--quiet");
const std::string loss_type_expectile("expectile");
const std::string loss_type_squared("squared");
constexpr float parameter(0.3f);

auto loss_expectile = getLossFunction(vw, loss_type_expectile, parameter);
auto loss_squared = getLossFunction(vw, loss_type_squared);
shared_data sd;
sd.min_label = 0.0f;
sd.max_label = 1.0f;
constexpr float eta = 0.1f; // learning rate
constexpr float weight = 1.0f; // example weight

constexpr float label = 0.5f;
constexpr float prediction = 0.4f;
constexpr float update_scale = eta * weight;
constexpr float pred_per_update = 1.0f; // Use dummy value here, see gd.cc for details.

BOOST_CHECK_CLOSE(loss_expectile->getLoss(&sd, prediction, label),
loss_squared->getLoss(&sd, prediction, label) * parameter, FLOAT_TOL);
BOOST_CHECK_CLOSE(loss_expectile->getUpdate(prediction, label, update_scale, pred_per_update),
loss_squared->getUpdate(prediction, label, update_scale * parameter, pred_per_update), FLOAT_TOL);
BOOST_CHECK_CLOSE(loss_expectile->getUnsafeUpdate(prediction, label, update_scale),
loss_squared->getUnsafeUpdate(prediction, label, update_scale * parameter), FLOAT_TOL);

BOOST_CHECK_CLOSE(loss_expectile->getSquareGrad(prediction, label),
loss_squared->getSquareGrad(prediction, label) * parameter * parameter, FLOAT_TOL);
BOOST_CHECK_CLOSE(loss_expectile->first_derivative(&sd, prediction, label),
loss_squared->first_derivative(&sd, prediction, label) * parameter, FLOAT_TOL);
BOOST_CHECK_CLOSE(loss_expectile->second_derivative(&sd, prediction, label),
loss_squared->second_derivative(&sd, prediction, label) * parameter, FLOAT_TOL);

VW::finish(vw);
}