|
|
| #include "neuroflow/model.hpp"
|
|
|
|
|
| #include <algorithm>
|
| #include <cmath>
|
|
|
|
|
| #ifdef _OPENMP
|
| #include <omp.h>
|
| #endif
|
|
|
|
|
| #include "neuroflow/backprop.hpp"
|
|
|
| namespace neuroflow {
|
|
|
| namespace {
|
|
|
| constexpr size_t OMP_MIN_ITER = 1024;
|
|
|
| #ifdef _OPENMP
|
| using omp_idx_t = long long;
|
| #else
|
| using omp_idx_t = size_t;
|
| #endif
|
|
|
| Tensor linear_backward_input(const Tensor& output_grad, const Tensor& weight) {
|
| size_t batch = output_grad.shape_[0];
|
| size_t out_f = output_grad.shape_[1];
|
| size_t in_f = weight.shape_[1];
|
| Tensor input_grad({batch, in_f}, QuantType::FP32);
|
| #ifdef USE_CBLAS
|
| const float* og = output_grad.as_fp32();
|
| const float* w = weight.as_fp32();
|
| float* ig = input_grad.as_fp32();
|
| cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
|
| batch, in_f, out_f,
|
| 1.0f, og, out_f, w, in_f,
|
| 0.0f, ig, in_f);
|
| #else
|
| const float* og = output_grad.as_fp32();
|
| const float* w = weight.as_fp32();
|
| float* ig = input_grad.as_fp32();
|
| size_t total = batch * in_f;
|
| #pragma omp parallel for schedule(static) if(total >= OMP_MIN_ITER)
|
| for (omp_idx_t b = 0; b < static_cast<omp_idx_t>(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;
|
| }
|
| }
|
| #endif
|
| return input_grad;
|
| }
|
|
|
| Tensor linear_backward_weight(const Tensor& input, const Tensor& output_grad) {
|
| size_t batch = input.shape_[0];
|
| size_t in_f = input.shape_[1];
|
| size_t out_f = output_grad.shape_[1];
|
| Tensor weight_grad({out_f, in_f}, QuantType::FP32);
|
| #ifdef USE_CBLAS
|
| const float* inp = input.as_fp32();
|
| const float* og = output_grad.as_fp32();
|
| float* wg = weight_grad.as_fp32();
|
| cblas_sgemm(CblasRowMajor, CblasTrans, CblasNoTrans,
|
| out_f, in_f, batch,
|
| 1.0f / batch, og, out_f, inp, in_f,
|
| 0.0f, wg, in_f);
|
| #else
|
| const float* inp = input.as_fp32();
|
| const float* og = output_grad.as_fp32();
|
| float* wg = weight_grad.as_fp32();
|
| size_t total = out_f * in_f;
|
| #pragma omp parallel for schedule(static) if(total >= OMP_MIN_ITER)
|
| for (omp_idx_t i = 0; i < static_cast<omp_idx_t>(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;
|
| }
|
| }
|
| #endif
|
| return weight_grad;
|
| }
|
|
|
| Tensor bias_backward(const Tensor& output_grad) {
|
| size_t batch = output_grad.shape_[0];
|
| size_t dim = output_grad.shape_[1];
|
| Tensor grad({dim}, QuantType::FP32);
|
| const float* og = output_grad.as_fp32();
|
| float* g = grad.as_fp32();
|
| for (size_t j = 0; j < dim; ++j) {
|
| float sum = 0.0f;
|
| for (size_t b = 0; b < batch; ++b) sum += og[b * dim + j];
|
| g[j] = sum / batch;
|
| }
|
| return grad;
|
| }
|
|
|
| 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();
|
| const float SQRT_2_INV = 0.7071067811865476f;
|
| const float SQRT_2PI_INV = 0.3989422804014327f;
|
| #pragma omp parallel for schedule(static) if(n >= OMP_MIN_ITER)
|
| for (omp_idx_t i = 0; i < static_cast<omp_idx_t>(n); ++i) {
|
| float x = inp[i];
|
| float gelu_grad = 0.5f * (1.0f + std::erf(x * SQRT_2_INV))
|
| + x * std::exp(-0.5f * x * x) * SQRT_2PI_INV;
|
| ig[i] = og[i] * gelu_grad;
|
| }
|
| return input_grad;
|
| }
|
|
|
| Tensor layernorm_backward(const Tensor& input, const Tensor& weight, 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* w = weight.as_fp32();
|
| const float* og = output_grad.as_fp32();
|
| float* ig = input_grad.as_fp32();
|
| #pragma omp parallel for schedule(static) if(batch >= 16)
|
| for (omp_idx_t b = 0; b < static_cast<omp_idx_t>(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 inv_std = 1.0f / std::sqrt(var + eps);
|
| float sum_gn = 0.0f, sum_gnx = 0.0f;
|
| for (size_t d = 0; d < dim; ++d) {
|
| float norm = (inp[b * dim + d] - mean) * inv_std;
|
| float gn = og[b * dim + d] * w[d];
|
| sum_gn += gn;
|
| sum_gnx += gn * norm;
|
| }
|
| for (size_t d = 0; d < dim; ++d) {
|
| float norm = (inp[b * dim + d] - mean) * inv_std;
|
| float gn = og[b * dim + d] * w[d];
|
| ig[b * dim + d] = inv_std * (gn - sum_gn / dim - norm * sum_gnx / dim);
|
| }
|
| }
|
| return input_grad;
|
| }
|
|
|
|
|
| struct LayernormParamGrads {
|
| Tensor weight_grad;
|
| Tensor bias_grad;
|
| };
|
|
|
| LayernormParamGrads layernorm_param_backward(
|
| const Tensor& input, const Tensor& output_grad, float eps = 1e-5f) {
|
| size_t batch = input.shape_[0];
|
| size_t dim = input.shape_[1];
|
|
|
| LayernormParamGrads result;
|
| result.weight_grad = Tensor({dim}, QuantType::FP32);
|
| result.bias_grad = Tensor({dim}, QuantType::FP32);
|
|
|
| const float* inp = input.as_fp32();
|
| const float* og = output_grad.as_fp32();
|
| float* wg = result.weight_grad.as_fp32();
|
| float* bg = result.bias_grad.as_fp32();
|
|
|
|
|
| std::vector<float> means(batch), inv_stds(batch);
|
| 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;
|
| means[b] = mean;
|
| float var = 0.0f;
|
| for (size_t d = 0; d < dim; ++d) {
|
| float diff = inp[b * dim + d] - mean;
|
| var += diff * diff;
|
| }
|
| var /= dim;
|
| inv_stds[b] = 1.0f / std::sqrt(var + eps);
|
| }
|
|
|
|
|
|
|
| for (size_t d = 0; d < dim; ++d) {
|
| float w_sum = 0.0f, b_sum = 0.0f;
|
| for (size_t b = 0; b < batch; ++b) {
|
| float normalized = (inp[b * dim + d] - means[b]) * inv_stds[b];
|
| w_sum += og[b * dim + d] * normalized;
|
| b_sum += og[b * dim + d];
|
| }
|
| wg[d] = w_sum / batch;
|
| bg[d] = b_sum / batch;
|
| }
|
|
|
| return result;
|
| }
|
|
|
| }
|
|
|
| FullBackpropEngine::FullBackpropEngine(NeuroFlowModel& m) : model(m) {}
|
|
|
| NeuroFlowModel::Output FullBackpropEngine::forward_with_cache(const Tensor& x) {
|
| cache.input = x.clone();
|
| size_t batch = x.shape_[0];
|
|
|
| cache.input_proj_pre = model.input_proj_linear->forward(x);
|
| cache.input_proj_post = model.input_proj_norm->forward(cache.input_proj_pre);
|
| cache.h = model.input_proj_gelu->forward(cache.input_proj_post);
|
|
|
| auto sn_out = model.sn->forward(cache.h);
|
| cache.sn_gates = sn_out.gates.clone();
|
|
|
|
|
| {
|
| Tensor gh = model.sn->gate1->forward(cache.h);
|
| cache.sn_gate_h = gh.clone();
|
| TensorOps::gelu(gh);
|
| cache.sn_gate_h_post = gh.clone();
|
| }
|
|
|
| Tensor h_ecn = cache.h.clone();
|
| cache.ecn_hidden.clear();
|
| cache.ecn_pre_linear.clear();
|
| cache.ecn_pre_norm.clear();
|
| for (size_t i = 0; i < model.ecn->num_layers; ++i) {
|
| cache.ecn_pre_linear.push_back(h_ecn.clone());
|
| h_ecn = model.ecn->dlpfc_linear[i]->forward(h_ecn);
|
| cache.ecn_pre_norm.push_back(h_ecn.clone());
|
| h_ecn = model.ecn->dlpfc_norm[i]->forward(h_ecn);
|
| h_ecn = model.ecn->dlpfc_gelu[i]->forward(h_ecn);
|
| cache.ecn_hidden.push_back(h_ecn.clone());
|
| }
|
|
|
| cache.ecn_vmpfc_pre = model.ecn->vmpfc1->forward(cache.ecn_hidden.back());
|
| cache.ecn_vmpfc_d = cache.ecn_vmpfc_pre.clone();
|
| TensorOps::gelu(cache.ecn_vmpfc_d);
|
|
|
| cache.ecn_ofc_pre = model.ecn->ofc1->forward(cache.ecn_hidden.back());
|
| cache.ecn_ofc_v = cache.ecn_ofc_pre.clone();
|
| TensorOps::gelu(cache.ecn_ofc_v);
|
|
|
| cache.memory_encoded = model.memory->encode(cache.h);
|
| cache.dmn_encoded = model.dmn->mem_encoder1->forward(cache.memory_encoded);
|
| TensorOps::gelu(cache.dmn_encoded);
|
| cache.dmn_latent = model.dmn->mem_encoder2->forward(cache.dmn_encoded);
|
|
|
| cache.dmn_associations.clear();
|
| cache.dmn_head1_outs.clear();
|
| for (auto& [h1, h2] : model.dmn->association_heads) {
|
| Tensor assoc_pre = h1->forward(cache.dmn_latent);
|
| TensorOps::gelu(assoc_pre);
|
| cache.dmn_head1_outs.push_back(assoc_pre.clone());
|
| Tensor assoc = h2->forward(assoc_pre);
|
| cache.dmn_associations.push_back(assoc.clone());
|
| }
|
|
|
| cache.dmn_vision = TensorOps::concat(cache.dmn_associations, 1);
|
| cache.dmn_vision = model.dmn->future_proj1->forward(cache.dmn_vision);
|
| cache.dmn_vision = model.dmn->future_norm->forward(cache.dmn_vision);
|
| cache.dmn_vision = model.dmn->future_gelu->forward(cache.dmn_vision);
|
|
|
| auto mem_out = model.memory->forward(cache.h);
|
| cache.memory_retrieved = mem_out.retrieved;
|
|
|
| NeuroFlowModel::Output out;
|
| out.saliency = sn_out.saliency;
|
| out.gates = sn_out.gates;
|
| out.anomaly = sn_out.anomaly;
|
|
|
| const float* gates = out.gates.as_fp32();
|
| Tensor ecn_gate({batch, 1}, QuantType::FP32);
|
| Tensor dmn_gate({batch, 1}, QuantType::FP32);
|
| for (size_t i = 0; i < batch; ++i) {
|
| ecn_gate.as_fp32()[i] = gates[i * 2];
|
| dmn_gate.as_fp32()[i] = gates[i * 2 + 1];
|
| }
|
| out.ecn_gate = ecn_gate;
|
| out.dmn_gate = dmn_gate;
|
|
|
| cache.ecn_decision = model.ecn->vmpfc2->forward(cache.ecn_vmpfc_d);
|
| out.decision = cache.ecn_decision;
|
| out.value = model.ecn->ofc2->forward(cache.ecn_ofc_v);
|
|
|
| Tensor ecn_weighted({batch, model.config.output_dim}, QuantType::FP32);
|
| Tensor dmn_weighted({batch, model.config.output_dim}, QuantType::FP32);
|
| Tensor mem_weighted({batch, model.config.output_dim}, QuantType::FP32);
|
|
|
| float* ew = ecn_weighted.as_fp32();
|
| float* dw = dmn_weighted.as_fp32();
|
| float* mw = mem_weighted.as_fp32();
|
| const float* ed = out.decision.as_fp32();
|
| const float* dv = cache.dmn_vision.as_fp32();
|
| const float* mr = cache.memory_retrieved.as_fp32();
|
| const float* eg = ecn_gate.as_fp32();
|
| const float* dg = dmn_gate.as_fp32();
|
| size_t out_dim = model.config.output_dim;
|
| size_t dmn_dim = cache.dmn_vision.shape_[1];
|
| size_t mem_dim = cache.memory_retrieved.shape_[1];
|
|
|
|
|
| for (size_t i = 0; i < batch; ++i) {
|
| float egv = eg[i];
|
| float dgv = dg[i];
|
| for (size_t j = 0; j < out_dim; ++j) {
|
| ew[i * out_dim + j] = ed[i * out_dim + j] * egv;
|
| if (j < dmn_dim)
|
| dw[i * out_dim + j] = dv[i * dmn_dim + j] * dgv;
|
| if (j < mem_dim)
|
| mw[i * out_dim + j] = mr[i * mem_dim + j];
|
| }
|
| }
|
|
|
| std::vector<Tensor> to_concat = {ecn_weighted, dmn_weighted, mem_weighted};
|
| cache.combined = TensorOps::concat(to_concat, 1);
|
|
|
| cache.fused_bn = model.output_fusion_down->forward(cache.combined);
|
| cache.fused_bn_pre_norm = cache.fused_bn.clone();
|
| cache.fused_bn = model.output_fusion_bottleneck_norm->forward(cache.fused_bn);
|
| cache.fused_bn_pre_relu = cache.fused_bn.clone();
|
| TensorOps::relu(cache.fused_bn);
|
| cache.fused_pre_norm = model.output_fusion_up->forward(cache.fused_bn);
|
| cache.fused = model.output_fusion_norm->forward(cache.fused_pre_norm);
|
| out.output = cache.fused;
|
|
|
| return out;
|
| }
|
|
|
| FullBackpropEngine::Gradients FullBackpropEngine::backward(const Tensor& output_grad) {
|
| Gradients grads;
|
| size_t batch = cache.input.shape_[0];
|
| size_t out_dim = model.config.output_dim;
|
| size_t combined_dim = cache.combined.shape_[1];
|
|
|
| Tensor up_input_grad = layernorm_backward(cache.fused_pre_norm, model.output_fusion_norm->weight, output_grad);
|
|
|
|
|
| auto out_norm_pg = layernorm_param_backward(cache.fused_pre_norm, output_grad);
|
| grads.output_fusion_norm_weight_grad = out_norm_pg.weight_grad;
|
| grads.output_fusion_norm_bias_grad = out_norm_pg.bias_grad;
|
|
|
| grads.output_fusion_up_weight_grad = linear_backward_weight(cache.fused_bn, up_input_grad);
|
| grads.output_fusion_up_bias_grad = bias_backward(up_input_grad);
|
|
|
| Tensor bn_grad = linear_backward_input(up_input_grad, model.output_fusion_up->weight);
|
| {
|
| const float* pre_relu = cache.fused_bn_pre_relu.as_fp32();
|
| float* bg = bn_grad.as_fp32();
|
| size_t n = bn_grad.numel();
|
| for (size_t i = 0; i < n; ++i) {
|
| bg[i] = (pre_relu[i] > 0.0f) ? bg[i] : 0.0f;
|
| }
|
| }
|
|
|
|
|
| Tensor bn_norm_grad = layernorm_backward(cache.fused_bn_pre_norm, model.output_fusion_bottleneck_norm->weight, bn_grad);
|
|
|
|
|
| auto bn_norm_pg = layernorm_param_backward(cache.fused_bn_pre_norm, bn_grad);
|
| grads.output_fusion_bottleneck_norm_weight_grad = bn_norm_pg.weight_grad;
|
| grads.output_fusion_bottleneck_norm_bias_grad = bn_norm_pg.bias_grad;
|
|
|
| grads.output_fusion_down_weight_grad = linear_backward_weight(cache.combined, bn_norm_grad);
|
| grads.output_fusion_down_bias_grad = bias_backward(bn_norm_grad);
|
|
|
| Tensor combined_grad = linear_backward_input(bn_norm_grad, model.output_fusion_down->weight);
|
|
|
| Tensor ecn_w_grad({batch, out_dim}, QuantType::FP32);
|
| Tensor dmn_w_grad({batch, out_dim}, QuantType::FP32);
|
| Tensor mem_w_grad({batch, out_dim}, QuantType::FP32);
|
| {
|
| const float* cg = combined_grad.as_fp32();
|
| float* eg = ecn_w_grad.as_fp32();
|
| float* dg = dmn_w_grad.as_fp32();
|
| float* mg = mem_w_grad.as_fp32();
|
|
|
| for (size_t b = 0; b < batch; ++b) {
|
| for (size_t j = 0; j < out_dim; ++j) {
|
| eg[b * out_dim + j] = cg[b * combined_dim + j];
|
| dg[b * out_dim + j] = cg[b * combined_dim + out_dim + j];
|
| mg[b * out_dim + j] = cg[b * combined_dim + 2 * out_dim + j];
|
| }
|
| }
|
| }
|
|
|
| const float* gates_data = cache.sn_gates.as_fp32();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Tensor sn_gate_output_grad({batch, 2}, QuantType::FP32);
|
| {
|
| const float* ewg = ecn_w_grad.as_fp32();
|
| const float* dwg = dmn_w_grad.as_fp32();
|
|
|
| const float* ed = cache.ecn_decision.as_fp32();
|
| float* sgg = sn_gate_output_grad.as_fp32();
|
| size_t dmn_dim = cache.dmn_vision.shape_[1];
|
| const float* dv = cache.dmn_vision.as_fp32();
|
|
|
| for (size_t b = 0; b < batch; ++b) {
|
| float ecn_sum = 0.0f, dmn_sum = 0.0f;
|
| for (size_t j = 0; j < out_dim; ++j) {
|
| ecn_sum += ewg[b * out_dim + j] * ed[b * out_dim + j];
|
| }
|
| for (size_t j = 0; j < out_dim && j < dmn_dim; ++j) {
|
| dmn_sum += dwg[b * out_dim + j] * dv[b * dmn_dim + j];
|
| }
|
| sgg[b * 2] = ecn_sum;
|
| sgg[b * 2 + 1] = dmn_sum;
|
| }
|
| }
|
|
|
|
|
| {
|
| float* sgg = sn_gate_output_grad.as_fp32();
|
| for (size_t b = 0; b < batch; ++b) {
|
| float g0 = gates_data[b * 2];
|
| float g1 = gates_data[b * 2 + 1];
|
|
|
| float dot = sgg[b * 2] * g0 + sgg[b * 2 + 1] * g1;
|
| sgg[b * 2] = g0 * (sgg[b * 2] - dot);
|
| sgg[b * 2 + 1] = g1 * (sgg[b * 2 + 1] - dot);
|
| }
|
| }
|
|
|
|
|
| Tensor gate2_input_grad = linear_backward_input(sn_gate_output_grad,
|
| model.sn->gate2->weight);
|
| grads.sn_gate2_weight_grad = linear_backward_weight(cache.sn_gate_h_post, sn_gate_output_grad);
|
| grads.sn_gate2_bias_grad = bias_backward(sn_gate_output_grad);
|
|
|
|
|
|
|
| Tensor gate1_gelu_grad = gelu_backward(cache.sn_gate_h, gate2_input_grad);
|
| grads.sn_gate1_weight_grad = linear_backward_weight(cache.h, gate1_gelu_grad);
|
| grads.sn_gate1_bias_grad = bias_backward(gate1_gelu_grad);
|
| Tensor sn_h_grad = linear_backward_input(gate1_gelu_grad, model.sn->gate1->weight);
|
|
|
|
|
|
|
| Tensor ecn_decision_grad({batch, out_dim}, QuantType::FP32);
|
| {
|
| const float* ewg = ecn_w_grad.as_fp32();
|
| float* edg = ecn_decision_grad.as_fp32();
|
|
|
| for (size_t b = 0; b < batch; ++b) {
|
| float ecn_gate = gates_data[b * 2];
|
| for (size_t j = 0; j < out_dim; ++j) {
|
| edg[b * out_dim + j] = ewg[b * out_dim + j] * ecn_gate;
|
| }
|
| }
|
| }
|
|
|
| Tensor vmpfc_d_grad = linear_backward_input(ecn_decision_grad, model.ecn->vmpfc2->weight);
|
| grads.ecn_vmpfc2_weight_grad = linear_backward_weight(cache.ecn_vmpfc_d, ecn_decision_grad);
|
| grads.ecn_vmpfc2_bias_grad = bias_backward(ecn_decision_grad);
|
|
|
| Tensor vmpfc_pre_grad = gelu_backward(cache.ecn_vmpfc_pre, vmpfc_d_grad);
|
| grads.ecn_vmpfc1_weight_grad = linear_backward_weight(cache.ecn_hidden.back(), vmpfc_pre_grad);
|
| grads.ecn_vmpfc1_bias_grad = bias_backward(vmpfc_pre_grad);
|
|
|
| Tensor ecn_last_h_grad = linear_backward_input(vmpfc_pre_grad, model.ecn->vmpfc1->weight);
|
|
|
| grads.ecn_dlpfc_weight_grads.clear();
|
| grads.ecn_dlpfc_bias_grads.clear();
|
| grads.ecn_dlpfc_norm_weight_grads.clear();
|
| grads.ecn_dlpfc_norm_bias_grads.clear();
|
|
|
| for (int i = static_cast<int>(model.ecn->num_layers) - 1; i >= 0; --i) {
|
| Tensor gelu_grad = gelu_backward(cache.ecn_pre_norm[i], ecn_last_h_grad);
|
| Tensor norm_grad_i = layernorm_backward(
|
| cache.ecn_pre_norm[i], model.ecn->dlpfc_norm[i]->weight, gelu_grad);
|
|
|
|
|
| auto norm_pg_i = layernorm_param_backward(cache.ecn_pre_norm[i], gelu_grad);
|
| grads.ecn_dlpfc_norm_weight_grads.insert(grads.ecn_dlpfc_norm_weight_grads.begin(), norm_pg_i.weight_grad);
|
| grads.ecn_dlpfc_norm_bias_grads.insert(grads.ecn_dlpfc_norm_bias_grads.begin(), norm_pg_i.bias_grad);
|
|
|
| const Tensor& input_for_layer = (i == 0) ? cache.h : cache.ecn_pre_linear[i];
|
| grads.ecn_dlpfc_weight_grads.insert(grads.ecn_dlpfc_weight_grads.begin(),
|
| linear_backward_weight(input_for_layer, norm_grad_i));
|
| grads.ecn_dlpfc_bias_grads.insert(grads.ecn_dlpfc_bias_grads.begin(),
|
| bias_backward(norm_grad_i));
|
|
|
| ecn_last_h_grad = linear_backward_input(norm_grad_i, model.ecn->dlpfc_linear[i]->weight);
|
| }
|
|
|
|
|
|
|
| size_t dmn_vision_dim = cache.dmn_vision.shape_[1];
|
| Tensor dmn_vision_grad({batch, dmn_vision_dim}, QuantType::FP32);
|
| {
|
| const float* dwg = dmn_w_grad.as_fp32();
|
| float* dvg = dmn_vision_grad.as_fp32();
|
|
|
| for (size_t b = 0; b < batch; ++b) {
|
| float dmn_gate = gates_data[b * 2 + 1];
|
| for (size_t j = 0; j < dmn_vision_dim; ++j) {
|
| if (j < out_dim)
|
| dvg[b * dmn_vision_dim + j] = dwg[b * out_dim + j] * dmn_gate;
|
| else
|
| dvg[b * dmn_vision_dim + j] = 0.0f;
|
| }
|
| }
|
| }
|
|
|
|
|
| grads.dmn_future_proj1_weight_grad = linear_backward_weight(
|
| TensorOps::concat(cache.dmn_associations, 1), dmn_vision_grad);
|
| grads.dmn_future_proj1_bias_grad = bias_backward(dmn_vision_grad);
|
|
|
|
|
| Tensor concat_assoc_grad = linear_backward_input(dmn_vision_grad,
|
| model.dmn->future_proj1->weight);
|
|
|
|
|
| size_t latent_dim = model.dmn->latent_dim;
|
| Tensor dmn_latent_grad({batch, latent_dim}, QuantType::FP32);
|
| {
|
| float* dlg = dmn_latent_grad.as_fp32();
|
| const float* cag = concat_assoc_grad.as_fp32();
|
|
|
| grads.dmn_head2_weight_grads.clear();
|
| grads.dmn_head2_bias_grads.clear();
|
| grads.dmn_head1_weight_grads.clear();
|
| grads.dmn_head1_bias_grads.clear();
|
|
|
| for (size_t h = 0; h < model.dmn->num_associations; ++h) {
|
|
|
| Tensor head_grad({batch, latent_dim}, QuantType::FP32);
|
| float* hg = head_grad.as_fp32();
|
| size_t concat_dim = concat_assoc_grad.shape_[1];
|
| for (size_t b = 0; b < batch; ++b) {
|
| for (size_t j = 0; j < latent_dim; ++j) {
|
| hg[b * latent_dim + j] = cag[b * concat_dim + h * latent_dim + j];
|
| }
|
| }
|
|
|
|
|
| auto& head2 = model.dmn->association_heads[h].second;
|
| grads.dmn_head2_weight_grads.push_back(
|
| linear_backward_weight(cache.dmn_head1_outs[h], head_grad));
|
| grads.dmn_head2_bias_grads.push_back(bias_backward(head_grad));
|
|
|
| Tensor head1_out_grad = linear_backward_input(head_grad, head2->weight);
|
|
|
|
|
|
|
| {
|
| float* h1og = head1_out_grad.as_fp32();
|
| const float* h1o = cache.dmn_head1_outs[h].as_fp32();
|
| size_t n = head1_out_grad.numel();
|
| for (size_t i = 0; i < n; ++i) {
|
|
|
| h1og[i] *= (h1o[i] > 0.0f) ? 1.0f : 0.1f;
|
| }
|
| }
|
|
|
|
|
| auto& head1 = model.dmn->association_heads[h].first;
|
| grads.dmn_head1_weight_grads.push_back(
|
| linear_backward_weight(cache.dmn_latent, head1_out_grad));
|
| grads.dmn_head1_bias_grads.push_back(bias_backward(head1_out_grad));
|
|
|
| Tensor head1_input_grad = linear_backward_input(head1_out_grad, head1->weight);
|
|
|
|
|
| float* dlgp = dmn_latent_grad.as_fp32();
|
| const float* h1ig = head1_input_grad.as_fp32();
|
| float inv_h = 1.0f / model.dmn->num_associations;
|
| for (size_t idx = 0; idx < batch * latent_dim; ++idx) {
|
| dlgp[idx] += h1ig[idx] * inv_h;
|
| }
|
| }
|
| }
|
|
|
|
|
| grads.dmn_mem_encoder2_weight_grad = linear_backward_weight(
|
| cache.dmn_encoded, dmn_latent_grad);
|
| grads.dmn_mem_encoder2_bias_grad = bias_backward(dmn_latent_grad);
|
|
|
| Tensor dmn_encoded_grad = linear_backward_input(dmn_latent_grad,
|
| model.dmn->mem_encoder2->weight);
|
|
|
|
|
|
|
|
|
| {
|
| float* deg = dmn_encoded_grad.as_fp32();
|
| const float* de = cache.dmn_encoded.as_fp32();
|
| size_t n = dmn_encoded_grad.numel();
|
| for (size_t i = 0; i < n; ++i) {
|
| deg[i] *= (de[i] > 0.0f) ? 1.0f : 0.1f;
|
| }
|
| }
|
|
|
|
|
| grads.dmn_mem_encoder1_weight_grad = linear_backward_weight(
|
| cache.memory_encoded, dmn_encoded_grad);
|
| grads.dmn_mem_encoder1_bias_grad = bias_backward(dmn_encoded_grad);
|
|
|
| Tensor dmn_h_grad = linear_backward_input(dmn_encoded_grad,
|
| model.dmn->mem_encoder1->weight);
|
|
|
|
|
|
|
|
|
|
|
|
|
| Tensor mem_retrieved_grad({batch, model.config.memory_dim}, QuantType::FP32);
|
| {
|
| const float* mwg = mem_w_grad.as_fp32();
|
| float* mrg = mem_retrieved_grad.as_fp32();
|
| size_t mem_dim = model.config.memory_dim;
|
|
|
| for (size_t b = 0; b < batch; ++b) {
|
| for (size_t j = 0; j < mem_dim; ++j) {
|
| mrg[b * mem_dim + j] = (j < out_dim) ? mwg[b * out_dim + j] : 0.0f;
|
| }
|
| }
|
| }
|
|
|
|
|
|
|
| auto mem_out = model.memory->retrieve(cache.h);
|
| grads.mem_encode_proj_weight_grad = linear_backward_weight(
|
| cache.h, mem_retrieved_grad);
|
| grads.mem_encode_proj_bias_grad = bias_backward(mem_retrieved_grad);
|
|
|
| Tensor mem_enc_h_grad = linear_backward_input(mem_retrieved_grad,
|
| model.memory->encode_proj->weight);
|
|
|
|
|
| grads.mem_query_proj_weight_grad = linear_backward_weight(
|
| cache.h, mem_retrieved_grad);
|
| grads.mem_query_proj_bias_grad = bias_backward(mem_retrieved_grad);
|
|
|
| Tensor mem_query_h_grad = linear_backward_input(mem_retrieved_grad,
|
| model.memory->query_proj->weight);
|
|
|
|
|
|
|
|
|
| Tensor h_grad({batch, model.config.hidden_dim}, QuantType::FP32);
|
| {
|
| float* hg = h_grad.as_fp32();
|
| const float* eg = ecn_last_h_grad.as_fp32();
|
| const float* dg = dmn_h_grad.as_fp32();
|
| const float* meg = mem_enc_h_grad.as_fp32();
|
| const float* mqg = mem_query_h_grad.as_fp32();
|
| const float* sg = sn_h_grad.as_fp32();
|
| size_t hd = model.config.hidden_dim;
|
| size_t md = model.config.memory_dim;
|
|
|
| for (size_t b = 0; b < batch; ++b) {
|
| for (size_t j = 0; j < hd; ++j) {
|
| hg[b * hd + j] = eg[b * hd + j]
|
| + (j < md ? dg[b * md + j] : 0.0f)
|
| + (j < md ? meg[b * md + j] : 0.0f)
|
| + (j < md ? mqg[b * md + j] : 0.0f)
|
| + (j < hd ? sg[b * hd + j] : 0.0f);
|
| }
|
| }
|
| }
|
|
|
| Tensor gelu_grad_input = gelu_backward(cache.input_proj_post, h_grad);
|
| Tensor norm_grad_input = layernorm_backward(
|
| cache.input_proj_pre, model.input_proj_norm->weight, gelu_grad_input);
|
|
|
|
|
| auto in_norm_pg = layernorm_param_backward(cache.input_proj_pre, gelu_grad_input);
|
| grads.input_proj_norm_weight_grad = in_norm_pg.weight_grad;
|
| grads.input_proj_norm_bias_grad = in_norm_pg.bias_grad;
|
|
|
| grads.input_proj_weight_grad = linear_backward_weight(cache.input, norm_grad_input);
|
| grads.input_proj_bias_grad = bias_backward(norm_grad_input);
|
|
|
| grads.input_grad = linear_backward_input(norm_grad_input, model.input_proj_linear->weight);
|
|
|
| return grads;
|
| }
|
|
|
| void FullTrainer::apply_gradients(FullBackpropEngine::Gradients& grads, float lr) {
|
| auto sgd_update = [&](Tensor& param, const Tensor& grad) {
|
| if (param.shape_ != grad.shape_ || param.numel() == 0 || grad.numel() == 0) return;
|
| float* p = param.as_fp32();
|
| const float* g = grad.as_fp32();
|
| size_t n = param.numel();
|
| #pragma omp parallel for schedule(static) if(n >= OMP_MIN_ITER)
|
| for (omp_idx_t i = 0; i < static_cast<omp_idx_t>(n); ++i) {
|
| p[i] -= lr * g[i];
|
| }
|
| };
|
|
|
| sgd_update(model.input_proj_linear->weight, grads.input_proj_weight_grad);
|
| sgd_update(model.input_proj_linear->bias, grads.input_proj_bias_grad);
|
| sgd_update(model.input_proj_norm->weight, grads.input_proj_norm_weight_grad);
|
| sgd_update(model.input_proj_norm->bias, grads.input_proj_norm_bias_grad);
|
| sgd_update(model.output_fusion_down->weight, grads.output_fusion_down_weight_grad);
|
| sgd_update(model.output_fusion_down->bias, grads.output_fusion_down_bias_grad);
|
| sgd_update(model.output_fusion_up->weight, grads.output_fusion_up_weight_grad);
|
| sgd_update(model.output_fusion_up->bias, grads.output_fusion_up_bias_grad);
|
| sgd_update(model.output_fusion_norm->weight, grads.output_fusion_norm_weight_grad);
|
| sgd_update(model.output_fusion_norm->bias, grads.output_fusion_norm_bias_grad);
|
| sgd_update(model.output_fusion_bottleneck_norm->weight, grads.output_fusion_bottleneck_norm_weight_grad);
|
| sgd_update(model.output_fusion_bottleneck_norm->bias, grads.output_fusion_bottleneck_norm_bias_grad);
|
| sgd_update(model.ecn->vmpfc2->weight, grads.ecn_vmpfc2_weight_grad);
|
| sgd_update(model.ecn->vmpfc2->bias, grads.ecn_vmpfc2_bias_grad);
|
| sgd_update(model.ecn->vmpfc1->weight, grads.ecn_vmpfc1_weight_grad);
|
| sgd_update(model.ecn->vmpfc1->bias, grads.ecn_vmpfc1_bias_grad);
|
|
|
| for (size_t i = 0; i < grads.ecn_dlpfc_weight_grads.size() && i < model.ecn->dlpfc_linear.size(); ++i) {
|
| sgd_update(model.ecn->dlpfc_linear[i]->weight, grads.ecn_dlpfc_weight_grads[i]);
|
| }
|
| for (size_t i = 0; i < grads.ecn_dlpfc_bias_grads.size() && i < model.ecn->dlpfc_linear.size(); ++i) {
|
| sgd_update(model.ecn->dlpfc_linear[i]->bias, grads.ecn_dlpfc_bias_grads[i]);
|
| }
|
| for (size_t i = 0; i < grads.ecn_dlpfc_norm_weight_grads.size() && i < model.ecn->dlpfc_norm.size(); ++i) {
|
| sgd_update(model.ecn->dlpfc_norm[i]->weight, grads.ecn_dlpfc_norm_weight_grads[i]);
|
| }
|
| for (size_t i = 0; i < grads.ecn_dlpfc_norm_bias_grads.size() && i < model.ecn->dlpfc_norm.size(); ++i) {
|
| sgd_update(model.ecn->dlpfc_norm[i]->bias, grads.ecn_dlpfc_norm_bias_grads[i]);
|
| }
|
|
|
|
|
| sgd_update(model.sn->gate2->weight, grads.sn_gate2_weight_grad);
|
| sgd_update(model.sn->gate2->bias, grads.sn_gate2_bias_grad);
|
| sgd_update(model.sn->gate1->weight, grads.sn_gate1_weight_grad);
|
| sgd_update(model.sn->gate1->bias, grads.sn_gate1_bias_grad);
|
|
|
|
|
| sgd_update(model.dmn->future_proj1->weight, grads.dmn_future_proj1_weight_grad);
|
| sgd_update(model.dmn->future_proj1->bias, grads.dmn_future_proj1_bias_grad);
|
| sgd_update(model.dmn->mem_encoder2->weight, grads.dmn_mem_encoder2_weight_grad);
|
| sgd_update(model.dmn->mem_encoder2->bias, grads.dmn_mem_encoder2_bias_grad);
|
| sgd_update(model.dmn->mem_encoder1->weight, grads.dmn_mem_encoder1_weight_grad);
|
| sgd_update(model.dmn->mem_encoder1->bias, grads.dmn_mem_encoder1_bias_grad);
|
| for (size_t i = 0; i < grads.dmn_head2_weight_grads.size() && i < model.dmn->association_heads.size(); ++i) {
|
| sgd_update(model.dmn->association_heads[i].second->weight, grads.dmn_head2_weight_grads[i]);
|
| sgd_update(model.dmn->association_heads[i].second->bias, grads.dmn_head2_bias_grads[i]);
|
| }
|
| for (size_t i = 0; i < grads.dmn_head1_weight_grads.size() && i < model.dmn->association_heads.size(); ++i) {
|
| sgd_update(model.dmn->association_heads[i].first->weight, grads.dmn_head1_weight_grads[i]);
|
| sgd_update(model.dmn->association_heads[i].first->bias, grads.dmn_head1_bias_grads[i]);
|
| }
|
|
|
|
|
| sgd_update(model.memory->encode_proj->weight, grads.mem_encode_proj_weight_grad);
|
| sgd_update(model.memory->encode_proj->bias, grads.mem_encode_proj_bias_grad);
|
| sgd_update(model.memory->query_proj->weight, grads.mem_query_proj_weight_grad);
|
| sgd_update(model.memory->query_proj->bias, grads.mem_query_proj_bias_grad);
|
| }
|
|
|
| FullTrainer::FullTrainer(NeuroFlowModel& m, float lr)
|
| : model(m), learning_rate(lr), backprop(m) {}
|
|
|
| FullTrainer::TrainStep FullTrainer::train_step(const Tensor& input, const Tensor& target) {
|
| TrainStep result;
|
|
|
| auto output = backprop.forward_with_cache(input);
|
|
|
| const float* pred = output.output.as_fp32();
|
| const float* tgt = target.as_fp32();
|
| size_t batch = input.shape_[0];
|
| size_t dim = target.shape_[1];
|
| size_t n = output.output.numel();
|
|
|
| bool is_onehot = false;
|
| for (size_t i = 0; i < std::min(n, batch * 10UL); ++i) {
|
| if (tgt[i] == 1.0f) { is_onehot = true; break; }
|
| }
|
|
|
| if (is_onehot && dim > 100) {
|
| float loss = 0.0f;
|
| Tensor output_grad({batch, dim}, QuantType::FP32);
|
| float* og = output_grad.as_fp32();
|
| float grad_norm = 0.0f;
|
|
|
| for (size_t b = 0; b < batch; ++b) {
|
| float max_val = -1e30f;
|
| for (size_t j = 0; j < dim; ++j) {
|
| if (pred[b * dim + j] > max_val) max_val = pred[b * dim + j];
|
| }
|
|
|
| float sum_exp = 0.0f;
|
| for (size_t j = 0; j < dim; ++j) {
|
| sum_exp += std::exp(pred[b * dim + j] - max_val);
|
| }
|
| float log_sum_exp = max_val + std::log(sum_exp);
|
|
|
| for (size_t j = 0; j < dim; ++j) {
|
| float softmax_val = std::exp(pred[b * dim + j] - max_val) / sum_exp;
|
| float t = tgt[b * dim + j];
|
| og[b * dim + j] = (softmax_val - t) / batch;
|
| grad_norm += og[b * dim + j] * og[b * dim + j];
|
| if (t > 0.5f) {
|
| loss -= (pred[b * dim + j] - log_sum_exp);
|
| }
|
| }
|
| }
|
|
|
| result.loss = loss / batch;
|
| result.grad_norm = std::sqrt(grad_norm);
|
|
|
| auto grads = backprop.backward(output_grad);
|
| apply_gradients(grads, learning_rate);
|
| } else {
|
| Tensor output_grad({batch, dim}, QuantType::FP32);
|
| float* og = output_grad.as_fp32();
|
| float scale = 2.0f / n;
|
| float loss = 0.0f;
|
| float grad_norm = 0.0f;
|
| #pragma omp parallel for schedule(static) reduction(+:loss,grad_norm) if(n >= OMP_MIN_ITER)
|
| for (omp_idx_t i = 0; i < static_cast<omp_idx_t>(n); ++i) {
|
| float diff = pred[i] - tgt[i];
|
| og[i] = scale * diff;
|
| loss += diff * diff;
|
| grad_norm += og[i] * og[i];
|
| }
|
| result.loss = loss / n;
|
| result.grad_norm = std::sqrt(grad_norm);
|
|
|
| auto grads = backprop.backward(output_grad);
|
| apply_gradients(grads, learning_rate);
|
| }
|
|
|
| model.memory->consolidate(backprop.cache.h);
|
|
|
| return result;
|
| }
|
|
|
| std::vector<float> FullTrainer::train(const std::vector<Tensor>& inputs,
|
| const std::vector<Tensor>& targets,
|
| int epochs) {
|
| std::vector<float> losses;
|
| for (int e = 0; e < epochs; ++e) {
|
| float epoch_loss = 0.0f;
|
| for (size_t i = 0; i < inputs.size(); ++i) {
|
| auto step = train_step(inputs[i], targets[i]);
|
| epoch_loss += step.loss;
|
| }
|
| epoch_loss /= inputs.size();
|
| losses.push_back(epoch_loss);
|
| }
|
| return losses;
|
| }
|
|
|
| FullTrainer::TrainStep FullTrainer::accumulate_step(const Tensor& input, const Tensor& target) {
|
| TrainStep result;
|
|
|
| auto output = backprop.forward_with_cache(input);
|
|
|
| const float* pred = output.output.as_fp32();
|
| const float* tgt = target.as_fp32();
|
| size_t batch = input.shape_[0];
|
| size_t dim = target.shape_[1];
|
| size_t n = output.output.numel();
|
|
|
| bool is_onehot = false;
|
| for (size_t i = 0; i < std::min(n, batch * 10UL); ++i) {
|
| if (tgt[i] == 1.0f) { is_onehot = true; break; }
|
| }
|
|
|
| Tensor output_grad({batch, dim}, QuantType::FP32);
|
| float* og = output_grad.as_fp32();
|
|
|
| if (is_onehot && dim > 100) {
|
| float loss = 0.0f;
|
| float grad_norm = 0.0f;
|
| for (size_t b = 0; b < batch; ++b) {
|
| float max_val = -1e30f;
|
| for (size_t j = 0; j < dim; ++j)
|
| if (pred[b * dim + j] > max_val) max_val = pred[b * dim + j];
|
| float sum_exp = 0.0f;
|
| for (size_t j = 0; j < dim; ++j)
|
| sum_exp += std::exp(pred[b * dim + j] - max_val);
|
| float log_sum_exp = max_val + std::log(sum_exp);
|
| for (size_t j = 0; j < dim; ++j) {
|
| float softmax_val = std::exp(pred[b * dim + j] - max_val) / sum_exp;
|
| float t = tgt[b * dim + j];
|
| og[b * dim + j] = (softmax_val - t) / batch;
|
| grad_norm += og[b * dim + j] * og[b * dim + j];
|
| if (t > 0.5f) loss -= (pred[b * dim + j] - log_sum_exp);
|
| }
|
| }
|
| result.loss = loss / batch;
|
| result.grad_norm = std::sqrt(grad_norm);
|
| } else {
|
| float scale = 2.0f / n;
|
| float loss = 0.0f;
|
| float grad_norm = 0.0f;
|
| for (size_t i = 0; i < n; ++i) {
|
| float diff = pred[i] - tgt[i];
|
| og[i] = scale * diff;
|
| loss += diff * diff;
|
| grad_norm += og[i] * og[i];
|
| }
|
| result.loss = loss / n;
|
| result.grad_norm = std::sqrt(grad_norm);
|
| }
|
|
|
| auto grads = backprop.backward(output_grad);
|
|
|
| if (!accum_initialized_) {
|
| accum_grads_.input_proj_weight_grad = grads.input_proj_weight_grad.clone();
|
| accum_grads_.input_proj_bias_grad = grads.input_proj_bias_grad.clone();
|
| accum_grads_.input_proj_norm_weight_grad = grads.input_proj_norm_weight_grad.clone();
|
| accum_grads_.input_proj_norm_bias_grad = grads.input_proj_norm_bias_grad.clone();
|
| accum_grads_.output_fusion_down_weight_grad = grads.output_fusion_down_weight_grad.clone();
|
| accum_grads_.output_fusion_down_bias_grad = grads.output_fusion_down_bias_grad.clone();
|
| accum_grads_.output_fusion_up_weight_grad = grads.output_fusion_up_weight_grad.clone();
|
| accum_grads_.output_fusion_up_bias_grad = grads.output_fusion_up_bias_grad.clone();
|
| accum_grads_.output_fusion_norm_weight_grad = grads.output_fusion_norm_weight_grad.clone();
|
| accum_grads_.output_fusion_norm_bias_grad = grads.output_fusion_norm_bias_grad.clone();
|
| accum_grads_.output_fusion_bottleneck_norm_weight_grad = grads.output_fusion_bottleneck_norm_weight_grad.clone();
|
| accum_grads_.output_fusion_bottleneck_norm_bias_grad = grads.output_fusion_bottleneck_norm_bias_grad.clone();
|
| accum_grads_.ecn_vmpfc2_weight_grad = grads.ecn_vmpfc2_weight_grad.clone();
|
| accum_grads_.ecn_vmpfc2_bias_grad = grads.ecn_vmpfc2_bias_grad.clone();
|
| accum_grads_.ecn_vmpfc1_weight_grad = grads.ecn_vmpfc1_weight_grad.clone();
|
| accum_grads_.ecn_vmpfc1_bias_grad = grads.ecn_vmpfc1_bias_grad.clone();
|
| accum_grads_.ecn_dlpfc_weight_grads = grads.ecn_dlpfc_weight_grads;
|
| accum_grads_.ecn_dlpfc_bias_grads = grads.ecn_dlpfc_bias_grads;
|
| accum_grads_.ecn_dlpfc_norm_weight_grads = grads.ecn_dlpfc_norm_weight_grads;
|
| accum_grads_.ecn_dlpfc_norm_bias_grads = grads.ecn_dlpfc_norm_bias_grads;
|
| accum_initialized_ = true;
|
| } else {
|
| auto add_tensor = [](Tensor& dst, const Tensor& src) {
|
| float* d = dst.as_fp32();
|
| const float* s = src.as_fp32();
|
| size_t n = dst.numel();
|
| for (size_t i = 0; i < n; ++i) d[i] += s[i];
|
| };
|
| add_tensor(accum_grads_.input_proj_weight_grad, grads.input_proj_weight_grad);
|
| add_tensor(accum_grads_.input_proj_bias_grad, grads.input_proj_bias_grad);
|
| add_tensor(accum_grads_.input_proj_norm_weight_grad, grads.input_proj_norm_weight_grad);
|
| add_tensor(accum_grads_.input_proj_norm_bias_grad, grads.input_proj_norm_bias_grad);
|
| add_tensor(accum_grads_.output_fusion_down_weight_grad, grads.output_fusion_down_weight_grad);
|
| add_tensor(accum_grads_.output_fusion_down_bias_grad, grads.output_fusion_down_bias_grad);
|
| add_tensor(accum_grads_.output_fusion_up_weight_grad, grads.output_fusion_up_weight_grad);
|
| add_tensor(accum_grads_.output_fusion_up_bias_grad, grads.output_fusion_up_bias_grad);
|
| add_tensor(accum_grads_.output_fusion_norm_weight_grad, grads.output_fusion_norm_weight_grad);
|
| add_tensor(accum_grads_.output_fusion_norm_bias_grad, grads.output_fusion_norm_bias_grad);
|
| add_tensor(accum_grads_.output_fusion_bottleneck_norm_weight_grad, grads.output_fusion_bottleneck_norm_weight_grad);
|
| add_tensor(accum_grads_.output_fusion_bottleneck_norm_bias_grad, grads.output_fusion_bottleneck_norm_bias_grad);
|
| add_tensor(accum_grads_.ecn_vmpfc2_weight_grad, grads.ecn_vmpfc2_weight_grad);
|
| add_tensor(accum_grads_.ecn_vmpfc2_bias_grad, grads.ecn_vmpfc2_bias_grad);
|
| add_tensor(accum_grads_.ecn_vmpfc1_weight_grad, grads.ecn_vmpfc1_weight_grad);
|
| add_tensor(accum_grads_.ecn_vmpfc1_bias_grad, grads.ecn_vmpfc1_bias_grad);
|
| for (size_t i = 0; i < grads.ecn_dlpfc_weight_grads.size() && i < accum_grads_.ecn_dlpfc_weight_grads.size(); ++i)
|
| add_tensor(accum_grads_.ecn_dlpfc_weight_grads[i], grads.ecn_dlpfc_weight_grads[i]);
|
| for (size_t i = 0; i < grads.ecn_dlpfc_bias_grads.size() && i < accum_grads_.ecn_dlpfc_bias_grads.size(); ++i)
|
| add_tensor(accum_grads_.ecn_dlpfc_bias_grads[i], grads.ecn_dlpfc_bias_grads[i]);
|
| for (size_t i = 0; i < grads.ecn_dlpfc_norm_weight_grads.size() && i < accum_grads_.ecn_dlpfc_norm_weight_grads.size(); ++i)
|
| add_tensor(accum_grads_.ecn_dlpfc_norm_weight_grads[i], grads.ecn_dlpfc_norm_weight_grads[i]);
|
| for (size_t i = 0; i < grads.ecn_dlpfc_norm_bias_grads.size() && i < accum_grads_.ecn_dlpfc_norm_bias_grads.size(); ++i)
|
| add_tensor(accum_grads_.ecn_dlpfc_norm_bias_grads[i], grads.ecn_dlpfc_norm_bias_grads[i]);
|
| }
|
|
|
| accum_loss_ += result.loss;
|
| accum_grad_norm_ += result.grad_norm;
|
|
|
| model.memory->consolidate(backprop.cache.h);
|
| return result;
|
| }
|
|
|
| void FullTrainer::apply_accumulated_gradients(int accum_steps) {
|
| if (!accum_initialized_ || accum_steps <= 0) return;
|
| float scale = 1.0f / accum_steps;
|
|
|
| auto scaled_sgd = [&](Tensor& param, Tensor& grad) {
|
| if (param.shape_ != grad.shape_ || param.numel() == 0) return;
|
| float* p = param.as_fp32();
|
| const float* g = grad.as_fp32();
|
| size_t n = param.numel();
|
| #pragma omp parallel for schedule(static) if(n >= OMP_MIN_ITER)
|
| for (omp_idx_t i = 0; i < static_cast<omp_idx_t>(n); ++i) {
|
| p[i] -= learning_rate * g[i] * scale;
|
| }
|
| };
|
|
|
| scaled_sgd(model.input_proj_linear->weight, accum_grads_.input_proj_weight_grad);
|
| scaled_sgd(model.input_proj_linear->bias, accum_grads_.input_proj_bias_grad);
|
| scaled_sgd(model.input_proj_norm->weight, accum_grads_.input_proj_norm_weight_grad);
|
| scaled_sgd(model.input_proj_norm->bias, accum_grads_.input_proj_norm_bias_grad);
|
| scaled_sgd(model.output_fusion_down->weight, accum_grads_.output_fusion_down_weight_grad);
|
| scaled_sgd(model.output_fusion_down->bias, accum_grads_.output_fusion_down_bias_grad);
|
| scaled_sgd(model.output_fusion_up->weight, accum_grads_.output_fusion_up_weight_grad);
|
| scaled_sgd(model.output_fusion_up->bias, accum_grads_.output_fusion_up_bias_grad);
|
| scaled_sgd(model.output_fusion_norm->weight, accum_grads_.output_fusion_norm_weight_grad);
|
| scaled_sgd(model.output_fusion_norm->bias, accum_grads_.output_fusion_norm_bias_grad);
|
| scaled_sgd(model.output_fusion_bottleneck_norm->weight, accum_grads_.output_fusion_bottleneck_norm_weight_grad);
|
| scaled_sgd(model.output_fusion_bottleneck_norm->bias, accum_grads_.output_fusion_bottleneck_norm_bias_grad);
|
| scaled_sgd(model.ecn->vmpfc2->weight, accum_grads_.ecn_vmpfc2_weight_grad);
|
| scaled_sgd(model.ecn->vmpfc2->bias, accum_grads_.ecn_vmpfc2_bias_grad);
|
| scaled_sgd(model.ecn->vmpfc1->weight, accum_grads_.ecn_vmpfc1_weight_grad);
|
| scaled_sgd(model.ecn->vmpfc1->bias, accum_grads_.ecn_vmpfc1_bias_grad);
|
|
|
| for (size_t i = 0; i < accum_grads_.ecn_dlpfc_weight_grads.size() && i < model.ecn->dlpfc_linear.size(); ++i)
|
| scaled_sgd(model.ecn->dlpfc_linear[i]->weight, accum_grads_.ecn_dlpfc_weight_grads[i]);
|
| for (size_t i = 0; i < accum_grads_.ecn_dlpfc_bias_grads.size() && i < model.ecn->dlpfc_linear.size(); ++i)
|
| scaled_sgd(model.ecn->dlpfc_linear[i]->bias, accum_grads_.ecn_dlpfc_bias_grads[i]);
|
| for (size_t i = 0; i < accum_grads_.ecn_dlpfc_norm_weight_grads.size() && i < model.ecn->dlpfc_norm.size(); ++i)
|
| scaled_sgd(model.ecn->dlpfc_norm[i]->weight, accum_grads_.ecn_dlpfc_norm_weight_grads[i]);
|
| for (size_t i = 0; i < accum_grads_.ecn_dlpfc_norm_bias_grads.size() && i < model.ecn->dlpfc_norm.size(); ++i)
|
| scaled_sgd(model.ecn->dlpfc_norm[i]->bias, accum_grads_.ecn_dlpfc_norm_bias_grads[i]);
|
|
|
| accum_initialized_ = false;
|
| accum_loss_ = 0.0f;
|
| accum_grad_norm_ = 0.0f;
|
| }
|
|
|
| }
|
|
|