0%

构建文搜图评测数据集

文搜图与CLIP

CLIP模型是OpenAI在2021年的一个多模态工作,讨论一种新型的(在当时看来)图片特征和文本特征对齐的方法,通过使用大量从互联网爬取的图文数据,CLIP在许多任务中展现了优秀性能。 简单来说,一个典型的CLIP结构包含一个图像编码器和一个文本编码器,这两个编码器分别单独将图像数据和文本数据映射成向量,在分别经过两个线性映射以及normalization后,图像特征向量和文本特征向量被映射为相同维度(CLIP模型最终的特征向量典型为512维)。 CLIP模型的训练数据是大量的图文匹配对,其中图片和文本均是互联网爬取的数据(所以可以想象这些训练数据的“质量”并不会很高😒),一般来说文本数据是对图片内容的描述,也就是说这里的文本数据在概念上对应了图片的Caption。不过由于互联网爬取的数据一般都比较脏,所以很多文本数据并不完全对应了图片的内容描述,有些看起来是广告词,有些看起来是文章的标题。 CLIP模型的训练loss形式上比较简单,在一个batch中,分别采样\(N\)个图文匹配对,然后计算一个\(N\times N\)的矩阵,其中第\((i,j)\)位置的元素是第\(i\)张图片的特征向量和第\(j\)个文本的特征向量的内积(对角线上的元素就是图片文本匹配对的内积结果)。 然后对这个\(N\times N\)的矩阵分别按行和列计算SoftMax得到两个统计了图文匹配概率的矩阵,其中的一个矩阵对每张图片计算与所有文本之间的匹配概率,另一个矩阵对每个文本计算与所有图片之间的匹配概率,最后两个矩阵分别求CrossEntropy取平均。一般来说这个\(N\)很大,基本是10K以上。

上述的是CLIP原论文的训练方式,后续的工作在训练方案上会有一些改进。

在形式上,CLIP在每个batch中针对每个图片和每个文本训练了一个10K量级的分类任务,其中标签就是互联网爬取的数据集定义的图片文本对匹配关系,即每张图片有唯一一个对应的文本,每个文本有唯一一个对应的图片。 所以一个很自然的想法是,可以用CLIP来做图像和文本搜索,而且既可以做到文搜图也可以做到图搜文。具体来说,假设需要实现文搜图,那么可以使用CLIP的图像编码器,将待处理的图像数据集全部转化为向量,存储在向量数据库中(个人demo体验的话可以试试Faiss,挺好用的)。在接受到一个检索问询Query后,在线地使用CLIP的文本编码器将Query也转化为向量,然后调用向量数据库的功能,找到和这个Query向量最相似(内积得分最高)的TopK个图片向量和对应的图片,这就完成了图片检索的任务。

中文CLIP可以参考项目: ChineseCLIP

评测数据集

在构建一个文搜图系统之前,还需要评测当前系统的性能。目前来说,有很多公开的学术数据集可用于评测中文CLIP的Recall性能:

  • MUGE数据集:这里特指的是MUGE数据集的图片检索任务,该任务要求模型根据自然语言形式的检索query,从给定的商品图片池中检索出相关图片,衡量模型多模态理解与匹配的能力。测试集包含大约5k的query和3w的图片候选池,其中query的风格类似于电商场景的搜索短语。
  • Flickr-30K-CN数据集Flickr-30K的中文版本,测试集包含大约5k的query和1k的图片候选池,其中query的风格大多是一个描述图片内容的句子(caption)。
  • COCO-CN数据集:基于MS-COCO构建,测试集有大约22218个人工构建的caption,以及对应的1000张图片,不过该数据集需要申请获得,非公开。
  • Noah-Wukong数据集:华为诺亚开源的数据集,其中测试集部分包含33365张图片以及对应的caption(query),不过我从提供的URL只能下载23106张图片。数据集应该是从百度爬取的,其中的caption部分并非人工标注的,所以风格上看起来比较凌乱。
  • TaiSu数据集(这个只有训练部分,不过也列在这里吧):一个图文一对一的训练数据集。
  • Zero数据集:360公司提供的数据集,其中测试部分分成了ICM, IQM, ICR, IQR四个子任务。分别是,image和caption的匹配任务(即判断image和caption之间是0还是1的匹配关系),image和query的匹配关系,image和query的检索关系(一对一的关系,没有一对多),image和query的检索关系。算是挺全面的了,其中caption是图片的内容描述(不过不是人工打标的,应该类似百度那种),query是真正用户的搜索query(至少数据集的发布方是这么宣称的)。

上面这些评测数据集的问题在于:

  • 只提供了图文一对一的关系,因此只能用来算Recall,不能算Precision。
  • 文本部分(除了COCO-CN)是互联网爬取的图文数据,比较脏,有些看起来是广告词,有些看起来是文章的标题,实在不能算是通常意义上的检索query。
  • 这些图片要么来自于互联网,要么是特定领域( MUGE是电商数据集,COCO-CN源自detection),无法针对某一需求domain评测。
  • 这些数据集的训练集部分很多都被clip train过了,在同一个domain,所以指标上会比较好看,但是很难体现出实际使用中的效果。

最近业务上需要构建文搜图评测数据集,评测文搜图系统的性能。

基于LLM构建Recall测试集

本节考虑的问题是,如果需要在某个特定domain,比如就是一般人在日常生活中可能拍摄的一些照片(普通用户的个人相册场景)这种,如何构建对应的评测数据集。

首先需要明确的前提是,上述的场景设定假设是只能够获得一些照片数据以及这些照片的元信息,比如照片拍摄的时间和地点,但是不包含这些照片在文字域的补充信息,比如标签、主题或是照片中出现的物体和人物等等。 在这种情况下,我认为构建“检索准确度指标”(比如Precision5、Precision10)不说是毫无办法,至少也是比较困难的。因为在计算Precision时,需要为每一个问询Query指定一个图片的GroundTruth集合,这个集合的数量一般大于1(否则就只能计算Precision1了,那和Recall1是一样的)。 那么也就是说,我们需要制定一个规则,用来分辨哪些图片可以被归类为同一类,即属于同一个GroundTruth集合,绑定同一个问询Query。

这个给图片分类,或者说打标签的规则,是很难制定的,因为图片内容的变化可能会比较连续,不好设定一个阈值,区分两张图片分别属于两个不同的问询Query。 举个例子,假设一家人去西湖旅游,拍摄了很多照片,这些照面的内容假设是一家人在西湖边的合照(或独照),背景是西湖的常见景物。 那么现在考虑存在这么一张图片作为pivot,照片中出现了3个人,背景有湖水、柳树和雷峰塔。 进一步,我们可以合理假设存在很多其他内容相似的照片,比如照片是2个人或1个人,背景有或没有湖水,有或没有柳树,有或没有,雷峰塔。 那么请问,这些照片,应该被分为同一个GroundTruth,还是多个不同的GroundTruth?

一个直观的想法是,对于不同的问询Query,这些图片可能属于同一个Query,也可能属于不同的Query。比如如果Query是“西湖旅游”,那么这些照片显然应该是属于这个问询的;但是如果Query是“西湖旅游全家福”,那么至少应该要排除掉那些只出现了2个人或1个人的图片。 另一方面,别忘了我们只能使用这些照片本身的数据,没有任何关于图片的准确文字描述。 所以如果要构造一个能够评测Precision的数据集,一方面需要能够基于图片(或图片集合)生成问询Query;另一方面需要能够区分不同层次的Query,并有能力为不同的图片生成聚类,绑定同一个问询Query。 除了上述的难点外,为了评测Precision,需要保证构建评测数据集时,GroundTruth集合能找得足够全,否则测试的指标会很低,因为如果本该是GroundTruth的图片没有被纳入,无形中降低了Precision指标的上限。

关于这一点我个人的想法是,构建评测Precision的条件是,至少需要找到一个足够优秀的打标签(tag)系统。 假设存在这么一个给图片打标签的系统,能够比较准确地检测出图片中出现的内容,那么我们也许可以根据这个系统的标签GroundTruth集合,构建一颗标签树。 在最简单的情况下,假设标签集合中的所有标签都没有上下级关系,那么这个树只有两层(假设第一层的根节点是dummy),所有的标签信息都位于叶子节点; 在稍微复杂的情况下,很多模型提供的是有层级关系的标签集合,那么标签之间是有粒度关系的,比如可能某个级别的标签是“动物”,“动物”分类下有“狗”,“狗”的分类下又会有具体的品种,等等。 根据标签系统构建测试benchmark的好处是,即使这个GroundTruth标签集合比较小,那么也是不影响评测的准确性的。 考虑如下情况:假设存在一个打标签系统,所有的标签只有两个,分别是“白天”和“黑夜”,那么根据当前标签能构建出的问询Query也只有两个,那么此时评测的就是待测检索系统对“白天”和“黑夜”概念的检索效果。 即使被测图片集合中可能会存在很多内容,但是这并不影响评测,因为这些概念并不会出现在Query集合中。 此外,基于上述思想,使用打标签系统构建的评测数据集是有很好的可扩展性的,即使初版的标签树很简单,就如上述的“白天”和“黑夜”的例子,在后续依然可以持续完善标签树的复杂度,丰富测试的问询Query集合,并且无需最图片集合做出修改。

但是,构建这个一个值得信赖的标签系统是很困难的,因此我在第一次构建评测数据集时放弃了测试Precision指标。 我的思路回到了开源benchmark的做法,即为图文构建一对一的绑定关系,先搞定测试Recall指标。 然而正如上述提到的前提,我只有图片数据,因此一个自然的想法是,能不能借助LLM的力量为图片批量生成较高质量的caption?以LLM生成的caption作为问询Query,测试文搜图系统在特定domain的Recall性能。

模型选择

在当前时间节点(23年9月),我主要考虑了以下几个可用于图像描述的多模态模型:

  • Salesforce的Blip2系列: blip系列是现在这段时间内很有代表性的多模态模型架构了,很多多模态图文大模型都是基于blip的结构。但是这个huggingface上的官方blip仓库只提供了图片英文caption功能,不适用于我当前的中文文搜图场景。
  • Ziya-BLIP2-14B-Visual-v1: 也是基于blip2结构的图文多模态大模型,支持中文,不过我最后没有选择该模型,原因是需要申请LLama的权重,在此期间我尝试了其他模型,效果基本符合要求,因此没有采用Ziya
  • VisualGLM-6B: 和知名的ChatGLM同源,试用后发现问答和详细图片内容描述还是很不错的。但是我的场景需要大约40字描述图片的主要内容,语言风格尽量朴实,类似COCO-CN数据集的caption风格。但是VisualGLM-6B的图片描述类似写作文,加入了很多不必要的细节描述以及个人化猜测,而且这些行为很难通过修改prompt避免(至少我自己尝试了不同prompt后发现差别不大),因此放弃使用该模型。
  • Qwen-VL: 这个注意要使用23年9月25日以后的版本,和第一版差别比较大。该版本和VisualGLM-6B相比,在我这个场景下的差异主要是Qwen-VL更能遵循指令要求,比较容易控制生成的图片caption的风格和详略程度,因此最后选择了该模型。

图片数据清洗

在我处理的图片数据集场景中,我可以获取绝大多数图片的拍摄时间和地点信息,这帮助我从千万量级的图片全集中筛选出我需要的部分。 需要强调的是,在我的场景中,并不是所有图片都能直接参与构建评测数据集。 一方面,全量数据太多了,不可能所有千万张图片都让LLM生成caption;另一方面,很多图片是相似的,这不利于准确评测Recall指标。 设想一些景区的旅游图片,这些图片在观感上可能是非常相似的,因此一个符合其中一张图片的caption可能也会适用于其他的所有图片。 然而,对于评测Recall来说,图片和其caption是一一对应的,那么,当我们用一个caption检索出了一些图片,即使 ground truth 图片不在top5序列中,这些检索出来的图片很可能也符合caption的描述,但是规则上我们错误地将这个 evaluation case 标记为 False。 具体而言,使用如下规则从全量图片集合中筛选出需要的部分:

  1. 选取宽高均小于5000的图片,并且宽高比大于0.2小于5,不符合该要求的图片将被剔除
  2. shuffle整个图片列表
  3. 有限选取不同地点的图片,若图片数量已满足需求,则直接返回图片集合;否则进行下一步
  4. 有限选择不同地点或不同时间的图片,若图片数量已满足需求,则直接返回图片集合;否则进行下一步
  5. 在剩余的图片中随机选取需要的数量,返回图片集合
  6. 选取的图片统一resize成512*512大小,方便保存

Prompt模板和Generation参数

使用Qwen-VL-Chat为每一张筛选出的图片生成caption,其中生成参数是:

1
2
3
4
5
6
7
8
response = qwen_vl_chat(
image = image_path,
text = "用40字描述图片的主要内容用于检索",
do_sample = False,
num_beams = 5,
max_new_tokens = 48,
min_new_tokens = 46
) # 单张图片耗时约2.2s
生成的caption可能会包含一些无用的前缀,这里我是用规则剔除了,需要多跑一些case搜集前缀。 生成的caption比较像COCO数据集的风格,以下是一些例子:

  • 一排排的课本书籍,主要是语文课课练作业本,封面是粉红色的书脊是绿色的
  • 一个公园内的人们在休闲散步
  • 一个电子电路板,上面有一个数码管,背景是一个台灯

我分别构建了100K、10K、1K三种不同规格的数据集,100K数据集下评测出的指标很低,检查过后发现有大量相似内容的图片,因此很容易导致前述的 Recall 指标缺陷的问题;1K数据集下的指标很好,应该是图片数量少,检索难度不大。

Badcase分析

总的来说,检索指标低大约有以下三种原因:

  1. ChineseClip没有正确召回
  2. 图片的Caption标注错误(这个现象比较少,我检查过一些图片case,总的来说qwen还是比较靠谱的)
  3. 存在相似图片,并且图片的caption无法区分(这个比较重要,毕竟40左右的字数很难准确定位唯一一张图片)

基于Object365构建Precision评测集

前一节我们主要讨论了如何基于自有图片为文搜图构建Recall测试集,在本节中我们将考虑构建Precision测试集(当然这个测试集也可以用来评测Recall,不过我觉得意义不大)。 具体而言,基本想法是借助Detection数据集的标注结果,按照规则构建图片query,进而获得query与多张 ground truth 图片之间的对应关系。 这个思路类似之前我们讨论过的建立标签树系统,不过对图片打标这件事情需要耗费大量人力,因此我想到了借助Detection数据集的标注结果,最终我选择了Object365-2020数据集,该数据集比较好下载,而且标注的物体类别数量相比COCO等也比较多。

数据来源及构建方式

图片数据来自Object365-2020数据集,该数据集原本用于训练检测任务,为365种不同的物体标注了检测框和类别,相比之前的COCO2017数据集,Object365-2020的图片数量更多,涵盖的物体类别更多,平均每张图片的标注框也更多。构建流程选择了Object365-2020数据集的测试集部分,共包含193588张图片,以及2826303个标注框。Object365-2020数据集的类别标签都是英文的,因此我首先用python库translators将这些标签翻译为中文,然后人工检查正确性。

构建Query

  • 首先需要找出种子标签:
    • 首先过滤出可检索标签 。由于Object365-2020数据集的很多标注物体尺寸很小(例如香烟),很难被检索出来,因此我统计了每个类别的检测框占比原图的大小面积比例。只有当该面积比例大于10%并且该类别下累计的图片数量大于等于10的时候,该类别才会被纳入可检索的范围。例如对于Lipstick(口红)这个标签类别,最大的检测框占比原图的面积是0.9%,因此Lipstick(口红)不会被纳入可检索范畴。上述步骤将原数据集的365个标签类别过滤为267个可检索的一级混合标签
    • 增加混合等级。上一步骤筛选出了267个可检索的一级混合标签,基于这些标签集合,在一级混合的基础上,随机从该集合中选择另一个标签,生成可检索的二级混合标签(注意该步骤同样需要满足累计图片数量大于等于10的要求),该步骤总计生成了1212个可检索的二级混合标签
    • 以此类推,在可检索的二级混合标签的基础上继续增加新的标签,总计生成了889个可检索的三级混合标签
    • 最终获得了2368个各级可检索混合标签,相当于2368个检索Query种子。
  • 基于前述步骤,将每张图片与其对应的标签构建对应关系(当然一张图片可能与多个标签建立对应关系)。需要注意的是,此时不考虑检测框占比图片面积的限制,只要是数据集标注了的,都要建立对应关系。
  • 建立可兼容的标签映射。由于Object365-2020数据集的标注和翻译等问题,导致有些标签之间存在包含关系,需要额外的人工处理:
    • 例如数据集中有标签Other ShoesSneakersLeather Shoes等等,这里的Other Shoes是除了运动鞋皮鞋等等以外的其他种类的鞋子,但是不合适翻译成某个具体的中文概念。因此我人工建立了可兼容的标签映射,当搜索鞋子(Other Shoes)时候,如果检索出的图片有标签皮鞋(Leather Shoes),视作兼容标签鞋子(Other Shoes),判定为检索正确。
    • 对于另一些情况,比如Slippers(拖鞋)Sandals(凉鞋),这两种图片比较相似,而且大多数情况下占比图片的面积很小,个人觉得没必要区分,因此这两种标签互相兼容。
    • 前述两个例子的不同点在于:
      • 第一种兼容是非对称的,搜索鞋子(Other Shoes),返回结果为皮鞋(Leather Shoes)判定为正确检索,但是反过来则判定为错误检索。
      • 第二种兼容是对称的,Slippers (拖鞋)Sandals (凉鞋)这两种标签可以互相搜索。
  • 在获得多级可检索标签的基础上,生成用于检索的Query(这里可以考虑使用LLM造句子,不过我用了很简单的规则):
    • 第一种是使用"&"符号将标签连接起来,例如“人&摩托车&小汽车”,这种拼接规则称为“word”
    • 第二种是使用固定的模板,例如“一张人和摩托车和小汽车的图片”,这种拼接规则称为“sentence”

PS:为什么我认为上述步骤构建的数据集不适合用于评测Recall指标?因为一些Query对应的图片数量实在是太多了(可能有上千张),一般的Recall也就计算到top10,因此我认为评测Recall没有意义。

Badcase分析

总的来说,检索指标低大约有以下原因:

  1. Object365-2020存在漏标的情况
  2. ChineseClip对混合概念的检索效果不好:当检索Query包含多个检索实体概念的时候,ChineseClip检索的结果经常只能满足其中一个实体概念

同时评测Recall和Precision

在经过前述两种方案的“练习”之后,我开始构思如何构建一个更准确的评测数据集,我的主要想法是从两方面改进之前的方案:

  1. 最好能够同时评测检索器的Precision和Recall两个指标。这是当时我被要求做的,老板对于检索图片的准确率有非常要求,即要求不符合Query的结果不应该出现在检索结果中,当然这对于纯向量的ChineseClip来说无疑是很困难的,因为这些向量内积的数值只在相对大小上有意义,很难确定唯一一个阈值划分出那些与Query相关的结果。为了满足这些条件,团队用上了很多过滤规则,无疑会损害Recall指标,于是最好能在一个场景数据集下同时测试Precision和Recall,以期望能达到每个平衡。
  2. 检索的Query最好能不用图片的Caption。我在华为的诺亚Wukong测试集上测试过ChineseClip,指标异常地高,我猜想这是由于ChineseClip使用了Wukong的训练集部分训练(实际上是的,论文里就是这样写的)。这件事情让我意识到的是Query的形态对检索的结果会影响很大,我去看了一些Clip类型模型的训练集,其中提到有很多都是从互联网(中文的尤其是百度那)爬取图片,我建议读者也去看看百度搜图结果的文字表述部分,这些就是训练Clip的数据集,这些文字风格和COCO的Caption差别很大,当然也和正常人的搜索习惯差别很大。在之前的测试中我要求大模型生成类似COCO的Caption风格的Query,现在我想要试试如何生成更符合一般人检索习惯的Query。

Self-Instruct

在此期间我看到了一篇论文,名字我忘了,但是应该很容易用“Self-Instruct”作为关键字搜出来。这篇论文是22年的,主要想法是用GPT3(pretrain版本)生成用于指令微调的数据集,然后用这个GPT3生成的数据集微调GPT3自己,最终提高性能。

在具体做法上,这篇论文定义指令微调数据集由多个task组成,每个task可以分成instruction、input、output三个部分(有些特殊task只有instruction和output两个部分),其中instruction描述了任务需求,input给出了任务输入,output作为任务输出的groundtruth,一个task可能会包含多个实例,因为一个instruction兼容多个input和output。论文方案的基本流程是:

  1. 首先人工构建175个task作为种子task,每个task一个实例,有唯一的input和output;
  2. 从task pool中随机抽取6个种子task以及2个模型生成的task,取出其中的instruction部分,让模型再生成更多的instruction;
  3. 生成的instruction经过打标分类后由LLM生成对应的input和output,组成一个完整的task实例;
  4. 该task实例经过一些过滤规则后归档到task pool,作为下次迭代的素材,然后重复上述的迭代流程。

基本想法

基于self-instruct的思路,很容易想到如下的抽象方案:

  1. 首先人工构建一些Query作为“种子”;
  2. 然后写一个Prompt模板,要求LLM帮忙参照给出的范例生成更多的Query,Prompt中留出一个变量的位置存放“Query List”;
  3. 在第一次迭代中,将人工构建的Query种子放入Prompt模板的“Query List”位置,然后解析LLM的输出,收获LLM生成的Query样本,按照某些规则过滤后存入Query Pool;
  4. 在第k次迭代过程中,从Query Pool中随机采样一些Query加入Prompt模板的“Query List”位置,然后解析LLM的输出,获得LLM生成的Query样本,按照某些规则过滤后存入Query Pool;
  5. 重复迭代第四步,直到收集了足够多的Query样本或是达到最大迭代次数

一些失败的尝试

由于某些原因我无法使用ChatGPT,下面的尝试我都是使用Qwen-Chat-14B完成的,如果你有性能更好的LLM,应该做这些更轻松。 我首先想到的是人工构建不同的“主题”,然后针对每个“主题”人工列出一些“范例”,最后解析LLM的输出尝试获得更多的结果,以动物主题为例,Prompt可以设计为:

1
2
3
4
5
6
7
8
9
10
11
12
prompt = """
请给出一些名词用于检索图片,这些名词需要符合下面几个条件:

1. 需要是动物的名词
2. 至少包含两个字
3. 该名词代表的图片容易辨认,有特定的颜色和形状

以下是一些符合标准的例子:

1. 鲨鱼;是鲨总目动物的通称;通常是灰色
2. 狸花猫;是一种猫,通常是灰色
""".lstrip()
Prompt中的示例可以多写几个,主要思路是few-shot,尝试让LLM根据提供的少量样本生成更多的相似样本。 上述做法的缺点非常明显,需要人工构建不同的“主题”,每个主题又要想Prompt模板,费时费力。

在这之后我又修改了方案,针对第一版方案需要人工写“主题”以及配套Prompt模板的缺点,第二版尝试让LLM模板直接生成描述图片内容的Query。 为了能够流程化地批量生产Query,需要指定一套模板,通过few-shot教会模型生成符合要求的Query。 在多次尝试后,为了避免LLM胡言乱语,需要设计范例,提示LLM经过思考后再给出答案,Prompt可以为:

1
2
3
4
5
6
7
8
9
10
11
12
13
GEN_QUERY_TEMPLATE = """
我需要收集一些图片素材,你被要求给出一些用于检索图片的“短语”,这些“短语”需要满足如下规则:

1. “短语”至少包含1个字,但是不多于10个字。
2. “短语”由多个“视觉元素”组成,“视觉元素”描述了检索目标图片的主要内容,“视觉元素”的数量是1个或者2个。
3. “视觉元素”可以是“动物”、“植物”、“颜色”、“形状”、“动作”、“知名景点”、“知名人物”、“交通工具”、“服装”、“家具”、“电子产品”等等
4. “视觉元素”的选取和搭配需要符合常识,并且尽量简洁。
5. “短语”应该符合人类的语言习惯,并且能够准确描述一类图片的内容。

下面是一些符合标准的短语例子,你被要求按照“思考”和答案两个步骤分行给出额外的5个结果,再次强调**“视觉元素”的选取和搭配需要符合常识**:

{samples}
""".lstrip()
其中的范例可以如下(实际使用时建议多写几个):
1
2
3
4
5
6
7
samples = """
思考:我决定选择(1)个视觉元素,该视觉元素选取(动物)分类中的(猫),最终选择的视觉元素是(猫)
答案:猫

思考:我决定选择(2)个视觉元素,第一个视觉元素选择(动物)分类中的(猫),(猫)有不同的外形和颜色,第二个可搭配(猫)的视觉元素选择形容词(长毛),最终选择的视觉元素是(猫)和(长毛)
答案:长毛的猫
""".lstrip()
在实际调试的时候我发现LLM生成的结果通常围绕“教堂”、“摩天大楼”、“圣诞节”、“瀑布”、“寿司”、“披萨”等意象反复组合其他限定词(比如美味的寿司、新鲜的寿司、海鲜寿司、日式寿司),虽然生成了很多结果,但实际上是少数几种物体的组合,造成测试图片的内容局限。 另一方面,我发现LLM生成的结果容易“发散”,这个在之前的尝试中也出现了,但是我按照第二版方案开始大规模生成数据的时候才发现了这个问题。Prompt中给出的“视觉元素”词语通常都是简短的,并且最终生成的Query只是“视觉元素”的简单组合。但是LLM在迭代生成的过程中,有打破这个限制,生成的短语越来越长,单个“视觉元素”由多种概念组合而成的现象。 这些“发散”的Query会再次进入Prompt成为新一轮迭代给LLM提供的范例,因此这种“发散”情况会越来越严重,最终导致LLM看起来生成了很多不同的Query,但是实际上都是车轱辘话。 我后来思考这种“视觉概念”方案是否合理,认为这种概念的定义还是太模糊了,很难把握基本视觉元素这个概念。例如“艺术家工作室中正在创作的油画”,其可能的视觉元素是“油画+艺术家工作室”,但是也有可能是“油画+艺术家+工作室”。 类似的例子还有很多,总之,我认为直接要求LLM给出Query是不合适的,并且考虑到使用的Qwen-Chat-14B的性能,要求组合概念也比较困难。

方案构思

在一系列尝试之后,最终数据集的构建流程分为两个阶段:

  1. 首先人工想一些范例,这些范例包含了“主题”和“物体”,这里需要的量很少,大约只用到3~5个范例;
  2. 然后让LLM基于已有的范例生成更多的“主题”和“物体”,但是这里我仅仅需要“主题”,不同太关心“物体”部分的生成质量;
  3. 人工挑选出适合下一阶段的“主题”和“物体”;
  4. 二阶段,对每一个“主题”,根据已有的“物体”范例,让LLM生成更多的“物体范例”
  5. 最后人工过滤所有的“物体”范例

对于一阶段的Topic生成,可以编写如下Prompt:

1
2
3
4
5
6
7
8
9
10
11
12
TOPIC_PROMPT_TEMPLATE = """
我需要收集一些图片素材,你被要求给出一些存在实体概念的#物体#,你需要遵守如下规则的思考过程:

1. 你给出的每个#物体#都必须是一个#名词#
2. 你必须先选择#主题#,然后从该#主题#中生成对应的#物体#,主题可以是#动物#、#植物#、#景点#、#服饰#、#家具#、#电子产品#、#生活用品#等等,可自由发挥
3. 你被鼓励尽可能生成多样化的#物体#,发挥你的想象力
4. 你可以采用范例中已有的#主题#,也可以自行提出新的#主题#。

以下是一些符合标准的范例,请按照范例的“思考”和“答案”步骤给出一个#主题#,然后给出最少5个#物体#。记住,你选择的#主题#和#物体#必须有视觉概念:

{topic_samples}
""".lstrip()
其中topic_samples可以是:
1
2
3
4
INIT_TOPIC_SAMPLES = """
思考:选择主题#食物#
答案:根据#食物#主题,我想出了#物体#:#饺子#,#意大利面#,#汉堡#,#炸鸡#,#米饭#,#寿司#
""".lstrip()
等等,我在实际使用的时候写了5个。

对于LLM的生成结果,需要执行去重过滤。首先考虑生成的主题是否命中了主题pool中已有的主题,如果命中了,那么直接将生成的物体加入该主题的物体pool。 如果产生了新的主题,那么检查所有的物体pool,如果生成的物体有一个命中了任意pool,那么将所有的物体加入对应的这和物体pool。 在生成最终Prompt时,首先需要从主题pool中采样最多50条样本,然后对每个主题,从对应的物体pool中采样最多10条样本,最终将多个主题样本和对应的物体样本组装成完整的Prompt。

在人工过滤一阶段的生成结果后开始二阶段,Prompt模板可以设计为:

1
2
3
4
5
6
7
8
9
10
11
12
OBJECT_PROMPT_TEMPLATE = """
我需要收集一些图片素材,请帮我想一些用于检索图片的问询,你需要遵循如下规则:

1. 你给出的每个答案必须符合当前主题,当前主题是:{{{topic}}}。
2. 你的用词需要精准,不要口语化。
3. 用中文回答。

记住,当前的主题是{{{topic}}}
以下是一些范例,请严格按照范例格式再给出额外的5个答案,发挥你的想象力:

{{{object_samples}}}
""".lstrip()
在过滤方面,除了一些按照字符的过滤规则,我还加入了按照相似度过滤,使用bge-small-v1.5对所有object结果计算向量,若当前生成结果与向量pool中最相似的内积大于0.85,则直接抛弃当前结果。 在生成时,每个主题从对应的物体pool中选择一些物体作为样本,这里7个样本是从第一阶段人工筛选出的样本库中选择,3个样本是从LLM的历史生成结果中选出。 当某个主题发生以下两种情况中的一种,终止该主题的生成流程。

  1. 当前主题达到最大迭代次数
  2. 当前主题迭代10次后仍未能产生符合要求(即通过了所有过滤规则)的物体样本

最后需要强调的是,LLM生成的物体样本依然存在很多错误,这里我是人工检查删改的。

在经过前两个阶段的LLM生成,最终我得到了20个主题共1488个Query样本。 接下来即从百度搜图爬取对应的图片样本:

  1. 输入Query,获得百度统计的相似图片数量,若当前问询的相似图片数量小于100,则直接抛弃该Query
  2. 选择返回结果的前24张图片,去除所有广告图片,去除宽或高小于112的图片,去除长短边比大于2的图片
  3. 若过滤完成后的图片数量小于12张,直接抛弃该Query
  4. 若当前batch图片中的任意一个url已经被下载过,说明当前Query与历史Query可能导致相同的检索结果风险,直接抛弃该Query
  5. 保存过滤后的图片列表的前12张图片,下载保存为jpg格式,同时保存图片的caption备用
  6. 最终得到16920张图片,每个Query对应12张图片

为什么选择12张?A:我当时想要测试的指标是Precision1, Precisino5, Precision10, Recall15, Recall20。因此我认为一个Query对应12张Ground Truth是合理的,也考虑了冗余。

在典型的ChineseClipVitB16模型中,测试得到的指标分别是:

1
2
3
4
5
6
7
{
"Precision@1": 0.8121,
"Precision@5": 0.7750,
"Precision@10": 0.7124,
"Recall@15": 0.7346,
"Recall@20": 0.8001
}
比较好的是,我可以通过观察一些检索差的结果发现Clip的能力不足的地方,比如“窗花”在Clip的向量中类似于“窗台边的花”,而不是那个剪纸艺术。

总结

这一系列实验是我第一次开始使用LLM帮助构建评测系统,因为我只有一个人负责这件事情,不可能花大量精力去人工打标签。 于是我想要借助LLM的力量,帮我覆盖绝大多数繁杂的打标工作,快速构建一个评测系统。 我将我的思考过程记录在这里,希望也能帮到其他人。