本系列文章配套代码获取有以下两种途径:
-
通过百度网盘获取:
链接:https://pan.baidu.com/s/1XuxKa9_G00NznvSK0cr5qw?pwd=mnsj
提取码:mnsj
-
前往GitHub获取:
https://github.com/returu/PyTorch
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(3, 10)
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>)
为了避免上述情况,PyTorch的torch.nn.init模块提供了许多常用的初始化方法,同时也允许用户自定义初始化逻辑:
-
nn.init.xavier_uniform_()和nn.init.xavier_normal_():
-
nn.init.kaiming_uniform_()和nn.init.kaiming_normal_():
-
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(3, 10)
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>)
初始化网络参数:
class ConvNet(nn.Module):
def __init__(self,num_classes=10):
super().__init__()
# 第一个卷积层
self.conv1 = nn.Conv2d(3, 32, kernel_size=3)
self.bn1 = nn.BatchNorm2d(32)
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
# 第二个卷积层
self.conv2 = nn.Conv2d(32, 64, kernel_size=3)
self.bn2 = nn.BatchNorm2d(64)
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
# 第三个卷积层
self.conv3 = nn.Conv2d(64, 128, kernel_size=3)
self.bn3 = nn.BatchNorm2d(128)
# 全连接层
self.fc1 = nn.Linear(128 * 4 * 4, 512)
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(3, 32, kernel_size=3)
self.bn1 = nn.BatchNorm2d(32)
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
# 第二个卷积层
self.conv2 = nn.Conv2d(32, 64, kernel_size=3)
self.bn2 = nn.BatchNorm2d(64)
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
# 第三个卷积层
self.conv3 = nn.Conv2d(64, 128, kernel_size=3)
self.bn3 = nn.BatchNorm2d(128)
# 全连接层
self.fc1 = nn.Linear(128 * 4 * 4, 512)
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/
本篇文章来源于微信公众号: 码农设计师