华为深度学习面试手撕题:手写nn.Conv2d()函数

news2025/6/4 4:23:36

题目

只允许利用numpy包,实现Pytorch二维卷积函数nn.Conv2d()

解答

此代码考察二维卷积的概念,详见:

6.2. 图像卷积 — 动手学深度学习 2.0.0 documentation

6.3. 填充和步幅 — 动手学深度学习 2.0.0 documentation

6.4. 多输入多输出通道 — 动手学深度学习 2.0.0 documentation

代码实现:

import numpy as np
import torch
import torch.nn as nn

def conv2d(input, weight, bias=None, stride=1, padding=0):
    """
    实现二维卷积操作
    
    参数:
        input:  输入数据, 形状为 (batch_size, in_channels, height, width)
        weight: 卷积核, 形状为 (out_channels, in_channels, kernel_h, kernel_w)
        bias:   偏置项, 形状为 (out_channels,)
        stride: 步长, 可以是整数或元组 (stride_h, stride_w)
        padding: 填充, 可以是整数或元组 (pad_h, pad_w)
    
    返回:
        输出特征图, 形状为 (batch_size, out_channels, out_h, out_w)
    """
    # 解析步长和填充参数
    if isinstance(stride, int):
        stride_h = stride_w = stride
    else:
        stride_h, stride_w = stride
    
    if isinstance(padding, int):
        pad_h = pad_w = padding
    else:
        pad_h, pad_w = padding
    
    # 获取输入尺寸
    batch_size, in_channels, in_h, in_w = input.shape
    out_channels, _, kernel_h, kernel_w = weight.shape
    
    # 计算输出尺寸
    out_h = (in_h + 2 * pad_h - kernel_h) // stride_h + 1
    out_w = (in_w + 2 * pad_w - kernel_w) // stride_w + 1
    
    # 添加填充
    if pad_h > 0 or pad_w > 0:
        # 使用零填充
        padded_input = np.pad(
            input, 
            ((0, 0), (0, 0), (pad_h, pad_h), (pad_w, pad_w)),
            mode='constant'
        )
    else:
        padded_input = input
    
    # 初始化输出数组
    output = np.zeros((batch_size, out_channels, out_h, out_w))
    
    # 执行卷积操作
    for b in range(batch_size):
        for c_out in range(out_channels):
            for h_out in range(out_h):
                for w_out in range(out_w):
                    # 计算输入窗口位置
                    h_start = h_out * stride_h
                    w_start = w_out * stride_w
                    h_end = h_start + kernel_h
                    w_end = w_start + kernel_w
                    
                    # 提取输入窗口
                    window = padded_input[b, :, h_start:h_end, w_start:w_end]
                    
                    # 计算点积 (卷积操作)
                    conv_val = np.sum(window * weight[c_out])
                    
                    # 添加偏置
                    if bias is not None:
                        conv_val += bias[c_out]
                    
                    # 存储结果
                    output[b, c_out, h_out, w_out] = conv_val
    
    return output

import torch
import torch.nn as nn

if __name__ == "__main__":
    # 创建测试数据
    np.random.seed(42)
    
    # 输入数据: (batch_size=2, in_channels=3, height=5, width=5)
    input_data = np.random.randn(2, 3, 5, 5).astype(np.float32)
    
    # 卷积核: (out_channels=2, in_channels=3, kernel_h=3, kernel_w=3)
    weights = np.random.randn(2, 3, 3, 3).astype(np.float32)
    
    # 偏置: (out_channels=2)
    bias = np.array([0.5, -0.5], dtype=np.float32)
    
    # 转换为 PyTorch 张量
    input_torch = torch.tensor(input_data)
    weights_torch = torch.tensor(weights)
    bias_torch = torch.tensor(bias)
    
    # 测试1: 无填充, 步长=1
    print("测试1: 无填充, 步长=1")
    output1 = conv2d(input_data, weights, bias, stride=1, padding=0)
    
    # 创建 PyTorch 卷积层
    conv1_nn = nn.Conv2d(in_channels=3, out_channels=2, kernel_size=3, 
                         stride=1, padding=0, bias=True)
    # 设置权重和偏置
    with torch.no_grad():
        conv1_nn.weight.data = weights_torch
        conv1_nn.bias.data = bias_torch
    
    # 计算 PyTorch 输出
    output1_nn = conv1_nn(input_torch).detach().numpy()
    
    # 比较结果
    print("自定义实现与PyTorch输出是否一致:", np.allclose(output1, output1_nn, atol=1e-6))
    print(f"输出形状: {output1.shape}")
    print("自定义实现输出 (第一个样本的第一个通道前2x2):")
    print(output1[0, 0, :2, :2])
    print("PyTorch输出 (第一个样本的第一个通道前2x2):")
    print(output1_nn[0, 0, :2, :2])
    
    # 测试2: 填充=1, 步长=1
    print("\n测试2: 填充=1, 步长=1")
    output2 = conv2d(input_data, weights, bias, stride=1, padding=1)
    
    # 创建 PyTorch 卷积层
    conv2_nn = nn.Conv2d(in_channels=3, out_channels=2, kernel_size=3, 
                         stride=1, padding=1, bias=True)
    with torch.no_grad():
        conv2_nn.weight.data = weights_torch
        conv2_nn.bias.data = bias_torch
    
    output2_nn = conv2_nn(input_torch).detach().numpy()
    
    print("自定义实现与PyTorch输出是否一致:", np.allclose(output2, output2_nn, atol=1e-6))
    print(f"输出形状: {output2.shape}")
    print("自定义实现输出 (第一个样本的第一个通道前2x2):")
    print(output2[0, 0, :2, :2])
    print("PyTorch输出 (第一个样本的第一个通道前2x2):")
    print(output2_nn[0, 0, :2, :2])
    
    # 测试3: 无填充, 步长=2
    print("\n测试3: 无填充, 步长=2")
    output3 = conv2d(input_data, weights, bias, stride=2, padding=0)
    
    # 创建 PyTorch 卷积层
    conv3_nn = nn.Conv2d(in_channels=3, out_channels=2, kernel_size=3, 
                         stride=2, padding=0, bias=True)
    with torch.no_grad():
        conv3_nn.weight.data = weights_torch
        conv3_nn.bias.data = bias_torch
    
    output3_nn = conv3_nn(input_torch).detach().numpy()
    
    print("自定义实现与PyTorch输出是否一致:", np.allclose(output3, output3_nn, atol=1e-6))
    print(f"输出形状: {output3.shape}")
    print("自定义实现输出 (第一个样本的第一个通道):")
    print(output3[0, 0])
    print("PyTorch输出 (第一个样本的第一个通道):")
    print(output3_nn[0, 0])
    
    # 测试4: 无偏置
    print("\n测试4: 无偏置")
    output4 = conv2d(input_data, weights, None, stride=1, padding=0)
    
    # 创建 PyTorch 卷积层
    conv4_nn = nn.Conv2d(in_channels=3, out_channels=2, kernel_size=3, 
                         stride=1, padding=0, bias=False)
    with torch.no_grad():
        conv4_nn.weight.data = weights_torch
    
    output4_nn = conv4_nn(input_torch).detach().numpy()
    
    print("自定义实现与PyTorch输出是否一致:", np.allclose(output4, output4_nn, atol=1e-6))
    print("自定义实现输出 (第一个样本的第一个通道前2x2):")
    print(output4[0, 0, :2, :2])
    print("PyTorch输出 (第一个样本的第一个通道前2x2):")
    print(output4_nn[0, 0, :2, :2])

'''
测试1: 无填充, 步长=1
自定义实现与PyTorch输出是否一致: True
输出形状: (2, 2, 3, 3)
自定义实现输出 (第一个样本的第一个通道前2x2):
[[-6.4546895  -2.49435902]
 [-6.27663374  3.31103873]]
PyTorch输出 (第一个样本的第一个通道前2x2):
[[-6.4546895 -2.4943593]
 [-6.276634   3.3110385]]

测试2: 填充=1, 步长=1
自定义实现与PyTorch输出是否一致: True
输出形状: (2, 2, 5, 5)
自定义实现输出 (第一个样本的第一个通道前2x2):
[[ 1.17402518  1.28695214]
 [-0.09722954 -6.4546895 ]]
PyTorch输出 (第一个样本的第一个通道前2x2):
[[ 1.1740253   1.2869523 ]
 [-0.09722958 -6.4546895 ]]

测试3: 无填充, 步长=2
自定义实现与PyTorch输出是否一致: True
输出形状: (2, 2, 2, 2)
自定义实现输出 (第一个样本的第一个通道):
[[-6.4546895   1.38441801]
 [ 3.1934371  -1.1537782 ]]
PyTorch输出 (第一个样本的第一个通道):
[[-6.4546895  1.3844179]
 [ 3.1934366 -1.1537789]]

测试4: 无偏置
自定义实现与PyTorch输出是否一致: True
自定义实现输出 (第一个样本的第一个通道前2x2):
[[-6.9546895  -2.99435902]
 [-6.77663374  2.81103873]]
PyTorch输出 (第一个样本的第一个通道前2x2):
[[-6.9546895 -2.9943593]
 [-6.776634   2.811039 ]]
'''

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2395668.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

归一化相关

归一化相关问题 归一化方式Batch NormalizationLayer NormalizationInstance NormalizationGroup NormalizationRMSNorm(Root Mean Square Layer Normalization):RMSNorm 和 LayerNorm区别?归一化方式 Batch Normalization 在每一层的输入进行归一化处理,使其在每个批次内…

Git深入解析功能逻辑与核心业务场景流程

一、Git核心功能逻辑架构 #mermaid-svg-9tj1iCr99u6QenJM {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-9tj1iCr99u6QenJM .error-icon{fill:#552222;}#mermaid-svg-9tj1iCr99u6QenJM .error-text{fill:#552222;st…

LINUX528 重定向

2>&1 我的理解: 2>&1,2stderr错误输出,1stdout输出,stderr一般和stdout是分别输出(管道符只传递stdout,据元宝,stderr默认输出到终端;如果重定向符不进行2显示重定向&…

研华工控机安装Windows10系统,适用UEFI(GPT)格式安装

主要硬件 主板:AIMB-787 、CPU:i5-6500 U盘启动工具:通过网盘分享的文件:rufus-3.20.zip 链接: https://pan.baidu.com/s/1YlFfd-_EhFHCG4sEHBQ8dQ?pwdQT12 提取码: QT12 Win10 22H2 Pro 纯净版系统:通过网盘分享…

1、树莓派更换软件下载源

树莓派官方系统raspbian自带的是国外的软件源,在国内使用经常会遇到无法下载软件的问题。 以下是把raspbian系统(buster版本)的下载源改为阿里云软件源的方法。 1、修改sources.list文件 sudo nano /etc/apt/sources.list 将初始化中的代…

历年中山大学计算机保研上机真题

历年中山大学计算机保研上机真题 2025中山大学计算机保研上机真题 2024中山大学计算机保研上机真题 2023中山大学计算机保研上机真题 在线测评链接:https://pgcode.cn/school 不连续1的子串 题目描述 给定一个数字 n n n,输出长度为 n n n 的 01…

Python----目标检测(《SSD: Single Shot MultiBox Detector》论文和SSD的原理与网络结构)

一、SSD:单次多框检测器 1.1、基本信息 标题:SSD: Single Shot MultiBox Detector 作者:Wei Liu (UNC Chapel Hill), Dragomir Anguelov (Zoox Inc.), Dumitru Erhan, Christian Szegedy (Google Inc.), Scott Reed (University of Michiga…

springboot集成websocket给前端推送消息

一般通常情况下,我们都是前端主动朝后端发送请求,那么有没有可能,后端主动给前端推送消息呢?这时候就可以借助websocket来实现。下面给出一个简单的实现样例。 首先创建一个websocketDemo工程,该工程的整体结构如下&a…

0527漏洞原理:XSS笔记

理论知识 01 前端基础知识 1.1 HTML基础 定义&#xff1a;HTML&#xff08;超文本标记语言&#xff09;用于描述网页结构。标准结构&#xff1a; 内嵌脚本&#xff1a; <script>JavaScript代码</script>1.4 JavaScript弹窗函数 函数描述alert("文本&quo…

智能制造之精读——RPA制造行业常见场景【附全文阅读】

RPA 在制造行业应用广泛&#xff0c;为企业带来显著价值&#xff0c;是极具潜力的智能化解决方案。它能节省成本&#xff0c;降低人力与管理成本&#xff1b;提升运营效率&#xff0c;减少人机交互损耗&#xff1b;提高质量&#xff0c;保障流程准确性&#xff1b;还能增强合规…

深入剖析 Docker 容器化原理与实战应用,开启技术新征程!

文章目录 前言一、为什么 是Docker &#xff1f;二、Docker 容器化原理分析2.1 镜像&#xff08;Image&#xff09;2.2 容器&#xff08;Container&#xff09;2.3 仓库&#xff08;Registry&#xff09; 三、Docker 容器化实践3.1 Docker安装3.2 创建一个 Docker 镜像3.3 运行…

计算机网络(4)——网络层

1.概述 1.1 网络层服务 (1) 网络层为不同主机(Host)之间提供了一种逻辑通信机制 (2)每个主机和路由器都运行网络层协议 发送方&#xff1a;将来自传输层的消息封装到数据报(datagram)中接收方&#xff1a;向传输层交付数据段(segment) 1.2 网络层核心功能 路由选择(routing…

ESP32基础知识1:项目工程建立和烧录

ESP32基础知识1&#xff1a;项目工程建立和烧录 一、本文内容与前置知识点1. 本文内容2. 前置知识点 二、新建工程1. 工程配置2. 依照模板建立项目 三、硬件烧录1. 硬件准备2. 烧录器和ESP32连接3. 电脑端设置4. 烧录成功演示 四、参考文献 一、本文内容与前置知识点 1. 本文内…

allWebPlugin中间件VLC专用版之录像功能介绍

背景 VLC控件原有接口是不支持录像的&#xff0c;且libVLC提供的接口库&#xff0c;不能获取录像文件完整名称&#xff08;VLC-3.0.11 录制直播时有的无法保存视频的解决方法 - 1CM - 博客园&#xff09;&#xff1b;因此&#xff0c;非常的不友好。为了能够彻底解决这个问题&a…

Vim 支持多种编程语言编辑器

软件简介 Vim是Vi编辑器的增强版&#xff0c;它提供了更多的功能和快捷键。Vim是一款自由软件&#xff0c;它是由Bram Moolenaar在1991年创建的。Vim支持多种编程语言&#xff0c;包括C、C、Java、Python、Perl等等。它是一款轻量级的编辑器&#xff0c;可以快速打开和编辑大型…

解决 IDEA 在运行时中文乱码问题

直接说解决办法 编译 IDEA 所在目录的启动的 .vmoptions 文件&#xff0c;添加以下JVM 参数即可 -Dfile.encodingUTF-8如下图所示&#xff0c;Help > Edit Custom VM Options&#xff0c;随后在编辑框中添加-Dfile.encodingUTF-8 的 JVM 参数

Diffusion Planner:扩散模型重塑自动驾驶路径规划(ICLR‘25)

1. 概述 2025年2月14日&#xff0c;清华大学AIR智能产业研究院联合毫末智行、中科院自动化所和香港中文大学团队&#xff0c;在ICLR 2025会议上发布了Diffusion Planner——一种创新性的基于Diffusion Transformer的自动驾驶规划模型架构。该系统联合建模周车运动预测与自车行…

华为OD机试真题——阿里巴巴找黄金宝箱 IV(2025A卷:200分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025 A卷 200分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 2025华为OD真题目录+全流程解析/备考攻略/经验分享 华为OD机试真题《阿里巴巴找黄金宝箱 IV》:…

数据结构:时间复杂度(Time Complexity)和空间复杂度(Space Complexity)

目录 什么是时间复杂度&#xff1f; 如何表示时间复杂度&#xff1f; 为什么需要时间复杂度&#xff1f; 用几个例子理解 怎么分析代码的时间复杂度&#xff1f; 什么是空间复杂度&#xff1f; 举例理解 什么是时间复杂度&#xff1f; 时间复杂度是用来衡量一个算法“…

SSL/TLS 协议详解:安全通信的基石

一、概述 SSL&#xff08;Secure Sockets Layer&#xff09; 及其继任者 TLS&#xff08;Transport Layer Security&#xff09; 是位于 传输层&#xff08;TCP&#xff09;与应用层之间 的加密协议&#xff0c;用于在网络通信中实现 机密性、身份认证和数据完整性。 核心目标…