您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

深度学习之模型压缩、加速模型推理

时间:2023-11-20 14:49:09  来源:微信公众号  作者:小白玩转Python

简介

当将一个机器学习模型部署到生产环境中时,通常需要满足一些在模型原型阶段没有考虑到的要求。例如,在生产中使用的模型将不得不处理来自不同用户的大量请求。因此,您将希望进行优化,以获得较低的延迟和/或吞吐量。

  • 延迟:是任务完成所需的时间,就像单击链接后加载网页所需的时间。它是开始某项任务和看到结果之间的等待时间。
  • 吞吐量:是系统在一定时间内可以处理的请求数。

这意味着机器学习模型在进行预测时必须非常快速,为此有各种技术可以提高模型推断的速度,本文将介绍其中最重要的一些。

模型压缩

有一些旨在使模型更小的技术,因此它们被称为模型压缩技术,而另一些则侧重于使模型在推断阶段更快,因此属于模型优化领域。但通常使模型更小也有助于提高推断速度,因此在这两个研究领域之间的界限非常模糊。

1.低秩分解

这是我们首次看到的第一种方法,它正在受到广泛研究,事实上,最近已经有很多关于它的论文发布。

基本思想是用低维度的矩阵(虽然更正确的说法是张量,因为我们经常有超过2维的矩阵)替换神经网络的矩阵(表示网络层的矩阵)。通过这种方式,我们将减少网络参数的数量,从而提高推断速度。

一个微不足道的例子是,在CNN网络中,将3x3的卷积替换为1x1的卷积。这种技术被用于网络结构中,比如Squeez.NET

最近,类似的思想也被应用于其他用途,比如允许在资源有限的情况下微调大型语言模型。当为下游任务微调预训练模型时,仍然需要在预训练模型的所有参数上训练模型,这可能非常昂贵。

因此,名为“大型语言模型的低秩适应”(或LoRA)的方法的思想是用较小的矩阵对原始模型进行替换(使用矩阵分解),这些矩阵具有较小的尺寸。这样,只需要重新训练这些新矩阵,以使预训练模型适应更多下游任务。

深度学习之模型压缩、加速模型推理

在LoRA中的矩阵分解

现在,让我们看看如何使用Hugging Face的PEFT库来实现对LoRA进行微调。假设我们想要使用LoRA对bigscience/mt0-large进行微调。首先,我们必须确保导入我们需要的内容。

!pip install peft
!pip install transformers
 
  from transformers import AutoModelForSeq2SeqLM
  from peft import get_peft_model, LoraConfig, TaskType

  model_name_or_path = "bigscience/mt0-large"
  tokenizer_name_or_path = "bigscience/mt0-large"

接下来的步骤将是创建在微调期间应用于LoRA的配置。

peft_config = LoraConfig(
    task_type=TaskType.SEQ_2_SEQ_LM, inference_mode=False, r=8, lora_alpha=32, lora_dropout=0.1
)

然后,我们使用Transformers库的基本模型以及我们为LoRA创建的配置对象来实例化模型。

model = AutoModelForSeq2SeqLM.from_pretrAIned(model_name_or_path)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()

2.知识蒸馏

这是另一种方法,允许我们将“小”模型放入生产中。思想是有一个称为教师的大模型,和一个称为学生的较小模型,我们将使用教师的知识来教学生如何进行预测。这样,我们可以只将学生放入生产环境中。

深度学习之模型压缩、加速模型推理

这种方法的一个经典示例是以这种方式开发的模型DistillBERT,它是BERT的学生模型。DistilBERT比BERT小40%,但保留了97%的语言理解能力,并且推断速度快60%。这种方法有一个缺点是:您仍然需要拥有大型教师模型,以便对学生进行训练,而您可能没有足够的资源来训练类似教师的模型。

让我们看看如何在Python/ target=_blank class=infotextkey>Python中进行知识蒸馏的简单示例。要理解的一个关键概念是Kullback–Leibler散度,它是一个用于理解两个分布之间差异的数学概念,实际上在我们的案例中,我们想要理解两个模型的预测之间的差异,因此训练的损失函数将基于这个数学概念。

import tensorflow as tf
from tensorflow.keras import layers, models
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
import numpy as np

# Load the MNIST dataset
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

# Preprocess the data
train_images = train_images.reshape((60000, 28, 28, 1)).astype('float32') / 255
test_images = test_images.reshape((10000, 28, 28, 1)).astype('float32') / 255
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

# Define the teacher model (a larger model)
teacher_model = models.Sequential([
    layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
    layers.MaxPooling2D((2, 2)),
    layers.Conv2D(64, (3, 3), activation='relu'),
    layers.MaxPooling2D((2, 2)),
    layers.Conv2D(64, (3, 3), activation='relu'),
    layers.Flatten(),
    layers.Dense(64, activation='relu'),
    layers.Dense(10, activation='softmax')
])

teacher_model.compile(optimizer='adam',
                      loss='categorical_crossentropy',
                      metrics=['accuracy'])

# Train the teacher model
teacher_model.fit(train_images, train_labels, epochs=5, batch_size=64, validation_split=0.2)

# Define the student model (a smaller model)
student_model = models.Sequential([
    layers.Flatten(input_shape=(28, 28, 1)),
    layers.Dense(64, activation='relu'),
    layers.Dense(10, activation='softmax')
])

student_model.compile(optimizer='adam',
                      loss='categorical_crossentropy',
                      metrics=['accuracy'])

# Knowledge distillation step: Transfer knowledge from the teacher to the student
def distillation_loss(y_true, y_pred):
    alpha = 0.1  # Temperature parameter (adjust as needed)
    return tf.keras.losses.KLDivergence()(tf.nn.softmax(y_true / alpha, axis=1),
                                           tf.nn.softmax(y_pred / alpha, axis=1))

# Train the student model using knowledge distillation
student_model.fit(train_images, train_labels, epochs=10, batch_size=64,
                  validation_split=0.2, loss=distillation_loss)

# Evaluate the student model
test_loss, test_acc = student_model.evaluate(test_images, test_labels)
print(f'Test accuracy: {test_acc * 100:.2f}%')

3.剪枝

剪枝是我在研究生论文中研究过的一种模型压缩方法,事实上,我之前曾发表过一篇关于如何在Julia中实现剪枝的文章:Julia中用于人工神经网络的迭代剪枝方法。

剪枝是为了解决决策树中的过拟合问题而诞生的,实际上是通过剪掉树的分支来减小树的深度。该概念后来被用于神经网络,其中会删除网络中的边和/或节点(取决于是否执行非结构化剪枝或结构化剪枝)。

深度学习之模型压缩、加速模型推理

假设要从网络中删除整个节点,表示层的矩阵将变小,因此您的模型也会变小,因此也会变快。相反,如果我们删除单个边,矩阵的大小将保持不变,但是我们将在删除的边的位置放置零,因此我们将获得非常稀疏的矩阵。因此,在非结构化剪枝中,优势不在于增加速度,而在于内存,因为将稀疏矩阵保存在内存中比保存密集矩阵要占用更少的空间。

但我们要剪枝的是哪些节点或边呢?通常是最不必要的节点或边,推荐大家可以研究下下面两篇论文:《Optimal Brain Damage》和《Optimal Brain Surgeon and general network pruning》。

让我们看一个如何在简单的MNIST模型中实现剪枝的Python脚本。

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow_model_optimization.sparsity import keras as sparsity
import numpy as np

# Load the MNIST dataset
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

# Preprocess the data
train_images = train_images.reshape((60000, 28, 28, 1)).astype('float32') / 255
test_images = test_images.reshape((10000, 28, 28, 1)).astype('float32') / 255
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

# Create a simple neural network model
def create_model():
    model = Sequential([
        tf.keras.layers.Flatten(input_shape=(28, 28, 1)),
        tf.keras.layers.Dense(128, activation='relu'),
        tf.keras.layers.Dropout(0.2),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dropout(0.2),
        tf.keras.layers.Dense(10, activation='softmax')
    ])
    return model

# Create and compile the original model
model = create_model()
model.compile(optimizer='adam',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Train the original model
model.fit(train_images, train_labels, epochs=5, batch_size=64, validation_split=0.2)

# Prune the model
# Specify the pruning parameters
pruning_params = {
    'pruning_schedule': sparsity.PolynomialDecay(initial_sparsity=0.50,
                                                 final_sparsity=0.90,
                                                 begin_step=0,
                                                 end_step=2000,
                                                 frequency=100)
}

# Create a pruned model
pruned_model = sparsity.prune_low_magnitude(create_model(), **pruning_params)

# Compile the pruned model
pruned_model.compile(optimizer='adam',
                     loss='categorical_crossentropy',
                     metrics=['accuracy'])

# Train the pruned model (fine-tuning)
pruned_model.fit(train_images, train_labels, epochs=2, batch_size=64, validation_split=0.2)

# Strip pruning wrAppers to create a smaller and faster model
final_model = sparsity.strip_pruning(pruned_model)

# Evaluate the final pruned model
test_loss, test_acc = final_model.evaluate(test_images, test_labels)
print(f'Test accuracy after pruning: {test_acc * 100:.2f}%')

量化

我认为没有错的说量化可能是目前最广泛使用的压缩技术。同样,基本思想很简单。通常,我们使用32位浮点数表示神经网络的参数。但如果我们使用更低精度的数值呢?我们可以使用16位、8位、4位,甚至1位,并且拥有二进制网络!

这意味着什么?通过使用较低精度的数字,模型将更轻,更小,但也会失去精度,提供比原始模型更近似的结果。当我们需要在边缘设备上部署时,特别是在某些特殊硬件上,如智能手机上,这是一种经常使用的技术,因为它允许我们大大缩小网络的大小。许多框架允许轻松应用量化,例如TensorFlow Lite、PyTorch或TensorRT。

量化可以在训练前应用,因此我们直接截断了一个网络,其参数只能在某个范围内取值,或者在训练后应用,因此最终会对参数的值进行四舍五入。在这里,我们再次快速看一下如何在Python中应用量化。


import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten, Dropout
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
import numpy as np

# Load the MNIST dataset
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

# Preprocess the data
train_images = train_images.reshape((60000, 28, 28, 1)).astype('float32') / 255
test_images = test_images.reshape((10000, 28, 28, 1)).astype('float32') / 255
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

# Create a simple neural network model
def create_model():
    model = Sequential([
        Flatten(input_shape=(28, 28, 1)),
        Dense(128, activation='relu'),
        Dropout(0.2),
        Dense(64, activation='relu'),
        Dropout(0.2),
        Dense(10, activation='softmax')
    ])
    return model

# Create and compile the original model
model = create_model()
model.compile(optimizer='adam',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Train the original model
model.fit(train_images, train_labels, epochs=5, batch_size=64, validation_split=0.2)

# Quantize the model to 8-bit integers
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
quantized_model = converter.convert()

# Save the quantized model to a file
with open('quantized_model.tflite', 'wb') as f:
    f.write(quantized_model)

# Load the quantized model for inference
interpreter = tf.lite.Interpreter(model_path='quantized_model.tflite')
interpreter.allocate_tensors()

# Evaluate the quantized model
test_loss, test_acc = 0.0, 0.0
for i in range(len(test_images)):
    input_data = np.array([test_images[i]], dtype=np.float32)
    interpreter.set_tensor(interpreter.get_input_details()[0]['index'], input_data)
    interpreter.invoke()
    output_data = interpreter.get_tensor(interpreter.get_output_details()[0]['index'])
    test_loss += tf.keras.losses.categorical_crossentropy(test_labels[i], output_data).numpy()
    test_acc += np.argmax(test_labels[i]) == np.argmax(output_data)

test_loss /= len(test_images)
test_acc /= len(test_images)

print(f'Test accuracy after quantization: {test_acc * 100:.2f}%')

结论

在本文中,我们探讨了几种模型压缩方法,以加速模型推断阶段,这对于生产中的模型来说可能是一个关键要求。特别是,我们关注了低秩分解、知识蒸馏、剪枝和量化等方法,解释了基本思想,并展示了Python中的简单实现。模型压缩对于在具有有限资源(RAM、GPU等)的特定硬件上部署模型也非常有用,比如智能手机。



Tags:深度学习   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
深度学习中的梯度裁剪策略比较
在深度学习的训练过程中,梯度裁剪是一种常用的技术,用于防止梯度爆炸问题,即梯度的值变得非常大,导致模型训练不稳定。梯度裁剪通过限制梯度的最大值或最小值,帮助模型更稳定地收...【详细内容】
2024-03-05  Search: 深度学习  点击:(28)  评论:(0)  加入收藏
深度学习的未来:趋势和新兴技术
深度学习是人工智能(AI)的一个子集,持续推动技术进步,塑造机器感知、分析和响应数据的方式。本文将探索将在未来几年重新定义人工智能格局的最新趋势和新兴技术。模型规模指数...【详细内容】
2024-02-19  Search: 深度学习  点击:(50)  评论:(0)  加入收藏
基于深度学习的虚拟现实图像生成技术研究与应用
随着虚拟现实(VirtualReality,简称VR)技术的快速发展,人们对于逼真、沉浸式的虚拟体验需求不断增加。而虚拟现实图像生成是VR技术中的重要环节之一,它通过模拟和生成逼真的虚拟场...【详细内容】
2024-01-04  Search: 深度学习  点击:(139)  评论:(0)  加入收藏
深度学习框架解读—Yolov5/Yolov7/Halcon对比分析
作为一名机器视觉深度学习算法工程师,我从技术实现、性能、适用场景和易用性等方面来评价YOLOv5、YOLOv7和Halcon中的深度学习框架。以YOLOv5和YOLOv7进行比较,并结合Halcon的...【详细内容】
2024-01-03  Search: 深度学习  点击:(46)  评论:(0)  加入收藏
基于深度学习的人体姿态估计技术探索
人体姿态估计是计算机视觉领域的重要研究方向之一,旨在通过对图像或视频中人体姿势的分析和理解,推测出人体的关节点位置和姿态信息。近年来,随着深度学习技术的进步和应用,基于...【详细内容】
2024-01-02  Search: 深度学习  点击:(73)  评论:(0)  加入收藏
深度学习中的图像生成对抗攻击与防御方法综述
随着深度学习技术的快速发展,图像生成对抗攻击成为了一个备受关注的研究领域。图像生成对抗攻击是指通过对抗样本的生成,欺骗深度学习模型,使其产生错误的分类结果。为了应对这...【详细内容】
2023-11-28  Search: 深度学习  点击:(234)  评论:(0)  加入收藏
面向时间序列数据的深度学习应用综述
时间序列数据是指按照时间顺序排列的数据集合,如股票价格、气象数据、交通流量等。深度学习作为一种强大的机器学习技术,已经在时间序列数据分析中取得了显著的成果。本文将对...【详细内容】
2023-11-27  Search: 深度学习  点击:(112)  评论:(0)  加入收藏
深度学习之模型压缩、加速模型推理
简介当将一个机器学习模型部署到生产环境中时,通常需要满足一些在模型原型阶段没有考虑到的要求。例如,在生产中使用的模型将不得不处理来自不同用户的大量请求。因此,您将希望...【详细内容】
2023-11-20  Search: 深度学习  点击:(202)  评论:(0)  加入收藏
深度学习模型与人类认知的对比与解释
深度学习模型是近年来人工智能领域的热门研究方向,其在图像识别、自然语言处理等任务上取得了令人瞩目的成果。然而,与人类认知相比,深度学习模型仍存在一些差距。本文将探讨深...【详细内容】
2023-11-17  Search: 深度学习  点击:(160)  评论:(0)  加入收藏
超大规模数据下的分布式深度学习模型训练技术
随着互联网和物联网的快速发展,数据规模呈现爆炸式增长。在这样的背景下,如何高效地训练深度学习模型成为了一个亟待解决的问题。传统的深度学习模型训练方式往往需要大量的计...【详细内容】
2023-11-08  Search: 深度学习  点击:(255)  评论:(0)  加入收藏
▌简易百科推荐
即将过时的 5 种软件开发技能!
作者 | Eran Yahav编译 | 言征出品 | 51CTO技术栈(微信号:blog51cto) 时至今日,AI编码工具已经进化到足够强大了吗?这未必好回答,但从2023 年 Stack Overflow 上的调查数据来看,44%...【详细内容】
2024-04-03    51CTO  Tags:软件开发   点击:(5)  评论:(0)  加入收藏
跳转链接代码怎么写?
在网页开发中,跳转链接是一项常见的功能。然而,对于非技术人员来说,编写跳转链接代码可能会显得有些困难。不用担心!我们可以借助外链平台来简化操作,即使没有编程经验,也能轻松实...【详细内容】
2024-03-27  蓝色天纪    Tags:跳转链接   点击:(12)  评论:(0)  加入收藏
中台亡了,问题到底出在哪里?
曾几何时,中台一度被当做“变革灵药”,嫁接在“前台作战单元”和“后台资源部门”之间,实现企业各业务线的“打通”和全域业务能力集成,提高开发和服务效率。但在中台如火如荼之...【详细内容】
2024-03-27  dbaplus社群    Tags:中台   点击:(8)  评论:(0)  加入收藏
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  dbaplus社群    Tags:Bug   点击:(5)  评论:(0)  加入收藏
我们一起聊聊什么是正向代理和反向代理
从字面意思上看,代理就是代替处理的意思,一个对象有能力代替另一个对象处理某一件事。代理,这个词在我们的日常生活中也不陌生,比如在购物、旅游等场景中,我们经常会委托别人代替...【详细内容】
2024-03-26  萤火架构  微信公众号  Tags:正向代理   点击:(10)  评论:(0)  加入收藏
看一遍就理解:IO模型详解
前言大家好,我是程序员田螺。今天我们一起来学习IO模型。在本文开始前呢,先问问大家几个问题哈~什么是IO呢?什么是阻塞非阻塞IO?什么是同步异步IO?什么是IO多路复用?select/epoll...【详细内容】
2024-03-26  捡田螺的小男孩  微信公众号  Tags:IO模型   点击:(8)  评论:(0)  加入收藏
为什么都说 HashMap 是线程不安全的?
做Java开发的人,应该都用过 HashMap 这种集合。今天就和大家来聊聊,为什么 HashMap 是线程不安全的。1.HashMap 数据结构简单来说,HashMap 基于哈希表实现。它使用键的哈希码来...【详细内容】
2024-03-22  Java技术指北  微信公众号  Tags:HashMap   点击:(11)  评论:(0)  加入收藏
如何从头开始编写LoRA代码,这有一份教程
选自 lightning.ai作者:Sebastian Raschka机器之心编译编辑:陈萍作者表示:在各种有效的 LLM 微调方法中,LoRA 仍然是他的首选。LoRA(Low-Rank Adaptation)作为一种用于微调 LLM(大...【详细内容】
2024-03-21  机器之心Pro    Tags:LoRA   点击:(12)  评论:(0)  加入收藏
这样搭建日志中心,传统的ELK就扔了吧!
最近客户有个新需求,就是想查看网站的访问情况。由于网站没有做google的统计和百度的统计,所以访问情况,只能通过日志查看,通过脚本的形式给客户导出也不太实际,给客户写个简单的...【详细内容】
2024-03-20  dbaplus社群    Tags:日志   点击:(4)  评论:(0)  加入收藏
Kubernetes 究竟有没有 LTS?
从一个有趣的问题引出很多人都在关注的 Kubernetes LTS 的问题。有趣的问题2019 年,一个名为 apiserver LoopbackClient Server cert expired after 1 year[1] 的 issue 中提...【详细内容】
2024-03-15  云原生散修  微信公众号  Tags:Kubernetes   点击:(5)  评论:(0)  加入收藏
站内最新
站内热门
站内头条