二、模型
2.1 模型的搭建
2.1.1 模型定义的三要
- 必须继承
nn.Module
这个类,要让PyTorch知道这个类是一 个Module
- 在
__init(self)__
中设置好需要的“组件”(如conv、pooling、Linear、BatchNorm等) - 在
forward(self, x)
中用定义好的“组件”进行组装,就像搭积木,把网络结构搭建出来
2.1.2 nn.Sequential
torch.nn.Sequential
其实就是Sequential容器,该容器将一系列操作按先后顺序给抱起来,方便重复使用
2.2 权值初始化的10种方法
- 初始化流程:
- 先设定什么层用什么初始化方法,初始化方法在
torch.nn.init
中给出 - 实例化一个模型之后,执行该函数,即可完成初始化
- 先设定什么层用什么初始化方法,初始化方法在
2.2.1 Xavier均匀分布
torch.nn.init.xavier_uniform_(tensor, gain=1)
- 服从均匀分布 U ( − a , a ) U(-a, a) U(−a,a),分布的参数 a = g a i n ∗ 6 f a n _ i n + f a n _ o u t a=gain* \sqrt{\frac{6}{fan\_in}+fan\_out} a=gain∗fan_in6+fan_out
gain
的大小是依据激活函数来信来设定- eg:
nn.init.xavier_uniform_(w, gain=nn.init.calculate_gain('relu'))
2.2.2 Xavier正态分布
torch.nn.init.xavier_normal_(tensor, gain=1)
- 服从正态分布
- m e a n = 0 mean=0 mean=0, s t d = g a i n ∗ 2 f a n _ i n + f a n _ o u t std=gain*\sqrt{\frac{2}{fan\_in}+fan\_out} std=gain∗fan_in2+fan_outstd=gain*sqrt(2/fan_in+fan_out)
2.2.3 kaiming均匀分布
torch.nn.init.kaiming_uniform_(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu')
- 此为均匀分布, U ∼ ( − b o u n d , b o u n d ) U\sim(-bound,bound) U∼(−bound,bound), b o u n d = 6 1 + a 2 ∗ f a n _ i n bound=\sqrt{\frac{6}{1+a^2}*fan\_in} bound=1+a26∗fan_in,其中 a a a为激活函数的负半轴的斜率,relu是0
- mode可选为
fan_in
(正向传播时方差一致)或fan_out
(反向传播时,方差一致) - nonlinearity可选
relu
和leaky_relu
2.2.4 kaiming正态分布
torch.nn.init.kaiming_normal_(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu')
- 此为0均值的正态分布, N ∼ ( 0 , s t d ) N\sim(0,std) N∼(0,std),其中 s t d = 2 1 + a 2 ∗ f a n _ i n std=\sqrt{\frac{2}{1+a^2}*fan\_in} std=1+a22∗fan_in,其中 a a a为激活函数的负半轴的斜率,relu是0
- mode可选为
fan_in
(正向传播时方差一致)或fan_out
(反向传播时,方差一致) - nonlinearity可选
relu
和leaky_relu
2.2.5 均匀分布初始化
torch.nn.init.uniform_(tensor, a=0, b=1)
- 服从均匀分布 U ( a , b ) U(a,b) U(a,b)
2.2.6 正态分布初始化
torch.nn.init.normal_(tensor, mean=0, std=1)
- 服从正态分布 N ( m e a n , s t d ) N(mean,std) N(mean,std)
2.2.7 常数初始化
torch.nn.init.constant_(tensor, val)
- 使值为常数 v a l val val
2.2.8 单位矩阵初始化
torch.nn.init.eye_(tensor)
- 将二维tensor初始化为单位矩阵
2.2.9 正交初始化
torch.nn.init.orthogonal_(tensor, gain=1)
- 使得tensor是正交的
2.2.10 稀疏初始化
torch.nn.init.sparse_(tensor, sparsity, std=0.01)
- 从正态分布 N ( 0 , s t d ) N(0,std) N(0,std)中进行稀疏化,使得每一个column有一部分为0
- sparsity-每一个column稀疏的比例,即为0的比例
2.2.11 计算增益
torch.nn.init.calculate_gain(nonlinearity, param=None)
2.3 模型Finetune
一个良好的权值初始化,可以使收敛速度加快,甚至可以获得更好的精度。而在实际应用中,通常采用一个已经训练模型的模型的权值参数作为模型的初始化参数,也称之为Finetune,更宽泛的称之为迁移学习。迁移学习中的Finetune技术,本质上就是让新构建的模型,拥有一个较好的权值初始值。
- finetune流程:
- 保存模型,拥有一个预训练模型
- 加载模型,把预训练模型中的权值取出来
- 初始化,将权值对应的“放”到新模型中
2.3.1 Finetune之权值初始化
- 保存模型的方法
- 保存整个模型
- 仅保存模型参数(官方推荐)
- 保存模型
-
保存模型参数
net = Net() torch.save(net.state_dict(), 'net_params.pkl')
-
加载模型(加载模型的参数)
pretrained_dict = torch.load('net_params.pkl')
-
初始化
将取到的权值,对应放到新模型中
# 创建新模型net net = Net() # 获取已创建net的state_dict net_state_dict = net.state_dict() # 将pretrained_dict里不属于net_state_dict的键剔除掉 pretrained_dict_1 = {k:v for k, v in pretrained_dict.items() if k in net_state_dict} # 用预训练模型的参数字典对新模型的参数字典net_state_dict进行更新 net_state_dict.updata(pretrained_dict_1) # 将更新了参数的字典“放”回到网络中 net.load_state_dict(net_state_dict)
-
2.3.2不同层设置不同的学习率
采用finetune的训练过程中,有时候希望前面层的学习率低一些,改变不要太大,而后面的全连接层的学习率相对大一些。就需要对不同的层设置不同的学习率
-
为不同层设置不同的学习率,主要通过优化器对多个参数组进行设置不同的参数
-
实例
将原始参数“切分”成fc3层参数和其余参数,为fc3层设置更大的学习率。挑选出特定的层的机制是利用内存地址作为过滤条件。
# 将fc3层的参数net.fc3.parameters()从原始参数net.parameters()中剥离出来 ingored_params = list(map(id, net.fc3.parameters())) # 返回的是parameters的内存地址 base_params = filter(lambda p: id(p) not in ignored_params, net.parameters()) # 返回剥离了fc3层的参数的其余参数 optimizer = optim.SGD( [{'params': base_params}, {'params': net.fc3.parameters(), 'lr': 0.001*10}], 0.001, momentum=0.9, weight_decay=1e-4 )