第三周学习
神经网络
本节主要简单介绍了神经网络的相关知识,并且讲解了如何通过代码进行查看神经网络中的参数。
import torch from torch import nn from torch.nn import functional as F net = nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10)) X = torch.rand(2, 20) net(X) # 构建一个简单的网络 class MLP(nn.Module): def __init__(self): super().__init__() self.hidden = nn.Linear(20, 256) self.out = nn.Linear(256, 10) def forward(self, X): return self.out(F.relu(self.hidden(X))) net = MLP() net(X) # 简单实现Sequential class MySequential(nn.Module): def __init__(self, *args): super().__init__() for block in args: self._modules[block] = block def forward(self, X): for block in self._modules: X = block(X) return X net = MySequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10)) net(X) # 混合使用 Sequential 和继承 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)) chimera = nn.Sequential(NestMLP(), nn.Linear(16, 20), FixedHiddenMLP()) chimera(X) class FixedHiddenMLP(nn.Module): def __init__(self): super().__init__() 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() net(X) import torch from torch import nn net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1)) X = torch.rand(size=(2, 4)) net(X) # 查看网络参数 print(net[2].state_dict()) print(type(net[2].bias)) print(net[2].bias) print(net[2].bias.data) print(net[2].bias.grad) print(net[2].weight.grad) print(net[2].weight) print(*[(name, param.shape) for name, param in net[0].named_parameters()]) print(*[(name, param.shape) for name, param in net.named_parameters()]) net.state_dict()['2.bias'].data def block1(): return nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 4), nn.ReLU()) def block2(): net = nn.Sequential() for i in range(4): net.add_module(f'block{i}', block1()) return net rgnet = nn.Sequential(block2(), nn.Linear(4, 1)) rgnet(X) print(rgnet) def init_normal(m): if type(m) == nn.Linear: nn.init.normal_(m.weight, mean=0, std=0.01) nn.init.zeros_(m.bias) net.apply(init_normal) net[0].weight.data[0], net[0].bias.data[0] def init_constant(m): if type(m) == nn.Linear: nn.init.constant_(m.weight, 1) nn.init.zeros_(m.bias) net.apply(init_constant) net[0].weight.data[0], net[0].bias.data[0] def xavier(m): if type(m) == nn.Linear: nn.init.xavier_uniform_(m.weight) def init_42(m): if type(m) == nn.Linear: nn.init.constant_(m.weight, 42) net[0].apply(xavier) net[2].apply(init_42) print(net[0].weight.data[0]) print(net[2].weight.data) 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) m.weight.data *= m.weight.data.abs() >= 5 net.apply(my_init) net[0].weight[:2] net[0].weight.data[:] += 1 net[0].weight.data[0, 0] = 42 net[0].weight.data[0] # 共享参数的方法 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 print(net[2].weight.data[0] == net[4].weight.data[0]) import torch from torch import nn import torch.nn.functional as F class CenteredLayer(nn.Module): def __init__(self): super().__init__() def forward(self, X): return X - X.mean() layer = CenteredLayer() layer(torch.FloatTensor([1, 2, 3, 4, 5])) net = nn.Sequential(nn.Linear(8, 128), CenteredLayer()) Y = net(torch.rand(4, 8)) Y.mean() # 自己实现线性层,核心是使用 nn.Parameter class MyLinear(nn.Module): def __init__(self, in_units, units): super().__init__() self.weight = nn.Parameter(torch.randn(in_units, units)) 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) linear.weight linear(torch.rand(2, 5)) net = nn.Sequential(MyLinear(64, 8), MyLinear(8, 1)) net(torch.rand(2, 64)) # 保存参数 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') x2 mydict = {'x': x, 'y': y} torch.save(mydict, 'mydict') mydict2 = torch.load('mydict') mydict2 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) Y # 保存学习的权重 torch.save(net.state_dict(), 'mlp.params') # 加载学习到的权重 clone = MLP() clone.load_state_dict(torch.load('mlp.params')) clone.eval() Y_clone = clone(X) Y_clone == Y
GPU的使用
本节内容主要讲解了如何将数据和网络加载到 GPU 以提高训练速度
import torch from torch import nn torch.device('cpu'), torch.cuda.device('cuda'), torch.cuda.device('cuda:0') def try_gpu(i=0): if torch.cuda.device_count() >= i + 1: return torch.device(f'cuda:{i}') return torch.device('cpu') def try_all_gpus(): devices = [ torch.device(f'cuda:{i}') for i in range(torch.cuda.device_count())] return devices if devices else [torch.device('cpu')] try_gpu(), try_gpu(10), try_all_gpus() # 加载数据到 GPU x = torch.tensor([1, 2, 3]) x.device X = torch.ones(2, 3, device=try_gpu()) X Y = torch.rand(2, 3, device=try_gpu(0)) Y Z = X.cuda(0) print(X) print(Z) Y + Z Z.cuda(0) is Z # 加载网络到 GPU net = nn.Sequential(nn.Linear(3, 1)) net = net.to(device=try_gpu()) net(X) net[0].weight.data.device
卷积层
本节主要讲解了卷积的概念和操作方法,以及代码的实现
import torch from torch import nn from d2l import torch as d2l def corr2d(X, K): # 计算二维互相关运算 h, w = K.shape Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1)) for i in range(Y.shape[0]): for j in range(Y.shape[1]): Y[i, j] = (X[i: i+h, j: j+w] * K).sum() return Y X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]]) K = torch.tensor([[0.0, 1.0], [2.0, 3.0]]) corr2d(X, K) # 自己实现卷积 class Conv2D(nn.Module): def __init__(self, kernel_size): super().__init__() self.weight = nn.Patameter(torch.rand(kernel_size)) self.bias = nn.Parameter(torch.zeros(1)) def forward(self, X): return corr2d(X, self.weight) + self.bias X = torch.ones((6, 8)) X[:, 2:6] = 0 X K = torch.tensor([[1.0, -1.0]]) Y = corr2d(X, K) Y corr2d(X.t(), K) # 使用 pytorch 实现的卷积 conv2d = nn.Conv2d(1, 1, kernel_size=(1, 2), bias=False) X = X.reshape((1, 1, 6, 8)) Y = Y.reshape((1, 1, 6, 7)) for i in range(10): Y_hat = conv2d(X) l = (Y_hat - Y) ** 2 conv2d.zero_grad() l.sum().backward() conv2d.weight.data[:] -= 3e-2 * conv2d.weight.grad if (i + 1) % 2 == 0: print(f'batch {i+1}, loss {l.sum(): .3f}') conv2d.weight.data.reshape((1, 2))
步幅和填充
本节主要讲解了通过改变步幅和对图形填充来改变图形的尺寸大小,使图像更适合训练
import torch from torch import nn def comp_conv2d(conv2d, X): X = X.reshape((1, 1) + X.shape) Y = conv2d(X) return Y.reshape(Y.shape[2:]) # 使用 pytorch 自带的填充进行操作 conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1) X = torch.rand(size=(8, 8)) comp_conv2d(conv2d, X).shape conv2d = nn.Conv2d(1, 1, kernel_size=(5, 3), padding=(2, 1)) comp_conv2d(conv2d, X).shape # 使用 pytorch 自带的步幅进行操作 conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1, stride=2) comp_conv2d(conv2d, X).shape conv2d = nn.Conv2d(1, 1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4)) comp_conv2d(conv2d, X).shape
多输入多输出通道
本节内容较为重要,是神经网络中大家经常调节的参数
import torch from d2l import torch as d2l # 定义互相关运算 def corr2d_multi_in(X, K): return sum(d2l.corr2d(x, k) for x, k in zip(X, K)) X = torch.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]], [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]]) K = torch.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]]) corr2d_multi_in(X, K) def corr2d_multi_in_out(X, K): return torch.stack([corr2d_multi_in(X, k) for k in K], 0) K = torch.stack((K, K + 1, K + 2), 0) K.shape corr2d_multi_in_out(X, K) # 1x1 卷积进行通道数的调整 def corr2d_multi_in_out_1x1(X, K): c_i, h, w = X.shape c_o = K.shape[0] X = X.reshape((c_i, h*w)) print(X) K = K.reshape((c_o, c_i)) print(K) Y = torch.matmul(K, X) print(Y) return Y.reshape((c_o, h, w)) X = torch.normal(0, 1, (3, 3, 3)) K = torch.normal(0, 1, (2, 3, 1, 1)) print(X) print(K) Y1 = corr2d_multi_in_out_1x1(X, K) Y2 = corr2d_multi_in_out(X, K) assert float(torch.abs(Y1 - Y2).sum()) < 1e-6
池化层
本节内容主要讲解了池化操作,分为最大值池化和平均值池化两种
import torch from torch import nn from d2l import torch as d2l # 自定义简单的二维池化 def pool2d(X, pool_size, mode='max'): p_h, p_w = pool_size Y = torch.zeros(X.shape[0] - p_h +1, X.shape[1] - p_w + 1) for i in range(Y.shape[0]): for j in range(Y.shape[1]): if mode == 'max': Y[i, j] = X[i: i + p_h, j: j + p_w].max() elif mode == 'avg': Y[i, j] = X[i: i + p_h, j: j + p_w].mean() return Y X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]]) pool2d(X, (2, 2)) pool2d(X, (2, 2), 'avg') X = torch.arange(16, dtype=torch.float32).reshape(1, 1, 4, 4) X # 使用 pytorch 自带的池化操作 pool2d = nn.MaxPool2d(3) pool2d(X) pool2d = nn.MaxPool2d(3, padding=1, stride=2) pool2d(X) pool2d = nn.MaxPool2d((2, 3), padding=(1, 1), stride=(2, 3)) pool2d(X) X = torch.cat((X, X+1), 1) X pool2d = nn.MaxPool2d(3, padding=1, stride=2) pool2d(X)
LeNet
LeNet 是较为简单的网络,操作较少,性能相对于现在的网络来说比较差,但在当时是很先进的网络
import torch from torch import nn from d2l import torch as d2l class Reshape(nn.Module): def forward(self, x): return x.view(-1, 1, 28, 28) # LeNet 的网络结构 net = nn.Sequential(Reshape(), nn.Conv2d(1, 6, kernel_size=5, padding=2), nn.Sigmoid(), nn.AvgPool2d(kernel_size=2, stride=2), nn.Conv2d(6, 16, kernel_size=5), nn.Sigmoid(), nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(), nn.Linear(16*5*5, 120), nn.Sigmoid(), nn.Linear(120, 84), nn.Sigmoid(), nn.Linear(84, 10)) X = torch.rand(size=(1, 1, 28, 28), dtype=torch.float32) for layer in net: X = layer(X) print(layer.__class__.__name__, 'output shape: \t', X.shape) # 加载数据集 batch_size = 256 train_iter, test_iter, = d2l.load_data_fashion_mnist(batch_size=batch_size) # 计算准确率 def evaluate_accuracy_gpu(net, data_iter, device=None): if isinstance(net, torch.nn.Module): net.eval() if not device: device = next(iter(net.parameters())).device metric = d2l.Accumulator(2) for X, y in data_iter: if isinstance(X, list): X = [x.to(device) for x in X] else: X = X.to(device) y = y.to(device) metric.add(d2l.accuracy(net(X), y), y.numel()) return metric[0] / metric[1] # 训练函数 def train_ch6(net, train_iter, test_iter, num_epochs, lr, device): def init_weights(m): if type(m) == nn.Linear or type(m) == nn.Conv2d: nn.init.xavier_uniform_(m.weight) net.apply(init_weights) print('train on', device) net.to(device) optimizer = torch.optim.SGD(net.parameters(), lr=lr) loss = nn.CrossEntropyLoss() animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs], legend=['train loss', 'train acc', 'test acc']) timer, num_batches = d2l.Timer(), len(train_iter) for epoch in range(num_epochs): metric = d2l.Accumulator(3) net.train() for i, (X, y) in enumerate(train_iter): timer.start() optimizer.zero_grad() X, y = X.to(device), y.to(device) y_hat = net(X) l = loss(y_hat, y) l.backward() optimizer.step() with torch.no_grad(): metric.add(l * X.shape[0], d2l.accuracy(y_hat, y), X.shape[0]) timer.stop() train_l = metric[0] / metric[2] train_acc = metric[1] / metric[2] if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1: animator.add(epoch + (i + 1) / num_batches, (train_l, train_acc, None)) test_acc = evaluate_accuracy_gpu(net, test_iter) animator.add(epoch + 1, (None, None, test_acc)) print(f'loss {train_l:.3f}, train acc {train_acc:.3f}, ' f'test acc{test_acc:.3f}') print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec ' f'on {str(device)}') # 进行训练 lr, num_epochs = 0.9, 10 train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
猫狗大战
尝试进行训练,发现不收敛,准备继续尝试修改代码和参数
【推荐】博客园的心动:当一群程序员决定开源共建一个真诚相亲平台
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】Flutter适配HarmonyOS 5知识地图,实战解析+高频避坑指南
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合终身会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· C# 代码如何影响 CPU 缓存速度?
· 智能桌面机器人:使用 .NET 为树莓派开发 Wifi 配网功能
· C# 模式匹配全解:原理、用法与易错点
· 记一次SSD性能瓶颈排查之路——寿命与性能之间的取舍
· 理解 .NET 结构体字段的内存布局
· 时隔半年,拾笔分享:来自一个大龄程序员的迷茫自问
· 3 个超火的开源项目「GitHub 热点速览」
· C#-Visual Studio工具使用实践
· [原创]《C#高级GDI+实战:从零开发一个流程图》第02章:画一个矩形,能拖动!
· WineHQ 发布的 Framework Mono 6.14 的这个特性对Windows Form