Bootstrap

第七章:卷积神经网络(CNN)并进行梯度检查与可视化

在深度学习领域,卷积神经网络(CNN)是处理图像数据的强大工具。本文将详细介绍如何使用Python实现一个简单的卷积神经网络,并通过梯度检查来验证反向传播的正确性。此外,我们还将展示如何可视化卷积层的滤波器,并应用这些滤波器到图像上,帮助读者更好地理解CNN的工作原理。

配套代码资源:https://download.csdn.net/download/weixin_74773078/90234673

1. 实现简单的卷积神经网络

卷积神经网络(CNN)是专门用于处理图像数据的深度学习模型。它通过卷积层提取图像的局部特征,并通过池化层降低特征图的维度,最后通过全连接层进行分类。我们实现了一个简单的卷积神经网络(SimpleConvNet),其结构如下:

  • 卷积层:使用卷积核提取图像的局部特征。

  • ReLU激活函数:引入非线性,增强模型的表达能力。

  • 池化层:通过最大池化操作降低特征图的维度,减少计算量。

  • 全连接层:将特征图展平后输入全连接层,最终输出分类结果。

以下是网络的核心代码:

class SimpleConvNet:
    def __init__(self, input_dim=(1, 28, 28),
                 conv_param={'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},
                 hidden_size=100, output_size=10, weight_init_std=0.01):
        # 初始化网络参数
        self.params = {}
        # 构建网络层
        self.layers = OrderedDict()
        self.layers['Conv1'] = Convolution(self.params['W1'], self.params['b1'],
                                           conv_param['stride'], conv_param['pad'])
        self.layers['Relu1'] = Relu()
        self.layers['Conv2'] = Convolution(self.params['W2'], self.params['b2'],
                                           filter_stride2, filter_pad2)
        self.layers['Relu2'] = Relu()
        self.layers['Pool1'] = Pooling(pool_h=2, pool_w=2, stride=2)
        self.layers['Affine1'] = Affine(self.params['W3'], self.params['b3'])
        self.layers['Relu3'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W4'], self.params['b4'])
        self.last_layer = SoftmaxWithLoss()

在这个实现中,我们定义了两层卷积层、两层ReLU激活函数、一层池化层和两层全连接层。通过这种结构,网络能够有效地提取图像的特征并进行分类。

2. 梯度检查

在实现反向传播算法时,确保其正确性非常重要。为了验证反向传播的实现是否正确,我们可以通过数值微分来计算梯度,并将其与反向传播计算的梯度进行比较。如果两者接近,说明反向传播的实现是正确的。

以下是梯度检查的代码:

network = SimpleConvNet(input_dim=(1,10, 10), 
                        conv_param = {'filter_num':10, 'filter_size':3, 'pad':0, 'stride':1},
                        hidden_size=10, output_size=10, weight_init_std=0.01)

X = np.random.rand(100).reshape((1, 1, 10, 10))
T = np.array([1]).reshape((1,1))

grad_num = network.numerical_gradient(X, T)
grad = network.gradient(X, T)

for key, val in grad_num.items():
    print(key, np.abs(grad_num[key] - grad[key]).mean())

 

通过比较数值微分和反向传播计算的梯度,我们可以验证反向传播的实现是否正确。如果两者的差异非常小,说明反向传播的实现是正确的。

3. 训练卷积神经网络

接下来,我们使用MNIST数据集来训练这个卷积神经网络。MNIST数据集包含60,000张手写数字图像,每张图像的尺寸为28x28。我们使用这些数据来训练网络,使其能够正确分类手写数字。

以下是训练代码:

(x_train, t_train), (x_test, t_test) = load_mnist(flatten=False)

max_epochs = 50

network = SimpleConvNet(input_dim=(1,28,28), 
                        conv_param = {'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},
                        hidden_size=100, output_size=10, weight_init_std=0.01)
                        
trainer = Trainer(network, x_train, t_train, x_test, t_test,
                  epochs=max_epochs, mini_batch_size=100,
                  optimizer='Adam', optimizer_param={'lr': 0.001},
                  evaluate_sample_num_per_epoch=1000)
trainer.train()

network.save_params("params.pkl")

在训练过程中,我们使用了Adam优化器来更新网络参数,并设置了学习率为0.001。训练完成后,我们可以保存模型的参数,以便后续使用。

4. 可视化卷积滤波器

卷积层的滤波器是CNN的核心组成部分,它们负责从图像中提取特征。通过可视化这些滤波器,我们可以直观地看到网络学习到的特征。

以下是可视化代码:

def filter_show(filters, nx=8, margin=3, scale=10):
    FN, C, FH, FW = filters.shape
    ny = int(np.ceil(FN / nx))

    fig = plt.figure()
    fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)

    for i in range(FN):
        ax = fig.add_subplot(ny, nx, i+1, xticks=[], yticks=[])
        ax.imshow(filters[i, 0], cmap=plt.cm.gray_r, interpolation='nearest')
    plt.show()

network = SimpleConvNet()
filter_show(network.params['W1'])

network.load_params("params.pkl")
filter_show(network.params['W1'])

通过可视化,我们可以看到卷积层在训练前后的滤波器变化。训练前的滤波器是随机初始化的,而训练后的滤波器则学习到了图像中的有用特征。

5. 应用卷积滤波器到图像

为了进一步理解卷积滤波器的作用,我们可以将训练好的卷积滤波器应用到图像上,观察滤波器的效果。以下是应用滤波器的代码:

img = imread('../dataset/lena_gray.png')
img = img.reshape(1, 1, *img.shape)

fig = plt.figure()

for i in range(16):
    w = network.params['W1'][i]
    b = 0
    conv_layer = Convolution(w, b) 
    out = conv_layer.forward(img)
    out = out.reshape(out.shape[2], out.shape[3])
    
    ax = fig.add_subplot(4, 4, i+1, xticks=[], yticks=[])
    ax.imshow(out, cmap=plt.cm.gray_r, interpolation='nearest')

plt.show()

 通过将滤波器应用到图像上,我们可以直观地看到每个滤波器提取的特征。例如,某些滤波器可能对边缘敏感,而另一些滤波器可能对纹理敏感。这些特征提取过程是CNN能够有效处理图像数据的关键。

 

;