翻译: 4.2. 从零开始实现多层感知器MLP pytorch

翻译: 4.2. 从零开始实现多层感知器MLP pytorch

现在我们已经在数学上描述了多层感知器 (MLP),让我们尝试自己实现一个。为了与我们之前通过 softmax 回归(第 3.6 节)获得的结果进行比较,我们将继续使用 Fashion-MNIST 图像分类数据集(第 3.5 节)。

import torch
from torch import nn
from d2l import torch as d2l

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

4.2.1。初始化模型参数

回想一下,Fashion-MNIST 包含 10 个类,并且每个图像都包含一个28 x 28 = 784灰度像素值网格。同样,我们现在将忽略像素之间的空间结构,因此我们可以将其视为具有 784 个输入特征和 10 个类别的简单分类数据集。首先,我们将实现一个具有一个隐藏层和 256 个隐藏单元的 MLP。请注意,我们可以将这两个量都视为超参数。通常,我们选择以 2 的幂为单位的层宽度,由于内存在硬件中的分配和寻址方式,这往往具有计算效率。

同样,我们将用几个张量来表示我们的参数。请注意, 对于每一层,我们必须跟踪一个权重矩阵和一个偏置向量。与往常一样,我们为这些参数的损失梯度分配内存。

num_inputs, num_outputs, num_hiddens = 784, 10, 256

W1 = nn.Parameter(torch.randn(
    num_inputs, num_hiddens, requires_grad=True) * 0.01)
b1 = nn.Parameter(torch.zeros(num_hiddens, requires_grad=True))
W2 = nn.Parameter(torch.randn(
    num_hiddens, num_outputs, requires_grad=True) * 0.01)
b2 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))

params = [W1, b1, W2, b2]

4.2.2. 激活函数

为了确保我们知道一切是如何工作的,我们将使用最大函数自己实现 ReLU 激活,而不是直接调用内置relu函数。

def relu(X):
    a = torch.zeros_like(X)
    return torch.max(X, a)

4.2.3 模型 Model

因为我们不考虑空间结构,所以我们将reshape每个二维图像转换为长度为 的平面向量num_inputs。最后,我们只用几行代码来实现我们的模型。

def net(X):
    X = X.reshape((-1, num_inputs))
    H = relu(X@W1 + b1)  # Here '@' stands for matrix multiplication
    return (H@W2 + b2)

4.2.4. 损失函数

为了确保数值稳定性,并且因为我们已经从头开始实现了 softmax 函数(第 3.6 节),我们利用来自高级 API 的集成函数来计算 softmax 和交叉熵损失。回想一下我们之前在第 3.7.2 节中对这些复杂性的讨论 。我们鼓励感兴趣的读者检查损失函数的源代码,以加深他们对实现细节的了解。

loss = nn.CrossEntropyLoss(reduction='none')

4.2.5 训练

幸运的是,MLP 的训练循环与 softmax 回归完全相同。再次利用该d2l包,我们调用该 train_ch3函数(参见第 3.6 节),将 epoch 数设置为 10,将学习率设置为 0.1。

num_epochs, lr = 10, 0.1
updater = torch.optim.SGD(params, lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, updater)
www.zeeklog.com  - 翻译: 4.2. 从零开始实现多层感知器MLP pytorch


为了评估学习模型,我们将其应用于一些测试数据。

d2l.predict_ch3(net, test_iter)
www.zeeklog.com  - 翻译: 4.2. 从零开始实现多层感知器MLP pytorch

4.2.6 概括

我们看到实现一个简单的 MLP 很容易,即使手动完成也是如此。

然而,对于大量的层,从头开始实现 MLP 仍然会变得混乱(例如,命名和跟踪我们模型的参数)。

4.2.7。练习

  1. 更改超参数的值num_hiddens并查看此超参数如何影响您的结果。确定这个超参数的最佳值,保持所有其他参数不变。

原始值num_inputs, num_outputs, num_hiddens = 784, 10, 256

www.zeeklog.com  - 翻译: 4.2. 从零开始实现多层感知器MLP pytorch

num_hiddens变小了,速度变快了,损失函数会变大一点,也就是结果变差了
改为num_hiddens = 56num_inputs, num_outputs, num_hiddens = 784, 10, 56

www.zeeklog.com  - 翻译: 4.2. 从零开始实现多层感知器MLP pytorch

变大点num_inputs, num_outputs, num_hiddens = 784, 10, 2560
num_hiddens变大了,速度变慢了,损失函数会变小一点,也就是结果变好了一点

www.zeeklog.com  - 翻译: 4.2. 从零开始实现多层感知器MLP pytorch
  1. 尝试添加一个额外的隐藏层,看看它如何影响结果。
    加了一个隐藏层 128,初始化参数需要变
num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 128

W1 = nn.Parameter(torch.randn(
    num_inputs, num_hiddens1, requires_grad=True) * 0.01)
b1 = nn.Parameter(torch.zeros(num_hiddens1, requires_grad=True))
W2 = nn.Parameter(torch.randn(
    num_hiddens1, num_hiddens2, requires_grad=True) * 0.01)
b2 = nn.Parameter(torch.zeros(num_hiddens2, requires_grad=True))
W3 = nn.Parameter(torch.randn(
    num_hiddens2, num_outputs, requires_grad=True) * 0.01)
b3 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))

params = [W1, b1, W2, b2, W3, b3]

对应的model函数也需要变

def net(X):
    X = X.reshape((-1, num_inputs))
    H = relu(X@W1 + b1)  # Here '@' stands for matrix multiplication
    H2 = relu(H@W2 + b2)
    return (H2@W3 + b3)
www.zeeklog.com  - 翻译: 4.2. 从零开始实现多层感知器MLP pytorch


结果变化不大,但是acc 准确度初始值的时候变小了。

  1. 改变学习率如何改变你的结果?修复模型架构和其他超参数(包括 epoch 数),什么学习率可以给你最好的结果?

参考基线 num_epochs, lr = 10, 0.1

www.zeeklog.com  - 翻译: 4.2. 从零开始实现多层感知器MLP pytorch


增加 epoch num_epochs, lr = 100, 0.1, 速度明显变慢,准确度上升

www.zeeklog.com  - 翻译: 4.2. 从零开始实现多层感知器MLP pytorch


减少epoch num_epochs, lr = 5, 0.1, 速度边框,test rate也没变化多少

www.zeeklog.com  - 翻译: 4.2. 从零开始实现多层感知器MLP pytorch
  1. 通过联合优化所有超参数(学习率、时期数、隐藏层数、每层隐藏单元数),您可以获得的最佳结果是什么?
www.zeeklog.com  - 翻译: 4.2. 从零开始实现多层感知器MLP pytorch

Test Rate大约在85%

  1. 描述为什么处理多个超参数更具挑战性。
    因为计算量,指数型增加。
    If we have multiple hyperparameters, so mix-and-match of all the parameters will create an exponential amount of parameters to optimize.
  1. 对于构建多个超参数的搜索,您能想到的最聪明的策略是什么?

Grid search can be a good way to go about it. Increase the value exponentially. Maybe try binary search. The idea is to not go linearly but in order of log.

参考

https://d2l.ai/chapter_multilayer-perceptrons/mlp-scratch.html

Read more

Java垃圾回收—— 垃圾收集器

Java垃圾回收—— 垃圾收集器

* * * * 概述 在我们上一篇文章中讲述到了垃圾回收的三个要点 : 1. 哪些内存需要回收? 2. 什么时候回收? 3. 如何回收? 当然这些是内存回收的理论方法,那么今天要介绍的就是内存回收的具体实现。 但是了解垃圾收集器之前,我们还需要了解如下四个知识点。 Stop The World 可达性分析对执行时间的敏感还体现在GC停顿上。 因为这项分析工作必须在一个能确保一致性快照中进行——这里“一致性”的意思是在指整个分析期间整个执行系统看起来就像被冻结在某个时间点上,不可以出现分析过程中对象引用关系还在不断变化的情况。 这点是导致GC进行时必须停顿所有Java执行线程的重要原因之一。 换个的说话就是 : 判定垃圾回收的时候要保持整个引用不改变,(我在打扫房间,数地板上的垃圾时,不允许别人清理或者增加垃圾,否则就乱套了)。 所以当可达性分析引用链的时候,就要全部暂停(STOP!),但是这个暂停时间特别短暂,对程序的影响也是的,这就是GC卡顿的原因由来。 枚举GC Roots 从可达性分析中从GC Roots节点找引用

By Ne0inhk
MySQL索引原理及慢查询优化

MySQL索引原理及慢查询优化

背景 MySQL凭借着出色的性能、低廉的成本、丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库。虽然性能出色,但所谓“好马配好鞍”,如何能够更好的使用它,已经成为开发工程师的必修课,我们经常会从职位描述上看到诸如“精通MySQL”、“SQL语句优化”、“了解数据库原理”等要求。我们知道一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,所以查询语句的优化显然是重中之重。 本人从13年7月份起,一直在美团核心业务系统部做慢查询的优化工作,共计十余个系统,累计解决和积累了上百个慢查询案例。随着业务的复杂性提升,遇到的问题千奇百怪,五花八门,匪夷所思。本文旨在以开发工程师的角度来解释数据库索引的原理和如何优化慢查询。 索引目的 索引的目的在于提高查询效率,可以类比字典,如果要查“mysql”这个单词,我们肯定需要定位到m字母,然后从下往下找到y字母,再找到剩下的sql。如果没有索引,那么你可能需要把所有单词看一遍才能找到你想要的,如果我想找到m开头的单词呢?或者ze开头的单词呢?是不是

By Ne0inhk
Mysql加锁分析

Mysql加锁分析

最近逛社区发现精华贴,转过来共同学习! 1. 背景 MySQL/InnoDB的加锁分析,一直是一个比较困难的话题。我在工作过程中,经常会有同事咨询这方面的问题。同时,微博上也经常会收到MySQL锁相关的私信,让我帮助解决一些死锁的问题。本文,准备就MySQL/InnoDB的加锁问题,展开较为深入的分析与讨论,主要是介绍一种思路,运用此思路,拿到任何一条SQL语句,都能完整的分析出这条语句会加什么锁?会有什么样的使用风险?甚至是分析线上的一个死锁场景,了解死锁产生的原因。 注:MySQL是一个支持插件式存储引擎的数据库系统。本文下面的所有介绍,都是基于InnoDB存储引擎,其他引擎的表现,会有较大的区别。 1. MVCC:Snapshot Read vs Current Read MySQL InnoDB存储引擎,实现的是基于多版本的并发控制协议——MVCC () (注:与MVCC相对的,是基于锁的并发控制,Lock-Based Concurrency Cont

By Ne0inhk
case interview

case interview

java面试知识大法【== 持续更新中ing...建议收藏关注 ==】 * first part 语法篇 * 1、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 可以。 1、如果有公开类,只能有一个类的访问权限为public,且此类名为.java的文件名。 2、无公开类,符合命名规则即可。 注释: 1、一个编译单元(.java文件)只允许一个public对外出口,若有两个public文件时,将会引起歧义(将不能为编译单元,同一文件将会编译两次),导致报错。 2、因为public类是公共访问的,jvm运行时并不是把所有类都加载至内存,当遇到import的时候,才会根据路径(包名)去编译.java文件。public类名与文件名一致,便于寻找,开销减少。 3、若同一.java有非public类,将视为这些类为支持public类的辅助类,毕竟不是public,无法对外。

By Ne0inhk