#include #include #include #include #include #include #include #include using namespace psemek::ml; using namespace psemek::random; using namespace psemek::math; test_case(ml_neural__net_activation) { generator rng; for (std::size_t i = 0; i < activation_type_values().size(); ++i) { auto a = static_cast(i); for (std::size_t j = 0; j < 64; ++j) { double const eps = 1e-6; double x = uniform(rng, -10.0, 10.0); double value = activation(x, a); double exact_derivative = activation_derivative(value, a); double approx_derivative = (activation(x + eps, a) - value) / eps; expect_close(exact_derivative, approx_derivative, 1e-6); } } } test_case(ml_neural__net_gradient) { generator rng; for (std::size_t iteration = 0; iteration < 64; ++iteration) { std::vector sizes; sizes.resize(uniform(rng, 2, 5)); for (auto & s : sizes) s = uniform(rng, 1, 50); std::vector activations(sizes.size() - 1); for (auto & a : activations) a = static_cast(uniform(rng, 0, static_cast(activation_type_values().size()) - 1)); neural_net nn(std::move(sizes), std::move(activations)); randomize_normal(nn, rng); std::vector input(nn.layer_sizes().front()); for (auto & x : input) x = uniform(rng); std::vector output(nn.layer_sizes().back()); for (auto & x : output) x = uniform(rng); neural_net_learner learner; learner.apply(nn, input); learner.backpropagate_l2(nn, output); double const eps = 1e-6; neural_net_evaluator evaluator; for (std::size_t i = 0; i < nn.weights().size(); ++i) { double old = nn.weights()[i]; nn.weights()[i] -= eps; double v0 = l2_loss(evaluator.apply(nn, input), output); nn.weights()[i] += 2.0 * eps; double v1 = l2_loss(evaluator.apply(nn, input), output); nn.weights()[i] = old; double numeric_gradient = (v1 - v0) / 2.0 / eps; expect_close(numeric_gradient, learner.gradient()[i], 1e-4); } } } test_case(ml_neural__net_arg__gradient) { generator rng; for (std::size_t iteration = 0; iteration < 64; ++iteration) { std::vector sizes; sizes.resize(uniform(rng, 2, 5)); for (auto & s : sizes) s = uniform(rng, 1, 50); std::vector activations(sizes.size() - 1); for (auto & a : activations) a = static_cast(uniform(rng, 0, static_cast(activation_type_values().size()) - 1)); neural_net nn(std::move(sizes), std::move(activations)); randomize_normal(nn, rng); std::vector input(nn.layer_sizes().front()); for (auto & x : input) x = uniform(rng); std::vector output(nn.layer_sizes().back()); for (auto & x : output) x = uniform(rng); neural_net_learner learner; learner.apply(nn, input); learner.backpropagate_l2(nn, output); double const eps = 1e-6; neural_net_evaluator evaluator; for (std::size_t i = 0; i < input.size(); ++i) { double old = input[i]; input[i] -= eps; double v0 = l2_loss(evaluator.apply(nn, input), output); input[i] += 2.0 * eps; double v1 = l2_loss(evaluator.apply(nn, input), output); input[i] = old; double numeric_gradient = (v1 - v0) / 2.0 / eps; expect_close(numeric_gradient, learner.arg_gradient()[i], 1e-4); } } }