当前位置:首页 » 《休闲阅读》 » 正文

深度学习总结——用自己的数据集微调CLIP

18 人参与  2024年04月24日 14:30  分类 : 《休闲阅读》  评论

点击全文阅读


CLIP概述

CLIP(Contrastive Language-Image Pretraining)是由OpenAI开发的一种深度学习模型,用于将图像和自然语言文本进行联合编码。它采用了多模态学习的方法,使得模型能够理解图像和文本之间的语义关系。

它的核心思想是将图像和文本视为同等重要的输入,并通过联合训练来学习它们之间的联系。CLIP模型使用了一个共享的编码器,它将图像和文本分别映射到一个共享的特征空间中。通过将图像和文本的编码向量进行比较,模型能够判断它们之间的相似性和相关性。

它在训练过程中使用了对比损失函数,以鼓励模型将相关的图像和文本对编码得更接近,而将不相关的图像和文本对编码得更远。这使得CLIP模型能够具有良好的泛化能力,能够在训练过程中学习到通用的图像和文本理解能力。

它的整体流程如下:
在这里插入图片描述

它展现了强大的zero-shot能力,在许多视觉与语言任务中表现出色,如图像分类、图像生成描述、图像问答等。它的多模态能力使得CLIP模型能够在图像和文本之间建立强大的语义联系,为各种应用场景提供了更全面的理解和分析能力。

正是因为它出色的zero-shot能力,因此训练的模型本身就含有很多可以利用的知识,因此在一些任务上,如分类任务,caption任务,可以尝试在自己的数据集上微调CLIP,或许通过这种操作就能获得不错的性能。但是目前如何微调CLIP网上并没有看到很详细的介绍,因此我整理了相关的知识并在此记录。
参考链接

微调代码

第三方库

clip-by-openaitorch

下面以我做的图像分类任务为例,介绍相关的步骤。

步骤介绍

1.构建数据集

构建自己的数据集,每次迭代返回的数据包括:RGB图像和图像的标签(a photo of {label})
代码示例如下:

import osfrom PIL import Imageimport numpy as npimport clipclass YourDataset(Dataset):    def __init__(self,img_root,meta_root,is_train,preprocess):        # 1.根目录(根据自己的情况更改)        self.img_root = img_root        self.meta_root = meta_root        # 2.训练图片和测试图片地址(根据自己的情况更改)        self.train_set_file = os.path.join(meta_root,'train.txt')        self.test_set_file = os.path.join(meta_root,'test.txt')        # 3.训练 or 测试(根据自己的情况更改)        self.is_train = is_train        # 4.处理图像        self.img_process = preprocess        # 5.获得数据(根据自己的情况更改)        self.samples = []        self.sam_labels = []        # 5.1 训练还是测试数据集        self.read_file = ""        if is_train:            self.read_file = self.train_set_file        else:            self.read_file = self.test_set_file# 5.2 获得所有的样本(根据自己的情况更改)        with open(self.read_file,'r') as f:            for line in f:                img_path = os.path.join(self.img_root,line.strip() + '.jpg')                label = line.strip().split('/')[0]                label = label.replace("_"," ")                label = "a photo of " + label                self.samples.append(img_path)                self.sam_labels.append(label)        # 转换为token        self.tokens = clip.tokenize(self.sam_labels)    def __len__(self):        return len(self.samples)    def __getitem__(self, idx):        img_path = self.samples[idx]        token = self.tokens[idx]        # 加载图像        image = Image.open(img_path).convert('RGB')        # 对图像进行转换        image = self.img_process(image)        return image,token
2.加载预训练CLIP模型和相关配置

首先使用第三方库加载预训练的CLIP模型,会返回一个CLIP模型和一个图像预处理函数preprocess,这将用于之后的数据加载过程。

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")net, preprocess = clip.load("RN50",device=device,jit=False)

然后初始化优化器,损失函数,需要注意的是,如果刚开始你的损失很大或者出现异常,可以调整优化器的学习率和其他参数来进行调整,通常是调整的更小会有效果。

optimizer = optim.Adam(net.parameters(), lr=1e-6,betas=(0.9,0.98),eps=1e-6,weight_decay=0.001)scheduler = lr_scheduler.StepLR(        optimizer, step_size=10, gamma=0.1)# 创建损失函数loss_img = nn.CrossEntropyLoss()loss_txt = nn.CrossEntropyLoss()
3.加载数据

该步骤主要是调用第一步中创建的类,然后使用DataLoader函数加载自己的数据集。
代码如下:

your_dataset = YourDataset(img_root= '/images',                                          meta_root= '/meta',                                          is_train=True,preprocess=preprocess)dataset_size_your = len(your_dataset)your_dataloader = DataLoader(your_dataset,batch_size=4,shuffle=True,num_workers=4,pin_memory=False)
4.开始训练

训练代码按照模板来写即可,总共要训练epoches次,每次要将一个数据集里面的所有数据都训练一次,然后在每次训练完成的时候保存模型,这里分为两种:

保存模型的参数保存模型的参数、优化器、迭代次数

该部分的代码如下:

phase = "train"model_name = "your model name"ckt_gap = 4epoches = 30for epoch in range(epoches):    scheduler.step()    total_loss = 0    batch_num = 0    # 使用混合精度,占用显存更小    with torch.cuda.amp.autocast(enabled=True):        for images,label_tokens in your_dataloader:            # 将图片和标签token转移到device设备            images = images.to(device)            label_tokens = label_tokens.to(device)            batch_num += 1            # 优化器梯度清零            optimizer.zero_grad()            with torch.set_grad_enabled(phase == "train"):                logits_per_image, logits_per_text = net(images, label_tokens)                ground_truth = torch.arange(len(images),dtype=torch.long,device=device)                cur_loss = (loss_img(logits_per_image,ground_truth) + loss_txt(logits_per_text,ground_truth))/2                total_loss += cur_loss                if phase == "train":                    cur_loss.backward()                    if device == "cpu":                        optimizer.step()                    else:                        optimizer.step()                        clip.model.convert_weights(net)             if batch_num % 4 == 0:                logger.info('{} epoch:{} loss:{}'.format(phase,epoch,cur_loss))        epoch_loss = total_loss / dataset_size_your        torch.save(net.state_dict(),f"{model_name}_epoch_{epoch}.pth")        logger.info(f"weights_{epoch} saved")        if epoch % ckt_gap == 0:            checkpoint_path = f"{model_name}_ckt.pth"            checkpoint = {                'it': epoch,                'network': net.state_dict(),                'optimizer': optimizer.state_dict(),                'scheduler': scheduler.state_dict()}            torch.save(checkpoint, checkpoint_path)            logger.info(f"checkpoint_{epoch} saved")        logger.info('{} Loss: {:.4f}'.format(            phase, epoch_loss))

全部代码

import osfrom PIL import Imageimport numpy as npimport clipfrom loguru import loggerfrom torch.utils.data import Dataset, DataLoader, ConcatDatasetimport torch.optim as optimfrom torch.optim import lr_schedulerimport torch.nn as nnclass YourDataset(Dataset):    def __init__(self,img_root,meta_root,is_train,preprocess):        # 1.根目录(根据自己的情况更改)        self.img_root = img_root        self.meta_root = meta_root        # 2.训练图片和测试图片地址(根据自己的情况更改)        self.train_set_file = os.path.join(meta_root,'train.txt')        self.test_set_file = os.path.join(meta_root,'test.txt')        # 3.训练 or 测试(根据自己的情况更改)        self.is_train = is_train        # 4.处理图像        self.img_process = preprocess        # 5.获得数据(根据自己的情况更改)        self.samples = []        self.sam_labels = []        # 5.1 训练还是测试数据集        self.read_file = ""        if is_train:            self.read_file = self.train_set_file        else:            self.read_file = self.test_set_file# 5.2 获得所有的样本(根据自己的情况更改)        with open(self.read_file,'r') as f:            for line in f:                img_path = os.path.join(self.img_root,line.strip() + '.jpg')                label = line.strip().split('/')[0]                label = label.replace("_"," ")                label = "photo if " + label                self.samples.append(img_path)                self.sam_labels.append(label)        # 转换为token        self.tokens = clip.tokenize(self.sam_labels)    def __len__(self):        return len(self.samples)    def __getitem__(self, idx):        img_path = self.samples[idx]        token = self.tokens[idx]        # 加载图像        image = Image.open(img_path).convert('RGB')        # 对图像进行转换        image = self.img_process(image)        return image,token# 创建模型device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")net, preprocess = clip.load("RN50",device=device,jit=False)optimizer = optim.Adam(net.parameters(), lr=1e-6,betas=(0.9,0.98),eps=1e-6,weight_decay=0.001)scheduler = lr_scheduler.StepLR(        optimizer, step_size=10, gamma=0.1)# 创建损失函数loss_img = nn.CrossEntropyLoss()loss_txt = nn.CrossEntropyLoss()# 加载数据集your_dataset = YourDataset(img_root= '/images',                                          meta_root= '/meta',                                          is_train=True,preprocess=preprocess)dataset_size_your = len(your_dataset)your_dataloader = DataLoader(your_dataset,batch_size=4,shuffle=True,num_workers=4,pin_memory=False)phase = "train"model_name = "your model name"ckt_gap = 4for epoch in range(st,args.epoches):    scheduler.step()    total_loss = 0    batch_num = 0    # 使用混合精度,占用显存更小    with torch.cuda.amp.autocast(enabled=True):        for images,label_tokens in your_dataloader:            # 将图片和标签token转移到device设备            images = images.to(device)            label_tokens = label_tokens.to(device)            batch_num += 1            # 优化器梯度清零            optimizer.zero_grad()            with torch.set_grad_enabled(phase == "train"):                logits_per_image, logits_per_text = net(images, label_tokens)                ground_truth = torch.arange(len(images),dtype=torch.long,device=device)                cur_loss = (loss_img(logits_per_image,ground_truth) + loss_txt(logits_per_text,ground_truth))/2                total_loss += cur_loss                if phase == "train":                    cur_loss.backward()                    if device == "cpu":                        optimizer.step()                    else:                        optimizer.step()                        clip.model.convert_weights(net)             if batch_num % 4 == 0:                logger.info('{} epoch:{} loss:{}'.format(phase,epoch,cur_loss))        epoch_loss = total_loss / dataset_size_food101        torch.save(net.state_dict(),f"{model_name}_epoch_{epoch}.pth")        logger.info(f"weights_{epoch} saved")        if epoch % ckt_gap == 0:            checkpoint_path = f"{model_name}_ckt.pth"            checkpoint = {                'it': epoch,                'network': net.state_dict(),                'optimizer': optimizer.state_dict(),                'scheduler': scheduler.state_dict()}            torch.save(checkpoint, checkpoint_path)            logger.info(f"checkpoint_{epoch} saved")        logger.info('{} Loss: {:.4f}'.format(            phase, epoch_loss))

点击全文阅读


本文链接:http://zhangshiyu.com/post/99562.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1