PyTorch应用实战五:实现二值化神经网络

简介: PyTorch应用实战五:实现二值化神经网络

实验环境

python3.6 + pytorch1.8.0

import torch
print(torch.__version__)
1.8.0

二值化网络概述

二值化网络(BinaryNet)是一种深度学习网络类型,使用二进制(1和0)代替浮点数作为网络的输入和参数。这种网络类型由加拿大的Yaroslav Bulatov和Artem Babenko在2016年提出。二值化网络的独特之处在于它通过使用二进制数值(1或-1)来代替常规的浮点数,极大地减少了神经网络中的存储空间和计算量。这大大提高了神经网络的处理速度和效率,并使其在较小的处理器和设备上的应用更加可行。


在传统的神经网络中,每个神经元的输出值在0到1之间取值。网络的权重也是浮点数,可以在训练时根据错误来进行微调。但是,在二值化网络中,这些值被替换为-1和1。这种二进制表示法允许网络使用异或逻辑门和位移操作来代替复杂的乘法和加法运算,从而加快了计算速度。此外,网络的存储要求也减少了,因为每个神经元和权重只需要1个比特来表示。


虽然在计算速度和存储方面二值化网络有一定的优势,但在某些情况下,它会降低网络的准确性。二值化网络的主要问题在于,它不适合处理复杂的特征和数据集。因此,它通常用于解决图像分类、人脸识别和目标检测等简单任务。


在研究过程中,学者们提出了许多改进二值化网络的方法。例如,包括XNOR-Net、BinaryConnect、Hinton Binarized Neural Networks和Real-to-Binary Conversion等。这些方法可以使网络在保持二值化的优点的同时提高其准确性和适用性。


在实际应用中,二值化网络通常是一种加速深度学习推理的技术。例如,在移动设备上处理图像或运行深度学习模型时,二值化网络可以在不损失准确性的情况下体现出更高的速度和效率。

实验内容

import torch
import torch.nn as nn
import math
from torch.autograd import Function
import torch.nn.functional as F
from torchvision import datasets, transforms
class Binarize(Function):
    @staticmethod
    def forward(ctx, input):
        ctx.save_for_backward(input)
        return torch.sign(input + 1e-20)
    @staticmethod
    def backward(ctx, grad_output):
        input = ctx.saved_tensors[0]
        grad_output[input > 1] = 0
        grad_output[input < -1] = 0
        return grad_output

该代码实现了一个二值化函数,将输入张量中的每个元素变成-1或1,如果元素大于等于0则变成1,否则变成-1。该函数通过继承PyTorch的Function类来实现自定义反向传播。具体来说,该类实现了两个静态方法forward和backward,作用分别是前向传播和反向传播。


在前向传播中,函数使用torch.sign(input + 1e-20)将输入张量二值化,并将结果返回。需要注意的是,在计算sign时,为了避免输入为0的情况,添加了1e-20的偏移量。


在反向传播中,函数首先从上下文中获取之前保存的输入张量input。然后对于所有大于1或小于-1的元素,在梯度grad_output中将其对应位置上的梯度设为0,表示这些位置上的输入不应该对梯度做出贡献。最后返回更新后的梯度grad_output。


总的来说,该函数简单易懂,用途广泛,可以用于二值化卷积神经网络中的权重或激活值。


ctx.save_for_backward(input)是将输入保存在上下文ctx对象中,以供后续的反向传播使用。在PyTorch中,要实现自定义的反向传播需要重载torch.autograd.Function中的forward和backward方法,使用ctx.save_for_backward(input)可以将forward方法中的输入input保存下来,在backward方法中可以使用ctx.saved_tensors获取到这些保存的张量数据,进而计算梯度。


@staticmethod 是 Python 中的一个装饰器(decorator),它用于定义一个静态方法。静态方法是与类相关联的方法,但是不需要访问类或实例的状态,也就是说,它们在执行时不依赖于实例的状态,因此可以在不创建实例的情况下直接调用。通过在方法定义上添加@staticmethod装饰器,可以将一个普通函数声明为静态方法。

class BinarizedLinear(nn.Module):
    def __init__(self, in_features, out_features, binarize_input=True):
        super(BinarizedLinear, self).__init__()
        self.binarize_input = binarize_input
        self.weight = nn.Parameter(torch.Tensor(out_features, in_features))
        nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5))
    def forward(self, x):
        if self.binarize_input:
            x = Binarize.apply(x)    #调用静态方法
        w = Binarize.apply(self.weight)
        out = torch.matmul(x, w.t())
        return out

这段代码是实现了一个二值化的全连接层 BinarizedLinear,继承自 PyTorch 的 nn.Module。主要包含以下几个部分:


1.构造函数:接收两个参数,输入特征数和输出特征数,并初始化权重参数 self.weight。其中,参数 binarize_input 表示是否对输入数据进行二值化处理。

2.forward 函数:接收输入数据 x,如果 binarize_input 为 True,则对输入数据进行二值化处理,然后对权重参数也进行二值化处理,并使用矩阵乘法计算输出值 out。

3.Binarize 类:定义了一个自定义的二值化操作 Binarize.apply,是一个继承自 PyTorch 的 autograd.Function,可以实现自动求导。其作用是将输入数据 x 的取值二值化为 -1 或 1,根据具体取值的正负号。

通过在网络中使用 BinarizedLinear 层,可以减少模型的计算量,提高模型的计算速度和效率,但可能会降低模型的准确率。

model = nn.Sequential(
    BinarizedLinear(784, 2048, False),
    nn.BatchNorm1d(2048),
    BinarizedLinear(2048, 2048),
    nn.BatchNorm1d(2048),
    BinarizedLinear(2048, 2048),
    nn.Dropout(0.5),
    nn.BatchNorm1d(2048),
    nn.Linear(2048, 10)
)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=40, gamma=0.1)

这是一个使用BinarizedLinear层构建的神经网络模型,该层用于二值化输入和权重。该模型共有6个层,分别是3个BinarizedLinear层、2个BatchNorm1d层和1个普通的线性层。其中,第一个BinarizedLinear层的输入大小为784,输出大小为2048;第二个和第三个BinarizedLinear层的输入和输出大小都是2048。最后一个普通线性层用于将2048个输出映射到10个输出,对应10个分类。在第三个BinarizedLinear层和第四个BatchNorm1d层之间添加了一个50%的dropout层,以减少过拟合。


优化器使用Adam优化器,学习率为0.01,使用StepLR调度器每过40个epoch将学习率调整为原来的0.1倍,以加速收敛。

train_batch_size = 100  #一共60000个数据,100个数据一组,一共600组(N=100)
test_batch_size = 100   #一共10000个数据,100个数据一组,一共100组(N=100)
# 训练集
train_loader = torch.utils.data.DataLoader(
    datasets.MNIST(
        './data',train=True,download=True,  
        transform=transforms.Compose([
            transforms.ToTensor(),  #转为tensor
            transforms.Normalize(0.5,0.5) #正则化
        ])
    ),
    batch_size = train_batch_size, shuffle=True  #打乱位置
)
# 测试集
test_loader = torch.utils.data.DataLoader(
    datasets.MNIST(
        './data',train=False,download=True,
        transform=transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize(0.5,0.5)
        ])
    ),
    batch_size = test_batch_size, shuffle=False
)

这段代码实现了通过PyTorch的DataLoader加载MNIST数据集。MNIST是一个手写数字数据集,包含60,000张28x28的训练图像和10,000张测试图像,每张图像代表一个从0到9中的数字。


首先定义了两个超参数train_batch_size和test_batch_size,这是训练集和测试集每组数据的大小。在这里分别为100,也就是每次加载100张图片进行训练或测试。


然后定义了训练集和测试集的DataLoader,使用MNIST类加载数据集,参数中train=True表示加载训练集,train=False表示加载测试集。同时也指定了加载的路径和应用的预处理操作,包括将图像转换为张量和进行正则化处理。


最后,指定了batch_size和shuffle参数,表示每次加载的图像数量和是否打乱图像的顺序。在训练过程中,通常会在每个epoch结束时打乱图像的顺序,以保证模型可以学习到更多的变化和模式。在测试过程中,没有必要打乱图像的顺序,因为我们只需要评估模型的性能。

epochs = 100
interval = 100
for epoch in range(epochs):
    for i, (data, label) in enumerate(train_loader):
        model.train()
        optimizer.zero_grad()
        output = model(data.view(-1, 28*28))
        loss = F.cross_entropy(output, label)  #交叉熵函数
        loss.backward()   #反向求导
        optimizer.step()
        if i % interval == 0:
            print("Epoch %03d [%03d/%03d]\tLoss:%.4f"%(epoch, i, len(train_loader), loss.item()))
    correct_num = 0
    total_num = 0
    with torch.no_grad():
        for data, label in test_loader:
            model.eval()
            output = model(data.view(-1, 28*28))
            pred = output.max(1)[1]
            correct_num += (pred == label).sum().item()
            total_num += len(data)
    acc = correct_num / total_num
    print('...Testing @ Epoch %03d\tAcc:%.4f'%(epoch, acc))
    scheduler.step()
Epoch 000 [000/600] Loss:2.4242
Epoch 000 [100/600] Loss:0.7715
Epoch 000 [200/600] Loss:0.6427
Epoch 000 [300/600] Loss:0.6404
Epoch 000 [400/600] Loss:0.5750
Epoch 000 [500/600] Loss:0.1281
...Testing @ Epoch 000  Acc:0.9402
Epoch 001 [000/600] Loss:0.1153
Epoch 001 [100/600] Loss:0.3760
Epoch 001 [200/600] Loss:0.2282
Epoch 001 [300/600] Loss:0.3495
Epoch 001 [400/600] Loss:0.0987
Epoch 001 [500/600] Loss:0.2286
...Testing @ Epoch 001  Acc:0.9598
Epoch 002 [000/600] Loss:0.1307
Epoch 002 [100/600] Loss:0.0409
Epoch 002 [200/600] Loss:0.2278
Epoch 002 [300/600] Loss:0.1454
Epoch 002 [400/600] Loss:0.0510
Epoch 002 [500/600] Loss:0.1283
...Testing @ Epoch 002  Acc:0.9661
Epoch 003 [000/600] Loss:0.0372
Epoch 003 [100/600] Loss:0.0778
Epoch 003 [200/600] Loss:0.0903
Epoch 003 [300/600] Loss:0.0772
Epoch 003 [400/600] Loss:0.0523
Epoch 003 [500/600] Loss:0.1434
...Testing @ Epoch 003  Acc:0.9603
Epoch 004 [000/600] Loss:0.0792
Epoch 004 [100/600] Loss:0.2278
Epoch 004 [200/600] Loss:0.0707
Epoch 004 [300/600] Loss:0.1063
Epoch 004 [400/600] Loss:0.1302
Epoch 004 [500/600] Loss:0.1531
...Testing @ Epoch 004  Acc:0.9700
Epoch 005 [000/600] Loss:0.1077
Epoch 005 [100/600] Loss:0.0279
Epoch 005 [200/600] Loss:0.1569
Epoch 005 [300/600] Loss:0.0495
Epoch 005 [400/600] Loss:0.0547
Epoch 005 [500/600] Loss:0.1252
...Testing @ Epoch 005  Acc:0.9677
Epoch 006 [000/600] Loss:0.0139
Epoch 006 [100/600] Loss:0.0064
Epoch 006 [200/600] Loss:0.0180
Epoch 006 [300/600] Loss:0.0480
Epoch 006 [400/600] Loss:0.0868
Epoch 006 [500/600] Loss:0.0651
...Testing @ Epoch 006  Acc:0.9648
Epoch 007 [000/600] Loss:0.0300
Epoch 007 [100/600] Loss:0.0104
Epoch 007 [200/600] Loss:0.0738
Epoch 007 [300/600] Loss:0.0654
Epoch 007 [400/600] Loss:0.1252
Epoch 007 [500/600] Loss:0.1658
...Testing @ Epoch 007  Acc:0.9625
Epoch 008 [000/600] Loss:0.1689
Epoch 008 [100/600] Loss:0.0250

这段代码是一个简单的神经网络模型的训练和测试过程。


首先定义了两个变量epochs和interval,分别表示训练过程中的总轮数和每训练interval个batch就输出一次训练结果。


然后使用了一个双重循环,外层循环遍历epochs轮,内层循环遍历训练数据集中的所有batch。在每个batch中,首先将模型设为训练模式,然后将优化器的梯度清零,接着将模型输入当前batch的数据并得到输出,计算损失值并进行反向传播求导,最后使用优化器更新模型参数。


当训练完interval个batch时,输出当前轮的训练结果,其中包括当前轮数、当前batch数、总batch数和当前batch的损失值。


接着,在训练完成一轮后,使用测试数据集对模型进行测试。首先将模型设置为评估模式,然后遍历测试数据集中的所有数据,将模型输入测试数据并得到输出,计算预测结果并与真实标签进行比较,统计正确预测的数量和总数量,最后计算测试集的准确率。


最后,使用scheduler.step()函数更新学习率。scheduler是调整学习率的一个函数,根据不同的策略进行学习率的调整,例如每隔一定轮数降低学习率等。这里使用的是StepLR函数,每隔一定轮数降低学习率。

目录
相关文章
|
4天前
|
存储 安全 物联网
网络安全与信息安全:防护之道与实战策略
【5月更文挑战第30天】在数字化时代,数据是最宝贵的资产之一,而网络安全和信息安全则是维护这些资产的坚固盾牌。本文将深入探讨网络安全漏洞的概念、加密技术的应用以及提升安全意识的重要性。通过对网络威胁的剖析,我们揭示了防御机制中存在的薄弱环节,并提出了相应的强化措施。文章还将介绍当前最前沿的加密方法,以及如何通过教育和技术手段增强个人和组织的安全防护能力。
|
2天前
|
传感器 网络协议 C语言
C语言在网络编程中的实际应用
C语言在网络编程中的实际应用
11 1
|
5天前
|
设计模式 安全 测试技术
深入理解与应用自动化测试框架 — 以Selenium为例网络防线的构筑者:洞悉网络安全与信息安全的核心要素
【5月更文挑战第29天】 在快速迭代的软件开发过程中,自动化测试已成为提高测试效率、确保软件质量的重要手段。本文将深入探讨自动化测试框架Selenium的核心概念、架构以及实际应用中的关键技巧,旨在为读者提供一篇系统性的分析与实践指南。文章首先概述了自动化测试的必要性和Selenium框架的基本特征;随后详细剖析了Selenium的组件结构,并结合实例讲解如何高效地设计和执行测试用例;最后,讨论了当前自动化测试面临的挑战及未来发展趋势。
|
2天前
|
安全 网络安全 区块链
【计算巢】区块链技术在网络安全中的应用与挑战
【5月更文挑战第31天】区块链技术为网络安全带来新机遇,其去中心化、不可篡改和共识机制特性有助于身份验证、数据完整性保护及提高网络抗攻击性。但面临性能、隐私保护和法规监管等挑战。简单Python代码展示了区块链在数据完整性验证的应用。随着技术发展,区块链有望在网络安全领域发挥更大作用,未来可能与其它安全技术融合,为网络安全提供更强保障。
|
3天前
|
机器学习/深度学习 安全 网络安全
云端防御:云计算环境中的网络安全与信息保护策略深度学习在图像识别中的应用与挑战
【5月更文挑战第31天】 在数字化转型的浪潮中,云计算已成为企业及个人存储和处理数据的首选平台。然而,随着云服务的广泛采用,网络安全威胁也随之增加,使得信息安全成为亟待解决的挑战。本文聚焦于云计算环境特有的安全风险,探讨了多层次、多维度的防御策略,旨在为读者提供一套综合的云安全解决方案蓝图。通过分析当前云服务中的安全缺陷,并提出相应的防护措施,文章不仅强调了技术层面的对策,还涉及了管理与合规性方面的重要性。
|
3天前
|
JSON Android开发 开发者
构建高效Android应用:采用Kotlin协程优化网络请求
【5月更文挑战第31天】 在移动开发领域,尤其是针对Android平台,网络请求的管理和性能优化一直是开发者关注的焦点。随着Kotlin语言的普及,其提供的协程特性为异步编程提供了全新的解决方案。本文将深入探讨如何利用Kotlin协程来优化Android应用中的网络请求,从而提升应用的响应速度和用户体验。我们将通过具体实例分析协程与传统异步处理方式的差异,并展示如何在现有项目中集成协程进行网络请求优化。
|
3天前
|
存储 安全 算法
网络安全与信息安全:防范漏洞、应用加密、提升意识
【5月更文挑战第31天】在数字化的浪潮中,网络安全与信息安全已成为守护个人隐私、企业资料与国家安全的重要屏障。本文将深入探讨网络安全漏洞的成因与对策,解析加密技术的种类及其应用,并强调安全意识在整体防御体系中的核心作用。通过技术性分析与实践建议,旨在为读者提供全面的网络安全知识框架和实用的防护指南。
|
3天前
|
SQL 安全 算法
网络安全与信息安全:防范漏洞、应用加密、提升意识
【5月更文挑战第30天】 在数字化时代,网络安全与信息安全已成为保护个人隐私、企业数据和国家安全的关键。本文深入探讨了网络安全漏洞的成因及其影响,分析了加密技术如何作为防御手段,以及提升安全意识的重要性。通过案例分析和技术讲解,我们旨在为读者提供一套全面的网络安全知识框架,以应对日益复杂的网络威胁。
|
4天前
|
监控 安全 算法
网络安全与信息安全:防护之道与实战技巧
【5月更文挑战第30天】在数字化时代,网络与信息安全已成为一个不断演进的战场。本文将深入探讨网络安全漏洞的成因、加密技术的应用以及提升安全意识的重要性。通过对常见网络威胁的分析,我们揭示了防护措施的必要性,并分享了如何通过多层次的安全策略来增强个人和组织的信息安全防线。
|
4天前
|
存储 安全 网络安全
云端防御战线:云计算环境下的网络安全与信息保护策略未来交织:新兴技术趋势与跨界应用探索
【5月更文挑战第30天】 随着企业数字化转型的加速,云计算作为支撑现代业务架构的关键技术之一,其安全性已成为业界关注的热点。本文深入探讨了在动态且复杂的云环境中,如何通过多层次、多维度的安全措施确保数据的保密性、完整性与可用性。我们将从云服务模型出发,分析不同服务层次上的安全挑战,并结合最新的网络威胁情报,提出一系列创新的防御机制。这些机制包括基于行为分析的入侵检测系统、加密技术的最新进展以及人工智能在安全事件响应中的应用。文章旨在为云服务提供商和使用者提供一套综合性的网络安全解决方案,以增强云计算环境的整体安全性。