####################
# Author: James Hickey
#
# Series of tests for gbm loss functionality
#
####################
context("Error checking for GBM loss methods")
test_that("Error thrown responses not vector of doubles", {
# Given offset, weights, predictions and default dist
N <- 100
dist <- gbm_dist()
offset <- runif(N)
weights <- runif(N)
preds <- runif(N)
# When responses are not a vector of doubles
resps <- rep("1", N)
resps_2 <- NA
resps_3 <- Inf
resps_4 <- rep(NaN, N)
# Then error thrown when trying to calculate the loss
expect_error(loss(resps, preds, weights, offset, dist))
expect_error(loss(resps_2, preds, weights, offset, dist))
expect_error(loss(resps_3, preds, weights, offset, dist))
expect_error(loss(resps_4, preds, weights, offset, dist))
})
test_that("Error thrown when predictions not vector of doubles", {
# Given offset, weights, responses and default dist
N <- 100
dist <- gbm_dist()
offset <- runif(N)
weights <- runif(N)
resps <- runif(N)
# When predictions are not a vector of doubles
preds <- rep("1", N)
preds_2 <- NA
preds_3 <- Inf
preds_4 <- rep(NaN, N)
# Then error thrown when trying to calculate the loss
expect_error(loss(resps, preds, weights, offset, dist))
expect_error(loss(resps, preds_2, weights, offset, dist))
expect_error(loss(resps, preds_3, weights, offset, dist))
expect_error(loss(resps, preds_4, weights, offset, dist))
})
test_that("Error thrown when weights not vector of doubles", {
# Given offset, responses, predictions and default dist
N <- 100
dist <- gbm_dist()
offset <- runif(N)
resps <- runif(N)
preds <- runif(N)
# When weights are not a vector of doubles
weights <- rep("1", N)
weights_2 <- NA
weights_3 <- Inf
weights_4 <- rep(NaN, N)
# Then error thrown when trying to calculate the loss
expect_error(loss(resps, preds, weights, offset, dist))
expect_error(loss(resps, preds, weights_2, offset, dist))
expect_error(loss(resps, preds, weights_3, offset, dist))
expect_error(loss(resps, preds, weights_4, offset, dist))
})
test_that("Error thrown when offset not vector of doubles", {
# Given responses, weights, predictions and default dist
N <- 100
dist <- gbm_dist()
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
# When offsets are not a vector of doubles
offset <- rep("1", N)
offset_2 <- NA
offset_3 <- Inf
offset_4 <- rep(NaN, N)
# Then error thrown when trying to calculate the loss
expect_error(loss(resps, preds, weights, offset, dist))
expect_error(loss(resps, preds, weights, offset_2, dist))
expect_error(loss(resps, preds, weights, offset_3, dist))
expect_error(loss(resps, preds, weights, offset_4, dist))
})
test_that("Error thrown when baseline not vector of doubles", {
# Given responses, weights, offset, predictions and default dist
N <- 100
dist <- gbm_dist()
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
# When baseline are not a vector of doubles
baseline <- rep("1", N)
baseline_2 <- NA
baseline_3 <- Inf
baseline_4 <- rep(NaN, N)
# Then error thrown when trying to calculate the loss
expect_error(loss(resps, preds, weights, offset, dist, baseline))
expect_error(loss(resps, preds, weights, offset, dist, baseline_2))
expect_error(loss(resps, preds, weights, offset, dist, baseline_3))
expect_error(loss(resps, preds, weights, offset, dist, baseline_4))
})
test_that("Error thrown when distribution is not a GBMDist", {
# Given responses, weights, offset, predictions and default dist
N <- 100
dist <- gbm_dist()
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
# When dist obj is not GBMDist
class(dist) <- ""
# Then error thrown when trying to calculate the loss
expect_error(loss(resps, preds, weights, offset, dist))
})
test_that("Error thrown when length of responses, predictions, weights and baseline not the same", {
# Given responses, weights, offset, predictions and a baselines
# of varying lengths
N <- 100
N2 <- 102
dist <- gbm_dist()
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
baseline <- runif(N)
resps_2 <- runif(N2)
weights_2 <- runif(N2)
preds_2 <- runif(N2)
offset_2 <- runif(N2)
baseline_2 <- runif(N2)
# Then error thrown when trying to calculate the loss if
# length of responses, predictions, weights and baselines not the same
expect_error(loss(resps_2, preds, weights, offset, dist, baseline))
expect_error(loss(resps, preds_2, weights, offset, dist, baseline))
expect_error(loss(resps, preds, weights_2, offset, dist, baseline))
expect_error(loss(resps, preds_2, weights, offset_2, dist, baseline))
expect_error(loss(resps, preds, weights, offset, dist, baseline_2))
})
test_that("Error thrown when length of offset is not the same as predictions", {
# Given responses, weights, predictions and default dist
N <- 100
dist <- gbm_dist()
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
# When offset has different number of elements to preds
offset <- runif(N+1)
# Then error thrown when trying to calculate the loss
expect_error(loss(resps, preds, weights, offset, dist))
})
test_that("Error thrown when distribution object not recognised - default method", {
# Given responses, weights, predictions and offset
N <- 100
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
# When distribution not recognised
dist <- gbm_dist()
class(dist) <- c("WeirdGBMDist", "GBMDist")
# Then error thrown when trying to calculate the loss
expect_error(loss(resps, preds, weights, offset, dist))
})
test_that("Error thrown when distribution is CoxPH", {
# Given responses, weights, predictions, offset and
# CoxPH dist
N <- 100
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
dist <- gbm_dist("CoxPH")
# Then error thrown when trying to calculate the loss
expect_error(loss(resps, preds, weights, offset, dist))
})
test_that("Error thrown when distribution is Gamma", {
# Given responses, weights, predictions, offset and
# Gamma dist
N <- 100
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
dist <- gbm_dist("Gamma")
# Then error thrown when trying to calculate the loss
expect_error(loss(resps, preds, weights, offset, dist))
})
test_that("Error thrown when distribution is Huberized", {
# Given responses, weights, predictions, offset and
# Huberized dist
N <- 100
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
dist <- gbm_dist("Huberized")
# Then error thrown when trying to calculate the loss
expect_error(loss(resps, preds, weights, offset, dist))
})
test_that("Error thrown when distribution is Quantile", {
# Given responses, weights, predictions, offset and
# Quantile dist
N <- 100
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
dist <- gbm_dist("Quantile")
# Then error thrown when trying to calculate the loss
expect_error(loss(resps, preds, weights, offset, dist))
})
test_that("Error thrown when distribution is TDist", {
# Given responses, weights, predictions, offset and
# TDist dist
N <- 100
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
dist <- gbm_dist("TDist")
# Then error thrown when trying to calculate the loss
expect_error(loss(resps, preds, weights, offset, dist))
})
test_that("Error thrown when distribution is Tweedie", {
# Given responses, weights, predictions, offset and
# Tweedie dist
N <- 100
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
dist <- gbm_dist("Tweedie")
# Then error thrown when trying to calculate the loss
expect_error(loss(resps, preds, weights, offset, dist))
})
test_that("Error thrown if Pairwise but group_index is NULL", {
# Given responses, weights, predictions, offset and
# Pairwise dist but group_index not specified
N <- 100
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
dist <- gbm_dist("Pairwise", group="query")
# Then error thrown when trying to calculate the loss
expect_error(loss(resps, preds, weights, offset, dist))
})
context("Check loss calculation correct for various distributions")
test_that("Correctly calculates AdaBoost loss", {
# Given responses, weights, predictions, offset, baseline and
# AdaBoost dist
N <- 100
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
baseline <- runif(N)
dist <- gbm_dist("AdaBoost")
# When calculting loss
calc_loss <- loss(resps, preds, weights, offset, dist, baseline)
# Then it is correct
preds <- preds + offset
loss_true <- weighted.mean(exp(-(2*resps-1)*preds), weights) - baseline
expect_equal(calc_loss, loss_true)
})
test_that("Correctly calculates Bernoulli loss", {
# Given responses, weights, predictions, offset, baseline and
# Bernoulli dist
N <- 100
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
baseline <- runif(N)
dist <- gbm_dist("Bernoulli")
# When calculting loss
calc_loss <- loss(resps, preds, weights, offset, dist, baseline)
# Then it is correct
preds <- preds + offset
loss_true <- -2*weighted.mean(resps*preds - log(1+exp(preds)), weights) - baseline
expect_equal(calc_loss, loss_true)
})
test_that("Correctly calculates Gaussian loss", {
# Given responses, weights, predictions, offset, baseline and
# Gaussian dist
N <- 100
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
baseline <- runif(N)
dist <- gbm_dist("Gaussian")
# When calculting loss
calc_loss <- loss(resps, preds, weights, offset, dist, baseline)
# Then it is correct
preds <- preds + offset
loss_true <- weighted.mean((resps - preds)^2, weights) - baseline
expect_equal(calc_loss, loss_true)
})
test_that("Correctly calculates Laplace loss", {
# Given responses, weights, predictions, offset, baseline and
# Laplace dist
N <- 100
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
baseline <- runif(N)
dist <- gbm_dist("Laplace")
# When calculting loss
calc_loss <- loss(resps, preds, weights, offset, dist, baseline)
# Then it is correct
preds <- preds + offset
loss_true <- weighted.mean(abs(resps-preds), weights) - baseline
expect_equal(calc_loss, loss_true)
})
test_that("Correctly calculates Pairwise loss - ndcg", {
skip("Skipping pairwise")
# Given responses, weights, predictions, offset, baseline and
# Given data and a fitted pairwise
# create query groups, with an average size of 25 items each
N <- 1000
num.queries <- floor(N/25)
query <- sample(1:num.queries, N, replace=TRUE)
# X1 is a variable determined by query group only
query.level <- runif(num.queries)
X1 <- query.level[query]
# X2 varies with each item
X2 <- runif(N)
# X3 is uncorrelated with target
X3 <- runif(N)
# The target
Y <- X1 + X2
# Add some random noise to X2 that is correlated with
# queries, but uncorrelated with items
X2 <- X2 + scale(runif(num.queries))[query]
# Add some random noise to target
SNR <- 5 # signal-to-noise ratio
sigma <- sqrt(var(Y)/SNR)
Y <- Y + runif(N, 0, sigma)
data <- data.frame(Y, query=query, X1, X2, X3)
dist <- gbm_dist("Pairwise", metric="ndcg", group="query")
params <- training_params(num_trees = 2000, num_train = nrow(data),
id=seq_len(nrow(data)),
interaction_depth = 3)
fit <- gbmt(Y~X1+X2+X3, # formula
data=data, # dataset
distribution=dist,
train_params=params,
keep_gbm_data=TRUE, # store copy of input data in model
cv_folds=5, # number of cross validation folds
is_verbose = FALSE , # don't print progress
par_details=gbmParallel())
baseline <- runif(length(fit$fit))
# When calculting loss
set.seed(1)
calc_loss <- loss(Y, fit$fit, fit$gbm_data_obj$weights, fit$gbm_data_obj$offset, fit$distribution, baseline)
# Then it is correct
preds <- fit$fit + fit$gbm_data_obj$offset
set.seed(1)
loss_true <- (1 - perf_pairwise(Y, preds, fit$distribution$group_index, dist$metric,
fit$gbm_data_obj$weights, fit$distribution$max_rank)) - baseline
expect_equal(calc_loss, loss_true) # tolerance for random tie breaking
})
test_that("Correctly calculates Pairwise loss - conc", {
skip("Skipping pairwise")
# Given responses, weights, predictions, offset, baseline and
# Given data and a fitted pairwise
# create query groups, with an average size of 25 items each
N <- 1000
num.queries <- floor(N/25)
query <- sample(1:num.queries, N, replace=TRUE)
# X1 is a variable determined by query group only
query.level <- runif(num.queries)
X1 <- query.level[query]
# X2 varies with each item
X2 <- runif(N)
# X3 is uncorrelated with target
X3 <- runif(N)
# The target
Y <- X1 + X2
# Add some random noise to X2 that is correlated with
# queries, but uncorrelated with items
X2 <- X2 + scale(runif(num.queries))[query]
# Add some random noise to target
SNR <- 5 # signal-to-noise ratio
sigma <- sqrt(var(Y)/SNR)
Y <- Y + runif(N, 0, sigma)
data <- data.frame(Y, query=query, X1, X2, X3)
dist <- gbm_dist("Pairwise", metric="conc", group="query")
params <- training_params(num_trees = 2000, num_train = nrow(data), id=seq_len(nrow(data)),
interaction_depth = 3)
fit <- gbmt(Y~X1+X2+X3, # formula
data=data, # dataset
distribution=dist,
train_params=params,
keep_gbm_data=TRUE, # store copy of input data in model
cv_folds=5, # number of cross validation folds
is_verbose = FALSE , # don't print progress
par_details=gbmParallel())
baseline <- runif(length(fit$fit))
# When calculting loss
set.seed(1)
calc_loss <- loss(Y, fit$fit, fit$gbm_data_obj$weights, fit$gbm_data_obj$offset, fit$distribution, baseline)
# Then it is correct
preds <- fit$fit + fit$gbm_data_obj$offset
set.seed(1)
loss_true <- (1 - perf_pairwise(Y, preds, fit$distribution$group_index, dist$metric,
fit$gbm_data_obj$weights, fit$distribution$max_rank)) - baseline
expect_equal(calc_loss, loss_true) # tolerance for random tie breaking
})
test_that("Correctly calculates Pairwise loss - map", {
skip("Skipping pairwise")
# Given responses, weights, predictions, offset, baseline and
# Given data and a fitted pairwise
# create query groups, with an average size of 25 items each
N <- 1000
num.queries <- floor(N/25)
query <- sample(1:num.queries, N, replace=TRUE)
# X1 is a variable determined by query group only
query.level <- runif(num.queries)
X1 <- query.level[query]
# X2 varies with each item
X2 <- runif(N)
# X3 is uncorrelated with target
X3 <- runif(N)
# The target
Y <- X1 + X2
# Add some random noise to X2 that is correlated with
# queries, but uncorrelated with items
X2 <- X2 + scale(runif(num.queries))[query]
# Add some random noise to target
SNR <- 5 # signal-to-noise ratio
sigma <- sqrt(var(Y)/SNR)
Y <- Y + runif(N, 0, sigma)
Y[Y >= 1] <- 1
Y[Y < 1] <- 0
data <- data.frame(Y, query=query, X1, X2, X3)
dist <- gbm_dist("Pairwise", metric="map", group="query")
params <- training_params(num_trees = 2000, num_train = nrow(data),
id=seq_len(nrow(data)),
interaction_depth = 3)
fit <- gbmt(Y~X1+X2+X3, # formula
data=data, # dataset
distribution=dist,
train_params=params,
keep_gbm_data=TRUE, # store copy of input data in model
cv_folds=5, # number of cross validation folds
is_verbose = FALSE , # don't print progress
par_details=gbmParallel())
baseline <- runif(length(fit$fit))
# When calculting loss
set.seed(1)
calc_loss <- loss(Y, fit$fit, fit$gbm_data_obj$weights, fit$gbm_data_obj$offset, fit$distribution, baseline)
# Then it is correct
preds <- fit$fit + fit$gbm_data_obj$offset
set.seed(1)
loss_true <- (1 - perf_pairwise(Y, preds, fit$distribution$group_index, dist$metric,
fit$gbm_data_obj$weights, fit$distribution$max_rank)) - baseline
expect_equal(calc_loss, loss_true) # tolerance for random tie breaking
})
test_that("Correctly calculates Pairwise loss - mrr", {
skip("Skipping pairwise")
# Given responses, weights, predictions, offset, baseline and
# Given data and a fitted pairwise
# create query groups, with an average size of 25 items each
N <- 1000
num.queries <- floor(N/25)
query <- sample(1:num.queries, N, replace=TRUE)
# X1 is a variable determined by query group only
query.level <- runif(num.queries)
X1 <- query.level[query]
# X2 varies with each item
X2 <- runif(N)
# X3 is uncorrelated with target
X3 <- runif(N)
# The target
Y <- X1 + X2
# Add some random noise to X2 that is correlated with
# queries, but uncorrelated with items
X2 <- X2 + scale(runif(num.queries))[query]
# Add some random noise to target
SNR <- 5 # signal-to-noise ratio
sigma <- sqrt(var(Y)/SNR)
Y <- Y + runif(N, 0, sigma)
Y[Y >= 1] <- 1
Y[Y < 1] <- 0
data <- data.frame(Y, query=query, X1, X2, X3)
dist <- gbm_dist("Pairwise", metric="mrr", group="query")
params <- training_params(num_trees = 2000, num_train = nrow(data),
id=seq_len(nrow(data)),
interaction_depth = 3)
fit <- gbmt(Y~X1+X2+X3, # formula
data=data, # dataset
distribution=dist,
train_params=params,
keep_gbm_data=TRUE, # store copy of input data in model
cv_folds=5, # number of cross validation folds
is_verbose = FALSE , # don't print progress
par_details=gbmParallel())
baseline <- runif(length(fit$fit))
# When calculting loss
set.seed(1)
calc_loss <- loss(Y, fit$fit, fit$gbm_data_obj$weights, fit$gbm_data_obj$offset, fit$distribution, baseline)
# Then it is correct
preds <- fit$fit + fit$gbm_data_obj$offset
set.seed(1)
loss_true <- (1 - perf_pairwise(Y, preds, fit$distribution$group_index, dist$metric,
fit$gbm_data_obj$weights, fit$distribution$max_rank)) - baseline
expect_equal(calc_loss, loss_true) # tolerance for random tie breaking
})
test_that("Correctly calculates Poisson loss", {
# Given responses, weights, predictions, offset, baseline and
# AdaBoost dist
N <- 100
resps <- runif(N)
weights <- runif(N)
preds <- runif(N)
offset <- runif(N)
baseline <- runif(N)
dist <- gbm_dist("Poisson")
# When calculting loss
calc_loss <- loss(resps, preds, weights, offset, dist, baseline)
# Then it is correct
preds <- preds + offset
loss_true <- -2*weighted.mean(resps*preds-exp(preds), weights) - baseline
expect_equal(calc_loss, loss_true)
})
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.