use serde_json::Value;
use crate::array::Array;
use crate::error::Result;
use crate::nn::{LayerNorm, Linear, WeightMap};
use crate::ops;
#[derive(Debug, Clone)]
pub struct UnifiedVisionConfig {
pub patch_size: i32,
pub pooling_kernel_size: i32,
pub mm_embed_dim: i32,
pub mm_posemb_size: i32,
pub num_soft_tokens: i32,
pub rms_norm_eps: f32,
}
impl UnifiedVisionConfig {
pub fn from_json(cfg: &Value) -> Self {
let gi = |k: &str, d: i32| {
cfg.get(k)
.and_then(|v| v.as_i64())
.map(|v| v as i32)
.unwrap_or(d)
};
let gf = |k: &str, d: f32| {
cfg.get(k)
.and_then(|v| v.as_f64())
.map(|v| v as f32)
.unwrap_or(d)
};
UnifiedVisionConfig {
patch_size: gi("patch_size", 16),
pooling_kernel_size: gi("pooling_kernel_size", 3),
mm_embed_dim: gi("mm_embed_dim", 3840),
mm_posemb_size: gi("mm_posemb_size", 1120),
num_soft_tokens: gi("num_soft_tokens", 280),
rms_norm_eps: gf("rms_norm_eps", 1e-6),
}
}
pub fn model_patch_size(&self) -> i32 {
self.patch_size * self.pooling_kernel_size
}
}
pub struct UnifiedVisionEmbedder {
model_patch_size: i32,
pooling_kernel_size: i32,
patch_ln1: LayerNorm,
patch_dense: Linear,
patch_ln2: LayerNorm,
pos_embedding: Array,
pos_norm: LayerNorm,
}
impl UnifiedVisionEmbedder {
pub fn load(w: &mut WeightMap, cfg: &UnifiedVisionConfig) -> Result<Self> {
let prefix = "vision_embedder";
let eps = cfg.rms_norm_eps;
Ok(UnifiedVisionEmbedder {
model_patch_size: cfg.model_patch_size(),
pooling_kernel_size: cfg.pooling_kernel_size,
patch_ln1: w.layer_norm(&format!("{prefix}.patch_ln1"), eps)?,
patch_dense: w.linear(&format!("{prefix}.patch_dense"))?,
patch_ln2: w.layer_norm(&format!("{prefix}.patch_ln2"), eps)?,
pos_embedding: w.take(&format!("{prefix}.pos_embedding"))?,
pos_norm: w.layer_norm(&format!("{prefix}.pos_norm"), eps)?,
})
}
pub fn forward(&self, pixel_values: &Array, patch_h: i32, patch_w: i32) -> Result<Array> {
let k = self.pooling_kernel_size;
let (grid_h, grid_w) = (patch_h / k, patch_w / k);
let patches = self.patchify(pixel_values, grid_h, grid_w)?;
let positions = build_positions(grid_h, grid_w);
let hidden = self.patch_ln1.forward(&patches)?;
let hidden = self.patch_dense.forward(&hidden)?;
let hidden = self.patch_ln2.forward(&hidden)?;
let hidden = self.add_position_embeddings(&hidden, &positions)?;
let hidden = self.pos_norm.forward(&hidden)?;
ops::expand_dims(&hidden, 0)
}
fn patchify(&self, pixel_values: &Array, grid_h: i32, grid_w: i32) -> Result<Array> {
let shape = pixel_values.shape();
let c = shape[1];
let p = self.model_patch_size;
let patches = ops::reshape(pixel_values, &[1, c, grid_h, p, grid_w, p])?;
let patches = ops::transpose_axes(&patches, &[0, 2, 4, 3, 5, 1])?;
let patches = ops::reshape(&patches, &[grid_h * grid_w, p * p * c])?;
let target_dtype = self.patch_dense.weight_dtype(patches.dtype());
ops::astype(&patches, target_dtype)
}
fn add_position_embeddings(&self, hidden: &Array, positions: &Array) -> Result<Array> {
let dtype = hidden.dtype();
let n = positions.dim(0);
let d = self.pos_embedding.dim(2);
let pes = self.pos_embedding.dim(0);
let x_idx = ops::reshape(&ops::slice(positions, &[0, 0], &[n, 1])?, &[n])?;
let y_idx = ops::reshape(&ops::slice(positions, &[0, 1], &[n, 2])?, &[n])?;
let plane_x = ops::reshape(
&ops::slice(&self.pos_embedding, &[0, 0, 0], &[pes, 1, d])?,
&[pes, d],
)?;
let plane_y = ops::reshape(
&ops::slice(&self.pos_embedding, &[0, 1, 0], &[pes, 2, d])?,
&[pes, d],
)?;
let pe_x = ops::astype(&ops::take_axis(&plane_x, &x_idx, 0)?, dtype)?;
let pe_y = ops::astype(&ops::take_axis(&plane_y, &y_idx, 0)?, dtype)?;
let pe = ops::add(&pe_x, &pe_y)?;
ops::add(hidden, &pe)
}
}
fn build_positions(grid_h: i32, grid_w: i32) -> Array {
let n = (grid_h * grid_w) as usize;
let mut data = Vec::with_capacity(n * 2);
for row in 0..grid_h {
for col in 0..grid_w {
data.push(col);
data.push(row);
}
}
Array::from_slice(&data, &[grid_h * grid_w, 2])
}