如果我们已知原始的图像数据,和这个图像经过某个卷积核计算后,得到的新的数据,那么如何计算出这个卷积核中的具体参数呢?
例如,已知输入图像是12*12的,经过某个3*3的卷积核,得到的输出数据是10*10的。
那么如何求出,这个卷积核中的9个参数呢?
可能有的同学知道,中间的这个卷积核,其实是一个拉普拉斯算子,也就是中间是8,周围是-1的,3乘3数组。
我们接下来要做的是,使用pytorch框架,通过梯度下降算法,基于输入和输出数据,训练出这个拉普拉斯卷积核。
在main函数中,声明img数组,保存输入图像。
因为我们需要生成输出数据,所以要提前设置一个拉普拉斯卷积核kernel,用作输出数据Y的生成。
然后调用函数functional.conv2d,计算img和kernel的卷积运算,得到输出图像,保存在Y中。
特别要注意的是,在后面训练过程中,我们只会使用img和Y,并不会使用kernel。
此处的kernel,只是用来计算输出图像Y的,而后面要根据img和Y,重新训练出kernel中的参数。
接着,构造一个卷积核weight。
这里要注意,卷积核是四维张量,前两个维度代表了卷积核的数量和输入通道数量,这里都是1。
后面的两个3,代表了卷积核的大小,是3乘3的。
定义学习速率lr和迭代次数num。
我们需要注意的是,如果学习速率lr过大,梯度下降的过程中就会出现溢出错误。
如果迭代次数不足,则无法收敛到最优解。
这里定义的学习速率是10的-7次方,迭代次数是10000,这些值都是经过实验得出的合适值。
然后进入卷积核的迭代循环。
在循环中,首先计算基于当前参数的预测值predict。
根据平方误差,计算预测值和真实值之间的损失值loss。
这里就是要训练出一组参数,使loss取得最小值。
换句话说,就是要找到使predict和Y相等的参数。
接着使用backward函数进行反向传播,计算出损失loss关于参数weight的梯度。
梯度会保存在weight.grad中。这里我们直接使用梯度下降算法,更新weight保存的数据。
最后调用zero_grad清空上一轮迭代的梯度。
为了便于调试,我们每迭代1000轮,就打印一次loss的值进行观察。
最后打印训练好的卷积核中的参数。
运行程序,我们会发现,经过10000轮的迭代,损失值loss会变得非常小。
最终就得到一个3乘3的卷积核:
而该卷积核中保存的数据,就非常接近原来设置的卷积核了。