使用PyTorch进行知识蒸馏的代码示例

news2024/12/23 2:11:11

随着机器学习模型的复杂性和能力不断增加。提高大型复杂模型在小数据集性能的一种有效技术是知识蒸馏,它包括训练一个更小、更有效的模型来模仿一个更大的“教师”模型的行为。

在本文中,我们将探索知识蒸馏的概念,以及如何在PyTorch中实现它。我们将看到如何使用它将一个庞大、笨重的模型压缩成一个更小、更高效的模型,并且仍然保留原始模型的准确性和性能。

我们首先定义知识蒸馏要解决的问题。

我们训练了一个大型深度神经网络来执行复杂的任务,比如图像分类或机器翻译。这个模型可能有数千层和数百万个参数,这使得它很难部署在现实应用程序、边缘设备等中。并且这个超大的模型还需要大量的计算资源来运行,这使得它在一些资源受限的平台上无法工作。

解决这个问题的一种方法是使用知识蒸馏将大模型压缩成较小的模型。这个过程包括训练一个较小的模型来模仿给定任务中大型模型的行为。

我们将使用来自Kaggle的胸部x光数据集进行肺炎分类来进行知识蒸馏的示例。我们使用的数据集被组织成3个文件夹(train, test, val),并包含每个图像类别的子文件夹(Pneumonia/Normal)。共有5,863张x射线图像(JPEG)和2个类别(肺炎/正常)。

比较一下这两个类的图片:

数据的加载和预处理与我们是否使用知识蒸馏或特定模型无关,代码片段可能如下所示:

 transforms_train = transforms.Compose([
     transforms.Resize((224, 224)),
     transforms.RandomHorizontalFlip(),
     transforms.ToTensor(),
     transforms.Normalize([0.485, 0.456, 0.406],
                          [0.229, 0.224, 0.225])])
 
 transforms_test = transforms.Compose([
     transforms.Resize((224, 224)),
     transforms.ToTensor(),
     transforms.Normalize([0.485, 0.456, 0.406],
                          [0.229, 0.224, 0.225])])
 
 train_data = ImageFolder(root=train_dir, transform=transforms_train)
 test_data = ImageFolder(root=test_dir, transform=transforms_test)
 
 train_loader = DataLoader(train_data, batch_size=32, shuffle=True)
 test_loader = DataLoader(test_data, batch_size=32, shuffle=True)

教师模型

在这个背景中教师模型我们使用Resnet-18并且在这个数据集上进行了微调。

 import torch
 import torch.nn as nn
 import torchvision
 
 class TeacherNet(nn.Module):
     def __init__(self):
         super().__init__()
         self.model = torchvision.models.resnet18(pretrained=True)
         for params in self.model.parameters():
             params.requires_grad_ = False
 
         n_filters = self.model.fc.in_features
         self.model.fc = nn.Linear(n_filters, 2)
 
     def forward(self, x):
         x = self.model(x)
         return x

微调训练的代码如下

 def train(model, train_loader, test_loader, optimizer, criterion, device):
     dataloaders = {'train': train_loader, 'val': test_loader}
 
     for epoch in range(30):
         print('Epoch {}/{}'.format(epoch, num_epochs - 1))
         print('-' * 10)
 
         for phase in ['train', 'val']:
             if phase == 'train':
                 model.train()
             else:
                 model.eval()
 
             running_loss = 0.0
             running_corrects = 0
 
             for inputs, labels in tqdm.tqdm(dataloaders[phase]):
                 inputs = inputs.to(device)
                 labels = labels.to(device)
 
                 optimizer.zero_grad()
 
                 with torch.set_grad_enabled(phase == 'train'):
                     outputs = model(inputs)
                     loss = criterion(outputs, labels)
 
                     _, preds = torch.max(outputs, 1)
 
                     if phase == 'train':
                         loss.backward()
                         optimizer.step()
 
                 running_loss += loss.item() * inputs.size(0)
                 running_corrects += torch.sum(preds == labels.data)
 
             epoch_loss = running_loss / len(dataloaders[phase].dataset)
             epoch_acc = running_corrects.double() / len(dataloaders[phase].dataset)
 
             print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))

这是一个标准的微调训练步骤,训练后我们可以看到该模型在测试集上达到了91%的准确性,这也就是我们没有选择更大模型的原因,因为作为测试91的准确率已经足够作为基类模型来使用了。

我们知道模型有1170万个参数,因此不一定能够适应边缘设备或其他特定场景。

学生模型

我们的学生是一个更浅的CNN,只有几层和大约100k个参数。

 class StudentNet(nn.Module):
     def __init__(self):
         super().__init__()
         self.layer1 = nn.Sequential(
             nn.Conv2d(3, 4, kernel_size=3, padding=1),
             nn.BatchNorm2d(4),
             nn.ReLU(),
             nn.MaxPool2d(kernel_size=2, stride=2)
         )
         self.fc = nn.Linear(4 * 112 * 112, 2)
 
     def forward(self, x):
         out = self.layer1(x)
         out = out.view(out.size(0), -1)
         out = self.fc(out)
         return out

看代码就非常的简单,对吧。

如果我可以简单地训练这个更小的神经网络,我为什么还要费心进行知识蒸馏呢?我们最后会附上我们通过超参数调整等手段从头训练这个网络的结果最为对比。

但是现在我们继续我们的知识蒸馏的步骤

知识蒸馏训练

训练的基本步骤是不变的,但是区别是如何计算最终的训练损失,我们将使用教师模型损失,学生模型的损失和蒸馏损失一起来计算最终的损失。

 class DistillationLoss:
     def __init__(self):
         self.student_loss = nn.CrossEntropyLoss()
         self.distillation_loss = nn.KLDivLoss()
         self.temperature = 1
         self.alpha = 0.25
 
     def __call__(self, student_logits, student_target_loss, teacher_logits):
         distillation_loss = self.distillation_loss(F.log_softmax(student_logits / self.temperature, dim=1),
                                                    F.softmax(teacher_logits / self.temperature, dim=1))
 
         loss = (1 - self.alpha) * student_target_loss + self.alpha * distillation_loss
         return loss

损失函数是下面两个东西的加权和:

  • 分类损失,称为student_target_loss
  • 蒸馏损失,学生对数和教师对数之间的交叉熵损失

简单的讲,我们的教师模型需要教导学生如何“思考”的,这就是指的是它的不确定性;例如,如果教师模型的最终输出概率是[0.53,0.47],我们希望学生也得到同样类似结果,这些预测之间的差异就是蒸馏损失。

为了控制损失,还有有两个主要参数:

  • 蒸馏损失的权重:0意味着我们只考虑蒸馏损失,反之亦然。
  • 温度:衡量教师预测的不确定性。

在上面的要点中,alpha和temperature的值都是根据我们尝试过一些组合得到的最佳结果的值。

结果对比

这是这个实验的表格摘要。

我们可以清楚地看到使用更小(99.14%),更浅的CNN所获得的巨大好处:与无蒸馏训练相比,准确率提升了10点,并且比Resnet-18快11倍!也就是说,我们的小模型真的从大模型中学到了有用的东西。

https://avoid.overfit.cn/post/482f5c111e7344179e2aba57865427ea

作者:Alessandro Lamberti

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/95878.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机毕设Python+Vue校园舆情监控系统(程序+LW+部署)

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

C#修改富文本框(RichTextBox)指定内容颜色

1.前言 最近给客户做了一个协议解包与组包的工具,以便于他们给终端客户或者集成商使用,让客户能够快速集成产品协议,降低客户集成工作量,产品协议是基于JT/T808,但是有增加了自己的一些特殊修改。 客户使用的是C#开发…

数据库原理及MySQL应用 | 并发控制

无论何时,只要有多个查询需要在同一时刻修改数据,都会产生并发控制问题,MySQL通过多版本并发控制和加锁实现并发控制。 多用户并发执行事务访问同一个数据库时,可能引发脏写、脏读、不可重复读、幻读等一致性问题。并发事务访问相同记录的情况&#xff0…

Golang 【basic_leaming】数组

阅读目录Array(数组)数组定义数组的初始化方法一方法二方法三数组的遍历数组是值类型多维数组二维数组的定义二维数组的遍历数组练习题1、请求出一个数组的和以及平均值 for-range2、请求出一个数组的最大值,并得到对应的下标3、从数组 [1, 3, 5, 7, 8] 中找出和为 …

探花交友_第10章_搭建后台系统(新版)

探花交友_第10章_搭建后台系统(新版) 文章目录探花交友_第10章_搭建后台系统(新版)1.1 概述1.2 API网关1.2.1 搭建网关依赖引导类跨域问题配置类配置文件测试1.2.2 配置鉴权管理器1.3 Nacos配置中心1.3.1 添加依赖1.3.2 添加boots…

33.数据统计

数据统计 后台系统首页中,显示各种统计数据,比如:累计用户数、新增用户数、登录次数等内容。 解决方案 数据库表分析 一、数据采集 需求: 1、探花系统将用户操作日志写入RabbitMQ 2、管理后台获取最新消息,构造日…

SpringBoot+微信小程序实现的云音乐小程序系统 附带详细运行指导视频

文章目录一、项目演示二、项目介绍三、项目运行截图四、主要代码一、项目演示 项目演示地址: 视频地址 二、项目介绍 项目描述:这是一个基于SpringBoot微信小程序框架开发的云音乐微信小程序系统。首先,这是一个前后端分离的项目&#xff…

C语言百日刷题第十五天

前言 今天是刷题第15天,放弃不难,但坚持一定很酷~ 再刷一套模拟题 C语言百日刷题第十五天前言一、选择题二、判断题三、多选题四、填空题五、分析程序题一、选择题 1.下列选项中,不属于开发一个C语言应用程序的具体实现步骤的是&#xf…

为什么网络应用程序是今年的主要攻击媒介之一

网络犯罪分子在绕过最新的网络应用程序防火墙方面的独创性正在将互联网应用程序变成今年增长最快的攻击媒介。面向公众的 Web 应用程序现在是渗透组织边界的最广泛使用的攻击媒介。 根据卡巴斯基全球应急响应团队最近的一份报告,始于 Web 应用程序的攻击从 2020 年…

ocker高级篇1-dockeran安装mysql主从复制

大家好,咱们前面通过十篇的文章介绍了docker的基础篇,从本篇开始,咱们的《docker学习系列》将要进入到高级篇阶段(基础篇大家可以查看之前发布的文章)。 咱们先来介绍:docker复杂方式安装软件。通过按照mysql\redis两个案例来讲解…

XC6SLX100-3FGG484C规格、XC7A15T-2CPG236I产品概述及应用

Spartan-6系列提供领先的系统集成能力,为大批量应用提供最低的总成本。这个由13个成员组成的家族扩展了逻辑单元的密度,从3840个扩展到147443个,功耗仅为之前斯巴达家族的一半,并且具有更快、更全面的连接。 Spartan-6系列基于成熟…

拯救动画卡顿之FLIP

前置知识 什么是FPS FPS是浏览器的每秒的渲染帧数,也就是浏览器切换画面的次数,大多数设备的刷新率都是60FPS,一般来说FPS越低页面就会越卡顿。 什么是像素管道? 像素管道是浏览器单个帧的渲染流水线,如果其中有某…

vue数据双向绑定

5.Vue数据双向绑定 5.1.什么是双向数据绑定 Vue.js 是一个 MVVM 框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化。这也算是 Vue.js 的精髓之处了。 值得…

[ MessAuto ]: 短信验证码自动填充,理论支持所有浏览器或 APP, Only For Mac

MessAuto 开源地址:https://github.com/LeeeSe/MessAuto MessAuto 是一款 macOS 平台 自动提取 短信验证码并 粘贴回车 的软件,百分百由Rust开发,适用于任何APP。 特点: 轻量:程序占用存储 1.8 M,占用内…

NLP学习笔记(三) GRU基本介绍

大家好,我是半虹,这篇文章来讲门控循环单元 (Gated Recurrent Unit, GRU) 文章行文思路如下: 首先通过长短期记忆网络引出为什么需要门控循环单元然后介绍门控循环单元的核心思想与运作方式最后通过简洁的代码深入理解门控循环单元的运作方…

奇舞周刊 476 期:代码在内存中的 “形状”

记得点击文章末尾的“ 阅读原文 ”查看哟~下面先一起看下本期周刊 摘要 吧~奇舞推荐■ ■ ■代码在内存中的 “形状”众所周知,js 的基本数据类型有 number、string、boolean、null、undefined 等。那么问题来了 typeof null 和 typeof undefined 分别是什么呢&…

[附源码]Node.js计算机毕业设计果蔬预约种植管理系统Express

项目运行 环境配置: Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术: Express框架 Node.js Vue 等等组成,B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境:最好是Nodejs最新版,我…

[内网渗透]—NTLM网络认证及NTLM-Relay攻击

NTML网络认证 Windows认证分为本地认证和网络认证,当我们开机登录用户账户时,就需要将lsass.exe进程转换的明文密码hash与 sam文件进行比对,这种方式即为——本地认证 而当我们访问同一局域网的一台主机上的SMB共享时,需要提供凭证通过验证才能访问,这个过程就会设计win…

【C++】list 的模拟实现

​🌠 作者:阿亮joy. 🎆专栏:《吃透西嘎嘎》 🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根 目录👉前言&…

Halcon条码和二维码质量评级

现在各行各业的人们都使用条码/二维码从生产阶段到销售点全程追踪他们 的产品。那么怎么验证生产出来的具有可读性,码的质量等级如何呢? 其实ISO行业标准已经给出了如何评估码的质量等级的标准,以下三种主要验证标准用于确定一维条码、二维码…