首页人工智能Pytorch【深度学习(PyTorch...

【深度学习(PyTorch篇)】40.初始化策略

本系列文章配套代码获取有以下两种途径:

  • 通过百度网盘获取:
链接:https://pan.baidu.com/s/1XuxKa9_G00NznvSK0cr5qw?pwd=mnsj 提取码:mnsj
  • 前往GitHub获取
https://github.com/returu/PyTorch





01

torch.nn.init


Pytorch中,网络模型的参数初始化对于训练过程和最终模型性能有着重要影响。良好的权重初始化策略可以帮助网络更快收敛,并避免梯度消失或梯度爆炸等问题。

之前都是使用torch.Tensor() 来创建参数初始值,但是该方式返回的都是内存中的随机数,很可能会有极大值,在实际训练网络时会出现溢出或者梯度消失的问题。

class CustomLinearLayer(nn.Module):  
    # 继承nn.Module,需要重写构造函数__init__()以及前向传播函数forward()
    def __init__(self, input_features, output_features):  
        super().__init__()  
        # nn.Parameter()内的参数是网络中可学习的参数
        self.weight = nn.Parameter(torch.Tensor(output_features, input_features))  
        self.bias = nn.Parameter(torch.Tensor(output_features))  

    def forward(self, x):  
        # 执行线性变换并添加偏置:y=wx+b
        out = torch.mm(x, self.weight.t()) + self.bias  

        return out  

# 实例化自定义的全连接层  
custom_linear = CustomLinearLayer(input_features=10, output_features=5)  
# 假设输入是一个3x10的张量,即3个样本,每个样本10个特征  
input_tensor = torch.randn(310)
output_tensor = custom_linear(input_tensor)  
output_tensor

输出结果为:

tensor([[ 2.9847e+31, -1.1774e+30, -1.6674e+32,  1.2302e-22,  1.5602e-03],
        [-4.3957e+31,  2.2418e+30, -3.7796e+32,  4.6057e-22, -2.0284e-03],
        [-1.9614e+31,  3.9608e+30, -3.4233e+32, -1.3787e-21, -7.8113e-04]]
,
       grad_fn=<AddBackward0>)

为了避免上述情况,PyTorchtorch.nn.init模块提供了许多常用的初始化方法,同时也允许用户自定义初始化逻辑

  • nn.init.xavier_uniform_()和nn.init.xavier_normal_():
这两种方法根据Xavier Glorot提出的初始化策略,分别使用均匀分布和正态分布来初始化权重。
  • nn.init.kaiming_uniform_()和nn.init.kaiming_normal_():
这些方法根据Kaiming He提出的初始化策略,分别使用均匀分布和正态分布初始化权重。它们适合于ReLU和其变体激活函数的层。
  • nn.init.uniform_() 和nn.init.normal_():
这些方法使用简单的均匀分布或正态分布初始化权重。用户需要指定分布的范围或标准差。
  • nn.init.constant_():
将所有权重设置为常数值,通常用于偏置项。
更多初始化方法可以前往官方文档查看。这些初始化方法需要根据具体任务和数据集的特点进行选择。在实际应用中,建议尝试不同的初始化方法以找到最适合当前任务的方法。
class CustomLinearLayer(nn.Module):  
    def __init__(self, input_features, output_features):  
        super().__init__()  
        self.weight = nn.Parameter(torch.Tensor(output_features, input_features))  
        self.bias = nn.Parameter(torch.Tensor(output_features))

        # 使用xavier初始化权重  
        init.xavier_normal_(self.weight)  
        # 初始化偏置为0  
        init.zeros_(self.bias)

    def forward(self, x):  
        out = torch.mm(x, self.weight.t()) + self.bias  

        return out  

# 实例化自定义的全连接层  
custom_linear = CustomLinearLayer(input_features=10, output_features=5)  
# 假设输入是一个3x10的张量,即3个样本,每个样本10个特征  
input_tensor = torch.randn(310)
output_tensor = custom_linear(input_tensor)  
output_tensor
输出结果为:
tensor([[-1.5251, -0.3599,  1.0420, -1.2345,  1.6323],
        [ 1.5804,  0.0220,  0.6327,  0.2433,  1.8729],
        [-0.8394, -0.5402, -0.7079,  2.5849, -0.1935]]
, grad_fn=<AddBackward0>)
通过输出结果可以看出,权重将根据Xavier初始化的原则从正态分布中随机采样,这样可以确保权重既不会太大也不会太小,从而有助于模型更稳定、更快速地学习。
如果需要更复杂的初始化逻辑,可以自定义一个初始化函数,然后在模型构建完成后调用它。自定义初始化函数通常接受一个模块作为参数,检查模块类型,并根据需要初始化其参数。
02

初始化网络参数


如果要使用Xavier初始化来初始化之前定义的ConvNet类中的所有权重参数,可以使用以下两种方式。
class ConvNet(nn.Module):  
    def __init__(self,num_classes=10):  
        super().__init__()  

        # 第一个卷积层  
        self.conv1 = nn.Conv2d(332, kernel_size=3)  
        self.bn1 = nn.BatchNorm2d(32)  
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)  

        # 第二个卷积层  
        self.conv2 = nn.Conv2d(3264, kernel_size=3)  
        self.bn2 = nn.BatchNorm2d(64)  
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)  

        # 第三个卷积层  
        self.conv3 = nn.Conv2d(64128, kernel_size=3)  
        self.bn3 = nn.BatchNorm2d(128)  

        # 全连接层
        self.fc1 = nn.Linear(128 * 4 * 4512)
        self.fc2 = nn.Linear(512,num_classes)

    def forward(self, x):  
        # 第一个卷积层(卷积 -> 标准化 -> 激活 -> 池化)
        x = F.relu(self.bn1(self.conv1(x)))  
        x = self.pool1(x)  

        # 第二个卷积层(卷积 -> 标准化 -> 激活 -> 池化)
        x = F.relu(self.bn2(self.conv2(x)))  
        x = self.pool2(x)  

        # 第三个卷积层(卷积 -> 标准化 -> 激活)
        x = F.relu(self.bn3(self.conv3(x)))  

        # 将特征图展平  
        x = x.view(x.size(0), -1

        # 全连接层  
        x = F.relu(self.fc1(x))  
        x = self.fc2(x)  

        return x


  • 方法1:在__init__中初始化

__init__方法中,可以针对每个需要初始化的层手动调用初始化函数。

class ConvNet(nn.Module):  
    def __init__(self,num_classes=10):  
        super().__init__()  

        # 第一个卷积层  
        self.conv1 = nn.Conv2d(332, kernel_size=3)
        self.bn1 = nn.BatchNorm2d(32)  
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)  

        # 第二个卷积层  
        self.conv2 = nn.Conv2d(3264, kernel_size=3)  
        self.bn2 = nn.BatchNorm2d(64)  
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)  

        # 第三个卷积层  
        self.conv3 = nn.Conv2d(64128, kernel_size=3)  
        self.bn3 = nn.BatchNorm2d(128)  

        # 全连接层
        self.fc1 = nn.Linear(128 * 4 * 4512)
        self.fc2 = nn.Linear(512,num_classes)

        # 初始化权重参数
        init.xavier_normal_(self.conv1.weight)
        init.xavier_normal_(self.conv2.weight)
        init.xavier_normal_(self.conv3.weight)
        init.xavier_normal_(self.fc1.weight)
        init.xavier_normal_(self.fc2.weight)


    def forward(self, x):  
        # 前向传播
        pass

  • 方法2:使用apply()函数初始化:

另一种方法是在模型实例化后,使用apply()函数遍历所有子模块并初始化它们的参数。这种方法更简洁,可以自动应用于模型中的所有层,而无需显式地列出每一层。

class ConvNet(nn.Module):  
    def __init__(self,num_classes=10):  
        super().__init__()  
        # 定义网络层...  
        pass

    def forward(self, x):  
        # 前向传播... 
        pass

# 实例化网络(假设有10个类别)  
net = ConvNet(num_classes=10)  

# 定义初始化函数
def init_params(m):
    if isinstance(m, nn.Conv2d) or isinstance(m, nn.Linear):
        init.xavier_uniform_(m.weight)
        if m.bias is not None:
            init.zeros_(m.bias)

# 应用初始化
net.apply(init_params)

在这两个方法中,可以根据具体情况选择最适合自己需求的方式。不过,使用apply()函数通常更通用,因为它可以自动处理模型架构的变化,而无需修改初始化代码。


更多内容可以前往官网查看

https://pytorch.org/


本篇文章来源于微信公众号: 码农设计师

RELATED ARTICLES

欢迎留下您的宝贵建议

Please enter your comment!
Please enter your name here

- Advertisment -

Most Popular

Recent Comments