Caffe中如何配置固定预定义卷积核(如Sobel滤波器)
刚好做过类似的需求!在Caffe里用固定预定义卷积核(比如Sobel)替代可学习滤波器,有两种实用的方法,我给你一步步讲清楚:
方法1:预加载固定权重文件(最常用)
这种方法是先把你定义好的卷积核保存成Caffe的caffemodel格式,然后在网络结构里加载这个权重,配合lr_mult:0 decay_mult:0确保权重不会被更新。
步骤1:准备并保存预定义核
用Python代码生成你需要的卷积核,然后保存成caffemodel文件。比如你要10个3×3的核,其中包含Sobel x/y方向的核:
import caffe import numpy as np # 配置参数,和你的prototxt对应 num_output = 10 kernel_size = 3 in_channels = 1 # 输入数据的通道数,比如单通道灰度图就设1,RGB设3 # 初始化权重数组,形状是(num_output, in_channels, kernel_size, kernel_size) weights = np.zeros((num_output, in_channels, kernel_size, kernel_size)) # 填充Sobel x和y核 sobel_x = np.array([[1, 0, -1], [2, 0, -2], [1, 0, -1]]) sobel_y = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]]) weights[0, 0, :, :] = sobel_x weights[1, 0, :, :] = sobel_y # 剩下的8个核可以自定义,比如用其他边缘检测核或者全1矩阵,这里示例用递增的常量核 for i in range(2, num_output): weights[i, 0, :, :] = np.ones((3,3)) * (i+1)/10 # 创建临时网络结构来保存权重 temp_prototxt = """ layer { name: "conv1" type: "Convolution" bottom: "data" top: "conv1" param { lr_mult: 0 decay_mult: 0 } convolution_param { num_output: 10 kernel_size: 3 stride: 2 bias_term: false # 如果不需要偏置就设false,需要的话保留并初始化偏置 } } """ with open('temp_conv.prototxt', 'w') as f: f.write(temp_prototxt) # 加载临时网络,赋值权重并保存 net = caffe.Net('temp_conv.prototxt', caffe.TEST) net.params['conv1'][0].data[...] = weights # 如果需要偏置,添加这一行:net.params['conv1'][1].data[...] = np.zeros(num_output) net.save('fixed_conv_weights.caffemodel')
步骤2:修改你的原始prototxt加载权重
在你的卷积层里添加weights字段,指定刚才保存的caffemodel路径:
layer { name: "conv1" type: "Convolution" bottom: "data" top: "conv1" param { lr_mult: 0 decay_mult: 0 } # 添加这行加载预定义权重 weights { filename: "fixed_conv_weights.caffemodel" } convolution_param { num_output: 10 kernel_size: 3 stride: 2 bias_term: false # 和临时prototxt保持一致 # 这里的weight_filler随便写就行,会被加载的权重覆盖 weight_filler { type: "constant" } } }
方法2:用Python层直接实现(更灵活)
如果不想单独保存权重文件,可以用Caffe的Python层直接在代码里定义卷积核并执行卷积,适合快速迭代自定义核的场景。
步骤1:修改prototxt定义Python层
替换原来的Convolution层为Python层,把核的参数通过param_str传入:
layer { name: "fixed_conv" type: "Python" bottom: "data" top: "conv1" python_param { module: "fixed_convolution" layer: "FixedConvolutionLayer" # 把每个核展平成一维数组,比如Sobel x展平后是[1,0,-1,2,0,-2,1,0,-1] param_str: '{"num_output":10, "kernel_size":3, "stride":2, "kernels": [[1,0,-1,2,0,-2,1,0,-1], [1,2,1,0,0,0,-1,-2,-1], [1,1,1,1,1,1,1,1,1], ...]}' } }
步骤2:编写Python层代码
创建fixed_convolution.py文件,实现自定义的固定卷积层:
import caffe import numpy as np from scipy.ndimage import convolve class FixedConvolutionLayer(caffe.Layer): def setup(self, bottom, top): # 解析传入的参数 params = eval(self.param_str) self.num_output = params['num_output'] self.kernel_size = params['kernel_size'] self.stride = params['stride'] # 把展平的核恢复成三维形状 self.kernels = [np.array(k).reshape((self.kernel_size, self.kernel_size)) for k in params['kernels']] assert len(self.kernels) == self.num_output, "核的数量必须和num_output一致" def reshape(self, bottom, top): # 计算输出特征图的尺寸 N, C, H, W = bottom[0].data.shape H_out = (H - self.kernel_size) // self.stride + 1 W_out = (W - self.kernel_size) // self.stride + 1 top[0].reshape(N, self.num_output, H_out, W_out) def forward(self, bottom, top): data = bottom[0].data output = np.zeros(top[0].shape) # 对每个样本、每个通道、每个核执行卷积 for n in range(data.shape[0]): for c in range(data.shape[1]): for k_idx in range(self.num_output): # 用scipy的convolve实现带步长的卷积 output[n, k_idx, :, :] = convolve(data[n, c, :, :], self.kernels[k_idx], mode='constant')[::self.stride, ::self.stride] top[0].data[...] = output def backward(self, top, propagate_down, bottom): # 权重固定,不需要计算梯度,把输入梯度设为0 if propagate_down[0]: bottom[0].diff[...] = 0
注意事项
- 如果输入是多通道(比如RGB),方法1里的权重形状要改成
(num_output, 3, 3, 3),每个输出核对应3个输入通道的核;方法2里也要在forward里处理多通道的卷积逻辑。 - 如果需要偏置,方法1里要初始化偏置并保存,方法2里要在param_str里添加偏置参数,在forward里加上偏置项。
- 一定要确保
param { lr_mult: 0 decay_mult: 0 }(方法1)或者backward里不计算权重梯度(方法2),保证核不会被更新。
内容的提问来源于stack exchange,提问作者Nima
相关产品推荐
相关产品推荐

