基于深度学习的闭集 + 开集狗品种分类,支持 ViT / ResNet / EfficientNet / CLIP 多架构对比
细粒度图像分类(Fine-Grained Visual Categorization)是计算机视觉中的经典难题。狗品种识别作为其代表性任务,类间差异小、类内差异大——比如金毛和拉布拉多在毛色、体型上极为相似,而同一品种的不同个体可能因拍摄角度、光照、年龄等因素差异显著。
本项目围绕以下问题展开:
flowchart TD
INPUT["🖼️ 输入图片"]
subgraph CLOSED["🔒 第一级:闭集分类(ViT)"]
C1["ViT-L/16 预训练"]
C2["加载 checkpoint 权重"]
C3["Softmax → 100 类概率"]
C1 --> C2 --> C3
end
subgraph OPEN["🔓 第二级:开集分类(CLIP)"]
O1["CLIP 图像编码器"]
O2["112 模板文本编码"]
O3["余弦相似度 + 双阈值"]
O1 --> O2 --> O3
end
INPUT --> CLOSED
CLOSED -->|"conf ≥ 0.90 且 margin ≥ 0.2"| RESULT_CLOSED["✅ 返回品种名 (closed)"]
CLOSED -->|"置信度不足"| OPEN
OPEN -->|"sim ≥ 0.30 且 margin ≥ 0.10"| RESULT_OPEN["✅ 返回品种名 (open)"]
OPEN -->|"相似度不足"| RESULT_UNKNOWN["❌ 返回 unknown"]
classDef closed fill:#d1eafa,stroke:#0277bd,stroke-width:2px,rx:10,ry:10
classDef open fill:#ffe0b2,stroke:#ef6c00,stroke-width:2px,rx:10,ry:10
classDef result fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px,rx:10,ry:10
classDef unknown fill:#ffcdd2,stroke:#c62828,stroke-width:2px,rx:10,ry:10
class CLOSED closed
class OPEN open
class RESULT_CLOSED,RESULT_OPEN result
class RESULT_UNKNOWN unknown
unknown。为什么用两级? ViT 在已知品种上精度高,但遇到未知品种会"强行分类"(深度学习的过度自信问题)。CLIP 的零样本对比能力天然适合拒绝未知类别,两者互补。
Stanford 数据集用于训练和验证,模型在该数据集上的表现可能因"见过"而偏高。Atharva 是一个完全独立的外部数据集,图片采集条件、品种命名方式、图片质量都与 Stanford 不同。在 Atharva 上评估能检验模型的泛化能力,避免"在自己出的卷子上考高分"的假象。
本项目实现了四种骨干网络的对比实验:
flowchart LR
subgraph MODELS["模型架构"]
V["ViT-L/16"]
R["ResNet50 + Attention"]
P["ResNet50 V2(纯)"]
E["EfficientNet-B0"]
end
V -->|"src/"| D1["100 类闭集(不用 120 类是<br/>便于后面开集扩展)"]
R -->|"src_resnet/"| D2["120 类闭集"]
P -->|"src_resnet_pure/"| D3["120 类闭集"]
E -->|"src_efficientnet/"| D4["120 类闭集"]
classDef model fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,rx:8,ry:8
class V,R,P,E model
| 模型 | 骨干网络 | 参数量 | 特点 |
|---|---|---|---|
| ViT-L/16 | Vision Transformer (Large) | ~307M | 全局自注意力,擅长捕捉长距离依赖 |
| ResNet50 + Attention | ResNet50 V2 + 自注意力模块 | ~25M | 在残差网络基础上加入空间注意力机制 |
| ResNet50 V2 | ResNet50 (纯) | ~25M | 经典基线,结构简单稳定 |
| EfficientNet-B0 | EfficientNet | ~5.3M | 轻量化架构,参数量最少,效率最高 |
所有模型均使用 ImageNet 预训练权重,冻结主干网络,仅训练分类头。
vit-dog/
│
├── input/ # 数据集
│ ├── Stanford-Dogs-Dataset/ # Stanford 数据集
│ └── Atharva-Dog-Breeds-Dataset/ # Atharva 数据集
│
├── checkpoint/ # 训练好的模型权重
│ ├── ViT-top80.pt # ViT (前 80 类)
│ ├── ViT-top100.pt # ViT (前 100 类)
│ └── ViT-full120.pt # ViT (全部 120 类)
│
├── src/ # ViT-L/16 闭集分类
│ ├── model.py # PretrainViT 模型定义
│ ├── dataset.py # 数据集加载
│ ├── train.py # 训练脚本
│ ├── inference.py # 推理 + 生成提交文件
│ └── config.py # 路径与超参数配置
│
├── src_resnet/ # ResNet50 + 自注意力
├── src_resnet_pure/ # ResNet50 V2(纯)
├── src_efficientnet/ # EfficientNet-B0
├── src_vit_org/ # ViT 原始版本(参考)
│
├── open_finegrained_stanford/ # 开集系统(Stanford 评估)
├── open_finegrained_Atharva_demo/ # 开集系统(Atharva 评估)
├── open_finegrained_stanford_param_analysis/ # 阈值参数分析
│
├── FG-CLIP/ # FG-CLIP 源码仓库
├── fg-clip-base/ # FG-CLIP HuggingFace 推理包
└── src_FG_CLIP/ # FG-CLIP 实验代码
开集分类器不使用 Softmax,而是基于 CLIP 的零样本对比学习:
flowchart LR
subgraph OFFLINE["离线阶段:文本特征预计算"]
T1["112 个文本模板"]
T2["品种名填入模板"]
T3["CLIP 文本编码器"]
T4["取均值 → 文本特征向量"]
T1 --> T2 --> T3 --> T4
end
subgraph ONLINE["在线阶段:推理"]
I1["输入图片"]
I2["CLIP 图像编码器"]
I3["余弦相似度"]
I4["双阈值决策"]
I1 --> I2 --> I3 --> I4
end
T4 -->|"预计算好的特征矩阵"| I3
classDef offline fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,rx:8,ry:8
classDef online fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:8,ry:8
class OFFLINE offline
class ONLINE online
模板覆盖多个维度,提升文本特征的鲁棒性:
| 维度 | 示例 |
|---|---|
| 基础 | a photo of a {} |
| 场景 | a photo of a {} running on the green grass |
| 动作 | a photo of a {} wagging its tail and barking happily |
| 外观 | a photo of a fluffy {} with golden fur and floppy ears |
| 状态 | a photo of a wet {} after playing in the puddle |
| 交互 | a photo of a {} playing fetch with its owner in the park |
| 视角 | a macro photo of the {}'s paw pads and tiny claws |
OPEN_SIM_THRESHOLD = 0.30):最大余弦相似度必须超过此值OPEN_MARGIN_THRESHOLD = 0.10):top1 与 top2 的差值必须超过此值两个条件同时满足才认为是"已知品种",否则判为 unknown。
# 创建 conda 环境
conda create -n Static_Learn python=3.10 -y
conda activate Static_Learn
# 安装依赖
pip3 install typing-extensions==4.10.0
pip3 install torch torchvision torchaudio --index-url https://mirrors.nju.edu.cn/pytorch/whl/cu126
pip install pandas scikit-learn pillow tqdm matplotlib transformers gradio fastapi uvicorn
cd src/
python train.py
cd src/
python inference.py
cd open_finegrained_Atharva_demo/
python run.py
cd open_finegrained_Atharva_demo/demo/
# 需先修改 open_finegrained_Atharva_demo/config.py 中的 SRC_VIT_MODEL_PATH 参数
# 为指向 ViT-top100.pt 的权重路径
python gradio_app.py
# 浏览器访问 http://127.0.0.1:7861
| 文件 | 说明 |
|---|---|
ViT-top80.pt | ViT-L/16,前 80 个品种(用于参数分析) |
ViT-top100.pt | ViT-L/16,前 100 个品种(最终权重) |
ViT-full120.pt | ViT-L/16,全部 120 个品种(中期模型,纯闭集分类) |
16 commits
1 commits
Python
99.3%
基于深度学习的闭集 + 开集狗品种分类,支持 ViT / ResNet / EfficientNet / CLIP 多架构对比
细粒度图像分类(Fine-Grained Visual Categorization)是计算机视觉中的经典难题。狗品种识别作为其代表性任务,类间差异小、类内差异大——比如金毛和拉布拉多在毛色、体型上极为相似,而同一品种的不同个体可能因拍摄角度、光照、年龄等因素差异显著。
本项目围绕以下问题展开:
flowchart TD
INPUT["🖼️ 输入图片"]
subgraph CLOSED["🔒 第一级:闭集分类(ViT)"]
C1["ViT-L/16 预训练"]
C2["加载 checkpoint 权重"]
C3["Softmax → 100 类概率"]
C1 --> C2 --> C3
end
subgraph OPEN["🔓 第二级:开集分类(CLIP)"]
O1["CLIP 图像编码器"]
O2["112 模板文本编码"]
O3["余弦相似度 + 双阈值"]
O1 --> O2 --> O3
end
INPUT --> CLOSED
CLOSED -->|"conf ≥ 0.90 且 margin ≥ 0.2"| RESULT_CLOSED["✅ 返回品种名 (closed)"]
CLOSED -->|"置信度不足"| OPEN
OPEN -->|"sim ≥ 0.30 且 margin ≥ 0.10"| RESULT_OPEN["✅ 返回品种名 (open)"]
OPEN -->|"相似度不足"| RESULT_UNKNOWN["❌ 返回 unknown"]
classDef closed fill:#d1eafa,stroke:#0277bd,stroke-width:2px,rx:10,ry:10
classDef open fill:#ffe0b2,stroke:#ef6c00,stroke-width:2px,rx:10,ry:10
classDef result fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px,rx:10,ry:10
classDef unknown fill:#ffcdd2,stroke:#c62828,stroke-width:2px,rx:10,ry:10
class CLOSED closed
class OPEN open
class RESULT_CLOSED,RESULT_OPEN result
class RESULT_UNKNOWN unknown
unknown。为什么用两级? ViT 在已知品种上精度高,但遇到未知品种会"强行分类"(深度学习的过度自信问题)。CLIP 的零样本对比能力天然适合拒绝未知类别,两者互补。
Stanford 数据集用于训练和验证,模型在该数据集上的表现可能因"见过"而偏高。Atharva 是一个完全独立的外部数据集,图片采集条件、品种命名方式、图片质量都与 Stanford 不同。在 Atharva 上评估能检验模型的泛化能力,避免"在自己出的卷子上考高分"的假象。
本项目实现了四种骨干网络的对比实验:
flowchart LR
subgraph MODELS["模型架构"]
V["ViT-L/16"]
R["ResNet50 + Attention"]
P["ResNet50 V2(纯)"]
E["EfficientNet-B0"]
end
V -->|"src/"| D1["100 类闭集(不用 120 类是<br/>便于后面开集扩展)"]
R -->|"src_resnet/"| D2["120 类闭集"]
P -->|"src_resnet_pure/"| D3["120 类闭集"]
E -->|"src_efficientnet/"| D4["120 类闭集"]
classDef model fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,rx:8,ry:8
class V,R,P,E model
| 模型 | 骨干网络 | 参数量 | 特点 |
|---|---|---|---|
| ViT-L/16 | Vision Transformer (Large) | ~307M | 全局自注意力,擅长捕捉长距离依赖 |
| ResNet50 + Attention | ResNet50 V2 + 自注意力模块 | ~25M | 在残差网络基础上加入空间注意力机制 |
| ResNet50 V2 | ResNet50 (纯) | ~25M | 经典基线,结构简单稳定 |
| EfficientNet-B0 | EfficientNet | ~5.3M | 轻量化架构,参数量最少,效率最高 |
所有模型均使用 ImageNet 预训练权重,冻结主干网络,仅训练分类头。
vit-dog/
│
├── input/ # 数据集
│ ├── Stanford-Dogs-Dataset/ # Stanford 数据集
│ └── Atharva-Dog-Breeds-Dataset/ # Atharva 数据集
│
├── checkpoint/ # 训练好的模型权重
│ ├── ViT-top80.pt # ViT (前 80 类)
│ ├── ViT-top100.pt # ViT (前 100 类)
│ └── ViT-full120.pt # ViT (全部 120 类)
│
├── src/ # ViT-L/16 闭集分类
│ ├── model.py # PretrainViT 模型定义
│ ├── dataset.py # 数据集加载
│ ├── train.py # 训练脚本
│ ├── inference.py # 推理 + 生成提交文件
│ └── config.py # 路径与超参数配置
│
├── src_resnet/ # ResNet50 + 自注意力
├── src_resnet_pure/ # ResNet50 V2(纯)
├── src_efficientnet/ # EfficientNet-B0
├── src_vit_org/ # ViT 原始版本(参考)
│
├── open_finegrained_stanford/ # 开集系统(Stanford 评估)
├── open_finegrained_Atharva_demo/ # 开集系统(Atharva 评估)
├── open_finegrained_stanford_param_analysis/ # 阈值参数分析
│
├── FG-CLIP/ # FG-CLIP 源码仓库
├── fg-clip-base/ # FG-CLIP HuggingFace 推理包
└── src_FG_CLIP/ # FG-CLIP 实验代码
开集分类器不使用 Softmax,而是基于 CLIP 的零样本对比学习:
flowchart LR
subgraph OFFLINE["离线阶段:文本特征预计算"]
T1["112 个文本模板"]
T2["品种名填入模板"]
T3["CLIP 文本编码器"]
T4["取均值 → 文本特征向量"]
T1 --> T2 --> T3 --> T4
end
subgraph ONLINE["在线阶段:推理"]
I1["输入图片"]
I2["CLIP 图像编码器"]
I3["余弦相似度"]
I4["双阈值决策"]
I1 --> I2 --> I3 --> I4
end
T4 -->|"预计算好的特征矩阵"| I3
classDef offline fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,rx:8,ry:8
classDef online fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:8,ry:8
class OFFLINE offline
class ONLINE online
模板覆盖多个维度,提升文本特征的鲁棒性:
| 维度 | 示例 |
|---|---|
| 基础 | a photo of a {} |
| 场景 | a photo of a {} running on the green grass |
| 动作 | a photo of a {} wagging its tail and barking happily |
| 外观 | a photo of a fluffy {} with golden fur and floppy ears |
| 状态 | a photo of a wet {} after playing in the puddle |
| 交互 | a photo of a {} playing fetch with its owner in the park |
| 视角 | a macro photo of the {}'s paw pads and tiny claws |
OPEN_SIM_THRESHOLD = 0.30):最大余弦相似度必须超过此值OPEN_MARGIN_THRESHOLD = 0.10):top1 与 top2 的差值必须超过此值两个条件同时满足才认为是"已知品种",否则判为 unknown。
# 创建 conda 环境
conda create -n Static_Learn python=3.10 -y
conda activate Static_Learn
# 安装依赖
pip3 install typing-extensions==4.10.0
pip3 install torch torchvision torchaudio --index-url https://mirrors.nju.edu.cn/pytorch/whl/cu126
pip install pandas scikit-learn pillow tqdm matplotlib transformers gradio fastapi uvicorn
cd src/
python train.py
cd src/
python inference.py
cd open_finegrained_Atharva_demo/
python run.py
cd open_finegrained_Atharva_demo/demo/
# 需先修改 open_finegrained_Atharva_demo/config.py 中的 SRC_VIT_MODEL_PATH 参数
# 为指向 ViT-top100.pt 的权重路径
python gradio_app.py
# 浏览器访问 http://127.0.0.1:7861
| 文件 | 说明 |
|---|---|
ViT-top80.pt | ViT-L/16,前 80 个品种(用于参数分析) |
ViT-top100.pt | ViT-L/16,前 100 个品种(最终权重) |
ViT-full120.pt | ViT-L/16,全部 120 个品种(中期模型,纯闭集分类) |
16 commits
1 commits
Python
99.3%