use crate::model::{DataType, Model, TensorData};
pub fn detect_layers(model: &Model, prefix: &str) -> Vec<u32> {
let mut ids: Vec<u32> = model
.tensors
.keys()
.filter_map(|k| {
let tail = k.strip_prefix(prefix)?;
let (idx, _) = tail.split_once('.')?;
idx.parse::<u32>().ok()
})
.collect();
ids.sort_unstable();
ids.dedup();
ids
}
pub fn llama_layers(model: &Model) -> Vec<u32> {
let mut ids: Vec<u32> = Vec::new();
for prefix in ["model.layers.", "layers."] {
for id in detect_layers(model, prefix) {
if !ids.contains(&id) {
ids.push(id);
}
}
}
ids.sort_unstable();
ids.dedup();
ids
}
pub fn has_pair(model: &Model, weight_key: &str, bias_key: &str) -> bool {
let w = match model.tensors.get(weight_key) {
Some(t) => t,
None => return false,
};
model.tensors.contains_key(bias_key) && w.dtype == DataType::F32
}
pub fn tensor_rows(tensor: &TensorData) -> Option<usize> {
tensor.shape.first().copied()
}
pub fn tensor_cols(tensor: &TensorData) -> Option<usize> {
tensor.shape.get(1).copied()
}
pub fn gpt2_hidden_dim(model: &Model, layers: &[u32]) -> usize {
if let Some(&l0) = layers.first() {
let p = format!("transformer.h.{l0}.");
for (w, b) in [
(
format!("{p}attn.c_attn.weight"),
format!("{p}attn.c_attn.bias"),
),
(format!("{p}ln_1.weight"), format!("{p}ln_1.bias")),
] {
if let Some(t) = model.tensors.get(&w).or_else(|| model.tensors.get(&b))
&& let Some(cols) = tensor_cols(t).or_else(|| tensor_rows(t))
{
if w.ends_with("c_attn.weight")
&& let Some(c) = tensor_cols(t)
{
return c;
}
return cols;
}
}
}
model
.tensors
.get("transformer.wte.weight")
.and_then(tensor_cols)
.or_else(|| model.tensors.get("lm_head.weight").and_then(tensor_cols))
.unwrap_or(0)
}
pub fn gpt2_head_count(hidden: usize) -> usize {
if hidden == 0 { 1 } else { (hidden / 64).max(1) }
}
pub fn llama_hidden_dim(model: &Model, layers: &[u32]) -> usize {
if let Some(&l0) = layers.first() {
let p = format!("model.layers.{l0}.");
for key in [
format!("{p}self_attn.q_proj.weight"),
format!("{p}input_layernorm.weight"),
] {
if let Some(t) = model.tensors.get(&key) {
if key.ends_with("q_proj.weight")
&& let Some(c) = tensor_cols(t)
{
return c;
}
if let Some(r) = tensor_rows(t) {
return r;
}
}
}
}
model
.tensors
.get("model.embed_tokens.weight")
.and_then(tensor_cols)
.or_else(|| model.tensors.get("lm_head.weight").and_then(tensor_cols))
.unwrap_or(0)
}
pub fn llama_head_count(model: &Model, hidden: usize) -> usize {
if let Some(h) = model
.metadata
.get("attention.head_count")
.and_then(|s| s.parse::<usize>().ok())
{
return h.max(1);
}
if hidden == 0 { 1 } else { (hidden / 64).max(1) }
}