• pytorch深度学习实战lesson16


       第十六课 神经网络基础

            本节课主要是从实践代码的角度看神经网络的各个结构,以及各个结构的实现方法。虽然没有太多理论,但是精华都在代码的注释中~

    目录

    模型构造(层和块)

    参数管理

    自定义层

    读写文件


    模型构造(层和块)

    1. #层和块
    2. #首先,我们回顾一下多层感知机
    3. import torch
    4. from torch import nn
    5. from torch.nn import functional as F#一些函数
    6. #单层神经网络=线性层+relu+线性层
    7. net = nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
    8. #2*20的随机矩阵作为输入,2是批量大小,20是输入维度
    9. X = torch.rand(2, 20)
    10. print(net(X))
    11. print("###########################################################################")
    12. #自定义块
    13. #module是pytorch中很重要的概念
    14. #nn.Sequential定义了一种特殊的Module,任何一个层和一个神经网络都是module的一个子类
    15. #比如下面的MLP就是nn.module的一个子类
    16. #module有两个比较重要的函数,一个是__init__,可以在里面定义我们所需要的类和参数;
    17. class MLP(nn.Module):
    18. def __init__(self):
    19. super().__init__()#调用父类,以设好所需要的内部参数。方便初始化权重之类的参数
    20. self.hidden = nn.Linear(20, 256)#隐藏层,输入维度20,输出维度256.将其存入类的成员变量里。
    21. self.out = nn.Linear(256, 10)#输出层,输入维度100,输出10,也藏入了类成员变量里。
    22. def forward(self, X):#定义前向函数
    23. return self.out(F.relu(self.hidden(X)))#很清晰,先将输入放隐藏层里,再通过激活函数输出。
    24. #实例化多层感知机的层,然后在每次调用正向传播函数时调用这些层
    25. net = MLP()
    26. print(net(X))#这个就是输出
    27. print("###########################################################################")
    28. #顺序块,与nn.sequential的效果一样
    29. class MySequential(nn.Module):
    30. def __init__(self, *args):#*args是收集参数,相当于把若干个参数打包成一个来传入
    31. super().__init__()#调用父类的初始化函数
    32. for block in args:
    33. self._modules[block] = block#定义一个专门存放神经网络层的容器,并把层自己作为key,每一层是按先后顺序存入这个容器的。
    34. def forward(self, X):#前向函数
    35. #关于为啥是字典形式;因为self._moudles是父类的属性,这个属性类型是OrderedDict()
    36. #有序字典,这样添加层是将你的层嵌入到模型中,这也是为什么此处并没有重写forward函数
    37. for block in self._modules.values():#调用容器中的每一层
    38. X = block(X)#最后返回X
    39. return X
    40. #下面使用的linear层、relu层还有最后的linear层的时候先放进init的args参数里面,再按顺序放入_modules里面
    41. net = MySequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
    42. print(net(X))
    43. print("###########################################################################")
    44. #在正向传播函数中执行代码
    45. #当sequential这个类不能满足需求时,自己创建的好处是在init和forward里面可以做大量的自定义的计算
    46. #fixedhiddenmlp这个子类其实是个例子,并没什么特殊的意义,
    47. #它表明了可以做较灵活的方法——继承nn.module这个父类去灵活调用参数的样子,以及前向计算的方法。
    48. class FixedHiddenMLP(nn.Module):
    49. def __init__(self):
    50. super().__init__()
    51. #随机生成一个不参与训练的20*20的rand_weight,它不会计算梯度。
    52. self.rand_weight = torch.rand((20, 20), requires_grad=False)
    53. self.linear = nn.Linear(20, 20)#常规的线性层
    54. #返回的是一个标量
    55. def forward(self, X):
    56. X = self.linear(X)
    57. X = F.relu(torch.mm(X, self.rand_weight) + 1)
    58. X = self.linear(X)
    59. while X.abs().sum() > 1:
    60. X /= 2
    61. return X.sum()
    62. net = FixedHiddenMLP()
    63. print(net(X))
    64. print("###########################################################################")
    65. #混合搭配各种组合块的方法
    66. #可以嵌套nn.module中的子类
    67. class NestMLP(nn.Module):
    68. def __init__(self):
    69. super().__init__()
    70. self.net = nn.Sequential(nn.Linear(20, 64), nn.ReLU(),
    71. nn.Linear(64, 32), nn.ReLU())
    72. self.linear = nn.Linear(32, 16)
    73. def forward(self, X):
    74. return self.linear(self.net(X))
    75. #对于sequential来说它的输入可以是任何nn.module的子类
    76. chimera = nn.Sequential(NestMLP(), nn.Linear(16, 20), FixedHiddenMLP())
    77. print(chimera(X))
    78. print("###########################################################################")

    输出:

    tensor([[ 0.1749, -0.3245,  0.1620,  0.1845, -0.0466,  0.1724,  0.0984,  0.1156,
             -0.2163,  0.0740],
            [ 0.1999, -0.2860,  0.0773,  0.2436,  0.0061,  0.1541,  0.1506,  0.0610,
             -0.2478,  0.0616]], grad_fn=)
    ###########################################################################
    tensor([[-0.0035,  0.1688, -0.3114,  0.1776, -0.0458,  0.2133, -0.1536,  0.1132,
             -0.0146, -0.0474],
            [ 0.0534,  0.1533, -0.2868,  0.2102,  0.0708,  0.0851, -0.0283,  0.1604,
             -0.0227,  0.0477]], grad_fn=)
    ###########################################################################
    tensor([[-0.2692, -0.0503, -0.1107, -0.3261,  0.0452, -0.2579,  0.0814,  0.1218,
              0.3661, -0.0557],
            [-0.2674, -0.2123,  0.0190, -0.2354,  0.0484, -0.2457,  0.1021,  0.1995,
              0.1764, -0.0074]], grad_fn=)
    ###########################################################################
    tensor(0.0249, grad_fn=)
    ###########################################################################
    tensor(0.1072, grad_fn=)
    ###########################################################################

    进程已结束,退出代码0

    参数管理

    1. #参数管理
    2. #我们首先关注具有单隐藏层的多层感知机
    3. import torch
    4. from torch import nn
    5. ##################### net【0】 ######## net【1】 ####### net【2】
    6. net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1))
    7. X = torch.rand(size=(2, 4))
    8. print(net(X))
    9. print("###########################################################################")
    10. #参数访问
    11. #把每一层的权重拿出
    12. print(net[2].state_dict())#把最后的线性层的权重和偏重(状态)拿出来
    13. print("###########################################################################")
    14. #目标参数
    15. print(type(net[2].bias))#最后一层的偏移的类型是可以优化的参数parameter
    16. print(net[2].bias)#最后一层的偏移
    17. print(net[2].bias.data)#用.data真正访问它的值,而不访问梯度
    18. print("###########################################################################")
    19. print(net[2].weight.grad == None)#.grad是访问它的梯度,因为还没有进行反向计算,所有这个输出TRUE
    20. print("###########################################################################")
    21. #一次性访问所有参数
    22. #使用named_parameters()函数访问第一层的所有参数,但这里我们要打印的是参数的名字和形状
    23. print(*[(name, param.shape) for name, param in net[0].named_parameters()])
    24. #使用named_parameters()函数访问所有参数,但这里我们要打印的是参数的名字和形状
    25. print(*[(name, param.shape) for name, param in net.named_parameters()])
    26. print("###########################################################################")
    27. print(net.state_dict()['2.bias'].data)#通过名字获取参数,“2.bias”表示最后一个的偏移
    28. print("###########################################################################")
    29. #从嵌套块收集参数
    30. #看看有嵌套的网络的情况
    31. def block1():
    32. return nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 4),nn.ReLU())
    33. def block2():#block2本身也是线性层,但是他插入了4个block1
    34. net = nn.Sequential()
    35. for i in range(4):
    36. # 前面的“f'block {i}'”可以把“2.bias”中的2替换成“block2”
    37. # 然后block2嵌套了4个block1
    38. net.add_module(f'block {i}', block1())
    39. return net
    40. rgnet = nn.Sequential(block2(), nn.Linear(4, 1))#它应当包含4个block1和一个linear层
    41. print(rgnet(X))#此处返回一个标量
    42. print("###########################################################################")
    43. #我们已经设计了网络,让我们看看它是如何组织的
    44. print(rgnet)
    45. print("###########################################################################")
    46. #print(rgnet[0][1][0].bias.data)
    47. #print("###########################################################################")
    48. #内置初始化
    49. def init_normal(m):
    50. if type(m) == nn.Linear:#如果传入的module是个线性类(全连接层的话)的话
    51. #下面的下划线表示这里的normal函数是个替换函数,它不会返回值,只是说把module的权重给替换掉
    52. nn.init.normal_(m.weight, mean=0, std=0.01)#就对它的权重做均值为0方差为0.01的初始化
    53. nn.init.zeros_(m.bias)#偏移置零
    54. net.apply(init_normal)#对于所有net里的层进行遍历,然后传入到init_normal函数里
    55. print(net[0].weight.data[0], net[0].bias.data[0])#输出正态分布后的参数
    56. print("###########################################################################")
    57. def init_constant(m):#和上面init_normal对比
    58. if type(m) == nn.Linear:
    59. nn.init.constant_(m.weight, 1)#这里和上面的区别就是权重被替换成1了
    60. nn.init.zeros_(m.bias)
    61. net.apply(init_constant)
    62. print(net[0].weight.data[0], net[0].bias.data[0])
    63. print("###########################################################################")
    64. #对某些块应用不同的初始化方法
    65. def xavier(m):
    66. if type(m) == nn.Linear:
    67. nn.init.xavier_uniform_(m.weight)#对权重做Xavier初始化
    68. def init_42(m):
    69. if type(m) == nn.Linear:
    70. nn.init.constant_(m.weight, 42)#把权重置为42
    71. net[0].apply(xavier)#第一个线性层用x初始化
    72. net[2].apply(init_42)#最后一个用“宇宙的答案”初始化
    73. print(net[0].weight.data[0])#data[0]表示权重的第0行
    74. print(net[2].weight.data)
    75. print("###########################################################################")
    76. #自定义初始化
    77. def my_init(m):
    78. if type(m) == nn.Linear:
    79. print(
    80. "Init",
    81. *[(name, param.shape) for name, param in m.named_parameters()][0])#输出权重的名字和尺寸
    82. nn.init.uniform_(m.weight, -10, 10)#把权重替换成-10到10之间的数
    83. m.weight.data *= m.weight.data.abs() >= 5#保留绝对值大于等于5的权重,小于5的权重设为0
    84. net.apply(my_init)
    85. print(net[0].weight[:2])
    86. print("###########################################################################")
    87. #更暴力的方法
    88. net[0].weight.data[:] += 1#把第一层线性层的权重全部加1
    89. net[0].weight.data[0, 0] = 42#把第一层权重的第一个值变为42
    90. print(net[0].weight.data[0])#把替换后的第一行输出
    91. print("###########################################################################")
    92. #参数绑定(共享权重)
    93. shared = nn.Linear(8, 8)
    94. net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), shared, nn.ReLU(), shared,
    95. nn.ReLU(), nn.Linear(8, 1))#意思是第二个和第三个隐藏层的权重是一样的
    96. net(X)
    97. print(net[2].weight.data[0] == net[4].weight.data[0])
    98. net[2].weight.data[0, 0] = 100#如果我把第二个层的权重改成100的话
    99. print(net[2].weight.data[0] == net[4].weight.data[0])#输出后发现第三个层的权重也改成100了
    100. print("###########################################################################")

    输出:

    tensor([[-0.1347],
            [ 0.0572]], grad_fn=)
    ###########################################################################
    OrderedDict([('weight', tensor([[-0.1971,  0.2016, -0.2314,  0.0127,  0.0735,  0.1721, -0.1707, -0.1857]])), ('bias', tensor([0.3370]))])
    ###########################################################################

    Parameter containing:
    tensor([0.3370], requires_grad=True)
    tensor([0.3370])
    ###########################################################################
    True
    ###########################################################################
    ('weight', torch.Size([8, 4])) ('bias', torch.Size([8]))
    ('0.weight', torch.Size([8, 4])) ('0.bias', torch.Size([8])) ('2.weight', torch.Size([1, 8])) ('2.bias', torch.Size([1]))
    ###########################################################################
    tensor([0.3370])
    ###########################################################################
    tensor([[-0.0955],
            [-0.0955]], grad_fn=)
    ###########################################################################
    Sequential(
      (0): Sequential(
        (block 0): Sequential(
          (0): Linear(in_features=4, out_features=8, bias=True)
          (1): ReLU()
          (2): Linear(in_features=8, out_features=4, bias=True)
          (3): ReLU()
        )
        (block 1): Sequential(
          (0): Linear(in_features=4, out_features=8, bias=True)
          (1): ReLU()
          (2): Linear(in_features=8, out_features=4, bias=True)
          (3): ReLU()
        )
        (block 2): Sequential(
          (0): Linear(in_features=4, out_features=8, bias=True)
          (1): ReLU()
          (2): Linear(in_features=8, out_features=4, bias=True)
          (3): ReLU()
        )
        (block 3): Sequential(
          (0): Linear(in_features=4, out_features=8, bias=True)
          (1): ReLU()
          (2): Linear(in_features=8, out_features=4, bias=True)
          (3): ReLU()
        )
      )
      (1): Linear(in_features=4, out_features=1, bias=True)
    )
    ###########################################################################
    tensor([ 0.1287,  0.4435,  0.3574,  0.2990, -0.1065, -0.0520, -0.3925,  0.4156])
    ###########################################################################
    tensor([-0.0094, -0.0043, -0.0081,  0.0036]) tensor(0.)
    ###########################################################################
    tensor([1., 1., 1., 1.]) tensor(0.)
    ###########################################################################
    tensor([ 0.5943, -0.3961,  0.3483,  0.5476])
    tensor([[42., 42., 42., 42., 42., 42., 42., 42.]])
    ###########################################################################
    Init weight torch.Size([8, 4])
    Init weight torch.Size([1, 8])
    tensor([[-0.0000,  6.6172, -9.3483,  6.1634],
            [ 7.6437,  0.0000, -0.0000, -0.0000]], grad_fn=)
    ###########################################################################
    tensor([42.0000,  7.6172, -8.3483,  7.1634])
    ###########################################################################
    tensor([True, True, True, True, True, True, True, True])
    tensor([True, True, True, True, True, True, True, True])
    ###########################################################################

    自定义层

    1. #自定义层
    2. #构造一个没有任何参数的自定义层
    3. import torch
    4. import torch.nn.functional as F
    5. from torch import nn
    6. class CenteredLayer(nn.Module):
    7. def __init__(self):
    8. super().__init__()
    9. def forward(self, X):
    10. return X - X.mean()#输入减均值,把生成的张量均值变成0
    11. layer = CenteredLayer()
    12. print(layer(torch.FloatTensor([1, 2, 3, 4, 5])))
    13. #将层作为组件合并到构建更复杂的模型中
    14. net = nn.Sequential(nn.Linear(8, 128), CenteredLayer())#由一个线性层(输入八组拥有128个特征的输入)和一个centeredlayer组成
    15. Y = net(torch.rand(4, 8))#生成4组形状和输入一样的矩阵。它是【0,1)之间的均匀分布。
    16. #randn是返回一个包含从标准正态分布中抽取的随机数张量。
    17. #print(Y)
    18. print(Y.mean())#计算会有些误差
    19. #带参数的层
    20. class MyLinear(nn.Module):
    21. def __init__(self, in_units, units):
    22. super().__init__()
    23. #torch.randn(in_units, units)表示输出一个“输入乘输出大小”的0-1之间随机分布的矩阵,
    24. # 然后把它放入nn.parameter中后
    25. #会把梯度和名字加上
    26. self.weight = nn.Parameter(torch.randn(in_units, units))#调用parameter类就能弄参数。
    27. self.bias = nn.Parameter(torch.randn(units,))
    28. def forward(self, X):
    29. linear = torch.matmul(X, self.weight.data) + self.bias.data
    30. return F.relu(linear)
    31. linear = MyLinear(5, 3)#输入是5组,输出是每组3个,也得输出5组
    32. print(linear.weight)
    33. #使用自定义层直接执行正向传播计算
    34. print(linear(torch.rand(2, 5)))
    35. #使用自定义层构建模型
    36. net = nn.Sequential(MyLinear(64, 8), MyLinear(8, 1))
    37. print(net(torch.rand(2, 64)))#rand的第二个参数要和第一层的输入一样,也就是和“MyLinear(64, 8)”的64一样

    输出:

    tensor([-2., -1.,  0.,  1.,  2.])
    tensor(0., grad_fn=)
    Parameter containing:
    tensor([[-1.3415,  1.3760,  1.4122],
            [ 0.1237,  0.4629,  1.0791],
            [ 0.8389,  1.2107,  0.2606],
            [-0.2440,  0.9892, -0.5109],
            [-2.2064,  0.8119, -0.0849]], requires_grad=True)
    tensor([[0.0000, 2.6836, 0.5100],
            [0.0000, 3.4144, 0.5948]])
    tensor([[20.3297],
            [10.0761]])

    读写文件

    1. #读写文件(训练好的东西如何存下来)
    2. #加载和保存张量(矩阵)--------------------矩阵
    3. import torch
    4. from torch import nn
    5. from torch.nn import functional as F
    6. x = torch.arange(4)
    7. torch.save(x, 'x-file')
    8. x2 = torch.load('x-file')
    9. print(x2)
    10. print("###########################################################################")
    11. #存储一个张量列表,然后把它们读回内存---------列表
    12. y = torch.zeros(4)
    13. torch.save([x, y], 'x-files')
    14. x2, y2 = torch.load('x-files')
    15. print((x2, y2))
    16. print("###########################################################################")
    17. #写入或读取从字符串映射到张量的字典-----------字典
    18. mydict = {'x': x, 'y': y}
    19. torch.save(mydict, 'mydict')
    20. mydict2 = torch.load('mydict')
    21. print(mydict2)
    22. print("###########################################################################")
    23. #加载和保存模型参数
    24. class MLP(nn.Module):
    25. def __init__(self):
    26. super().__init__()
    27. self.hidden = nn.Linear(20, 256)
    28. self.output = nn.Linear(256, 10)
    29. def forward(self, x):
    30. return self.output(F.relu(self.hidden(x)))
    31. net = MLP()
    32. X = torch.randn(size=(2, 20))
    33. Y = net(X)
    34. #将模型的参数存储为一个叫做“mlp.params”的文件
    35. torch.save(net.state_dict(), 'mlp.params')#把mlp的所有参数存成一个字典
    36. #实例化了原始多层感知机模型的一个备份。 直接读取文件中存储的参数
    37. clone = MLP()#在进行load参数之前,需要先对网络进行定义,不然没有load的对象。
    38. clone.load_state_dict(torch.load('mlp.params'))#定义之后把所有参数加载到mlp中
    39. print(clone.eval())#看一下这个网络
    40. Y_clone = clone(X)
    41. print(Y_clone == Y)
    42. print("###########################################################################")

    输出:

    tensor([0, 1, 2, 3])
    ###########################################################################
    (tensor([0, 1, 2, 3]), tensor([0., 0., 0., 0.]))
    ###########################################################################
    {'x': tensor([0, 1, 2, 3]), 'y': tensor([0., 0., 0., 0.])}
    ###########################################################################
    MLP(
      (hidden): Linear(in_features=20, out_features=256, bias=True)
      (output): Linear(in_features=256, out_features=10, bias=True)
    )
    tensor([[True, True, True, True, True, True, True, True, True, True],
            [True, True, True, True, True, True, True, True, True, True]])
    ###########################################################################

  • 相关阅读:
    【大数据】NiFi 中的处理器(一):GenerateTableFetch
    飞天+CIPU体为元宇宙带来更大想象空间
    Vue--注册组件的方法
    大数据培训—DolphinSchedular(一)
    会话边界控制器(SBC)
    用Python生成Hilbert矩阵
    C++11特性-自动类型推导
    JSCORE day_02(7.1)
    电脑监控系统,实现可视化管理!
    玩转Linux与运维岗(36)
  • 原文地址:https://blog.csdn.net/weixin_48304306/article/details/127885769