news 2026/1/13 13:45:33

卷积运算结果的非线性处理|结果非负性

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
卷积运算结果的非线性处理|结果非负性

引言

前序学习进程中,已经简单学习了全连接层的概念,知晓全连接层先把数据展平,然后使用激活函数运算数据,最后将输出结果直接传递给下一层。
实际上全连接层就是激活函数发挥功能的层,通过激活函数可以引入非线性或者仅保留线性。
在最近的学习中,卷积运算的矩阵扩充和改变卷积核移动步长操作已经相对熟练,为此,我们把上述内容结合一下,对卷积运算后的结果矩阵稍作运算,把负值改为0,正值都保留。

非负值操作

在PyTorch里面,取非负值可以直接调用torch.max()函数,学习的官方链接为:torch.max。

torch.max(input,*,out)原理非常简单,给定输入张量input,直接返回所有输入张量里面的最大值。

维度最值计算

要想实现数据非负的转变,就把最小值设为0.0,比0小的全部都会改换成0.0,比0.0大的都会保持原值。由于我们的计算结果基本都是float.32这样的浮点数,所以把最小值设置为浮点数有利于保持数据稳定。
这里给一个简单示例:

importtorch# 生成5行5列的随机矩阵,符合标准正态分布a=torch.randn(5,5)print('a=',a)# 输出torch.max()函数对比效果print('torch.max(a, 1)=\n',torch.max(a,1))

这里先生成一个5行5列的标准正态分布随机数矩阵,然后进行非负性运算,获得的输出效果为:

根据输出可以看到,每一行的最大值都被挑选出来,并且还多了一行indices用来记录每一行中最大值所在的列位置。
我们最开始可能会以为所有数是和1比大小,但实际上是每一行内部元素自己比大小,1代表列这个维度,这是torch.max(a,1)中1的本意。

元素最值计算

那该如何让元素和1比大小,这就要仔细观察,我们是在张量里面定义数据,1不是张量,所以要对代码稍作改变,先把1转换为张量torch.tensor(1.0):

importtorch# 生成5行5列的随机矩阵,符合标准正态分布a=torch.randn(5,5)print('a=',a)# 输出torch.max()函数对比效果print('torch.max(a, 1)=\n',torch.max(a,1))t=torch.tensor(1.0)print('torch.max(a, torch.tensor(1.0))=\n',torch.max(a,t))

次时代码的输出效果为:

很显然,小于1.0的元素值都被强制赋值1.0,其余元素保持原值

卷积元素那结果非负运算

在上述讨论的基础上,直接进行卷积运算结果的非负运算就比较简单直观。
只需要先定义一个和原始计算结果一样大小的全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)

实际上只用了一行代码就完成了对比。
上述代码中的第一行被注释掉是因为确实没有用,进行torch.max()对比时,会自动给relu_manual_s创造一个和padded_output_cal_tensor等大的矩阵。
此时的输出效果和之前一样,这里给出两种代码写法输出的对比效果:

此时的完整代码为:

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)

细节说明

torch.max()函数包括维度最值和元素最值两种写法,虽然简单,但一定要区分比较的元素属性。

总结

学习了卷积运算结果的非线性处理中的非负性操作。

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

如何高效构建行为面试中的领导力案例:创新策略指南

行为面试是评估候选人实际工作能力的重要方式,而领导力案例的展示直接影响面试官对你的管理潜力和团队协作能力的判断。本文将为你提供完整的领导力案例构建策略,帮助你在面试中自信地展现领导才能,为职业发展创造更多机会。 【免费下载链接】…

作者头像 李华
网站建设 2026/1/12 5:13:01

基于STM32单片机的自动加湿器系统(论文+源码)

在基于单片机的自动加湿器设计中,以STM32作为系统主控核心,通过DHT11传感器实现环境中温度和湿度检测,系统检测到的数据可以通过OLED12864液晶实时显示。用户可以通过按键设定实现的下限和上电,当湿度低于设定下限时,控…

作者头像 李华
网站建设 2026/1/11 16:08:36

Step-Audio 2音频大模型:重新定义智能语音交互新纪元

Step-Audio 2音频大模型:重新定义智能语音交互新纪元 【免费下载链接】Step-Audio-2-mini-Think 项目地址: https://ai.gitcode.com/StepFun/Step-Audio-2-mini-Think 当语音助手只能机械地重复指令,当智能客服无法感知用户情绪,当车…

作者头像 李华
网站建设 2025/12/24 16:43:24

ExifToolGUI完全攻略:快速上手元数据编辑与GPS定位

ExifToolGUI完全攻略:快速上手元数据编辑与GPS定位 【免费下载链接】ExifToolGui A GUI for ExifTool 项目地址: https://gitcode.com/gh_mirrors/ex/ExifToolGui ExifToolGUI是一款功能强大的元数据编辑工具,作为ExifTool的图形界面版本&#xf…

作者头像 李华
网站建设 2025/12/17 11:26:33

3分钟掌握Marp:终极Markdown幻灯片制作指南

3分钟掌握Marp:终极Markdown幻灯片制作指南 【免费下载链接】marp The site of classic Markdown presentation writer app 项目地址: https://gitcode.com/gh_mirrors/ma/marp 还在为制作演示文稿而烦恼吗?Marp为你带来了全新的解决方案&#xf…

作者头像 李华
网站建设 2026/1/13 10:05:20

MATLAB COCO API实战指南:从数据困惑到精准分析的蜕变之路

还在为计算机视觉项目中的复杂数据处理而头疼吗?面对海量的标注信息,你是否曾经感到无从下手?别担心,MATLAB COCO API正是为你量身打造的解决方案,它将彻底改变你处理Microsoft COCO数据集的方式! 【免费下…

作者头像 李华