#include "neuroflow/swiglu.hpp" #include #include #include #ifdef USE_CBLAS #include #endif #ifdef USE_CUDA #include "cuda_context.hpp" #endif namespace neuroflow { static Tensor linear_backward_weight(const Tensor& input, const Tensor& output_grad) { size_t in_dim = input.shape_.back(); size_t out_dim = output_grad.shape_.back(); size_t batch = input.numel() / in_dim; Tensor grad({out_dim, in_dim}, QuantType::FP32); float* gp = grad.as_fp32(); memset(gp, 0, grad.data_size_); const float* ip = input.as_fp32(); const float* op = output_grad.as_fp32(); #ifdef USE_CBLAS cblas_sgemm(CblasRowMajor, CblasTrans, CblasNoTrans, static_cast(out_dim), static_cast(in_dim), static_cast(batch), 1.0f, op, static_cast(out_dim), ip, static_cast(in_dim), 0.0f, gp, static_cast(in_dim)); #else for (size_t b = 0; b < batch; ++b) { for (size_t o = 0; o < out_dim; ++o) { for (size_t i = 0; i < in_dim; ++i) { gp[o * in_dim + i] += op[b * out_dim + o] * ip[b * in_dim + i]; } } } #endif return grad; } static Tensor linear_backward_input(const Tensor& output_grad, const Tensor& weight) { size_t out_dim = output_grad.shape_.back(); size_t batch = output_grad.numel() / out_dim; size_t in_dim = weight.shape_[1]; Tensor grad({batch, in_dim}, QuantType::FP32); float* gp = grad.as_fp32(); const float* op = output_grad.as_fp32(); const float* wp = weight.as_fp32(); #ifdef USE_CBLAS cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, static_cast(batch), static_cast(in_dim), static_cast(out_dim), 1.0f, op, static_cast(out_dim), wp, static_cast(in_dim), 0.0f, gp, static_cast(in_dim)); #else for (size_t b = 0; b < batch; ++b) { for (size_t i = 0; i < in_dim; ++i) { float val = 0.0f; for (size_t o = 0; o < out_dim; ++o) { val += op[b * out_dim + o] * wp[o * in_dim + i]; } gp[b * in_dim + i] = val; } } #endif return grad; } static Tensor bias_backward(const Tensor& output_grad) { size_t out_dim = output_grad.shape_.back(); size_t batch = output_grad.numel() / out_dim; Tensor grad({out_dim}, QuantType::FP32); float* gp = grad.as_fp32(); const float* op = output_grad.as_fp32(); memset(gp, 0, grad.data_size_); for (size_t b = 0; b < batch; ++b) { for (size_t o = 0; o < out_dim; ++o) { gp[o] += op[b * out_dim + o]; } } return grad; } SwiGLUFFN::SwiGLUFFN(size_t d_model, size_t d_ff) : d_model_(d_model), d_ff_(d_ff > 0 ? d_ff : d_model * 4) { w_gate_ = std::make_shared(d_model_, d_ff_, true); w_up_ = std::make_shared(d_model_, d_ff_, true); w_down_ = std::make_shared(d_ff_, d_model_, true); } Tensor SwiGLUFFN::forward(const Tensor& x) { cache_.input = x.clone(); cache_.gate_out = w_gate_->forward(x); cache_.up_out = w_up_->forward(x); size_t n = cache_.gate_out.numel(); cache_.gate_activated = cache_.gate_out.clone(); #ifdef USE_CUDA if (CudaContext::instance().is_available() && cache_.gate_activated.is_on_gpu()) { launch_silu(cache_.gate_activated.as_gpu_fp32(), n, CudaContext::instance().stream()); cache_.gate_activated.gpu_dirty_ = true; } else { #endif float* ga = cache_.gate_activated.as_fp32(); for (size_t i = 0; i < n; ++i) { float v = ga[i]; ga[i] = v / (1.0f + expf(-v)); } #ifdef USE_CUDA } #endif cache_.multiplied = Tensor(cache_.gate_activated.shape_, QuantType::FP32); #ifdef USE_CUDA if (CudaContext::instance().is_available() && cache_.gate_activated.is_on_gpu()) { cache_.multiplied.to_gpu(); launch_elementwise_mul(cache_.multiplied.as_gpu_fp32(), cache_.gate_activated.as_gpu_fp32(), cache_.up_out.as_gpu_fp32(), n, CudaContext::instance().stream()); cache_.multiplied.gpu_dirty_ = true; } else { #endif const float* ga_p = cache_.gate_activated.as_fp32(); const float* up_p = cache_.up_out.as_fp32(); float* mp = cache_.multiplied.as_fp32(); for (size_t i = 0; i < n; ++i) { mp[i] = ga_p[i] * up_p[i]; } #ifdef USE_CUDA } #endif return w_down_->forward(cache_.multiplied); } SwiGLUFFN::Gradients SwiGLUFFN::backward(const Tensor& output_grad) { Gradients grads; grads.w_down_weight_grad = linear_backward_weight(cache_.multiplied, output_grad); grads.w_down_bias_grad = bias_backward(output_grad); Tensor d_multiplied = linear_backward_input(output_grad, w_down_->weight); Tensor d_gate_activated(d_multiplied.shape_, QuantType::FP32); Tensor d_up(d_multiplied.shape_, QuantType::FP32); const float* dm = d_multiplied.as_fp32(); const float* ga_p = cache_.gate_activated.as_fp32(); const float* up_p = cache_.up_out.as_fp32(); float* dga = d_gate_activated.as_fp32(); float* dup = d_up.as_fp32(); size_t n = d_multiplied.numel(); for (size_t i = 0; i < n; ++i) { dga[i] = dm[i] * up_p[i]; dup[i] = dm[i] * ga_p[i]; } const float* go_p = cache_.gate_out.as_fp32(); Tensor d_gate_out(d_gate_activated.shape_, QuantType::FP32); float* dgo = d_gate_out.as_fp32(); for (size_t i = 0; i < n; ++i) { float sig = ga_p[i]; dgo[i] = dga[i] * sig * (1.0f + go_p[i] * (1.0f - sig)); } grads.w_gate_weight_grad = linear_backward_weight(cache_.input, d_gate_out); grads.w_gate_bias_grad = bias_backward(d_gate_out); Tensor d_input_gate = linear_backward_input(d_gate_out, w_gate_->weight); grads.w_up_weight_grad = linear_backward_weight(cache_.input, d_up); grads.w_up_bias_grad = bias_backward(d_up); Tensor d_input_up = linear_backward_input(d_up, w_up_->weight); grads.input_grad = Tensor(cache_.input.shape_, QuantType::FP32); const float* dig = d_input_gate.as_fp32(); const float* diu = d_input_up.as_fp32(); float* ig = grads.input_grad.as_fp32(); for (size_t i = 0; i < grads.input_grad.numel(); ++i) { ig[i] = dig[i] + diu[i]; } return grads; } #ifdef USE_CUDA __global__ void kernel_silu_impl(float* data, size_t n) { size_t idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx >= n) return; float v = data[idx]; data[idx] = v / (1.0f + expf(-v)); } void launch_silu(float* data, size_t n, cudaStream_t stream) { int block = 256; int grid = (static_cast(n) + block - 1) / block; kernel_silu_impl<<>>(data, n); } __global__ void kernel_elementwise_mul_impl(float* out, const float* a, const float* b, size_t n) { size_t idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx >= n) return; out[idx] = a[idx] * b[idx]; } void launch_elementwise_mul(float* out, const float* a, const float* b, size_t n, cudaStream_t stream) { int block = 256; int grid = (static_cast(n) + block - 1) / block; kernel_elementwise_mul_impl<<>>(out, a, b, n); } #endif } // namespace neuroflow