共计 2110 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点
原始 CLIP 模型虽然在通用领域表现出色,但在特定业务场景(如电商商品搜索)中往往面临以下问题:
- 领域分布偏移:CLIP 训练数据(如网络图片)与业务数据(如商品白底图)存在显著差异
- 细粒度特征不足:鞋款差异、口红色号等细微特征难以被通用模型捕捉
- 文本描述不匹配:用户搜索 query(” 适合夏天的连衣裙 ”)与商品标题(”2023 新款女装碎花裙 ”)存在语义鸿沟
技术对比
Fine-tuning vs Prompt Tuning
- Fine-tuning 优势:
- 可调整整个模型参数,适应领域数据分布
- 通过对比学习直接优化 embedding 空间
-
适合数据量充足的场景(>10 万样本)
-
Prompt Tuning 优势:
- 仅需调整少量参数(<1%),训练成本低
- 保持原始模型的知识不被破坏
- 适合小样本场景(<1 万样本)
损失函数选择
- 对比损失(Contrastive Loss):
- 优点:直接优化正负样本间距,适合跨模态对齐
-
公式:$L = -\log\frac{e^{sim(q,k^+)/\tau}}{\sum_{i=1}^N e^{sim(q,k_i)/\tau}}$
-
交叉熵损失:
- 优点:分类任务性能稳定
- 局限:需预定义类别,不适用开放域检索
核心实现
数据加载器构建
import torch
from transformers import CLIPTokenizer
from torchvision import transforms
# 图像增强
img_transform = transforms.Compose([transforms.Resize(224), # CLIP 标准输入尺寸
transforms.RandomHorizontalFlip(), # 增加数据多样性
transforms.ToTensor(),
transforms.Normalize((0.481, 0.457, 0.408), (0.268, 0.261, 0.275)) # CLIP 统计值
])
# 文本处理
tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-base-patch32")
def text_encode(text):
return tokenizer(text, padding='max_length', truncation=True, max_length=77, return_tensors="pt")["input_ids"][0]
双编码器架构

– 共享 Projection Head:将图像 / 文本向量映射到相同维度
– 温度系数 τ:控制相似度分布的尖锐程度(典型值 0.07)
关键训练代码
# 混合精度训练
scaler = torch.cuda.amp.GradScaler()
for epoch in range(epochs):
for i, (images, texts) in enumerate(dataloader):
# 梯度累积步骤
with torch.cuda.amp.autocast():
image_features = model.encode_image(images)
text_features = model.encode_text(texts)
# 计算对比损失
logits = (text_features @ image_features.T) * model.logit_scale.exp()
labels = torch.arange(len(logits)).to(device)
loss = (F.cross_entropy(logits, labels) + F.cross_entropy(logits.T, labels)) / 2
scaler.scale(loss).backward()
if (i+1) % 4 == 0: # 每 4 步更新一次
scaler.step(optimizer)
scaler.update()
optimizer.zero_grad()
性能优化
显存占用对比
| 精度 | Batch=32 | Batch=64 |
|---|---|---|
| FP32 | 9.8GB | OOM |
| FP16 | 5.2GB | 9.1GB |
学习率预热
# 线性预热(前 10% 训练步)def lr_lambda(current_step):
warmup_steps = total_steps * 0.1
if current_step < warmup_steps:
return float(current_step) / float(max(1, warmup_steps))
return 1.0
scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda)
避坑指南
- 文本长度问题:
-
解决方案:截断前 77token(CLIP 限制)后,添加 均值池化层
-
负样本比例:
- 经验值:batch 内负样本: 外部负样本 ≈ 3:1
-
硬负样本挖掘:选择相似度 top30% 的负样本
-
模型蒸馏:
- 可行方案:用微调后 CLIP 指导小模型(如 MobileViT)
- 保留 90% 性能时,模型可压缩至 1 / 5 大小
开放问题
当商品描述包含无关信息(如促销文案)时,如何改进对比学习对核心特征的关注度?可能的思路:
- 使用 Attention Mask 突出关键词
- 基于商品类别的自适应温度系数
- 先进行文本清洗的预处理流程
正文完
发表至: 未分类
近一天内
