6.2图像卷积
6.2.1互相关运算
#二维互相关运算输出 import numpy import torch def corr2d(X,K): h,w=K.shape Y=torch.zeros((X.shape[0]-h+1,X.shape[1]-w+1)) for i in range(Y.shape[0]): for j in range(Y.shape[1]): Y[i,j]=(X[i:i+h,j:j+w]*K).sum() return Y X=numpy.array([[0.0,1.0,2.0], [3.0,4.0,5.0], [6.0,7.0,8.0]]) K=numpy.array([[0.0,1.0], [2.0,3.0]]) print(corr2d(X,K)) #实现卷积层 import torch.nn as nn class Conv2D(nn.Module): def __init__(self,kernel_size,**kwargs): super().__init__(**kwargs) self.weight=nn.Parameter(torch.rand(kernel_size)) self.bias=nn.Parameter(torch.zeros(1)) def forward(self,x): return corr2d(x,self.weight.data())+self.bias.data() #图像中目标的边缘检测,找到像素变化的位置,来检测图像中不同颜色的边缘(寻找黑白边缘) #6×8像素的黑白图像,中间四列为黑色,其余像素为白色 X=torch.ones([6,8]).float() X[:,2:6]=0 print(X) #卷积核:1×2,互相关运算,水平相邻两元素相同,则输出为0,否则输出为非0 K=numpy.array([[1.0,-1.0]]) #互相关运算,输出Y中的1代表从白色到黑色的边缘,-1代表从黑色到白色的边缘,其他情况的输出为0 Y=corr2d(X,K) print(Y) #输入的二维图像转置,再进行互相关运算。垂直边缘消失(这个卷积核K只可以检测垂直边缘,无法检测水平边缘) #A=corr2d(torch.transpose(torch.tensor(X),dim0=0,dim1=1),K)#转换数据类型tensor, #print(A) #6.2.4学习卷积核 ''' 更复杂数值的卷积核+连续的卷积层:由’输入-输出‘对来学习由X生成Y的卷积核 构造一个卷积层,并将其卷积核初始化为随机张量,接下来,在每次的迭代中,我们比较Y与卷积层输出的平方误差,然后计算梯度来更新卷积核。 ''' #构造一个二维卷积层,具有1个输出通道和形状为(1,2)的卷积核 conv2d=nn.Conv2d(1,kernel_size=(1,2),bias=False,out_channels=1)#使用内置的二维卷积层,忽略偏置 #二维卷积层使用四维输入和输出格式(批量大小,通道,高度,宽度) X=X.reshape(1,1,6,8) Y=Y.reshape(1,1,6,7) lr=3e-2#学习率 for i in range(10): Y_hat=conv2d(X) l = (Y_hat - Y) ** 2 conv2d.zero_grad() l.sum().backward() #迭代卷积核 conv2d.weight.data[:] -= lr * conv2d.weight.grad if (i + 1) % 2 == 0: print(f'epoch{i + 1},loss {float(l.sum()):3f}') #l.requires_grad=True#RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn #l.backward(torch.ones_like(l))#RuntimeError: grad can be implicitly created only for scalar outputs #卷积核的权重张量(卷积核权重接近之前定义的卷积核) print(conv2d.weight.data.reshape(1,2))#TypeError: 'Tensor' object is not callable,解决办法:去掉()
6.3填充和步幅
#错误:TypeError: can only concatenate tuple (not "builtin_function_or_method") tuple,解决办法:将形状大小shape()函数改为view() #填充 import torch import torch.nn as nn def comp_conv2d(conv2d,X): #view()调整size,相当于reshape,resize #(1,1)批量大下和通道数 X=X.view((1,1)+X.shape)#view()是把原先tensor中的数据进行排列,排成一行,然后根据所给的view()中的参数从一行中按顺序选择组成最终的tensor。 Y=conv2d(X) return Y.view(Y.shape[2:]) #每边都填充了1行1列,相当于总共添加了2行或2列 conv2d=nn.Conv2d(1,kernel_size=3,padding=1,out_channels=1) X=torch.rand(8,8) print(comp_conv2d(conv2d,X).shape)#torch.Size([8, 8]) #卷积核的高度和宽度不同,填充不同的高度和宽度,使输出和输入具有相同的高度和宽度 conv2d=nn.Conv2d(1,kernel_size=(5,3),padding=(2,1),out_channels=1) print(comp_conv2d(conv2d,X).shape)#torch.Size([8, 8]) #步幅2 conv2d=nn.Conv2d(1,kernel_size=3,padding=1,stride=2,out_channels=1) print(comp_conv2d(conv2d,X).shape)#torch.Size([4, 4]) #步幅,垂直步幅3,水平步幅4 conv2d=nn.Conv2d(1,kernel_size=(3,5),padding=(0,1),stride=(3,4),out_channels=1) print(comp_conv2d(conv2d,X).shape)#torch.Size([2, 2])
6.4多输入多输出通道
#多输入通道互相关运算 import torch import torch.nn as nn def corr2d_multi_in(X,K): return sum(corr2d(x,k)for x,k in zip(X,K)) X=numpy.array([[[0.0,1.0,2.0], [3.0,4.0,5.0], [6.0,7.0,8.0]], [[1.0,2.0,3.0], [4.0,5.0,6.0], [7.0,8.0,9.0]]]) K=numpy.array([[[0.0,1.0], [2.0,3.0]], [[1.0,2.0], [3.0,4.0]]]) print(corr2d_multi_in(X,K)) #多通道输出的互相关函数 def corr2d_multi_in_out(X,K): #迭代K的第0个维度,每次都对输入X执行互相关运算,最后将所有结果都叠加在一起 return numpy.stack([corr2d_multi_in(X,k)for k in K],0) K=numpy.stack((K,K+1,K+2),0)#开辟一个新的维度,原始的维度在新的维度上拼接起来 print(K.shape)#(3, 2, 2, 2) print(corr2d_multi_in_out(X,K))#输出包含3个通道