前引


这个月还有两天了 可以多忙一会儿 希望这两天可以过大概30个视频 到50%左右的进度吧
头发有点长了 还有抽时间去剪头发。。。


从零开始的推荐系统学习之路(十三)---- 动手学深度学习系列 卷积神经网络 基础部分 CNN(卷积层 & 填充与步幅 & 多输入输出 & 池化层)


1、卷积层


这里卷积层 主要有两个特性吧 可以这样理解 一个是平移性 即 例如输入样本 相关纬度的数据 发生位置偏移 但是 对于训练好的模型 不同样本位置发生偏移 那结果肯定就不准了 所以有平移性 我是这样理解的。。 至于怎么实现后面再看

第二个就是 局部性 因为其实只需要关注 一张图片 也只需要关注局部性信息 其他区域的信息其实是可以相对而言不关心的


import torch
from torch import nn
import numpy as np


torch.rand((1, 2)) + 1

(1, 1) + (1, 1)


# K kernel
def corr2d(X, K):
    kx, ky = K.shape

    yx, yy = X.shape[0] - kx + 1, X.shape[1] - ky + 1

    Y = torch.zeros((yx, yy))
    for i, j in np.ndindex(yx, yy):
        Y[i][j] = (X[i:i+kx, j:j+ky] * K).sum()

    return Y

X = torch.ones((3, 3))
X[:, 2:5] = 0

K = torch.tensor([[1, 2], [3, 4]])
X, K, corr2d(X, K)
# for i, j in np.ndindex(1, 2):
#     print(i, j)


class Conv2d(nn.Module):
    def __init__(self, kernel_shape):
        super().__init__()
        self.weight = torch.rand(kernel_shape, requires_grad=True)
        self.bias = torch.zeros(1, requires_grad=True)

    def forward(self, X):
        return corr2d(X, self.weight) + self.bias

Conv2d(K.shape)

X = torch.ones((7, 7))
X[:, 2:5] = 0
X

K = torch.tensor([[1.0, -1.0]])

Y = corr2d(X, K)
print(X, "\n", Y)

net = Conv2d(K.shape)
loss = nn.MSELoss()
trainer = torch.optim.Adam(params=[net.weight, net.bias], lr=5e-1)

for i in range(100):
    l = loss(net(X), Y)
    trainer.zero_grad()
    net.zero_grad()
    l.backward()
    trainer.step()
    print("epoch:{}, l:{:.4f}".format(i + 1, l.data))
print(net.weight)


net = nn.Conv2d(1, 1, kernel_size=K.shape, bias=False)
loss = nn.MSELoss()
# trainer = torch.optim.Adam(params=[net.weight, net.bias], lr=5e-1)
X = X.reshape((1, 1) + X.shape)
Y = Y.reshape((1, 1) + Y.shape)

for i in range(100):
    l = loss(net(X), Y)
    net.zero_grad()
    l.sum().backward()
    net.weight.data -= 3e-1 * net.weight.grad
    print("epoch:{}, l:{:.4f}".format(i + 1, l.data))
print(net.weight)

在这里插入图片描述

在这里插入图片描述


2、填充和步幅


这个填充也是字面意思的 上下左右去填充
步幅指的是 去越多步幅去搞 这样可以更少的层数就可以收敛

输出高度 = floor((输入高度 + 2padding高度 - 卷积核高度) / 步幅高度 + 1)
输出宽度 = floor((输入宽度 + 2
padding宽度 - 卷积核宽度) / 步幅宽度 + 1


def comp_conv2d(conv2d, X):
    X = X.reshape((1, 1) + X.shape)
    Y = conv2d(X)

    return Y.reshape(Y.shape[2:])

net = nn.Conv2d(1, 1, kernel_size=(2, 1), bias=False, padding=1)
X = torch.ones((7, 7))

# (7, 7) - (2-1, 1-1) + (2, 2)
comp_conv2d(net, X).shape



net = nn.Conv2d(1, 1, kernel_size=(3, 3), bias=False, padding=2, stride=4)
X = torch.ones((7, 7))

# 输出高度 = floor((输入高度 + 2*padding高度 - 卷积核高度) / 步幅高度 + 1)
# 输出宽度 = floor((输入宽度 + 2*padding宽度 - 卷积核宽度) / 步幅宽度 + 1
comp_conv2d(net, X).shape

在这里插入图片描述


3、多输入输出通道


# 多一个通道纬度 
def corr2d_multi_in(X, K):
    print(X.shape, K.shape)
    # return sum([d2l.corr2d(x, k) for x, k in zip(X, K)])
    return sum(d2l.corr2d(x, k) for x, k in zip(X, K))

def corr2d_multi_in_for_test(X, K):
    # return sum([d2l.corr2d(x, k) for x, k in zip(X, K)])
    return [d2l.corr2d(x, k) for x, k in zip(X, K)]

# 多一个输出通道纬度
def corr2d_multi_in_out(X, K):
    # return sum([d2l.corr2d(x, k) for x, k in zip(X, K)])
    return torch.stack([corr2d_multi_in(X, k) for k in K], dim=0)

X = torch.tensor([[[0.0, 1.0, 2.0, 3.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, 10.0, 11.0, 12.0]]])
K = torch.tensor([[[0.0, 1.0],[1.0, 2.0]], 
                  [[1.0, 2.0],[3.0, 4.0]]])
print(corr2d_multi_in(X, K))


print(corr2d_multi_in_for_test(X, K))

K = torch.tensor([[[0.0, 1.0],[1.0, 2.0]], 
                  [[1.0, 2.0],[3.0, 4.0]]])

K = torch.stack([K, K + 1, K + 2], dim=0)
corr2d_multi_in_out(X, K)


X = torch.normal(0, 1, (3, 3, 3))
K = torch.normal(0, 1, (2, 3, 1, 1))

# X 为 3dim K 为 4dim 其中最外围为输出纬度
def corr_multi_in_out_1x1(X, K):
    ci, h, w = X.shape
    co, ci, kh, kw = K.shape

    X = X.reshape((ci, h * w))
    K = K.reshape((co, ci))

    Y = torch.matmul(K, X)
    return Y.reshape((co, h, w))

corr2d_multi_out(X, K), corr_multi_in_out_1x1(X, K)

Y1 = corr_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)

assert (Y1 - Y2).abs().sum() <= 1e-6

在这里插入图片描述

在这里插入图片描述


4、池化层


中转层去单独处理

X = torch.normal(0, 1, (3, 3, 3))
K = torch.normal(0, 1, (2, 3, 1, 1))

# X 为 3dim K 为 4dim 其中最外围为输出纬度
def corr_multi_in_out_1x1(X, K):
    ci, h, w = X.shape
    co, ci, kh, kw = K.shape

    X = X.reshape((ci, h * w))
    K = K.reshape((co, ci))

    Y = torch.matmul(K, X)
    return Y.reshape((co, h, w))

corr2d_multi_out(X, K), corr_multi_in_out_1x1(X, K)

Y1 = corr_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)

assert (Y1 - Y2).abs().sum() <= 1e-6

    
    
    
# 池化 2d输入
def pool2d(X, pool_size, mode='max'):
    pool_h, pool_w = pool_size
    h, w = X.shape

    Y = torch.zeros((h - pool_h + 1, w - pool_w + 1))
    yh, yw = Y.shape

    
    for i, j in np.ndindex(yh, yw):
        if mode == 'max':
            Y[i][j] = X[i:i+pool_h, j:j+pool_w].max()
        elif mode == 'avg':
            Y[i][j] = X[i:i+pool_h, j:j+pool_w].mean()

    return Y






X = torch.arange(9).reshape(3, -1).to(torch.float32)
X, pool2d(X, (2,2)), pool2d(X, (2,2), mode='avg')


X = torch.arange(9).reshape(1, 1, 3, -1).to(torch.float32)





# 步幅和窗口大小一样。。。 等于不会有重复格子
pool2d = nn.MaxPool2d(3)
print(pool2d(X))

pool2d = nn.MaxPool2d(2, padding=0, stride=1)
print(pool2d(X))

pool2d = nn.MaxPool2d(2, padding=1, stride=1)
print(pool2d(X))





X = torch.arange(9).reshape(1, 1, 3, -1).to(torch.float32)
X = torch.cat([X, X + 1], dim=0)

pool2d(X)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

更多推荐