本节课主要是从实践代码的角度看神经网络的各个结构,以及各个结构的实现方法。虽然没有太多理论,但是精华都在代码的注释中~
目录
- #层和块
- #首先,我们回顾一下多层感知机
- import torch
- from torch import nn
- from torch.nn import functional as F#一些函数
- #单层神经网络=线性层+relu+线性层
- net = nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
- #2*20的随机矩阵作为输入,2是批量大小,20是输入维度
- X = torch.rand(2, 20)
- print(net(X))
- print("###########################################################################")
- #自定义块
- #module是pytorch中很重要的概念
- #nn.Sequential定义了一种特殊的Module,任何一个层和一个神经网络都是module的一个子类
- #比如下面的MLP就是nn.module的一个子类
- #module有两个比较重要的函数,一个是__init__,可以在里面定义我们所需要的类和参数;
- class MLP(nn.Module):
- def __init__(self):
- super().__init__()#调用父类,以设好所需要的内部参数。方便初始化权重之类的参数
- self.hidden = nn.Linear(20, 256)#隐藏层,输入维度20,输出维度256.将其存入类的成员变量里。
- self.out = nn.Linear(256, 10)#输出层,输入维度100,输出10,也藏入了类成员变量里。
- def forward(self, X):#定义前向函数
- return self.out(F.relu(self.hidden(X)))#很清晰,先将输入放隐藏层里,再通过激活函数输出。
- #实例化多层感知机的层,然后在每次调用正向传播函数时调用这些层
- net = MLP()
- print(net(X))#这个就是输出
- print("###########################################################################")
- #顺序块,与nn.sequential的效果一样
- class MySequential(nn.Module):
- def __init__(self, *args):#*args是收集参数,相当于把若干个参数打包成一个来传入
- super().__init__()#调用父类的初始化函数
- for block in args:
- self._modules[block] = block#定义一个专门存放神经网络层的容器,并把层自己作为key,每一层是按先后顺序存入这个容器的。
-
- def forward(self, X):#前向函数
- #关于为啥是字典形式;因为self._moudles是父类的属性,这个属性类型是OrderedDict()
- #有序字典,这样添加层是将你的层嵌入到模型中,这也是为什么此处并没有重写forward函数
- for block in self._modules.values():#调用容器中的每一层
- X = block(X)#最后返回X
- return X
- #下面使用的linear层、relu层还有最后的linear层的时候先放进init的args参数里面,再按顺序放入_modules里面
- net = MySequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
- print(net(X))
- print("###########################################################################")
- #在正向传播函数中执行代码
- #当sequential这个类不能满足需求时,自己创建的好处是在init和forward里面可以做大量的自定义的计算
- #fixedhiddenmlp这个子类其实是个例子,并没什么特殊的意义,
- #它表明了可以做较灵活的方法——继承nn.module这个父类去灵活调用参数的样子,以及前向计算的方法。
- class FixedHiddenMLP(nn.Module):
- def __init__(self):
- super().__init__()
- #随机生成一个不参与训练的20*20的rand_weight,它不会计算梯度。
- self.rand_weight = torch.rand((20, 20), requires_grad=False)
- self.linear = nn.Linear(20, 20)#常规的线性层
- #返回的是一个标量
- def forward(self, X):
- X = self.linear(X)
- X = F.relu(torch.mm(X, self.rand_weight) + 1)
- X = self.linear(X)
- while X.abs().sum() > 1:
- X /= 2
- return X.sum()
- net = FixedHiddenMLP()
- print(net(X))
- print("###########################################################################")
- #混合搭配各种组合块的方法
- #可以嵌套nn.module中的子类
- class NestMLP(nn.Module):
- def __init__(self):
- super().__init__()
- self.net = nn.Sequential(nn.Linear(20, 64), nn.ReLU(),
- nn.Linear(64, 32), nn.ReLU())
- self.linear = nn.Linear(32, 16)
- def forward(self, X):
- return self.linear(self.net(X))
- #对于sequential来说它的输入可以是任何nn.module的子类
- chimera = nn.Sequential(NestMLP(), nn.Linear(16, 20), FixedHiddenMLP())
- print(chimera(X))
- 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
- #参数管理
- #我们首先关注具有单隐藏层的多层感知机
- import torch
- from torch import nn
- ##################### net【0】 ######## net【1】 ####### net【2】
- net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1))
- X = torch.rand(size=(2, 4))
- print(net(X))
- print("###########################################################################")
- #参数访问
- #把每一层的权重拿出
- print(net[2].state_dict())#把最后的线性层的权重和偏重(状态)拿出来
- print("###########################################################################")
- #目标参数
- print(type(net[2].bias))#最后一层的偏移的类型是可以优化的参数parameter
- print(net[2].bias)#最后一层的偏移
- print(net[2].bias.data)#用.data真正访问它的值,而不访问梯度
- print("###########################################################################")
- print(net[2].weight.grad == None)#.grad是访问它的梯度,因为还没有进行反向计算,所有这个输出TRUE
- print("###########################################################################")
- #一次性访问所有参数
- #使用named_parameters()函数访问第一层的所有参数,但这里我们要打印的是参数的名字和形状
- print(*[(name, param.shape) for name, param in net[0].named_parameters()])
- #使用named_parameters()函数访问所有参数,但这里我们要打印的是参数的名字和形状
- print(*[(name, param.shape) for name, param in net.named_parameters()])
- print("###########################################################################")
- print(net.state_dict()['2.bias'].data)#通过名字获取参数,“2.bias”表示最后一个的偏移
- print("###########################################################################")
- #从嵌套块收集参数
- #看看有嵌套的网络的情况
- def block1():
- return nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 4),nn.ReLU())
- def block2():#block2本身也是线性层,但是他插入了4个block1
- net = nn.Sequential()
- for i in range(4):
- # 前面的“f'block {i}'”可以把“2.bias”中的2替换成“block2”
- # 然后block2嵌套了4个block1
- net.add_module(f'block {i}', block1())
- return net
- rgnet = nn.Sequential(block2(), nn.Linear(4, 1))#它应当包含4个block1和一个linear层
- print(rgnet(X))#此处返回一个标量
- print("###########################################################################")
- #我们已经设计了网络,让我们看看它是如何组织的
- print(rgnet)
- print("###########################################################################")
- #print(rgnet[0][1][0].bias.data)
- #print("###########################################################################")
- #内置初始化
- def init_normal(m):
- if type(m) == nn.Linear:#如果传入的module是个线性类(全连接层的话)的话
- #下面的下划线表示这里的normal函数是个替换函数,它不会返回值,只是说把module的权重给替换掉
- nn.init.normal_(m.weight, mean=0, std=0.01)#就对它的权重做均值为0方差为0.01的初始化
- nn.init.zeros_(m.bias)#偏移置零
- net.apply(init_normal)#对于所有net里的层进行遍历,然后传入到init_normal函数里
- print(net[0].weight.data[0], net[0].bias.data[0])#输出正态分布后的参数
- print("###########################################################################")
- def init_constant(m):#和上面init_normal对比
- if type(m) == nn.Linear:
- nn.init.constant_(m.weight, 1)#这里和上面的区别就是权重被替换成1了
- nn.init.zeros_(m.bias)
- net.apply(init_constant)
- print(net[0].weight.data[0], net[0].bias.data[0])
- print("###########################################################################")
- #对某些块应用不同的初始化方法
- def xavier(m):
- if type(m) == nn.Linear:
- nn.init.xavier_uniform_(m.weight)#对权重做Xavier初始化
- def init_42(m):
- if type(m) == nn.Linear:
- nn.init.constant_(m.weight, 42)#把权重置为42
- net[0].apply(xavier)#第一个线性层用x初始化
- net[2].apply(init_42)#最后一个用“宇宙的答案”初始化
- print(net[0].weight.data[0])#data[0]表示权重的第0行
- print(net[2].weight.data)
- print("###########################################################################")
- #自定义初始化
- def my_init(m):
- if type(m) == nn.Linear:
- print(
- "Init",
- *[(name, param.shape) for name, param in m.named_parameters()][0])#输出权重的名字和尺寸
- nn.init.uniform_(m.weight, -10, 10)#把权重替换成-10到10之间的数
- m.weight.data *= m.weight.data.abs() >= 5#保留绝对值大于等于5的权重,小于5的权重设为0
- net.apply(my_init)
- print(net[0].weight[:2])
- print("###########################################################################")
- #更暴力的方法
- net[0].weight.data[:] += 1#把第一层线性层的权重全部加1
- net[0].weight.data[0, 0] = 42#把第一层权重的第一个值变为42
- print(net[0].weight.data[0])#把替换后的第一行输出
- print("###########################################################################")
- #参数绑定(共享权重)
- shared = nn.Linear(8, 8)
- net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), shared, nn.ReLU(), shared,
- nn.ReLU(), nn.Linear(8, 1))#意思是第二个和第三个隐藏层的权重是一样的
- net(X)
- print(net[2].weight.data[0] == net[4].weight.data[0])
- net[2].weight.data[0, 0] = 100#如果我把第二个层的权重改成100的话
- print(net[2].weight.data[0] == net[4].weight.data[0])#输出后发现第三个层的权重也改成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])
###########################################################################
- #自定义层
- #构造一个没有任何参数的自定义层
- import torch
- import torch.nn.functional as F
- from torch import nn
- class CenteredLayer(nn.Module):
- def __init__(self):
- super().__init__()
- def forward(self, X):
- return X - X.mean()#输入减均值,把生成的张量均值变成0
- layer = CenteredLayer()
- print(layer(torch.FloatTensor([1, 2, 3, 4, 5])))
- #将层作为组件合并到构建更复杂的模型中
- net = nn.Sequential(nn.Linear(8, 128), CenteredLayer())#由一个线性层(输入八组拥有128个特征的输入)和一个centeredlayer组成
- Y = net(torch.rand(4, 8))#生成4组形状和输入一样的矩阵。它是【0,1)之间的均匀分布。
- #randn是返回一个包含从标准正态分布中抽取的随机数张量。
- #print(Y)
- print(Y.mean())#计算会有些误差
- #带参数的层
- class MyLinear(nn.Module):
- def __init__(self, in_units, units):
- super().__init__()
- #torch.randn(in_units, units)表示输出一个“输入乘输出大小”的0-1之间随机分布的矩阵,
- # 然后把它放入nn.parameter中后
- #会把梯度和名字加上
- self.weight = nn.Parameter(torch.randn(in_units, units))#调用parameter类就能弄参数。
- self.bias = nn.Parameter(torch.randn(units,))
- def forward(self, X):
- linear = torch.matmul(X, self.weight.data) + self.bias.data
- return F.relu(linear)
- linear = MyLinear(5, 3)#输入是5组,输出是每组3个,也得输出5组
- print(linear.weight)
- #使用自定义层直接执行正向传播计算
- print(linear(torch.rand(2, 5)))
- #使用自定义层构建模型
- net = nn.Sequential(MyLinear(64, 8), MyLinear(8, 1))
- 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]])
- #读写文件(训练好的东西如何存下来)
- #加载和保存张量(矩阵)--------------------矩阵
- import torch
- from torch import nn
- from torch.nn import functional as F
- x = torch.arange(4)
- torch.save(x, 'x-file')
- x2 = torch.load('x-file')
- print(x2)
- print("###########################################################################")
- #存储一个张量列表,然后把它们读回内存---------列表
- y = torch.zeros(4)
- torch.save([x, y], 'x-files')
- x2, y2 = torch.load('x-files')
- print((x2, y2))
- print("###########################################################################")
- #写入或读取从字符串映射到张量的字典-----------字典
- mydict = {'x': x, 'y': y}
- torch.save(mydict, 'mydict')
- mydict2 = torch.load('mydict')
- print(mydict2)
- print("###########################################################################")
- #加载和保存模型参数
- class MLP(nn.Module):
- def __init__(self):
- super().__init__()
- self.hidden = nn.Linear(20, 256)
- self.output = nn.Linear(256, 10)
- def forward(self, x):
- return self.output(F.relu(self.hidden(x)))
- net = MLP()
- X = torch.randn(size=(2, 20))
- Y = net(X)
- #将模型的参数存储为一个叫做“mlp.params”的文件
- torch.save(net.state_dict(), 'mlp.params')#把mlp的所有参数存成一个字典
- #实例化了原始多层感知机模型的一个备份。 直接读取文件中存储的参数
- clone = MLP()#在进行load参数之前,需要先对网络进行定义,不然没有load的对象。
- clone.load_state_dict(torch.load('mlp.params'))#定义之后把所有参数加载到mlp中
- print(clone.eval())#看一下这个网络
- Y_clone = clone(X)
- print(Y_clone == Y)
- 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]])
###########################################################################