记录复现多模态大模型论文OPERA的一周工作(2)

二. OPERA论文复现

在做好上述的准备工作之后,我们的环境搭建、数据准备和传输已经完成。复现工作就可以正式开始了。在github下载OPERA官方代码文件,按照官方提示搭建好相关的环境。

1. 详细情况

在README.md中官方说明有两种方式可以运行OPERA解码策略。一是将OPERA的代码集成到自己现有的代码中,也就是缝合模块的方式,二是用官方提供的脚本直接进行数据集批量推理。

1.1. 部署推理出现的问题

我们选择后者,直接快速验证OPERA解码策略的有效性。

bash 复制代码
OMP_NUM_THREADS=8 python chair_eval.py \
    --model llava-1.5 \
    --data_path /root/autodl-tmp/project/dataset/MSCOCO/val2014/ \
    --gpu-id 0 \
    --beam 5 \
    --scale_factor 50 \
    --threshold 15 \
    --num_attn_candidates 5 \
    --penalty_weights 1

chair_eval.py代码会随机选取500张数据集中的图片(代码中将种子固定为42,方便复现),进行图片回答的生成。当然,脚本真正跑起来的过程并不是一帆风顺,出现了一些问题如下:

  1. hugging face连接问题

这个问题比较好解决,当时想用本地下载好的文件做替代,却发现怎么也不成功,于是在环境变量上做了点修改,hugging face网络连接问题就被解决了。

bash 复制代码
$ export HF_ENDPOINT="https://hf-mirror.com"

在脚本运行前设置好这个临时环境变量,只在该终端生效!!!

如果运行后问题仍然没有得到解决,可以参考下面这个博文来操作:https://zhuanlan.zhihu.com/p/689290456

里面教你如何修改项目源代码,将下载源从https://huggingface.co 修改为镜像网站https://hf-mirror.com

  1. 维度不匹配问题

脚本运行报错如下:

bash 复制代码
Traceback (most recent call last):
  File "/root/autodl-tmp/project/OPERA/OPERA-main/chair_eval.py", line 173, in <module>
    out = model.generate(
  File "/root/miniconda3/envs/llava1.5/lib/python3.9/site-packages/torch/utils/_contextlib.py", line 115, in decorate_context
    return func(*args, **kwargs)
  File "/root/autodl-tmp/project/OPERA/OPERA-main/minigpt4/models/llava.py", line 212, in generate
    output_ids = self.llama_model.generate(
  File "/root/miniconda3/envs/llava1.5/lib/python3.9/site-packages/torch/utils/_contextlib.py", line 115, in decorate_context
    return func(*args, **kwargs)
  File "/root/autodl-tmp/project/OPERA/OPERA-main/transformers-4.29.2/src/transformers/generation/utils.py", line 1649, in generate
    return self.opera_beam_search(
  File "/root/autodl-tmp/project/OPERA/OPERA-main/transformers-4.29.2/src/transformers/generation/utils.py", line 3353, in opera_beam_search
    attn_previous = torch.cat(
RuntimeError: Sizes of tensors must match except in dimension 2. Expected size 657 but got size 694 for tensor number 1 in the list.

报错信息已经很明显,指出了最深层次的报错代码所在:

File "/root/autodl-tmp/project/OPERA/OPERA-main/transformers-4.29.2/src/transformers/generation/utils.py", line 3353

所以我们需要修改项目源码来修复这个问题,这个报错在官方github库中的issue问题探讨中也被人提到过,但是没有提供解决方案。

在尝试了许多方法之后,我得到了切实可行的方案:

  • 问题分析

在 OPERA 的 beam search 实现中,attn_previous 试图拼接多个 beam 的注意力矩阵,但它们的序列长度不一致。这通常由以下原因导致:

根本原因: LLaVA 1.5 使用动态分辨率或不同数量的图像 token,导致不同样本的序列长度不同。当 beam search 跨样本处理时,序列长度不匹配。

  • 问题定位

错误发生在这一行:

python 复制代码
attn_previous = torch.cat(
    [attn_previous[beam_idx], outputs.attentions[-1].clone().max(1, keepdim=True).values.data], -2)

两个张量在 dim=-2(即 q 维度)上拼接,但它们的 kv 维度(dim=-1)不匹配:

attn_previous[beam_idx] 的 kv 维度:657

outputs.attentions[-1] 的 kv 维度:694

根本原因: LLaVA 1.5 对不同图片会产生不同数量的图像 token(因为使用了动态分辨率 AnyRes),导致 past_key_values 中 KV cache 的长度在不同 beam 间不一致,使得 attn_previous 在更新时 kv 维度对不上。

  • 修复方案

在拼接之前,对 kv 维度(dim=-1)做对齐,取较大的那个,不足的用 0 填充:

python 复制代码
if not "past_key_values" in model_kwargs.keys():
    attn_previous = outputs.attentions[-1].clone()
else:
    assert beam_idx is not None and attn_previous is not None
    attn_previous = torch.cat(
        [attn_previous, torch.zeros_like(attn_previous).sum(-1, keepdim=True)], -1
    )

    a = attn_previous[beam_idx]
    b = outputs.attentions[-1].clone().max(1, keepdim=True).values.data

    # 对齐 kv 维度(dim=-1)
    diff = a.size(-1) - b.size(-1)
    if diff > 0:
        b = torch.nn.functional.pad(b, (0, diff))   # b 补 0
    elif diff < 0:
        a = torch.nn.functional.pad(a, (0, -diff))  # a 补 0

    attn_previous = torch.cat([a, b], dim=-2)

修改后脚本可以正常运行。

1.2 计算CHAIR指标

在OPERA官方代码仓库里明确写着使用的开源CHAIR指标计算代码库https://github.com/Maxlinn/CHAIR-metric-standalone

下载后我们使用其自动计算CHAIR指标的脚本,来计算LLAVA1.5 + OPERA解码结合后生成的文本幻觉率。

图像字幕(Image Captioning)任务中,专门评估物体幻觉问题的CHAIR 指标的两个子维度,分别是物体级幻觉率Cs(CHAIR-s)和样本级幻觉率Ci(CHAIR-i)

  • 物体级幻觉率 Cs
    分子∣{hallucinated objects}∣:幻觉物体的总数量,即生成的描述里提到的、但在对应图片的真实标注中不存在的物体的个数。
    分母∣{all mentioned objects}∣:生成描述里提到的所有物体的总数量,包含真实存在的物体和幻觉物体。
    含义:衡量生成内容中,幻觉物体占所有提到物体的比例,是更精细的、针对物体本身的幻觉严重程度的评估,数值越高,说明生成内容里幻觉的物体占比越高。
  • 样本级幻觉率 Ci
    分子∣{captions w/ hallucinated objects}∣:包含幻觉物体的描述总条数,只要某一条生成的描述里,存在至少 1 个幻觉物体,这条描述就会被计入统计。
    分母∣{all captions}∣:测试集中所有生成的描述的总条数。
    含义:衡量整个测试集中,存在幻觉的描述占所有描述的比例,是宏观的、针对样本的幻觉覆盖度评估,数值越高,说明有越多比例的生成描述本身存在幻觉问题。

运行脚本可能存在的问题:Pattern库安装失败

Pattern库维护比较差,所以在下载时比较容易出现安装失败或者是版本不对的问题。

常见报错如下:

bash 复制代码
Traceback (most recent call last):
  File "/root/autodl-tmp/project/CHAIR-metric-standalone/chair.py", line 18, in <module>
    from pattern.en import singularize
ModuleNotFoundError: No module named 'pattern.en'

解决方案:

  • 卸载原版本

用pip install pattern命令可能下载到其他一些假冒/无关的包如pattern==0.0.1a0。所以需要卸载原下载的版本。

bash 复制代码
pip uninstall pattern -y
  • 安装正确版本
bash 复制代码
# 安装社区维护的 pattern3 分支
pip install git+https://github.com/clips/pattern.git
  • 可能出现的下一步报错
bash 复制代码
Exception: Can not find valid pkg-config name.
      Specify MYSQLCLIENT_CFLAGS and MYSQLCLIENT_LDFLAGS env vars manually
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
ERROR: Failed to build 'mysqlclient' when getting requirements to build wheel

问题根源:安装官方Pattern库时,它强制尝试编译依赖项mysqlclient(用于数据库功能),但你的系统里没有安装MySQL的开发库(pkg-config找不到)。

  • 安装需要的依赖
bash 复制代码
apt update && apt install -y libmariadb-dev pkg-config
  • 重新运行安装命令
bash 复制代码
# 安装社区维护的 pattern3 分支
pip install git+https://github.com/clips/pattern.git

如何利用这个官方库计算CHAIR指标,在其README.md中已经讲得很清楚了,不多赘述。