目录
一、前言
二、完成情况
2.1 特征图保存方法
2.1.1 定义网络模型
2.1.2 定义保存特征图的钩子函数
2.1.3 为模型层注册钩子
2.1.4 运行模型并检查特征图
2.2 实验情况
三、下周计划
一、前言
本周的7月11日~7月14日参加了机器培训的学习讨论会,对很多概念有了更深的理解。同时,在空闲时间利用笔记本跑了小批量的数据。
二、完成情况
2.1 特征图保存方法
在深度学习领域,在网络训练过程中生成每一阶段的特征图是一个常见的需求。
2.1.1 定义网络模型
首先,需要确定我们当前使用的网络模型,不同的模型结果具有不同的层数和层类型,这将影响特征图的生成。
这里以InversionNet为例:
class InversionNet(nn.Module):
def __init__(self, dim1=32, dim2=64, dim3=128, dim4=256, dim5=512, sample_spatial=1.0, **kwargs):
super(InversionNet, self).__init__()
self.convblock1 = ConvBlock(5, dim1, kernel_size=(7, 1), stride=(2, 1), padding=(3, 0))
self.convblock2_1 = ConvBlock(dim1, dim2, kernel_size=(3, 1), stride=(2, 1), padding=(1, 0))
self.convblock2_2 = ConvBlock(dim2, dim2, kernel_size=(3, 1), padding=(1, 0))
self.convblock3_1 = ConvBlock(dim2, dim2, kernel_size=(3, 1), stride=(2, 1), padding=(1, 0))
self.convblock3_2 = ConvBlock(dim2, dim2, kernel_size=(3, 1), padding=(1, 0))
self.convblock4_1 = ConvBlock(dim2, dim3, kernel_size=(3, 1), stride=(2, 1), padding=(1, 0))
self.convblock4_2 = ConvBlock(dim3, dim3, kernel_size=(3, 1), padding=(1, 0))
self.convblock5_1 = ConvBlock(dim3, dim3, stride=2)
self.convblock5_2 = ConvBlock(dim3, dim3)
self.convblock6_1 = ConvBlock(dim3, dim4, stride=2)
self.convblock6_2 = ConvBlock(dim4, dim4)
self.convblock7_1 = ConvBlock(dim4, dim4, stride=2)
self.convblock7_2 = ConvBlock(dim4, dim4)
self.convblock8 = ConvBlock(dim4, dim5, kernel_size=(8, ceil(70 * sample_spatial / 8)), padding=0)
self.deconv1_1 = DeconvBlock(dim5, dim5, kernel_size=5)
self.deconv1_2 = ConvBlock(dim5, dim5)
self.deconv2_1 = DeconvBlock(dim5, dim4, kernel_size=4, stride=2, padding=1)
self.deconv2_2 = ConvBlock(dim4, dim4)
self.deconv3_1 = DeconvBlock(dim4, dim3, kernel_size=4, stride=2, padding=1)
self.deconv3_2 = ConvBlock(dim3, dim3)
self.deconv4_1 = DeconvBlock(dim3, dim2, kernel_size=4, stride=2, padding=1)
self.deconv4_2 = ConvBlock(dim2, dim2)
self.deconv5_1 = DeconvBlock(dim2, dim1, kernel_size=4, stride=2, padding=1)
self.deconv5_2 = ConvBlock(dim1, dim1)
self.deconv6 = ConvBlock_Tanh(dim1, 1)
def forward(self, x):
# Encoder Part
x = self.convblock1(x) # (None, 32, 500, 70)
x = self.convblock2_1(x) # (None, 64, 250, 70)
x = self.convblock2_2(x) # (None, 64, 250, 70)
x = self.convblock3_1(x) # (None, 64, 125, 70)
x = self.convblock3_2(x) # (None, 64, 125, 70)
x = self.convblock4_1(x) # (None, 128, 63, 70)
x = self.convblock4_2(x) # (None, 128, 63, 70)
x = self.convblock5_1(x) # (None, 128, 32, 35)
x = self.convblock5_2(x) # (None, 128, 32, 35)
x = self.convblock6_1(x) # (None, 256, 16, 18)
x = self.convblock6_2(x) # (None, 256, 16, 18)
x = self.convblock7_1(x) # (None, 256, 8, 9)
x = self.convblock7_2(x) # (None, 256, 8, 9)
x = self.convblock8(x) # (None, 512, 1, 1)
# Decoder Part Vmodel
x = self.deconv1_1(x) # (None, 512, 5, 5)
x = self.deconv1_2(x) # (None, 512, 5, 5)
x = self.deconv2_1(x) # (None, 256, 10, 10)
x = self.deconv2_2(x) # (None, 256, 10, 10)
x = self.deconv3_1(x) # (None, 128, 20, 20)
x = self.deconv3_2(x) # (None, 128, 20, 20)
x = self.deconv4_1(x) # (None, 64, 40, 40)
x = self.deconv4_2(x) # (None, 64, 40, 40)
x = self.deconv5_1(x) # (None, 32, 80, 80)
x = self.deconv5_2(x) # (None, 32, 80, 80)
x = F.pad(x, [-5, -5, -5, -5], mode="constant", value=0) # (None, 32, 70, 70) 125, 100
x = self.deconv6(x) # (None, 1, 70, 70)
return x
2.1.2 定义保存特征图的钩子函数
接下来,定义特征图保存函数。PyTorch提供了register_forward_hook
方法,允许在模型的特定层上注册一个回调函数,该函数将在该层的前向传播之后被调用。因此,我们可以利用这个机制来自动保存每个阶段的特征图。钩子方法(hook method)是回调函数(callback function)的一种,它相当于一个监测器,在消息传递的过程中,捕获自己感兴趣的内容,然后去处理。
import torch
# 全局的字典用于存储特征图
feature_maps = {}
def save_feature_map(module, input, output):
# 'module' 是注册了钩子的模块(在这里以一个convblock为例)
# 'input' 是该模块的输入
# 'output' 是该模块的输出,即我们要保存的特征图
# 我们需要一种方式来唯一标识每个convblock,这里我们简单地使用模块的名称
key = module.__class__.__name__ if hasattr(module, '__class__') else type(module).__name__
if key.startswith('ConvBlock'): # 假设所有convblock类的名称都以'ConvBlock'开头
feature_maps[key] = output.detach().cpu() # 保存特征图到CPU
# 注意:上面的key获取方式是基于假设的,需要根据实际的convblock类名来调整
2.1.3 为模型层注册钩子
然后,需要再模型初始化初始化或某个适当的地方,为这些convblock
注册钩子。这里为所有的convblock层注册钩子,也可以单独注册。
# 一个方法来注册所有ConvBlock的钩子
def register_ConvBlock_hooks(self):
for name, module in self._modules.items():
if isinstance(module, convblock):
handle = module.register_forward_hook(self.save_feature_map)
self.hook_handles.append((name, handle))
2.1.4 运行模型并检查特征图
在训练循环中,除了正常的梯度计算和参数更新外,还需要确保特征图保存机制被正确触发。在每个epoch或每个batch之后,根据需求保存特征图。
def save_feature_map(self, module, input, output):
# 打印输出尺寸作为示例
print(f"Feature map shape from {module._get_name()}: {output.shape}")
# 假设我们想要保存第一个批次和第一个通道的特征图
batch_idx, channel_idx = 0, 0
# 提取特征图的一个批次和一个通道
feature_map = output[batch_idx, channel_idx, :, :]
# 将tensor从GPU(如果有)移动到CPU,并转换为numpy数组
feature_map_np = feature_map.detach().cpu().numpy()
# 归一化特征图到[0, 1]
feature_map_np = (feature_map_np - feature_map_np.min()) / (feature_map_np.max() - feature_map_np.min())
# 使用PIL或其他库保存图像
# 由于PIL期望的是[0, 255]的整数,需要将归一化后的图像乘以255并转换为uint8
image = Image.fromarray((feature_map_np * 255).astype(np.uint8))
# 为模块创建一个唯一的名称(这里只是示例)
module_name = module.__class__.__name__ + "_" + str(id(module))
# 保存图像
image_path = f"{module_name}_feature_map.png"
image.save(image_path)
print(f"Saved feature map to {image_path}")
目前只能生成灰度图像,在生成彩色图片时遇到问题,报错如下所示,下周解决该问题。
2.2 实验情况
在本周对设计的实验进行了验证,在验证过程中发现选择合适的学习率很重要,指标相差也很大。
目前的网络结构效果在5000的 CurveFaultA 数据集上优于InversionNet,但是不知道大数据集上表现如何。
损失函数的表现在小数据集上不太明显,部分指标较好,但是部分指标不好。
三、下周计划
-
利用工作站完成实验;
-
验证特征图保存方法是否适用;
-
了解高级优化算法;