点击下方“深度学习爱好者”,选择加"星标"或“置顶”
传统的机器学习模型通常需要大量特定任务的标记数据集进行微调。例如,一个训练用来识别狗的模型可能在识别猫方面表现不佳,除非它专门针对猫的图片进行了微调。 CLIP的架构支持零样本学习,这意味着它可以执行它没有直接训练过的任务,通过利用其在图像和文本之间学到的广泛关联。例如,基于它们的文本描述,它可以对它在训练期间从未见过的图片进行分类。引用他们的论文:“我们在零样本的情况下匹配原始ResNet-50在ImageNet上的准确性,而不需要使用它训练时的128万个训练样本。”
文本编码器 图像编码器 自定义数据集(如果你正在训练) 对称损失
class TextEncoder(nn.Module):
def __init__(self, embed_dim, proj_dim):
super().__init__()
self.model = DistilBertModel(config=DistilBertConfig())
self.layer_norm = nn.LayerNorm(proj_dim)
def forward(self, input_ids, attention_mask):
x = self.model(input_ids=input_ids, attention_mask=attention_mask).last_hidden_state
return self.layer_norm(x)
tokenizer = DistilBertTokenizer.from_pretrained('distilbert-base-uncased')
texts = ["This is a sample sentence.", "This is another example."]
inputs= tokenizer(texts, padding=True, truncation=True, return_tensors="pt").to(device)
encoder = ImageEncoder(embed_dim=768, proj_dim=256)
inputs = encoder(inputs['input_ids'], inputs['mask'])
class TextEncoder(nn.Module):
def __init__(self, embed_dim, proj_dim):
super().__init__()
self.model = DistilBertModel(config=DistilBertConfig())
self.projection = nn.Linear(embed_dim, proj_dim)
self.layer_norm = nn.LayerNorm(proj_dim)
def forward(self, input_ids, attention_mask):
x = self.model(input_ids=input_ids, attention_mask=attention_mask).last_hidden_state
x = x[:, 0, :] # B, T[cls], E
x = self.projection(x)
return self.layer_norm(x)
class ImageEncoder(nn.Module):
def __init__(self, base_model, embed_dim, proj_dim):
super().__init__()
self.model = base_model
for param in self.model.parameters():
param.requires_grad = True
self.projection = nn.Linear(embed_dim, proj_dim)
self.layer_norm = nn.LayerNorm(proj_dim)
def forward(self, x):
x = self.projection(self.model(x))
return self.layer_norm(x)
class CustomDataset(Dataset):
def __init__(self, texts, image_paths):
self.image_paths = image_paths
self.texts = texts
tokenizer = DistilBertTokenizer.from_pretrained('distilbert-base-uncased')
self.inputs = tokenizer(texts, padding=True, truncation=True, return_tensors="pt")
self.transform = torchvision.transforms.ToTensor()
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
img_path = self.image_paths[idx]
image = Image.open(img_path)
image = self.transform(image)
caption, mask = self.inputs[idx].items()
return {
"image": image,
"input_ids": caption["input_ids"],
"mask": mask["attention_mask"]
}
image_paths:你选择的数据集中图像的路径列表。 texts:数据集中每张图片的标题或文本描述。
class CLIPModel(nn.Module):
def __init__(self):
super().__init__()
self.device = "cuda" if torch.cuda.is_available() else "cpu"
ViT = VissionTransformer(
num_layers=8,
img_size=224,
emb_size=768,
patch_size=16,
num_head=6,
num_class=768).to(self.device)
self.image_encoder = ImageEncoder(base_model=ViT, embed_dim=768, proj_dim=256)
self.text_encoder = TextEncoder(embed_dim=768, proj_dim=256)
from vit import VissionTransformer # Importing ViT from previous implementaton (GitHub: Ml-Models)
import numpy as np
import torch.nn.functional as F
class CLIPModel(nn.Module):
def __init__(self):
super().__init__()
self.device = "cuda" if torch.cuda.is_available() else "cpu"
ViT = VissionTransformer(
num_layers=8,
img_size=224,
emb_size=768,
patch_size=16,
num_head=6,
num_class=False).to(self.device)
self.image_encoder = ImageEncoder(base_model=ViT, embed_dim=768, proj_dim=256)
self.text_encoder = TextEncoder(embed_dim=768, proj_dim=256)
self.temperature = nn.Parameter(torch.ones([])*np.log(1/7)).to(self.device)
def forward(self, x):
I_t = self.image_encoder(x["image"])
T_t = self.text_encoder(x["input_ids"], x["mask"])
logits = I_t@T_t.T * torch.exp(self.temperature)
labels = torch.arange(I_t.size(0)).to(self.device)
loss_I = F.cross_entropy(logits.T, labels)
loss_T = F.cross_entropy(logits, labels)
loss = (loss_I + loss_T)/2.0
return loss, logits
我们得到I_t和T_t(大小:B, Token_Size, Embed_Size) 我们通过取点积来计算logits,如前所述,然后乘以温度参数的指数。如果你熟悉对比学习或读过我关于DINO(无标签蒸馏)的文章,你可能知道通常使用除以温度来锐化输出分布。然而,我们不直接除以温度,而是乘以一个可训练的张量,该张量使用nn.Parameter()设置,并初始化为log(1/7)。由于eln(x)=x,那么exp(log(1/T))应该是1/T,你可能会想知道我们为什么不简单地乘以1/T。原因是使用log(1/T)可以让优化器在训练期间更容易计算和更新梯度。这种方法在深度学习中是一种常见的做法,因为它可以带来更平滑的训练和更稳定的模型权重更新。 标签简单地用批次大小生成([0, 1,..N])。正如我们之前讨论的,目标是最大化每个对角线元素(i1T1, i2T2,..inTn),因此整个矩阵中每一行的标签是[0, 1, 2, ..N],对应于哪一行的元素应该是最大的。 如伪代码所述,嵌入已经归一化,但我们不需要这样做,因为我们在返回图像和文本编码器的输出时已经应用了层归一化。 按照伪代码,计算行和列的交叉熵损失。我们通过传递logits的转置和正常的logits以及标签,取两个损失的平均值,现在我们有最终的损失结果。
texts = ["This is a sample sentence.", "This is another example."]
# You can Use a CustomDataset as we Implemented above for training
train_data = CustomDataset(texts, image_path)
train_loader = DataLoader(train_data, batch_size, shuffle=True)
# Example Usage
device = 'cuda' if torch.cuda.is_available() else 'cpu'
tokenizer = DistilBertTokenizer.from_pretrained('distilbert-base-uncased')
inputs= tokenizer(texts, padding=True, truncation=True, return_tensors="pt").to(device)
test = {
"image" : torch.rand(2, 3, 224, 224).to(device),
"input_ids" : inputs["input_ids"],
"mask" : inputs["attention_mask"]
}
model = CLIPModel().to(device)
loss, logits = model(test)
print("Loss:", loss, "Logits:", logits)
下载1:Pytoch常用函数手册 在「深度学习爱好者」公众号后台回复:Pytorch常用函数手册,即可下载全网第一份Pytorch常用函数手册,涵盖Tensors介绍、基础函数介绍、数据处理函数、优化函数、CUDA编程、多线程处理等十四章章内容。 下载2:Python视觉实战项目52讲 在「小白学视觉」公众号后台回复:Python视觉实战项目,即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。 交流群
欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉驶、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~