| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #pragma once |
|
|
| #ifndef _USE_MATH_DEFINES |
| #define _USE_MATH_DEFINES |
| #endif |
|
|
| #include <cmath> |
| #include <memory> |
| #include <vector> |
| #include "backprop.hpp" |
| #include "memory.hpp" |
| #include "model.hpp" |
| #include "networks.hpp" |
| #include "tensor.hpp" |
|
|
| namespace neuroflow { |
|
|
| |
| |
| |
| class LossFunctions { |
| public: |
| |
| static float cross_entropy(const Tensor& pred, const Tensor& target) { |
| size_t batch = pred.shape_[0]; |
| size_t classes = pred.shape_[1]; |
| |
| const float* p = pred.as_fp32(); |
| const float* t = target.as_fp32(); |
| |
| float loss = 0.0f; |
| for (size_t b = 0; b < batch; ++b) { |
| |
| std::vector<float> probs(classes); |
| float max_val = p[b * classes]; |
| for (size_t c = 1; c < classes; ++c) { |
| max_val = std::max(max_val, p[b * classes + c]); |
| } |
| |
| float sum = 0.0f; |
| for (size_t c = 0; c < classes; ++c) { |
| probs[c] = std::exp(p[b * classes + c] - max_val); |
| sum += probs[c]; |
| } |
| for (size_t c = 0; c < classes; ++c) { |
| probs[c] /= sum; |
| } |
| |
| |
| for (size_t c = 0; c < classes; ++c) { |
| if (t[b * classes + c] > 0) { |
| loss -= t[b * classes + c] * std::log(std::max(probs[c], 1e-7f)); |
| } |
| } |
| } |
| |
| return loss / batch; |
| } |
| |
| |
| static float mse(const Tensor& pred, const Tensor& target) { |
| size_t n = pred.numel(); |
| |
| const float* p = pred.as_fp32(); |
| const float* t = target.as_fp32(); |
| |
| float loss = 0.0f; |
| for (size_t i = 0; i < n; ++i) { |
| float diff = p[i] - t[i]; |
| loss += diff * diff; |
| } |
| |
| return loss / n; |
| } |
| |
| |
| static void softmax(Tensor& x) { |
| if (x.shape_.size() != 2) return; |
| |
| size_t batch = x.shape_[0]; |
| size_t classes = x.shape_[1]; |
| float* p = x.as_fp32(); |
| |
| for (size_t b = 0; b < batch; ++b) { |
| float max_val = p[b * classes]; |
| for (size_t c = 1; c < classes; ++c) { |
| max_val = std::max(max_val, p[b * classes + c]); |
| } |
| |
| float sum = 0.0f; |
| for (size_t c = 0; c < classes; ++c) { |
| p[b * classes + c] = std::exp(p[b * classes + c] - max_val); |
| sum += p[b * classes + c]; |
| } |
| |
| for (size_t c = 0; c < classes; ++c) { |
| p[b * classes + c] /= sum; |
| } |
| } |
| } |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| class GradientCalculator { |
| public: |
| |
| struct LinearGradients { |
| Tensor weight_grad; |
| Tensor input_grad; |
| }; |
| |
| static LinearGradients linear_backward( |
| const Tensor& input, |
| const Tensor& output_grad, |
| const Tensor& weight |
| ) { |
| LinearGradients grads; |
| |
| size_t batch = input.shape_[0]; |
| size_t in_f = input.shape_[1]; |
| size_t out_f = output_grad.shape_[1]; |
| |
| grads.weight_grad = Tensor({out_f, in_f}, QuantType::FP32); |
| grads.input_grad = Tensor({batch, in_f}, QuantType::FP32); |
| |
| const float* inp = input.as_fp32(); |
| const float* og = output_grad.as_fp32(); |
| const float* w = weight.as_fp32(); |
| float* wg = grads.weight_grad.as_fp32(); |
| float* ig = grads.input_grad.as_fp32(); |
| |
| |
| |
| for (size_t i = 0; i < out_f; ++i) { |
| for (size_t j = 0; j < in_f; ++j) { |
| float sum = 0.0f; |
| for (size_t b = 0; b < batch; ++b) { |
| sum += og[b * out_f + i] * inp[b * in_f + j]; |
| } |
| wg[i * in_f + j] = sum / batch; |
| } |
| } |
| |
| |
| |
| for (size_t b = 0; b < batch; ++b) { |
| for (size_t j = 0; j < in_f; ++j) { |
| float sum = 0.0f; |
| for (size_t i = 0; i < out_f; ++i) { |
| sum += og[b * out_f + i] * w[i * in_f + j]; |
| } |
| ig[b * in_f + j] = sum; |
| } |
| } |
| |
| return grads; |
| } |
| |
| |
| static Tensor layernorm_backward( |
| const Tensor& input, |
| const Tensor& output_grad, |
| float eps = 1e-5f |
| ) { |
| size_t batch = input.shape_[0]; |
| size_t dim = input.shape_[1]; |
| |
| Tensor input_grad({batch, dim}, QuantType::FP32); |
| |
| const float* inp = input.as_fp32(); |
| const float* og = output_grad.as_fp32(); |
| float* ig = input_grad.as_fp32(); |
| |
| for (size_t b = 0; b < batch; ++b) { |
| |
| float mean = 0.0f; |
| for (size_t d = 0; d < dim; ++d) { |
| mean += inp[b * dim + d]; |
| } |
| mean /= dim; |
| |
| float var = 0.0f; |
| for (size_t d = 0; d < dim; ++d) { |
| float diff = inp[b * dim + d] - mean; |
| var += diff * diff; |
| } |
| var /= dim; |
| float std = std::sqrt(var + eps); |
| |
| |
| float sum_grad = 0.0f; |
| float sum_grad_x = 0.0f; |
| for (size_t d = 0; d < dim; ++d) { |
| float normalized = (inp[b * dim + d] - mean) / std; |
| sum_grad += og[b * dim + d]; |
| sum_grad_x += og[b * dim + d] * normalized; |
| } |
| |
| for (size_t d = 0; d < dim; ++d) { |
| float normalized = (inp[b * dim + d] - mean) / std; |
| ig[b * dim + d] = (og[b * dim + d] - sum_grad / dim - normalized * sum_grad_x / dim) / std; |
| } |
| } |
| |
| return input_grad; |
| } |
| |
| |
| static Tensor gelu_backward(const Tensor& input, const Tensor& output_grad) { |
| size_t n = input.numel(); |
| |
| Tensor input_grad(input.shape_, QuantType::FP32); |
| |
| const float* inp = input.as_fp32(); |
| const float* og = output_grad.as_fp32(); |
| float* ig = input_grad.as_fp32(); |
| |
| for (size_t i = 0; i < n; ++i) { |
| float x = inp[i]; |
| float gelu_grad = 0.5f * (1.0f + std::erf(x / std::sqrt(2.0f))) |
| + x * std::exp(-x * x / 2.0f) / std::sqrt(2.0f * 3.14159265358979323846f); |
| ig[i] = og[i] * gelu_grad; |
| } |
| |
| return input_grad; |
| } |
| }; |
|
|
| |
| |
| |
| class Optimizer { |
| public: |
| float lr; |
| float weight_decay; |
| |
| Optimizer(float learning_rate = 0.001f, float wd = 0.0f) |
| : lr(learning_rate), weight_decay(wd) {} |
| |
| |
| void sgd_step(Tensor& param, const Tensor& grad) { |
| if (param.shape_ != grad.shape_) return; |
| |
| float* p = param.as_fp32(); |
| const float* g = grad.as_fp32(); |
| |
| for (size_t i = 0; i < param.numel(); ++i) { |
| p[i] -= lr * (g[i] + weight_decay * p[i]); |
| } |
| } |
| |
| |
| struct AdamState { |
| Tensor m; |
| Tensor v; |
| int t = 0; |
| }; |
| |
| AdamState create_adam_state(const Tensor& param) { |
| AdamState state; |
| state.m = Tensor(param.shape_, QuantType::FP32); |
| state.v = Tensor(param.shape_, QuantType::FP32); |
| memset(state.m.as_fp32(), 0, state.m.numel() * sizeof(float)); |
| memset(state.v.as_fp32(), 0, state.v.numel() * sizeof(float)); |
| return state; |
| } |
| |
| |
| void adam_step(Tensor& param, const Tensor& grad, AdamState& state, |
| float beta1 = 0.9f, float beta2 = 0.999f, float eps = 1e-8f) { |
| if (param.shape_ != grad.shape_) return; |
| |
| state.t++; |
| |
| float* p = param.as_fp32(); |
| const float* g = grad.as_fp32(); |
| float* m = state.m.as_fp32(); |
| float* v = state.v.as_fp32(); |
| |
| for (size_t i = 0; i < param.numel(); ++i) { |
| m[i] = beta1 * m[i] + (1 - beta1) * g[i]; |
| v[i] = beta2 * v[i] + (1 - beta2) * g[i] * g[i]; |
| |
| float m_hat = m[i] / (1 - std::pow(beta1, state.t)); |
| float v_hat = v[i] / (1 - std::pow(beta2, state.t)); |
| |
| p[i] -= lr * (m_hat / (std::sqrt(v_hat) + eps) + weight_decay * p[i]); |
| } |
| } |
| }; |
|
|
| |
| |
| |
| |
| |
| class OnlineLearner { |
| public: |
| NeuroFlowModel& model; |
| FullTrainer trainer; |
| |
| OnlineLearner(NeuroFlowModel& m, float lr = 0.01f) |
| : model(m), trainer(m, lr) {} |
| |
| |
| struct LearnResult { |
| float initial_loss; |
| float final_loss; |
| float loss_reduction; |
| int steps; |
| }; |
| |
| LearnResult learn_step( |
| const Tensor& input, |
| const Tensor& target, |
| int num_steps = 5, |
| bool use_memory = true |
| ) { |
| LearnResult result; |
| result.steps = num_steps; |
| |
| |
| NeuroFlowModel::Output output = model.forward(input); |
| result.initial_loss = LossFunctions::mse(output.output, target); |
| |
| |
| for (int step = 0; step < num_steps; ++step) { |
| auto step_result = trainer.train_step(input, target); |
| (void)step_result; |
| } |
| |
| |
| NeuroFlowModel::Output final_output = model.forward(input); |
| result.final_loss = LossFunctions::mse(final_output.output, target); |
| result.loss_reduction = result.initial_loss - result.final_loss; |
| |
| return result; |
| } |
| |
| |
| LearnResult few_shot_adapt( |
| const std::vector<Tensor>& examples, |
| const std::vector<Tensor>& targets, |
| int num_steps = 10 |
| ) { |
| LearnResult result; |
| |
| if (examples.empty()) { |
| result.initial_loss = result.final_loss = 0.0f; |
| return result; |
| } |
| |
| |
| size_t batch = examples.size(); |
| Tensor input({batch, examples[0].shape_[1]}, QuantType::FP32); |
| Tensor target({batch, targets[0].shape_[1]}, QuantType::FP32); |
| |
| float* inp = input.as_fp32(); |
| float* tgt = target.as_fp32(); |
| |
| for (size_t b = 0; b < batch; ++b) { |
| memcpy(inp + b * examples[0].numel(), |
| examples[b].as_fp32(), |
| examples[b].numel() * sizeof(float)); |
| memcpy(tgt + b * targets[0].numel(), |
| targets[b].as_fp32(), |
| targets[b].numel() * sizeof(float)); |
| } |
| |
| return learn_step(input, target, num_steps, true); |
| } |
| |
| |
| void meta_learn_step( |
| const Tensor& support_input, |
| const Tensor& support_target, |
| const Tensor& query_input, |
| const Tensor& query_target, |
| int inner_steps = 5 |
| ) { |
| |
| LearnResult inner = learn_step(support_input, support_target, inner_steps, true); |
| |
| |
| NeuroFlowModel::Output query_pred = model.forward(query_input); |
| float query_loss = LossFunctions::mse(query_pred.output, query_target); |
| |
| |
| |
| model.memory->consolidate(query_input); |
| } |
| }; |
|
|
| |
| |
| |
| inline void test_online_learning() { |
| printf("\n=== Online Learning Test ===\n"); |
| |
| |
| NeuroFlowModel::Config cfg; |
| cfg.input_dim = 512; |
| cfg.hidden_dim = 256; |
| cfg.output_dim = 10; |
| NeuroFlowModel model(cfg); |
| OnlineLearner learner(model, 0.01f); |
| |
| |
| Tensor input(std::vector<size_t>{1, 512}, QuantType::FP32); |
| Tensor target(std::vector<size_t>{1, 10}, QuantType::FP32); |
| |
| |
| float* inp = input.as_fp32(); |
| float* tgt = target.as_fp32(); |
| for (size_t i = 0; i < 512; ++i) inp[i] = (static_cast<float>(rand()) / RAND_MAX - 0.5f) * 0.1f; |
| for (size_t i = 0; i < 10; ++i) tgt[i] = (i == 3) ? 1.0f : 0.0f; |
| |
| |
| OnlineLearner::LearnResult result = learner.learn_step(input, target, 5, true); |
| |
| printf(" Single sample adaptation:\n"); |
| printf(" Initial loss: %.4f\n", result.initial_loss); |
| printf(" Final loss: %.4f\n", result.final_loss); |
| printf(" Loss reduction: %.4f\n", result.loss_reduction); |
| |
| |
| std::vector<Tensor> few_inputs(5); |
| std::vector<Tensor> few_targets(5); |
| |
| for (size_t i = 0; i < 5; ++i) { |
| few_inputs[i] = Tensor(std::vector<size_t>{1, 512}, QuantType::FP32); |
| few_targets[i] = Tensor(std::vector<size_t>{1, 10}, QuantType::FP32); |
| |
| float* fi = few_inputs[i].as_fp32(); |
| float* ft = few_targets[i].as_fp32(); |
| for (size_t j = 0; j < 512; ++j) fi[j] = (static_cast<float>(rand()) / RAND_MAX - 0.5f) * 0.1f; |
| for (size_t j = 0; j < 10; ++j) ft[j] = (j == (i % 10)) ? 1.0f : 0.0f; |
| } |
| |
| result = learner.few_shot_adapt(few_inputs, few_targets, 10); |
| |
| printf(" Few-shot adaptation (5 samples):\n"); |
| printf(" Initial loss: %.4f\n", result.initial_loss); |
| printf(" Final loss: %.4f\n", result.final_loss); |
| |
| |
| printf(" Memory consolidation test:\n"); |
| float mem_before = model.memory->memory_bank.as_fp32()[0]; |
| |
| Tensor batch(std::vector<size_t>{32, cfg.hidden_dim}, QuantType::FP32); |
| model.memory->consolidate(batch); |
| |
| float mem_after = model.memory->memory_bank.as_fp32()[0]; |
| printf(" Memory bank change: %.6f\n", std::abs(mem_after - mem_before)); |
| printf(" LTP rate: %.4f\n", model.memory->ltp_rate); |
| |
| printf("=== Test Complete ===\n\n"); |
| } |
|
|
| } |