Skip to content

Add Support for RMSNorm and optional QKV Biases to Onnx backends - #2427

Open
john-sp wants to merge 4 commits into
LeelaChessZero:masterfrom
john-sp:onnx-rms-qkv
Open

Add Support for RMSNorm and optional QKV Biases to Onnx backends#2427
john-sp wants to merge 4 commits into
LeelaChessZero:masterfrom
john-sp:onnx-rms-qkv

Conversation

@john-sp

@john-sp john-sp commented Jul 11, 2026

Copy link
Copy Markdown
Member

Add ONNX conversion support for attention networks that use RMSNorm and omit
Q/K/V projection biases.

By Menkib:

  • Detect empty LayerNorm beta tensors and export RMSNorm instead.
  • Add an alt_rmsnorm fallback that expresses RMSNorm using standard ONNX nodes, keeping it compatible with runtimes that do not support the native RMSNormalization operator.
  • Add native RMSNormalization export support for opset 23.
  • Select the RMSNorm fallback by default where native RMSNormalization is not expected to be supported.

By me:

  • Infer encoder projection width from q_w when q_b is absent, and omit Q/K/V bias-add nodes for missing bias tensors.

Planned follow up PR: Updating TensorRT version to support native RMSNorm node

This has been tested locally on test networks.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the ONNX conversion/export path to handle newer attention weight formats by (1) exporting RMSNorm when LayerNorm betas are missing/empty (with an optional discrete fallback for runtimes lacking native RMSNormalization) and (2) supporting attention Q/K/V projections with optional (missing) bias tensors.

Changes:

  • Add RMSNorm export support, including a discrete ONNX-node fallback and native RMSNormalization emission for opset 23.
  • Detect “LayerNorm with empty betas” and export as RMSNorm instead.
  • Support missing Q/K/V projection biases by inferring projection width from q_w and skipping bias-add nodes when biases are absent.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/neural/onnx/converter.h Adds alt_rmsnorm option to select discrete RMSNorm fallback behavior.
src/neural/onnx/converter.cc Implements RMSNorm (native + fallback), auto-detects RMSNorm via empty betas, and makes Q/K/V bias adds optional.
src/neural/onnx/builder.h Adds RMSNormalization(...) builder API.
src/neural/onnx/builder.cc Extends supported opset range to 23 and emits native RMSNormalization nodes.
src/neural/backends/onnx/network_onnx.cc Wires opset and alt_rmsnorm into runtime conversion option parsing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 981 to 983
converter_options.data_type =
WeightsToOnnxConverterOptions::StringToDataType(datatype);
converter_options.opset = opts.GetOrDefault<int>(
"opset", converter_options.data_type ==
WeightsToOnnxConverterOptions::DataType::kBFloat16
? 22
: 17);

Comment on lines +48 to 50
if (ir < 3 || ir > 11) {
throw Exception("Only ONNX IR between 3 and 10 is supported.");
}
Comment on lines +397 to +408
// Only supported since opset 23
std::string OnnxBuilder::RMSNormalization(const std::string& name,
const std::string& input,
const OnnxConst& scale, int axis,
float epsilon) {
auto* node = model_.mutable_graph()->add_node();
auto out = PopulateStdNodeFields(node, name, input, "RMSNormalization");
node->add_input(AddInitializer(name + "/w/scale", scale));
AddIntAttribute(node, "axis", axis);
AddFloatAttribute(node, "epsilon", epsilon);
return out;
}
Comment on lines +561 to 569
// Q/K/V biases are optional in newer weight files. The projection width is
// normally available from q_b, but must be inferred from q_w when it is
// omitted. ONNX's MatMul does not need a bias input, so simply skip the
// corresponding Add in that case.
const int d_model = layer.mha.q_b.empty()
? layer.mha.q_w.size() / embedding_size
: layer.mha.q_b.size();
const int depth = d_model / heads;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants