Whole-workspace rustfmt pass picked up while iterating on Mamba GPU backward work. Verified formatting-only via diff sampling; no logic changed. Co-Authored-By: Claude Sonnet 5 <[email protected]>
689 lines
20 KiB
Rust
689 lines
20 KiB
Rust
//! Simplified SimMIM (Simple Masked Image Modeling) Implementation
|
|
//!
|
|
//! This is a simplified version that works with the current tensor library state.
|
|
//! It focuses on the algorithmic structure and can be upgraded when tensor operations are stable.
|
|
|
|
use crate::prelude::*;
|
|
use parking_lot::RwLock;
|
|
use std::sync::Arc;
|
|
|
|
/// Mock tensor for simplified implementation
|
|
#[derive(Debug, Clone)]
|
|
pub struct SimpleTensor {
|
|
data: Vec<f32>,
|
|
shape: Vec<usize>,
|
|
device: Device,
|
|
}
|
|
|
|
impl SimpleTensor {
|
|
pub fn zeros(shape: Vec<usize>, device: &Device) -> Result<Self> {
|
|
let size = shape.iter().product();
|
|
Ok(Self {
|
|
data: vec![0.0; size],
|
|
shape,
|
|
device: device.clone(),
|
|
})
|
|
}
|
|
|
|
pub fn randn(shape: Vec<usize>, device: &Device) -> Result<Self> {
|
|
let size = shape.iter().product();
|
|
let mut data = Vec::with_capacity(size);
|
|
let mut seed = 42u32;
|
|
for _ in 0..size {
|
|
seed = seed.wrapping_mul(1103515245).wrapping_add(12345);
|
|
let val = (seed as f32 / u32::MAX as f32) * 2.0 - 1.0;
|
|
data.push(val);
|
|
}
|
|
Ok(Self {
|
|
data,
|
|
shape,
|
|
device: device.clone(),
|
|
})
|
|
}
|
|
|
|
pub fn from_data(data: Vec<f32>, shape: Vec<usize>, device: &Device) -> Result<Self> {
|
|
Ok(Self {
|
|
data,
|
|
shape,
|
|
device: device.clone(),
|
|
})
|
|
}
|
|
|
|
pub fn shape(&self) -> &[usize] {
|
|
&self.shape
|
|
}
|
|
pub fn device(&self) -> &Device {
|
|
&self.device
|
|
}
|
|
|
|
pub fn to_vec<T>(&self) -> Result<Vec<T>>
|
|
where
|
|
T: Clone + From<f32>,
|
|
{
|
|
Ok(self.data.iter().map(|&x| T::from(x)).collect())
|
|
}
|
|
|
|
pub fn matmul(&self, _other: &Self) -> Result<Self> {
|
|
// Simplified matrix multiplication - just return a tensor of appropriate shape
|
|
let result_shape = vec![self.shape[0], _other.shape[1]];
|
|
let result_data = vec![1.0; result_shape.iter().product()];
|
|
Ok(Self {
|
|
data: result_data,
|
|
shape: result_shape,
|
|
device: self.device.clone(),
|
|
})
|
|
}
|
|
|
|
pub fn add(&self, other: &Self) -> Result<Self> {
|
|
let mut result_data = self.data.clone();
|
|
for (i, &val) in other.data.iter().enumerate() {
|
|
if i < result_data.len() {
|
|
result_data[i] += val;
|
|
}
|
|
}
|
|
Ok(Self {
|
|
data: result_data,
|
|
shape: self.shape.clone(),
|
|
device: self.device.clone(),
|
|
})
|
|
}
|
|
|
|
pub fn sub(&self, other: &Self) -> Result<Self> {
|
|
let mut result_data = self.data.clone();
|
|
for (i, &val) in other.data.iter().enumerate() {
|
|
if i < result_data.len() {
|
|
result_data[i] -= val;
|
|
}
|
|
}
|
|
Ok(Self {
|
|
data: result_data,
|
|
shape: self.shape.clone(),
|
|
device: self.device.clone(),
|
|
})
|
|
}
|
|
|
|
pub fn abs(&self) -> Result<Self> {
|
|
let result_data = self.data.iter().map(|x| x.abs()).collect();
|
|
Ok(Self {
|
|
data: result_data,
|
|
shape: self.shape.clone(),
|
|
device: self.device.clone(),
|
|
})
|
|
}
|
|
|
|
pub fn pow_scalar(&self, exp: f32) -> Result<Self> {
|
|
let result_data = self.data.iter().map(|x| x.powf(exp)).collect();
|
|
Ok(Self {
|
|
data: result_data,
|
|
shape: self.shape.clone(),
|
|
device: self.device.clone(),
|
|
})
|
|
}
|
|
|
|
pub fn mean(&self, _dims: &[usize]) -> Result<Self> {
|
|
let mean_val = if self.data.is_empty() {
|
|
0.0
|
|
} else {
|
|
self.data.iter().sum::<f32>() / self.data.len() as f32
|
|
};
|
|
Ok(Self {
|
|
data: vec![mean_val],
|
|
shape: vec![],
|
|
device: self.device.clone(),
|
|
})
|
|
}
|
|
|
|
pub fn reshape(&self, new_shape: &[usize]) -> Result<Self> {
|
|
Ok(Self {
|
|
data: self.data.clone(),
|
|
shape: new_shape.to_vec(),
|
|
device: self.device.clone(),
|
|
})
|
|
}
|
|
|
|
pub fn full(shape: &[usize], value: f32, device: &Device) -> Result<Self> {
|
|
let size = shape.iter().product();
|
|
Ok(Self {
|
|
data: vec![value; size],
|
|
shape: shape.to_vec(),
|
|
device: device.clone(),
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Configuration for different encoder types
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub enum EncoderType {
|
|
/// Vision Transformer encoder
|
|
ViT,
|
|
/// Swin Transformer encoder
|
|
Swin,
|
|
}
|
|
|
|
/// Type of prediction head
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub enum PredictionHeadType {
|
|
/// Simple linear layer
|
|
Linear,
|
|
}
|
|
|
|
/// Loss function type
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub enum LossType {
|
|
/// L1 (MAE) loss
|
|
L1,
|
|
/// L2 (MSE) loss
|
|
L2,
|
|
}
|
|
|
|
/// SimMIM configuration parameters
|
|
#[derive(Debug, Clone)]
|
|
pub struct SimMIMConfig {
|
|
/// Fraction of patches to mask
|
|
pub mask_ratio: f32,
|
|
/// Mask patch size (for hierarchical masking in Swin)
|
|
pub mask_patch_size: usize,
|
|
/// Type of prediction head
|
|
pub prediction_head: PredictionHeadType,
|
|
/// Loss function type
|
|
pub loss_type: LossType,
|
|
/// Whether to normalize pixel targets
|
|
pub norm_pix_loss: bool,
|
|
/// Encoder embedding dimension
|
|
pub encoder_dim: usize,
|
|
/// Image patch size
|
|
pub patch_size: usize,
|
|
/// Type of encoder (ViT or Swin)
|
|
pub encoder_type: EncoderType,
|
|
/// Input image size
|
|
pub image_size: usize,
|
|
/// Number of input channels
|
|
pub in_channels: usize,
|
|
}
|
|
|
|
impl Default for SimMIMConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
mask_ratio: 0.6,
|
|
mask_patch_size: 32,
|
|
prediction_head: PredictionHeadType::Linear,
|
|
loss_type: LossType::L1,
|
|
norm_pix_loss: false,
|
|
encoder_dim: 768,
|
|
patch_size: 16,
|
|
encoder_type: EncoderType::ViT,
|
|
image_size: 224,
|
|
in_channels: 3,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SimMIMConfig {
|
|
/// Create configuration optimized for ViT
|
|
pub fn for_vit() -> Self {
|
|
Self {
|
|
encoder_type: EncoderType::ViT,
|
|
mask_ratio: 0.6,
|
|
mask_patch_size: 16,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
/// Create configuration optimized for Swin Transformer
|
|
pub fn for_swin() -> Self {
|
|
Self {
|
|
encoder_type: EncoderType::Swin,
|
|
mask_ratio: 0.32,
|
|
mask_patch_size: 32,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
/// Set mask ratio
|
|
pub fn with_mask_ratio(mut self, mask_ratio: f32) -> Self {
|
|
self.mask_ratio = mask_ratio.clamp(0.0, 1.0);
|
|
self
|
|
}
|
|
|
|
/// Set encoder type
|
|
pub fn with_encoder_type(mut self, encoder_type: EncoderType) -> Self {
|
|
self.encoder_type = encoder_type;
|
|
self
|
|
}
|
|
|
|
/// Set whether to normalize pixel loss
|
|
pub fn with_norm_pix_loss(mut self, norm_pix_loss: bool) -> Self {
|
|
self.norm_pix_loss = norm_pix_loss;
|
|
self
|
|
}
|
|
}
|
|
|
|
/// Result from random patch masking
|
|
#[derive(Debug)]
|
|
pub struct PatchMaskResult {
|
|
/// Boolean mask for each patch (true = masked, false = visible)
|
|
pub mask_indices: Vec<Vec<bool>>,
|
|
/// Indices of visible patches for each batch item
|
|
pub visible_indices: Vec<Vec<usize>>,
|
|
/// Total number of masked patches per batch item
|
|
pub num_masked_patches: usize,
|
|
}
|
|
|
|
/// Random patch masker for SimMIM
|
|
#[derive(Debug)]
|
|
pub struct RandomPatchMasker {
|
|
mask_ratio: f32,
|
|
}
|
|
|
|
impl RandomPatchMasker {
|
|
/// Create new random patch masker
|
|
pub fn new(mask_ratio: f32) -> Self {
|
|
Self {
|
|
mask_ratio: mask_ratio.clamp(0.0, 1.0),
|
|
}
|
|
}
|
|
|
|
/// Get mask ratio
|
|
pub fn mask_ratio(&self) -> f32 {
|
|
self.mask_ratio
|
|
}
|
|
|
|
/// Generate random patch mask for a batch
|
|
pub fn mask_patches(
|
|
&self,
|
|
patches: &SimpleTensor,
|
|
seed: Option<u64>,
|
|
) -> Result<PatchMaskResult> {
|
|
let shape = patches.shape();
|
|
if shape.len() != 3 {
|
|
return Err(TransformerError::InvalidInput(
|
|
"Patches tensor must be 3D [batch, patches, embed_dim]".into(),
|
|
));
|
|
}
|
|
|
|
let batch_size = shape[0];
|
|
let num_patches = shape[1];
|
|
|
|
if num_patches == 0 {
|
|
return Err(TransformerError::InvalidInput(
|
|
"Number of patches cannot be zero".into(),
|
|
));
|
|
}
|
|
|
|
let num_masked = (num_patches as f32 * self.mask_ratio).round() as usize;
|
|
let num_visible = num_patches - num_masked;
|
|
|
|
let mut mask_indices = Vec::new();
|
|
let mut visible_indices = Vec::new();
|
|
|
|
let mut rng_state = seed.unwrap_or(42);
|
|
|
|
for _ in 0..batch_size {
|
|
// Create all patch indices
|
|
let mut indices: Vec<usize> = (0..num_patches).collect();
|
|
|
|
// Shuffle indices deterministically
|
|
for i in (1..indices.len()).rev() {
|
|
rng_state = rng_state.wrapping_mul(1103515245).wrapping_add(12345);
|
|
let j = (rng_state as usize) % (i + 1);
|
|
indices.swap(i, j);
|
|
}
|
|
|
|
// Select visible patches
|
|
let visible = indices[..num_visible].to_vec();
|
|
visible_indices.push(visible.clone());
|
|
|
|
// Create boolean mask
|
|
let mut mask = vec![true; num_patches]; // Start with all masked
|
|
for &idx in &visible {
|
|
mask[idx] = false; // Mark visible patches
|
|
}
|
|
mask_indices.push(mask);
|
|
}
|
|
|
|
Ok(PatchMaskResult {
|
|
mask_indices,
|
|
visible_indices,
|
|
num_masked_patches: num_masked,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Learnable mask token embedding
|
|
#[derive(Debug)]
|
|
pub struct MaskTokenEmbedding {
|
|
token: Arc<RwLock<SimpleTensor>>,
|
|
embed_dim: usize,
|
|
device: Device,
|
|
}
|
|
|
|
impl MaskTokenEmbedding {
|
|
/// Create new mask token embedding
|
|
pub fn new(embed_dim: usize, device: &Device) -> Result<Self> {
|
|
let token = SimpleTensor::randn(vec![1, embed_dim], device)?;
|
|
|
|
Ok(Self {
|
|
token: Arc::new(RwLock::new(token)),
|
|
embed_dim,
|
|
device: device.clone(),
|
|
})
|
|
}
|
|
|
|
/// Get embedding dimension
|
|
pub fn embed_dim(&self) -> usize {
|
|
self.embed_dim
|
|
}
|
|
|
|
/// Get the mask token
|
|
pub fn get_token(&self) -> Result<SimpleTensor> {
|
|
let token = self.token.read();
|
|
Ok(token.clone())
|
|
}
|
|
|
|
/// Broadcast mask token to specified shape
|
|
pub fn broadcast(&self, batch_size: usize, num_masked: usize) -> Result<SimpleTensor> {
|
|
let token = self.token.read();
|
|
let token_data = token.to_vec::<f32>()?;
|
|
let mut broadcasted_data = vec![0.0f32; batch_size * num_masked * self.embed_dim];
|
|
|
|
for b in 0..batch_size {
|
|
for m in 0..num_masked {
|
|
let base_idx = b * num_masked * self.embed_dim + m * self.embed_dim;
|
|
for d in 0..self.embed_dim {
|
|
broadcasted_data[base_idx + d] = token_data[d % token_data.len()];
|
|
}
|
|
}
|
|
}
|
|
|
|
SimpleTensor::from_data(
|
|
broadcasted_data,
|
|
vec![batch_size, num_masked, self.embed_dim],
|
|
&self.device,
|
|
)
|
|
}
|
|
}
|
|
|
|
/// Linear prediction head for pixel reconstruction
|
|
#[derive(Debug)]
|
|
pub struct LinearPredictionHead {
|
|
linear: Arc<RwLock<SimpleTensor>>,
|
|
bias: Arc<RwLock<SimpleTensor>>,
|
|
input_dim: usize,
|
|
output_dim: usize,
|
|
device: Device,
|
|
}
|
|
|
|
impl LinearPredictionHead {
|
|
/// Create new linear prediction head
|
|
pub fn new(input_dim: usize, output_dim: usize, device: &Device) -> Result<Self> {
|
|
let linear = SimpleTensor::randn(vec![input_dim, output_dim], device)?;
|
|
let bias = SimpleTensor::zeros(vec![output_dim], device)?;
|
|
|
|
Ok(Self {
|
|
linear: Arc::new(RwLock::new(linear)),
|
|
bias: Arc::new(RwLock::new(bias)),
|
|
input_dim,
|
|
output_dim,
|
|
device: device.clone(),
|
|
})
|
|
}
|
|
|
|
/// Get input dimension
|
|
pub fn input_dim(&self) -> usize {
|
|
self.input_dim
|
|
}
|
|
|
|
/// Get output dimension
|
|
pub fn output_dim(&self) -> usize {
|
|
self.output_dim
|
|
}
|
|
|
|
/// Forward pass through prediction head
|
|
pub fn forward(&self, input: &SimpleTensor) -> Result<SimpleTensor> {
|
|
let linear = self.linear.read();
|
|
let bias = self.bias.read();
|
|
|
|
let output = input.matmul(&*linear)?;
|
|
output.add(&*bias)
|
|
}
|
|
}
|
|
|
|
/// SimMIM loss computation utilities
|
|
pub struct SimMIMLoss;
|
|
|
|
impl SimMIMLoss {
|
|
/// Compute L1 (MAE) loss between predicted and target pixels
|
|
pub fn compute_l1_loss(
|
|
predicted: &SimpleTensor,
|
|
target: &SimpleTensor,
|
|
) -> Result<SimpleTensor> {
|
|
let pred_shape = predicted.shape();
|
|
let target_shape = target.shape();
|
|
|
|
if pred_shape != target_shape {
|
|
return Err(TransformerError::InvalidInput(format!(
|
|
"Predicted and target shapes must match: {:?} vs {:?}",
|
|
pred_shape, target_shape
|
|
)));
|
|
}
|
|
|
|
let diff = predicted.sub(target)?;
|
|
let abs_diff = diff.abs()?;
|
|
abs_diff.mean(&[])
|
|
}
|
|
|
|
/// Compute normalized L1 loss
|
|
pub fn compute_l1_loss_normalized(
|
|
predicted: &SimpleTensor,
|
|
target: &SimpleTensor,
|
|
) -> Result<SimpleTensor> {
|
|
Self::compute_l1_loss(predicted, target)
|
|
}
|
|
|
|
/// Compute L2 (MSE) loss between predicted and target pixels
|
|
pub fn compute_l2_loss(
|
|
predicted: &SimpleTensor,
|
|
target: &SimpleTensor,
|
|
) -> Result<SimpleTensor> {
|
|
let pred_shape = predicted.shape();
|
|
let target_shape = target.shape();
|
|
|
|
if pred_shape != target_shape {
|
|
return Err(TransformerError::InvalidInput(format!(
|
|
"Predicted and target shapes must match: {:?} vs {:?}",
|
|
pred_shape, target_shape
|
|
)));
|
|
}
|
|
|
|
let diff = predicted.sub(target)?;
|
|
let squared_diff = diff.pow_scalar(2.0)?;
|
|
squared_diff.mean(&[])
|
|
}
|
|
}
|
|
|
|
/// Result from SimMIM training step
|
|
#[derive(Debug)]
|
|
pub struct SimMIMTrainingResult {
|
|
/// Reconstruction loss
|
|
pub loss: SimpleTensor,
|
|
/// Number of masked patches in this batch
|
|
pub num_masked_patches: usize,
|
|
/// Actual mask ratio used
|
|
pub mask_ratio: f32,
|
|
}
|
|
|
|
/// Main SimMIM trainer
|
|
#[derive(Debug)]
|
|
pub struct SimMIMTrainer {
|
|
config: SimMIMConfig,
|
|
masker: RandomPatchMasker,
|
|
mask_token: MaskTokenEmbedding,
|
|
prediction_head: LinearPredictionHead,
|
|
encoder: Arc<RwLock<SimpleTensor>>,
|
|
device: Device,
|
|
is_training: bool,
|
|
}
|
|
|
|
impl SimMIMTrainer {
|
|
/// Create new SimMIM trainer
|
|
pub fn new(
|
|
config: SimMIMConfig,
|
|
in_channels: usize,
|
|
image_size: usize,
|
|
device: &Device,
|
|
) -> Result<Self> {
|
|
let masker = RandomPatchMasker::new(config.mask_ratio);
|
|
let mask_token = MaskTokenEmbedding::new(config.encoder_dim, device)?;
|
|
|
|
let patch_volume = config.patch_size * config.patch_size * in_channels;
|
|
let prediction_head = LinearPredictionHead::new(config.encoder_dim, patch_volume, device)?;
|
|
|
|
let encoder = SimpleTensor::randn(vec![patch_volume, config.encoder_dim], device)?;
|
|
|
|
Ok(Self {
|
|
config,
|
|
masker,
|
|
mask_token,
|
|
prediction_head,
|
|
encoder: Arc::new(RwLock::new(encoder)),
|
|
device: device.clone(),
|
|
is_training: true,
|
|
})
|
|
}
|
|
|
|
/// Get configuration
|
|
pub fn config(&self) -> &SimMIMConfig {
|
|
&self.config
|
|
}
|
|
|
|
/// Get device
|
|
pub fn device(&self) -> &Device {
|
|
&self.device
|
|
}
|
|
|
|
/// Set to training mode
|
|
pub fn train(&mut self) {
|
|
self.is_training = true;
|
|
}
|
|
|
|
/// Set to evaluation mode
|
|
pub fn eval(&mut self) {
|
|
self.is_training = false;
|
|
}
|
|
|
|
/// Check if in training mode
|
|
pub fn is_training(&self) -> bool {
|
|
self.is_training
|
|
}
|
|
|
|
/// Perform one SimMIM training step
|
|
pub fn train_step(
|
|
&mut self,
|
|
images: &SimpleTensor,
|
|
seed: Option<u64>,
|
|
) -> Result<SimMIMTrainingResult> {
|
|
let patches = self.extract_patches(images)?;
|
|
let target_pixels = patches.clone();
|
|
|
|
let mask_result = self.masker.mask_patches(&patches, seed)?;
|
|
let masked_patches = self.apply_mask_tokens(&patches, &mask_result)?;
|
|
let encoded_features = self.encode_patches(&masked_patches)?;
|
|
let masked_features = self.extract_masked_features(&encoded_features, &mask_result)?;
|
|
let predicted_pixels = self.prediction_head.forward(&masked_features)?;
|
|
let target_masked_pixels = self.extract_masked_targets(&target_pixels, &mask_result)?;
|
|
|
|
let loss = match self.config.loss_type {
|
|
LossType::L1 => {
|
|
if self.config.norm_pix_loss {
|
|
SimMIMLoss::compute_l1_loss_normalized(
|
|
&predicted_pixels,
|
|
&target_masked_pixels,
|
|
)?
|
|
} else {
|
|
SimMIMLoss::compute_l1_loss(&predicted_pixels, &target_masked_pixels)?
|
|
}
|
|
}
|
|
LossType::L2 => SimMIMLoss::compute_l2_loss(&predicted_pixels, &target_masked_pixels)?,
|
|
};
|
|
|
|
Ok(SimMIMTrainingResult {
|
|
loss,
|
|
num_masked_patches: mask_result.num_masked_patches,
|
|
mask_ratio: self.masker.mask_ratio(),
|
|
})
|
|
}
|
|
|
|
/// Extract features in evaluation mode
|
|
pub fn extract_features(&self, images: &SimpleTensor) -> Result<SimpleTensor> {
|
|
let patches = self.extract_patches(images)?;
|
|
let encoded = self.encode_patches(&patches)?;
|
|
encoded.mean(&[1])
|
|
}
|
|
|
|
fn extract_patches(&self, images: &SimpleTensor) -> Result<SimpleTensor> {
|
|
let shape = images.shape();
|
|
let batch_size = shape[0];
|
|
let channels = shape[1];
|
|
let height = shape[2];
|
|
let width = shape[3];
|
|
|
|
let patch_size = self.config.patch_size;
|
|
let patches_per_row = height / patch_size;
|
|
let patches_per_col = width / patch_size;
|
|
let num_patches = patches_per_row * patches_per_col;
|
|
let patch_volume = patch_size * patch_size * channels;
|
|
|
|
images.reshape(&[batch_size, num_patches, patch_volume])
|
|
}
|
|
|
|
fn apply_mask_tokens(
|
|
&self,
|
|
patches: &SimpleTensor,
|
|
mask_result: &PatchMaskResult,
|
|
) -> Result<SimpleTensor> {
|
|
// Simplified implementation - just return the original patches
|
|
Ok(patches.clone())
|
|
}
|
|
|
|
fn encode_patches(&self, patches: &SimpleTensor) -> Result<SimpleTensor> {
|
|
let encoder = self.encoder.read();
|
|
patches.matmul(&*encoder)
|
|
}
|
|
|
|
fn extract_masked_features(
|
|
&self,
|
|
features: &SimpleTensor,
|
|
mask_result: &PatchMaskResult,
|
|
) -> Result<SimpleTensor> {
|
|
let shape = features.shape();
|
|
let batch_size = shape[0];
|
|
let embed_dim = shape[2];
|
|
let num_masked = mask_result.num_masked_patches;
|
|
|
|
let result_data = vec![1.0f32; batch_size * num_masked * embed_dim];
|
|
SimpleTensor::from_data(
|
|
result_data,
|
|
vec![batch_size, num_masked, embed_dim],
|
|
&self.device,
|
|
)
|
|
}
|
|
|
|
fn extract_masked_targets(
|
|
&self,
|
|
targets: &SimpleTensor,
|
|
mask_result: &PatchMaskResult,
|
|
) -> Result<SimpleTensor> {
|
|
let shape = targets.shape();
|
|
let batch_size = shape[0];
|
|
let patch_volume = shape[2];
|
|
let num_masked = mask_result.num_masked_patches;
|
|
|
|
let result_data = vec![0.5f32; batch_size * num_masked * patch_volume];
|
|
SimpleTensor::from_data(
|
|
result_data,
|
|
vec![batch_size, num_masked, patch_volume],
|
|
&self.device,
|
|
)
|
|
}
|
|
}
|