添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
Keras深度学习——使用 Keras 构建卷积神经网络

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天, 点击查看活动详情

使用 Keras 构建卷积神经网络

为了进一步加深对卷积神经网络 ( Convolutional Neural Network , CNN ) 的理解,我们将使用 Keras 构建基于 CNN 的体系结构,并通过使用 Keras Numpy 从输入开始构建 CNN 前向传播过程获得输出,来增强我们对 CNN 的理解。

CNN 使用示例

我们首先定义一个输入和预期输出数据的简单示例来实现 CNN

  • 创建输入和输出数据集:
  • import numpy as np
    x_train = np.array([[[1,2,3,3],
                        [2,3,4,5],
                        [4,5,6,7],
                        [1,3,4,6]],
                        [[-1,2,3,-5],
                        [2,-2,4,5],
                        [-3,5,-4,9],
                        [-1,-3,-2,-5]]])
    y_train = np.array([0, 1])
    

    在以上代码中,我们创建了以下数据:输入全为正数得到的输出为 0,带有负值的输入则得到 1 作为输出。

  • 缩放输入数据集:
  • x_train = x_train / 9
    
  • 对输入数据集的形状进行整形,以使每个输入图像都以 (宽度 x 高度 x 通道数) 的格式表示:
  • x_train = x_train.reshape(x_train.shape[0], x_train.shape[1], x_train.shape[1], 1)
    
  • 准备数据后,构建模型架构,导入相关方法后实例化模型:
  • from keras.models import Sequential
    from keras.layers import Conv2D, Flatten, MaxPooling2D
    model = Sequential()
    
  • 接下来,我们将执行卷积操作:
  • model.add(Conv2D(1, (3, 3), input_shape=(4, 4, 1), activation='relu'))
    

    在上述代码中,我们对输入数据执行 2D 卷积 Conv2D,其中有 1 个大小为 3 x 3 的卷积核,由于这是实例化模型中的第一层,我们需要指定输入形状,为 (4, 4, 1);最后,我们在卷积的输出之上使用 ReLU 激活函数。由于我们没有对输入进行填充,因此输出的特征图大小将缩小,卷积运算输出的形状为 2 x 2 x 1

  • 接下来,我们将添加一个执行最大池化操作的网络层 MaxPooling2D,如下所示:
  • model.add(MaxPooling2D(pool_size=(2, 2)))
    

    我们在上一层获得的输出之上执行最大池化 (池化核大小为 2 x 2),这表示需要计算图像每个滑动窗口 2 x 2 部分中的最大值,得到池化运算输出的形状为 1 x 1 x 1

  • 接下来,展平池化层的输出:
  • model.add(Flatten())
    

    一旦执行了展平 Flatten 处理,展平层的操作十分简单——将多维的输入整形为一维数组,Flatten操作不影响第一维的 batch_size,在本例中,执行 Flatten 操作后,数据形状由 (batch_size, 1,1,1) 变为 (batch_size, 1)。之后就与在标准前馈神经网络中执行的过程非常相似,先连接到若干隐藏层,最后连接到输出层。在这里,我们使用 sigmoid 激活函数,并直接将展平层的输出连接到输出层:

    model.add(Dense(1, activation='sigmoid'))
    

    查看该模型的相关信息:

    model.summary()
    

    输出的模型简要信息如下:

    Model: "sequential"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #   
    =================================================================
    conv2d (Conv2D)              (None, 2, 2, 1)           10        
    _________________________________________________________________
    max_pooling2d (MaxPooling2D) (None, 1, 1, 1)           0         
    _________________________________________________________________
    flatten (Flatten)            (None, 1)                 0         
    _________________________________________________________________
    dense (Dense)                (None, 1)                 2         
    =================================================================
    Total params: 12
    Trainable params: 12
    Non-trainable params: 0
    _________________________________________________________________
    

    如上所示,卷积层中有 10 个参数,因为一个 3 x 3 的卷积核,其具有 9 个权重和 1 个偏置项。池化层和展平层没有任何参数,因为它们只需某个区域中提取最大值(最大池化 MaxPooling2D),或者展平前一层的输出(展平层 Flatten),因此不需要在其中一个权重进行修改的操作这些层。输出层具有两个参数,因为展平层只有一个输出,该输出连接到具有一个值的输出层,因此具有一个权重和一个偏置项来连接展平层和输出层。

  • 最后,编译并拟合模型:
  • model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['acc'])
    model.fit(x_train, y_train, epochs=50)
    

    在上述代码中,我们将损失指定为二进制交叉熵,因为输出结果是 10

    验证 CNN 输出

    在上一节中,我们已经拟合了模型,接下来,通过实现 CNN 的前向传播过程来验证从模型中获得的输出。

  • 首先,我们提取权重和偏置的相关信息:
  • print(model.get_weights())
    

    输出结果如下:

    [<tf.Variable 'conv2d/kernel:0' shape=(3, 3, 1, 1) dtype=float32, numpy=
    array([[[[ 0.1985341 ]],
            [[ 0.27599618]],
            [[ 0.20717546]]],
           [[[-0.41702896]],
            [[-0.21289168]],
            [[ 0.12980239]]],
           [[[-0.14379142]],
            [[ 0.55261314]],
            [[-0.49706236]]]], dtype=float32)>,
    <tf.Variable 'conv2d/bias:0' shape=(1,) dtype=float32, numpy=array([0.04198299], dtype=float32)>,
    <tf.Variable 'dense/kernel:0' shape=(1, 1) dtype=float32, numpy=array([[1.5805249]], dtype=float32)>,
    <tf.Variable 'dense/bias:0' shape=(1,) dtype=float32, numpy=array([-0.28272304], dtype=float32)>]
    

    可以看到,首先显示了卷积层的权重,然后是偏置,最后是输出层中的权重和偏置。卷积层中权重的形状为 (3, 3, 1, 1),因为卷积核的形状为 3 x 3 x 1 (形状中的前三个值),形状中的第四个值 1 用于在卷积层中指定卷积核的数量。如果我们指定 64 作为卷积中的卷积核数量,则权重的形状为 3 x 3 x 1 x 64,而如果对具有 3 个通道的图像执行卷积运算,则每个卷积核的形状将为 3 x 3 x 3

  • 使用 modelweights 属性提取各层的权重值:
  • print(model.weights)
    

    接下来,我们使用模型计算第一个输入 x_train[0] 的输出,以便我们可以通过前向传播查看 CNN 计算结果:

    print(model.predict(x_train[0].reshape(1,4,4,1)))
    # 输出如下
    # [[0.04299431]]
    

    以上代码对输入数据整形,同时将其传递给预测方法,因为模型希望接受输入的形状为 (None, 4, 4, 1),其中 None 用于指定批大小,可以是任何数字。我们运行的模型预测输出为 0.04299431

  • 接下来,我们通过模拟卷积过程进行验证,对输入数据执行卷积,输入图像的形状为 4 x 4,而卷积核的形状为 3 x 3,在代码中沿着行和列执行矩阵乘法(卷积):
  • sumprod = []
    for i in range(x_train[0].shape[0]-model.get_weights()[0].shape[0]+1):
        for j in range(x_train[0].shape[0]-model.get_weights()[0].shape[0]+1):
            img_subset = np.array(x_train[0,i:(i+3),j:(j+3),0])
            filter = model.get_weights()[0].reshape(3,3)
            val = np.sum(img_subset*filter) + model.get_weights()[1]
            sumprod.append(val)
    

    在以上代码中,我们初始化一个名为 sumprod 的空列表,用于存储卷积核与输入数据的每个子矩阵卷积的输出。

  • 整形 sumprod 的输出,以便将其传递到池化层:
  • sumprod = np.array(sumprod).reshape(2, 2, 1)
    
  • 在卷积的输出传递到池化层之前,先对其使用激活函数:
  • sumprod = np.where(sumprod>0, sumprod, 0)
    
  • 将卷积输出传递到池化层,根据以上模型定义,考虑到卷积的输出为 2 x 2,根据最大池化层的定义我们取输出中的最大值:
  • pooling_layer_output = np.max(sumprod)
    
  • 将池化层的输出连接到输出层,将池化层的输出乘以输出层中的权重,然后在输出层中加上偏置值:
  • intermediate_output_value = pooling_layer_output * model.get_weights()[2] + model.get_weights()[3]
    
  • 计算 Sigmoid 输出:
  • print(1/(1+np.exp(-intermediate_output_value)))
    

    以上操作的输出如下:

    [[0.42994314]]
    

    可以看到输出与我们使用 model.predict 方法获得的输出相同,从而加深了我们对 CNN 工作流程的了解。

    卷积神经网络基本概念详解