简介COCO-CN 是一个面向跨语言多媒体研究的中英双语图像描述数据集通过人工书写的中文句子与标签对 MS-COCO 进行扩充适用于图像标注、图像字幕生成及跨语言图像检索等任务适合从事多模态学习、视觉语言预训练与跨语言迁移的研究者与开发者使用。资源包共 519 个文件以 Python 脚本、JavaScript 前端代码、文本与 Shell 脚本为主另含少量模型权重、图片、网页与配置文件压缩包约 133.53MB兼顾数据处理、模型训练与结果可视化。数据集 201805 版包含 20,341 张图像划分为训练、验证与测试三部分并关联 22,218 条人工书写中文句子与 5,000 条人工翻译句子同时提供机器翻译对照便于开展跨语言对比实验。包内还附带基于 ResNeXt-101 的评测工具可用于在 COCO-CN 测试集上检验不同字幕系统的表现。目前已有 820 人学习下载适合需要中文图文对齐语料与评测基准的读者参考使用。1. 当 MS-COCO 遇上中文一份跨语言多媒体任务的资源包拆解做跨语言图像描述这行的都知道一个尴尬事MS-COCO 是英文图像描述领域最常用的基准5 万多张图、每张 5 条英文标注生态成熟得不能再成熟。但一旦要把模型往中文场景迁移问题就来了——你拿不到与 MS-COCO 图像一一对应的中文句子和标签。自己翻成本高、质量参差、对齐关系还容易乱。coco-cn 这个资源包就是冲着这个缺口来的它在 MS-COCO 原有图像基础上补充了中文描述句子和中文标签让同一批图同时具备中英双语监督信号。适合做中文 image-captioning、image-tagging、跨语言图文检索的从业者也适合想验证多语言模型在图文任务上表现的研究者。下面我从数据组织、加载方式、训练接入、坑点排查几个层面把这份资源拆开讲清楚。2. 数据组织与加载中文句子和标签到底怎么存的2.1 文件结构与字段含义coco-cn 的核心思路是不改动原始图像文件只在标注层面做扩展。常见做法是沿用 COCO 的 JSON 标注格式把中文句子和标签分别放在独立的 JSON 文件里通过 image_id 与原始图像对齐。典型的目录结构大致是这样coco-cn/ ├── annotations/ │ ├── captions_train_zh.json │ ├── captions_val_zh.json │ ├── tags_train_zh.json │ └── tags_val_zh.json ├── images/ │ ├── train2017/ │ └── val2017/ └── splits/ ├── train_ids.txt └── val_ids.txt中文描述文件沿用 COCO captions 的字段设计每条记录包含image_id、caption中文句子、id标注唯一编号。标签文件则更轻量通常是image_id对应一个中文标签列表。这里有个关键点image_id必须与原始 MS-COCO 完全一致否则图文对齐会直接崩掉。我一般会先跑一遍校验脚本确认中文标注覆盖的图像集合是原始图像集合的子集且没有重复 image_id。2.2 用 Python 加载并做基本校验加载逻辑不复杂但校验步骤不能省。下面这段代码做三件事读入中文描述、检查 image_id 是否都在原始图像列表里、统计每张图的标注数量分布。import json from collections import Counter # 加载中文描述标注 with open(coco-cn/annotations/captions_train_zh.json, r, encodingutf-8) as f: zh_captions json.load(f) # 加载原始 COCO 图像 id 列表假设已导出为 txt with open(coco-cn/splits/train_ids.txt, r) as f: valid_ids set(int(line.strip()) for line in f) # 校验中文标注的 image_id 是否都在有效图像集合中 annotations zh_captions[annotations] img_ids_in_ann set(a[image_id] for a in annotations) missing img_ids_in_ann - valid_ids print(f标注中出现了 {len(missing)} 个不在图像列表里的 image_id) # 统计每张图的标注条数 count_per_img Counter(a[image_id] for a in annotations) dist Counter(count_per_img.values()) print(f每张图标注条数分布: {dict(dist)})逻辑说明第一段读 JSON第二段读图像 id 白名单第三段做集合差集找异常。参数方面encodingutf-8必须显式指定中文标注文件在部分系统默认编码下会读成乱码。count_per_img的分布结果很关键——如果大部分图只有 1 条中文标注而原始 COCO 是 5 条英文那在做跨语言对齐训练时就要考虑采样策略不能让中文侧监督信号太弱。2.3 标签数据的加载与过滤标签文件比描述文件简单但坑在于标签粒度不统一。有的标签是物体类别如“人”“车”有的是属性如“红色”“木质”直接拿来当多标签分类目标会引入噪声。我一般会先做一轮频次过滤把出现次数低于阈值的标签丢掉再决定是否保留。with open(coco-cn/annotations/tags_train_zh.json, r, encodingutf-8) as f: zh_tags json.load(f) # 统计标签频次 tag_counter Counter() for item in zh_tags: for tag in item[tags]: tag_counter[tag] 1 # 过滤低频标签阈值设为 5 MIN_FREQ 5 valid_tags {t for t, c in tag_counter.items() if c MIN_FREQ} print(f原始标签数: {len(tag_counter)}, 过滤后: {len(valid_tags)}) # 构建 image_id - 过滤后标签列表 的映射 img2tags {} for item in zh_tags: filtered [t for t in item[tags] if t in valid_tags] if filtered: img2tags[item[image_id]] filteredMIN_FREQ这个参数没有绝对标准5 是一个保守起点。如果下游任务是标签预测阈值可以适当提高如果只是做辅助监督低一点也无妨。过滤后为空的图像建议直接跳过不要塞空列表进去否则训练时 batch 内会出现无效样本。3. 接入 image-captioning 训练从数据到模型的完整链路3.1 构建中文描述的数据管道把 coco-cn 接进 captioning 训练核心是把中文句子转成 token 序列并与图像特征对齐。常见做法是继承 PyTorch 的 Dataset 类在__getitem__里同时返回图像张量和中文 caption 的 token id。下面是一个简化但可运行的管道示例import torch from torch.utils.data import Dataset from PIL import Image from transformers import BertTokenizer class CocoCNCaptionDataset(Dataset): def __init__(self, ann_file, img_root, tokenizer_namebert-base-chinese, max_len32): with open(ann_file, r, encodingutf-8) as f: data json.load(f) self.annotations data[annotations] self.img_root img_root self.tokenizer BertTokenizer.from_pretrained(tokenizer_name) self.max_len max_len def __len__(self): return len(self.annotations) def __getitem__(self, idx): ann self.annotations[idx] img_id ann[image_id] caption ann[caption] # 图像加载与预处理 img_path f{self.img_root}/{img_id:012d}.jpg image Image.open(img_path).convert(RGB) image image.resize((224, 224)) image torch.tensor(list(image.getdata()), dtypetorch.float32).view(224, 224, 3).permute(2, 0, 1) / 255.0 # 中文分词与编码 encoded self.tokenizer( caption, max_lengthself.max_len, paddingmax_length, truncationTrue, return_tensorspt ) return image, encoded[input_ids].squeeze(0), encoded[attention_mask].squeeze(0)逻辑说明img_path的格式化用了012d因为 COCO 图像文件名是 12 位数字补零这个细节错了会直接报 FileNotFoundError。tokenizer_name选中文 BERT 的词表是因为它原生支持中文字符切分比用英文 tokenizer 硬切中文效果好得多。max_len32对中文描述通常够用中文句子平均长度比英文短设太大反而浪费显存。图像预处理这里写得比较粗糙实际项目中建议用 torchvision 的 transforms 做归一化和增强。3.2 跨语言检索场景下的双塔结构接入如果下游任务是 cross-lingual image retrieval数据用法就不一样了需要同时利用英文和中文描述让图像特征与两种语言的文本特征在同一空间对齐。常见做法是双塔结构图像侧用 CNN 或 ViT文本侧用多语言 BERT然后用对比损失拉近匹配对。import torch.nn as nn from transformers import BertModel class DualEncoder(nn.Module): def __init__(self, image_dim512, text_dim768, embed_dim256): super().__init__() # 图像编码器简化全连接模拟已提取特征 self.image_proj nn.Linear(image_dim, embed_dim) # 文本编码器 self.text_encoder BertModel.from_pretrained(bert-base-multilingual-cased) self.text_proj nn.Linear(text_dim, embed_dim) def forward(self, image_feat, input_ids, attention_mask): img_emb self.image_proj(image_feat) text_out self.text_encoder(input_idsinput_ids, attention_maskattention_mask) text_emb self.text_proj(text_out.last_hidden_state[:, 0, :]) # L2 归一化便于计算余弦相似度 img_emb nn.functional.normalize(img_emb, dim-1) text_emb nn.functional.normalize(text_emb, dim-1) return img_emb, text_emb这里选bert-base-multilingual-cased而不是中文 BERT是因为跨语言检索需要中英文文本共享同一个编码空间。embed_dim256是投影后的公共维度太小会损失信息太大对比学习收敛慢256 到 512 之间是比较稳妥的范围。归一化步骤不能省否则余弦相似度计算会受向量模长干扰。训练时用 InfoNCE 损失batch 内负样本就够用但 batch size 建议不低于 64否则负样本太少模型学不到有区分度的表示。3.3 标签辅助任务的多任务学习接入coco-cn 的中文标签可以当作辅助监督信号和 captioning 主任务做多任务学习。做法是在解码器之外加一个标签分类头用多标签交叉熵损失。这样做的价值在于标签提供了比句子更直接的物体级监督能帮助模型更快收敛。class MultiTaskModel(nn.Module): def __init__(self, num_tags, hidden_dim512): super().__init__() self.encoder nn.Linear(2048, hidden_dim) # 图像特征降维 self.caption_head nn.Linear(hidden_dim, 30000) # 词表大小 self.tag_head nn.Linear(hidden_dim, num_tags) def forward(self, image_feat): h torch.relu(self.encoder(image_feat)) caption_logits self.caption_head(h) tag_logits self.tag_head(h) return caption_logits, tag_logits # 损失组合 caption_loss nn.CrossEntropyLoss(ignore_index0) tag_loss nn.BCEWithLogitsLoss() total_loss caption_loss(caption_logits, caption_targets) 0.3 * tag_loss(tag_logits, tag_targets)0.3这个权重系数是标签损失的缩放因子我一般从 0.1 到 0.5 之间试。标签损失太大会压制 captioning 主任务太小则起不到辅助作用。ignore_index0是把 padding 位置排除在损失计算外这个必须设否则模型会去学预测 padding。num_tags取决于前面标签过滤后的数量建议在构建模型前先确定好。4. 避坑与排查中文标注接入时最容易翻车的五个地方4.1 现象训练 loss 正常下降但生成的中文描述全是重复字原因中文 tokenizer 的 padding token 和 decoder 的起始 token 冲突模型把 padding 当成了有效输入。解决检查 tokenizer 的pad_token_id和模型 decoder 的bos_token_id是否相同如果相同手动把 padding 的 label 设为-100或ignore_index确保损失计算时跳过。4.2 现象跨语言检索时中文 query 检索英文描述图的效果远差于英文 query原因中文和英文描述在数据量上不均衡中文标注条数通常少于英文导致文本编码器偏向英文。解决在采样时对中文样本做上采样或者在损失函数里给中文对更高的权重。我一般会在 DataLoader 的 sampler 里做分层采样保证每个 batch 中英样本比例接近 1:1。4.3 现象标签分类准确率虚高但实际预测全是高频标签原因标签分布长尾严重模型学会了只预测“人”“车”这类高频标签就能拿到不错的整体准确率。解决改用 macro-F1 或 per-class recall 作为评估指标同时在损失里给低频标签更高权重。BCEWithLogitsLoss的pos_weight参数可以传入一个与类别频次成反比的张量。4.4 现象图像文件读取时报 FileNotFoundError但文件明明存在原因COCO 图像文件名是 12 位数字补零而中文标注里的 image_id 可能是整数格式化时位数不对。解决统一用f{img_id:012d}.jpg格式化或者在数据加载前先把所有 image_id 转成字符串并补零到 12 位。这个坑我踩过不止一次后来直接在 Dataset 初始化时做一次全局校验。4.5 现象中文描述里混入了英文单词或乱码原因原始中文标注在采集或转换过程中编码不一致部分文件用了 GBK 或 Latin-1。解决加载时统一用encodingutf-8如果报 UnicodeDecodeError用errorsignore先跳过然后单独把异常样本挑出来人工检查。更稳妥的做法是在数据预处理阶段加一个正则过滤把非中文、非标点的字符占比过高的句子直接剔除。5. 进阶技巧用中文标签做零样本检索的验证闭环资源包用顺了之后我习惯拿中文标签做一件事验证跨语言检索模型的零样本能力。具体做法是把标签当作查询词去检索训练集中没见过的图像看 top-k 命中率。这个验证不依赖描述句子能独立反映模型对中文语义和视觉内容的对齐程度。操作上分三步。第一步从标签文件里挑出频次中等既不是最高频也不是只出现一两次的标签比如出现 20 到 100 次的构建查询集。第二步用训练好的双塔模型提取所有验证集图像的 embedding存成矩阵。第三步对每个查询标签用文本编码器生成 embedding算余弦相似度取 top-10 图像人工或半自动判断是否相关。import numpy as np # 假设 img_embeds: [N, 256], tag_embeds: [M, 256] sim_matrix tag_embeds img_embeds.T # 余弦相似度已归一化 # 取每个标签的 top-10 图像索引 topk_indices np.argsort(-sim_matrix, axis1)[:, :10] # 计算命中率假设有 ground truth 映射 hits 0 for i, indices in enumerate(topk_indices): if any(idx in gt_map[i] for idx in indices): hits 1 print(fTop-10 命中率: {hits / len(topk_indices):.4f})这里的关键参数是 top-k 的 k 值。k10 适合快速验证k1 更能反映精确匹配能力。gt_map需要自己根据标签和图像的对应关系构建如果标签本身就是从图像标注里来的可以直接用原始映射。这个闭环的好处是不需要额外标注用资源包自带的中文标签就能跑迭代速度快。还有一个技巧是标签去偏。中文标签里有些词天然容易匹配到大量图像比如“天空”有些则很具体比如“红色消防栓”。做零样本验证时我会把查询标签按频次分成三档分别统计命中率这样能看出模型是真正理解了语义还是只会抓高频词。频次分档的边界可以按分位数来定比如 33% 和 66% 分位。从那以后我每次拿到新的跨语言图文数据都会先跑一遍标签零样本检索确认中文语义和视觉特征确实对齐了再投入大规模训练。这个习惯帮我省下了不少白跑的 GPU 时间。希望帮到你。本文还有配套的精品资源点击获取