Files
effectcraft/crates/sam2/src/weights.rs
T
Kristian MyramandClaude Sonnet 5.5 92b3a34732 S2.11: Hiera backbone matches PyTorch block by block
Weights loader with shape/finite validation (S2.10), patch embed, bicubic
positional embedding, window/global attention, query pooling. Reference test
(skips without weights): max abs error 2.6e-5 over all 12 blocks.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 08:43:30 +01:00

98 lines
3.8 KiB
Rust

// Modified work: loads the safetensors checkpoint of Hugging Face `transformers` SAM 2
// (Apache-2.0, Copyright The HuggingFace Inc. team). See NOTICE.
//! Checkpoint loading with validation: every tensor is f32 and finite, every lookup checks its
//! shape, so a truncated, mislabelled or poisoned file is an error, not a crash or garbage.
use std::collections::HashMap;
use std::path::Path;
use candle_core::{DType, Device, Tensor};
use crate::{Error, Result};
/// Named tensors of one checkpoint.
pub struct Weights {
map: HashMap<String, Tensor>,
}
impl Weights {
/// Read a `.safetensors` file; rejects non-finite values and unsupported dtypes.
pub fn open(path: &Path) -> Result<Weights> {
let map = candle_core::safetensors::load(path, &Device::Cpu).map_err(|e| Error::Weights(format!("{}: {e}", path.display())))?;
Weights::from_map(map)
}
/// Validate an already-loaded set of tensors.
pub fn from_map(map: HashMap<String, Tensor>) -> Result<Weights> {
let mut out = HashMap::with_capacity(map.len());
for (name, t) in map {
let t = match t.dtype() {
DType::F32 => t,
DType::F16 | DType::BF16 | DType::F64 => t.to_dtype(DType::F32)?,
other => return Err(Error::Weights(format!("`{name}` has unsupported dtype {other:?}"))),
};
let flat = t.flatten_all()?.to_vec1::<f32>()?;
if flat.iter().any(|v| !v.is_finite()) {
return Err(Error::Weights(format!("`{name}` contains NaN or infinity")));
}
out.insert(name, t);
}
Ok(Weights { map: out })
}
pub fn len(&self) -> usize {
self.map.len()
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
/// The tensor `name`, which must have exactly `shape`.
pub fn get(&self, name: &str, shape: &[usize]) -> Result<Tensor> {
let t = self.map.get(name).ok_or_else(|| Error::Weights(format!("missing tensor `{name}`")))?;
if t.dims() != shape {
return Err(Error::Weights(format!("`{name}` has shape {:?}, expected {shape:?}", t.dims())));
}
Ok(t.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn one(name: &str, t: Tensor) -> HashMap<String, Tensor> {
HashMap::from([(name.to_string(), t)])
}
#[test]
fn rejects_missing_wrong_shape_and_non_finite() {
let ok = Tensor::zeros((2, 3), DType::F32, &Device::Cpu).unwrap();
let w = Weights::from_map(one("a", ok)).unwrap();
assert!(w.get("a", &[2, 3]).is_ok());
assert!(matches!(w.get("a", &[3, 2]), Err(Error::Weights(_))));
assert!(matches!(w.get("b", &[1]), Err(Error::Weights(_))));
let bad = Tensor::new(&[1.0f32, f32::NAN], &Device::Cpu).unwrap();
assert!(matches!(Weights::from_map(one("n", bad)), Err(Error::Weights(_))));
let inf = Tensor::new(&[f32::INFINITY], &Device::Cpu).unwrap();
assert!(Weights::from_map(one("i", inf)).is_err());
let ints = Tensor::new(&[1u32], &Device::Cpu).unwrap();
assert!(Weights::from_map(one("u", ints)).is_err());
}
#[test]
fn a_truncated_file_is_an_error() {
let dir = std::env::temp_dir().join(format!("sam2-weights-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let p = dir.join("w.safetensors");
let t = Tensor::zeros((4, 4), DType::F32, &Device::Cpu).unwrap();
candle_core::safetensors::save(&one("x", t), &p).unwrap();
let bytes = std::fs::read(&p).unwrap();
std::fs::write(&p, &bytes[..bytes.len() - 8]).unwrap();
assert!(matches!(Weights::open(&p), Err(Error::Weights(_))));
assert!(matches!(Weights::open(&dir.join("absent")), Err(Error::Weights(_))));
std::fs::remove_dir_all(&dir).ok();
}
}