|
|
|
|
|
|
| from typing import Tuple
|
| import numpy as np
|
| import torch
|
| import torch.nn.functional as F
|
| from torch import Tensor
|
| from functools import lru_cache
|
|
|
| @torch.no_grad()
|
| def _resize_2d(x: Tensor, shape: Tuple[int, int],
|
| interpolation: str = "bicubic",
|
| antialias: bool = True) -> Tensor:
|
| """
|
| Resize a 2D tensor x[h0,w0] -> shape[h,w] using torch interpolate.
|
| Matches the "wrap with [None,None,...]" trick from your flex_patch_embed.py.
|
| """
|
| x_resized = F.interpolate(
|
| x[None, None, ...],
|
| shape,
|
| mode=interpolation,
|
| antialias=antialias,
|
| )
|
| return x_resized[0, 0, ...]
|
|
|
|
|
| @lru_cache(maxsize=256)
|
| def _calculate_pinv_2d(old_shape: Tuple[int, int],
|
| new_shape: Tuple[int, int],
|
| interpolation: str = "bicubic",
|
| antialias: bool = True,
|
| device: torch.device = torch.device("cpu"),
|
| dtype: torch.dtype = torch.float32) -> Tensor:
|
| """
|
| Build the (flattened) resize matrix R s.t. vec(new) = R @ vec(old),
|
| then return pinv(R). This mirrors your flex_patch_embed.py approach.
|
|
|
| Args:
|
| old_shape: (h0, w0)
|
| new_shape: (h, w)
|
| Returns:
|
| pinv(R): Tensor of shape [(h*w), (h0*w0)]
|
| """
|
|
|
| mat = []
|
| h0, w0 = int(old_shape[0]), int(old_shape[1])
|
| for i in range(int(np.prod(old_shape))):
|
| basis = torch.zeros((h0, w0), dtype=dtype, device=device)
|
| idx = np.unravel_index(i, (h0, w0))
|
| basis[idx] = 1.0
|
| mat.append(_resize_2d(basis, new_shape, interpolation, antialias).reshape(-1))
|
| resize_matrix = torch.stack(mat)
|
| pinv = torch.linalg.pinv(resize_matrix)
|
| return pinv
|
|
|