pytorch FX模型静态量化

本文详细记录了从手动PyTorch静态量化遇到的困难,如合并层处理和版本兼容性问题,到使用FX量化工具的便捷体验。作者介绍了FX版本迁移的重要性,并分享了量化模型的完整代码,包括校准和验证过程。最终,量化模型精度略有下降,但便于部署在特定平台.

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

以前面文章写到的mobilenet图像分类为例,本文主要记录一下pytorchh训练后静态量化的过程。


一、pytorch静态量化(手动版)

静态量化是最常用的量化形式,float32的模型量化成int8,模型大小大概变为原来的1/4,推理速度我在intel 8700k CPU上测试速度正好快4倍,但是在AMD的5800h CPU 上测试速度反而慢了两倍,应该是AMD不支持某些指令集加速。

踩坑:

之前手动添加量化节点的方式搞了好几天,最后模型是出来了,但是推理时候报错,大多数时候是RuntimeError: Could not run ‘--------’ with arguments from the ‘CPU’ backend,网上是说推理的时候没有安插QuantStub()和DeQuantStub(),可能是用的这个MobilenetV3网络结构复杂,某些地方没有手动添加到,这种方式肯定是可以成功的,只是比较麻烦容易出错。

# 加载模型
model = MobileNetV3_Large(2).to(device)  # 加载一个网络,我这边是二分类传了一个2
checkpoint = torch.load(weights, map_location=device)
model.load_state_dict(checkpoint)
model.to('cpu').eval()

合并层对于一些重复使用的Block和nn.Sequential要打印出来看,然后append到mix_list 里面
比如

# 打印model
for name, module in model.named_children():
	print(name, module)

比如这里Sequential里面存在conv+bn+relu,append进去的应该是[‘bneck.0.conv1’, ‘bneck.0.bn1’,‘nolinear1’],但是nolinear1是个变量,也就是说某些时候是relu某些时候又不是,这种时候就要一个个分析判断好然后写代码,稍微复杂点就容易出错或者遗漏。

backend = "fbgemm"  # x86平台
model.qconfig = torch.quantization.get_default_qconfig(backend)
mix_list = [['conv1','bn1'], ['conv2','bn2']] # 合并层只支持conv+bn conv+relu conv+bn+relu等操作,具体可以查一下,网络中存在的这些操作都append到mix_list里面
model = torch.quantization.fuse_modules(model,listmix) # 合并某些层
model_fp32_prepared = torch.quantization.prepare(model)
model_int8 = torch.quantization.convert(model_fp32_prepared)

有时候存在不支持的操作relu6这些要替换成relu,加法操作也要替换,最后还要输入一批图像校准模型等

self.skip_add = nn.quantized.FloatFunctional()
# forward的时候比如return a+b 改为return self.skip_add.add(a, b)

一系列注意事项操作完毕,最后推理各种报错,放弃了

二、使用FX量化

1.版本

fx量化版本也有坑,之前在torch 1.7版本操作总是报错搞不定,换成1.12.0版本就正常了,这一点非常重要。

2.代码如下:

import torch
import torch.nn as nn
import torch.nn.functional as F
import copy
import torchvision
from torchvision import transforms
from torch.quantization.quantize_fx import prepare_fx, convert_fx
from torch.quantization import get_default_qconfig
from torch import optim
import os
import time
from utils import load_data
from models.mobilenetv3copy import MobileNetV3_Large


def evaluate_model(model, test_loader, device, criterion=None):
    model.eval()
    model.to(device)
    running_loss = 0
    running_corrects = 0
    for inputs, labels in test_loader:
        inputs = inputs.to(device)
        labels = labels.to(device)

        outputs = model(inputs)
        _, preds = torch.max(outputs, 1)
        if criterion is not None:
            loss = criterion(outputs, labels).item()
        else:
            loss = 0
        # statistics
        running_loss += loss * inputs.size(0)
        running_corrects += torch.sum(preds == labels.data)
    eval_loss = running_loss / len(test_loader.dataset)
    eval_accuracy = running_corrects / len(test_loader.dataset)
    return eval_loss, eval_accuracy


def quant_fx(model, data_loader):
    model_to_quantize = copy.deepcopy(model)
    model_to_quantize.eval()
    qconfig = get_default_qconfig("fbgemm")
    qconfig_dict = {"": qconfig}
    prepared_model = prepare_fx(model_to_quantize, qconfig_dict)
    print("开始校准")
    calibrate(prepared_model, data_loader)  # 这是输入一批有代表性的数据来校准
    print("校准完毕")
    quantized_model = convert_fx(prepared_model)  # 转换
    return quantized_model


def calibrate(model, data_loader):
    model.eval()
    with torch.no_grad():
        for image, target in train_loader:
            model(image)


if __name__ == "__main__":
    cuda_device = torch.device("cuda:0")
    cpu_device = torch.device("cpu:0")
    model = MobileNetV3_Large(2)  # 加载自己的网络
    train_loader, test_loader = load_data(64, 8)  # 自己写一个pytorch加载数据的方法
    
    # quantization
    state_dict = torch.load('./mymodel.pth')  # 加载一个正常训练好的模型
    model.load_state_dict(state_dict)
    model.to('cpu')
    model.eval()
    quant_model = quant_fx(model, train_loader)  # 执行量化代码
    quant_model.eval()
    print("开始验证")
    eval_loss, eval_accuracy = evaluate_model(model=quant_model,
                                              test_loader=test_loader,
                                              device=cpu_device,
                                              criterion=nn.CrossEntropyLoss())
    print("Epoch: {:02d} Eval Loss: {:.3f} Eval Acc: {:.3f}".format(
        -1, eval_loss, eval_accuracy))
    torch.jit.save(torch.jit.script(quant_model), 'outQuant.pth')  # 保存量化后的模型
    
    # 加载量化模型推理
    loaded_quantized_model = torch.jit.load('outQuant.pth')
    eval_loss, eval_accuracy = evaluate_model(model=quant_model,
                                              test_loader=test_loader,
                                              device=cpu_device,
                                              criterion=nn.CrossEntropyLoss())
    print("Epoch: {:02d} Eval Loss: {:.3f} Eval Acc: {:.3f}".format(
        -1, eval_loss, eval_accuracy))

fx量化也不用管里面什么算子不支持之类的,开箱即用,以上代码参考pytorch官网https://pytorch.org/docs/stable/fx.html
最后验证模型精度下降0.02%可以忽略不计,pytorch量化的模型是不支持gpu推理的,只能在arm或者x86平台实现压缩提速。要用cuda的话要上tensorrt+onnx,以后完成了再讲。完整的训练模型量化模型的代码后面会放到github上面。
完整代码:https://github.com/Ysnower/pytorch-static-quant


总结

刚开始搞量化坑比较多,一个是某些操作不支持,合并层麻烦,另外有版本问题导致的报错可能搞很久,觉得有用的各位吴彦祖麻烦送个免费三连

MobilenetV3的PyTorch实现量化可以通过使用PyTorch量化工具来实现。量化是将浮点模型转换为定点模型的过程,可以减少模型的存储空间和计算量,同时保持模型的准确性。在量化过程中,需要添加QuantStub()和DeQuantStub()来确保推理时的正确性。 首先,加载MobilenetV3模型并将其转移到CPU上进行推理。可以使用以下代码加载模型和权重: ``` model = MobileNetV3_Large(2).to(device) checkpoint = torch.load(weights, map_location=device) model.load_state_dict(checkpoint) model.to('cpu').eval() ``` 接下来,使用PyTorch量化工具对模型进行量化。可以使用torch.quantization.quantize_dynamic方法来实现动态量化。以下是一个示例代码片段,展示了如何对模型进行动态量化: ``` import torch.quantization # 创建一个量化配置 quantization_config = torch.quantization.get_default_qconfig('fbgemm') # 将模型转换为量化模型 quantized_model = torch.quantization.quantize_dynamic( model, # 原始模型 {torch.nn.Linear}, # 需要量化的模块类型 dtype=torch.qint8, # 量化后的数据类型 qconfig_spec=quantization_config # 量化配置 ) # 打印量化后的模型 print(quantized_model) ``` 在上述代码中,我们首先创建了一个量化配置,然后使用torch.quantization.quantize_dynamic方法将模型转换为量化模型。我们指定了需要量化的模块类型为torch.nn.Linear,并将量化后的数据类型设置为torch.qint8。 通过以上步骤,你可以实现MobilenetV3的PyTorch实现量化。请注意,量化过程可能会导致一定的精度损失,因此在量化之前应该对模型进行充分的训练和验证。 #### 引用[.reference_title] - *1* [pytorch量化Quantization](https://blog.youkuaiyun.com/qq_29007291/article/details/124898605)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [详解pytorch动态量化](https://blog.youkuaiyun.com/qq_43800119/article/details/125748669)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [pytorch FX模型静态量化](https://blog.youkuaiyun.com/Y_snower/article/details/125962912)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序鱼鱼mj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值