字体分类 ViT 微调项目

基于 DINOv2 微调的字体分类系统,能够识别 214 种字体。从数据生成、模型训练到推理部署全流程自研。

训练状态:已完成(GPU: NVIDIA L40S,20 epochs)


最终训练结果

服务器与训练配置

项目 配置
GPU NVIDIA L40S
显存 44.4 GB
模型路径 models/dinov2-base(本地离线)
混合精度 (AMP) 开启
batch_size 64
学习率 (lr) 3e-5
权重衰减 (wd) 0.01
Label Smoothing (ls) 0.1
最大 epochs 20
Early Stopping patience=5

数据与迭代

项目 数值
训练样本 63,690
验证样本 11,210
每 epoch batch 数 996 (63,690 ÷ 64)
每 epoch 耗时 ~60s
总训练耗时 ~20 分钟 (20 epochs)
显存占用 ~1.8 GB

训练结果

指标 数值
val_acc (Top-1) 67.74%
train_acc 86.1%
train_loss (Epoch 20) 1.39
val_loss (Epoch 20) 1.94

推理测试结果

测试样本

测试样本 字体类型 Top-1 预测 置信度 结果
Overpass_Regular/0036.png 几何无衬线 Figtree_Regular 60.15% Top-2 正确(Overpass 6.49%)
Dancing_Script_Regular/0022.png 手写体 Dancing_Script_Regular 88.12% ✅ Top-1 正确
Playfair_Display_Regular/0065.png 衬线体 Playfair_Display_Regular 97.79% ✅ Top-1 正确

推理表现总结

  • 特殊字体(手写体、衬线体、等宽体等):识别准确率很高,Top-1 置信度可达 88%~98%
  • 普通无衬线体(Roboto、Inter、Overpass 等):风格相近易混淆,Top-1 可能不准,但正确答案通常出现在 Top-1~Top-5 内
  • 使用建议:业务上可返回 Top-5 供用户选择,或对 Top-1 置信度低于某阈值时展示 Top-K 候选

一、技术选型与理由

Backbone: DINOv2-base (facebook/dinov2-base)

  • Meta 开源的自监督视觉基础模型,在 1.42 亿张图片上预训练
  • 相比 ViT-ImageNet 分类预训练,DINOv2 学到的是更通用的视觉特征,对字体笔画、结构等细粒度特征有更好的表征能力
  • 86M 参数,推理速度适中,适合部署

为什么不用 CLIP / ResNet / 传统 CNN?

  • CLIP:偏向图文对齐,字体分类是纯视觉任务,DINOv2 更合适
  • ResNet:对全局特征建模较弱,ViT 的 self-attention 能捕捉字体的全局结构特征(如字体的统一风格)
  • 传统 CNN:需要从头训练,数据需求量大得多

二、数据工程

数据来源

从 Google Fonts 批量下载 216 种字体文件(含 Regular/Bold 变体),使用 PIL 渲染生成训练数据。

数据规模

  • 每种字体生成 350 张图片
  • 总计约 75,000 张(训练 63,690 + 验证 11,210)
  • 训练/验证拆分比例:85% / 15%
  • 多进程并行生成,利用全部 CPU 核心

tests/font_vit/
├── fonts/ ✅ 216 个字体文件
├── dataset/ ✅ 已生成
│ ├── train/ ✅ 214 个类别
│ └── val/ ✅ 216 个类别
├── output/ ✅ 训练输出
│ ├── best_model.pt ✅ 347MB
│ └── class_mapping.json ✅
├── download_fonts.py ✅
├── generate_dataset.py ✅
├── train.py ✅ (已修复)
└── inference.py ✅

数据增强策略

增强方式 目的
随机文本内容(中英文混合) 避免模型记忆文本内容而非字体特征
CJK 字体检测(渲染测试) 自动判断字体是否支持中文,避免生成空白样本
随机字号(18-90px) 适应不同尺度
随机背景色 + 对比度文字色 模拟真实场景
随机位置偏移(非居中) 避免位置偏见
轻微旋转(-5°~5°) 模拟拍照/扫描倾斜
高斯模糊(20%概率) 模拟低质量图片
随机噪声(15%概率) 增强鲁棒性
对比度抖动(10%概率) 模拟不同显示条件

三、模型架构与训练策略

微调策略(参数高效)

DINOv2-base (12层 Transformer Encoder)
├── Layer 0-9:   冻结(不参与训练)
├── Layer 10-11: 解冻(微调)
├── LayerNorm:   解冻
└── Classifier:  新增全连接层(768 → 214)

可训练参数仅占 16.7%(14.5M / 86.9M),大幅减少计算量和过拟合风险。

优化器与调度

组件 配置 理由
优化器 AdamW 适合 Transformer,自带权重衰减解耦
学习率 3e-5 微调经验值,太大破坏预训练特征
权重衰减 0.01 防止过拟合
LR Schedule Cosine Annealing 训练后期平滑降低学习率,稳定收敛
Warmup 前 2 个 epoch 线性预热 避免初期大学习率破坏预训练权重
Label Smoothing 0.1 软化标签,防止模型过于自信,提升泛化
梯度裁剪 max_norm=1.0 防止梯度爆炸

Early Stopping

验证集准确率连续 5 个 epoch 无提升则停止,保存最优模型。


四、训练过程与结果

训练环境

  • GPU 训练:NVIDIA L40S, 44GB 显存, batch_size=64, AMP 混合精度
Gpu
python train_gpu.py  --model models/dinov2-base 



  Epoch 1 [950/996] loss=4.7552 acc=0.0665 lr=1.43e-05
  Epoch 1/20  train_loss=4.7122  train_acc=0.0711  val_loss=3.8577  val_acc=0.1580  lr=1.50e-05  time=60.1s  gpu_mem=1.8GB
  ★ 新最优! val_acc=0.1580
  Epoch 2 [150/996] loss=3.7191 acc=0.1862 lr=1.73e-05
这是因为 batch_size 不同:

环境
batch_size
训练样本
每 epoch batch 数
Mac (train.py)
16
63,690
63,690 ÷ 16 = 3981
GPU (train_gpu.py)
64
63,690
63,690 ÷ 64 = 996

GPU 版把 batch_size 设成 64,所以每个 epoch 的 batch 数约为 Mac 的 1/4,训练会更快。

  • Mac 训练:M4, MPS(需 PYTORCH_ENABLE_MPS_FALLBACK=1
  ★ 新最优! val_acc=0.6417
  Epoch 10 [3900/3981] loss=1.6665 acc=0.7438 lr=1.77e-05
  Epoch 10/20  train_loss=1.6662  train_acc=0.7440  val_loss=1.9765  val_acc=0.6525  lr=1.76e-05  time=1231.1s
  ★ 新最优! val_acc=0.6525

收敛曲线(GPU 最终)

Epoch train_acc val_acc train_loss 观察
1 9.5% 18.7% 4.47 warmup 阶段
2 27.1% 29.5% 3.27 快速学习
3 38.6% 42.8% 2.82 持续上升
20 86.1% 67.74% 1.39 训练完成

train_acc > val_acc 约 18%,存在一定过拟合;相似无衬线体易混淆。


五、推理流程

输入图片 → Resize(224×224) → Normalize → DINOv2 Backbone → Classifier → Top-K 预测

加载类别映射文件 (class_mapping.json),输出 Top-5 字体名称及置信度。


六、关键设计决策总结

决策 选择 权衡
数据来源 PIL 渲染生成 可控且无标注成本,但与真实截图有 domain gap
预训练模型 DINOv2 (自监督) 通用特征优于分类预训练,适合细粒度任务
微调范围 只训练最后 2 层 参数效率高,防止过拟合,训练快
LR 策略 Warmup + Cosine 保护预训练权重,后期稳定收敛
损失函数 CrossEntropy + Label Smoothing 软标签提升泛化,对相似字体更友好

七、项目结构

font_vit/
├── fonts/                  # 216 个字体文件 (.ttf)
├── models/
│   └── dinov2-base/        # 本地 DINOv2 模型(离线推理用)
├── dataset/
│   ├── train/              # 训练集(214 类,63,690 张)
│   └── val/                # 验证集(214 类,11,210 张)
├── output/
│   ├── best_model.pt       # 最优模型权重(347MB)
│   ├── final_model.pt      # 最终模型权重
│   ├── class_mapping.json  # 类别索引映射
│   └── training_history.json # 训练历史记录
├── download_fonts.py       # 从 Google Fonts 批量下载字体
├── get_model.py            # 下载 DINOv2 到本地(离线/GPU 用)
├── generate_dataset.py     # PIL 渲染生成训练数据
├── train.py                # 训练脚本(Mac MPS)
├── train_gpu.py            # 训练脚本(GPU CUDA)
├── inference_mac.py        # 推理脚本(Mac MPS)
└── inference_gpu.py        # 推理脚本(GPU CUDA)

八、使用方式

# 1. 下载字体
python download_fonts.py

# 2. 生成训练数据
python generate_dataset.py

# 3. 下载 DINOv2 到本地(GPU 服务器离线用)
python get_model.py

# 4. 训练
python train_gpu.py --model models/dinov2-base   # GPU
python train.py                                  # Mac MPS

# 5. 推理预测
python inference_gpu.py <image_path>   # GPU 服务器
python inference_mac.py <image_path>    # Mac

九、可延伸方向

  • 对比学习:用 triplet loss 学习字体 embedding,支持相似字体检索
  • Hard Negative Mining:针对高度相似的字体对(如同一字体 Regular vs Bold)加强训练
  • ONNX / TorchScript 导出:部署优化,减少推理延迟
  • FAISS 向量检索:提取字体 embedding 构建索引,支持以图搜字体
  • 混合精度训练:加速训练,降低显存占用
  • TensorBoard 可视化:实时监控训练曲线
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐