PaddleHub 轻量级图像分类实战:ghostnet_x1_3_imagenet 模块的安装、推理、Fine-tune 与 Serving 部署全指南
本文以 PaddleFormers 仓库中的 ghostnet_x1_3_imagenet 图像分类模块为主线,系统讲解 GhostNet 轻量级网络的模块结构、环境安装、命令行与 Python API 预测、基于 Flowers 数据集的 Fine-tune 全流程,以及 PaddleHub Serving 在线服务化部署。读完本文,你将能够独立完成从模型安装、单张图片分类预测,到自定义数据集微调并部署为 HTTP 分类服务的完整链路。
一、模型基本信息与适用场景
ghostnet_x1_3_imagenet 是 PaddleHub 提供的一个图像分类模块,其核心信息如下:
| 模型名称 | ghostnet_x1_3_imagenet |
|---|---|
| 类别 | 图像-图像分类 |
| 网络 | GhostNet |
| 数据集 | ImageNet-2012 |
| 是否支持 Fine-tuning | 是 |
| 模型大小 | 43MB |
| 指标 | - |
| 最新更新日期 | 2021-09-14 |
GhostNet 是华为在 2020 年提出的全新轻量级网络结构,其核心思想是引入 ghost 模块(Ghost Module):传统深度网络产生的特征图中存在大量冗余,Ghost 模块通过"先做少量固有卷积生成一部分特征图,再对这部分特征图施加廉价线性变换(cheap operation)生成其余特征图"的方式,显著缓解了特征的冗余计算问题,从而大幅减少网络参数和计算量。x1_3 表示通道数缩放系数(width multiplier)为 1.3,属于在标准 GhostNet 基础上加宽通道的变体,在轻量与精度之间取得平衡。
在 PaddleFormers 仓库中,该模块的完整源码位于 module.py,模块元信息通过 @moduleinfo 装饰器声明(name="ghostnet_x1_3_imagenet"、type="CV/classification"、version="1.0.0"),继承自 ImageClassifierModule(定义于 cv_module.py),因而天然具备 predict、training_step、serving_method、run_cmd 等分类模块的标准能力。
二、GhostNet 网络结构的源码级解析
结合 module.py 的实现,可以清晰看到该模块的组成单元与数据流。
2.1 GhostModule:核心"幽灵"模块
GhostModule 是网络的基本构建块(module.py 第 88-114 行):
class GhostModule(nn.Layer):
def __init__(self, in_channels, output_channels, kernel_size=1, ratio=2, dw_size=3, stride=1, relu=True, name=None):
super(GhostModule, self).__init__()
init_channels = int(math.ceil(output_channels / ratio))
new_channels = int(init_channels * (ratio - 1))
self.primary_conv = ConvBNLayer(...) # 主卷积:生成 init_channels 个特征图
self.cheap_operation = ConvBNLayer( # 廉价操作:深度可分离卷积生成其余特征图
..., groups=init_channels, ...)
def forward(self, inputs):
x = self.primary_conv(inputs)
y = self.cheap_operation(x)
out = paddle.concat([x, y], axis=1) # 沿通道拼接
return out
其原理可拆解为:
primary_conv:一个 1×1(或指定 kernel_size)普通卷积,输出通道数为ceil(output_channels / ratio),ratio默认为 2;cheap_operation:一个 kernel_size 为 3(dw_size)、groups=init_channels的深度可分离卷积,在每张主特征图上独立做廉价变换,生成剩余init_channels * (ratio - 1)个通道;- 最后将两部分在通道维拼接(
paddle.concat),得到完整的output_channels通道输出。
由于廉价变换的计算成本远低于普通卷积,整体 FLOPs 和参数量被显著压缩,这正是 GhostNet 轻量的根本原因。
2.2 GhostBottleneck:倒残差瓶颈
GhostBottleneck(module.py 第 117-180 行)将两个 GhostModule 组合成一个类似 MobileNetV2 的倒残差瓶颈结构,并支持可选的下采样与 SE 注意力:
ghost_module_1:1×1 升维(hidden_dim通道),relu 激活;- 当
stride == 2时,插入一个groups=hidden_dim的深度可分离卷积完成空间下采样; - 当
use_se为真时,插入SEBlock(squeeze-and-excitation,先全局平均池化、两层线性映射、paddle.clip(x, min=0, max=1)限幅得到通道权重,再与输入逐元素相乘); ghost_module_2:1×1 降维回output_channels,无 relu;- 残差捷径:当
stride == 1且通道数不变时直接恒等映射,否则通过深度可分离卷积 + 1×1 卷积对齐通道后与主分支相加。
2.3 网络骨架:cfgs 配置与 width multiplier
GhostNet 类中定义了一份结构配置表 self.cfgs(module.py 第 209-227 行),每行格式为 [kernel_size, expand_size, output_channels, use_se, stride],共 16 个瓶颈层;同时设置 self.scale = 1.3,并通过 _make_divisible 函数将所有层的通道数规整为 4 的倍数(保证硬件友好的对齐)。头部由 3×3 stride=2 的 conv1、conv_last、全局平均池化、1×1 卷积升维到 1280、Dropout(0.2) 和输出维度为 class_dim 的全连接 fc_1 组成。
class_dim 的来源有两个分支:
- 若构造时传入
label_list,则以len(label_list)作为分类数; - 否则读取模块目录下的 label_list.txt,该文件包含 ImageNet-2012 的 1000 个类别名(如 tench、goldfish、daisy 等),
class_dim = 1000。
forward 的返回值为 (x, feature),其中 x 是分类 logits,feature 是池化后的特征向量,这一设计同时支持分类输出与特征提取场景。
三、环境依赖与模块安装
3.1 环境依赖
运行本模块需要满足以下版本要求:
paddlepaddle >= 2.0.0paddlehub >= 2.0.0
3.2 安装模块
使用 PaddleHub 命令行一键安装:
$ hub install ghostnet_x1_3_imagenet
安装成功后,可通过 hub show ghostnet_x1_3_imagenet 查看模块信息,或直接进入下一节的预测环节。如您在安装过程中遇到环境问题,可参考官方安装指引:零基础 Windows 安装、零基础 Linux 安装、零基础 MacOS 安装。
四、模型 API 预测
该模块继承自 ImageClassifierModule,其 predict 实现(cv_module.py 第 82-122 行)会先调用模块内置的 transforms 预处理(Resize((256, 256)) → CenterCrop(224) → ImageNet 均值/方差归一化,to_rgb=True),再在 paddle.no_grad() 下前向得到 logits,经 F.softmax 后按概率降序取 top_k 个类别,返回 {类别名: 置信度} 形式的字典列表。
4.1 命令行预测
$ hub run ghostnet_x1_3_imagenet --input_path "/PATH/TO/IMAGE" --top_k 5
--input_path:必填,待分类图片路径;--top_k:可选,输出概率最高的前 k 个类别,默认值为 1。
命令行入口由 ImageClassifierModule.run_cmd(cv_module.py 第 139-157 行)解析上述参数并调用 predict 完成。
4.2 Python 代码预测
import paddle
import paddlehub as hub
if __name__ == '__main__':
model = hub.Module(name='ghostnet_x1_3_imagenet')
result = model.predict(['flower.jpg'])
hub.Module(name='ghostnet_x1_3_imagenet') 会从本地加载已安装的模块及其预训练权重(源码中通过 paddle.load(os.path.join(self.directory, 'model.pdparams')) 加载),predict 接受图片路径或 BGR 格式的 np.ndarray 列表,并支持 batch_size 与 top_k 参数。
五、基于 Flowers 数据集的 Fine-tune
该模块支持 Fine-tuning,仓库在 demo/image_classification/train.py 中提供了完整的训练脚本模板,将 name 替换为 ghostnet_x1_3_imagenet 即可复用。整体流程分为五个步骤。
Step 1:定义数据预处理方式
import paddlehub.vision.transforms as T
transforms = T.Compose([T.Resize((256, 256)),
T.CenterCrop(224),
T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])],
to_rgb=True)
transforms 数据增强模块定义了丰富的数据预处理方式,用户可按照需求替换为自己需要的数据预处理方式。这里的组合(缩放 256×256 → 中心裁剪 224×224 → 按 ImageNet 统计值归一化)与模块内置的预测预处理保持一致,保证微调与推理的输入分布一致。
Step 2:下载数据集并使用
from paddlehub.datasets import Flowers
flowers = Flowers(transforms)
flowers_validate = Flowers(transforms, mode='val')
参数说明:
transforms:数据预处理方式;mode:选择数据模式,可选项有train、test、val,默认为train。
Flowers 数据集的实现位于 paddlehub/datasets/flowers.py:类上标注了 @download_data(url='https://bj.bcebos.com/paddlehub-dataset/flower_photos.tar.gz') 装饰器,hub.datasets.Flowers() 会自动从网络下载数据集并解压到用户目录下 $HOME/.paddlehub/dataset 目录,num_classes = 5,三种 mode 分别对应 train_list.txt、test_list.txt、validate_list.txt 文件索引,标签类别依次为 roses、tulips、daisy、sunflowers、dandelion。
Step 3:加载预训练模型
model = hub.Module(name="ghostnet_x1_3_imagenet",
label_list=["roses", "tulips", "daisy", "sunflowers", "dandelion"])
name:选择预训练模型的名字;label_list:设置输出分类类别,默认为 ImageNet-2012 的 1000 个类别(即 label_list.txt)。传入自定义label_list后,class_dim会被设为 5,分类头fc_1相应重建,预训练骨干权重仍被加载,从而实现迁移学习。
Step 4:选择优化策略和运行配置
optimizer = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters())
trainer = Trainer(model, optimizer, checkpoint_dir='img_classification_ckpt')
trainer.train(flowers, epochs=100, batch_size=32, eval_dataset=flowers_validate, save_interval=1)
Trainer 主要控制 Fine-tune 的训练,其实现位于 paddlehub/finetune/trainer.py,包含以下可控制参数:
model:被优化模型;optimizer:优化器选择;use_gpu:是否使用 GPU 运行,默认False(源码中通过paddle.set_device('gpu')/paddle.set_device('cpu')切换);use_vdl:是否使用 VisualDL 可视化训练过程,默认True,日志写入checkpoint_dir/visualization;checkpoint_dir:保存模型参数的地址,默认值为ckpt_{time.time()},Trainer 初始化时会自动扫描该目录下epoch_*检查点实现断点续训;compare_metrics:保存最优模型的衡量指标比较函数,默认取验证指标字典第一个键、数值越大越优。
trainer.train 主要控制具体的训练过程,包含以下可控制参数:
train_dataset:训练时所用的数据集;epochs:训练轮数;batch_size:训练的批大小,如果使用 GPU,请根据实际情况调整 batch_size;num_workers:数据加载子进程数量,默认为 0;eval_dataset:验证集,非空时 Trainer 每save_interval轮执行一次验证(源码中要求模型具备validation_step方法,ImageClassifierModule已实现,返回loss与acc指标);log_interval:打印日志的间隔,单位为执行批训练的次数,默认 10;save_interval:保存模型的间隔频次,单位为执行训练的轮数,默认 10;collate_fn:自定义 mini-batch 拼接函数,默认按第 0 维 stack。
训练过程中,Trainer 会:按 log_interval 打印 Epoch/Step/loss/acc/lr/step/sec/ETA 日志并同步到 VisualDL;在每个 save_interval 轮末先做验证,若 compare_metrics 判定指标更优,则将模型保存到 ${checkpoint_dir}/best_model(含 model.pdparams 与 model.pdopt),同时把周期性检查点保存到 epoch_{n} 目录,并自动恢复上次的 metrics.pkl 与 best_metrics。
模型预测(加载微调产物)
当完成 Fine-tune 后,Fine-tune 过程在验证集上表现最优的模型会被保存在 ${CHECKPOINT_DIR}/best_model 目录下,其中 ${CHECKPOINT_DIR} 目录为 Fine-tune 时所选择的保存 checkpoint 的目录。我们使用该模型来进行预测:
import paddle
import paddlehub as hub
if __name__ == '__main__':
model = hub.Module(name='ghostnet_x1_3_imagenet',
label_list=["roses", "tulips", "daisy", "sunflowers", "dandelion"],
load_checkpoint='/PATH/TO/CHECKPOINT')
result = model.predict(['flower.jpg'])
load_checkpoint 指向微调产物的路径(源码中通过 paddle.load(load_checkpoint) 加载并 set_dict 覆盖权重),从而使用微调后的参数进行推理。
NOTE: 进行预测时,所选择的 module、checkpoint_dir、dataset 必须和 Fine-tune 所用的一样,否则类别对齐与权重加载都会出错。
六、服务部署:PaddleHub Serving
PaddleHub Serving 可以部署一个在线分类任务服务,将模型封装为 HTTP API 供外部系统调用。
第一步:启动 PaddleHub Serving
$ hub serving start -m ghostnet_x1_3_imagenet
这样就完成了一个分类任务服务化 API 的部署,默认端口号为 8866。
NOTE: 如使用 GPU 预测,则需要在启动服务之前,设置
CUDA_VISIBLE_DEVICES环境变量,否则不用设置。
服务端实际上会调用模块的 serving_method(cv_module.py 第 124-137 行):接收 base64 编码的图片列表与 top_k 参数,解码后转给 predict,并把 numpy 置信度转换为 Python float 后以 {"data": [{"类别": 置信度}, ...]} 结构返回。
第二步:发送预测请求
配置好服务端后,以下数行代码即可实现发送预测请求、获取预测结果:
import requests
import json
import cv2
import base64
import numpy as np
def cv2_to_base64(image):
data = cv2.imencode('.jpg', image)[1]
return base64.b64encode(data.tostring()).decode('utf8')
def base64_to_cv2(b64str):
data = base64.b64decode(b64str.encode('utf8'))
data = np.fromstring(data, np.uint8)
data = cv2.imdecode(data, cv2.IMREAD_COLOR)
return data
# 发送HTTP请求
org_im = cv2.imread('/PATH/TO/IMAGE')
data = {'images': [cv2_to_base64(org_im)], 'top_k': 2}
headers = {"Content-type": "application/json"}
url = "http://127.0.0.1:8866/predict/ghostnet_x1_3_imagenet"
r = requests.post(url=url, headers=headers, data=json.dumps(data))
data = r.json()["results"]['data']
请求体中 images 为 base64 编码的图片列表,top_k 控制返回的候选类别数;响应中的 results.data 即每张图片按置信度降序排列的类别预测结果。
七、更新历史
- 1.0.0:初始发布,提供基于 ImageNet-2012 预训练的 GhostNet(width=1.3)图像分类能力。
延伸阅读
- 模块完整源码:GhostModule/GhostBottleneck/SEBlock 的实现细节与网络配置表
- ImageNet-2012 标签文件:默认 1000 类类别名
- Flowers 数据集实现:数据自动下载与 train/val/test 划分逻辑
- Trainer 训练器源码:检查点保存、断点续训、VisualDL 可视化与最优模型选择机制
- ImageClassifierModule 基类:predict/training_step/validation_step/serving_method 的通用实现
- 图像分类 Demo 训练脚本:可直接替换模型名的微调示例
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



