CNN卷积计算

【1】引言

前序学习进程中,已经对使用pytorch绘制正态分布函数图有了一定探索:

https://blog.csdn.net/weixin_44855046/article/details/152095080?spm=1001.2014.3001.5502

https://blog.csdn.net/weixin_44855046/article/details/152282802?spm=1001.2014.3001.5502

众所周知,PyTorch非常适合用于CNN计算。

为了用好PyTorch开展CNN计算,我们先来铺垫一下,了解卷积计算的基本原理。

【2】卷积计算代码解读

【2.1】完整代码

我们在这里使用AI生成了一段简单的代码,但dan包含完整的卷积计算过程,这里先展示一下完整代码。

python 复制代码
# 引入模块
import numpy as np
import matplotlib.pyplot as plt

# --------------------------
# 1. 定义输入和卷积核(简化尺寸便于观察)
# --------------------------
# 输入图像:4x4的简单矩阵(模拟灰度图)
input_image = np.array([
    [1, 2, 3, 0],
    [4, 5, 6, 0],
    [7, 8, 9, 0],
    [0, 0, 0, 0]
], dtype=np.float32)
plt.imshow(input_image, cmap='gray', vmin=0, vmax=9)
# 卷积核:2x2(用于提取简单特征)
kernel = np.array([
    [1, 0],
    [0, -1]
], dtype=np.float32)

stride = 1  # 步长为1

# --------------------------
# 2. 手动计算卷积过程并记录每一步
# --------------------------
input_h, input_w = input_image.shape
kernel_h, kernel_w = kernel.shape
out_h = (input_h - kernel_h) // stride + 1  # 输出高度:3
out_w = (input_w - kernel_w) // stride + 1  # 输出宽度:3

# 存储每一步的滑动窗口和计算结果
steps = []  # 每个元素是 (窗口区域, 计算过程字符串, 结果值)

for i in range(out_h):
    for j in range(out_w):
        # 提取当前滑动窗口(输入的局部区域)
        start_i, start_j = i * stride, j * stride
        window = input_image[start_i:start_i + kernel_h, start_j:start_j + kernel_w]

        # 计算卷积:元素相乘再求和
        product = window * kernel  # 元素相乘
        result = np.sum(product)  # 求和

        # 记录计算过程(用于可视化)
        calc_str = f"({window[0, 0]}×{kernel[0, 0]}) + ({window[0, 1]}×{kernel[0, 1]}) + \n"
        calc_str += f"({window[1, 0]}×{kernel[1, 0]}) + ({window[1, 1]}×{kernel[1, 1]}) = \n"
        calc_str += f"({product[0, 0]}) + ({product[0, 1]}) + ({product[1, 0]}) + ({product[1, 1]}) = {result}"

        steps.append((window, calc_str, result))

# --------------------------
# 3. 可视化卷积全过程(修正子图布局为4x3)
# --------------------------
plt.figure(figsize=(15, 12))  # 增大画布尺寸

# 显示输入图像和卷积核
plt.subplot(4, 3, 1)  # 第1个位置
plt.title("input_image (4x4)")
plt.imshow(input_image, cmap='gray', vmin=0, vmax=9)
for i in range(input_h):
    for j in range(input_w):
        plt.text(j, i, f"{input_image[i, j]}", ha='center', va='center', color='red')

plt.subplot(4, 3, 2)  # 第2个位置
plt.title("CNN_kernel (2x2)")
plt.imshow(kernel, cmap='gray', vmin=-1, vmax=1)
for i in range(kernel_h):
    for j in range(kernel_w):
        plt.text(j, i, f"{kernel[i, j]}", ha='center', va='center', color='blue')

# 显示输出特征图(3x3)
feature_map = np.array([s[2] for s in steps]).reshape(out_h, out_w)
plt.subplot(4, 3, 3)  # 第3个位置
plt.title("output_image (3x3)")
plt.imshow(feature_map, cmap='gray')
for i in range(out_h):
    for j in range(out_w):
        plt.text(j, i, f"{feature_map[i, j]:.0f}", ha='center', va='center', color='green')

# 显示每一步的滑动窗口和计算过程(共9步,从第4个位置开始)
for idx, (window, calc_str, result) in enumerate(steps, start=4):
    plt.subplot(4, 3, idx)  # 4x3网格支持到第12个位置,足够容纳9步
    plt.title(f"steps{idx - 3}:location ({(idx - 4) // 3}, {(idx - 4) % 3})")
    plt.imshow(window, cmap='gray', vmin=0, vmax=9)
    for i in range(kernel_h):
        for j in range(kernel_w):
            plt.text(j, i, f"{window[i, j]}", ha='center', va='center', color='red')
    plt.text(1.5, 0.5, calc_str, ha='left', va='center', fontsize=8, color='purple')

plt.tight_layout()
plt.show()

【2.2】代码结构

整体代码结构非常简单,首先引入必要模块,然后定义了要被卷积计算的矩阵和一个卷积核,之后就是开展卷积计算,最后把计算过程输出。

在有了这样一个基本概念之后,我们就进入详细的代码解读阶段。

【2.3】引入模块

为了实现快速入门和详细展示计算过程,只使用了计算numpy和绘图matplotlib两个模块。

python 复制代码
# 引入模块
# 为了实现计算过程详细展示,只使用了数学计算模块numpy和画图模块matplotlib
import numpy as np
import matplotlib.pyplot as plt

【2.4】引入模块

python 复制代码
# 1. 定义输入和卷积核(简化尺寸便于观察)
# --------------------------
# 输入图像:4x4的简单矩阵(模拟灰度图)
input_image = np.array([
    [1, 2, 3, 0],
    [4, 5, 6, 0],
    [7, 8, 9, 0],
    [0, 0, 0, 0]
], dtype=np.float32)
plt.imshow(input_image, cmap='gray', vmin=0, vmax=9)
# 卷积核:2x2(用于提取简单特征)
kernel = np.array([
    [1, 0],
    [0, -1]
], dtype=np.float32)

stride = 1  # 步长为1

# --------------------------
# 2. 手动计算卷积过程并记录每一步
# --------------------------
input_h, input_w = input_image.shape
kernel_h, kernel_w = kernel.shape
out_h = (input_h - kernel_h) // stride + 1  # 输出高度:3
out_w = (input_w - kernel_w) // stride + 1  # 输出宽度:3

# 存储每一步的滑动窗口和计算结果
steps = []  # 每个元素是 (窗口区域, 计算过程字符串, 结果值)

这理解定义了一些基础模块,相对简单。

【2.5】卷积计算

卷积计算使用卷积核一个模块一个模块进行元素对位相乘后再求和:

python 复制代码
# 卷积计算过程
for i in range(out_h):
    for j in range(out_w):
        # 提取当前滑动窗口(输入的局部区域)
        start_i, start_j = i * stride, j * stride
        # 因为+ kernel_h和+ kernel_w,实现真正的滑动
        # 这里实际上将每一小块元素都单独取出来了,就是windows
        window = input_image[start_i:start_i + kernel_h, start_j:start_j + kernel_w]

        # 计算卷积:元素相乘再求和
        product = window * kernel  # 元素相乘
        result = np.sum(product)  # 求和

        # 记录计算过程(用于可视化)
        # 由于取出来的windows大小都是2行2列,所以实现计算的过程中只需要关注[0,0],[0,1],[1,0]和[1,1]这四个位置
        # 先算第一行
        calc_str = f"({window[0, 0]}×{kernel[0, 0]}) + ({window[0, 1]}×{kernel[0, 1]}) + \n"
        # 再算第二行
        calc_str += f"({window[1, 0]}×{kernel[1, 0]}) + ({window[1, 1]}×{kernel[1, 1]}) = \n"
        # 将前两步计算获得的结果相加
        calc_str += f"({product[0, 0]}) + ({product[0, 1]}) + ({product[1, 0]}) + ({product[1, 1]}) = {result}"
        # 保存所有元素
        steps.append((window, calc_str, result))

由于想展示计算过程,所以使用了分步相乘再求和的步骤:

复制代码
# 记录计算过程(用于可视化)
# 由于取出来的windows大小都是2行2列,所以实现计算的过程中只需要关注[0,0],[0,1],[1,0]和[1,1]这四个位置
# 先算第一行
calc_str = f"({window[0, 0]}×{kernel[0, 0]}) + ({window[0, 1]}×{kernel[0, 1]}) + \n"
# 再算第二行
calc_str += f"({window[1, 0]}×{kernel[1, 0]}) + ({window[1, 1]}×{kernel[1, 1]}) = \n"
# 将前两步计算获得的结果相加
calc_str += f"({product[0, 0]}) + ({product[0, 1]}) + ({product[1, 0]}) + ({product[1, 1]}) = {result}"

【2.6】效果展示

最后相对简单,进行效果展示:

python 复制代码
# 3. 可视化卷积全过程(修正子图布局为4x3)
# --------------------------
plt.figure(figsize=(15, 12))  # 增大画布尺寸

# 显示输入图像和卷积核
plt.subplot(4, 3, 1)  # 第1个位置
plt.title("input_image (4x4)")
plt.imshow(input_image, cmap='gray', vmin=0, vmax=9)
for i in range(input_h):
    for j in range(input_w):
        plt.text(j, i, f"{input_image[i, j]}", ha='center', va='center', color='red')

plt.subplot(4, 3, 2)  # 第2个位置
plt.title("CNN_kernel (2x2)")
plt.imshow(kernel, cmap='gray', vmin=-1, vmax=1)
for i in range(kernel_h):
    for j in range(kernel_w):
        plt.text(j, i, f"{kernel[i, j]}", ha='center', va='center', color='blue')

# 显示输出特征图(3x3)
feature_map = np.array([s[2] for s in steps]).reshape(out_h, out_w)
plt.subplot(4, 3, 3)  # 第3个位置
plt.title("output_image (3x3)")
plt.imshow(feature_map, cmap='gray')
for i in range(out_h):
    for j in range(out_w):
        plt.text(j, i, f"{feature_map[i, j]:.0f}", ha='center', va='center', color='green')

# 显示每一步的滑动窗口和计算过程(共9步,从第4个位置开始)
for idx, (window, calc_str, result) in enumerate(steps, start=4):
    plt.subplot(4, 3, idx)  # 4x3网格支持到第12个位置,足够容纳9步
    plt.title(f"steps{idx - 3}:location ({(idx - 4) // 3}, {(idx - 4) % 3})")
    plt.imshow(window, cmap='gray', vmin=0, vmax=9)
    for i in range(kernel_h):
        for j in range(kernel_w):
            plt.text(j, i, f"{window[i, j]}", ha='center', va='center', color='red')
    plt.text(1.5, 0.5, calc_str, ha='left', va='center', fontsize=8, color='purple')

plt.tight_layout()
plt.show()

输出效果为:

这是原始图像,然后按照2X2大小对每个块进行卷积计算,先元素对位相乘,然后求和,对应的效果为:

此时的完整代码:

python 复制代码
# 引入模块
# 为了实现计算过程详细展示,只使用了数学计算模块numpy和画图模块matplotlib
import numpy as np
import matplotlib.pyplot as plt

# --------------------------
# 1. 定义输入和卷积核(简化尺寸便于观察)
# --------------------------
# 输入图像:4x4的简单矩阵(模拟灰度图)
input_image = np.array([
    [1, 2, 3, 0],
    [4, 5, 6, 0],
    [7, 8, 9, 0],
    [0, 0, 0, 0]
], dtype=np.float32)
# 先输出一下原始图像
plt.imshow(input_image, cmap='gray', vmin=0, vmax=9)
# 卷积核:2x2(用于提取简单特征)
kernel = np.array([
    [1, 0],
    [0, -1]
], dtype=np.float32)

# 步长手动设置,可以改变
stride = 1  # 步长为1

# --------------------------
# 2. 手动计算卷积过程并记录每一步
# --------------------------
# 用input_h, input_w分别提取原始图像input_image的高度和长度
input_h, input_w = input_image.shape
# 用kernel_h, kernel_w分别提取卷积核kernel的高度和长度
kernel_h, kernel_w = kernel.shape
# 先计算差分,可以知道原始矩阵和卷积核的大小差多少
# 实际上差多少,卷积核就要滑动多少步
# 但在最开始的时候,卷积核天然的就可以覆盖原始矩阵的一部分,因为覆盖,所以差分=0
# 所以这一步已经存在了,所以实际的步骤还要+1
out_h = (input_h - kernel_h) // stride + 1  # 输出高度:3
out_w = (input_w - kernel_w) // stride + 1  # 输出宽度:3

# 存储每一步的滑动窗口和计算结果
steps = []  # 每个元素是 (窗口区域, 计算过程字符串, 结果值)

# 卷积计算过程
for i in range(out_h):
    for j in range(out_w):
        # 提取当前滑动窗口(输入的局部区域)
        start_i, start_j = i * stride, j * stride
        # 因为+ kernel_h和+ kernel_w,实现真正的滑动
        # 这里实际上将每一小块元素都单独取出来了,就是windows
        window = input_image[start_i:start_i + kernel_h, start_j:start_j + kernel_w]

        # 计算卷积:元素相乘再求和
        product = window * kernel  # 元素相乘
        result = np.sum(product)  # 求和

        # 记录计算过程(用于可视化)
        # 由于取出来的windows大小都是2行2列,所以实现计算的过程中只需要关注[0,0],[0,1],[1,0]和[1,1]这四个位置
        # 先算第一行
        calc_str = f"({window[0, 0]}×{kernel[0, 0]}) + ({window[0, 1]}×{kernel[0, 1]}) + \n"
        # 再算第二行
        calc_str += f"({window[1, 0]}×{kernel[1, 0]}) + ({window[1, 1]}×{kernel[1, 1]}) = \n"
        # 将前两步计算获得的结果相加
        calc_str += f"({product[0, 0]}) + ({product[0, 1]}) + ({product[1, 0]}) + ({product[1, 1]}) = {result}"
        # 保存所有元素
        steps.append((window, calc_str, result))

# --------------------------
# 3. 可视化卷积全过程(修正子图布局为4x3)
# --------------------------
plt.figure(figsize=(15, 12))  # 增大画布尺寸

# 显示输入图像和卷积核
plt.subplot(4, 3, 1)  # 第1个位置
plt.title("input_image (4x4)")
plt.imshow(input_image, cmap='gray', vmin=0, vmax=9)
for i in range(input_h):
    for j in range(input_w):
        plt.text(j, i, f"{input_image[i, j]}", ha='center', va='center', color='red')

plt.subplot(4, 3, 2)  # 第2个位置
plt.title("CNN_kernel (2x2)")
plt.imshow(kernel, cmap='gray', vmin=-1, vmax=1)
for i in range(kernel_h):
    for j in range(kernel_w):
        plt.text(j, i, f"{kernel[i, j]}", ha='center', va='center', color='blue')

# 显示输出特征图(3x3)
feature_map = np.array([s[2] for s in steps]).reshape(out_h, out_w)
plt.subplot(4, 3, 3)  # 第3个位置
plt.title("output_image (3x3)")
plt.imshow(feature_map, cmap='gray')
for i in range(out_h):
    for j in range(out_w):
        plt.text(j, i, f"{feature_map[i, j]:.0f}", ha='center', va='center', color='green')

# 显示每一步的滑动窗口和计算过程(共9步,从第4个位置开始)
for idx, (window, calc_str, result) in enumerate(steps, start=4):
    plt.subplot(4, 3, idx)  # 4x3网格支持到第12个位置,足够容纳9步
    plt.title(f"steps{idx - 3}:location ({(idx - 4) // 3}, {(idx - 4) % 3})")
    plt.imshow(window, cmap='gray', vmin=0, vmax=9)
    for i in range(kernel_h):
        for j in range(kernel_w):
            plt.text(j, i, f"{window[i, j]}", ha='center', va='center', color='red')
    plt.text(1.5, 0.5, calc_str, ha='left', va='center', fontsize=8, color='purple')

plt.tight_layout()
plt.show()

【3】总结

学习了CNN卷积计算的基本过程。

相关推荐
云和数据.ChenGuang21 小时前
Python 重写(Override)深度解析:从 MRO 到 super() 的完整机制
java·开发语言·人工智能·python·langchain·1024程序员节
西西木科技丨Shopify开发机构21 小时前
CalDigit TS4案例:AI产品图的Shopify整站接入与验收
人工智能·shopify·独立站
2601_965448871 天前
AI视频制作:做系列节气民俗视频音色不统一,AI 音色克隆省事的方法
人工智能·机器学习·音视频
美狐美颜SDK开放平台1 天前
视频美颜SDK试用DEMO阶段应该测试什么?直播APP接入实操指南
开发语言·人工智能·深度学习·音视频·美颜sdk·直播美颜sdk·第三方美颜sdk
HRaitest1 天前
秋招技术岗招聘提效:三类 AI 招聘方案的技术底层与选型逻辑
大数据·人工智能·求职招聘·ai-native
天远数科1 天前
零信任架构实战:基于天远企业股权变更构建自动化商业银行对公授信存续期股权异动与资产抽逃预警网关
运维·人工智能·架构·自动化
车软学堂1 天前
从测试转开发,到底要补什么
网络·人工智能·python·功能测试·单片机·车载系统·自动化
海盗12341 天前
AI 新闻日报 10-28:Atria Dawn 完整版开源、Cursor Agent SDK 转正、Genie 4 实时世界模型
人工智能·机器人·开源·人工智能aigc
志尊宝1 天前
AI原生前端开发(035):AI时代的工程化最佳实践总结
人工智能·ai-native
冬奇Lab1 天前
一天一个开源项目(第251篇):Claude SEO —— 装进 Claude Code 的 SEO 审计团队,26 个子技能 + 19 个专家 Agent 并行体检网站
人工智能·开源