专栏首页机器学习炼丹术小白学PyTorch | 4 构建模型三要素与权重初始化

小白学PyTorch | 4 构建模型三要素与权重初始化

文章目录:

  • 1 模型三要素
  • 2 参数初始化
  • 3 完整运行代码
  • 4 尺寸计算与参数计算

这篇文章内容不多,比较基础,里面的代码块可以复制到本地进行实践,以加深理解。

喜欢的话,可以给公众号加一个星标,点点在看,这是对我最大的支持

1 模型三要素

三要素其实很简单

  1. 必须要继承nn.Module这个类,要让PyTorch知道这个类是一个Module
  2. 在__init__(self)中设置好需要的组件,比如conv,pooling,Linear,BatchNorm等等
  3. 最后在forward(self,x)中用定义好的组件进行组装,就像搭积木,把网络结构搭建出来,这样一个模型就定义好了

我们来看一个例子:先看__init__(self)函数

def __init__(self):
 super(Net,self).__init__()
 self.conv1 = nn.Conv2d(3,6,5)
 self.pool1 = nn.MaxPool2d(2,2)
 self.conv2 = nn.Conv2d(6,16,5)
 self.pool2 = nn.MaxPool2d(2,2)
 self.fc1 = nn.Linear(16*5*5,120)
 self.fc2 = nn.Linear(120,84)
 self.fc3 = nn.Linear(84,10)

第一行是初始化,往后定义了一系列组件。nn.Conv2d就是一般图片处理的卷积模块,然后池化层,全连接层等等。

定义完这些定义forward函数

def forward(self,x):
 x = self.pool1(F.relu(self.conv1(x)))
 x = self.pool2(F.relu(self.conv2(x)))
 x = x.view(-1,16*5*5)
 x = F.relu(self.fc1(x))
 x = F.relu(self.fc2(x))
 x = self.fc3(x)
 return x

x为模型的输入,第一行表示x经过conv1,然后经过激活函数relu,然后经过pool1操作 第三行表示对x进行reshape,为后面的全连接层做准备

至此,对一个模型的定义完毕,如何使用呢?

例如:

net = Net()
outputs = net(inputs)

其实net(inputs),就是类似于使用了net.forward(inputs)这个函数。

2 参数初始化

简单地说就是设定什么层用什么初始方法,初始化的方法会在torch.nn.init中

话不多说,看一个案例:

# 定义权值初始化
def initialize_weights(self):
 for m in self.modules():
  if isinstance(m,nn.Conv2d):
   torch.nn.init.xavier_normal_(m.weight.data)
   if m.bias is not None:
    m.bias.data.zero_()
  elif isinstance(m,nn.BatchNorm2d):
   m.weight.data.fill_(1)
   m.bias.data.zero_()
  elif isinstance(m,nn.Linear):
   torch.nn.init.normal_(m.weight.data,0,0.01)
   # m.weight.data.normal_(0,0.01)
   m.bias.data.zero_()

这段代码的基本流程就是,先从self.modules()中遍历每一层,然后判断更曾属于什么类型,是否是Conv2d,是否是BatchNorm2d,是否是Linear的,然后根据不同类型的层,设定不同的权值初始化方法,例如Xavier,kaiming,normal_等等。kaiming也是MSRA初始化,是何恺明大佬在微软亚洲研究院的时候,因此得名。

上面代码中用到了self.modules(),这个是什么东西呢?

# self.modules的源码
def modules(self):
 for name,module in self.named_modules():
  yield module

功能就是:能依次返回模型中的各层,yield是让一个函数可以像迭代器一样可以用for循环不断从里面遍历(可能说的不太明确)。

3 完整运行代码

我们用下面的例子来更深入的理解self.modules(),同时也把上面的内容都串起来(下面的代码块可以运行):

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset,DataLoader

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool1 = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.pool2 = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool1(F.relu(self.conv1(x)))
        x = self.pool2(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

    def initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                torch.nn.init.xavier_normal_(m.weight.data)
                if m.bias is not None:
                    m.bias.data.zero_()
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()
            elif isinstance(m, nn.Linear):
                torch.nn.init.normal_(m.weight.data, 0, 0.01)
                # m.weight.data.normal_(0,0.01)
                m.bias.data.zero_()

net = Net()
net.initialize_weights()
print(net.modules())
for m in net.modules():
    print(m)

运行结果:

# 这个是print(net.modules())的输出
<generator object Module.modules at 0x0000023BDCA23258>
# 这个是第一次从net.modules()取出来的东西,是整个网络的结构
Net(
  (conv1): Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))
  (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
  (pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (fc1): Linear(in_features=400, out_features=120, bias=True)
  (fc2): Linear(in_features=120, out_features=84, bias=True)
  (fc3): Linear(in_features=84, out_features=10, bias=True)
)
# 从net.modules()第二次开始取得东西就是每一层了
Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))
MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
Linear(in_features=400, out_features=120, bias=True)
Linear(in_features=120, out_features=84, bias=True)
Linear(in_features=84, out_features=10, bias=True)

其中呢,并不是每一层都有偏执bias的,有的卷积层可以设置成不要bias的,所以对于卷积网络参数的初始化,需要判断一下是否有bias,(不过我好像记得bias默认初始化为0?不确定,有知道的朋友可以交流)

torch.nn.init.xavier_normal(m.weight.data)
if m.bias is not None:
 m.bias.data.zero_()

上面代码表示用xavier_normal方法对该层的weight初始化,并判断是否存在偏执bias,若存在,将bias初始化为0。

4 尺寸计算与参数计算

我们把上面的主函数部分改成:

net = Net()
net.initialize_weights()
layers = {}
for m in net.modules():
    if isinstance(m,nn.Conv2d):
        print(m)
        break

这里的输出m就是:

Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))

这个卷积层,就是我们设置的第一个卷积层,含义就是:输入3通道,输出6通道,卷积核

5\times 5

,步长1,padding=0.

【问题1:输入特征图和输出特征图的尺寸计算】

之前的文章也讲过这个了,

output = \frac{input+2\times padding -kernel}{stride}+1

用代码来验证一下这个公式:

net = Net()
net.initialize_weights()
input = torch.ones((16,3,10,10))
output = net.conv1(input)
print(input.shape)
print(output.shape)

初始结果:

torch.Size([16, 3, 10, 10])
torch.Size([16, 6, 6, 6])

第一个维度上batch,第二个是通道channel,第三个和第四个是图片(特征图)的尺寸。

\frac{10+2\times 0-5}{1}+1=6

算出来的结果没毛病。

【问题2:这个卷积层中有多少的参数?】输入通道是3通道的,输出是6通道的,卷积核是

5\times 5

的,所以理解为6个

3\times 5\times 5

的卷积核,所以不考虑bias的话,参数量是

3\times 5\times 5\times 6=450

,考虑bais的话,就每一个卷积核再增加一个偏置值。(这是一个一般人会忽略的知识点欸)

下面用代码来验证:

net = Net()
net.initialize_weights()
for m in net.modules():
    if isinstance(m,nn.Conv2d):
        print(m)
        print(m.weight.shape)
        print(m.bias.shape)
        break

输出结果是:

Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))
torch.Size([6, 3, 5, 5])
torch.Size([6])

都和预料中一样。

- END -

本文分享自微信公众号 - 机器学习炼丹术(liandanshu),作者:机器学习炼丹术

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-08-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 小白学PyTorch | 6 模型的构建访问遍历存储(附代码)

    torch.nn.Module是所有网络的基类,在PyTorch实现模型的类中都要继承这个类(这个在之前的课程中已经提到)。在构建Module中,Module是...

    机器学习炼丹术
  • 小白学PyTorch | 12 SENet详解及PyTorch实现

    上一节课讲解了MobileNet的一个DSC深度可分离卷积的概念,希望大家可以在实际的任务中使用这种方法,现在再来介绍EfficientNet的另外一个基础知识...

    机器学习炼丹术
  • 五分钟了解:端侧神经网络GhostNet(2019)

    GhostNet是华为诺亚方舟实验室提出的一个新型神经网络结构。目的类似Google提出的MobileNet,都是为了硬件、移动端设计的轻小网络,但是效果相比M...

    机器学习炼丹术
  • Github项目推荐 | PyTorch代码规范最佳实践和样式指南

    AI 科技评论按,本文不是 Python 的官方风格指南。本文总结了使用 PyTorch 框架进行深入学习的一年多经验中的最佳实践。本文分享的知识主要是以研究的...

    AI科技评论
  • 【动手学深度学习笔记】之构造MLP模型的几种方法

    Module类是nn模块里提供的一个模型构造类,通过继承Module实现MLP的程序如下

    树枝990
  • PyTorch最佳实践,怎样才能写出一手风格优美的代码

    虽然这是一个非官方的 PyTorch 指南,但本文总结了一年多使用 PyTorch 框架的经验,尤其是用它开发深度学习相关工作的最优解决方案。请注意,我们分享的...

    机器之心
  • 私有云架构建设, 你做好准备了吗?

    私有云基础架构的构成要素 随着越来越多的企业设定了构建内部云服务的目标,规划和构建企业内部云服务平台就成为IT部门的职责。每个企业都有自己特有的环境和具体的目标...

    静一
  • Pytorch中Module,Parameter和Buffer的区别

    通过上面的例子可以看到,nn.parameter.Paramter的requires_grad属性值默认为True。另外上面例子给出了三种读取parameter...

    marsggbo
  • 盘点与警示:2017年加密货币市场的重大事故

    在2017年的最后一天,细数2017年发生在加密货币市场的一些重大事故,这或在2018年加密货币的交易中予以我们一些警示。 CoinDash的ICO黑客事件 支...

    企鹅号小编
  • 2019-08-01easyMock 的使用

    用户4344670

扫码关注云+社区

领取腾讯云代金券