自定义数据 微调CLIP (结合paper)

2024-04-19 22:52

本文主要是介绍自定义数据 微调CLIP (结合paper),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

CLIP 是 Contrastive Language-Image Pre-training 的缩写,是一个擅长理解文本和图像之间关系的模型,下面是一个简单的介绍:

优点: CLIP 在零样本学习方面特别强大,它可以(用自然语言)给出图像的描述,并在基于该描述对新图像进行分类方面表现良好,例如,您可以将图像描述为“a”。猫的黑白照片”,CLIP 可以准确地对猫的新照片进行分类,即使它以前没有见过这些特定图像。
训练: CLIP 在从互联网收集的大量文本图像对数据集上进行训练,这使得它能够学习视觉概念及其描述之间的联系。
局限性: CLIP 也有缺点,训练的计算成本可能很高,并且在需要非常具体或抽象概念的任务上,或者对于与训练所用的文本描述非常不同的数据时,可能表现不佳。训练可能会将社会偏见引入模型中。

paper:Learning Transferable Visual Models From Natural Language Supervision

本文用CLIP做一个零样本分类,
CLIP训练的时候用的是图片和文本描述对,并没有分类的标签,那如何让CLIP做零样本分类?
我们需要给出标签的文本,让图像和所有的文本标签进行匹配,得分高的就是匹配到的标签文本。

paper中提到预测哪个文本整体与哪个图像配对,而不是该文本的准确单词。

在这里插入图片描述

下面通过一个kaggle数据集来具体说明。

这里选用indo fashion dataset, 它有15种印度服饰。

在这里插入图片描述
类别如下:
在这里插入图片描述

数据集结构:
其中images文件夹下又有train, val, test文件夹。

在这里插入图片描述

再看一下json文件,
image_path指的是上面images文件夹下的路径,
product_title是和图片对应的文本描述,训练的时候就是用图片和这个文本进行匹配。
class_label训练的时候不需要,最后验证分类是否正确时会用到。

在这里插入图片描述

import需要的库,定义数据集的文件夹,读取json数据

import json
from PIL import Image
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
import clip
from transformers import CLIPProcessor,CLIPModel
from tqdm import tqdmjson_path = 'your_path/train_data.json'
image_path = 'your_path/images/train/'input_data = []
with open(json_path, 'r') as f:for line in f:obj = json.loads(line)input_data.append(obj)

CLIP模型,如果不能download, 手动下载走offline模式。

model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
Setting our device to GPU (Cuda) and loading the pre-trained CLIP model.device = "cuda:0" if torch.cuda.is_available() else "cpu" model, preprocess = clip.load("ViT-B/32", device=device, jit=False)

定义Dataloader

# Define a custom dataset
class image_title_dataset():def __init__(self, list_image_path, list_txt):self.image_path = list_image_path# Tokenize text using CLIP's tokenizerself.title = clip.tokenize(list_txt)def __len__(self):# Define the length of the datasetreturn len(self.title)def __getitem__(self, idx):image = preprocess(Image.open(self.image_path[idx]))title = self.title[idx]return image, title

这里的dataset需要传入list_image_path和list_txt,
格式是这种:
list_image_path = [‘folder/image1.jpg’,‘folder2/image2.jpg’]
list_txt = [‘description for image1.jpg’ , ‘description for image2.jpg’]
所以要把image_path和product_title都装进list里面。

注意,CLIP的最大序列长度限制在76, 而有些文本描述非常长,需要截掉一部分,
当然截到76长度也有很多种方法,这里简单粗暴就从开头取长度76.

实际代码中,indo数据集不限制长度会报错,而博主觉得这个76可能是text被tokenize之后的token的长度,而不是原文本的长度,
因为把文本截到长度>77也是可以的。
而token的长度是由tokenize的算法决定的。具体最大极限文本长度是多少没测,这里简单地截取到77.

在这里插入图片描述

list_image_path = []
list_txt = []
for item in input_data:img_path = image_path + item['image_path'].split('/')[-1]caption = item['product_title'][:77]list_image_path.append(img_path)list_txt.append(caption)dataset = image_title_dataset(list_image_path, list_txt)
train_dataloader = DataLoader(dataset, batch_size=100, shuffle=True) # Function to convert model's parameters to FP32 format
#转精度省内存.
def convert_models_to_fp32(model): for p in model.parameters(): p.data = p.data.float() p.grad.data = p.grad.data.float() if device == "cpu":model.float()  # Convert the model's parameters to float if using CPU

optimizer用Adam,参数按paper中的设置.
不过博主的机器容纳不了这么大的batch_size, 具体batch_size设多少合适,需要自己去验证。

在这里插入图片描述
由于数据集比较小,lr设得更小一些。

optimizer = torch.optim.Adam(model.parameters(), lr=5e-5, betas=(0.9, 0.98), eps=1e-6 ,weight_decay=0.2) 

训练

paper中的训练是这样的
在这里插入图片描述

    for epoch in range(num_epochs):pbar = tqdm(train_dataloader, total=len(train_dataloader))for batch in pbar:optimizer.zero_grad()images, texts = batchimages = images.to(device)texts = texts.to(device)logits_per_image, logits_per_text = model(images, texts)ground_truth = torch.arange(len(images), dtype=torch.long, device=device)total_loss = (loss_img(logits_per_image, ground_truth) + loss_txt(logits_per_text, ground_truth)) / 2total_loss.backward()if device == "cpu":optimizer.step()else:convert_models_to_fp32(model)optimizer.step()clip.model.convert_weights(model)pbar.set_description(f"Epoch {epoch}/{num_epochs}, Loss: {total_loss.item():.4f}")if torch.isnan(total_loss).any():print("epoch {} loss is NaN".format(epoch))epoch = num_epochsbreak

训练中,遇到了这些问题:
loss出现了NaN, 调整batch_size能解决,batch_size不要太小。
loss降不下去了,看看paper中的参数,有哪些需要调整。

训练完之后,找来一张图片测试。
这里又有一些注意事项,
请看paper.
因为训练的时候是图片和一段文本描述匹配的,而不是图片和一个单词。
所以你做零样本分类时,类别文本最好不要只写一个单词,比如只写"Saree"。
你要写"A photo of Saree", 这就成了一个句子,效果就会好一些。

在这里插入图片描述

model, preprocess = clip.load("ViT-B/32", device=device)checkpoint = torch.load("model.pt")
model.load_state_dict(checkpoint['model_state_dict'])clothing_items = ["Saree","Lehenga","Women Kurta","Dupatta","Gown","Nehru Jacket","Sherwani","Men Kurta","Men Mojari","Leggings and Salwar","Blouse","Palazzo","Dhoti Pants","Petticoat","Women Mojari"
]

这里你可能要问,那json文件里面的标签不是这么写的,比如"Women Kurta",json文件的标签是"women_kurta",
为什么不写成"women_kurta"。
这个博主是测试过的,写成json文件里面的标签形式准确率会降低,可能是因为"Women Kurta"更接近自然语言,更贴合训练数据吧。

把15个类别的标签都写成"A photo of {label}" 进行测试。

#你想测的第几张图片
index_ = 500
image_json = input_data[index_]
image_path = os.path.join("indo-fashion-dataset", image_json['image_path'])
image_class = image_json['class_label']
image = preprocess(Image.open(image_path)).unsqueeze(0).to(device)
text = torch.cat([clip.tokenize(f"a photo of a {c}") for c in clothing_items]).to(device)with torch.no_grad():# Encode image and textimage_features = model.encode_image(image)text_features = model.encode_text(text)# Calculate similarity scores between image and textlogits_per_image, logits_per_text = model(image, text)probs = logits_per_image.softmax(dim=-1).cpu().numpy()# Normalize image and text features
image_features /= image_features.norm(dim=-1, keepdim=True)
text_features /= text_features.norm(dim=-1, keepdim=True)# Calculate similarity scores
similarity = (100.0 * image_features @ text_features.T).softmax(dim=-1)
values, indices = similarity[0].topk(5)# Print the top predictions
print("\nTop predictions:\n")
for value, index in zip(values, indices):print(f"{clothing_items[index]:>16s}: {100 * value.item():.2f}%")# Display the image with its class label
plt.imshow(plt.imread(image_path))
plt.title(f"Image for class: {image_class}")
plt.axis('off')
plt.show()

请添加图片描述
请添加图片描述

训练中并没有精调参数,也没有训练很多epoch. 效果如下。
统计了一下测试集中7450张图片的top1和top3准确率。
top1: 77.7%, top3: 93.57%

请添加图片描述

paper中说CLIP 模型的 Top-5 准确率明显高于其 Top-1 准确率, 本文虽测的是top3, 但也是明显高于top1的。

在这里插入图片描述

又试了一下这种方法,这里效果并没有变好。

在这里插入图片描述

参考资料1
参考资料2

这篇关于自定义数据 微调CLIP (结合paper)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/918663

相关文章

Toolbar+DrawerLayout使用详情结合网络各大神

最近也想搞下toolbar+drawerlayout的使用。结合网络上各大神的杰作,我把大部分的内容效果都完成了遍。现在记录下各个功能效果的实现以及一些细节注意点。 这图弹出两个菜单内容都是仿QQ界面的选项。左边一个是drawerlayout的弹窗。右边是toolbar的popup弹窗。 开始实现步骤详情: 1.创建toolbar布局跟drawerlayout布局 <?xml vers

移动硬盘盒:便携与交互的完美结合 PD 充电IC

在数字化时代的浪潮中,数据已成为我们生活中不可或缺的一部分。随着数据的不断增长,人们对于数据存储的需求也在不断增加。传统的存储设备如U盘、光盘等,虽然具有一定的便携性,但在容量和稳定性方面往往难以满足现代人的需求。而移动硬盘,以其大容量、高稳定性和可移动性,成为了数据存储的优选方案。然而,单纯的移动硬盘在携带和使用上仍存在诸多不便,于是,移动硬盘盒应运而生,以其独特的便携性和交互性,成为了数据存储

图形编辑器基于Paper.js教程03:认识Paper.js中的所有类

先来认一下Paper的资源对象,小弟有哪些,有个整体的认识。认个脸。 在Paper.js的 官方文档中类大致有如下这些: 基类: ProjectViewItemPointToolSizeSegmentRectangleCurveCurveLocationMatrixColorStyleTweenToolEventGradientGradientStopEvent 二级或三级类 继承Ite

Tkinter和selenium结合实现登录UC后台,最后打包成exe

主要实现的功能:小号模式自动登录UC阿里汇川广告后台,屏蔽账号密码输入 主要用的技术:用Tkinter展示所有的广告账号界面,使用selenium控制谷歌浏览器,打开阿里汇川登录页,登录汇川后台。 第一次写,遇到的坑比较多,三天,搞定。给自己一个棒棒~☺️ import Tkinter as tk import osimport sysimport requestsfrom sel

红队内网攻防渗透:内网渗透之内网对抗:横向移动篇Kerberos委派安全RBCD资源Operators组成员HTLMRelay结合

基于资源的约束委派(RBCD)是在Windows Server 2012中新加入的功能,与传统的约束委派相比,它不再需要域管理员权限去设置相关属性。RBCD把设置委派的权限赋予了机器自身,既机器自己可以决定谁可以被委派来控制我。也就是说机器自身可以直接在自己账户上配置msDS-AllowedToActOnBehalfOfOtherIdentity属性来设置RBCD。 所以核心就是谁或什么权限能修改

B站大模型指令微调入门实战(完整代码),一键打造你的数字分身

前两天,想导出微信聊天记录,于是搞了个小工具。 感兴趣的小伙伴,可以回看: 微信聊天记录导出为电脑文件实操教程(附代码) 一键获取所有微信聊天记录(附PyQT6入门实战) 拿到这些数据都有什么用? 突发奇想:如果把微信上,所有和我相关的聊天对话提取出来,再结合大语言模型 LLM,是不是就可以打造我的数字分身了? 选择一个基座大模型,通过指令微调的方式,打造个性化AI Bot,不失为

使用AGG里面的clip_box函数裁剪画布, 绘制裁剪后的图形

// 矩形裁剪图片, 透明void agg_testImageClipbox_rgba32(unsigned char* buffer, unsigned int width, unsigned int height){// ========= 创建渲染缓冲区 =========agg::rendering_buffer rbuf;// BMP是上下倒置的,为了和GDI习惯相同,最后一个参数是

SLAM Paper Reading和代码解析

最近对VINS、LIO-SAM等重新进行了Paper Reading和代码解析。这两篇paper和代码大约在三年前就读过,如今重新读起来,仍觉得十分经典,对SLAM算法研发具有十分重要的借鉴和指导意义。重新来读,对其中的一些关键计算过程也获得了更新清晰的了解,现整理分享出来,供有需要的同学参考。 VINS-MONO算法总结-徐胜攀.pdf资源-CSDN文库 对VINS-MONO的算法框架进

指令微调数据集构建方法

指令微调(Instruction Tuning),是指使用自然语言形式的数据对预训练后的大语言模型进行参数微调,在一些文章中也称为有监督微调(Supervised Fine-tuning,SFT)或多任务提示训练(Multitask Prompted Training)。指令微调的第一步就是收集或构建指令化的实例,然后通过有监督的方式对LLM的参数进行微调,经过指令微调后,大语言模型能够展现出较强

工厂方法模式--结合具体例子学习工厂方法模式

在学习工厂方法模式之前,可以先学习一下简单工厂模式,网址是http://blog.csdn.net/u012116457/article/details/21650421,这里仍以水果的实例讲解。   先来说说简单工厂模式的特点,简单工厂模式将具体类的创建交给了工厂,使客户端不再直接依赖于产品,但是其违背了OCP原则,当对系统进行扩展时,仍然需要去修改原有的工厂类的代码。 而工厂方法模式则解