#include "nexus_model/adapters/nexus_optim.hpp" #include "nexus_model/nexus_model.hpp" #include "nexusdata/core/ndarray.hpp" #include "nexusdata/dataset/in_memory.hpp" #include "nexusdata/dataset/split.hpp" #include "nexusdata/loading/dataloader.hpp" #include "nexusloss/nexusloss.hpp" #include "nexus_optim/algorithms/adamw.hpp" #include #include #include #include int main() { constexpr int samples = 96; constexpr int features = 8; constexpr int classes = 3; constexpr int epochs = 8; nexusdata::NDArray x(nexusdata::Shape{samples, features}, nexusdata::DType::Float32); nexusdata::NDArray y(nexusdata::Shape{samples}, nexusdata::DType::Float32); float* xf = x.data(); float* yf = y.data(); for (int n = 0; n < samples; ++n) { const int label = n % classes; yf[n] = static_cast(label); for (int f = 0; f < features; ++f) { xf[n * features + f] = (f % classes == label) ? 1.f : 0.05f * static_cast((n + f) % 5); } } nexusdata::InMemoryDataset dataset(x, y); auto split = nexusdata::random_split(dataset, {0.8, 0.2}, 7); nexusdata::DataLoaderOptions options; options.batch_size = 16; options.shuffle = true; options.seed = 7; nexusdata::DataLoader loader(split.first, options); auto model = nexus_model::Sequential({ std::make_shared(features, 16), std::make_shared(), std::make_shared(16, classes), }); nexusloss::classification::CrossEntropyLoss loss_fn; nexus_optim::ParamGroupOptions group_options; group_options.learning_rate = 1e-2; nexus_optim::AdamW::Options adam; adam.lr = 1e-2; adam.weight_decay = 1e-2; nexus_optim::AdamW optimizer({nexus_model::as_param_group(model, group_options)}, adam); float last = 0.f; for (int epoch = 0; epoch < epochs; ++epoch) { double total = 0.0; int seen = 0; for (nexusdata::Batch batch : loader) { optimizer.zero_grad(); const std::size_t batch_n = batch.inputs.shape()[0]; nexus_model::Tensor inputs = nexus_model::Tensor::zeros({batch_n, static_cast(features)}); for (std::size_t i = 0; i < inputs.numel(); ++i) inputs[i] = batch.inputs.data()[i]; nexus_model::Tensor prediction = model.forward(inputs); nexus_model::Tensor grad = nexus_model::Tensor::zeros(prediction.shape_vec()); double loss = 0.0; for (std::size_t row = 0; row < batch_n; ++row) { const std::size_t label = static_cast(batch.labels.data()[row] + 0.5f); std::span logits(prediction.data() + row * classes, classes); loss += loss_fn.forward(logits, label); const std::vector row_grad = loss_fn.backward(); for (int c = 0; c < classes; ++c) { grad[row * classes + static_cast(c)] = row_grad[static_cast(c)] / static_cast(batch_n); } } model.backward(grad); optimizer.step(); total += loss; seen += static_cast(batch_n); } last = static_cast(total / seen); std::cout << "epoch " << epoch << " loss " << last << "\n"; } return last < 0.6f ? 0 : 1; }