最近在看论文,发现一种有趣的通道注意力机制,这里记录一下,方便以查阅。
脑电信号处理——加入通道注意力
方案示例
定义一个可训练的张量
理论说明
torch.nn.Parameter
是继承自torch.Tensor的子类,其主要作用是作为nn.Module中的可训练参数使用。它与torch.Tensor的区别就是nn.Parameter会自动被认为是module的可训练参数,即加入到parameter()这个迭代器中去;而module中非nn.Parameter()的普通tensor是不在parameter中的。
torch.nn.parameter.Parameter(data=None, requires_grad=True)
nn.Parameter可以看作是一个·类型转换函数·,将一个不可训练的类型 Tensor 转换成可以训练的类型 parameter ,并将这个 parameter 绑定到这个module 里面(net.parameter() 中就有这个绑定的 parameter,所以在参数优化的时候可以进行优化),所以经过类型转换这个变量就变成了模型的一部分,成为了模型中根据训练可以改动的参数。使用这个函数的目的也是想让某些变量在学习的过程中不断的修改其值以达到最优化。
在nn.Module类中,pytorch也是使用nn.Parameter来对每一个module的参数进行初始化的:
但是如果 nn.Parameter(requires_grad=False) 那么这个参数虽然绑定到模型里了,但是还是不可训练的,只是为了模型完整性这样写。
requires_grad默认值为True,表示可训练,False表示不可训练。
这样写还有一个好处就是,这个参数会随着模型的被移到cuda上,即如果执行过model.cuda(), 那么这个参数也就被移到了cuda上了。
举个栗子
import torch
from torch import nn
class MyModule(nn.Module):
def __init__(self, input_size, output_size):
super(MyModule, self).__init__()
self.test = torch.rand(input_size, output_size)
self.linear = nn.Linear(input_size, output_size)
def forward(self, x):
return self.linear(x)
model = MyModule(4, 2)
print(list(model.named_parameters()))
import torch
from torch import nn
class MyModule(nn.Module):
def __init__(self, input_size, output_size):
super(MyModule, self).__init__()
self.test = nn.Parameter(torch.rand(input_size, output_size))
self.linear = nn.Linear(input_size, output_size)
def forward(self, x):
return self.linear(x)
model = MyModule(4, 2)
print(list(model.named_parameters()))
也可以在外面,通过register_parameter()
注册
import torch
from torch import nn
class MyModule(nn.Module):
def __init__(self, input_size, output_size):
super(MyModule, self).__init__()
self.linear = nn.Linear(input_size, output_size)
def forward(self, x):
return self.linear(x)
model = MyModule(4, 2)
my_test = nn.Parameter(torch.rand(4, 2))
model.register_parameter('test',my_test)
print(list(model.named_parameters()))
Kaiming initialization
参数初始化就是这么一个容易被忽视的重要因素,因为不仅使用者对其重要性缺乏概念,而且这些操作都被TF、pytorch这些框架封装了,糟糕的参数初始化是会阻碍复杂非线性系统的训练的。
参数初始化条件:
- 初始化必要条件一:各层激活值不会出现饱和现象。
- 初始化必要条件二:各层激活值不为0。
简答的说就是(以sigmoid为例):
- 如果初始值很大,那么随着层数的传递,方差会迅速增加,此时输入值变得很大,而sigmoid在大输入值导数趋近于0,反向传播时会遇到梯度消失的问题
- 如果初始化值很小,那么随着层数的传递,方差就会趋于0,此时输入值 也变得越来越小,在sigmoid上就是在0附近,接近于线性,失去了非线性
Xavier初始化
上面给出了参数初始化的必要条件。但是这两个条件只保证了训练过程中可以学到有用的信息——参数梯度不为0。而Glorot认为:优秀的初始化应该使得各层的激活值和状态梯度的方差在传播过程中的方差保持一致:
∀ ( i , j ) , V a r ( h i ) = V a r ( h j ) ∀(i,j),Var(h^i)=Var(h^j) ∀(i,j),Var(hi)=Var(hj)
∀ ( i , j ) , V a r ( ∂ C o s t ∂ z i ) = V a r ( ∂ C o s t ∂ z j ) ∀(i,j),Var(\frac{∂Cost}{∂z^i})=Var(\frac{∂Cost}{∂z^j}) ∀(i,j),Var(∂zi∂Cost)=Var(∂zj