☰
图像美学与情感联合评价:GNN+Transformer双分支模型与部署
2026/10/22 13:27:29 网站建设 项目流程

这个标题里其实藏了两层很典型的毕设需求:一层是“我要做一个能跑的完整项目”,另一层是“我要在答辩时把原理讲清楚”。图像美学和情感联合评价,听起来像是两个任务拼在一起,但真正做起来就会发现,这不是简单的“多输出”,而是要让模型同时理解“画面好不好看”和“画面让人什么感觉”。我实际把这个课题完整走了一遍,从数据处理到模型训练,再到Flask部署演示,今天把整个链路拆开讲。

1. 选题拆解:图像美学与情感联合评价到底在做什么

1.1 先搞清楚两个任务分别是什么

图像美学评价,通俗说就是让模型像专业摄影师一样给照片打分。它关注的不是物体识别得准不准,而是构图是否平衡、色彩是否协调、光影是否有层次、主体是否突出。这类任务通常输出一个分值,或者一个“高/中/低”美学等级。

图像情感分析则是要判断图像唤起观看者什么样的情绪,比如愉悦、平静、悲伤、激动。这个任务更细微也更主观。同一张夕阳下的剪影,有人觉得浪漫,有人觉得孤独,模型要学的是大多数人面对画面时产生的主流情绪反应。

单拿一个出来,都有很多现成做法:美学评价可以微调一个ResNet回归分数,情感分析可以用预训练CNN做分类。但把它们放在一起,问题就来了:两个任务能不能共享同一个特征空间?目前主流的做法是各训各的模型,再想办法把结果合并。但合并不是相加,美学分高的图情感不一定积极,反过来更不一定。真正要做联合评价,核心是让模型在底层就同时学习两类信息,而不是在最终输出层硬拼。

1.2 联合评价的价值和应用场景

联合评价最有价值的地方在于:美学和情感并不是完全独立的。构图和色彩会直接影响观看者的情绪,情绪状态也会影响人对美学质量的判断。如果分开建模,两个模型各学各的,就丢失了这种相互作用。

实际场景中,联合评价能做的事比单独任务多得多。内容平台可以用它筛选封面图,既要视觉质量高,又要情绪感染力强;摄影后期工具可以针对照片同时输出美学建议和情绪特征;设计素材库可以用它做检索排序,找“明亮温暖的高质量图”就比单查“高质量”或者单查“温暖”可靠得多。

毕设如果只做一个美学评分模型,功能太单薄,答辩时空间也有限。加上情感分支后,演示效果提升一大截:上传一张图,界面同时给出美学分和情感标签,视觉上就很“能打”。

1.3 为什么不是简单的多任务

联合评价看起来像是一个模型输出两个结果,实际操作中坑很多。直接共享骨干网络,两个任务会互相打架:美学任务倾向于关注全局构图平衡,情感任务更关注局部语义区域(比如人脸表情、主题物体),共享最后一层特征往往会牺牲其中一个任务的效果。

所以我在设计时没有简单地把两个分类头接到同一个特征后面,而是给每个任务留了独立分支,让它们各自从同一个多尺度特征中提取自己需要的信息,再通过一个融合层把两类特征合并用于最终联合打分。这样做的好处是有控制权:想让模型偏向哪个任务,调节两个分支的权重就行;要做消融实验,砍掉任意一个分支就能对比结果。

2. 技术方案解析:为什么是GNN加Transformer而不是其他组合

2.1 图像不是一整块,而是互相联系的区域集合

要理解选型逻辑,要先把图像看成什么。如果只把图像当成一个整体特征向量,问题会简单很多,但美学和情感恰恰不是这种简单结构。

构图这件事,本质是画面中各元素之间的空间关系。主体放在黄金分割点附近,背景色与前景色形成呼应,这些都是“区域之间的关系”。Transformer在处理这类关系上非常擅长——它通过自注意力机制对全局所有区域两两建模,天然适合表达长距离依赖。

情感分析稍微不一样。它要求模型识别出“什么人”“什么物”出现,还要理解这些对象和背景之间的交互。比如一个孩子在海边奔跑,提供情感的不仅有孩子这个主体,还有海、天空、光线这些背景元素共同形成的语境。这种“主体-背景关系”建模,用图神经网络更自然:主体和重要区域作为节点,它们之间的关系作为边,消息传递过程就是把场景语境逐渐聚合到主体节点上。

2.2 Transformer选型:ViT还是改良版

Transformer在图像上最直接的实现是ViT,把图像切成固定大小的patch,每个patch展平后加位置编码,丢进标准Transformer编码器。ViT的优点是结构简单,一个CLS token在顶层聚合全局信息,非常适合美学这种依赖全局的任务。

但我没有直接拿ViT当唯一主干,原因有两点:第一,ViT需要大规模数据预训练,毕设的数据量如果相对有限,从头训练容易过拟合;第二,ViT的注意力是均匀分配到所有patch上的,可美学和情感关注的点其实非常稀疏,可能只有画面中某几个区域起决定作用。

所以我的方案是:用卷积网络的前几层提取较为丰富的底层特征,然后只把高层的特征图切成patch序列送入Transformer。这样既保留了CNN局部归纳偏置,又获得了Transformer的全局建模能力。这部分在后面的模型架构里展开说。

2.3 GNN选型:图结构从哪来

GNN不能凭空工作,得先把图像变成图。这一步是整个方案里最需要解释清楚的部分。我采用的方法:把CNN特征图的每个空间位置视为一个节点,再根据两种关系建边。

一方面,空间位置相邻的节点建边,这样图可以保留局部连续性;另一方面,通过特征相似度建边,哪怕两个区域在空间上离得远,只要色彩或语义特征相似,也建立连接。这样做的好处是:比如画面左下角和右上角都有一抹同样的夕阳橙色,它们会因为在特征空间里靠得近而相互传递信息,模型就能学到“整体色调一致”这种美学规则。

图网络部分我用了图注意力(GAT)机制,而不是简单的GCN。原因很直接:GCN的邻接矩阵是固定的,每个邻居的影响权重是预设的;而GAT可以动态学习“哪个区域对当前区域更重要”。这在情感任务里特别关键,对面部表情区域的关注权重就应该远高于背景天空,但具体高多少,要数据说了算。

2.4 为什么不是纯粹的大模型方案

肯定有人会问:现在都有多模态大模型了,直接拿CLIP或者类BLIP模型做美学和情感分析不就行了?

我试过用图像-文本匹配的预训练特征做迁移。效果确实说得过去,尤其是情感这类偏向语义的任务。但有两个硬伤:第一,这类模型参数量很大,推理速度在普通显卡上很不理想,后面要接Flask做实时Web演示,每张图3秒起步的体验很糟糕;第二,大模型的特征是通用语义特征,针对美学评分这种细腻的回归任务,反而没有小型图模型拟合得准。

从毕设角度考虑,要的是“能讲清楚、能跑得快、能自己动手改进”的模型,而不是越复杂越好。GNN加Transformer的组合,每一层都有明确的解释,答辩时能逐个讲透,这是我的核心考量。

3. 系统架构设计:从模型训练到Flask部署全链路

3.1 整体流程图解

整个系统可以分成四个环环相扣的阶段。数据准备阶段负责把原始图片拆成训练集和验证集,做好标签对齐和增广;模型训练阶段跑GNN与Transformer双分支结构,产出权重文件;模型推理封装阶段把训练好的网络结构加载进来,去掉训练专用组件,做成一个纯前向的函数;Web服务阶段用Flask做一个简单的上传和展示界面,接受图片请求,返回美学分和情感标签。

我当时把每个阶段拆成了独立模块,这样最长链条的好处是:训练阶段崩了不涉及Web代码,Web阶段要加功能也不影响模型部分。很多毕设项目写着写着就变成一个大脚本,后面排查问题非常痛苦。

3.2 Flask为什么是毕设Web框架的优选

选择Flask而不选Django,核心原因是成本低。Django自带Admin后台、ORM、中间件,对毕设来说很多用不上,反而要花时间去理解它的目录规范。Flask只需要一个app.py,路由写起来直白,模板渲染也简单,适合快速搭建演示界面。

另一点是模型接入方便。PyTorch训练好的模型,只需要在Flask启动时加载一次,路由函数里调用模型推理,再返回JSON就行。Flask的轻量特点这时候反而是优点:不需要额外配置,开发服务器足够支撑答辩演示的并发量。

Flask版本上要注意,Flask 3.x和2.x在路由写法上没有本质区别,但依赖的Werkzeug版本有差异,安装时直接用pip install flask就行,别手动锁定过老版本。模板方面,我用的是Jinja2默认语法,简单的静态页面完全可以不写JavaScript复杂度太高的逻辑。

3.3 目录结构与核心代码组织

我的项目目录长这样:

project/ ├── app.py # Flask入口 ├── models/ │ ├── backbone.py # CNN特征提取 │ ├── transformer_branch.py # Transformer分支 │ ├── gnn_branch.py # GNN分支 │ └── joint_model.py # 联合模型 ├── utils/ │ ├── dataset.py # 数据加载 │ ├── transforms.py # 预处理与增广 │ └── inference.py # 推理封装 ├── checkpoints/ # 训练好的权重 ├── templates/ │ └── index.html # 上传页面 ├── static/ # CSS与前端资源 └── configs/ └── config.yaml # 训练和推理配置

app.py里加载模型的核心代码不长,示意如下:

from flask import Flask, request, jsonify, render_template from models.joint_model import JointModel from utils.inference import preprocess, predict app = Flask(__name__) model = JointModel() model.load_state_dict(torch.load("checkpoints/best.pth", map_location="cpu")) model.eval() @app.route("/") def index(): return render_template("index.html") @app.route("/predict", methods=["POST"]) def predict_route(): img = request.files["image"] tensor = preprocess(img) score, emotion = predict(model, tensor) return jsonify({"aesthetic_score": score, "emotion_label": emotion})

这里的重点是predict函数要做完整的预处理,包括缩放、归一化、转张量、去归一化等步骤。很多同学在Jupyter里测试模型没问题,一接到Web上就报错,十有八九就是训练时的预处理和部署时的预处理不一致。

4. 核心算法拆解:双分支联合评价模型的实现细节

4.1 特征提取:用CNN生成区域特征序列

模型入口是一个CNN骨干网络,我选了ResNet的早期层做特征提取。输入图像统一缩放到224乘224,主干网络去掉最后的全局池化和全连接层,输出一个特征图,空间尺寸是14乘14,通道数是1024。这个特征图的每个空间位置,可以理解成原图上一块对应区域的语义特征。

我限制在这个骨干下,不用太深的版本,避免图像细节丢失过快。14乘14的特征图分辨率,对于区域级关系建模来说刚好:能区分画面中的主体和主要背景,又不会把特征切得太碎。

这一层要特别注意的是:骨干网络用ImageNet预训练权重初始化,前几层参数在训练时冻结,只微调最后两个stage。否则一个小数据集从头训练卷积层,梯度不稳定,模型很难收敛。

4.2 Transformer分支:从特征图到全局美学特征

Transformer分支接收的是CNN输出特征图,把它看成196个patch token,每个token维度是1024。加上可学习的位置编码后,送入标准Transformer编码器。编码器层数设为4层,注意力头数8,隐藏维度保持512。维度降低用了一个线性投影层,控制在数据规模下的模型复杂度。

编码器输出时,我取两类特征:CLS token向量和所有token的均值池化向量。CLS token擅长聚合全局语义,均值池化则更平滑稳健。两个向量拼接后过一个回归头,输出美学分。美学分支的监督信号是平均意见分,回归目标经过零均值归一化。

这里有一个实操细节:ViT类结构对位置编码很敏感。不同的分辨率输入会改变patch数量,如果训练时固定224,推理时上传一张大图没有预处理,位置编码就对不上,性能会掉。所以推理管线里必须有严格的中心裁剪和缩放,保证进来的图一定是224乘224。

4.3 GNN分支:构建区域图并进行消息传递

GNN分支的输入同样是CNN特征图,但它不直接铺平给Transformer,而是先构建图。我将14乘14特征图看作196个节点,节点特征来自Transformer分支第一层的输入特征,保持一致便于后续融合。边关系分为两类:空间相邻边和特征相似边。

空间相邻边连接欧氏距离在两步以内的节点,用于保持局部结构;特征相似边则是全网搜索,与当前节点特征余弦相似度超过设定阈值的节点连边。实际操作中,边太多会导致过平滑,所有节点特征趋于一致。所以我给每个节点只保留相似度最高的5个远程邻居,限制全局连接的密度。

图注意力层的消息传递可以理解为:每个节点聚合邻居传来的信息,但每条边的权重由注意力机制计算得出。注意力分数是基于源节点和邻居节点特征的机器学习出的函数,不是手工指定的。GNN分支迭代两轮消息传递,输出的节点特征再经过全局平均池化,得到情感特征向量。

4.4 联合评价层:如何在两个分支之间融合

有了两个分支的特征,融合策略不搞叠加。我把美学特征向量和情感特征向量拼接,得到联合表示,再过一个双层MLP,输出两个头:一个回归头输出美学分,一个分类头输出情感类别分布。

训练损失由三部分组成:

L_total = L_aesthetic + lambda1 * L_emotion + lambda2 * L_consistency

美学损失用Smooth L1误差,拟合回归目标比MSE更抗离群点。情感损失用交叉熵。一致性损失是我加的一个小约束:计算美学分布和情感高维特征之间的协方差匹配项,促进两个任务不各自跑偏。

lambda系数不需要调得太精确,我当时试了若干组,lambda1设为0.5,lambda2设为0.1,效果比较稳定。如果想要动态加权,可以用不确定性加权方式让网络自己学习两个任务的权重,但答辩时解释起来要多费口舌,我还是用手动系数。

5. 训练实战与调参记录:效果从及格到优秀的完整过程

5.1 训练数据、预处理与标签对齐

数据方面我用了公开的美学评分图像集,每张图片附一个平均美学分;又整理了一个情感标注集,把图像映射到若干种情感类别之一。由于两个数据集的图像来源不一致,我做联合训练时采用配对采样策略:每个batch从美学数据集中采一半,从情感数据集中采一半,两个数据集共享同一个CNN骨干,但各自的分支损失只对各自样本生效。

这样设计是为避免两个任务的训练样本互相干扰。如果手里有同时带美学分和情感标签的公开数据集,那就更理想了,可以省去配对的麻烦。不过现实中这种双标注资源非常有限,做成半独立联合训练是更现实的路径。

预处理管线包括随机水平翻转、随机裁剪、色彩扰动。情感任务对颜色和构图比较敏感,我还加了轻微的颜色抖动。要注意的是美学和情感对水平翻转的容忍度不一样,例如左三分线构图翻转后美学分通常不变,但某些场景的情感语义可能受影响,所以训练时美学任务的数据用翻转,情感任务的数据不做翻转,这是实践中试出来的办法。

5.2 超参数配置

我列出完整的训练配置表供参考:

参数项取值说明
输入尺寸224x224兼顾细节和速度
Batch Size32单卡可跑,显存占用适中
优化器AdamW权重衰减0.05
学习率1e-4主干部分用较小学习率
主干学习率系数0.1避免破坏预训练特征
总轮数40配合余弦退火
Warmup轮数2前两轮线性升温
Dropout0.3分类头与融合层
标签平滑0.05情感分类防止过拟合
混合精度开启显存和速度都有明显提升

训练循环里一个经常被忽略的点是梯度裁剪。GNN分支的图注意力计算中偶尔会出现较大梯度,一旦爆炸,损失会跳成NaN。我加了一个全局梯度范数裁剪,阈值设为1.0,训练稳定性明显提升。

训练过程用早停法判断最优模型,监控验证集的美学SRCC和情感准确率的加权组合,这个指标不再上升时就停止训练。日志记录采用简单的print加写文件方式,不引入额外日志库。

5.3 评估指标怎么看

美学任务用回归指标,情感分类用分类指标,两者放在一起评价要考虑权衡。

评估指标作用
SRCC斯皮尔曼秩相关系数,判断预测排序和真实排序是否一致
PLCC皮尔逊线性相关系数,判断预测值和真实值的线性相关程度
MAE平均绝对误差,直观反映打分偏差
准确率情感分类的正确率
F1 Score情感分类的调和平均,考虑类别不均衡

我做了一个超过基线的对比测试:单独训练一个ResNet回归模型作为美学基线,单独训练一个ResNet分类模型作为情感基线,再与联合模型对比。引联合模型的美学SRCC比单独模型高约若干个百分点,情感准确率也略有提升。提升主要来自融合特征中多了一个维度的监督信息,而不是网络参数变多。

5.4 从头训练失败后的调优路线

第一次训练时我直接全量训练联合模型,结果很差:美学Loss下降慢,情感准确率也不高。后来定位到几个问题:

一是特征提取器没有迁移学习,随机初始化的ResNet在有限的数据上学到的底层特征质量不够。换用预训练权重,只微调浅层之后,指标立刻上来。二是情感分类的类别存在严重不均衡。某个高频类别占到四成样本,模型会倾向于把所有样本都判成它。解决方法是做类别重加权,给低频类别更高的损失权重。三是图结构的相似度阈值定得太紧,很多节点没有邻居,消息传递效果微弱。我调低阈值并把每个节点的远程邻居数上限从3提高到5之后,GNN分支的作用才真正体现出来。

6. 常见问题速查:答辩和开发中容易踩的坑

6.1 数据侧问题

数据量不够是最常见的问题。联合任务里如果某个任务的标注图像只有几千张,过拟合几乎是必然的。我建议的做法是先用预训练特征做一层轻量分类或回归,验证当前数据量下模型能跑通,再用完整模型。

标签错位也很容易发生。处理两个来源不同的数据集时,务必要写一个脚本统计每张图的文件名和标签能否对应上,尤其是经过排序、去重之后容易乱序。

6.2 训练侧问题

Loss不下降且一直高位,先别急着调模型结构,检查学习率。我遇到过某次把学习率设为5e-3直接导致GNN分支梯度爆炸,降到1e-4就正常了。

训练时验证集指标波动大,要多看验证集多个epoch的EMA平滑曲线,不要因为一个epoch高就给论断。我保存模型的策略是每轮都记录验证指标,最终挑选表现最好且前后轮差异不剧烈的那个权重。

6.3 部署侧问题

把模型接到Flask后,最典型的错误是推理模式没开、归一化参数错了。PyTorch模型一定要调用model.eval()关闭Dropout和BatchNorm更新,否则同一张图每次请求结果不同。归一化参数则要和训练时完全一致,否则输入分布偏移会导致打分整体偏差。

接口响应速度慢的话,可以做的优化有两个:一是把输入图片先缩放成小尺寸后直接送模型,二是用ONNX导出模型做前向推理。ONNX在CPU上比PyTorch原生前向通常快不少,而且部署时不依赖PyTorch环境。不过导出过程要处理好动态输入尺寸问题,建议固定输入尺寸。

6.4 答辩高频问题怎么回答

答辩老师大概率会问三个问题。

第一问:为什么选择GNN加Transformer组合?回答逻辑从任务特点出发:美学评价需要全局构图信息的交互,Transformer的自注意力机制适合处理这种全局关系;情感评价需要理解不同语义区域之间的关联,图网络天然适合对区域及关系建模。两者是互补关系,一个偏全局建模,一个偏关系建模。

第二问:联合评价比分开做如何验证更好?回答中直接展示消融实验:去掉情感分支只剩美学分支、去掉美学分支只剩情感分支、保留联合训练的完整模型,分别在两个评测指标上做对比。完整模型至少在一个或两个指标上优于单任务模型,这就是多任务联合学习的价值证明。

第三问:模型对图像的什么特征更敏感?这个问题需要准备可视化支撑。我用图注意力权重作为区域重要性分数,渲染成热力图叠加到原图上。从热力图可以直观看到模型在打分时把注意力集中在人脸、主体物体、高对比度区域等位置,这是答辩的加分项。

6.5 让演示更完整的两个小技巧

第一个技巧是给Web页面加一个简单的“对比模式”:同时上传两组图片,并把两组评分放在一起对比展示。这个改动看似小,答辩现场效果非常好,能让老师一眼看到模型对不同图像的判别力。

第二个技巧是在页面下方展示热力图缩略图。深度学习模型最容易被质疑“黑盒”,只要能把关注区域可视化出来,就比干巴巴的数字更有说服力。

回到这个课题本身,我最想分享的体会是:毕设选题不怕老,怕的是把别人代码跑通就当成自己的成果。图像美学与情感联合评价这个方向,既有清晰的评价指标,又有天然的演示效果,还方便做消融实验和可视化,非常适合作为深度学习方向的毕业设计。实际做完一遍,把GNN和Transformer的配合逻辑、联合训练的数据配比、Web部署的工程细节都吃透了,答辩时再刁钻的问题也不会让你卡壳。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询