简介:基于GAN生成对抗网络的行人重识别毕业设计资源包,面向深度学习、计算机视觉方向的高年级本科生和研究生,适合正在开展相关课题或课程设计的读者。该课题聚焦跨摄像头下的行人检索任务,利用生成对抗网络增强行人特征的判别性与域适应性,是当前学术研究与工程应用中的热点。资源已通过导师审核,作为高分毕业设计成果,调试运行稳定,可直接启动训练与测试流程。压缩包共48个文件,主体为8个Python脚本,辅以2份PDF实验报告、1份PPTX答辩演示、7个TXT配置记录、22张JPG结果图、3张PNG结构图,并包含LICENSE、README等工程辅助文件;整体仅18.49MB,目录按代码、报告、演示和结果分层,便于检索。已有409人学习下载,内容覆盖数据预处理、GAN模型构建、行人重识别训练与评估全流程,并配有实验报告和答辩PPT,不仅适合作为毕业设计参考,还能帮助初学者快速理解GAN在此任务中的实际落地方式。
1. 用GAN做行人重识别:先弄清楚这条路线能走多远
白天从A栋大厅的监控里走过,晚上又出现在B栋车库的摄像头里,系统要从几百个陌生人的抓拍中认出这是同一个人——这就是行人重识别(Person Re-identification,ReID)要回答的问题。标题里的“基于GAN深度学习生成对抗网络”,并不是让GAN直接去做检索,而是让它补偿ReID最难搞的短板:同一个行人在不同摄像头下的外观差异太大,而带标注的训练数据又太少。GAN本身就是生成对抗网络的缩写,答辩时别把这两个词当成两个东西。这套方案在校园监控、商场客流、智慧安防项目里都有人用,对做毕业设计或刚入门深度学习的开发者来说是性价比很高的切入点。如果你手里拿到这份源码,目标是要跑出排得上号的检索结果、写出实验报告、答辩讲得清,那你真正要搞清楚的是三件事:数据怎么喂、标签怎么用、GAN怎么插进训练流程而不拖后腿。
2. 数据集与评测口径:rank-1 和 mAP 可信的前提
2.1 公开数据集怎么选:Market1501、DukeMTMC-reID 与 CUHK03 的取舍
行人重识别最常见的基准数据集是 Market1501、DukeMTMC-reID 和 CUHK03。三个数据集的采集场景、标注方式和难度都不一样,选哪个直接决定你的实验报告有没有说服力。做 GAN 增强方向的话,首选 Market1501,因为它的规模适中、划分文件是学术界默认口径,跑一轮实验时间可控;DukeMTMC-reID 相机数量更多、场景光照更杂,能体现 GAN 对跨相机风格差异的补偿能力;CUHK03 的检测框质量参差,适合说明你的方法对检测误差鲁棒,但训练难度明显更高。
关于数据集大家口口相传的几个数字,我给个常用口径,你写报告时以官方划分文件为准,别报错:
| 数据集 | 训练身份数 | 训练图像数 | query 数 | gallery 数 | 特点 |
|---|---|---|---|---|---|
| Market1501 | 751 | 约 12936 | 3368 | 15913 | 6 个摄像头,校园场景,划分最通用 |
| DukeMTMC-reID | 702 | 约 16522 | 2228 | 17661 | 8 个摄像头,光照变化更大,更难 |
| CUHK03 | 767 | 约 7368 | 1400 | 5328 | 检测框/手工框两种标注,难度高 |
2.2 评测指标与计算口径:rank-1 与 mAP 的常见误区
评测一个 ReID 模型,报告里最常出现两个指标:rank-1 和 mAP。rank-1 的意思是,对每个 query 行人,系统从 gallery 里给出按相似度降序的检索列表,排在第一位的那个结果恰好是同一个人。mAP 则看整个排序列表的质量——同一个人的所有正确结果是否尽可能排在前面。只看 rank-1 会掩盖“正确结果排第 2、第 3 位”的普通表现,所以两个指标要一起报。这是检索类任务的基本共识,也是答辩时老师必看的一组数。
写完指标还要说清评测口径:计算距离时,query 和 gallery 来自同一个摄像头且身份相同的样本需要排除。否则,模型只要学会“记住这个摄像头的背景”,rank-1 就能虚高。这块属于“看着简单实际上特别容易翻车”的环节,我见过不少人把同相机同身份样本算进 gallery,最后 rank-1 刷到 95% 以上,换数据集立刻掉回 50%。评测代码不复杂,但口径错了整个实验就白做:
import numpy as np from scipy.spatial.distance import cdist # query_feats / gallery_feats 是 L2 归一化后的特征向量 dist = cdist(query_feats, gallery_feats, metric='cosine') # 按距离从小到大排序,得到每个 query 对应的 gallery 下标序列 rank_idx = np.argsort(dist, axis=1) # 屏蔽规则:排除 与 query 相同的摄像头编号 且 身份标签相同 的 gallery 样本 # 这是 Market1501 官方评测的默认口径,少了这步指标会虚高 valid = ~((same_cam_mask == 1) & (gallery_label == query_label)) for i in range(len(rank_idx)): valid_rank = rank_idx[i][valid[i]] # 计算第一个命中正确身份的排名位置,再统计 rank-k2.3 训练集与验证集的切分原则
ReID 数据集的标注形式是“身份 ID + 摄像头编号 + 图像路径”,训练和测试的身份集合是严格不相交的。这一点和 ImageNet 分类完全不同:分类任务里测试类别在训练里见过,ReID 必须保证测试行人从未出现在训练中,否则就是“认识这个人”,而不是“认出这个人”。做实验时,你要检查自己的数据加载代码有没有把训练集的 ID 泄漏到测试集,一个常见低级错误是数据划分时用了全局随机打乱,导致同一个人既在 train 里又在 query 里。
用 GAN 增强时还要注意:生成图像的标签必须继承源图像的身份 ID,不能凭空分配新 ID。生成样本只是同一个行人在新姿态、新光照下的外观变换,身份标签不能变。如果生成器把两张不同身份的图像融合成一张,这张图的标签就没人说得清,喂进训练集后会让分类头学出错误边界。这是 GAN-ReID 方向最容易忽略的标签伪标定问题,后面第 4 章会细讲。
3. GAN 在 ReID 里的真实角色:数据增强、特征对抗与损失设计
3.1 三类典型做法:图像风格迁移、姿态生成与特征级对抗
GAN 在 ReID 里的角色从来不是“主力模型”,而是陪练。最常见的三类做法,难度和收益从低到高排列:
第一类是图像风格迁移。用 CycleGAN 或 StarGAN 把 A 摄像头的行人图像转成 B 摄像头的光照、纹理风格,身份保持不变。这样做的好处是,不需要跨摄像头的成对标注,CycleGAN 天然支持非配对训练。实际项目里,我会把 Market1501 的 6 个摄像头按“光照差异大”的原则分成两组,做风格迁移后扩充训练集。这个做法实现简单,训练稳定,适合毕业设计做基线增强。
第二类是姿态生成。用姿态关键点引导生成器,把行人从正面视角转成侧面或背面。这类做法需要额外的姿态估计模型,比如 OpenPose 或 AlphaPose,成本高,但能直接补足 ReID 最头疼的视角缺失问题。训练时容易踩坑:姿态关键点预测错了,生成出来的图像肢体扭曲,反而污染训练集。我的经验是离线跑一批生成结果,肉眼筛一遍再做数据混合,别全自动灌进去。
第三类是特征级对抗。不做图像层面的生成,而是给骨干网络的特征加一个判别器,判别器负责判断“这个特征来自哪个摄像头”。如果特征中包含了相机风格的偏置,判别器就能轻松分辨出来;反过来骨干网络会尽量去掉这些偏置信息,让判别器分不清。这个做法不产生额外图像,训练开销小,但调参难度高,特征层的分布不像图像那样直观,出了问题很难排查。
3.2 把 GAN 输出接进 ReID 训练:两种接入方式对比
接入方式决定了你的整个训练流程怎么组织。我用过两种方案,各有利弊:
| 接入方式 | 实现要点 | 优点 | 缺点 |
|---|---|---|---|
| 离线生成 + 混合训练 | 先用 GAN 生成一批增强图像,保存到磁盘,再和原图按比例混合成一个数据集 | 训练稳定,生成和 ReID 训练解耦,出问题容易定位 | 生成图像多样性固定,无法根据 ReID 训练状态动态调整 |
| 在线对抗训练 | 生成器与 ReID 模型在同一训练循环中交替更新 | 生成分布会随训练动态变化,理论上更能适配困难样本 | 训练不稳定,显存占用翻倍,调参难度大 |
对毕业设计和大部分项目,我强烈推荐离线方案。理由是:在线方案把两个不稳定系统绑在一起,生成器崩溃时 ReID 模型也会跟着掉点,你会分不清是 GAN 训崩了还是 ReID 训练有问题。离线方案相当于给实验加了一道闸,先把生成质量这个变量控制住,再做对比。答辩时,离线方案也更方便画流程图和数据管线,老师听着不累。
3.3 生成样本不能直接灌进训练集:标签伪标定与采样比例
直接把 GAN 生成的图像灌进训练集是新手最容易犯的错误,而且刚开始看不出问题。生成图像和原图在分布上仍有差异,如果混合比例过高,ReID 模型会对生成图像的纹理过度拟合,导致在真实测试集上掉点。我一般把生成样本的比例控制在 20%~40%,具体看生成质量来调整。
另外要注意标签伪标定:风格迁移类方法只是变换了外观,身份 ID 不变,可以采用硬标签;但如果是姿态生成或跨域生成,生成图像的质量参差,部分图像的判别性特征已经被破坏,这时可以用标签平滑或给生成样本设置一个较低的权重,让模型“参考但不盲信”。我在实验里常用的做法是,给生成样本的 ID 分类损失乘一个 0.5 的系数,三元组损失正常算,这样既能利用生成样本的多样性,又不会让它们喧宾夺主。
4. 在 Python 里跑通最小 GAN-ReID 实验:环境搭建、训练步骤与 3 个必调参数
4.1 环境搭建:先从 CPU 跑通流程,再上 GPU
拿到源码的第一步不是直接训练,而是先把环境搭好、把测试流程跑通。官方代码库或毕设工程一般基于 PyTorch,环境配置遵循固定顺序能省掉一半的“玄学报错”。我建议的安装顺序是:先建 conda 虚拟环境,再装 PyTorch,最后装 scipy、tensorboard、tqdm 等辅助库。
# 1. 创建独立环境,避免和别的项目互相污染 conda create -n reid python=3.8 # 2. 激活环境 conda activate reid # 3. 安装 PyTorch 与配套库 # 注意:这里的 CUDA 版本要和你本机 nvidia-smi 显示的驱动版本匹配 conda install pytorch==1.12.1 torchvision==0.13.1 cudatoolkit=11.3 -c pytorch # 4. 安装常用依赖 pip install scipy tqdm tensorboard pillow # 5. 验证 PyTorch 是否能调用 GPU python -c "import torch; print(torch.cuda.is_available())"这里的逻辑是先用 conda 锁住 Python 和 CUDA 的版本组合,避免 pip 自动升级把环境搞乱。第 5 步如果输出 False,先检查驱动,再检查 cudatoolkit 版本,不要急着重装。如果你的机器显存小于 6G,先用 CPU 模式把数据加载和评测流程跑通,确定代码没问题再换 GPU 训练,否则每次改代码都要排队等显存,效率太低。
4.2 最小复现链路:数据准备、训练、评估三阶段
ReID 工程的目录结构通常包含 config、data、model、utils 几个模块。拿到源码后,你需要先找到三个入口脚本:数据划分脚本、训练脚本、评估脚本。一般会有一个 README 或 run.sh 说明执行顺序。我习惯让工程按下面这个流程工作:
# 阶段1:数据准备 # 把 Market1501 原始数据按 train/query/gallery 划分好 # 得到 images.txt、train_path.txt、query_path.txt、gallery_path.txt 四个文件 python prepare_data.py --dataset market1501 --data_dir /path/to/Market1501 # 阶段2:训练 ReID 模型(此时不接入 GAN,先建立基线) # 训练 60 个 epoch,保存 best_model.pth python train.py --config configs/reid_base.yaml # 阶段3:评估 # 加载训练好的模型,输出 rank-1、mAP python test.py --config configs/reid_base.yaml --resume checkpoints/best_model.pth数据准备阶段最关键的是检查路径文件的行数是否匹配官方划分:train_path 行数 12936,query_path 行数 3368,gallery_path 行数 15913。这三个数字对不上,后面所有环节都会错。训练阶段先跑基线,不要一上来就叠 GAN。先确认基线模型在 Market1501 上能达到公开论文的常见水平——现在主流方法在 90% 左右,具体值随预训练和 trick 浮动——再开始加 GAN。
4.3 三处必调参数:生成器权重、混合比例与 BN 统计量
GAN 接入 ReID 训练后,有这三个参数值得反复调,也是实验报告里最值得写的消融点。
第一是生成样本在训练 batch 中的比例。我习惯用 0.3 起步,生成质量高可以调到 0.4,生成质量一般就降到 0.2。这个参数直接控制模型对生成分布的拟合程度。第二个是生成样本的 ID 损失权重,前面说过从 0.5 起步,如果发现 rank-1 不升反降,优先调它。第三个是 BN 层的统计量设置,这是 ReID 训练里公认的“细节决定成败”:在行人重识别里,模型最终只保留 backbone 的全局特征,分类层会丢弃,所以 backbone 的最后一个 stage 的 BN 需要把 momentum 调大(比如 0.1),并关闭 bias。这个 trick 能让特征分布更稳定,rank-1 能涨 1~3 个点。
# 最小 GAN-ReID 训练循环中的核心损失计算 import torch import torch.nn.functional as F # 假设: # backbone 输出特征 f:形状 [batch_size, 2048] # classifier 输出 ID 分类 logits:形状 [batch_size, num_ids] # 每个 batch 中,前 num_gen 个样本是 GAN 生成的增强图像 def compute_loss(f, logits, labels, num_gen, lamda=0.5): # 1. ID 分类损失:所有样本都参与,但生成样本权重降低 loss_id_all = F.cross_entropy(logits, labels) loss_id_gen = F.cross_entropy(logits[:num_gen], labels[:num_gen]) loss_id = (1 - lamda) * loss_id_all + lamda * loss_id_gen # 2. 三元组损失:主要约束真实样本之间的相对距离 # 这里用 batch-hard 策略,采样难正例和难负例 loss_triplet = triplet_loss(f[num_gen:], labels[num_gen:], margin=0.5) # 3. 总损失,backward 后统一更新 backbone 和 classifier return loss_id + loss_triplet这段代码的逻辑是:生成样本参与了 ID 分类损失,但对难样本三元组损失不贡献。原因是生成样本的特征分布和真实样本有偏移,让它们参与三元组计算会拉偏特征空间的距离度量。如果你做完实验发现 rank-1 不升反降,优先检查这个设置——把生成样本的全部分类权重去掉,只保留数据多样性,有时反而效果更好。这边的损失权重 0.5 只是一个起点,你的实验报告里应该做成一个参数扫描表:0.0、0.3、0.5、0.7 各跑一遍,报告会非常扎实。
5. 行人重识别避坑清单:5 个必经的坑与排查路径
5.1 训练损失下降但 rank-1 纹丝不动:BN 统计量在作怪
现象:训练集损失一路下降,收敛也正常,但测试集 rank-1 卡在 40% 左右上不去。
原因:ReID 模型在训练时用 batch 内的 BN 统计量,推理时用全局统计量。如果训练时 batch size 太小,BN 的均值方差估计不准,模型学习到的特征分布和推理时不一致。另一个常见原因是骨干网络最后一层 BN 没有特殊处理,导致特征在推理时分布漂移。
解决:把骨干网络最后一个 stage 的 BN momentum 设为 0.1,并设置 affine 参数中的 bias 为 False;batch size 尽量不小于 16,如果显存不够就把输入分辨率从 256x128 降到 128x64,而不是强行减小 batch size。这个改动通常能带来 2~4 个点的提升,属于成本最低的涨点手段。
5.2 GAN 生成的图像全是黑白噪点或色块
现象:生成器输出的图像看起来像随机噪声,完全看不出行人轮廓,训练损失里生成器 loss 一直在震荡。
原因:多数情况下是生成器的输出层没有匹配图像数据的归一化范围。ReID 输入图像经过归一化后像素范围在 [-1, 1],而生成器输出层如果用了 sigmoid(范围 [0, 1]),或者没有加 tanh,就会导致图像失真。另一个原因是判别器学习率过高,把生成器压得毫无还手之力。
解决:检查生成器最后一层是否为 tanh 激活;把判别器换成带谱归一化的结构,学习率设置为生成器的一半。同时把 GAN 损失从普通 BCE 换成 LSGAN 或 WGAN-GP,训练稳定性会好很多。顺带说一个血泪经验:生成图像直接可视化看效果,不要只觉得 loss 数字好看就行。
5.3 显存不够但代码能跑:batch size 与分辨率的取舍
现象:程序启动时报 CUDA out of memory,但代码逻辑本身没问题。
原因:ReID 计算的是整图的全局特征,图像分辨率越高、batch size 越大,显存占用越高。如果同时跑生成器和 ReID 模型,显存需求直接翻倍。
解决:优先降 batch size,保证每个 batch 内身份数量足够(比如 8 个身份,每个身份 4 张图);再降输入分辨率,从 256x128 降到 224x112 或 128x64。要注意的是,离线生成 GAN 图像可以在廉价机器上批量跑完,存成图片后再开始 ReID 训练,这样能绕开显存瓶颈。在线训练的显存代价不小,对设备不友好的情况建议直接放弃。
5.4 跨域测试效果暴跌:数据集的风格偏置
现象:在 Market1501 上 rank-1 有 80% 多,直接换到 DukeMTMC-reID 上测试掉到 20% 以下。
原因:ReID 对数据集的风格偏置非常敏感。训练集里的摄像头数量、光照条件、行人姿态分布都被模型学进了特征里,换一个数据分布就失效。很多论文为了掩盖这个问题,只做同域内的随机划分,这在实际项目里没有太大参考价值。
解决:训练阶段引入风格迁移 GAN 做跨数据集增强,把 Market1501 的图像转成 Duke 风格;评估阶段一定要留出跨域测试集,不能只在同域内自嗨。如果你的硬件资源有限,至少要在实验报告里诚实说明同域评测的局限,答辩老师更看重你是否知道这个问题存在。
5.5 答辩被问“GAN 提升有限怎么解释”:先做消融
现象:加了 GAN 之后,rank-1 只涨了 0.3 个点,实验报告不知道怎么圆。
原因:这根本不是 bug,而是常态。基线模型本身已经学得不错时,GAN 增强带来的增量必然有限。真正的工作量不在“跑出涨点”,而在“分析涨在哪”:是困难样本上的 rank-1 涨了,还是 mAP 涨了?在哪些摄像头对之间涨得最多?
解决:准备一张消融表,列四行:baseline、baseline + 随机裁剪翻转、baseline + GAN 增强、baseline + GAN 增强 + 标签平滑。再按 query 难度把样本分成简单/中等/困难三组,分别报 rank-1。这组数据比在答辩台上解释“GAN 就是有用”有说服力得多。诚实地说,我见过不少答辩现场,老师问的第一个问题就是“你的提升是不是数据增强带来的,换 RandomErasing 是不是也一样”,没有这张消融表,这个问题基本答不上来。
6. 进阶验证:从训练曲线到跨域泛化的三个自检动作
6.1 训练曲线之外:看生成图质量和特征分布两个“黑匣子”
训练曲线只能说明损失在降,不能说明 ReID 特征质量在涨。我的习惯是每 5 个 epoch 保存一批生成图像,肉眼检查清晰度、姿态自然度和身份一致性。特征分布方面,随机抽 20 个身份的特征做 TSNE 可视化,如果同一个身份的特征聚成一团、不同身份之间分得开,说明特征判别性不错。如果 TSNE 图上出现明显的摄像头分簇,说明特征还残留相机风格信息,风格迁移模块没过关。
6.2 跨域验证:同域评测通过不代表能用
把模型在 Market1501 上训练好,直接拿去 DukeMTMC-reID 的 query/gallery 上测一遍,这就是跨域评测。如果 rank-1 掉得很惨,你需要引入域适应或更强的风格增强。实际项目部署前,这一步是绕不开的。我认识的一线工程师,在项目验收前都会专门准备一份真实场景的采集数据当评测集,用训练时的同款评测脚本打分,避免实验室指标和现场表现差出一个量级。这个自检动作虽然费时间,却代表你的实验报告有真正的工程价值。
6.3 给答辩的最后一道准备:一张消融对比表与两个必答追问
消融表不要做得太复杂,我建议按下面这个模板组织,具体数字用你自己的实验结果填写:
| 配置 | rank-1 | mAP | 说明 |
|---|---|---|---|
| baseline | 88.3 | 71.2 | ResNet50 基线 |
| baseline + 常规增强 | 89.1 | 72.5 | 随机翻转、裁剪 |
| baseline + GAN 风格迁移 | 90.2 | 74.0 | 生成样本比例 0.3 |
| baseline + GAN + 标签平滑 | 90.6 | 74.8 | 生成样本权重 0.5 |
答辩时会有两个必答追问你要准备好。第一个是“为什么用 GAN 而不是直接加更强的数据增强”,答案核心是 GAN 能生成训练集中不存在的分布组合,比如把 A 摄像头的身份放到 B 摄像头的光照下,这是传统数据增强做不到的。第二个是“GAN 训练不稳定怎么控制”,你可以回答:谱归一化、梯度惩罚、判别器学习率降为生成器的一半、离线生成切断训练耦合。
最后说一个我自己的教训:读研时第一次跑 GAN-ReID,狠狠堆了三个生成器,成果展示时发现收益还不如多调两天 BN 参数。后来我想明白了,GAN 在 ReID 里永远是放大器,不是救世主,你的基线和评测口径不稳,GAN 再强也救不回来。先跑通流程,再谈生成质量,最后追求涨点,这个顺序不要颠倒。希望帮到你。
本文还有配套的精品资源,点击获取