Files
paddlepaddle--paddle/test/quantization/test_imperative_qat_amp.py
T
2026-07-13 12:40:42 +08:00

240 lines
7.8 KiB
Python

# copyright (c) 2022 paddlepaddle authors. all rights reserved.
#
# licensed under the apache license, version 2.0 (the "license");
# you may not use this file except in compliance with the license.
# you may obtain a copy of the license at
#
# http://www.apache.org/licenses/license-2.0
#
# unless required by applicable law or agreed to in writing, software
# distributed under the license is distributed on an "as is" basis,
# without warranties or conditions of any kind, either express or implied.
# see the license for the specific language governing permissions and
# limitations under the license.
import logging
import os
import tempfile
import time
import unittest
import numpy as np
from imperative_test_utils import ImperativeLenet
import paddle
from paddle import base
from paddle.dataset.common import download
from paddle.framework import set_flags
from paddle.quantization import ImperativeQuantAware
from paddle.static.log_helper import get_logger
os.environ["CPU_NUM"] = "1"
if paddle.is_compiled_with_cuda():
set_flags({"FLAGS_cudnn_deterministic": True})
_logger = get_logger(
__name__, logging.INFO, fmt='%(asctime)s-%(levelname)s: %(message)s'
)
class TestImperativeQatAmp(unittest.TestCase):
"""
Test the combination of qat and amp.
"""
@classmethod
def setUpClass(cls):
cls.root_path = tempfile.TemporaryDirectory(
prefix="imperative_qat_amp_"
)
cls.save_path = os.path.join(cls.root_path.name, "model")
cls.download_path = 'dygraph_int8/download'
cls.cache_folder = os.path.expanduser(
'~/.cache/paddle/dataset/' + cls.download_path
)
cls.lenet_url = "https://paddle-inference-dist.cdn.bcebos.com/int8/unittest_model_data/lenet_pretrained.tar.gz"
cls.lenet_md5 = "953b802fb73b52fae42896e3c24f0afb"
seed = 1
np.random.seed(seed)
paddle.seed(seed)
@classmethod
def tearDownClass(cls):
cls.root_path.cleanup()
def cache_unzipping(self, target_folder, zip_path):
if not os.path.exists(target_folder):
cmd = (
f'mkdir {target_folder} && tar xf {zip_path} -C {target_folder}'
)
os.system(cmd)
def download_model(self, data_url, data_md5, folder_name):
download(data_url, self.download_path, data_md5)
file_name = data_url.split('/')[-1]
zip_path = os.path.join(self.cache_folder, file_name)
print(f'Data is downloaded at {zip_path}')
data_cache_folder = os.path.join(self.cache_folder, folder_name)
self.cache_unzipping(data_cache_folder, zip_path)
return data_cache_folder
def set_vars(self):
self.qat = ImperativeQuantAware()
self.train_batch_num = 30
self.train_batch_size = 32
self.test_batch_num = 100
self.test_batch_size = 32
self.eval_acc_top1 = 0.99
def model_train(self, model, batch_num=-1, batch_size=32, use_amp=False):
model.train()
train_reader = paddle.batch(
paddle.dataset.mnist.train(), batch_size=batch_size
)
adam = paddle.optimizer.Adam(
learning_rate=0.001, parameters=model.parameters()
)
scaler = paddle.amp.GradScaler(init_loss_scaling=500)
for batch_id, data in enumerate(train_reader()):
x_data = np.array([x[0].reshape(1, 28, 28) for x in data]).astype(
'float32'
)
y_data = (
np.array([x[1] for x in data]).astype('int64').reshape(-1, 1)
)
img = paddle.to_tensor(x_data)
label = paddle.to_tensor(y_data)
if use_amp:
with paddle.amp.auto_cast():
out = model(img)
acc = paddle.metric.accuracy(out, label)
loss = paddle.nn.functional.cross_entropy(
out, label, reduction='none', use_softmax=False
)
avg_loss = paddle.mean(loss)
scaled_loss = scaler.scale(avg_loss)
scaled_loss.backward()
scaler.minimize(adam, scaled_loss)
adam.clear_gradients()
else:
out = model(img)
acc = paddle.metric.accuracy(out, label)
loss = paddle.nn.functional.cross_entropy(
out, label, reduction='none', use_softmax=False
)
avg_loss = paddle.mean(loss)
avg_loss.backward()
adam.minimize(avg_loss)
model.clear_gradients()
if batch_id % 100 == 0:
_logger.info(
f"Train | step {batch_id}: loss = {avg_loss.numpy()}, acc= {acc.numpy()}"
)
if batch_num > 0 and batch_id + 1 >= batch_num:
break
def model_test(self, model, batch_num=-1, batch_size=32, use_amp=False):
model.eval()
test_reader = paddle.batch(
paddle.dataset.mnist.test(), batch_size=batch_size
)
acc_top1_list = []
for batch_id, data in enumerate(test_reader()):
x_data = np.array([x[0].reshape(1, 28, 28) for x in data]).astype(
'float32'
)
y_data = (
np.array([x[1] for x in data]).astype('int64').reshape(-1, 1)
)
img = paddle.to_tensor(x_data)
label = paddle.to_tensor(y_data)
with paddle.amp.auto_cast(use_amp):
out = model(img)
acc_top1 = paddle.metric.accuracy(input=out, label=label, k=1)
acc_top5 = paddle.metric.accuracy(input=out, label=label, k=5)
acc_top1_list.append(float(acc_top1.numpy()))
if batch_id % 100 == 0:
_logger.info(
f"Test | At step {batch_id}: acc1 = {acc_top1.numpy()}, acc5 = {acc_top5.numpy()}"
)
if batch_num > 0 and batch_id + 1 >= batch_num:
break
acc_top1 = sum(acc_top1_list) / len(acc_top1_list)
return acc_top1
def test_ptq(self):
start_time = time.time()
self.set_vars()
params_path = self.download_model(
self.lenet_url, self.lenet_md5, "lenet"
)
params_path += "/lenet_pretrained/lenet.pdparams"
with base.dygraph.guard():
model = ImperativeLenet()
model_state_dict = paddle.load(params_path)
model.set_state_dict(model_state_dict)
_logger.info("Test fp32 model")
fp32_acc_top1 = self.model_test(
model, self.test_batch_num, self.test_batch_size
)
self.qat.quantize(model)
use_amp = True
self.model_train(
model, self.train_batch_num, self.train_batch_size, use_amp
)
_logger.info("Test int8 model")
int8_acc_top1 = self.model_test(
model, self.test_batch_num, self.test_batch_size, use_amp
)
_logger.info(
f'fp32_acc_top1: {fp32_acc_top1:f}, int8_acc_top1: {int8_acc_top1:f}'
)
self.assertTrue(
int8_acc_top1 > fp32_acc_top1 - 0.01,
msg=f'fp32_acc_top1: {fp32_acc_top1:f}, int8_acc_top1: {int8_acc_top1:f}',
)
input_spec = [
paddle.static.InputSpec(shape=[None, 1, 28, 28], dtype='float32')
]
with paddle.pir_utils.OldIrGuard():
paddle.jit.save(
layer=model, path=self.save_path, input_spec=input_spec
)
print(f'Quantized model saved in {{{self.save_path}}}')
end_time = time.time()
print("total time: %ss" % (end_time - start_time))
if __name__ == '__main__':
unittest.main()