activations.py
utils\activations.py
目录
activations.py
1.所需的库和模块
2.class SiLU(nn.Module):
3.class Hardswish(nn.Module):
4.class MemoryEfficientSwish(nn.Module):
5.class Mish(nn.Module):
6.class MemoryEfficientMish(nn.Module):
7.class FReLU(nn.Module):
1.所需的库和模块
# Activation functions
import torch
import torch.nn as nn
import torch.nn.functional as F
2.class SiLU(nn.Module):
# 在不考虑计算量大小的前提下,相同的模型。 Mish > SiLU > Hardswish
# SiLU https://arxiv.org/pdf/1606.08415.pdf ----------------------------------------------------------------------------
# SiLU 激活函数,也被称为 Swish 函数 。这个函数的数学表达式是 f(x) = x * sigmoid(x) 。
class SiLU(nn.Module): # export-friendly version of nn.SiLU()
# @staticmethod
# @staticmethod 是一个python装饰器,用于标记一个静态方法。
# 静态方法是属于类而不是类的实例的方法。
# 静态方法不需要访问类的实例或实例的状态。因此,它们不接受 self 参数。
# 可以通过类名直接调用静态方法,也可以通过实例调用。
@staticmethod
def forward(x):
return x * torch.sigmoid(x)
# 这里的代码是 SiLU 激活函数的一个简单实现,它覆盖了 forward 方法,这是 PyTorch 中所有模块必须实现的一个方法,用于定义前向传播的行为。
# 在这个 forward 方法中,输入 x 被乘以 x 的 Sigmoid 函数,Sigmoid 函数的输出范围是 (0, 1),因此 SiLU 函数的输出范围是 (0, x)。
3.class Hardswish(nn.Module):
class Hardswish(nn.Module): # export-friendly version of nn.Hardswish()
@staticmethod
def forward(x):
# return x * F.hardsigmoid(x) # for torchscript and CoreML
# torch.nn.functional.hardtanh(input, min_val=-1.0, max_val=1.0, inplace=False)
# F.hardtanh 函数是PyTorch中 torch.nn.functional 模块提供的HardTanh激活函数的实现。HardTanh函数是一个分段线性函数,它将输入值限制在指定的范围内。
# 具体来说,如果输入值大于最大值 max_val ,则输出 max_val ;如果输入值小于最小值 min_val ,则输出 min_val ;否则,输出输入值本身。
# 这个函数的特点是计算效率高,因为它避免了Tanh函数中的指数运算,而是通过简单的比较和赋值来实现。这使得HardTanh在某些情况下比Tanh函数更快,尤其是在需要处理大量数据时。
# 参数 :
# input ( Tensor ):输入的张量。
# min_val ( float ):线性区域的最小值,默认为-1。
# max_val ( float ):线性区域的最大值,默认为1。
# inplace ( bool ):是否在原地进行操作,默认为 False 。
return x * F.hardtanh(x + 3, 0., 6.) / 6. # for torchscript, CoreML and ONNX
# Hardswish 函数的数学表达式是 f(x) = x * (x + 3) / 6 ,当 x 的值在 [-3, 3] 范围内时,这个函数的输出接近于 x 的线性函数,而在该范围之外时,输出会饱和。
# 这种设计使得 Hardswish 在某些情况下比 Swish 更快,因为它避免了计算 sigmoid 函数,而 sigmoid 函数在深度学习中通常被认为是计算密集型的。
# 在这段代码中, F.hardtanh 是 PyTorch 中的一个函数,它实现了硬双曲正切函数(hardtanh),这个函数将输入值限制在指定的范围内。
# 在这个例子中, F.hardtanh(x + 3, 0., 6.) 将 x + 3 的值限制在 [0, 6] 范围内。然后,这个结果被除以 6,以匹配 Hardswish 函数的定义。
# 注释掉的代码行 # return x * F.hardsigmoid(x) 提供了另一种实现 Hardswish 的方式,使用 F.hardsigmoid 函数,它是 sigmoid 函数的一个近似,通常用于提高性能。
# 但是,由于注释中提到这是为了 torchscript 和 CoreML ,而当前的实现是为了 torchscript 、 CoreML 和 ONNX ,所以选择了当前的实现方式。
4.class MemoryEfficientSwish(nn.Module):
# MemoryEfficientSwish 的激活函数的PyTorch模块。 MemoryEfficientSwish 是一个自定义的激活函数,它旨在提供一个计算上更高效的Swish激活函数的替代品。
# Swish函数是一种自门控的激活函数,其形式为 f(x) = x * sigmoid(x) ,其中 x 是输入, sigmoid 是Sigmoid函数。
# 在这个 MemoryEfficientSwish 类中,定义了一个内部类 F ,它继承自 torch.autograd.Function 。这个内部类重写了 forward 和 backward 两个静态方法,分别用于前向传播和反向传播。
class MemoryEfficientSwish(nn.Module):
class F(torch.autograd.Function):
@staticmethod
def forward(ctx, x):
# 保存输入 x 以便在反向传播时使用。
ctx.save_for_backward(x)
# 计算并返回 Swish 激活函数的前向结果。
return x * torch.sigmoid(x)
@staticmethod
def backward(ctx, grad_output):
# 从上下文中获取保存的输入 x 。
x = ctx.saved_tensors[0]
# 计算输入 x 的Sigmoid值。
sx = torch.sigmoid(x)
# 计算 Swish 激活函数的导数,这是根据 Swish 函数的导数定义计算的。导数的计算利用了链式法则和Sigmoid函数的性质。
return grad_output * (sx * (1 + x * (1 - sx)))
def forward(self, x):
# F.apply()
# 在PyTorch中, F.apply() 函数是一个特殊的函数,它允许用户自定义前向和反向传播的计算图。 F.apply() 通常用于实现自定义的自动微分函数,它是一个静态方法,属于 torch.autograd.Function 类。
# 当你定义一个继承自 torch.autograd.Function 的类,并重写了 forward 和 backward 方法时,可以通过 apply 方法来调用这些自定义的操作。
# 定义 :
# 当你定义一个类继承自 torch.autograd.Function 时,你需要至少实现两个静态方法: forward 和 backward 。
# @staticmethod
# def forward(ctx, *args):
# 这个方法定义了自定义操作的前向传播。
# ctx :是一个上下文对象,用于保存在前向传播中需要的信息,以便在反向传播时使用。
# *args :是传递给函数的输入参数。
# @staticmethod
# def backward(ctx, *grad_outputs):
# 这个方法定义了自定义操作的反向传播。
# ctx :是前向传播中保存的上下文对象。
# *grad_outputs :是输出相对于梯度的梯度。
# 使用 :
# 在你的自定义类中,你可以通过 self.F.apply() 来调用这些自定义的操作。这里的 F 是自定义类的名字, apply 是 torch.autograd.Function 类的一个方法,它将输入传递给 forward 方法,并返回前向传播的结果。
# 调用内部类 F 的 apply 方法来执行前向传播。
return self.F.apply(x)
5.class Mish(nn.Module):
# Mish https://github.com/digantamisra98/Mish --------------------------------------------------------------------------
# 这段代码定义了一个名为 Mish 的 PyTorch 模块,它实现了 Mish 激活函数。Mish 激活函数是一种在深度学习中使用的激活函数,它结合了软加(softplus)和双曲正切(tanh)函数。Mish 函数的定义如下:
# Mish(x) = x · tanh(log(1 + e^x)) 其中, tanh 是双曲正切函数, log(1 + e^x) 是软加函数,也称为平滑的ReLU。在这个 Mish 类中, forward 方法定义了 Mish 激活函数的前向传播:
class Mish(nn.Module):
@staticmethod
def forward(x):
# F.softplus(x) :软加函数,计算 log(1 + e^x) 。
# .tanh() :双曲正切函数,计算 tanh(softplus(x))
# x * ... :将输入张量 x 与软加函数的结果的双曲正切相乘,得到最终的 Mish 激活函数的输出。
return x * F.softplus(x).tanh()
6.class MemoryEfficientMish(nn.Module):
# 这段代码定义了一个名为 MemoryEfficientMish 的 PyTorch 模块,它实现了 Mish 激活函数,并且是内存高效的,因为它使用了 PyTorch 的自动微分机制 torch.autograd.Function 来避免在前向传播中保存额外的中间变量。
# MemoryEfficientMish 类包含一个内部类 F ,该内部类继承自 torch.autograd.Function 并重写了 forward 和 backward 方法。
class MemoryEfficientMish(nn.Module):
class F(torch.autograd.Function):
@staticmethod
# 1.ctx :是一个上下文对象,用于在前向传播中保存张量,以便在反向传播时使用。
def forward(ctx, x):
# 保存输入张量 x ,以便在反向传播中恢复。
ctx.save_for_backward(x)
# 计算 Mish 激活函数的前向传播。这里 F.softplus(x) 计算 ln(1 + exp(x)) , torch.tanh(...) 计算双曲正切,然后与输入 x 相乘。
return x.mul(torch.tanh(F.softplus(x))) # x * tanh(ln(1 + exp(x)))
@staticmethod
# 2.grad_output :是输出相对于梯度的梯度。
def backward(ctx, grad_output):
# x 是从上下文中恢复的输入张量。
x = ctx.saved_tensors[0]
# sx 计算 sigmoid(x) 。
sx = torch.sigmoid(x)
# fx 计算 Mish 激活函数的前向结果,即 F.softplus(x).tanh() 。
fx = F.softplus(x).tanh()
# 返回的梯度是 grad_output 乘以 Mish 激活函数的导数。Mish 函数的导数是 fx + x * sx * (1 - fx * fx) ,其中 fx 是 Mish 函数的输出, sx 是 sigmoid(x) 。
return grad_output * (fx + x * sx * (1 - fx * fx))
# MemoryEfficientMish 类的 forward 方法。
def forward(self, x):
# 调用内部类 F 的 apply 方法,执行前向传播。
return self.F.apply(x)
# 这个 MemoryEfficientMish 类的实现使得 Mish 激活函数在计算梯度时更加内存高效,因为它避免了在前向传播中保存中间结果,而是在反向传播中重新计算它们。这种方法在处理大规模数据或深度网络时特别有用,因为它可以减少内存占用。
7.class FReLU(nn.Module):
# FReLU https://arxiv.org/abs/2007.11824 -------------------------------------------------------------------------------
# 这段代码定义了一个名为 FReLU 的 PyTorch 模块,它实现了一种特殊的激活函数,称为 FReLU(Filter Rectified Linear Unit)。FReLU 结合了传统的 ReLU 激活函数和卷积操作,用于在通道维度上进行特征的竞争性选择。
class FReLU(nn.Module):
# __init__ 方法初始化 FReLU 模块。
# c1 :参数表示输入通道数。
# k :参数表示卷积核的大小,默认为 3。
def __init__(self, c1, k=3): # ch_in, kernel
super().__init__()
# 是一个深度卷积层( nn.Conv2d ),它使用 c1 个输入通道和输出通道,卷积核大小为 k ,步长为 1,填充为 1,分组卷积( groups=c1 )使得每个通道独立卷积,没有偏置项( bias=False )。
self.conv = nn.Conv2d(c1, c1, k, 1, 1, groups=c1, bias=False)
# 是一个批量归一化层( nn.BatchNorm2d ),用于归一化 c1 个通道。
self.bn = nn.BatchNorm2d(c1)
# x :是输入张量。
def forward(self, x):
# 首先对输入 x 应用深度卷积,然后应用批量归一化。
# 计算输入张量 x 和卷积归一化结果之间的逐元素最大值,返回这个最大值。
return torch.max(x, self.bn(self.conv(x)))
# 功能FReLU 的核心思想是在通道维度上进行竞争性选择。对于每个通道,它通过深度卷积和批量归一化处理输入,然后与原始输入进行逐元素比较,选择两者之间的最大值。这种方法可以增强特征的表达能力,因为它允许网络在每个通道上选择最有用的特征。