一:pytorch实现空洞卷积实验(torch实现)
要求:
从至少一个数据集上进行实验,同理,这里我选取了车辆分类数据集(后面的实验都是用的车辆分类数据集),主要在之前利用torch.nn实现二维卷积的基础上,为解决感受野比较的问题,将普通的卷积修改为空洞卷积,并且卷几率符合HDC条件(这里我选取了1,2,5),并且堆叠了2层HDC,即一共六层卷积层。
实验过程:
注:所谓的空洞卷积,与https://blog.csdn.net/qq_37534947/article/details/109726153的torch.nn实现的二维卷积除了模型的定义部分不一样之外,其他的如:数据集、画图、计时、优化器等都是一样的,当然参数设置可能会有些不同,主要在实验结果区分,所以我这里主要针对模型定义做相关介绍。
1.1 空洞卷积模型定义
1. #pytorch封装卷积层 2. class ConvModule(nn.Module): 3. def __init__(self): 4. super(ConvModule,self).__init__() 5. #定义六层卷积层 6. #两层HDC(1,2,5,1,2,5) 7. self.conv = nn.Sequential( 8. #第一层 (3-1)*1+1=3 (64-3)/1 + 1 =62 9. nn.Conv2d(in_channels = 3,out_channels = 32,kernel_size = 3 , stride = 1,padding=0,dilation=1), 10. nn.BatchNorm2d(32), 11. # inplace-选择是否进行覆盖运算 12. nn.ReLU(inplace=True), 13. #第二层 (3-1)*2+1=5 (62-5)/1 + 1 =58 14. nn.Conv2d(in_channels = 32,out_channels = 32,kernel_size = 3 , stride = 1,padding=0,dilation=2), 15. nn.BatchNorm2d(32), 16. # inplace-选择是否进行覆盖运算 17. nn.ReLU(inplace=True), 18. #第三层 (3-1)*5+1=11 (58-11)/1 +1=48 19. nn.Conv2d(in_channels = 32,out_channels = 64,kernel_size = 3 , stride = 1,padding=0,dilation=5), 20. nn.BatchNorm2d(64), 21. # inplace-选择是否进行覆盖运算 22. nn.ReLU(inplace=True), 23. #第四层(3-1)*1+1=3 (48-3)/1 + 1 =46 24. nn.Conv2d(in_channels = 64,out_channels = 64,kernel_size = 3 , stride = 1,padding=0,dilation=1), 25. nn.BatchNorm2d(64), 26. # inplace-选择是否进行覆盖运算 27. nn.ReLU(inplace=True), 28. #第五层 (3-1)*2+1=5 (46-5)/1 + 1 =42 29. nn.Conv2d(in_channels = 64,out_channels = 64,kernel_size = 3 , stride = 1,padding=0,dilation=2), 30. nn.BatchNorm2d(64), 31. # inplace-选择是否进行覆盖运算 32. nn.ReLU(inplace=True), 33. #第六层 (3-1)*5+1=11 (42-11)/1 +1=32 34. nn.Conv2d(in_channels = 64,out_channels = 128,kernel_size = 3 , stride = 1,padding=0,dilation=5), 35. nn.BatchNorm2d(128), 36. # inplace-选择是否进行覆盖运算 37. nn.ReLU(inplace=True) 38. 39. ) 40. #输出层,将通道数变为分类数量 41. self.fc = nn.Linear(128,num_classes) 42. 43. def forward(self,x): 44. #图片经过三层卷积,输出维度变为(batch_size,C_out,H,W) 45. out = self.conv(x) 46. #使用平均池化层将图片的大小变为1x1,第二个参数为最后输出的长和宽(这里默认相等了) 47. out = F.avg_pool2d(out,32) 48. #将张量out从shape batchx128x1x1 变为 batch x128 49. out = out.squeeze() 50. #输入到全连接层将输出的维度变为3 51. out = self.fc(out) 52. return out
注:可以从上面可以看出一共有6个空洞卷积层,其空洞率分别是1、2、5、1、2、5;1、2、5不包含大于1的公约数,所以其是包含2个的HDC,然后其每一层的输入和输出的算法见注释。
二:pytorch实现残差实验(torch实现)
从至少一个数据集上进行实验,这里我选取了车辆分类数据集,在给定结构的残差网络中,进行模型的搭建以及训练。
实验结构:
实验过程:
这里实验过程和4上面大部分一样,主要是模型设计不同,所以这里主要介绍一下给定的残差网络的模型实现代码。
2.1残差网络块的实现
1. #残差网络块 2. #每个残差块都是两层 3. #默认3*3卷积下padding为1,则大小不会变化,如变化则是步长引起的。 4. class ResidualBlock(nn.Module): 5. def __init__(self, nin, nout, size, stride=1, shortcut=True): 6. super(ResidualBlock, self).__init__() 7. #两层卷积层 8. #不同步长只有第一层卷积层不同 9. self.block1 = nn.Sequential(nn.Conv2d(nin, nout, size, stride, padding=1), 10. nn.BatchNorm2d(nout), 11. nn.ReLU(inplace=True), 12. nn.Conv2d(nout, nout, size, 1, padding=1), 13. nn.BatchNorm2d(nout)) 14. self.shortcut = shortcut 15. #解决通道数变化以及步长不为1引起的图片大小的变化 16. self.block2 = nn.Sequential(nn.Conv2d(nin, nout, size, stride, 1), 17. nn.BatchNorm2d(nout)) 18. self.relu = nn.ReLU(inplace=True) 19. 20. def forward(self, input): 21. x = input 22. out = self.block1(x) 23. '''''若输入输出维度相等直接相加,不相等改变输入的维度--包括大小和通道''' 24. if self.shortcut: 25. out = x + out 26. else: 27. out = out + self.block2(x) 28. out = self.relu(out) 29. return out
注:这部分主要借鉴的是ppt和网上残差的实现,主要定义了每个残差块内的卷积操作,给定的残差结构内都是两个卷积,,所以这里的self.block1就是其实现,然后因为在残差块的开始会有通道的改变,以及步长的改变,从而导致图片的长和宽变化,所以定义了self.shortcut定义是否会导致变化,然后利用self.block2定义的卷积进行改变输入的大小使得通道可以相加
2.2模型定义
1. #定义给定的残差结构 2. class resnet(nn.Module): 3. def __init__(self): 4. super(resnet, self).__init__() 5. self.block = nn.Sequential(nn.Conv2d(3, 64, 3, stride=1, padding=1), 6. nn.BatchNorm2d(64), 7. nn.ReLU()) 8. #t表示2个相同的残差块,每个残差块两个卷积 9. self.d1 = self.make_layer(64, 64, 3, stride=1, t=2) 10. self.d2 = self.make_layer(64, 128, 3, stride=2, t=2) 11. self.d3 = self.make_layer(128, 256, 3, stride=2, t=2) 12. self.d4 = self.make_layer(256, 512, 3, stride=2, t=2) 13. 14. self.avgp = nn.AvgPool2d(8) 15. self.exit = nn.Linear(512, 3) 16. 17. def make_layer(self, in1, out1, ksize, stride, t): 18. layers = [] 19. for i in range(0, t): 20. if i == 0 and in1 != out1: 21. layers.append(ResidualBlock(in1, out1, ksize, stride, None)) 22. else: 23. layers.append(ResidualBlock(out1, out1, ksize, 1, True)) 24. return nn.Sequential(*layers) 25. 26. def forward(self, input): 27. x = self.block(input) # 输出维度 64 * 64 * 64 C * H * W 28. x = self.d1(x) # 输出维度 64 * 54 * 54 29. x = self.d2(x) # i=0 步长为2,输出维度128 * 32 * 32 30. x = self.d3(x) # i=0 步长为2,输出维度256 * 16 * 16 31. x = self.d4(x) # i=0 步长为2,输出维度512 * 8 * 8 32. x = self.avgp(x) # 512 * 1 * 1 33. #将张量out从shape batchx512x1x1 变为 batch x512 34. x = x.squeeze() 35. output = self.exit(x) 36. return output
注:这部分就是主要的模型结构的实现,定义了make_layer函数可以进行相同连续残差块的计算,然后调用之前定义好的残差块。