Advertisement

论文速读|PAR: A New Benchmark Dataset and A Large Language Model Augmented Framework.AAAI25

阅读量:

论文地址:https://arxiv.org/abs/2408.09720
代码地址:https://github.com/Event-AHU/OpenPAR
bib引用:

复制代码
    @misc{jin2024pedestrianattributerecognitionnew,
      title={Pedestrian Attribute Recognition: A New Benchmark Dataset and A Large Language Model Augmented Framework}, 
      author={Jiandong Jin and Xiao Wang and Qian Zhu and Haiyang Wang and Chenglong Li},
      year={2024},
      eprint={2408.09720},
      archivePrefix={arXiv},
      primaryClass={cs.CV},
      url={https://arxiv.org/abs/2408.09720}, 
    }
    
    
      
      
      
      
      
      
      
      
      
    

InShort

围绕行人属性识别(PAR)展开研究,提出新的基准数据集MSP60K和基于大语言模型增强的框架LLM-PAR:

  1. 研究背景与问题 :PAR在计算机视觉和人工智能领域应用广泛,但当前模型受低光照、运动模糊等因素影响,且现有数据集存在性能饱和、忽视跨域影响、未突出挑战因素等问题,阻碍了PAR发展。

  2. 相关工作 :介绍PAR的现有方法,包括先验引导、基于注意力和视觉语言建模等;回顾常用PAR基准数据集,如PETA、WIDER等及其问题;阐述视觉语言模型发展及在视觉任务中的挑战。

  3. MSP60K基准数据集 * 构建准则 :大规模(标注60,122张图像,每张57个属性)、多距离和视角、复杂多样场景、丰富行人身份来源、模拟复杂真实环境。

    • 属性分组与细节 :57个属性分为11组,涵盖性别、年龄、着装等方面。
    • 统计分析 :数据规模大、场景丰富,采用随机和跨域分割策略,并对部分图像进行合成退化处理。具有长尾效应,不同场景属性分布不同。
    • 基准基线 :评估17种代表性PAR模型,包括基于CNN、Transformer等不同类型。
  4. 方法 * LLM-PAR框架概述 :由多标签分类分支、大语言模型分支和模型聚合三部分组成,利用大语言模型探索属性间上下文关系辅助属性识别。

    • 多标签分类分支 :将图像分块投影为视觉令牌,经视觉编码器提取全局特征,MEQ-Former提取特定属性组特征,再用CBAM模块生成属性预测。
    • 大语言模型分支 :构建指令,获取指令嵌入与视觉特征拼接,输入大语言模型生成图像描述。
    • 模型聚合 :定义属性级和实例级分类器,融合两个分支结果提升预测效果。
    • 损失函数 :多标签分类分支用加权交叉熵损失,大语言模型分支用交叉熵损失。
  5. 实验 * 数据集与评估指标 :在PETA、PA100K等多个数据集上对比17种方法,采用平均准确率(mA)等5种指标评估性能。

    • 实现细节 :训练时用模板扩展属性微调大语言模型,采用掩蔽策略防止信息泄露;推理时根据指令和图像特征生成结果。
    • 对比实验结果 :LLM-PAR在多个数据集上取得最优或接近最优结果,验证了有效性。
    • 组件分析与消融实验 :分析各组件贡献,研究不同模块设置对性能影响,如真值掩蔽策略、AGFA层数等。
    • 可视化 :展示LLM-PAR识别结果和特征图,其能准确识别属性,特征图可聚焦行人特定区域。
  6. 结论与展望 :MSP60K数据集和LLM-PAR框架有效,未来计划扩大数据集规模并设计轻量级模型平衡精度和性能。

摘要

Pedestrian Attribute Recognition (PAR) is one of the indispensable tasks in human-centered research. However, existing datasets neglect different domains (e.g., environments, times, populations, and data sources), only conducting simple random splits, and the performance of these datasets has already approached saturation. In the past five years, no large-scale dataset has been opened to the public. To address this issue, this paper proposes a new large-scale, cross-domain pedestrian attribute recognition dataset to fill the data gap, termed MSP60K. It consists of 60,122 images and 57 attribute annotations across eight scenarios. Synthetic degradation is also conducted to further narrow the gap between the dataset and real-world challenging scenarios. To establish a more rigorous benchmark, we evaluate 17 representative PAR models under both random and cross-domain split protocols on our dataset. Additionally, we propose an innovative Large Language Model (LLM) augmented PAR framework, named LLM-PAR. This framework processes pedestrian images through a Vision Transformer (ViT) backbone to extract features and introduces a multi-embedding query Transformer to learn partial-aware features for attribute classification. Significantly, we enhance this framework with LLM for ensemble learning and visual feature augmentation. Comprehensive experiments across multiple PAR benchmark datasets have thoroughly validated the efficacy of our proposed framework. The dataset and source code accompanying this paper will be made publicly available at https://github.com/ Event-AHU/OpenPAR.
行人属性识别 (PAR) 是以人为中心的研究中不可或缺的任务之一。然而,现有的数据集忽略了不同的领域(例如,环境、时间、人口和数据源),只进行简单的随机拆分,这些数据集的性能已经接近饱和 。近 5 年来,没有大规模的数据集向公众开放。为了解决这个问题,本文提出了一种新的大规模、跨域行人属性识别数据集来填补数据空白,称为 MSP60K。它由 8 个场景中的 60,122 张图像和 57 个属性注释组成。还进行了合成降级,以进一步缩小数据集与现实世界具有挑战性的场景之间的差距 。为了建立更严格的基准,我们在数据集上评估了随机和跨域拆分协议下的 17 个代表性 PAR 模型。此外,我们还提出了一种创新的大型语言模型 (LLM) 增强 PAR 框架,名为 LLM-PAR。该框架通过 Vision Transformer (ViT) 主干网处理行人图像以提取特征,并引入多嵌入查询 Transformer 来学习用于属性分类的部分感知特征 。值得注意的是,我们使用 LLM 增强 了这个框架,用于集成学习和视觉特征增强。跨多个 PAR 基准数据集的全面实验彻底验证了我们提出的框架的有效性。本文随附的数据集和源代码将在 https://github.com/ Event-AHU/OpenPAR 上公开提供。

Introduction

研究现状

考虑到这些问题,我们仔细审查了 PAR 的现有工作和数据集,发现 PAR 领域的发展已开始进入瓶颈期。基准数据集作为推动 PAR 发展的有效推动力,发挥着至关重要的作用。但是,我们认为 PAR 社区需要解决基准数据集上的几个核心问题,如下所示:1). 现有行人属性识别数据集的性能接近饱和,新算法的性能提升呈减弱趋势。然而,在过去五年中,只有一个小规模的 PAR 相关数据集发布,因此迫切需要新的大规模数据集来支持新的研究工作。2). 现有的 PAR 数据集使用随机分区进行模型训练和测试,可以衡量 PAR 模型的整体识别能力。然而,这种分区机制忽略了跨域(例如,不同的环境、时间、群体和数据源)对 PAR 模型的影响。3). 现有的 PAR 数据集没有突出反映挑战因素,因此,这可能会导致在实际应用中忽视数据损坏的影响,从而在实际环境中引入安全隐患。总之,很明显,PAR 社区迫切需要一个新的大规模数据集来弥合现有的数据差距。
在这里插入图片描述

图 1.(a, b) 的现有 PAR 数据集与我们新提出的 MSP60K 数据集之间的比较。说明了我们在数据集中采用的合成降解挑战,以模拟复杂和动态的真实世界环境。

本文工作

在本文中,我们提出了一个新的基准数据集用于行人属性识别,称为 MSP60K,如图 1 所示。它包含 60,122 张图像和 5,000 多个个人 ID,这些图像是使用智能监控系统和移动电话收集的。

为了使我们的数据集更好地反映真实场景中的挑战,除了注释尽可能多的复杂图像外,我们还使用额外的破坏性操作来处理这些图像,包括模糊、遮挡、照明、添加噪点、jpeg 压缩等。由于这些图像属于不同的领域和场景,例如超市、厨房、建筑工地、滑雪场和各种户外场景,我们根据随机切分和跨域两种协议对这些图像进行切分。因此,新提出的基准数据集可以更好地验证 PAR 模型在真实场景中的性能,尤其是在跨域设置下。为了构建更全面的行人属性识别基准数据集,我们还训练并报告了 17 种具有代表性和最近发布的 PAR 算法。这些基准比较方法可以更好地促进未来 PAR 模型的后续验证和实验。

基于新提出的 MSP60K PAR 数据集,还提出了一种新的大型语言模型 (LLM) 增强行人属性识别框架,称为 LLM-PAR。基于广泛使用的多标签分类框架,我们重新思考行人图像感知与大语言模型之间的关系,作为这项工作的关键洞察。众所周知,大型语言模型在文本生成、理解和推理方面具有强大的能力。因此,我们引入了一个大型语言模型,该模型基于多标签分类框架生成图像属性的文本描述作为辅助任务。
这个 LLM 分支有双重目的:
一方面,它可以通过生成准确的文本描述来辅助视觉特征的学习 ,从而实现高性能的属性识别;
另一方面,LLM 可以促进视觉特征和提示之间的有效交互 。输出文本标记也可以与上述多标签分类框架集成,用于集成学习

As shown in Fig. 5, our proposed LLM-PAR can be divided into two main modules, i.e., the standard multi-label classification branch and the large language model augmentation branch. Specifically, we first partition the given pedestrian image into patches and project them into visual embeddings. Then, a visual encoder with LoRA [8] is utilized for global feature learning and a Multi-Embedding Query TransFormer (MEQ-Former) is proposed for partaware feature learning. After that, we adopt CBAM [40] attention modules to merge the output tokens and feed them into MLP (Multi-Layer Perceptron) layers for attribute classification. More importantly, we concatenate the part-aware visual tokens with the instruction prompt and feed them into the large language model for pedestrian attribute description. The text tokens are also fed into an attribute recognition head and ensembles with classification logits.
【LLM-PAR】:多标签分类+LLM数据增强

  • 给定的行人图像划分为块,并将它们投影到视觉嵌入中。
  • 利用带有 LoRA [8] 的视觉编码器进行全局特征学习,并提出了一种多嵌入查询 TransFormer (MEQ-Former) 用于部件感知特征学习 。【BLIPv2用的也是QFormer】
  • 采用 CBAM [40] 注意力模块来合并输出标记,并将它们馈送到 MLP(多层感知器)层进行属性分类
  • 零件感知的视觉标记与指令提示连接起来,并将它们输入到行人属性描述的大型语言模型中文本标记也被馈送到属性识别头和带有分类 logit 的集合中

在新提出的 MSP60K 数据集和其他广泛使用的 PAR 基准数据集上进行的广泛实验都验证了我们提出的 LLM-PAR 的有效性。

2. 相关工作

2.1. 行人属性识别

行人属性识别 [36] ^{1} 旨在根据一组预定义的属性对行人图像进行分类。目前的方法大致可分为先验引导、基于注意力和视觉语言建模方法。鉴于行人属性与特定身体组成部分之间的强相关性。各种方法,如 HPNet [26] 和 DAHAR [26, 43],都侧重于通过注意力机制定位属性相关区域。姿势和视点的变化通常会对行人属性识别构成挑战。为了应对这些挑战,一些研究人员 [10, 30] 结合了先前的增强技术或引入补充神经网络来有效地模拟这些关系。此外,行人属性密切相关。因此,JLAC [32] 和 PromptPAR [37] 联合建模属性上下文和图像属性关系。虽然当前方法认识到在 PAR 任务中探索上下文关系的重要性,但利用 Transformers 等模型来捕获数据集中的属性关系通常难以表示涉及稀有属性的连接。

2.2. PAR的基准数据集

最常用的 PAR 数据集是 PETA [3]、WIDER [22]、RAP [17, 18] 和 PA100K [26]。
为了增强远距离识别行人属性的能力,邓等[3]引入了一个名为PETA的新行人属性数据集,该数据集由10个小规模行人再识别数据集编译而成,标记了60多个属性。
与 PETA 的身份级注释不同,RAP 数据集 捕获了一个室内购物中心,并对行人图像使用了实例级注释。PETA 和 RAPv1 数据集都存在随机分割的问题,即出现在训练集中的个体也出现在测试集中,导致信息泄露
为了解决这个问题,Liu 等 [26] 提出了监控场景中最大的行人属性识别数据集 PA100K,其中包含 100,000 张行人图像和 26 个属性。该数据集通过确保训练集和测试集中的行人之间没有重叠来缓解信息泄露问题。但是,这些数据集仅包含

2.3. 视觉语言模型

随着自然语言处理领域的快速发展,出现了许多大型语言模型(LLM),如Flan-T5 [27]和LLaMA [34]。尽管在视觉领域引入了像 SAM [15] 这样著名的基础模型,但视觉任务的复杂性阻碍了广义多域视觉模型的发展。一些研究人员已经开始将 LLM 视为世界模型,利用它们作为认知核心来增强各种多模态任务。认识到从头开始训练大型多模态模型的高成本,BLIP 系列 [19, 20]、MiniGPT-4 [50] 将现有的预训练视觉模型和大型语言模型连接起来。尽管这些模型在视觉理解和文本生成领域有了显着改进,但仍然存在许多挑战,例如低分辨率图像识别、细粒度图像描述和 LLM 的幻觉

补充1:LLM幻觉类型、原因、解决方案

幻觉可以分为几种类型:

  • 逻辑谬误:模型在进行推理时出现了错误,提供错误的答案。

  • 捏造事实:模型自信地断言不存在的事实,而不是回答“我不知道”。

    • 例如:谷歌的 AI 聊天机器人 Bard 在第一次公开演示中犯了一个事实错误。
  • 数据驱动的偏见:由于某些数据的普遍存在,模型的输出可能会偏向某些方向,导致错误的结果。

    • 例如:自然语言处理模型中发现的政治偏见。

大语言模型中的幻觉源于数据压缩(data compression)和不一致性(inconsistency) 。 由于许多数据集可能已经过时或不可靠,因此质量保证具有挑战性。为了减轻幻觉,可以采取以下方法

  • 调整temperature参数以限制模型的创造力。 (temperature参数控制生成语言模型中生成文本的随机性和创造性,调整模型的softmax输出层中预测词的概率;其值越大,则预测词的概率的方差减小,即很多词被选择的可能性增大,利于文本多样化)
  • 注意提示工程。 要求模型逐步思考,并在回复中提供事实性信息和参考来源
  • 整合外部知识源来改进答案验证(answer verification)。

更多参考:
LLM 幻觉:现象剖析、影响与应对策略

OpenAI Lilian Weng万字长文解读LLM幻觉:从理解到克服

表 1.MSP60K 与现有 PAR 基准测试数据集之间的比较。

Dataset Year Attributes Images Scene Split
PETA [3] 2014 61 19,000 X
WIDER [22] 2016 14 57,524
RAPv1 [17] 2016 69 41,585
PA100K [26] 2017 26 100,000
RAPv2[18] 2019 76 84,928 xxxx>
Ours 2024 57 60.015

表 2.MSP60K 数据集中定义的属性组和详细信息。

Attribute Group Details
Gender Female
Age Child, Adult, Elderly
Body Size Fat, Normal, Thin
Viewpoint Front, Back, Side
Head Bald, Long Hair, Black Hair, Hat Glasses, Mask, Helmet, Scarf, Gloves
Upper Body Short Sleeves, Long Sleeves, Shirt, Jacket, Suit, Vest Cotton Coat, Coat, Graduation Gown, Chef Uniform
Lower Body Trousers, Shorts, Jeans, Long Skirt, Short Skirt, Dress
Shoes Leather Shoes, Casual Shoes, Boots, Sandals, Other Shoes
Bag Backpack, Shoulder Bag, Hand Bag Plastic Bag, Paper Bag, Suitcase, Others
Activity Calling. Smoking. Hands Back, Arms Crossed
Posture Walking, Running, Standing, Bicycle, Scooter, Skatcboard

3. MSP60K基准数据集【 60,122 张行人图像,每张图像有 57 个属性】

3.1. 协议

为了提供一个强大的平台来训练和评估真实条件下的行人属性识别 (PAR),我们在构建 MSP60K 基准数据集时遵循以下准则:
1.大规模:我们注释了 60,122 张行人图像,每张图像有 57 个属性,全面分析了各种条件下的行人特征。
2. 多个距离和视点 :使用各种相机和手持设备从不同的角度和距离捕获图像,覆盖前视图、后视图和侧视图。我们数据集中行人图像的分辨率从 30×80 到 2005×3008。
3. 复杂多样的场景:与具有统一背景的现有数据集不同,我们的数据集包括来自八种不同环境的图像,具有不同的背景和属性分布,有助于评估不同设置下的识别方法。
4. 丰富的行人身份来源:我们收集来自不同场景、国籍和季节变化的行人数据,增强具有不同风格和特征的数据集。
5. 模拟复杂的真实世界环境:该数据集包括照明、运动模糊、遮挡和恶劣天气条件的变化,模拟行人属性识别的真实挑战。

3.2. 属性组和详细信息

为了有效地评估现有 PAR 方法在复杂场景下的性能,我们数据集中的每张图像都标有 57 个属性,这些属性分为11 组:性别、年龄、体型、视点、头部、上半身、下半身、鞋子、包、身体运动和运动信息。表 2 中提供了已定义属性的完整列表。

3.3. 统计分析

如表 1 所示,MSP60K 提供 8 个不同的场景和 57 个属性,提供比 PA100K(26 个属性)和 WIDER(14 个属性)等数据集更丰富的注释。该数据集包含 60,122 张图像,超过 5,000 张独特的图像分度。它包括各种环境,例如市场、学校、厨房、滑雪胜地、各种户外和建筑工地,提供比其他数据集更广泛的范围。
在我们的基准测试数据集中,我们使用随机和跨域分区策略来拆分数据:

  • 随机分区:30,298 张图像用于训练,6,002 张图像用于验证,23,822 张用于测试,确保场景的随机分布,就像其他 PAR 基准测试数据集一样。
  • 跨域分区:为了验证 PAR 模型的域泛化和零镜头性能,我们根据场景划分数据集,即使用五个场景(建筑工地、市场、厨房、学校、滑雪场)34,128 张图像进行训练,而三个场景(Outdoors1、Outdoors2、Outdoors3)24,994 张图像用于测试。

为了评估模型的稳健性,我们通过引入照明变化、随机遮挡、模糊和噪声等变化,有意降低每个子集中 1/3 的图像质量。MSP60K 具有广泛的尺寸和多样化的条件,为评估 PAR 方法提供了一个全面的平台。
数据集还表现出长尾效应,类似于现有的 PAR 数据集 ,如图 2 (a) 所示,并反映了现实世界的属性分布。图 2 (b) 显示了行人属性的共现矩阵,其中每个单元格表示两个属性一起出现的频率。较暗的区域表示共现频率较高。例如,棉衬外套和长袖具有很强的关联,而秃头和长发/黑发等属性很少同时出现。图 2 (c) 显示了不同场景(如建筑工地、市场、厨房等)中的属性分布,属性在同心圆图中用不同的颜色表示。例如,School 场景具有更多的 Child 属性,而 Outdoors3 场景显示 Short Sleeves 和 Sandals 属性的普遍性更高。
在这里插入图片描述

图 2.(a) 属性分布:显示数据集中各个属性的普遍性的条形图;(b) 属性共现矩阵:显示属性对共现频率的对数热图;(c) 不同场景中的属性分布:圆形图表,说明了 8 个不同场景中的属性分布。

3.4. Benchmark Baselines

我们的评估涵盖多种方法(共 17 种),包括:
1)基于 CNN:DeepMAR [16]、RethinkPAR [11]、SSCNet [10]、SSPNet [31]。
2)基于Transformer :DFDT [46]、PARFormer [5]。
3) 基于Mamba :MambaPAR [39]、MaHDFT [38]。
4)PAR Human centric 的预训练模型:PLIP [51]、HAP [45]。
5)PAR 的视觉语言模型:VTB [2]、Label2Label [21]、PromptPAR [37]、SequencePAR [13]。
在这里插入图片描述

图 5.我们提出的 LLM-PAR 框架的插图说明了我们如何使用多模态大型语言模型 (MLLM) 进行深度语义推理,结合图像和描述性文本以提供更易解释的视觉理解。通过这个框架,我们可以识别行人属性并生成自然语言描述,从而提供更直观的解释。我们的框架由三个部分组成:视觉特征提取语言描述生成语言增强分类

在这里插入图片描述

图 6.(a) 详细架构 MEQ-Former (b)CBAM 模块

4. 方法

介绍提出的行人属性识别框架 LLM-PAR。三个主要部分:视觉特征提取图像描述生成分类 模块。

4.1. Overview

本文介绍了一种使用多模态大语言模型 (MLLM) 改进行人属性识别 (LLM-PAR) 的方法,该方法详细描述了图像。如图 5 所示,我们利用 MLLM 来探索属性之间的上下文关系,生成有助于属性识别的描述。该方法由三个主要模块组成:1) 多标签分类分支,2) 大型语言模型分支,以及 3) 模型聚合
具体来说,我们首先使用视觉编码器提取行人的视觉特征。
然后,设计了 MEQ-Former 来提取不同属性组的特定特征并转化为 MLLM 的潜在空间
属性组特征通过投影层集成到指令嵌入中,特征馈送到大型语言模型中以生成行人描述
最后,将每个组的视觉特征的分类结果与语言分支的结果聚合,以生成最终的分类结果

4.2. 多标签分类分支

给定一个输入行人图像 I \in \mathbb{R}^{H ×W ×3} ,如图 5,我们首先将其划分为补丁并将它们投影到视觉标记中。视觉标记是使用位置嵌入 (P.E.) 添加的,该嵌入对空间信息进行编码。输出将被馈送到视觉编码器(默认采用 EVA-ViT-G [6])中,以提取全局视觉表示 F_{V}
在实现中,冻结了预训练的视觉编码器的参数,并采用 LoRA [8] 来实现高效的调整。
Multi Embedding Query Transformer (MEQ-Former) 从主要视觉特征派生的不同属性组中提取特定特征
在这里,属性组是通过将属性分类为组 A^{j} | j={0,1, ..., K} ,根据其类型(如头部、上半身服装、动作)来获取的,其中 K 表示属性组的数量。

如图 6 所示,我们创建了 K 组部分查询 (PartQ) Q_{p} \in \mathbb{R}^{K ×L ×D}
其中 L 和 D 分别是查询的数量和维度。
这些嵌入被馈送到属性组特征聚合 (AGFA) 模块中,以提取不同属性组的特定特征 F_{g}= {F_{g}^{1}, F_{g}^{2}, ..., F_{g}^{K}}
AGFA 模块由堆叠的前馈网络 (FFN) 和交叉注意力 (CrossAttn) 层组成。
这个过程可以表述为:
F_{g}=F F N\left(Cross Attn\left(Q=Q_{p}, K=F_{V}, V=F_{V}\right)\right) (1)
F_{g} 被馈送到 Q-Former E_{Q} ,作为视觉和语言模态之间的桥梁,生成与文本相关的信息 F_{q}^{j} 。Q-Former 由堆叠的自注意力层和交叉注意力层组成,通过交叉注意力机制聚合图像信息。
然后,我们引入了卷积块注意力模块(CBAM)[40],以从F_{g}中捕获每个属性的细粒度特征,以产生特定于属性的预测。

补充2:CBAM

具体代码:https://github.com/Event-AHU/OpenPAR/blob/main/MSP60K_Benchmark_Dataset/LLM-PAR/models/cbam.py
ChannelAttention类实现通道注意力机制。
初始化方法定义平均池化层、最大池化层和一系列卷积层。
forward方法中,通过平均池化和最大池化得到两个特征图,分别经过一系列卷积操作后相加,再通过 Sigmoid 函数得到通道注意力权重。

SpatialAttention类实现空间注意力机制。
初始化方法中定义一个卷积层,forward方法中,先对输入特征在通道维度上求平均和最大值,然后将它们拼接起来,经过卷积操作后通过 Sigmoid 函数得到空间注意力权重。

CBAMforward方法中,先将输入特征的维度进行调整,然后与通道注意力权重相乘,再调整维度后与空间注意力权重相乘,最后进行全局平均池化和线性变换得到输出。

复制代码
    class CBAM(nn.Module):
    def __init__(self, in_channels, output_dim=1, reduction=16, kernel_size=7, hidden_dims=None):
        super(CBAM, self).__init__()
        self.channel_attention = ChannelAttention(in_channels, reduction=reduction)
        self.spatial_attention = SpatialAttention(kernel_size=kernel_size)
        self.relu = nn.ReLU()
        self.fc = nn.Linear(in_channels, output_dim)
    
        # 初始化线性层参数
        for m in self.fc.modules():
            if isinstance(m, nn.Linear):
                nn.init.kaiming_normal_(m.weight)
                if m.bias is not None:
                    nn.init.zeros_(m.bias)
    
    def forward(self, x):
        x = x.permute(0, 2, 1)  # [B, L, D] -> [B, D, L]
        x_out = x * self.channel_attention(x)
        x_out = x_out.permute(0, 2, 1)  # [B, D, L] -> [B, L, D]
        x_out = x_out * self.spatial_attention(x_out.permute(0, 2, 1)).permute(0, 2, 1)  # [B, L, D]
        x_out = torch.mean(x_out, dim=1)  # 对 L 维度进行全局平均池化,得到 [B, D]
        x_out = self.fc(self.relu(x_out))
        return x_out
    ...
    class ChannelAttention(nn.Module):
    def __init__(self, in_channels, reduction=16):
        super(ChannelAttention, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool1d(1)
        self.max_pool = nn.AdaptiveMaxPool1d(1)
        self.fc = nn.Sequential(
            nn.Conv1d(in_channels, in_channels // reduction, 1, bias=False),
            nn.ReLU(),
            nn.Conv1d(in_channels // reduction, in_channels, 1, bias=False)
        )
        self.sigmoid = nn.Sigmoid()
    
        # 初始化卷积和线性层参数
        for m in self.modules():
            if isinstance(m, nn.Conv1d) or isinstance(m, nn.Linear):
                nn.init.kaiming_normal_(m.weight)
                if m.bias is not None:
                    nn.init.zeros_(m.bias)
    
    def forward(self, x):
        avg_out = self.fc(self.avg_pool(x))
        max_out = self.fc(self.max_pool(x))
        out = avg_out + max_out
        return self.sigmoid(out)
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    

其中ChannelAttention类的操作与Squeeze-and-Excitation(SE)模块类似:

结构层面

  • 全局池化 :两者都首先使用了全局池化操作。在ChannelAttention类中,使用了平均池化self.avg_pool和最大池化self.max_pool对输入特征图在空间维度上进行压缩,得到通道维度上的全局信息。SE模块 通常使用全局平均池化,将每个通道的特征图压缩为一个数值 ,来获取通道的全局统计信息。
  • 全连接层/卷积层操作 :在全局池化之后,两者都通过类似全连接层的操作来对通道间的关系进行建模。ChannelAttention类中使用了由卷积层构成的self.fc模块,包含一个降维卷积操作将通道数降低为原来的1/reduction,再通过一个升维卷积操作恢复到原始通道数。SE模块 则是使用全连接层来实现通道间的挤压和激励操作 ,同样是先进行降维,再升维到原始通道数,以学习通道之间的依赖关系。

功能层面

  • 通道权重计算 :它们的核心功能都是计算通道注意力权重,以衡量每个通道的重要性。ChannelAttention类通过将平均池化和最大池化后的结果分别经过self.fc模块后相加,再通过sigmoid函数得到通道注意力权重,用于对原始输入特征图的通道进行加权。SE模块 通过全局平均池化得到通道的全局特征,经过全连接层的变换后通过sigmoid函数生成通道权重,对通道进行自适应的缩放,强调重要通道,抑制不重要的通道

差异:ChannelAttention同时使用了平均池化和最大池化两种方式来捕捉通道信息,而SE模块通常只使用全局平均池化。

4.3. 大型语言模型分支

虽然这种多标签分类框架可以达到不错的准确率,但它仍然没有考虑大型语言模型的逻辑推理,这在图像文本领域是显而易见的。因此,本文尝试使用 LLM 作为辅助分支来增强行人属性识别。如图 5 所示,我们首先根据每个属性组 A^{j} 构建指令,即 Human:分析人物的照片,并将其分类为属性。

复制代码
     <Img><ImageHere_Head></Img>
     What are wearing on their head?
    What are wearing on top? ... <Img><ImageHere Topwear></Img>
    Assistant:
    
    
      
      
      
      
    

然后,我们采用 Tokenizer [47] 来获取指令 嵌入 T_{E}={T_{E}^{1}, T_{E}^{2}, ..., T_{E}^{k+1}} 并连接它们具有视觉特征 F_{q} 的人类图像作为指令特征 F_{I}
请注意,在训练阶段,我们嵌入了 ground truth 并将其与 F_{I} 连接起来,作为 LLM 的初始输入。
Vicuna-7B [47] 和 OPT-6.7B [25] 被用作 LLM,在我们的实验中也使用 LoRA 进行了调谐。
最后,我们从 MLLM 中获取最后一个隐藏状态,并通过语言模型头获取相应的图像描述

4.4. Model Aggregation for PAR

在配备了 LLM 之后,我们的算法框架可以同时输出行人属性结果和完整的文本段落来描述给定行人的属性 。为了利用这两个分支的优势,我们设计了一个算法集成模块来实现增强的预测结果
如图 5 所示,我们定义了两个用于属性识别的视觉分类器,即属性级分类器实例级分类器 。还使用大型语言模型分支中的标记获取用于识别的分类器

在实施中,利用以下三种策略将这三种结果融合为结果。具体来说,
1).特定于属性的聚合 (ASA) :我们根据从训练子集中学到的权重,自适应地对每个分类器的属性预测进行加权和求和。
2). 均值池化 :我们直接将这三个分支的结果的平均值作为最终的模型输出。
3). Max Pooling :我们从三个预测分支中取 logits 的最大值作为最终预测结果。
请注意,如果未另行指定,我们将采用均值池化策略作为实验中的默认设置

4.5. 损失函数

在训练阶段,我们采用广泛使用的加权交叉熵损失 (WCE Loss) L_{wce }(\cdot) [16] 进行属性预测分支,即
\mathcal{L}_{M L C}=\mathcal{L}_{w c e}\left(\hat{y}, P_{a t t r}\right)+\mathcal{L}_{w c e}\left(\hat{y}, P_{in }\right)
我们还采用交叉熵损失 L_{ce }(\cdot) 在 LLM 分支中生成文本描述。
\mathcal{L}_{L L M}=\mathcal{L}_{w c e}\left(\hat{y}, P_{l l m}\right)+\mathcal{L}_{c e}\left(\hat{y}_{cap }, P_{c a p}\right) (3)
其中 \hat{y}\hat{y}_{cap } 分别表示实际地表标签和相应的行人属性描述。这P_{cap } 是大语言模型头生成的 logits。

更详细地说,L_{ce }(\cdot)L_{wce }(\cdot) 可以表述为:
\mathcal{L}_{ce }(\cdot)=-\frac{1}{M} \sum_{i=1}^{M} CE\left(y_{i}, p_{i}\right)
\mathcal{L}_{w c e}=-\frac{1}{M} \sum_{i=1}^{M} w_{i} CE\left(y_{i}, p_{i}\right)
其中 M 是属性数,w_{i} 用于调整不平衡类别的贡献,与类别正样本的数量成反比。

CE 可以表示为:
CE\left(y_{i}, p_{i}\right)=y_{i} log \left(p_{i}\right)+\left(1-y_{i}\right) log \left(1-p_{i}\right)

补充3:加权交叉熵损失

复制代码
    class CEL_Sigmoid(nn.Module):
    def __init__(self, sample_weight=None, size_average=True, attr_idx=None):
        super(CEL_Sigmoid, self).__init__()
    
        self.sample_weight = sample_weight
        self.size_average = size_average
        self.attr_idx = attr_idx
    
    def forward(self, logits, targets):
        batch_size = logits.shape[0]
    
        loss = F.binary_cross_entropy_with_logits(logits, targets, reduction='none')
    
        targets_mask = torch.where(targets.detach().cpu() > 0.5, torch.ones(1), torch.zeros(1))
        if self.sample_weight is not None:
            if self.attr_idx is not None and targets_mask.shape[1] != self.sample_weight.shape[0]:
                weight = ratio2weight(targets_mask[:, self.attr_idx], self.sample_weight)
                loss = loss[:, self.attr_idx]
            else:
                weight = ratio2weight(targets_mask, self.sample_weight)
            # import pdb;pdb.set_trace()
            loss = (loss * weight.cuda())
    
        loss = loss.sum() / batch_size if self.size_average else loss.sum()
    
        return loss
    
    def ratio2weight(targets, ratio):
    ratio = torch.from_numpy(ratio).type_as(targets)
    pos_weights = targets * (1 - ratio)
    neg_weights = (1 - targets) * ratio
    weights = torch.exp(neg_weights + pos_weights)
    
    # for RAP dataloader, targets element may be 2, with or without smooth, some element must great than 1
    weights[targets > 1] = 0.0
    
    return weights
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    

5. 实验

5.2. 实验细节【EVA-ViT-G;QFormer;Vicuna-7B】

  • 训练阶段,使用 ground truth 将属性扩展为模板的适当句子,创建 <instruction, answer> 集来微调 LLM

    • 利用 ground-truth sentences mask 策略来防止训练阶段的信息泄露,有助于有效地学习 LLM 分类头。
  • 推理:用 image 特征从指令中自回归生成句子,使用最后一步的 hidden state 来预测语言分支的结果。

我们使用 EVA-ViT-G [6] 作为视觉主干,它的最后三层用于初始化 AGFA 模块。Q-Former 采用 BERT [14],并添加了几个交叉注意力层以与视觉特征交互。我们默认使用 Vicuna-7B [47] 作为大型语言模型。所有 backbone 都根据 MiniGPT-4 [50] 设置和权重进行初始化。我们采用 LoRA [8] 来微调视觉主干和 LLM 的最后 3 层。LoRA 仅注入注意力层中 Q 和 V 的投影中,低秩维度 r 设置为 32。我们train使用 AdamW 优化器的 60 个epoch的模型,学习率为 0.00002,权重衰减为 0.0001。训练在具有 NVIDIA A800SXM4-80GB 的服务器上进行,批处理大小为 4 。更多详细信息可以在我们的源代码中找到。

Mini - GPT4 :

  • 零样本推理实验,利用它生成所有数据集描述,再通过 BERT 提取文本特征,训练全连接层进行分类,以测试模型在零样本情况下的性能。
  • 设置和权重初始化:视觉主干 EVA - ViT - G、大语言模型 Vicuna - 7B 等)提供初始参数

Vicuna - 7B : LLM - PAR 框架的LLM分支

6. 结论

本文通过引入 MSP60K 解决了现有行人属性识别 (PAR) 数据集的局限性,MSP60K 是一种新的大规模跨域数据集,在 8 个场景中包含 60,122 张图像和 57 个属性注释。通过整合合成降解,我们进一步弥合了数据集与现实世界具有挑战性的条件之间的差距。
我们在随机和跨域拆分协议下对 17 个具有代表性的 PAR 模型进行了综合评估,建立了更严格的基准。
此外,我们提出了 LLM-PAR 框架,它利用预先训练的视觉 Transformer 主干,一个用于部分感知特征学习的多嵌入查询 Transformer,并通过大型语言模型进行集成学习和视觉特征增强。
多个 PAR 基准数据集的实验结果证明了我们提出的框架的有效性。
MSP60K 数据集和源代码将在验收后向公众发布,为human centric 的研究和 PAR 技术的未来进步做出贡献。

补充4:LLM-PAR前向传播过程

具体代码:https://github.com/Event-AHU/OpenPAR/blob/main/MSP60K_Benchmark_Dataset/LLM-PAR/models/attr2vec_block.py

复制代码
    #for training
    def forward(self, im, simple_answer, random_sentences, gt_label=None):
        b_s=im.shape[0]
        # 获取im embed & ims_mask  from eva vit-g&Qformer
        vit_img_embeds, inputs_llama, atts_img = self.encode_img(im)
        #随机选取指令
        instruction = self.custom_insturction
        #将im embed 填入指令 更新后的im embed & imgae mask after llm_token_embedding
        img_embeds, atts_img = self.prompt_wrap(inputs_llama, atts_img, instruction)
        #处理真值
        self.llama_tokenizer.padding_side = "right"
        # text = [t + self.end_sym for t in answer]
        text = [t + self.end_sym for t in simple_answer]
        #真值token
        to_regress_tokens = self.llama_tokenizer(
            text,
            return_tensors="pt",
            padding="longest",
            truncation=True,
            max_length=self.max_txt_len,
            add_special_tokens=False
        ).to(im.device)
        
        random_maxlength=to_regress_tokens.input_ids.size(1)
        random_text = [t + self.end_sym for t in random_sentences]
        random_tokens = self.llama_tokenizer(
            random_text,
            return_tensors="pt",
            padding="max_length",
            truncation=True,
            max_length=random_maxlength,
            add_special_tokens=False
        ).to(im.device)     
        to_regress_embeds = self.embed_tokens(random_tokens.input_ids)
        
        batch_size = img_embeds.shape[0]
        bos = torch.ones([batch_size, 1],
                         dtype=to_regress_tokens.input_ids.dtype,
                         device=to_regress_tokens.input_ids.device) * self.llama_tokenizer.bos_token_id
        bos_embeds = self.embed_tokens(bos)
        atts_bos = atts_img[:, :1]
        IB,IL,ID=img_embeds.size()
        LB,LL,LD = to_regress_embeds.size()
        #回答这里的问题
        inputs_embeds, attention_mask, input_lens = \
            self.concat_emb_input_output(img_embeds, atts_img, to_regress_embeds, to_regress_tokens.attention_mask)#这里应该使用to_regress_token还是random_tokens的attention_mask
    
        inputs_embeds = torch.cat([bos_embeds, inputs_embeds], dim=1)
        attention_mask = torch.cat([atts_bos, attention_mask], dim=1)
        
        #填充真值
        part_targets = to_regress_tokens.input_ids.masked_fill(
            to_regress_tokens.input_ids == self.llama_tokenizer.pad_token_id, -100
        )
        labels = (
            torch.ones([inputs_embeds.shape[0], inputs_embeds.shape[1]],
                       dtype=torch.long).to(im.device).fill_(-100)
        )
        #input_lens : instruction长度
        for i, target in enumerate(part_targets):
            labels[i, input_lens[i] + 1:input_lens[i] + len(target) + 1] = target  # plus 1 for bos
        
        with self.maybe_autocast():
            outputs = self.llama_model(
                inputs_embeds=inputs_embeds,
                attention_mask=attention_mask,
                return_dict=True,
                output_hidden_states=True,
                labels=labels,
            )
        
        #添加BLEU CIDEr指标
        hidden_states = outputs.hidden_states[-1]
        hidden_states = self.llm_relu(self.llm_proj(hidden_states[:,-LL:]))
        llm_logits = torch.cat([self.cbam_llm[i](hidden_states) for i in range(self.attr_num)], dim=1)
        
        B,K,L,D = vit_img_embeds.size()
        
        logits_class = torch.cat([self.cbam_class[j + sum(self.group_details[:i])](vit_img_embeds[:,i]) for i in range(self.attr_group_num) for j in range(0, self.group_details[i])], dim=1)
        
        instance_vit_embeds = self.instance_relu(self.instance_proj(vit_img_embeds))
        logits_instance = torch.cat([self.cbam_instance[i](instance_vit_embeds[:,i]) for i in range(self.attr_group_num)], dim=1)
        return outputs.loss, self.bn_class(logits_class), self.bn_instance(logits_instance),  self.bn_llm(llm_logits)
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
  • 获取图像嵌入和图像掩码,随机选取指令并将图像嵌入填入指令更新后得到新的图像嵌入和掩码。
    • 处理真值文本,将其转换为特定格式的张量。
    • 拼接图像嵌入、真值嵌入等得到最终的输入嵌入、注意力掩码和输入长度。
    • 填充真值标签。
    • 通过模型计算并得到输出,提取隐藏状态进行进一步处理得到不同类型的对数几率。

补充5:Lora微调

复制代码
    def create_eva_vit_g(img_size=224,drop_path_rate=0.4,use_checkpoint=False,precision="fp16", cross_layer_num=3, vit_model_path=''):
    model = VisionTransformer(
        img_size=img_size,
        patch_size=14,
        use_mean_pooling=False,
        embed_dim=1408,
        depth=39,
        num_heads=1408//88,
        mlp_ratio=4.3637,
        qkv_bias=True,
        drop_path_rate=drop_path_rate,
        norm_layer=partial(nn.LayerNorm, eps=1e-6),
        use_checkpoint=use_checkpoint,
    )  
    
    state_dict = torch.load(vit_model_path, map_location="cpu")    
    interpolate_pos_embed(model,state_dict)
    
    incompatible_keys = model.load_state_dict(state_dict, strict=False)
    dpr = [x.item() for x in torch.linspace(0, 0.4, 39)][-cross_layer_num:]
    cross_layers = nn.ModuleList([Block(dim=1408, num_heads=1408//88, mlp_ratio=4.3637, qkv_bias=True, qk_scale=None,
                drop=0., attn_drop=0., drop_path=0.4, norm_layer=partial(nn.LayerNorm, eps=1e-6),
                init_values=None, window_size=None) for i in range(cross_layer_num)])
    for i in range(cross_layer_num):
        model_block = model.blocks[-(cross_layer_num - i)]
        model_block_state_dict = model_block.state_dict()
        cross_layer_state_dict = cross_layers[i].state_dict()
        for name, param in model_block_state_dict.items():
            if name in cross_layer_state_dict:
                cross_layer_state_dict[name].copy_(param)
        cross_layers[i].load_state_dict(cross_layer_state_dict)
    print(apply_cross(cross_layers)[0])
    print(apply_lora(model)[0])
    mark_only_lora_as_trainable(model)
    return model, cross_layers
    ....
    class Custom_Attention(nn.Module):
    def __init__(self, existing_mha, lora_r=args.v_lora_r, lora_alpha=args.v_lora_alpha, lora_dropout=args.v_lora_dropout, enable_lora=args.v_lora_target_modules):
        super(Custom_Attention, self).__init__()
        self.num_heads = existing_mha.num_heads
        dim = existing_mha.dim 
        head_dim = existing_mha.dim // self.num_heads 
        if existing_mha.attn_head_dim is not None:
            head_dim = existing_mha.attn_head_dim
        all_head_dim = head_dim * self.num_heads
        
        self.scale = existing_mha.scale
    
        qkv = existing_mha.qkv 
        self.q_bias = existing_mha.q_bias
        self.v_bias = existing_mha.v_bias
        
        self.q_proj = nn.Linear(dim, all_head_dim, bias=existing_mha.q_bias is not None)
        self.k_proj = nn.Linear(dim, all_head_dim, bias=existing_mha.q_bias is not None)
        self.v_proj = nn.Linear(dim, all_head_dim, bias=existing_mha.q_bias is not None)
        self.proj = existing_mha.proj
        with torch.no_grad():  
            # Initialize q_proj
            self.q_proj.weight.data.copy_(qkv.weight.data[:all_head_dim, :])
            # Initialize k_proj
            self.k_proj.weight.data.copy_(qkv.weight.data[all_head_dim:2*all_head_dim, :])
            # Initialize v_proj
            self.v_proj.weight.data.copy_(qkv.weight.data[2*all_head_dim:, :])
            if self.q_bias is not None:
                self.q_proj.bias.data.copy_(self.q_bias)
                self.k_proj.bias.data.copy_(torch.zeros_like(self.v_bias))
                self.v_proj.bias.data.copy_(self.v_bias)
        LoRALayer.__init__(self, r=lora_r, lora_alpha=lora_alpha, dropout_rate=lora_dropout)
        for item in enable_lora:
            if item == 'q':
                self.q_proj = LinearLoRA(self.q_proj,
                                         r=lora_r,
                                         lora_alpha=lora_alpha,
                                         fan_in_fan_out=False,
                                         dropout_rate = lora_dropout)
            elif item == 'k':
                self.k_proj = LinearLoRA(self.k_proj,
                                         r=lora_r,
                                         lora_alpha=lora_alpha,
                                         fan_in_fan_out=False,
                                         dropout_rate = lora_dropout)
            elif item == 'v':
                self.v_proj = LinearLoRA(self.v_proj,
                                         r=lora_r,
                                         lora_alpha=lora_alpha,
                                         fan_in_fan_out=False,
                                         dropout_rate = lora_dropout)
            elif item == 'o':
                self.proj = LinearLoRA(self.proj,
                                         r=lora_r,
                                         lora_alpha=lora_alpha,
                                         fan_in_fan_out=False,
                                         dropout_rate = lora_dropout)
                
        self.window_size = existing_mha.window_size
    
        if self.window_size:
            self.num_relative_distance = existing_mha.num_relative_distance
            self.relative_position_bias_table = existing_mha.relative_position_bias_table
            self.register_buffer("relative_position_index", existing_mha.relative_position_index)
        else:
            self.window_size = None
            self.relative_position_bias_table = None
            self.relative_position_index = None
    
        self.attn_drop = existing_mha.attn_drop
        self.proj_drop = existing_mha.proj_drop
    
        
    def forward(self, x, rel_pos_bias=None,**kwargs):
        B, N, C = x.shape
        q = self.q_proj(x).reshape(B, N, self.num_heads, -1).permute(0, 2, 1, 3)
        k = self.k_proj(x).reshape(B, N, self.num_heads, -1).permute(0, 2, 1, 3)
        v = self.v_proj(x).reshape(B, N, self.num_heads, -1).permute(0, 2, 1, 3)
        
        
        q = q * self.scale
        attn = (q @ k.transpose(-2, -1))
    
        if self.relative_position_bias_table is not None:
            relative_position_bias = \
                self.relative_position_bias_table[self.relative_position_index.view(-1)].view(
                    self.window_size[0] * self.window_size[1] + 1,
                    self.window_size[0] * self.window_size[1] + 1, -1)  # Wh*Ww,Wh*Ww,nH
            relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous()  # nH, Wh*Ww, Wh*Ww
            attn = attn + relative_position_bias.unsqueeze(0)
    
        if rel_pos_bias is not None:
            attn = attn + rel_pos_bias
        
        attn = attn.softmax(dim=-1)
        attn = self.attn_drop(attn)
    
        x = (attn @ v).transpose(1, 2).reshape(B, N, -1)
        x = self.proj(x)
        x = self.proj_drop(x)
        
        return x
    ...
    def apply_lora(clip_model):
    list_lora_layers = []
    for i, block in enumerate(clip_model.blocks):
        for name, submodule in block.named_children():
            if isinstance(submodule, Attention):
                new_multi_head_lora = Custom_Attention(submodule)
                setattr(block, name, new_multi_head_lora)
                list_lora_layers.append(new_multi_head_lora)
    return list_lora_layers
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    

全部评论 (0)

还没有任何评论哟~