1.
nn.Conv2d(mid_channel * 2, 2, kernel_size=1),
接受三个参数:输入通道数 mid_channel * 2
,输出通道数 2,以及卷积核大小 kernel_size=1
。因为卷积核大小为 1,所以这个层实际上就是一个 1x1 卷积层,它的作用是将 mid_channel * 2
维的特征图降维到 2 维。
在这个模型中,这个卷积层主要用于实现注意力机制,即将两个输入特征图进行融合。
因此,输出通道数设置为 2,表示将两个特征图融合成一个 2 通道的特征图,
其中第一通道表示权重,第二通道表示残差。
这里使用了 Sigmoid 激活函数对权重进行限制在 [0, 1] 范围内,
方便之后对特征图进行加权。
2.
nn.init.kaiming_uniform_(self.conv1[0].weight, a=1)
这行代码使用了 PyTorch 中提供的一种参数初始化方式,即 Kaiming Initialization。
它是一种针对深度神经网络的权重初始化方法,
旨在解决梯度消失和梯度爆炸的问题,同时也可以提高模型的收敛速度和精度。
具体而言,这行代码将 self.conv1
层的卷积核参数 weight
进行初始化。nn.init.kaiming_uniform_
是一个 PyTorch 提供的初始化函数,
它可以根据传入的参数初始化张量,并且支持多种不同的初始化方法。
这里使用了 kaiming_uniform_
方法,并且设置了一个参数 a=1
。
a
参数是一个负责激活函数非线性部分的斜率参数,它的取值范围一般在 [0, 2] 之间。
在 Kaiming Initialization 中,a=1
表示使用了 Rectified Linear Unit (ReLU) 激活函数,
它在非负区间上是线性的,而在负数区间上是不活跃的。
这个参数的具体取值需要根据激活函数的不同来选择。
3.
def forward(self, x, y=None, shape=None, out_shape=None):
这是一个 PyTorch 模型类中的前向传播函数。
它接收四个输入参数 x
、 y
、 shape
和 out_shape
,
表示输入张量、辅助张量、y
的空间尺度和输出张量的空间尺度。
具体而言,这个函数的作用是将输入 x
进行一些卷积计算和特征融合,
最终得到一个输出张量。
具体的计算流程如下:
- 对输入张量
x
进行 1x1 卷积和BN操作,得到一个中间特征张量x
. - 判断是否需要进行辅助特征融合。如果
self.att_conv
不为空,则进行融合。、、、、、、具体的操作是对辅助特征y
进行上采样,将x
和y
进行拼接,然后进行 1x1 卷积和 sigmoid 激活操作,得到一个融合权重张量z
. 接着,将x
和y
进行加权相加,得到一个新的中间特征张量x
. - 判断输出张量 x是否需要进行空间尺度的变换。
- 如果需要,进行上采样操作,将其变为指定的空间尺度
out_shape
. - 对新的中间特征张量
x
进行conv2操作,得到最终的输出张量y
.
最终返回值是一个包含 y
和 x
两个张量的元组,其中 y
是最终的输出张量,x
是经过特征融合后得到的中间特征张量。
这里有点乱 我再理理
4.x = F.interpolate(x, (out_shape, out_shape), mode="nearest")
这行代码使用了双线性插值将x张量的空间维度调整为指定的输出大小,
以便能够正常地作为下一层的输入。
具体来说,该函数使用了双线性插值算法,
将x张量的空间维度从当前大小调整为(out_shape, out_shape)。
mode="nearest"表示在调整大小时使用最近邻插值算法,
即每个新像素点的值 都等于 距离其最近的原始像素点的值。
5.mode="nearest"还有别的参数吗
mode="nearest"
:使用最近邻插值算法,即每个新像素点的值都等于距离其最近的原始像素点的值。mode="bilinear"
:使用双线性插值算法,即在二维空间上使用两个方向的线性插值。mode="bicubic"
:使用双三次插值算法,即在二维空间上使用两个方向的三次插值。mode="trilinear"
:使用三线性插值算法,即在三维空间上使用三个方向的线性插值。
此外,还可以使用mode="area"
将每个输出像素均匀分配到输入像素的区域中,并计算每个区域的平均值。该模式通常用于缩小图像的情况,可以保留图像的大致结构,并降低噪声。
6. if x.shape[-1] != out_shape:
x = F.interpolate(x, (out_shape, out_shape), mode="nearest")
y = self.conv2(x)
这段代码的作用是判断x的尺寸是否与out_shape相同,如果不同,就使用F.interpolate函数对x进行上采样或下采样,使得x的尺寸与out_shape相同,
然后将结果送入self.conv2进行卷积操作,生成最终的输出y。
这里使用的插值方式是最近邻插值(mode="nearest"),该插值方式会将目标像素的值设置为距离该像素最近的那个输入像素的值。
7. n, _, h, w = x.shape
- n 表示 batch size,即 x 张量的第一维的大小,也就是一个 batch 中有多少张图片。
- h 表示 x 张量的高度,即第三维的大小。
- w 表示 x 张量的宽度,即第四维的大小
那个_呢?
在 Python 中,下划线 _
通常用于占位符,表示一个值不需要使用,或者临时用作某个变量的名字,但不会在后续的代码中再次使用。在这里,将 x.shape
中的四个维度分别赋值给了 n, _, h, w
四个变量,但是只使用了 n, h, w
三个变量,因此使用下划线 _
占位符表示第二个维度不需要使用。
z = self.att_conv(z)
self.att_conv
是一个包含两个操作的序列。
首先,使用一个1x1卷积层将通道数从mid_channel*2
降到2。
然后,将得到的2个通道传递给一个sigmoid函数,将它们的值限制在0到1之间。
因此,z
是一个大小为(batch_size, 2, height, width)
的张量,
其中每个像素的值都是两个通道的sigmoid值,表示一个特定位置的权重。
这些权重被用来对特征进行融合。
x = x * z[:, 0].view(n, 1, h, w) + y * z[:, 1].view(n, 1, h, w)
这一行代码的作用是实现注意力加权融合。
首先,使用cat()
函数将x
和y
在通道维度上进行拼接,
然后传入self.att_conv()
中,这个self.att_conv()
是一个小型的卷积神经网络,
输入的是拼接好的特征图,输出的是大小为2(因为融合的特征图需要包含两个通道,即学生模型的特征图和教师模型的特征图)的注意力权重矩阵。
这个权重矩阵中的两个值表示在进行融合的时候,需要给哪个模型的特征图赋予更大的权重。
然后,将这个权重矩阵与x
和y
相乘并相加,即可得到最终的融合特征图,
即x * z[:, 0].view(n, 1, h, w) + y * z[:, 1].view(n, 1, h, w)
。
其中,z[:, 0]
表示第一个通道的注意力权重,z[:, 1]
表示第二个通道的注意力权重,
view(n, 1, h, w)
的作用是将权重矩阵转换为大小为(n, 1, h, w)
的张量,
方便与特征图相乘并相加。
7. .view(n, 1, h, w)
.view(n, 1, h, w)
是 PyTorch 中的一种 reshape 操作,
它会将一个张量变成一个大小为 (n, 1, h, w)
的四维张量,
其中 n
是 batch size,h
和 w
分别是图片的高和宽。
这个操作的作用是将一维张量 z[:, 0]
和 z[:, 1]
重塑成二维张量,
使得能够与三维张量 x
和 y
相乘。
这样做的原因是 x
和 y
是卷积层的输出,它们的形状是 (n, c, h, w)
,
而 z[:, 0]
和 z[:, 1]
是特征融合模块的输出,
形状是 (n, 2, h, w)
,二者需要相乘得到最终的特征张量。
z[:, 0] 取出了 z
中所有行的第一个元素(第一列)。
z[:, 1] 取出了 z
中所有行的第2个元素(第二列)
z[:, 0]
取出了 z
中所有行的第一个元素。由于 z
是一个4D张量,因此 z[:, 0]
返回的是一个3D张量,其形状为 (n, h, w)
。
在 PyTorch 中,一个4D张量可以被视为一个由n个3D张量组成的张量序列,其中每个3D张量表示输入的一个通道。因此,当我们使用 z[:, 0]
时,我们正在访问序列中的第一个通道,这将返回一个3D张量,其形状为 (n, h, w)
。同样地,z[:, 1]
返回的是序列中的第二个通道,其形状也为 (n, h, w)
。
x.shape[-1] != out_shape
x.shape[-1]
表示张量 x
在最后一个维度上的大小,通常在二维卷积操作中,它表示图像的宽度(width)。
而 out_shape
则是指定的目标图像宽度,
如果二者不一致,那么就需要通过插值操作(F.interpolate
)将图像大小变为目标大小。F.interpolate
的第一个参数是输入张量,第二个参数是目标输出大小(一个元组,表示高度和宽度),
mode
参数指定了插值方法,例如 "nearest" 表示最近邻插值,即将目标像素的值设为距离它最近的输入像素的值。
今天就先到这吧