""" @author: Yanzuo Lu @author: oliveryanzuolu@gmail.com """ import math import torch as th import torch.nn as nn from transformers import CLIPVisionModel class LayerNorm(nn.LayerNorm): """ Implementation that supports fp16 inputs but fp32 gains/biases. """ def forward(self, x: th.Tensor): return super().forward(x.float()).to(x.dtype) class MultiheadAttention(nn.Module): def __init__(self, n_ctx, width, heads): super().__init__() self.n_ctx = n_ctx self.width = width self.heads = heads self.c_qkv = nn.Linear(width, width * 3) self.c_proj = nn.Linear(width, width) self.attention = QKVMultiheadAttention(heads, n_ctx) def forward(self, x): x = self.c_qkv(x) x = self.attention(x) x = self.c_proj(x) return x class MLP(nn.Module): def __init__(self, width): super().__init__() self.width = width self.c_fc = nn.Linear(width, width * 4) self.c_proj = nn.Linear(width * 4, width) self.gelu = nn.GELU() def forward(self, x): return self.c_proj(self.gelu(self.c_fc(x))) class QKVMultiheadAttention(nn.Module): def __init__(self, n_heads: int, n_ctx: int): super().__init__() self.n_heads = n_heads self.n_ctx = n_ctx def forward(self, qkv): bs, n_ctx, width = qkv.shape attn_ch = width // self.n_heads // 3 scale = 1 / math.sqrt(math.sqrt(attn_ch)) qkv = qkv.view(bs, n_ctx, self.n_heads, -1) q, k, v = th.split(qkv, attn_ch, dim=-1) weight = th.einsum( "bthc,bshc->bhts", q * scale, k * scale ) # More stable with f16 than dividing afterwards wdtype = weight.dtype weight = th.softmax(weight.float(), dim=-1).type(wdtype) return th.einsum("bhts,bshc->bthc", weight, v).reshape(bs, n_ctx, -1) class ResidualAttentionBlock(nn.Module): def __init__( self, n_ctx: int, width: int, heads: int, ): super().__init__() self.attn = MultiheadAttention( n_ctx, width, heads, ) self.ln_1 = LayerNorm(width) self.mlp = MLP(width) self.ln_2 = LayerNorm(width) def forward(self, x: th.Tensor): x = x + self.attn(self.ln_1(x)) x = x + self.mlp(self.ln_2(x)) return x class Transformer(nn.Module): def __init__( self, n_ctx: int, width: int, layers: int, heads: int, ): super().__init__() self.n_ctx = n_ctx self.width = width self.layers = layers self.resblocks = nn.ModuleList( [ ResidualAttentionBlock( n_ctx, width, heads, ) for _ in range(layers) ] ) def forward(self, x: th.Tensor): for block in self.resblocks: x = block(x) return x class FrozenCLIPImageEmbedder(nn.Module): """Uses the CLIP transformer encoder for text (from Hugging Face)""" def __init__(self, version="openai/clip-vit-large-patch14"): super().__init__() self.transformer = CLIPVisionModel.from_pretrained("pretrained_models/clip", use_safetensors=True) self.final_ln = LayerNorm(768) self.mapper = nn.Sequential( nn.Linear(1024, 768, bias=False), Transformer(1, 768, 5, 1) ) self.freeze() def freeze(self): self.transformer = self.transformer.eval() for param in self.parameters(): param.requires_grad = False for param in self.mapper.parameters(): param.requires_grad = True for param in self.final_ln.parameters(): param.requires_grad = True def forward(self, image): outputs = self.transformer(pixel_values=image) z = outputs.pooler_output z = z.unsqueeze(1) z = self.mapper(z) z = self.final_ln(z) return z def encode(self, image): return self(image)