neuroflow-cpp / src /rms_norm.cpp
cwenzi's picture
Upload folder using huggingface_hub
26d5b81 verified
Raw
History Blame Contribute Delete
7.44 kB
#include "neuroflow/rms_norm.hpp"
#include <cmath>
#include <cstring>
#ifdef USE_CUDA
#include "cuda_context.hpp"
#endif
namespace neuroflow {
RMSNorm::RMSNorm(size_t dim, float eps)
: dim_(dim), eps_(eps), weight_({dim}, QuantType::FP32) {
float* wp = weight_.as_fp32();
for (size_t i = 0; i < dim; ++i) wp[i] = 1.0f;
}
Tensor RMSNorm::forward(const Tensor& x) {
cache_.input = x.clone();
size_t batch = x.numel() / dim_;
cache_.rms = Tensor({batch}, QuantType::FP32);
cache_.normalized = Tensor(x.shape_, QuantType::FP32);
Tensor output(x.shape_, QuantType::FP32);
#ifdef USE_CUDA
if (CudaContext::instance().is_available() && x.is_on_gpu()) {
output.to_gpu();
cache_.rms.to_gpu();
cache_.normalized.to_gpu();
launch_rms_norm_forward(output.as_gpu_fp32(), x.as_gpu_fp32(), weight_.as_gpu_fp32(),
cache_.rms.as_gpu_fp32(), cache_.normalized.as_gpu_fp32(),
batch, dim_, eps_, CudaContext::instance().stream());
output.gpu_dirty_ = true;
cache_.rms.gpu_dirty_ = true;
cache_.normalized.gpu_dirty_ = true;
return output;
}
#endif
const float* xp = x.as_fp32();
const float* wp = weight_.as_fp32();
float* rms_p = cache_.rms.as_fp32();
float* norm_p = cache_.normalized.as_fp32();
float* op = output.as_fp32();
for (size_t b = 0; b < batch; ++b) {
float ss = 0.0f;
for (size_t d = 0; d < dim_; ++d) {
float v = xp[b * dim_ + d];
ss += v * v;
}
float rms = std::sqrt(ss / static_cast<float>(dim_) + eps_);
rms_p[b] = rms;
for (size_t d = 0; d < dim_; ++d) {
float n = xp[b * dim_ + d] / rms;
norm_p[b * dim_ + d] = n;
op[b * dim_ + d] = n * wp[d];
}
}
return output;
}
RMSNorm::Gradients RMSNorm::backward(const Tensor& output_grad) {
Gradients grads;
size_t batch = cache_.input.numel() / dim_;
grads.weight_grad = Tensor({dim_}, QuantType::FP32);
grads.input_grad = Tensor(cache_.input.shape_, QuantType::FP32);
#ifdef USE_CUDA
if (CudaContext::instance().is_available() && output_grad.is_on_gpu()) {
grads.weight_grad.to_gpu();
grads.input_grad.to_gpu();
launch_rms_norm_backward(grads.input_grad.as_gpu_fp32(), grads.weight_grad.as_gpu_fp32(),
output_grad.as_gpu_fp32(), cache_.input.as_gpu_fp32(),
cache_.rms.as_gpu_fp32(), cache_.normalized.as_gpu_fp32(),
weight_.as_gpu_fp32(), batch, dim_,
CudaContext::instance().stream());
grads.weight_grad.gpu_dirty_ = true;
grads.input_grad.gpu_dirty_ = true;
return grads;
}
#endif
const float* og = output_grad.as_fp32();
const float* xp = cache_.input.as_fp32();
const float* rms_p = cache_.rms.as_fp32();
const float* norm_p = cache_.normalized.as_fp32();
const float* wp = weight_.as_fp32();
float* wg = grads.weight_grad.as_fp32();
float* ig = grads.input_grad.as_fp32();
memset(wg, 0, dim_ * sizeof(float));
for (size_t b = 0; b < batch; ++b) {
float rms = rms_p[b];
float inv_rms = 1.0f / rms;
for (size_t d = 0; d < dim_; ++d) {
wg[d] += og[b * dim_ + d] * norm_p[b * dim_ + d];
}
float dot_og_n = 0.0f;
for (size_t d = 0; d < dim_; ++d) {
dot_og_n += og[b * dim_ + d] * wp[d] * xp[b * dim_ + d];
}
for (size_t d = 0; d < dim_; ++d) {
float d_norm = og[b * dim_ + d] * wp[d];
float d_rms = -dot_og_n / (rms * static_cast<float>(dim_));
float d_ss = d_rms * 0.5f / rms;
ig[b * dim_ + d] = d_norm * inv_rms + 2.0f * xp[b * dim_ + d] * d_ss;
}
}
return grads;
}
#ifdef USE_CUDA
__global__ void kernel_rms_norm_forward_impl(float* out, const float* input, const float* weight,
float* rms, float* normalized,
size_t batch, size_t dim, float eps) {
size_t b = blockIdx.x;
if (b >= batch) return;
float ss = 0.0f;
for (size_t d = threadIdx.x; d < dim; d += blockDim.x) {
float v = input[b * dim + d];
ss += v * v;
}
__shared__ float s_ss;
if (threadIdx.x == 0) s_ss = 0.0f;
__syncthreads();
atomicAdd(&s_ss, ss);
__syncthreads();
float rms_val = sqrtf(s_ss / static_cast<float>(dim) + eps);
if (threadIdx.x == 0) rms[b] = rms_val;
for (size_t d = threadIdx.x; d < dim; d += blockDim.x) {
float n = input[b * dim + d] / rms_val;
normalized[b * dim + d] = n;
out[b * dim + d] = n * weight[d];
}
}
void launch_rms_norm_forward(float* out, const float* input, const float* weight,
float* rms, float* normalized, size_t batch, size_t dim,
float eps, cudaStream_t stream) {
int block = min(static_cast<int>(dim), 512);
kernel_rms_norm_forward_impl<<<static_cast<int>(batch), block, 0, stream>>>(
out, input, weight, rms, normalized, batch, dim, eps);
}
__global__ void kernel_rms_norm_backward_impl(float* input_grad, float* weight_grad,
const float* output_grad, const float* input,
const float* rms, const float* normalized,
const float* weight,
size_t batch, size_t dim) {
size_t b = blockIdx.x;
if (b >= batch) return;
float rms_val = rms[b];
float inv_rms = 1.0f / rms_val;
for (size_t d = threadIdx.x; d < dim; d += blockDim.x) {
atomicAdd(&weight_grad[d], output_grad[b * dim + d] * normalized[b * dim + d]);
}
__shared__ float s_dot;
if (threadIdx.x == 0) s_dot = 0.0f;
__syncthreads();
float local_dot = 0.0f;
for (size_t d = threadIdx.x; d < dim; d += blockDim.x) {
local_dot += output_grad[b * dim + d] * weight[d] * input[b * dim + d];
}
atomicAdd(&s_dot, local_dot);
__syncthreads();
for (size_t d = threadIdx.x; d < dim; d += blockDim.x) {
float d_norm = output_grad[b * dim + d] * weight[d];
float d_rms = -s_dot / (rms_val * static_cast<float>(dim));
float d_ss = d_rms * 0.5f / rms_val;
input_grad[b * dim + d] = d_norm * inv_rms + 2.0f * input[b * dim + d] * d_ss;
}
}
void launch_rms_norm_backward(float* input_grad, float* weight_grad,
const float* output_grad, const float* input,
const float* rms, const float* normalized,
const float* weight, size_t batch, size_t dim,
cudaStream_t stream) {
int block = min(static_cast<int>(dim), 512);
kernel_rms_norm_backward_impl<<<static_cast<int>(batch), block, 0, stream>>>(
input_grad, weight_grad, output_grad, input, rms, normalized, weight, batch, dim);
}
#endif
} // namespace neuroflow