Оптимизация моделей глубокого обучения с помощью весовой квантизации: практическое руководство по работе с ResNet18 в PyTorch

В современной области глубокого обучения оптимизация моделей для развёртывания в условиях ограниченных ресурсов становится как никогда важной. Весовая квантизация решает эту задачу, снижая точность параметров модели, обычно с 32-битных чисел с плавающей запятой до представлений с меньшей разрядностью. Это позволяет создавать более компактные модели, которые могут работать быстрее на оборудовании с ограниченными ресурсами.

В этом руководстве мы рассмотрим концепцию весовой квантизации, используя метод динамической квантизации из PyTorch на предварительно обученной модели ResNet18. Мы изучим, как анализировать распределения весов, применять динамическую квантизацию к ключевым слоям (например, к полносвязным слоям), сравнивать размеры моделей и визуализировать полученные изменения. Это руководство предоставит вам теоретические знания и практические навыки, необходимые для развёртывания моделей глубокого обучения.

import torch
import torch.nn as nn
import torch.quantization
import torchvision.models as models
import matplotlib.pyplot as plt
import numpy as np
import os

print(“Версия PyTorch:”, torch.__version__)

Здесь мы импортируем необходимые библиотеки, такие как PyTorch, torchvision и matplotlib, и выводим версию PyTorch, чтобы убедиться, что все необходимые модули готовы для манипуляции моделью и визуализации.

model_fp32 = models.resnet18(pretrained=True)
model_fp32.eval()

print(“Предварительно обученная модель ResNet18 (FP32) загружена.”)

Загружается предварительно обученная модель ResNet18 с точностью FP32 и переводится в режим оценки, что подготавливает её для дальнейшей обработки и квантизации.

fc_weights_fp32 = model_fp32.fc.weight.data.cpu().numpy().flatten()

plt.figure(figsize=(8, 4))
plt.hist(fc_weights_fp32, bins=50, color=’skyblue’, edgecolor=’black’)
plt.title(“Распределение весов полносвязного слоя (FP32)”)
plt.xlabel(“Значения весов”)
plt.ylabel(“Частота”)
plt.grid(True)
plt.show()

В этом блоке веса из последнего полносвязного слоя модели FP32 извлекаются и выравниваются, затем строится гистограмма для визуализации их распределения до применения квантизации.

quantized_model = torch.quantization.quantize_dynamic(model_fp32, {nn.Linear}, dtype=torch.qint8)
quantized_model.eval()

print(“К модели применена динамическая квантизация.”)

Мы применяем динамическую квантизацию к модели, ориентируясь в первую очередь на линейные слои, чтобы преобразовать их в форматы с более низкой точностью. Это демонстрирует ключевой метод уменьшения размера модели и задержки при выводе.

def get_model_size(model, filename=”temp.p”):
torch.save(model.state_dict(), filename)
size = os.path.getsize(filename) / 1e6
os.remove(filename)
return size

fp32_size = get_model_size(model_fp32, “fp32_model.p”)
quant_size = get_model_size(quantized_model, “quant_model.p”)

print(f”Размер модели FP32: {fp32_size:.2f} МБ”)
print(f”Размер квантованной модели: {quant_size:.2f} МБ”)

Определяется вспомогательная функция для сохранения и проверки размера модели на диске, которая затем используется для измерения и сравнения размеров исходной модели FP32 и квантованной модели. Это демонстрирует влияние квантизации на сжатие.

dummy_input = torch.randn(1, 3, 224, 224)

with torch.no_grad():
output_fp32 = model_fp32(dummy_input)
output_quant = quantized_model(dummy_input)

print(“Выходные данные модели FP32 (первые 5 элементов):”, output_fp32[0][:5])
print(“Выходные данные квантованной модели (первые 5 элементов):”, output_quant[0][:5])

Создаётся фиктивный входной тензор для имитации изображения, и обе модели — FP32 и квантованная — запускаются на этом входе, чтобы можно было сравнить их выходные данные и убедиться, что квантизация не приводит к резкому изменению прогнозов.

if hasattr(quantized_model.fc, ‘weight’):
fc_weights_quant = quantized_model.fc.weight().dequantize().cpu().numpy().flatten()
else:
fc_weights_quant = quantized_model.fc._packed_params._packed_weight.dequantize().cpu().numpy().flatten()

plt.figure(figsize=(14, 5))

plt.subplot(1, 2, 1)
plt.hist(fc_weights_fp32, bins=50, color=’skyblue’, edgecolor=’black’)
plt.title(“Распределение весов полносвязного слоя (FP32)”)
plt.xlabel(“Значения весов”)
plt.ylabel(“Частота”)
plt.grid(True)

plt.subplot(1, 2, 2)
plt.hist(fc_weights_quant, bins=50, color=’salmon’, edgecolor=’black’)
plt.title(“Распределение весов полносвязного слоя (квантованное)”)
plt.xlabel(“Значения весов”)
plt.ylabel(“Частота”)
plt.grid(True)

plt.tight_layout()
plt.show()

В этом блоке квантованные веса (после деквантизации) извлекаются из полносвязного слоя и сравниваются с исходными весами FP32 с помощью гистограмм, чтобы проиллюстрировать изменения в распределении весов, вызванные квантизацией.

В заключение, это руководство предоставило пошаговое руководство по пониманию и реализации весовой квантизации, подчеркнув её влияние на размер модели и производительность. Квантизируя предварительно обученную модель ResNet18, мы наблюдали сдвиги в распределении весов, ощутимые преимущества в сжатии модели и потенциальное улучшение скорости вывода. Это исследование закладывает основу для дальнейших экспериментов, таких как реализация обучения с учётом квантизации (QAT), которое может дополнительно оптимизировать производительность квантованных моделей.

Источник

Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *