一 随机数生成器种子
def make_deterministic(seed):
# https://github.com/pytorch/pytorch/issues/7068#issuecomment-487907668
random.seed(seed)
os.environ['PYTHONHASHSEED'] = str(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed) # if you are using multi-GPU.
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.deterministic = True
这段代码是在Python中,特别是使用了NumPy和PyTorch进行科学计算或深度学习时,用来设置随机数生成器种子的。这样做可以确保你的代码具有确定性,即多次运行下相同的输入将产生相同的输出,这对于确保实验的可重复性非常重要。以下是每行代码的具体解释:
random.seed(seed)
:
- 这行设置了Python内置的随机数生成器的种子。这影响所有使用Python内置
random
模块的随机数生成,使得其随机序列是可以预测的。
os.environ['PYTHONHASHSEED'] = str(seed)
:
- 设置环境变量
PYTHONHASHSEED
为特定的字符串表示的种子。这个变量控制Python 哈希(hash)的随机化。对于一些版本的Python,设置这个种子可以禁用哈希随机化,从而确保对象的哈希值在多次程序运行时是一致的。
np.random.seed(seed)
:
- 这行代码设置了NumPy随机数生成器的种子。NumPy广泛用于科学计算中的随机数生成,设定种子保证了NumPy操作(如随机数组生成)的确定性。
torch.manual_seed(seed)
:
- 这一行设置了PyTorch的随机数生成器的种子,对CPU操作影响。
torch.cuda.manual_seed(seed)
:
- 设置了CUDA随机数生成器的种子,只影响当前GPU。
torch.cuda.manual_seed_all(seed)
:
- 如果你使用多GPU,这行代码会设置所有CUDA随机数生成器的种子。
torch.backends.cudnn.benchmark = False
:
- 这行代码禁用了cuDNN的自动优化器。因为自动优化器会在运行时根据计算图形选择最快的操作算法,这可能会导致程序每次运行时的行为略有不同。禁用此功能可以增加程序的确定性。
torch.backends.cudnn.deterministic = True
:
- 设置这个选项后,cuDNN将仅使用确定性的卷积算法。这意味着对于固定的输入和网络,卷积的输出将在多次运行间保持一致。这对于需要重复实验结果的情况非常有用,但可能会牺牲一些性能。
总的来说,上述代码的目的是为了确保当你的代码使用随机数生成功能时,能够产生可重复的结果,这在进行科学研究和实验时非常重要。通过固定随机数种子,你可以确保每次运行代码时,即使是涉及随机选择的算法(如初始化模型权重、选择数据子集等),也能得到相同的结果。这有助于其他研究人员复制你的实验,以及在调试时跟踪问题。
二、动态加载和修改预训练模型
def get_image_encoder(name, pretrained=True):
weights = "DEFAULT" if pretrained else None
model = getattr(torchvision.models, name)(weights=weights)
if name.startswith('vgg'):
model.classifier[6] = nn.Identity()
elif name.startswith('resnet'):
model.fc = nn.Identity()
elif name.startswith('vit'):
model.heads = nn.Identity()
elif name.startswith('convnext'):
model.classifier[-1] = nn.Identity()
else:
raise ValueError(f"Unsupported image encoder: {
name}")
# Infer the output size of the image encoder
with torch.inference_mode():
out = model(torch.randn(5, 3, 224, 224))
assert out.dim() == 2
assert out.size(0) == 5
image_encoder_output_dim = out.size(1)
return model, image_encoder_output_dim
这段代码是一个Python函数的一部分,该函数用于动态加载和修改预训练的计算机视觉模型,并确定模型的输出维度。下面是代码的逐行解析:
weights = "DEFAULT" if pretrained else None
:
- 这行代码是一个条件表达式,用于设置
weights
变量的值。如果变量pretrained
为True
,则weights
被设置为字符串"DEFAULT"
,这意味着函数将加载有预训练权重的模型。否则,weights
被设置为None
,表示不使用预训练权重。
model = getattr(torchvision.models, name)(weights=weights)
:
- 这行代码利用
getattr
函数动态获取torchvision.models
中名为name
的模型类,并实例化该模型。如果pretrained
为True
,则使用默认的预训练权重。
- 接下来的几行代码根据模型的名称来修改模型的分类器部分:
- 如果模型名称以
'vgg'
开头,将VGG模型的分类器的最后一层替换为nn.Identity()
,这是一个占位层,其目的是不改变输入数据。 - 如果模型名称以
'resnet'
开头,将ResNet模型的全连接层(fc)替换为nn.Identity()
。 - 如果模型名称以
'vit'
开头,将Vision Transformer模型的头(heads)替换为nn.Identity()
。 - 如果模型名称以
'convnext'
开头,将ConvNeXt模型的分类器的最后一层替换为nn.Identity()
。
这一步通常在你想要使用这些模型作为特征提取器时进行。通过替换最后的层,你可以获取中间层的输出,而不是最终的分类结果。
else
块中,如果模型名称不是上述几种模型之一,将抛出ValueError
异常,指出不支持的图像编码器。- 在
with torch.inference_mode():
块中:
- 使用
torch.inference_mode()
上下文管理器来进行推理,这意味着在这个块中的计算不会保存梯度,从而节省内存并提高计算速度。 out = model(torch.randn(5, 3, 224, 224))
行创建了一个形状为(5, 3, 224, 224)
的随机张量,这模拟了5个具有3个颜色通道(例如RGB)和224x224像素的图像。然后通过模型进行前向传递以产生输出。
- 接下来的两个
assert
语句用于验证模型输出:
assert out.dim() == 2
确认输出out
是二维的,这通常意味着每个样本都有一个一维的输出向量。assert out.size(0) == 5
确保输出批次的大小为5,即输入批次中的样本数量。
image_encoder_output_dim = out.size(1)
:
- 这行代码获取输出向量的维度,即模型对每个输入图像生成的特征数量。
- 最后,函数返回修改后的模型和图像编码器的输出维度。
整体来说,这段代码用于准备和调整模型,使其适用于特征提取任务,而非最终的分类任务,并且能确定模型输出特征的维度。这在转移学习或多任务学习的上下文中是很常见的做法。
三、权重初始化
Apply Kaiming initialization to the attribute predictors
for predictor in self.attribute_predictors:
nn.init.kaiming_normal_(predictor.weight, nonlinearity='relu')
nn.init.zeros_(predictor.bias)
这段代码是用Python语言编写的,具体来说是使用了PyTorch这个深度学习框架。它的目的是对一个神经网络中的属性预测器(attribute predictors)部分进行参数初始化。这里分为两步:
nn.init.kaiming_normal_(predictor.weight, nonlinearity='relu')
:
这一行对每个属性预测器的权重(weight)进行Kaiming/He正态分布初始化。这种初始化方法是由Kaiming He等人提出的,特别适合配合ReLU激活函数使用的深度神经网络。它尝试保持输入和输出的方差一致,以此来避免在前向传播和反向传播时梯度消失或爆炸的问题。nn.init.zeros_(predictor.bias)
:
这一行将每个属性预测器的偏置(bias)初始化为零。这是一个常见的偏置初始化方法,它简单地将所有偏置值设置为零。
综上所述,这段代码中的循环遍历了一个名为self.attribute_predictors
的网络部分中的所有属性预测器,对每个预测器的权重和偏置进行了适当的初始化。这是训练深度神经网络时的一个重要步骤,因为合适的初始化可以显著提高训练的效率和最终模型的性能。