目录
一:运行环境
Chinese-CLIP的代码是在Ubuntu系统上运行的,运行方式是在命令行运行 .sh 脚本。
配置环境的时候尽量不要 pip requestment.txt ,(好像是会自动下载最高版本的torch,导致和CUDA不匹配),按照自己的CUDA版本下载对应的torch就好了。
二:代码架构
参考Github:
Chinese-CLIP/
├── run_scripts/
│ ├── muge_finetune_vit-b-16_rbt-base.sh
│ ├── flickr30k_finetune_vit-b-16_rbt-base.sh
│ └── ... # 更多finetune或评测脚本...
└── cn_clip/
├── clip/
├── eval/
├── preprocess/
└── training/${DATAPATH}
├── pretrained_weights/
├── experiments/
├── deploy/ # 用于存放ONNX & TensorRT部署模型
└── datasets/
└── MyData/ # 自定义数据集...自定义数据集的组织方式参考Coco,我这里是这样的,ImageData里放的是图片,word_test.csv是文本内容。
三:数据集准备
Cn-CLIP训练和测试时,要把数据转为LMDB数据库文件,方便运行时的随机读取。而数据保存的形式是:文本存在 json 文件,图像使用base64格式存在tsv文件。所以先把文本处理为json,图像处理为tsv,然后进行序列化,转为LMDB。
1. 文本数据处理
文本数据使用的是 json 格式,如果文本是保存在excel文件的话,那么就需要做相应的转换。Github上给出的 json 格式如下:
可以看到 json 里面包括文本的 id ,文本内容,图片 id。如果是训练集的话,知道图文对应关系,那么可以按照上述格式转换;如果是测试集,其实是不知道图文对应关系的,那么就直接把图片 id 写成空列表就行了。处理代码如下:
训练集文本处理
假设我训练集文本xlsx格式是这样
那么我就自己构建一个text_id,按顺序标号12345...:
import pandas as pd import json '''训练集文本转为json''' df = pd.read_excel("/MyData/train_texts.xlsx") # 构建JSON数据 with open("./train_texts.jsonl", "w", encoding='utf-8') as outfile: text_id = 1 for index, row in df.iterrows(): image_names = row[0] text = row[1] image_ids = [image_names] json_row = {"text_id": text_id,"text": text,"image_ids": image_ids} json.dump(json_row, outfile, ensure_ascii=False) # 写入单个 JSON 对象 outfile.write('\n') # 写入换行符,使得每个 JSON 对象占据一行 text_id += 1 print("JSON Lines 文件已生成:train_texts.jsonl")
测试集文本处理
假设我测试集文本xlsx文件是这样,测试集不知道文本对应的图像id,image_ids写成空列表就行。
'''测试集文本转为json''' df = pd.read_excel("MyData/test_texts.xlsx") # 构建JSON数据 with open("./test_texts.jsonl", "w", encoding='utf-8') as outfile: for index, row in df.iterrows(): text_id = row[0] text = row[1] image_ids = [] json_row = {"text_id": text_id,"text": text,"image_ids": image_ids} json.dump(json_row, outfile, ensure_ascii=False) # 写入单个 JSON 对象 outfile.write('\n') # 写入换行符,使得每个 JSON 对象占据一行 print("JSON Lines 文件已生成:test_texts.jsonl")
经过上面的操作,文本数据已经处理好了。
2. 图像数据处理
Github的原话是 ”不是将图片以大量的小文件方式存放,而是将训练/验证/测试图片以base64形式分别存放在${split}_imgs.tsv文件中。文件每行表示一张图片,包含图片id(int型)与图片base64,以tab隔开,格式如下:
假设我这里原本数据集的图像是以小文件方式保存的,例如
那就先把图片转为base64格式,保存到 tsv 文件中,代码如下:实际使用按照自己的存储路径修改路径就行了
from PIL import Image import io import base64 import csv import os import json """将图片转换为base64格式""" # 定义存储图像的文件夹路径 folder_path = "MyData/ImageData" # 准备写入的TSV文件路径 tsv_file_path = "MyData/test_imgs.tsv" # 打开或创建TSV文件,并写入列名 with open(tsv_file_path, "w", newline='') as tsvfile: writer = csv.writer(tsvfile, delimiter='\t') # 遍历文件夹中的图片文件 for filename in os.listdir(folder_path): if filename.endswith(".jpg") or filename.endswith(".png") or filename.endswith(".jpeg"): # 仅处理图片文件 # 构建图片文件的完整路径 image_path = os.path.join(folder_path, filename) # 打开图片文件 with Image.open(image_path) as img: # 将图片转换为Base64编码字符串 img_buffer = io.BytesIO() img.save(img_buffer, format=img.format) byte_data = img_buffer.getvalue() base64_str = base64.b64encode(byte_data).decode("utf-8") # 写入文件名和Base64编码字符串到TSV文件中 writer.writerow([filename, base64_str]) # print(filename) print("Base64编码结果已写入到TSV文件中:", tsv_file_path)
到这里已经把数据集格式处理完了:文本是json文件,图像是tsv文件。接下来进行序列化,转为LMDB数据库文件。
3. 生成LMDB数据库
这个代码GitHub有给,代码是在cn-clip/preprocess文件夹下的build_lmdb_dataset.py,
修改一下参数如下:
def parse_args(): parser = argparse.ArgumentParser() parser.add_argument( "--data_dir", type=str, default='/MyData/', help="the directory which stores the image tsvfiles and the text jsonl annotations" ) #路径 parser.add_argument( "--splits", type=str, default="test", help="specify the dataset splits which this script processes, concatenated by comma \ (e.g. train,valid,test)" )# 选择是训练集还是验证集还是测试集 parser.add_argument( "--lmdb_dir", type=str, default=None, help="specify the directory which stores the output lmdb files. \ If set to None, the lmdb_dir will be set to {args.data_dir}/lmdb" )#不用写 默认生成在MyData文件夹下 return parser.parse_args()
四、模型微调
如果是本地单卡训练,修改shell脚本部分参数如下:
# Number of GPUs per GPU worker GPUS_PER_NODE=1 # Number of GPU workers, for single-worker training, please set to 1 WORKER_CNT=1 # The ip address of the rank-0 worker, for single-worker training, please set to localhost export MASTER_ADDR=localhost # The port for communication export MASTER_PORT=8514 # The rank of this worker, should be in {0, ..., WORKER_CNT-1}, for single-worker training, please set to 0 export RANK=0 export PYTHONPATH=${PYTHONPATH}:`pwd`/cn_clip/ # data options train_data=${DATAPATH}xxx/MyData/lmdb/train #这里的xxx是要改成绝对路径 下面一样 val_data=${DATAPATH}xxx/MyData//lmdb/val # if val_data is not specified, the validation will be automatically disabled path to resume reset_data_offset="--reset-data-offset" reset_optimizer="--reset-optimizer" # reset_optimizer="" # output options output_base_dir=${DATAPATH}xxx/MyData/experiments/ #xxx是绝对路径
然后在命令行cd到Chinese-CLIP文件夹,运行下面这行代码
bash run_scripts/muge_finetune_vit-b-16_rbt-base.sh ${DataPath}
关于训练过程中如果出现其它的bug,需要自己慢慢调试了,基本上都是对training文件夹下的py文件做一些小修改。
五:模型验证与测试
训练完后权重会保存到 MyData/experiments/里面的checkpoints文件夹下。
1. 提取图文特征
这一步图搜文和文搜图,用的是一样的代码,都是要先对图片和文本提取特征,然后后面再进行相似度计算。代码是cn_clip/eval/文件夹下的extract_features.py ,根据注释直接在py文件里修改好参数。注意要将extract-image-feats和extract-text-feats参数设为True,才能开始提取特征。提取完后是保存在两个jsonl文件里的,这两个jsonl文件应该是默认保存在MyData文件夹下。
2. 图文检索
检索部分的代码是分开的,都在cn_clip/eval/文件夹下。文搜图是make_topk_predictions.py文件,图搜文是make_topk_predictions_tr.py文件。这里也是把提取的特征的两个jsonl文件填到路径里,然后设置一下输出的结果的路径。
检索完成后的结果保存在设置的输出路径中,是 jsonl 文件格式。
3. 计算召回率
代码都在cn_clip/eval/文件夹下,用evaluation.py计算文搜图的recall,evaluation_tr.py计算图搜文的recall。
standard_path = sys.argv[1]可以把sys.argv[1]改成本地的文件地址sys.argv[2]和[3]同理。
使用evaluation_tr.py前需要先通过 transform_ir_annotation_to_tr.py 把 train_texts.jsonl(就是图文对应的那个json文件)由文到图的格式转为图到文。
六:总结
1.用Make_json.py将xlsx里的文本转换成json格式(对于测试集只有文本,不知道图文对匹配关系的情况,每行的image_ids字段处理为空列表即可,即"image_ids": []); 用Img2base64将图片编码成base64格式(.tsv文件) 2.用build_Imdb_dataset.py把.tsv和.json文件转换为内存索引的LMDB数据库文件 2.5.对模型进行finetune(微调)。(可以不进行,直接用预训练模型) 3.用extrac_features.py提取图文特征得到train_texts.txt_feat.jsonl和train_imgs.img_feat.jsonl 注意文本最大字长设置为finetune时的一样,不然会影响准确率 4.用make_topk_predictions.py进行文到图检索(文本召回相关图片)通过3得到的两个jsonl文件得到train_predictions.jsonl; 用make_topk_predictions_tr.py进行图到文检索(图片召回相关文本)通过3得到的两个jsonl文件得到train_predictions_tr.jsonl 检索是利用提取出的图像特征和文本特征计算相似度,然后对相似度进行排序。 5.用evaluation.py计算文搜图的recall,evaluation_tr.py计算图搜文的recall。(transform_ir_annotation_to_tr.py用来把 train_texts.jsonl由文到图的格式转为图到文) standard_path = sys.argv[1]可以把sys.argv[1]改成本地的文件地址sys.argv[2]和[3]同理r
路径问题
如果运行eval文件夹里的文件,找不到cn_clip,路径有问题的话,可以加上
import os import sys # 获取当前脚本所在的目录路径 current_directory = os.path.dirname(__file__) # 找到 cn_clip 文件夹的路径 cn_clip_path = os.path.abspath(os.path.join(current_directory, '..', '..')) # 假设 cn_clip 文件夹在 eval 文件夹的上两级目录中