zoukankan      html  css  js  c++  java
  • pytorch(三)建立模型(网络)

    前置知识:OOP-面向对象编程(核心:继承和多态)

    我们知道,程序分为代码code和数据data两部分,而类完成了这两部分的封装。

    面向对象的代表就是类,类的属性attribute即是封装的数据data,类的方法method即是封装的代码code.

    神经网络中每一层就是面向对象编程的最好代表:层既涉及有数据(属性)的权重,又有变换操作的代码(方法)。

    (类实例化就会自动调用构造函数,但是类的方法要调用方法才会执行。)

    所有特殊的面向对象编程的python方法名称前后都有“__”。

    • __init__: 对象属性初始化
    • __repr__: 对象的字符串表达(print(object_name)输出的结果)
    • __call__: 调用类实例就会自动调用的特殊方法,很有用,pytorch在__call__方法下实现了self.forward的调用,pytorch的模型(包括layer和network)的实现都利用了__call__方法.

    简而言之,建立模型只需3步:

    1. extend the nn.Module base class-----nn.Module
    2. define layers as class attributes-----torch.nn
    3. implement forward method------nn.functional
    class Network(nn.Module):#1.扩展基类。继承基类的格式-----跟一个带基类名称的括号   
        def __init__(self):#2.在初始化函数里,通过torch.nn定义类的layer属性
            super().__init__()
    #这个super().__init__()是指调用父类的构造函数,其最大的优势是在多继承的时候。会逐级的遍历调用父类的构造函数,如果是多个父类继承自一个爷爷类,那么只会调用一次爷爷类的构造函数。
    #如果不用super(),而是用显示的写法去调用父类的构造函数,在多继承时会重复写多次,每个级别每个父类都要写一行代码,如base.__init__()
            self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5)
            self.conv2 = nn.Conv2d(in_channels=6, out_channels=12, kernel_size=5)
            
            self.fc1 = nn.Linear(in_features=12 * 4 * 4, out_features=120)
            self.fc2 = nn.Linear(in_features=120, out_features=60)
            self.out = nn.Linear(in_features=60, out_features=10)
            
        def forward(self, t):#3.通过属性配合nn.functional--API实现forward方法
            # implement the forward pass
            return t
    

    CNN的Layer

    每个layer都封装了两部分东西:

    • layer前向传播函数的定义
    • layer上附着的权重tensor

    nn.Module类实现了对每个layer上tensor的追踪,因此只要我们是扩展继承nn.Module类得到的模型,那么我们给模型分配的属性layer,模型都会将其上tensor注册为可学习参数,并且追踪。

    CNN中的parameter

    Parameter:就是占位符,在训练的时候会有数值(argument)传入.
    Hyperparameters are parameters whose values are chosen manually and arbitrarily.
    超参又分为数据依赖型超参和独立超参:

    • 第一个卷积层的input channel
    • 最后一个全连接层的输出长度

    实例化之前定义的Network

    network = Network()
    print(network)
    """
    #这里就是nn.module类通过 **覆写** 基类(python的对象)的__repr__方法实现的,字符表达函数。
    #覆写: 子类觉得基类的一个方法不够满足要求,去重新定义一个同名的方法。
    Network(  
        (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
        (conv2): Conv2d(6, 12, kernel_size=(5, 5), stride=(1, 1))
        (fc1): Linear(in_features=192, out_features=120, bias=True)
        (fc2): Linear(in_features=120, out_features=60, bias=True)
        (out): Linear(in_features=60, out_features=10, bias=True)
    )
    """
    

    访问网络中的layer

    network.conv1#通常用 点符号 来打开一个对象,获取内部的信息。
    network.fc1
    

    访问网络中的权重weights

    network.conv1.weight
    Parameter containing:#这里是如何实现的??? parameter类是继承的pytorch的tensor类,nn,module会寻找追踪所有的parameter实例。而nn.module类的属性如con2d和linear,weight tensor和bias tensor都是parameter类的实例,tensor只是他们的父类。
    tensor([[[[ 0.0692,  0.1029, -0.1793,  0.0495,  0.0619],
                [ 0.1860,  0.0503, -0.1270, -0.1240, -0.0872],
                [-0.1924, -0.0684, -0.0028,  0.1031, -0.1053],
                [-0.0607,  0.1332,  0.0191,  0.1069, -0.0977],
                [ 0.0095, -0.1570,  0.1730,  0.0674, -0.1589]]],
    
    
            [[[-0.1392,  0.1141, -0.0658,  0.1015,  0.0060],
                [-0.0519,  0.0341,  0.1161,  0.1492, -0.0370],
                [ 0.1077,  0.1146,  0.0707,  0.0927,  0.0192],
                [-0.0656,  0.0929, -0.1735,  0.1019, -0.0546],
                [ 0.0647, -0.0521, -0.0687,  0.1053, -0.0613]]],
    
    
            [[[-0.1066, -0.0885,  0.1483, -0.0563,  0.0517],
                [ 0.0266,  0.0752, -0.1901, -0.0931, -0.0657],
                [ 0.0502, -0.0652,  0.0523, -0.0789, -0.0471],
                [-0.0800,  0.1297, -0.0205,  0.0450, -0.1029],
                [-0.1542,  0.1634, -0.0448,  0.0998, -0.1385]]],
    
    
            [[[-0.0943,  0.0256,  0.1632, -0.0361, -0.0557],
                [ 0.1083, -0.1647,  0.0846, -0.0163,  0.0068],
                [-0.1241,  0.1761,  0.1914,  0.1492,  0.1270],
                [ 0.1583,  0.0905,  0.1406,  0.1439,  0.1804],
                [-0.1651,  0.1374,  0.0018,  0.0846, -0.1203]]],
    
    
            [[[ 0.1786, -0.0800, -0.0995,  0.1690, -0.0529],
                [ 0.0685,  0.1399,  0.0270,  0.1684,  0.1544],
                [ 0.1581, -0.0099, -0.0796,  0.0823, -0.1598],
                [ 0.1534, -0.1373, -0.0740, -0.0897,  0.1325],
                [ 0.1487, -0.0583, -0.0900,  0.1606,  0.0140]]],
    
    
            [[[ 0.0919,  0.0575,  0.0830, -0.1042, -0.1347],
                [-0.1615,  0.0451,  0.1563, -0.0577, -0.1096],
                [-0.0667, -0.1979,  0.0458,  0.1971, -0.1380],
                [-0.1279,  0.1753, -0.1063,  0.1230, -0.0475],
                [-0.0608, -0.0046, -0.0043, -0.1543,  0.1919]]]], 
                requires_grad=True
    )
    
    #可以看到pytorch源码中parameter类对__repr__的重写
    def __repr__(self):
        return 'Parameter containing:
    ' + super(Parameter, self).__repr__() #这里调用了parameter所有父类的__repr__方法。
    

    权重tensor的shape

    network.conv1.weight.shape
    torch.Size([6, 1, 5, 5])  #依次是out_channels(number of filters),input_channels(其实就是本层卷积核的深度depth),filter-heigth,filter-width
    

    Matrix Muliplication


    w * x = y

    以第一线性层为例,线性层w是一个矩阵,其行数就是输入x被(一个三维的空间基向量,w每行就是一个基向量)线性映射后降低的维数。一个4维的vec经线性映射后被降到3维。

    获取网络中的参数

    
    #method 1 
    for param in network.parameters():
        print(param.shape)
    #output:
    torch.Size([6, 1, 5, 5])
    torch.Size([6])
    torch.Size([12, 6, 5, 5])
    torch.Size([12])
    torch.Size([120, 192])
    torch.Size([120])
    torch.Size([60, 120])
    torch.Size([60])
    torch.Size([10, 60])
    torch.Size([10])
    
    # method2
    for name, param in network.named_parameters():
        print(name, '		', param.shape)
    
    conv1.weight 		 torch.Size([6, 1, 5, 5])
    conv1.bias 		 torch.Size([6])
    conv2.weight 		 torch.Size([12, 6, 5, 5])
    conv2.bias 		 torch.Size([12])
    fc1.weight 		 torch.Size([120, 192])
    fc1.bias 		 torch.Size([120])
    fc2.weight 		 torch.Size([60, 120])
    fc2.bias 		 torch.Size([60])
    out.weight 		 torch.Size([10, 60])
    out.bias 		 torch.Size([10])
    

    callable的layer和network

    都是通过覆写了python面向对象的__call__方法实现。
    比如nn.module以及所有的layer(如conv,linear)中的__call__方法调用了self.forward(),但是并没有实现这个方法,是个抽象类。
    因此我们继承nn.module需要实现这个forward方法,继而实现了调用网络实例(Network,而不是Network.forward())或者layer 类实例(self.conv(tensor),而不是self.conv.forward(tensor))就可以直接前向传播的效果。

    测试时关闭梯度跟踪

    torch.set_grad_enabled(False) #pytorch是动态图,关闭梯度跟踪可以减少内存花费
    

    单个batch数据的前向传播过程

    """
    按传统的多层神经网络来看,输入层就是1张图片的像素,输出层是1张图片的类别预测向量。
    一个batch的完成实际是被分成多层神经网络并行化执行batch次完成的。
    """
    data_loader = torch.utils.data.DataLoader(
         train_set, batch_size=10)
    
    batch = next(iter(data_loader))
    images, labels = batch
    
    images.shape
    #torch.Size([10, 1, 28, 28])
    
    labels.shape
    #torch.Size([10])
    
    preds = network(images)
    preds.shape
    #torch.Size([10, 10])
    
    preds
    #tensor(
        [
            [ 0.1072, -0.1255, -0.0782, -0.1073,  0.1048,  0.1142, -0.0804, -0.0087,  0.0082,  0.0180],
            [ 0.1070, -0.1233, -0.0798, -0.1060,  0.1065,  0.1163, -0.0689, -0.0142,  0.0085,  0.0134],
            [ 0.0985, -0.1287, -0.0979, -0.1001,  0.1092,  0.1129, -0.0605, -0.0248,  0.0290,  0.0066],
            [ 0.0989, -0.1295, -0.0944, -0.1054,  0.1071,  0.1146, -0.0596, -0.0249,  0.0273,  0.0059],
            [ 0.1004, -0.1273, -0.0843, -0.1127,  0.1072,  0.1183, -0.0670, -0.0162,  0.0129,  0.0101],
            [ 0.1036, -0.1245, -0.0842, -0.1047,  0.1097,  0.1176, -0.0682, -0.0126,  0.0128,  0.0147],
            [ 0.1093, -0.1292, -0.0961, -0.1006,  0.1106,  0.1096, -0.0633, -0.0163,  0.0215,  0.0046],
            [ 0.1026, -0.1204, -0.0799, -0.1060,  0.1077,  0.1207, -0.0741, -0.0124,  0.0098,  0.0202],
            [ 0.0991, -0.1275, -0.0911, -0.0980,  0.1109,  0.1134, -0.0625, -0.0391,  0.0318,  0.0104],
            [ 0.1007, -0.1212, -0.0918, -0.0962,  0.1168,  0.1105, -0.0719, -0.0265,  0.0207,  0.0157]
        ]
    )
    
    preds.argmax(dim = 1)
    # tensor([5, 5, 5, 5, 5, 5, 4, 5, 5, 4])
    
    labels
    # tensor([9, 0, 0, 3, 0, 2, 7, 2, 5, 5])
    
    #计算batch内预测标签正确的个数
    pres.argmax(dim = 1).eq(labels)
    # tensor([0, 0, 0, 0, 0, 0, 0, 0, 1, 0], dtype=torch.uint8)
    
    pres.argmax(dim = 1).eq(labels).sum()
    # tensor(1.)
    
    pres.argmax(dim = 1).eq(labels).sum().item()   #get_number_correct
    # 1
    
  • 相关阅读:
    Cisco 交换机配置的基本命令
    Mysql读写分离方案-Amoeba环境部署记录
    centos7下部署zabbix3.4+grafana
    Docker
    Linux 安装源码软件
    mysql 日志
    mysql导出导入数据
    mysql 数据库的备份和还原
    Mysql 数据库管理
    英语单词
  • 原文地址:https://www.cnblogs.com/Henry-ZHAO/p/13070799.html
Copyright © 2011-2022 走看看