news 2026/3/4 6:10:39

卷积运算效果的池化处理|最大值

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
卷积运算效果的池化处理|最大值

引言

前序学习进程中,已将掌握了卷积效果非线性处理|非负性的操作方法,引入非线性可以为后面的学习带来一些便利。
而在更早的学习中,我们已经掌握了基本池化操作组件的应用。
今天基于前序学习进度,尝试在非线性的基础上引入池化操作。

池化

池化的定义非常简单,在这里可以仿照卷积计算定义一个池化核,非常重要的是,池化核只需要定义核的高度和宽度,而不必写出具体的核元素。这是因为池化核各个位置的元素可以取任何值,但这些值不会参与实际运算,所有不必去定义这些元素。
基于这一层理解,可以再简化一些,与其说是池化核,不如说是池化框,只需要用这个池化框逐个选中矩阵,然后对选中矩阵的元素进行取极大值或者平均值即可。

实操

还是以之前的运算代码为基础,增添池化操作的核心代码。
如果单独对池化操作感兴趣,可以从基本池化操作组件回忆。
这是新增的池化操作代码:

# 定义池化功能函数defmanual_pooling(input_feature,pool_kernel_size=2,pool_stride=2,pool_type='max'):""" 手动实现池化操作 参数: input_feature: 输入特征图(2D数组) pool_kernel_size: 池化核大小(默认2×2) pool_stride: 滑动步长(默认2) pool_type: 池化类型('max' 最大池化 / 'avg' 平均池化) 返回: output: 池化后的输出特征图 """# 获取输入特征图尺寸h,w=input_feature.shape# 计算输出特征图尺寸(向下取整,不考虑填充)pool_out_h=(h-pool_kernel_size)//pool_stride+1pool_out_w=(w-pool_kernel_size)//pool_stride+1# 定义池化效果储存矩阵pool_patch=torch.zeros((pool_out_h,pool_out_w),dtype=torch.float32)# 开展池化操作# 使用for循环逐个选取矩阵foriinrange(pool_out_h):forjinrange(pool_out_w):pool_h_start=i*pool_stride pool_h_end=pool_h_start+pool_kernel_size pool_w_start=j*pool_stride pool_w_end=pool_w_start+pool_kernel_size# 获得当前窗口pool_window=input_feature[pool_h_start:pool_h_end,pool_w_start:pool_w_end]# 池化,取最大值pool_patch[i,j]=pool_window.max()returnpool_patch pool_output=manual_pooling(relu_manual,pool_kernel_size=2,pool_stride=2,pool_type='max')print('pool_output=',pool_output)

代码运行后:

这里可以看到pool_output 是一个2行2列矩阵,这是因为ReLu是一个5行5列矩阵,pool_kernel大小是2,pool_stride=2,所以(5-2)/2+1=2。
然后把pool_stride改为1,这里直接给出完整代码:

importtorch# 1. 定义原始输入(3通道5×5)和卷积核1(边缘检测核)input_tensor=torch.tensor([# 输入通道1(R):5×5[[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15],[16,17,18,19,20],[21,22,23,24,25]],# 输入通道2(G):5×5[[26,27,28,29,30],[31,32,33,34,35],[36,37,38,39,40],[41,42,43,44,45],[46,47,48,49,50]],# 输入通道3(B):5×5[[51,52,53,54,55],[56,57,58,59,60],[61,62,63,64,65],[66,67,68,69,70],[71,72,73,74,75]]],dtype=torch.float32)# 形状:(3,5,5)# 输出原始通道的形状input_tensor_channels,input_tensor_h,input_tensor_w=input_tensor.shapeprint('input_tensor_channels=',input_tensor_channels)print('input_tensor_h=',input_tensor_h)print('input_tensor_w=',input_tensor_w)# 卷积核1(边缘检测核):3个子核,每个3×3kernel1=torch.tensor([[[1,0,-1],[1,0,-1],[1,0,-1]],# 子核1(R通道)[[1,0,-1],[1,0,-1],[1,0,-1]],# 子核2(G通道)[[1,0,-1],[1,0,-1],[1,0,-1]]# 子核3(B通道)],dtype=torch.float32)# 形状:(3,3,3)# 输出卷积核的形状kernel1_channels,kernel1_h,kernel1_w=kernel1.shapeprint('kernel1_channels=',kernel1_channels)print('kernel1_h=',kernel1_h)print('kernel1_w=',kernel1_w)# 通过循环自动计算卷积值# 定义步长为1stride=1# 定义卷积运算后的输出矩阵大小out_h=(input_tensor_h-kernel1_h)//stride+1out_w=(input_tensor_w-kernel1_w)//stride+1# 为避免对原始矩阵造成破坏,新定义一个量来完全复制原始矩阵input_cal_tensor=input_tensor# 输出矩阵里面是所有卷积运算的效果,这个矩阵比原始矩阵小,先定义为纯0矩阵output_cal_tensor=torch.zeros((out_h,out_w),dtype=torch.float32)# 循环计算foriinrange(out_h):forjinrange(out_w):# 对原始矩阵取块进行计算,这里定义了取块的起始行in_h_start=i*stride# 取块的末行in_h_end=in_h_start+kernel1_h# 取块的起始列in_w_start=j*stride# 取块的末列in_w_end=in_w_start+kernel1_w# 这是取到的块input_patch=input_cal_tensor[:,in_h_start:in_h_end,in_w_start:in_w_end]# 定义一个空列表output_cal存储数据output_cal=[]forkinrange(input_tensor_channels):# k代表了通道数,因为每个通道都要进行卷积运算,# 因此需要先得到单通道的计算效果,然后把它们储存在列表中output_channel=(input_patch[k]*kernel1[k]).sum()#print('input_patch[',k,']=',input_patch[k])#print('output_patch[', k, ']=', kernel1[k])# 计算结果储存在列表中output_cal.append(output_channel)# 把通道效果叠加后,保存到卷积输出矩阵# 这里有3层,第一层是每一个子卷积核和原始矩阵的每一个通道进行卷积运算# 第二层是将第一层的卷积运算值按照顺序排放在列表output_cal中# 第三层再将列表中获得数据直接叠加,获得当下卷积运算的总值# 综合起来,卷积核和每一通道内的对应块对位相乘,然后全部求和,输出给当前记录卷积值的矩阵# 由于每一个i和j都会对应k个通道,所以使用了3层循环来表达output_cal_tensor[i,j]=sum(output_cal)#print('output_cal_tensor[', i, j, ']=', output_cal_tensor[i, j])print('output=',output_cal_tensor)# 扩充原始矩阵,周围补一圈0padding=1padded_h=input_tensor_h+2*padding padded_w=input_tensor_w+2*padding padded_input_tensor=torch.zeros((input_tensor_channels,padded_h,padded_w),dtype=torch.float32)padded_input_tensor[:,padding:-padding,padding:-padding]=input_tensorprint(padded_input_tensor)# 对扩充后的矩阵卷积运算# 计算卷积矩阵大小padded_out_h=(padded_h-kernel1_h)//stride+1padded_out_w=(padded_w-kernel1_w)//stride+1# 为避免对原始矩阵造成破坏,新定义一个量来完全复制原始矩阵padded_input_cal_tensor=padded_input_tensor# 输出矩阵里面是所有卷积运算的效果,这个矩阵比原始矩阵小,先定义为纯0矩阵padded_output_cal_tensor=torch.zeros((padded_out_h,padded_out_w),dtype=torch.float32)foriinrange(padded_out_h):forjinrange(padded_out_w):# 对原始矩阵取块进行计算,这里定义了取块的起始行in_h_start=i*stride# 取块的末行in_h_end=in_h_start+kernel1_h# 取块的起始列in_w_start=j*stride# 取块的末列in_w_end=in_w_start+kernel1_w# 这是取到的块input_patch=padded_input_cal_tensor[:,in_h_start:in_h_end,in_w_start:in_w_end]# 定义一个空列表output_cal存储数据output_cal=[]forkinrange(input_tensor_channels):# k代表了通道数,因为每个通道都要进行卷积运算,# 因此需要先得到单通道的计算效果,然后把它们储存在列表中output_channel=(input_patch[k]*kernel1[k]).sum()#print('input_patch[',k,']=',input_patch[k])#print('output_patch[', k, ']=', kernel1[k])# 计算结果储存在列表中output_cal.append(output_channel)# 把通道效果叠加后,保存到卷积输出矩阵# 这里有3层,第一层是每一个子卷积核和原始矩阵的每一个通道进行卷积运算# 第二层是将第一层的卷积运算值按照顺序排放在列表output_cal中# 第三层再将列表中获得数据直接叠加,获得当下卷积运算的总值# 综合起来,卷积核和每一通道内的对应块对位相乘,然后全部求和,输出给当前记录卷积值的矩阵# 由于每一个i和j都会对应k个通道,所以使用了3层循环来表达padded_output_cal_tensor[i,j]=sum(output_cal)#print('padded_output_cal_tensor[', i, j, ']=', padded_output_cal_tensor[i, j])print('padded_output=',padded_output_cal_tensor)# 使卷积计算的负值为0,正值不变# 初始化全0矩阵,使其和卷积计算的结果矩阵大小一致relu_manual=torch.zeros_like(padded_output_cal_tensor)foriinrange(padded_output_cal_tensor.shape[0]):forjinrange(padded_output_cal_tensor.shape[1]):relu_manual[i,j]=torch.max(torch.tensor(0.0),padded_output_cal_tensor[i,j])print('ReLu输出:')print(relu_manual)# 简洁代码# 初始化全0矩阵,使其和卷积计算的结果矩阵大小一致#relu_manual_s=torch.zeros_like(padded_output_cal_tensor)relu_manual_s=torch.max(torch.tensor(0.0),padded_output_cal_tensor)print('ReLu输出:')print(relu_manual_s)# 定义池化功能函数defmanual_pooling(input_feature,pool_kernel_size=2,pool_stride=1,pool_type='max'):""" 手动实现池化操作 参数: input_feature: 输入特征图(2D数组) pool_kernel_size: 池化核大小(默认2×2) pool_stride: 滑动步长(默认2) pool_type: 池化类型('max' 最大池化 / 'avg' 平均池化) 返回: output: 池化后的输出特征图 """# 获取输入特征图尺寸h,w=input_feature.shape# 计算输出特征图尺寸(向下取整,不考虑填充)pool_out_h=(h-pool_kernel_size)//pool_stride+1pool_out_w=(w-pool_kernel_size)//pool_stride+1print(pool_out_h)# 定义池化效果储存矩阵pool_patch=torch.zeros((pool_out_h,pool_out_w),dtype=torch.float32)# 开展池化操作# 使用for循环逐个选取矩阵foriinrange(pool_out_h):forjinrange(pool_out_w):pool_h_start=i*pool_stride pool_h_end=pool_h_start+pool_kernel_size pool_w_start=j*pool_stride pool_w_end=pool_w_start+pool_kernel_size# 获得当前窗口pool_window=input_feature[pool_h_start:pool_h_end,pool_w_start:pool_w_end]# 池化,取最大值pool_patch[i,j]=pool_window.max()returnpool_patch pool_output=manual_pooling(relu_manual,pool_kernel_size=2,pool_stride=1,pool_type='max')print('pool_output=',pool_output)

代码运行的实际效果为:

ReLu输出:
tensor([[ 0., 0., 0., 0., 189.],
[ 0., 0., 0., 0., 306.],
[ 0., 0., 0., 0., 351.],
[ 0., 0., 0., 0., 396.],
[ 0., 0., 0., 0., 279.]])
4
pool_output= tensor([[ 0., 0., 0., 306.],
[ 0., 0., 0., 351.],
[ 0., 0., 0., 396.],
[ 0., 0., 0., 396.]])

细节说明

这里比较粗糙的选用了最大值池化,也可以使用平均值池化。
由于池化单独定义了函数,需要修改池化步长pool_stride时,最好把pool_output=manual_pooling(relu_manual, pool_kernel_size=2, pool_stride=1, pool_type=‘max’)这一行的pool_stride参数提前修改,如果只修改def manual_pooling(input_feature, pool_kernel_size=2, pool_stride=1, pool_type=‘max’)这一行代码,不会有实际效果。

总结

学习了卷积运算效果的池化处理,取到了卷积运算后的最大值。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/3 11:19:09

day38GPU训练及类的call方法@浙大疏锦行

day38GPU训练及类的call方法浙大疏锦行 虽然 loss.item() 会触发从 GPU → CPU 的同步/拷贝,但总耗时由多个成分共同决定(固定开销 每 epoch 的计算 同步/打印开销),因此改变记录次数不一定线性改变剩余时长。另外存在异步排队…

作者头像 李华
网站建设 2026/3/3 11:19:07

GPT-OSS-20B vs ChatGPT:开源替代方案的性能对比实测

GPT-OSS-20B vs ChatGPT:开源替代方案的性能对比实测 在大模型席卷各行各业的今天,越来越多企业开始面临一个现实问题:我们是否必须依赖OpenAI的API来获得高质量的语言生成能力?尤其是当业务涉及敏感数据、高频调用或定制化需求时…

作者头像 李华
网站建设 2026/3/3 9:57:09

【场景】笛卡尔积

电商系统中商品多规格选项(颜色、容量、版本等)的组合问题,核心算法是「笛卡尔积(Cartesian Product)」;如果涉及「过滤无效组合(比如某颜色无某容量)」「关联SKU/价格/库存」&#…

作者头像 李华
网站建设 2026/3/3 9:57:05

GPT-OSS-20B如何通过Harmony响应格式提升专业任务准确率

GPT-OSS-20B如何通过Harmony响应格式提升专业任务准确率 在企业级AI应用日益深入的今天,一个现实问题摆在开发者面前:我们是否真的需要动辄上百亿参数、依赖昂贵GPU集群的大模型来处理专业领域的复杂任务?越来越多的实践表明,真正…

作者头像 李华
网站建设 2026/3/2 0:36:03

21届智能车赛规则文档风格借鉴:编写ACE-Step技术白皮书

ACE-Step:开源音乐生成模型的技术演进与工程实践 在内容创作全面加速的今天,音乐——这一曾经高度依赖专业技能的艺术形式,正经历一场由AI驱动的民主化变革。无论是短视频创作者急需一段贴合情绪的背景乐,还是游戏开发者希望实现动…

作者头像 李华
网站建设 2026/3/3 18:46:50

亚马逊云科技储瑞松:AI智能体正在重塑未来工作模式

在全球云计算与人工智能技术加速融合的时代大潮下,作为全球IT行业一年一度的顶级盛宴,亚马逊云科技2025 re:Invent全球大会在美国拉斯维加斯如约而至。来自大中华区的五百余位客户与合作伙伴,也在大会现场见证了这一行业盛事。大会期间&#…

作者头像 李华