深度学习,从零实现多层感知机——李沐老师的课程笔记
对于深度学习,非常推荐李沐老师的课程,受益匪浅
课程视频QA_哔哩哔哩_bilibili
这里对老师的讲解做一些注释,希望大家可以更好理解这里面的意思
没有基础不知道软件如何安装的可以看Python深度学习:安装Anaconda、PyTorch(GPU版)库与PyCharm_哔哩哔哩_bilibili
若有差错,请与我联系探讨
目录
卷积层
首先说一维:有人认为w之前就是一维,那是w一般另一个维度是1,只输出一个结果,所以当成一维了,在softmax那章可以看出,w是二维,如(748,10)这个样子,因为有十类需要区分。
然后就是二维了:x是二维时,w首先就要有两个维度来分配权重,然后加了高和宽两个维度,变成了4维,这里i、j对应的是输出的值在输出矩阵上的位置,k、l对应的是输入的值在输入矩阵上的位置,所以这里就抽象成了一个4维的张量,之前的权重之所以是2维的张量是因为之前的输入输出都是一维的向量,也可以理解成第一维表示输出向量的位置,第二维表示输入向量的位置,这里因为又还原成了矩阵,所以输入输出都变成了二维向量,所以说权重也变成了4维的张量
这个变化:V是W的重新索引,也就是说x原来位置是k、l,现在是i+a、j+b,这两个值前后还是没有变化的,唯一变化的就是索引变化,即v的下标对应的量变化了,比如v(0,0)对应w(1,1)
引用b友评论
说白了就是当w变成二维的时候,输出图也从一维变二维了(所以才在第一个点上写输入和输出变形为矩阵),所以w是四维,如果二维输出成一维,那w就变成三维了(但是那样不如直接用多层感知机了)
原则1-平移不变性
到第二幅图这里就可以理解了,首先变化i+a,j+b是为了保留i和j的信息,然后这里为了让v不会因为x变化而一直变化,所以把v强行转变为一个二维的矩阵(表面上是二维,其实是四维,但是相同地方值一样)
原则2-局部性
局部性说明的是,权重只在i和j周边有参数,其他远的地方参数直接为0
第一个Y是维度大小,第二个Y是真的算式,其中的⭐是二维交叉操作子的如下图:
这里说明为了方便,卷积层并没有用数学上卷积的严格定义,而是取了方便,用相同的但是不反转的二维交叉相关来计算。所以说是说卷积层,但是实现使用交叉相关。
这里核矩阵就是权重,而这也就解决了最开始的问题“在如今识别一张大约360m像素的猫狗图片时,如果用多层感知机MLP的话,假设只用一层,100个输入,那么w权重所需要的参数也会到达惊人的3.6b,这个数量甚至多于所有猫狗的总和,还不知每个图片进行一个标记”
而这个问题的答案就是用核矩阵(核)来担当新的w权重,以前是全连接层是将每个元素对应相乘,现在卷积层是将核矩阵过一遍输入矩阵。
以下是代码部分:
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]):
# 这里是从i到i+h-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.Parameter(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]])
# 上图把1当白,0当黑就可以检测颜色边缘了
# 检测结果如下,颜色变化的地方是1和负1
Y = corr2d(X, K)
Y
# 当然,我们这个k不能检测横向的,也就是转置的矩阵
corr2d(X.t(), K)
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