65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Python 和 Keras 实现卷积神经网络

starIconstarIconstarIconstarIconstarIcon

5.00/5 (7投票s)

2018年3月5日

CPOL

7分钟阅读

viewsIcon

23074

使用 Python 和 Keras 实现卷积神经网络

你有没有想过 Snapchat 是如何识别人脸的?自动驾驶汽车是如何知道道路在哪里的?你说得对,它们正在使用一种特殊类型的神经网络,用于计算机视觉——卷积神经网络。在上一篇文章中,我们有机会研究了它们的工作原理。我们涵盖了这些网络的层及其功能。基本上,卷积神经网络的附加层以标准神经网络可以处理的格式预处理图像。这样做的第一步是在输入图像上检测某些特征或属性。这是通过卷积层完成的。

此层使用滤波器检测低级特征(如边缘和曲线)以及更高级特征(如面部或手)。然后,卷积神经网络使用附加层来消除图像中的线性,这可能会导致过拟合。当消除线性后,将使用附加层来压缩图像并展平数据。最后,这些信息被传递到神经网络,在卷积神经网络的世界中称为全连接层。本文的目标是向您展示如何实现所有这些概念,因此会提供有关这些层的更多详细信息。

在我们深入探讨要解决的问题和代码本身之前,请务必设置好您的环境。与本系列所有之前的文章一样,我将使用 Python 3.6。此外,我正在使用 Anaconda 和 Spyder,但您可以使用任何您喜欢的 IDE。但是,重要的是要安装 Tensorflow 和 Keras。

MNIST 数据集

因此,在本文中,我们将教我们的网络如何识别图像中的数字。为此,我们将使用另一个著名的数据集——MNIST 数据集。作为其前身 NIST 的扩展,该数据集包含 60,000 个手写数字图像的训练集和 10,000 个图像的测试集。所有数字都经过大小归一化和居中。图像大小也已固定,因此图像数据预处理工作量最小。这就是为什么这个数据集如此受欢迎的原因。它被认为是卷积神经网络世界中的“Hello World”示例。

MNIST 数据集样本

此外,使用卷积神经网络,我们几乎可以获得与人类相同的结果。目前,记录由并行计算中心(乌克兰赫梅利尼茨基)保持。他们仅使用 5 个卷积神经网络的集成,就获得了 0.21% 的错误率。相当酷,不是吗?

导入库和数据

与本系列前面的文章一样,我们首先导入所有必要的库。其中一些会很熟悉,但有些我们会进一步解释。

import numpy as np 
from matplotlib import pyplot as plt
from keras.utils.np_utils import to_categorical 
from keras.models import Sequential 
from keras.layers.core import Dense, Dropout, Flatten 
from keras.layers import Conv2D, MaxPooling2D

如您所见,我们将使用 `numpy`,我们已经在前面的示例中用于多维数组和矩阵操作的库。此外,您可以看到我们正在使用 `Keras` 库中的一些功能,这些功能我们已经在本文章中使用过,但也有几个新的。`Sequential` 和 `Dense` 用于创建模型和标准层,即全连接层。

此外,我们可以看到我们从 `Keras` 中使用的一些新类。`Conv2D` 是我们将用于创建卷积层的类。`MaxPooling2D` 是用于池化层的类,`Flatten` 类用于展平层。我们还使用 `Keras utils` 中的 `to_categorical`。此类的作用是将向量(整数)转换为二进制类矩阵,即它用于 独热编码。最后,请注意我们将使用 `matplotlib` 来显示结果。

导入所有必要的库和类后,我们需要处理数据。幸运的是,Keras 提供了 MNIST 数据集,所以我们不需要下载它。如前所述,所有这些图像都已部分预处理。这意味着它们具有相同的大小,并且显示在它们上面的数字已正确定位。所以,让我们导入这个 `dataset` 并为我们的模型准备数据。

from keras.datasets import mnist

(X_train, y_train), (X_test, y_test) = mnist.load_data()

rows, cols = X_train[0].shape[0], X_train[0].shape[1] 
X_train = X_train.reshape(X_train.shape[0], rows, cols, 1) 
X_test = X_test.reshape(X_test.shape[0], rows, cols, 1) 

X_train = X_train.astype('float32')/255
X_test = X_test.astype('float32')/255

num_of_classes = len(set(y_train)) 
y_train = to_categorical(y_train, num_of_classes) 
y_test = to_categorical(y_test, num_of_classes) 

如您所见,我们从 `Keras datasets` 导入了 MNIST `dataset`。然后我们将数据加载到训练和测试矩阵中。之后,我们使用 `shape` 属性获取图像的维度,并重塑输入数据,使其表示单通道输入图像。基本上,我们只使用此图像的一个通道,而不是常规的三个(RGB)。这样做是为了简化此实现。然后我们对输入矩阵中的数据进行归一化。最后,我们使用 `to_categorical` 对输出矩阵进行编码。

模型创建

现在,数据已准备好,我们可以继续有趣的部分——创建模型。

model = Sequential() 

model.add(Conv2D(32, kernel_size=(3, 3), activation='rectifier', input_shape=(rows, cols, 1)))
model.add(Conv2D(64, kernel_size=(3, 3), activation='rectifier')) 
model.add(Conv2D(128, kernel_size=(3, 3), activation='rectifier')) 

model.add(Dropout(0.5)) 

model.add(MaxPooling2D(pool_size = (2, 2)))

model.add(Flatten())

model.add(Dense(128, activation='relu')) 
model.add(Dropout(0.5)) 
model.add(Dense(num_of_classes, activation='softmax'))

model.compile(loss= 'categorical_crossentropy', optimizer='adam', metrics=['accuracy']) 

当然,我们使用了 `Sequential`,并通过使用 `Conv2D` 类添加卷积层开始。如您所见,此类别使用了几个参数,因此让我们探讨一下它们。第一个参数定义了将使用的过滤器数量,即将被检测到的特征数量。通常的做法是从 32 开始,然后从那时起增加特征数量。这正是我们正在做的,在第一个卷积层中,我们检测 32 个特征,在第二个中检测 64 个,在第三个也是最后一个中检测 128 个特征。将使用的过滤器大小使用下一个参数 `kernel_size` 定义,我们选择了 3x3 的过滤器。

对于激活函数,我们使用 `整流器` 函数。这样,我们自动为每个卷积层添加了非线性级别。另一种实现此目的的方法(更高级一些)是使用 `keras.layers.advanced_activations` 中的 `LeakyReLU`。这不像标准整流器函数,但它不是将所有低于某个值的值压缩到 `0`,而是具有轻微的负斜率。如果您决定使用此方法,请注意您必须在 `Conv2D` 中使用线性激活。以下是该方法的示例:

from keras.layers.advanced_activations import LeakyReLU

model.add(Conv2D(32, kernel_size=(3, 3), activation='linear',input_shape=(rows, cols, 1)))
model.add(LeakyReLU(alpha=0.1))

我们稍微跑题了。让我们回到 `Conv2D` 及其参数。另一个非常重要的参数是 `input_shape`。使用此参数,我们定义了输入图像的维度。如前所述,我们只使用一个通道,这就是为什么 `input_shape` 的最终维度是 `1`。其他维度我们是从输入图像中获取的。

此外,我们还在模型中添加了其他层。Dropout 层帮助我们避免过拟合,之后我们使用 `MaxPooling2D` 类添加了池化层。该层显然使用 `max-pool` 算法,池化过滤器的大小为 2x2。池化层之后是展平层,再之后是全连接层。对于最终的全连接层,我们添加了一个包含两层的神经网络,为此我们使用了 `Dense` 类。最后,我们编译了模型,并使用了 Adam 优化器。

培训

非常好,我们的数据已经预处理,模型也已创建。让我们将它们合并在一起,并训练我们的模型。为此,我们使用已熟悉的 `fit` 函数。我们传递输入矩阵并定义 `batch_size` 和 epoch 数量。我们做的另一件事是定义 `validation_split`。此参数用于定义将用作验证数据的测试数据的比例。

基本上,模型会预留一部分训练数据,但它会在每个 epoch 结束时使用它来评估损失和其他模型指标。这与测试数据不同,因为我们在每个 epoch 后都使用它。

model.fit(X_train, y_train, batch_size=128, epochs=20, verbose=1, validation_split=0.2) 

score = model.evaluate(X_test, y_test, verbose=0) 

print('Accuracy:', score[1])

之后,我们的模型就训练好了。我们使用 `evaluate` 方法并向其传递测试集。在这里,我们将获得我们的卷积神经网络的准确性。

预测

我们还可以做的另一件事是收集我们的网络在测试 `dataset` 上的预测。通过这种方式,我们可以将预测结果与实际结果进行比较。为此,我们将使用 `predict` 方法。使用此方法,我们还可以对单个输入进行预测。

predictions = model.predict(X_test)

结果

让我们使用刚刚收集到的这些预测,为我们的实现画龙点睛。我们将显示预测数字和实际数字。并且我们将显示我们进行预测的图像。基本上,我们将为我们的实现制作漂亮的视觉表示,毕竟我们在这里正在处理图像。

plt.figure(figsize=(15, 15)) 
for i in range(10):    
    ax = plt.subplot(2, 10, i + 1)    
    plt.imshow(X_test[i, :, :, 0], cmap='gray')    
    plt.title("Digit: {}\nPredicted:    {}".format(np.argmax(y_test[i]), np.argmax(predictions[i])))    
    plt.axis('off') 
plt.show()

这里,我们使用 `pyplot` 显示了十张带有实际结果和我们预测的图像。这就是我们运行实现时的样子

我们运行了二十个 epoch,获得了 99.39% 的准确率。一点也不差。当然,总有改进的空间。

结论

卷积神经网络是一个非常有趣的子领域,也是计算机视觉领域最具影响力的创新之一。在这里,您可以看到如何实现这些网络的一个简单版本,以及如何使用它来检测 MNIST 数据集上的数字。

感谢阅读!

© . All rights reserved.