项目概述

ClipCap2Story 是一个把图像变为描述与后续故事的轻量化流水线:首先用 CLIP 提取图像特征,再通过一个映射网络(projection / mapping network)把视觉特征映射到语言模型的嵌入空间,最后用 GPT‑2(或类似的自回归模型)生成自然语言的 caption 或 story。流程简化为:

  • CLIP 提取图像向量(视觉特征)
  • 映射网络将视觉向量转换为语言模型可消费的前缀向量
  • GPT‑2 接收前缀并生成描述或续写故事

这种两阶段方案易于实现、推理速度快,但受限于训练数据匹配(图像→故事)时的稀缺性,往往不能做到端到端最优。

实现过程

我做了哪些事

  • 在服务器上搭建 Conda 环境,使用 Python 3.10。
  • 安装 PyTorch 2.9(匹配 CUDA 12.8)并验证 GPU(RTX 5060)可用。
  • 下载并配置 ClipCap 与 GPT‑2 的预训练权重(将必要模型权重放在服务器上)。
  • 解决 CLIP 离线安装问题:在可用网络的机器上手动下载 CLIP 的 ZIP 包,上传到腾讯云服务器并离线安装。
  • 修复旧版权重与新版 GPT‑2 权重键名不兼容问题(主要是权重字典中 attn.biasmasked_bias 等命名差异),编写了脚本转换键名以兼容当前 transformers 的加载方式。
  • 使用 FastAPI 封装两个端点:/caption(返回简短描述)与 /story(基于 caption 续写故事),并将服务部署在腾讯云(使用 uvicorn 运行,绑定 0.0.0.0:8000)。

示例部署命令(服务器上):

1
2
3
4
conda create -n clipcap python=3.10 -y
conda activate clipcap
pip install -r requirements.txt # 包含 fastapi、uvicorn、torch 等
uvicorn app:app --host 0.0.0.0 --port 8000 --workers 1

遇到的困难与解决

  • 网络无法访问 Hugging Face:配置 HF_ENDPOINT 为镜像源并启用本地缓存;关键模型文件手动下载并通过离线方式上传与加载。
  • GPU 不兼容:发现 RTX 5060 在较旧的 PyTorch 版本中问题较多,升级到 PyTorch 2.9 + CUDA 12.8 后稳定运行。
  • 终端环境配置反复失败:系统登录脚本与 conda 初始化在不同 shell/服务管理器下表现不一致,最终放弃自动激活,改为每次手动 conda activate clipcap
  • 版权重与新版 GPT‑2 键名不兼容:编写转换脚本,将旧权重键 attn.bias 等映射到新版所需的键名(例如 masked_bias),确保 model.load_state_dict 成功。
  • 故事生成质量不稳定(核心问题):找不到合适的“图片‑故事”配对数据集,无法进行端到端训练,只能采用两阶段生成(先 caption 再续写)。两阶段会在 caption → story 之间产生信息损失,导致生成过程容易偏航。
  • 尝试增强 caption 长度以保留更多视觉信息:将 ClipCap 的 entry_length 从 40 增加到 80,结果模型多次在句号处结束或输出重复混乱的 token。这表明 ClipCap 在训练时已习惯于短 caption,强行增长并不能得到有意义的长描述。结论:若要获得丰富视觉信息,应考虑使用 BLIP‑2、或收集图片+故事数据进行端到端微调。

最终方案

  • 接受两阶段方案的局限性,但通过调参减缓问题:
    • 将生成温度 temperature 适当降低(如 0.7→0.6)
    • 增加 repetition_penalty(如 1.2→1.5)
    • 调整 top_k / top_p 以控制创造性与稳定性
  • 将该服务定位为“创意发散”工具,而非高精度图像理解工具
  • 在文末提出未来改进方向(参考:换用 BLIP‑2、端到端微调等)

API 设计与使用说明

  • 接口:
    • POST /caption:接收单张图片(multipart/form-data),返回短描述 JSON:{"caption": "..."}
    • POST /story:接收单张图片或前端可先调用 /caption 获取 caption 再发送给 /story,返回故事 JSON:{"story": "..."}

请求示例(curl):

1
2
curl -X POST "http://xxx.xx.xxx.xx:8000/caption" -F "file=@/path/to/image.jpg"
curl -X POST "http://xxx.xx.xxx.xx:8000/story" -F "file=@/path/to/image.jpg"

成果展示(交互式前端示例)

下面是一个可以直接嵌入到 Hexo Markdown 的 HTML+JS 交互示例。前端接受图片上传(最大20M),分别调用 /caption/story,并在页面上显示结果。

ClipCap2Story 在线演示

代码关键点(后端实现思路)

  • 使用 CLIP 的图像编码器(或 ViT)得到图像特征向量:img_feats = clip_model.encode_image(image_tensor)
  • 训练或加载一个小型映射网络 proj_net,将 img_feats 映射为 GPT‑2 的前缀嵌入 prefix_embeds
  • prefix_embeds 拼接/注入到 GPT‑2 的初始输入(例如作为前缀 token 的嵌入),然后用 GPT‑2 做自回归生成
  • 重要的工程细节:
    • 离线安装 CLIP:把包与权重放在服务器路径并使用 pip pip install ./CLIP.zip 或手动导入
    • 权重键名兼容:加载 state_dict 前做键名替换脚本(python dict 操作)
    • 生成时调优 temperature、top_p、repetition_penalty 等参数以获得稳定输出

GitHub 仓库链接

总结与未来改进方向

  • 总结:ClipCap2Story 在工程实现上较为轻量,能快速把图片映射为可读文本并生成创意故事,但受限于训练数据与模型结构,无法在信息完整性与故事连贯性上与端到端专门训练的系统比肩。
  • 未来改进建议:
    • 使用 BLIP‑2 / Flamingo 等能更好利用视觉-语言对齐的模型,以提升图像—故事的一致性。
    • 收集或合成大规模“图片→故事”配对数据,进行端到端微调以减少中间信息损失。
    • 将 ClipCap 的映射网络改为更强的条件生成器(如 prefix‑tuning、adapter、或者视觉指令模块),并尝试联合训练。
    • 在服务端加入缓存、限流与异步任务队列(如 Celery / Redis)以支撑并发请求与大文件上传。
    • 在前端加入图像剪裁与多模态提示(如手动输入风格或主题)以提升故事生成的可控性。