新闻详情

新闻详情

首页 / 资讯中心 / 详情

深度学习图像美学评价系统:AVA数据集、CBAM注意力与EMD损失实战

发布时间:2026/9/28 17:12:37来源:尧图网络
深度学习图像美学评价系统:AVA数据集、CBAM注意力与EMD损失实战
简介基于深度学习的图像美学质量评价系统完整Python实现面向毕业设计、人工智能课程项目及图像质量研究初学者解决如何通过深度学习模型对图像美学进行自动化评分、分类与排序的问题。资源共39个文件压缩包仅346KB主体为28个Python源码与6个Jupyter Notebook另含QML界面文件、项目文档、HTML报告及JSON配置分别覆盖模型定义、训练流程、数据预处理、人机交互界面和结果展示等环节。已有110人学习浏览适合作为快速复现与二次开发的基础。工程代码按照datasets、models、ui、notebooks等模块清晰组织支持AVA、AADB、CUHK-PQ等常用美学数据集并集成CBAM注意力模块、损失函数与评估指标等关键实现从数据预处理、模型训练、评估测试到界面交互均有对应脚本notebook还提供数据集分析与观测过程结合README和report.html可快速上手便于逐模块理解深度学习美学评价的完整链路。1. 图像美学质量评价把“美不美”变成一套可复现的深度学习打分图像美学是个非常主观的领域同一张照片有人觉得构图舒服有人觉得光线刺眼但偏偏这种玄学任务在深度学习框架下反而能训练出与人工评分高度相关的模型。这套基于深度学习的图像美学质量评价系统是一份完整的 Python 源代码覆盖数据预处理、模型训练、评估测试和可视化界面不是只有模型文件的半成品。它适合正在做毕业设计、需要完整工程骨架的学生也适合想进入图像美学评价方向的工程师。系统的主线很清晰用 AVA 这类带评分分布的数据集训练卷积网络输出从 1 分到 10 分的分布再按期望值换算最终得分既保留评分的模糊性又让回归更稳定。拆这份资源时我印象最深的是它把 CBAM 注意力模块嵌进了主干网络在有限数据上换来了一点涨点空间。下面按数据、模型、训练、排错、落地的顺序把关键步骤和参数逐个讲清楚。2. 数据先行AVA、AADB、CUHK-PQ 怎么选、怎么吃进模型2.1 三个数据集差异和选型理由仓库里 datasets 目录下放了三个数据集的加载器ava.py、aadb.py、cuhk_pq.py。我一开始有点困惑为什么一个项目要同时兼容三套数据跑通之后才明白这三套数据恰好覆盖了三种典型的标注形态对应三种不同的训练目标。数据集样本量标注形式适合的任务AVA约 25 万张1 到 10 分的投票分布分布预测、回归AADB约 1 万张平均分加构图、光线等属性多任务学习、属性解释CUHK-PQ约 1.7 万张高质量/低质量二分类分类基线、快速验证主训练建议用 AVA原因是它的评分分布信息量最大。模型输出的不是单个分数而是每个分数档的投票比例这样能告诉使用者“这张图 7 分的概率是 40%5 分的概率是 25%”而不是冰冷地丢一个 6.3。AADB 的定位是辅助它自带属性标签适合做多任务扩展比如同时预测美学分数和构图质量。CUHK-PQ 则适合做快速冒烟测试数据小、跑一轮很快先把代码流程验证通了再上 AVA。2.2 从 ava.txt 到训练列表预处理脚本AVA 数据集的标注文件是纯文本每一行代表一张图格式大致是图像 ID然后是语义标签编号、风格标签编号接着才是 1 到 10 分各自的投票数最后还有两个水印和安全标记位。新手最容易翻车的地方就在这里容易把前两列的数字误当成评分列导致标签整体错位训练出来的模型预测结果和真实审美分布完全对不上。项目里 process_ava.ipynb 这个 notebook 干的事情就是把 ava.txt 解析成干净的训练列表。我在复现时用同样的逻辑整理了一份脚本核心代码如下import random def parse_ava(path): samples [] with open(path, r) as f: for line in f: parts line.strip().split() if len(parts) 12: continue image_id parts[0] # 第 3 到第 12 列才是 1~10 分的投票数 distribution [int(x) for x in parts[2:12]] samples.append((image_id, distribution)) return samples def split_and_dump(samples, val_ratio0.1, seed42): random.seed(seed) random.shuffle(samples) val_cnt int(len(samples) * val_ratio) train, val samples[val_cnt:], samples[:val_cnt] for name, subset in [(train_list.csv, train), (val_list.csv, val)]: with open(name, w) as f: for img_id, dist in subset: dist_str ,.join(map(str, dist)) f.write(f{img_id}|{dist_str}\n) if __name__ __main__: data parse_ava(ava.txt) split_and_dump(data)代码逻辑说明parse_ava 按空格切分每一行前两个数字是语义和风格标签所以取 parts[2:12] 作为评分分布。过滤条件 len(parts) 12 是为了跳过残缺行这类坏行在网上下载的标注文件里经常出现不过滤会让后面的数据集对齐直接崩掉。split_and_dump 按 9:1 划分训练和验证集把结果写成 CSV 格式每个字段用竖线分隔这样后续 dataset.py 读取时不容易和打分里的逗号混淆。参数说明val_ratio 取 0.1 是经验值25 万张图留 2.5 万张做验证足够稳定。如果你机器显存小、训练集加载慢可以适当缩到 0.05但验证集太小会导致 SRCC 指标抖动很厉害不建议低于 0.05。seed 固定是为了可复现换数据集时记得改一个不同值否则随机打乱的顺序每次都一样反而不利于后续做交叉验证实验。2.3 图像送入网络前的尺寸处理和标准化数据集列表准备好之后下一步是图像加载。项目里 dataset.py 和 utils.py 主要就做两件事把原始图片读进来转成模型需要的张量格式。主干网络用的是 ResNet 结构输入尺寸是 224x224。我见过不少人在这个尺寸上偷懒直接缩放结果训练集和验证集都用同样方式缩放模型很容易记住缩放痕迹而不是美学特征。import torch from torchvision import transforms from PIL import Image train_transform transforms.Compose([ transforms.Resize(256), transforms.RandomCrop(224), transforms.RandomHorizontalFlip(), transforms.ColorJitter(brightness0.2, contrast0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) val_transform transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) def load_image_into_tensor(path, transform): img Image.open(path).convert(RGB) return transform(img).unsqueeze(0)代码逻辑说明先缩放到 256再随机裁剪 224这是 ImageNet 时代最经典的做法。直接 Resize(224) 虽然省事但会把图像的全局比例破坏掉裁剪到 224 能保留更多局部结构同时给模型提供平移不变性。RandomHorizontalFlip 对美学任务影响不大风景和建筑构图在水平翻转后依然合理但如果你在做人像美学评价翻转后人脸朝向变化可能有副作用建议关掉。参数说明Fill 填充值、裁剪范围这些细节我没有写进代码因为它们对最终指标影响不大真正影响大的是 ColorJitter 的幅度。美学评价本身对色彩敏感亮度调到 0.2 已经是上限再大会让模型把过曝和欠曝当成正常美学特征评分会出现系统性偏移。Normalize 用 ImageNet 的 mean 和 std这是预训练模型的默认配置不用改改了反而会让预训练权重失效。2.4 踩坑AADB 图片名对不上AADB 数据集的文件命名比 AVA 混乱得多notebooks 里的 process_aadb.ipynb 处理的就是这个问题。常见情况是标注 CSV 里的文件名和实际图片文件不完全一致有后缀差异也有路径层级差异。我一般会在加载器里加一个文件名清洗函数把扩展名去掉、统一小写再和目录里的实际文件名做映射。这个步骤不做训练集加载时会有 30% 以上的图找不到程序通常不会直接报错而是静默跳过最后你会发现 loss 曲线忽高忽低但怎么查都查不出问题。3. 核心模型主干网络加 CBAM 注意力把美学评分做成分布预测3.1 为什么输出分布而不是直接输出分数美学评分最常见的翻车方式是当作回归任务直接预测一个浮点数然后用 L2 损失训练。表面上损失在收敛实际预测结果往往集中在 5 到 6 分之间因为回归模型倾向于学习到均值附近把高分和低分都拉向中间。这也是 AVA 数据集的特殊性决定的每张图都有多个人的打分本身就是分布形态。项目里的 model.py 把最后一层改成输出 10 维向量经过 softmax 得到每个分数档的概率再用期望值计算最终得分。这样做的好处有三个一是保留了人为评分的多样性模型可以学出“这张图有争议”这类信息二是损失函数在分布层面做比较梯度更平滑三是评测时可以同时看准确率和相关性指标更丰富。常见做法是把主干网络的 Global Average Pooling 后面的全连接层换成两层中间接一个 BN 和 ReLU最后一层输出 10 个节点。3.2 CBAM 注意力模块的作用和插入位置仓库里 models/cbam.py 实现的是 CBAM也就是 Convolutional Block Attention Module。它的核心思想是先在通道维度上做注意力告诉网络哪些特征通道更重要再在空间维度上做注意力告诉网络图像的哪些区域更重要。对于美学任务来说空间注意力尤其关键比如一张人像照片脸部区域的美学权重明显高于背景的天空空间注意力能让模型自动学会关注这些区域。CBAM 的插入位置很讲究我一般只加在最后两个残差块后面而不是每个 stage 都塞。加得太多参数量上去了训练速度变慢偶尔还会掉点加得少模型无法在高层语义特征上做有效筛选。项目里采用的做法是把 CBAM 模块插入到 ResNet 后两个 block 的输出处这是一个收益和开销均衡的位置选择。3.3 EMD 损失分布距离怎么算训练分布预测模型时项目里用的是 EMD 损失也就是 Earth Movers Distance中文叫推土机距离。它衡量的是把预测分布变成真实分布所需要移动的最小工作量。相比 KL 散度EMD 对分数档位之间的距离更敏感比如预测 7 分的概率很高但真实是 6 分这时 EMD 的惩罚会相对小一些而 KL 散度会把这种相邻档位的误差当成完全错误来惩罚不利于美学评分这种相邻档位本就模糊的任务。import torch import torch.nn as nn class EMDLoss(nn.Module): def __init__(self, num_bins10): super().__init__() self.num_bins num_bins def forward(self, pred, target): # pred: [batch, 10] 已经过 softmax # target: [batch, 10] 真实分布 pred_cdf torch.cumsum(pred, dim1) target_cdf torch.cumsum(target, dim1) # 每一档的累计分布差值的绝对值求和取平均 emd torch.mean(torch.abs(pred_cdf - target_cdf)) return emd代码逻辑说明累计分布差值的绝对值求和实际上就是推土机距离在离散分布下的一种简化形式。前面三行看似简单清空的坑在于 pred 必须是 softmax 输出不能直接拿全连接层的 logits 进来算否则 cumsum 出来的值没有一点概率意义。target 也必须是归一化后的分布AVA 的原始投票数是频次需要先除以总数这一步经常被忽略。参数说明num_bins 取 10 对应 1 到 10 分的分数档。如果你的数据集只有 5 档分数要把这个参数改成 5同时模型最后一层的输出维度也要同步修改否则会在 cumsum 时维度不匹配。loss 的数值量级在 0 到 1 之间训练时一般不需要额外加权。3.4 最终分数的换算训练结束后实际部署时我们需要一个直观的分数而不是一组分布。换算方式就是按概率做加权期望代码如下import torch def distribution_to_score(prob, devicecuda): # prob: [batch, 10] 概率分布 bins torch.arange(1, 11, dtypetorch.float32, devicedevice) scores torch.sum(prob * bins, dim1) return scores def score_to_grade(score): # 项目里把 1~4 分归为差5~7 归为中8~10 归为好 if score 8.0: return good elif score 5.0: return medium else: return bad参数说明bins 的起点是 1 而不是 0因为 AVA 的评分体系就是 1 到 10 分。score_to_grade 的阈值是项目里 report 模块用的一套标准你可以按业务需求调整比如只关心高分精品图就把 good 的阈值推到 8.5。注意这里 score 是浮点数分布极端的两张图可能得到相同期望值如果业务上需要区分最好同时保留分布信息。4. 训练与评估配置项、训练循环和指标解析4.1 config.json 里的关键参数项目根目录下有一个 config.json这是整个系统的总配置入口。我拆过的不少开源项目把超参数散落在代码各处改起来非常痛苦这份资源把能用 JSON 表达的参数都收拢到了一起训练前只需要改这个文件。参数名取值示例作用data_root/data/ava_images图片存放根目录train_listtrain_list.csv训练集列表路径val_listval_list.csv验证集列表路径batch_size64单次送入 GPU 的样本数epochs30最大训练轮数lr1e-4Adam 初始学习率weight_decay1e-5权重衰减系数backboneresnet50主干网络类型num_workers8数据加载线程数devicecuda:0训练设备这里我重点讲几个容易出问题的参数。batch_size 在单卡 11GB 显存下ResNet50 输入 224x224设置为 64 刚刚好如果用 16GB 显存可以开到 128 提速。num_workers 建议和 CPU 核心数匹配但如果你在 Windows 下跑num_workers 超过 0 有时会触发多进程启动异常设置成 0 虽然慢一点但胜在稳定。lr 用 1e-4 是 Adam 配合预训练权重时的保守选择从头训练建议调到 3e-4。4.2 从 main.py 入口跑一次训练项目里 train.py 负责读配置main.py 负责组装模型和启动训练。训练流程是标准的加载预训练权重、替换最后一层、初始化 trainer、按 epoch 循环。trainers.py 里保存了每个 epoch 的 checkpoint包含模型权重和优化器状态。python train.py --config config.json我自己在复现时的习惯是先跑一个 5 epoch 的短训练确认 loss 在下降、验证集指标在波动再开完整训练。这个动作在项目里可以通过在 config.json 里临时修改 epochs 实现不用改代码。训练日志会输出到终端内容包括每个 epoch 的平均损失、SRCC、预测准确率同时 trainers.py 会把指标写入跑批目录下的 txt 文件。# 伪代码展示 trainer 的核心循环实际实现参考 trainers.py for epoch in range(epochs): model.train() train_loss 0.0 for batch in train_loader: images, dists batch images images.to(device) dists dists.to(device) pred model(images) loss criterion(pred, dists) optimizer.zero_grad() loss.backward() optimizer.step() train_loss loss.item() val_srcc, val_acc evaluate(model, val_loader) scheduler.step()参数说明scheduler 在项目里用的是多步衰减每 10 个 epoch 学习率乘 0.1。如果你把 epochs 缩短到 5scheduler 永远触发不到所以短训练只用来排错不能作为最终实验结果。optimizer 用的是 Adambeta 默认值就好weight_decay 设 1e-5 是防止高分区过拟合的一个折中值太大会让模型欠拟合。4.3 评估指标SRCC 和分类准确率怎么看metrics.py 里封装了两个核心指标一个是 SRCC也就是 Spearman 秩相关系数另一个是预测分布和真实分布的准确率。SRCC 衡量的是排序一致性不关心具体分数差多少只关心两张图相对谁高谁低这一点和美学评价的本质高度契合。from scipy.stats import spearmanr from sklearn.metrics import accuracy_score def compute_srcc(pred_scores, true_scores): # 期望分数和真实平均分 srcc, _ spearmanr(pred_scores, true_scores) return srcc def compute_distribution_acc(pred_dist, true_dist): # 每张图取概率最高的档位作为预测档位 pred_label pred_dist.argmax(dim1) true_label true_dist.argmax(dim1) return accuracy_score(true_label.cpu(), pred_label.cpu())代码逻辑说明SRCC 的计算不需要手动实现排序比较scipy 的 spearmanr 直接搞定。distribution_acc 的作用是看模型的峰值概率是否能命中真实分布的最高档位这个指标在论文里常用但在业务里参考价值有限因为美学评分的相邻档位本来就模糊。真正实用的是 SRCC一般在 AVA 测试集上能到 0.6 到 0.7 之间就算不错的基线。注意这里 pred_scores 必须先经过 distribution_to_score 换算不能用分布原始值直接算相关性否则返回值没有意义。真实分数是投票分布的加权平均数据集在预处理时就要算好不要放到评估阶段重复计算容易出错。5. 避坑我复现时踩过的五个典型问题5.1 Loss 在降但 SRCC 是负的现象训练了几个 epochEMD loss 一路下降但验证集上的 SRCC 却是负值模型排名能力比随机还差。原因AVA 的 ava.txt 列序理解错误。我之前在解析时把语义标签列当成了评分列模型学到的是一个和美学无关的分布。loss 能下降是因为它拟合了一个固定模式的噪声分布排名自然没有意义。解决回到 parse_ava确保只取 parts[2:12]也就是跳过前两个标签列。建议写一个单元测试固定输入三行样本断言 distribution 的总和等于该图的投票人数这是最直接的自检方式。test_dataset.py 里已经有类似的测试用例跑一遍能提前发现问题。5.2 显存溢出改小 batch_size 后指标波动变大现象batch_size 从 64 降到 16 后训练不溢出了但每个 epoch 的验证指标跳来跳去很不稳定。原因batch_size 变小每个 step 的梯度估计噪声变大BN 层的统计量也变差最后导致验证集上的评估方差变大。解决不要只降 batch_size要同步调整学习率。常见做法是学习率按 batch_size 比例缩放64 配 1e-432 配 5e-516 配 2.5e-5。另外可以把梯度累积步数设成 4模拟 64 的 batch_size。项目里 trainers.py 支持累积梯度开关开启后显存占用没变但训练稳定性恢复很多。5.3 QML 界面上图片无法显示现象UI 能启动评分也能算出来但界面上的图片区域是空白的控制台也没有报错。原因main.qml 里用的图片路径是相对路径从命令行启动时工作目录不对图片加载失败。这类问题在 Qt 的 Image 组件里很常见相对路径在 UI 项目里就是一颗定时炸弹。解决把图片路径在 Python 侧转换成绝对路径再传给 QML。做法是在 context.py 里调用 os.path.abspath 提前解析或者在 QML 里改用 file:// 前缀拼接绝对路径。我从那以后所有演示项目的图片加载都强制用绝对路径。5.4 验证集表现很好测试集一塌糊涂现象验证集上 SRCC 到 0.65换到另一批测试图后直接崩到 0.5 以下。原因AVA 划分验证集时没有按摄影作品分组同一组系列照片可能同时出现在训练集和验证集。模型实际是记住了摄影师风格而不是美学规律。解决按图集分组切分一个系列的照片全部进同一侧。较快的做法是看图片 ID 的路径前缀把同名目录下所有图划到一起。代价是训练集变小模型收敛变慢但泛化能力明显改善。5.5 PyTorch CUDA 版本和驱动不匹配现象import torch 正常但第一次把张量搬到 GPU 时报错提示 CUDA driver version is insufficient。原因pip 安装的 PyTorch 默认带 CUDA 12.x 运行库而我机器的驱动只支持 CUDA 11.x。解决先去 NVIDIA 官网查显卡算力和驱动支持版本再到 PyTorch 官网用对应版本的安装命令重装。优先推荐装 CPU 版本做功能验证确认代码没逻辑问题后再换 GPU 版本省得两小时都在解决环境问题。6. 进阶落地用 QML 界面展示评分分布把训练结果导出成 HTML 报表训练完成的模型最终要给非技术背景的人使用或者自己快速预览一批图的打分情况。项目里的 ui 目录用 PyQt 加 QML 做了一套简单的可视化界面main.qml 负责界面布局context.py 负责把 Python 侧的推理结果暴露给 QML。核心交互是选择一张图片界面显示预测的平均分和各分数档的概率分布柱状图。# ui/context.py 中暴露给 QML 的调用示例 import os from PyQt5.QtCore import QUrl from PyQt5.QtQuick import QQuickView def show_image_with_score(view, image_path, score): abs_path os.path.abspath(image_path) view.rootContext().setContextProperty(currentImage, QUrl.fromLocalFile(abs_path)) view.rootContext().setContextProperty(scoreText, fScore: {score:.2f})参数说明QUrl.fromLocalFile 是必须的它能把本地绝对路径转成 QML Image 组件认识的 file:// 形式避免相对路径问题。scoreText 用 f-string 保留了两位小数界面上如果能同时显示分布柱状图建议把分布数组一次性传给 QML让前端用 ListView 渲染效率比逐条赋值高得多。报表输出方面项目里 outputs 目录会生成一个 report.htmltrainers.py 在每个 epoch 后把当前模型的验证指标插入到 HTML 模板中。这个做法的实用价值在于训练后不用再开 TensorBoard直接用浏览器打开 HTML 就能回顾整个训练过程。我习惯在 report.html 里额外记录每个 epoch 的预测示例图和真实分数这样后期排查问题时会方便很多。如果你需要批量评价一批图片不要在 Python 脚本里一张张调用 model.forward先把所有图路径读进列表一次性构造一个 batch按 32 或者 64 张一组送入 GPU。批量推理不仅能提高吞吐量还能减少 CPU 和 GPU 之间的数据拷贝次数。评价结果可以写成 CSV字段包括图片路径、预测分数、等级后面接一个简单的排序脚本就能输出一批图的好坏排行榜。这个项目我第一次跑的时候光数据集对齐就折腾了一个晚上原因就是 ava.txt 的列序理解错了。从那以后我每次接手新的数据集都会先打印几行样本确认字段含义和取值范围再写加载器。做图像美学评价这类标注形态多样的任务数据端的谨慎比模型端的调参更能决定最终效果。希望这篇拆解能帮你把这份源码顺利跑通。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Hindsight浏览器取证工具:还原访问轨迹与时间线 2026/9/28 17:58:18

Hindsight浏览器取证工具:还原访问轨迹与时间线

做取证分析这些年,我最大的感受是:很多案子根本轮不到高端内存取证、磁盘暗区恢复那一步,最崩溃的往往是最基础的问题——“这个人到底在电脑上访问了什么”。但只要机器上跑过Chrome或Chromium内核的浏览器,答案就藏在本地那几个…

阅读更多 →
金融系统核心设计:账户建模、幂等、对账与合规实践 2026/9/28 17:58:18

金融系统核心设计:账户建模、幂等、对账与合规实践

金融类项目从来都不是单纯的技术活。我在行业内摸爬滚打这么多年,手里做过的金融相关系统没有十个也有八个,从信贷审批到聚合支付,从账务清分到风控决策,几乎每一个项目上线初期都会被同一个问题困扰:为什么测试环境一…

阅读更多 →
金融服务业技术实现与合规实践指南 2026/9/28 17:58:18

金融服务业技术实现与合规实践指南

我无法基于当前输入生成符合要求的博文。原因如下:输入中仅提供了项目标题"financial-services",未提供任何实质性的项目正文、关键词列表或摘要描述;所谓“相关热搜词”和“最新网络热词”部分为空,未给出具体词汇&…

阅读更多 →
金融信息服务系统的技术架构与实践 2026/9/28 17:58:18

金融信息服务系统的技术架构与实践

我无法根据当前输入生成符合要求的博文内容。原因如下:输入中仅提供了项目标题"financial-services",未提供任何实质性的项目正文、关键词列表或摘要描述;所谓“相关热搜词”和“最新网络热词”部分为空,未给出具体词汇…

阅读更多 →
SequenceO1长上下文推理优化:Sketch Attention与STCA缓存筛选实战 2026/9/28 17:58:18

SequenceO1长上下文推理优化:Sketch Attention与STCA缓存筛选实战

1. 从标题到问题域:SequenceO1 到底在解决什么第一次看到“SequenceO1”这个名字,我下意识以为又是一个“把 Transformer 换个壳”的论文。真正把论文翻完、又把里面提到的 Sketch Attention、STCA、FlashSA 这几个模块对着代码结构捋了一遍之后&#xf…

阅读更多 →
J1900芯片4K硬解实战:Bay Trail平台VAAPI深度调优指南 2026/9/28 17:58:12

J1900芯片4K硬解实战:Bay Trail平台VAAPI深度调优指南

1. 为什么J1900这颗“古董CPU”还值得为4K解码较真?Intel J1900——Bay Trail平台的四核低功耗SoC,2013年底发布,TDP仅10W,基础频率2.0GHz,睿频2.42GHz,集成的是Intel HD Graphics(Gen7&#xf…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉