通俗易懂的知识蒸馏 Knowledge Distillation(下)——代码实践(附详细注释)

这篇具有很好参考价值的文章主要介绍了通俗易懂的知识蒸馏 Knowledge Distillation(下)——代码实践(附详细注释)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

第一步:导入所需要的包

import math
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision import datasets, transforms
import torch.utils.data

torch.manual_seed(0)		# 为CPU设置种子
torch.cuda.manual_seed(0)	# 为GPU设置种子

第二步:定义教师模型

教师模型网络结构(此处仅举一个例子):卷积层-卷积层-dropout-dropout-全连接层-全连接层

class TeacherNet(nn.Module):
    def __init__(self):
        super(TeacherNet, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, 1)		# 卷积层
        self.conv2 = nn.Conv2d(32, 64, 3, 1)	# 卷积层
        self.dropout1 = nn.Dropout2d(0.3)		# dropout
        self.dropout2 = nn.Dropout2d(0.5)		# dropout
        self.fc1 = nn.Linear(9216, 128)			# 全连接层
        self.fc2 = nn.Linear(128, 10)			# 全连接层

    def forward(self, x):
        x = self.conv1(x)
        x = F.relu(x)			# 激活函数
        x = self.conv2(x)
        x = F.relu(x)
        x = F.max_pool2d(x, 2)
        x = self.dropout1(x)
        x = torch.flatten(x, 1)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.dropout2(x)
        output = self.fc2(x)
        return output

第三步:定义训练教师模型方法

正常的定义一个神经网络模型

def train_teacher(model, device, train_loader, optimizer, epoch):
    model.train()
    trained_samples = 0
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)	# 将数据转移到CPU/GPU
        optimizer.zero_grad()	# 优化器将梯度全部置为0
        output = model(data)	# 数据经过模型向前传播
        loss = F.cross_entropy(output, target)  # 计算损失函数
        loss.backward()			# 反向传播
        optimizer.step()		# 更新梯度

        trained_samples += len(data)
        progress = math.ceil(batch_idx / len(train_loader) * 50) # 计算训练进度
        print("\rTrain epoch %d: %d/%d, [%-51s] %d%%" %
              (epoch, trained_samples, len(train_loader.dataset),
               '-' * progress + '>', progress * 2), end='')

第四步:定义教师模型测试方法

正常的定义一个神经网络模型

def test_teacher(model, device, test_loader):
    model.eval()  # 设置为评估模式
    test_loss = 0
    correct = 0
    with torch.no_grad():  # 不计算梯度,减少计算量
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)  # 将数据转移到CPU/GPU
            output = model(data)  # 经过模型正向传播得到结果
            test_loss += F.cross_entropy(output, target, reduction='sum').item()  # 计算总的损失函数
            pred = output.argmax(dim=1, keepdim=True)  # 获取最大对数概率索引
            correct += pred.eq(target.view_as(pred)).sum().item()  # pred.eq(target.view_as(pred)) 会返回一个布尔张量,其中每个元素表示预测值是否等于目标值。然后,.sum().item() 会将所有为 True 的元素相加,从而得到正确分类的数量。 

    test_loss /= len(test_loader.dataset)  # 计算损失函数

    print('\nTest: average loss: {:.4f}, accuracy: {}/{} ({:.0f}%)'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))
    return test_loss, correct / len(test_loader.dataset)

第五步:定义教师模型主函数

整体也和正常模型一样,但是这里使用了teacher_history去保留需要知识蒸馏的数据。

def teacher_main():
    epochs = 10
    batch_size = 64
    torch.manual_seed(0)
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # 使用的设备类型
    
	# 导入训练集
    train_loader = torch.utils.data.DataLoader(
        datasets.MNIST('../data/MNIST', train=True, download=True,
                       transform=transforms.Compose([
                           transforms.ToTensor(),
                           transforms.Normalize((0.1307,), (0.3081,))  # 数据正则化
                       ])),
        batch_size=batch_size, shuffle=True)
    
    # 导入测试集
    test_loader = torch.utils.data.DataLoader(
        datasets.MNIST('../data/MNIST', train=False, download=True, transform=transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize((0.1307,), (0.3081,))	# 数据正则化
        ])),
        batch_size=1000, shuffle=True)

    model = TeacherNet().to(device)  # 传输经过教师模型网络
    optimizer = torch.optim.Adadelta(model.parameters())  # 使用Adadelta优化器
    
    teacher_history = []  # 记录教师得到结果的历史
    for epoch in range(1, epochs + 1):
        train_teacher(model, device, train_loader, optimizer, epoch)  # 开始训练模型
        loss, acc = test_teacher(model, device, test_loader)  # 计算损失函数和准确率
        teacher_history.append((loss, acc))  # 记录教师模型得到的历史数据

    torch.save(model.state_dict(), "teacher.pt")  # 保存到权重文件
    return model, teacher_history

第六步:开始训练教师模型

# 训练教师网络
teacher_model, teacher_history = teacher_main()

第七步:定义学生模型网络结构

学生模型的网络结构定义时一般要比教师模型简单一些,这样才能达到知识蒸馏轻量化的目的

class StudentNet(nn.Module):
    def __init__(self):
        super(StudentNet, self).__init__()
        self.fc1 = nn.Linear(28 * 28, 128)	# 全连接层
        self.fc2 = nn.Linear(128, 64)		# 全连接层
        self.fc3 = nn.Linear(64, 10)		# 全连接层

    def forward(self, x):
        x = torch.flatten(x, 1)		# 将输入张量沿着第二维度平
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        output = F.relu(self.fc3(x))
        return output

第八步:定义知识蒸馏方法

这里定义知识蒸馏主要是实现其损失函数。

def distillation(y, labels, teacher_scores, temp, alpha):
    return nn.KLDivLoss()(F.log_softmax(y / temp, dim=1), F.softmax(teacher_scores / temp, dim=1)) * (
            temp * temp * 2.0 * alpha) + F.cross_entropy(y, labels) * (1. - alpha)

我们这里写一下这个公式:
K L D i v L o s s ( l o g ( s o f t m a x ( y t e m p ) ) , s o f t m a x ( t e a c h e r   s c o r e s t e m p ) ) ∗ 2 α t e m p 2 C r o s s E n t r o p y ( y , l a b e l s ) ( 1 − α ) KLDivLoss(log(softmax(\frac{y}{temp})),softmax(\frac{teacher~scores}{temp}))*2\alpha temp^2\\CrossEntropy(y,labels)(1-\alpha) KLDivLoss(log(softmax(tempy)),softmax(tempteacher scores))2αtemp2CrossEntropy(y,labels)(1α)
其中 α \alpha α 1 − α 1-\alpha 1α为系数, t e m p 2 temp^2 temp2用于调节量纲。

第九步:定义学生模型训练和测试方法

学生模型训练部分和教师模型训练部分基本一样,除了两个部分。

第一个部分是,需要重点关注teacher_output = teacher_output.detach()切断教师模型反向传播这一行。

第二个部分是,这里训练使用的损失函数是上面定义的知识蒸馏的损失函数

def train_student_kd(model, device, train_loader, optimizer, epoch):
    model.train()
    trained_samples = 0
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)  # 学生模型前向传播
        teacher_output = teacher_model(data)  # 教师模型前向传播
        teacher_output = teacher_output.detach()  # 切断老师网络的反向传播
        loss = distillation(output, target, teacher_output, temp=5.0, alpha=0.7)  # 计算总损失函数,这里使用的是知识蒸馏的损失函数
        loss.backward()  # 反向传播
        optimizer.step()  # 更新参数

        trained_samples += len(data)
        progress = math.ceil(batch_idx / len(train_loader) * 50)
        print("\rTrain epoch %d: %d/%d, [%-51s] %d%%" %
              (epoch, trained_samples, len(train_loader.dataset),
               '-' * progress + '>', progress * 2), end='')

def test_student_kd(model, device, test_loader):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            test_loss += F.cross_entropy(output, target, reduction='sum').item()  # 计算总的损失函数
            pred = output.argmax(dim=1, keepdim=True)  # 获取最大对数概率索引
            correct += pred.eq(target.view_as(pred)).sum().item()  # 计算准确率
    test_loss /= len(test_loader.dataset)

    print('\nTest: average loss: {:.4f}, accuracy: {}/{} ({:.0f}%)'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))
    return test_loss, correct / len(test_loader.dataset)

第十步:定义学生模型主函数

def student_kd_main():
    epochs = 10
    batch_size = 64
    torch.manual_seed(0)
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
	# 加载训练集
    train_loader = torch.utils.data.DataLoader(
        datasets.MNIST('../data/MNIST', train=True, download=True,
                       transform=transforms.Compose([
                           transforms.ToTensor(),
                           transforms.Normalize((0.1307,), (0.3081,))
                       ])),
        batch_size=batch_size, shuffle=True)
    # 加载测试集
    test_loader = torch.utils.data.DataLoader(
        datasets.MNIST('../data/MNIST', train=False, download=True, transform=transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize((0.1307,), (0.3081,))
        ])),
        batch_size=1000, shuffle=True)
	# 加载学生模型
    model = StudentNet().to(device)
    optimizer = torch.optim.Adadelta(model.parameters())
    
    student_history = []  # 记录学生训练的模型
    for epoch in range(1, epochs + 1):
        train_student_kd(model, device, train_loader, optimizer, epoch)
        loss, acc = test_student_kd(model, device, test_loader)
        student_history.append((loss, acc))

    torch.save(model.state_dict(), "student_kd.pt")
    return model, student_history
student_kd_model, student_kd_history = student_kd_main()

知识蒸馏步骤总结

(1)先训练教师模型,定义教师模型的训练方法和测试方法

(2)定义知识蒸馏损失函数

(3)再训练学生模型,定义学生模型的训练方法和测试方法

(4)训练学生模型的时候需要将教师模型得到数据输出经过知识蒸馏作为输入,并且要阻断教师模型的反向传播,并利用知识蒸馏损失函数进行反向传播

(5)训练结束后得到学生模型文章来源地址https://www.toymoban.com/news/detail-524288.html

到了这里,关于通俗易懂的知识蒸馏 Knowledge Distillation(下)——代码实践(附详细注释)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 扩散模型相关论文阅读,扩散模型和知识蒸馏的结合提升预测速度:Progressive Distillation for Fast Sampling of Diffusion Models

    谷歌research的成果,ICLR 2022 https://arxiv.org/abs/2202.00512 tenserflow官方开源代码: https://github.com/google-research/google-research/tree/master/diffusion_distillation pytorch非官方代码:https://github.com/lucidrains/imagen-pytorch 1.扩散模型虽然取得了很好的效果,但是预测速度慢。 2.作者提出了一种逐步蒸馏

    2024年02月16日
    浏览(39)
  • [读论文][backbone]Knowledge Diffusion for Distillation

    DiffKD 摘要 The representation gap between teacher and student is an emerging topic in knowledge distillation (KD). To reduce the gap and improve the performance, current methods often resort to complicated training schemes, loss functions, and feature alignments, which are task-specific and feature-specific. In this paper, we state that the essence of the

    2024年02月08日
    浏览(56)
  • 高频知识汇总 |【计算机网络】面试题汇总(万字长文通俗易懂)

    这篇【计算机网络】是我在学习时自己整理的,大部分都是按我个人的理解来写的答案。废话不多说直接铺干货。 这就一道题,回答时遵循如下原则即可: 横向看(为哪两个平行的东西提供服务) 纵向看(为上层提供什么服务) 协议举例 应用层: 为应用程序间提供通信和

    2024年02月09日
    浏览(51)
  • 建造者模式深入理解:演示建造单个和多个产品的实践,结合模板模式,通俗易懂

    首先呢看下建造者的定义是什么样的,先读一遍 建造者模式 (Builder Pattern)是一种创建型设计模式,它主要用于将一个复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建不同的表现形式。这种模式通过一系列可重用的独立的类(称为建造者或构建器)来一

    2024年01月22日
    浏览(44)
  • 深度学习概念(术语):Fine-tuning、Knowledge Distillation, etc

    这里的相关概念都是基于已有预训练模型,就是模型本身已经训练好,有一定泛化能力。需要“再加工”满足别的任务需求。 进入后GPT时代,对模型的Fine-tuning也将成为趋势,借此机会,我来科普下相关概念。 有些人认为微调和训练没有区别,都是训练模型,但是微调是在原

    2024年02月09日
    浏览(42)
  • 【论文阅读】SKDBERT: Compressing BERT via Stochastic Knowledge Distillation

    2022-2023年论文系列之模型轻量化和推理加速 通过Connected Papers搜索引用PaBEE/DeeBERT/FastBERT的最新工作,涵盖: 模型推理加速 边缘设备应用 生成模型 BERT模型 知识蒸馏 SmartBERT: A Promotion of Dynamic Early Exiting Mechanism for Accelerating BERT Inference SKDBERT: Compressing BERT via Stochastic Knowledge Di

    2024年02月12日
    浏览(39)
  • 回溯法解01背包问题(最通俗易懂,附C++代码)

    01背包问题是算法中的经典问题,问题描述如下: 对于给定的N个物品,第i个物品的重量为Wi,价值为Vi,对于一个最多能装重量C的背包,应该如何选择放入包中的物品,使得包中物品的总价值最大? 回溯法的本质其实就是一种蛮力法,只是通过一定的方法可以使得蛮力法中

    2023年04月08日
    浏览(36)
  • SVM(支持向量机)进行分类的原理和python代码----通俗易懂

    SVM(支持向量机,Support Vector Machine)是一种非常流行的机器学习算法,可用于二分类和多分类问题。其基本思想是通过在不同类别的样本之间构建最大化分类间隔的线性或非线性超平面来实现分类。 SVM分类的基本步骤如下: 根据训练集数据,选取最优的超平面(通常为线性

    2024年02月11日
    浏览(56)
  • 实际开发中常用的设计模式--------策略模式(知识跟业务场景结合)-----小白也能看懂(通俗易懂版本)

    1.策略模式定义: 策略模式是一种行为型设计模式,它允许在运行时动态地改变对象的行为。策略模式将将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换从而使得算法的变化不会影响到客户端 2.简单的策略模式示例代码: 在上述代码中,SortStra

    2024年02月13日
    浏览(51)
  • 论文笔记|CVPR2023:Supervised Masked Knowledge Distillation for Few-Shot Transformers

    这篇论文的题目是 用于小样本Transformers的监督遮掩知识蒸馏 论文接收: CVPR 2023 论文地址: https://arxiv.org/pdf/2303.15466.pdf 代码链接: https://github.com/HL-hanlin/SMKD 1.ViT在小样本学习(只有少量标记数据的小型数据集)中往往会 过拟合,并且由于缺乏 归纳偏置 而导致性能较差;

    2024年02月06日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包