CuRe / cure /backbone.py
mannicui0's picture
Mirror code and weights from GitHub
e304b09 verified
Raw History Blame Contribute Delete
4.76 kB
# Copyright (c) Meta Platforms, Inc. and affiliates.
# Adapted for fixed-resolution CuRe inference. See LICENSE and NOTICE.
"""PE Core L/14 patch features with Q/V LoRA at 336 x 336 resolution."""
from collections import OrderedDict
import torch
from torch import nn
from torch.nn import functional as F
class Rotary2D(nn.Module):
def __init__(self):
super().__init__()
frequencies = 1.0 / (10000 ** (torch.arange(0, 32, 2).float() / 32))
positions = torch.arange(24).to(frequencies.dtype) + 1
axis = torch.einsum("..., f -> ... f", positions, frequencies)
axis = axis.repeat_interleave(2, dim=-1)
vertical = axis[:, None].expand(24, 24, -1)
horizontal = axis[None, :].expand(24, 24, -1)
angles = torch.cat([horizontal, vertical], dim=-1).reshape(576, 64)
angles = torch.cat([torch.zeros(1, 64), angles], dim=0)
self.register_buffer("angles", angles[None, None], persistent=False)
@torch.amp.autocast("cuda", enabled=False)
def forward(self, values):
first, second = values.reshape(*values.shape[:-1], -1, 2).unbind(-1)
rotated = torch.stack([-second, first], dim=-1).flatten(-2)
result = values * self.angles.cos() + rotated * self.angles.sin()
return result.contiguous().to(values.dtype)
class Attention(nn.Module):
def __init__(self, rope):
super().__init__()
self.in_proj_weight = nn.Parameter(torch.empty(3072, 1024))
self.in_proj_bias = nn.Parameter(torch.empty(3072))
self.out_proj = nn.Linear(1024, 1024)
self.lora_A_q = nn.Parameter(torch.empty(64, 1024))
self.lora_B_q = nn.Parameter(torch.empty(1024, 64))
self.lora_A_v = nn.Parameter(torch.empty(64, 1024))
self.lora_B_v = nn.Parameter(torch.empty(1024, 64))
self.rope = rope
def forward(self, x):
batch, sequence, width = x.shape
projection = F.linear(x, self.in_proj_weight, self.in_proj_bias)
projection = (
projection.unflatten(-1, (3, width))
.unsqueeze(0).transpose(0, -2).squeeze(-2).contiguous()
)
query, key, value = projection[0], projection[1], projection[2]
query = query + 2.0 * F.linear(F.linear(x, self.lora_A_q), self.lora_B_q)
value = value + 2.0 * F.linear(F.linear(x, self.lora_A_v), self.lora_B_v)
query = query.reshape(batch, sequence, 16, 64).transpose(1, 2)
key = key.reshape(batch, sequence, 16, 64).transpose(1, 2)
value = value.reshape(batch, sequence, 16, 64).transpose(1, 2)
query, key = self.rope(query), self.rope(key)
attended = F.scaled_dot_product_attention(
query, key, value, attn_mask=None, dropout_p=0.0,
is_causal=False, scale=64 ** -0.5,
)
attended = attended.transpose(1, 2).reshape(batch, sequence, width)
return F.linear(attended, self.out_proj.weight, self.out_proj.bias)
class ResidualBlock(nn.Module):
def __init__(self, rope):
super().__init__()
self.attn = Attention(rope)
self.ln_1 = nn.LayerNorm(1024, eps=1e-5)
self.ln_2 = nn.LayerNorm(1024, eps=1e-5)
self.mlp = nn.Sequential(OrderedDict([
("c_fc", nn.Linear(1024, 4096)),
("gelu", nn.GELU()),
("c_proj", nn.Linear(4096, 1024)),
]))
def forward(self, x):
x = x + self.attn(self.ln_1(x))
return x + self.mlp(self.ln_2(x))
class Transformer(nn.Module):
def __init__(self, rope):
super().__init__()
self.resblocks = nn.ModuleList([ResidualBlock(rope) for _ in range(24)])
def forward(self, x):
for block in self.resblocks:
x = block(x)
return x
class PECore(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3, 1024, kernel_size=14, stride=14, bias=False)
self.class_embedding = nn.Parameter(torch.empty(1024))
self.positional_embedding = nn.Parameter(torch.empty(577, 1024))
self.ln_pre = nn.LayerNorm(1024, eps=1e-5)
self.ln_post = nn.LayerNorm(1024, eps=1e-5)
self.transformer = Transformer(Rotary2D())
def forward(self, images):
if images.ndim != 4 or tuple(images.shape[1:]) != (3, 336, 336):
raise ValueError("Expected image tensors with shape [N, 3, 336, 336]")
batch = images.shape[0]
x = self.conv1(images).permute(0, 2, 3, 1).reshape(batch, 576, 1024)
cls = self.class_embedding.view(1, 1, -1).expand(batch, -1, -1)
x = torch.cat([cls, x], dim=1)
x = x + self.positional_embedding[None]
x = self.transformer(self.ln_pre(x))
return self.ln_post(x)[:, 1:].mean(dim=1).float()