Pytorch神经网络魔改之:模型融合 - 速通(1)
本文将以几种常见方法为例,介绍如何进行Pytorch神经网络的模型融合:
1. 子模型串联(Sequential Concatenation)
在这个方法中,输入数据x首先通过FeatureExtractor(即:子模型1),处理后的结果再传递给Classifier(即:子模型2)。最后,返回Classifier的输出。这种方式允许将两个子模型串联起来,形成一个组合模型:
import torch.nn as nn
# 定义FeatureExtractor子模型,主要负责提取输入图像的特征
class FeatureExtractor(nn.Module):
def __init__(self):
super(FeatureExtractor, self).__init__()
# 定义第一个全连接层:输入特征维度为784(28x28图像拉平后的大小),输出维度为512
self.dense_layer1 = nn.Linear(784, 512)
# 定义ReLU激活函数以引入非线性,增强模型的表达能力
self.activation = nn.ReLU()
# 定义第二个全连接层:进一步转换特征到256维
self.dense_layer2 = nn.Linear(512, 256)
def forward(self, x):
# 将输入图像数据从二维图像格式拉平为一维向量格式
x = x.view(-1, 784)
# 数据流经第一个全连接层后,应用ReLU激活函数
x = self.dense_layer1(x)
x = self.activation(x)
# 经过第二个全连接层
x = self.dense_layer2(x)
return x
# 定义Classifier子模型,用于基于提取的特征进行分类
class Classifier(nn.Module):
def __init__(self):
super(Classifier, self).__init__()
# 定义第三个全连接层:从256维特征降至128维
self.dense_layer3 = nn.Linear(256, 128)
# 再次使用ReLU激活函数
self.activation = nn.ReLU()
# 定义第四个全连接层:最终将特征映射到10个输出类别(假设为10类分类问题)
self.dense_layer4 = nn.Linear(128, 10)
def forward(self, x):
# 数据流经第三个全连接层,再次应用ReLU激活
x = self.dense_layer3(x)
x = self.activation(x)
# 最后经过第四个全连接层,得到分类输出
x = self.dense_layer4(x)
return x
# 定义ImageClassifier组合模型,整合FeatureExtractor和Classifier完成从图像到分类标签的过程
class ImageClassifier(nn.Module):
def __init__(self, feature_extractor, classifier):
super(ImageClassifier, self).__init__()
# 特征提取模块
self.feature_extractor = feature_extractor
# 分类模块
self.classifier = classifier
def forward(self, x):
# 首先通过特征提取模块提取特征
x = self.feature_extractor(x)
# 然后通过分类模块得到分类结果
x = self.classifier(x)
return x
# 实例化FeatureExtractor和Classifier
feature_extractor = FeatureExtractor()
classifier = Classifier()
# 创建ImageClassifier组合模型,结合上述两个模型
image_classifier = ImageClassifier(feature_extractor, classifier)
在上方的子模型串联示例中,两个子模型(FeatureExtractor 和 Classifier)被顺序连接。首先,输入数据x被送入FeatureExtractor进行特征提取,处理后的结果再传递给Classifier进行分类。这种方式下,数据流是线性的,即从一个模块流向下一个模块,直到最终输出。这种模型结构在处理流水线式任务时非常有效,比如先提取特征,然后再做分类。
image_classifier = ImageClassifier(feature_extractor, classifier)
通过ImageClassifier类将FeatureExtractor和Classifier串联起来,ImageClassifier的forward方法首先通过feature_extractor提取特征,然后将这些特征传递给classifier获得最终的分类结果。
通过实现__init__和forward方法,ImageClassifier能够初始化子模型并定义它们的串联方式,这样就可以在训练和推理时作为一个单一的组合模型使用。
2. 并行拼接 (Parallel Concatenation)
在某些情况下,我们可能想要在特定维度上将多个模块的输出合并起来,比如在GoogLeNet中的Inception模块。
import torch
import torch.nn as nn
class ParallelConcatModule(nn.Module):
def __init__(self):
super(ParallelConcatModule, self).__init__()
# 定义第一个并行分支
self.branch1 = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=1),
nn.ReLU(),
)
# 定义第二个并行分支
self.branch2 = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=3, padding=1),
nn.ReLU(),
)
def forward(self, x):
# 分别计算两个分支
out1 = self.branch1(x)
out2 = self.branch2(x)
# 在特征维度上合并这两个分支的输出
out = torch.cat((out1, out2), dim=1) # dim=1 表示在通道维度上进行合并
return out
# 实例化模型
model = ParallelConcatModule()
# 假设输入图片大小为 [N, C, H, W] = [1, 3, 32, 32]
input_tensor = torch.randn(1, 3, 32, 32)
# 通过模型得到输出
output = model(input_tensor)
上方并行拼接示例展示了如何在特定维度上合并多个模块的输出。在这个例子中,有两个并行的分支(branch1 和 branch2),它们分别对相同的输入数据x进行处理。处理完成后,这两个分支的输出在通道维度(dim=1)上合并,使用torch.cat实现。
out = torch.cat((out1, out2), dim=1)
这种结构(并行拼接)允许模型同时学习和提取不同的特征表示,并将它们合并起来,从而获得更丰富的信息。这在需要模型从多个角度学习输入数据的特征时特别有用,例如在GoogLeNet的Inception模块中看到的。
串/并联对比
串联:模型按顺序执行,每个子模型的输出直接成为下一个子模型的输入。这种方式适用于任务可以分解为顺序子任务的场景。
并联:模型并行执行,多个子模型独立处理相同的输入,然后在某个维度上合并它们的输出。这种方式适用于需要模型从多个维度或尺度理解输入数据时。
3. 常用torch.nn方法
1. torch.nn.Sequential
torch.nn.Sequential是一种顺序容器。模块会按照它们在构造函数中传递的顺序添加到其中。对于Sequential,输入会按照定义的顺序通过所有模块:
import torch
import torch.nn as nn
# 使用Sequential定义一个简单的前馈神经网络
model = nn.Sequential(
nn.Linear(784, 256), # 将输入的784维特征映射到256维
nn.ReLU(), # 256维特征通过ReLU激活函数
nn.Linear(256, 128), # 再将256维映射到128维
nn.ReLU(), # 通过ReLU激活函数
nn.Linear(128, 10) # 最后将128维特征映射到10个输出类别
)
# 为每一层添加命名
model = nn.Sequential(
('input_to_hidden', nn.Linear(784, 256)),
('activation1', nn.ReLU()),
('hidden_to_hidden', nn.Linear(256, 128)),
('activation2', nn.ReLU()),
('hidden_to_output', nn.Linear(128, 10))
)
# 演示输入如何通过模型
input_tensor = torch.randn(1, 784) # 假设有一个1x784的随机输入向量
output = model(input_tensor) # 获取模型的输出
上面模型用类定义的等价形式如下:
import torch
import torch.nn as nn
class SimpleFeedForwardNN(nn.Module):
def __init__(self):
super(SimpleFeedForwardNN, self).__init__()
# 初始化网络层
self.input_to_hidden = nn.Linear(784, 256) # 将输入的784维特征映射到256维
self.activation1 = nn.ReLU() # 256维特征通过ReLU激活函数
self.hidden_to_hidden = nn.Linear(256, 128) # 再将256维映射到128维
self.activation2 = nn.ReLU() # 通过ReLU激活函数
self.hidden_to_output = nn.Linear(128, 10) # 最后将128维特征映射到10个输出类别
def forward(self, x):
# 定义前向传播路径
x = self.input_to_hidden(x)
x = self.activation1(x)
x = self.hidden_to_hidden(x)
x = self.activation2(x)
x = self.hidden_to_output(x)
return x
# 实例化模型
model = SimpleFeedForwardNN()
# 演示输入如何通过模型
input_tensor = torch.randn(1, 784) # 假设有一个1x784的随机输入向量
output = model(input_tensor) # 获取模型的输出
print(output) # 打印输出以确认模型工作正常
2. torch.nn.ModuleList
torch.nn.ModuleList是一个持有子模块的列表,可以像常规Python列表一样进行索引,但是在注册和管理子模块方面,它会被PyTorch认为是一个模块。
class CustomModel(nn.Module):
def __init__(self):
super(CustomModel, self).__init__()
# 使用ModuleList存储多个卷积层
self.conv_layers = nn.ModuleList([nn.Conv2d(1, 20, 5), nn.Conv2d(20, 40, 5)])
'''
ModuleList的等价形式:
self.conv1 = nn.Conv2d(1, 20, 5)
self.conv2 = nn.Conv2d(20, 40, 5)
'''
self.flatten = nn.Flatten() # Flatten层,用于将卷积层输出扁平化处理
self.fc_layer = nn.Linear(40, 10) # 全连接层
def forward(self, x):
# 依次通过所有卷积层
for layer in self.conv_layers:
x = nn.functional.relu(layer(x))
x = self.flatten(x) # 扁平化处理
x = self.fc_layer(x) # 通过全连接层
return x
3. torch.nn.ModuleDict
torch.nn.ModuleDict类似于ModuleList,但是模块以字典形式存储,这使得根据名字来访问特定模块更加方便。
class CustomModelDict(nn.Module):
def __init__(self):
super(CustomModelDict, self).__init__()
self.layers = nn.ModuleDict({
'conv1': nn.Conv2d(1, 20, 5),
'conv2': nn.Conv2d(20, 40, 5),
'flatten': nn.Flatten(),
'fc': nn.Linear(40, 10)
})
def forward(self, x):
x = nn.functional.relu(self.layers['conv1'](x))
x = nn.functional.relu(self.layers['conv2'](x))
x = self.layers['flatten'](x)
x = self.layers['fc'](x)
return x
4. torch.nn.functional
torch.nn.functional提供了许多函数式接口,这些接口允许构建更加灵活和动态的模型。在需要对模型的行为进行精细控制的情况下,这个模块非常有用。
import torch.nn.functional as F
class CustomFunctionalModel(nn.Module):
def __init__(self):
super(CustomFunctionalModel, self).__init__()
self.conv1 = nn.Conv2d(1, 20, 5)
self.conv2 = nn.Conv2d(20, 40, 5)
self.fc = nn.Linear(40, 10)
def forward(self, x):
x = F.relu(self.conv1(x)) # 使用functional接口进行激活
x = F.relu(self.conv2(x))
x = F.relu(self.fc(x))
return x
更多推荐
所有评论(0)