赞
踩
Dilation 的作用是对卷积核进行扩充;默认dilation=1不扩充,dilation>1 表示卷积核内两两行两两列之间填充dilation-1个0;如下图所示:
groups的作用是将输入和输出通道进行分组,避免所有的通道都进行融合,而是以组为单位进行融合,可以大大的减少训练参数;
import torch from torch.nn import functional as F import math # input = (bs,groups, in_channel//groups, input_h, input_w) # kernel = (groups,out_channel//groups, in_channel//groups, kernel_h, kernel_w) # Output1 = input * kernel # Output1 = (bs,groups,out_channel//groups,output_h,output_w) # Output2 = (bs,out_channel,output_h,output_w) # 自定义卷积操作, def matrix_multiplication_for_conv2d_final(input, kernel, bias=None, stride=1, padding=0, dilation=1, groups=1): # 如果有填充,就需要在卷积核的上下作用进行填充padding,pytorch中的pad函数是从里到外的,注意顺序 if padding > 0: # input.shape = torch.Size((batch_size,in_channel,input_h,input_w)) input = F.pad(input, (padding, padding, padding, padding, 0, 0, 0, 0)) # 获取输入input相关参数 bs, in_channel, input_h, input_w = input.shape # 获取相关卷积核参数 out_channel, m, kernel_h, kernel_w = kernel.shape # reshape input and kernel assert out_channel % groups == 0 and in_channel % groups == 0, "groups必须同时被输入通道数和输出通道数整除" # 将input进行分组 # before: (bs,in_channel,input_h,input_w) # after: (bs,groups,in_channel//groups,input_h,input_w) input = input.reshape((bs, groups, in_channel // groups, input_h, input_w)) # before: kernel.shape = (out_channel,in_channel//groups,kernel_h,kernel_w) # after : kernel.shape = (groups,out_channel//groups,in_channel//groups,kernel_h,kernel_w) kernel = kernel.reshape((groups, out_channel // groups, in_channel // groups, kernel_h, kernel_w)) # 得到扩张后的卷积核大小 kernel_h,kernel_w kernel_h = (kernel_h - 1) * (dilation - 1) + kernel_h kernel_w = (kernel_w - 1) * (dilation - 1) + kernel_w # 得到输出卷积的大小 output_h and output_w output_h = math.floor((input_h - kernel_h) / stride) + 1 output_w = math.floor((input_w - kernel_w) / stride) + 1 # 为了计算,需要将output添加一个groups维度 output_shape = (bs, groups, out_channel // groups, output_h, output_w) output = torch.zeros(output_shape) if bias is None: bias = torch.zeros(out_channel) for ind in range(bs): # 对batch_size进行遍历 for g in range(groups): # 对群组进行遍历 for oc in range(out_channel // groups): # 对分组后的输出通道进行遍历 for ic in range(in_channel // groups): # 对分组后的输入通道进行遍历 for i in range(0, input_h - kernel_h + 1, stride): # 对高度进行遍历 for j in range(0, input_w - kernel_w + 1, stride): # 对宽度进行遍历 # input_shape = [bs,groups,in_channel//groups,input_h,input_w] region = input[ind, g, ic, i:i + kernel_h:dilation, j:j + kernel_w:dilation] # 特征区域 # kernel_shape = [groups,out_channel//groups,in_channel//groups,kernel_h,kernel_w] # output_shape = [bs,groups,out_channel//groups,output_h,output_w] output[ind, g, oc, int(i / stride), int(j / stride)] += torch.sum( region * kernel[g, oc, ic]) output[ind, g, oc] += bias[g * (out_channel // groups) + oc] # 考虑偏置 # (bs,groups,out_channel//groups,output_h,output_w) -> (bs,out_channel,output_h,output_w) output = output.reshape((bs, out_channel, output_h, output_w)) # 还原成四维 return output kernel_size = 3 bs, in_channel, input_h, input_w = 2, 2, 5, 5 out_channel = 4 groups, dilation, stride, padding = 2, 2, 2, 1 input = torch.randn((bs, in_channel, input_h, input_w)) kernel = torch.randn(out_channel, in_channel // groups, kernel_size, kernel_size) bias = torch.randn(out_channel) pytorch_conv2d_api_ouput = F.conv2d(input, kernel, bias=bias, padding=padding, stride=stride, dilation=dilation, groups=groups) mm_conv2d_final_output = matrix_multiplication_for_conv2d_final(input, kernel, bias=bias, padding=padding, stride=stride, dilation=dilation, groups=groups) # 验证手写的卷积操作跟pytorch自带的卷积操作结果是否一致 flag = torch.allclose(pytorch_conv2d_api_ouput, mm_conv2d_final_output) # 如果flag=True表示计算结果与pytorch官网结果一致 print(f"flag={flag}")
flag=True
groups可以减少操作,将in_channel和out_channel进行分组后,每组之间进行通道融合
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。