手写一个简单的卷积

部署运行你感兴趣的模型镜像

看李沐小视频学的,觉得很有意思,记录一下,都是抄李沐老师的书的!!

目录

1、定义互相关运算

2、定义的一个类,初始化权重和前向传播

3、定义一些输入啥的,还有预期想要的结果(Y)

4、手写梯度下降!

5、最后输出结果


1、定义互相关运算
import torch
from torch import nn
from d2l import torch as d2l

def corr2d(X, K):  #@save
    """计算二维互相关运算"""
    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
2、定义的一个类,初始化权重和前向传播
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
3、定义一些输入啥的,还有预期想要的结果(Y)
X = torch.ones((6, 8))
X[:, 2:6] = 0
K = torch.tensor([[1.0, -1.0]])
Y = corr2d(X, K) #这里就是一个简单的垂直边缘检测
4、手写梯度下降!
# 构造一个二维卷积层,它具有1个输出通道和形状为(1,2)的卷积核
conv2d = nn.Conv2d(1,1, kernel_size=(1, 2), bias=False)

# 这个二维卷积层使用四维输入和输出格式(批量大小、通道、高度、宽度),
# 其中批量大小和通道数都为1
X = X.reshape((1, 1, 6, 8))
Y = Y.reshape((1, 1, 6, 7))
lr = 3e-2  # 学习率

for i in range(10):
    Y_hat = conv2d(X)
    l = (Y_hat - Y) ** 2
    conv2d.zero_grad()
    l.sum().backward()
    # 迭代卷积核
    conv2d.weight.data[:] -= lr * conv2d.weight.grad
    if (i + 1) % 2 == 0:
        print(f'epoch {i+1}, loss {l.sum():.3f}')
5、最后输出结果
conv2d.weight.data.reshape((1, 2))
#output
tensor([[ 1.0010, -0.9739]])  #可以看出与上面的K很接近了!!

可以看出效果不错!!

您可能感兴趣的与本文相关的镜像

PyTorch 2.5

PyTorch 2.5

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

### 卷积算法的手写实现 卷积操作深度学习中的核心组成部分,尤其在图像处理领域中被广泛应用。虽然现有的深度学习框架(如 TensorFlow 和 PyTorch)提供了高效的卷积实现,但在某些特定场景下,手写卷积代码可能更具有灵活性和学习价值。 以下是一个使用 Python 实现的简单卷积函数示例,该函数支持填充(padding)和步长(stride)参数,并且能够处理多通道输入与多输出通道的情况。 ```python import numpy as np def conv_naive(x, out_channels, kernel_size, padding=0, stride=1): # x 的形状为 [batch_size, height, width, in_channels] batch_size, in_height, in_width, in_channels = x.shape # 初始化卷积核,形状为 [kernel_size, kernel_size, in_channels, out_channels] kernel = np.random.rand(kernel_size, kernel_size, in_channels, out_channels) # 如果有填充,则对输入进行零填充 if padding > 0: pad_x = np.zeros((batch_size, in_height + 2 * padding, in_width + 2 * padding, in_channels)) pad_x[:, padding:-padding, padding:-padding, :] = x else: pad_x = x # 计算输出特征图的高度和宽度 out_height = (in_height + 2 * padding - kernel_size) // stride + 1 out_width = (in_width + 2 * padding - kernel_size) // stride + 1 # 初始化输出张量 out = np.zeros((batch_size, out_height, out_width, out_channels)) # 遍历输出特征图的每个位置 for i in range(out_height): for j in range(out_width): # 提取当前区域的输入块 roi_x = pad_x[:, i * stride:i * stride + kernel_size, j * stride:j * stride + kernel_size, :] # 对输入块与卷积核进行逐元素相乘并求和,得到输出值 # 将 roi_x 扩展一个维度以匹配 kernel 的形状 # 然后进行广播相乘,并对空间维度求和 conv_result = np.tile(np.expand_dims(roi_x, -1), (1, 1, 1, 1, out_channels)) * kernel out[:, i, j, :] = np.squeeze(np.sum(conv_result, axis=(1, 2, 3)), axis=3) return out ``` #### 使用示例 为了验证上述代码的功能,我们可以构造一个简单的输入张量并调用 `conv_naive` 函数: ```python if __name__ == '__main__': # 构造一个随机输入张量,形状为 [batch_size=1, height=10, width=10, channels=3] x = np.random.rand(1, 10, 10, 3) # 调用手写卷积函数,设置输出通道数为15,卷积核大小为3x3,填充为1,步长为2 output = conv_naive(x, out_channels=15, kernel_size=3, padding=1, stride=2) # 输出结果的形状应为 [1, 5, 5, 15] print(output.shape) # 应该打印 (1, 5, 5, 15) ``` 此代码通过手动遍历输入张量的每个局部区域,并将其与卷积核进行点乘,最后将结果累加来计算输出。尽管这种方法在性能上不如优化过的库实现,但它非常适合用于教学目的或理解卷积的工作原理。 #### 性能优化建议 - **向量化操作**:尽可能减少循环嵌套,利用 NumPy 的广播机制和矩阵运算来加速计算。 - **内存访问优化**:确保数据在内存中的布局有利于缓存行的使用,例如 NHWC 格式更适合现代 CPU 的内存访问模式。 - **并行化**:可以考虑使用多线程或多进程来并行处理不同的样本或特征图部分。 以上实现展示了如何从头开始构建一个基础的卷积层[^3]。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值