Python-OpenCV深度学习入门示例详解

0. 前言

深度学习已经成为机器学习中最受欢迎和发展最快的领域。自 2012 年深度学习性能超越机器学习等传统方法以来,深度学习架构开始快速应用于包括计算机视觉在内的众多领域。深度学习的常见应用包括语音识别、图像识别、自然语言处理、推荐系统等等。大多数现代深度学习架构都基于人工神经网络,深度学习中的“深”是指架构的层数。在本文中,首先介绍传统机器学习方法与深度学习间的差异,然后将介绍图像分类和对象检测中常见的深度学习架构,最后,将介绍深度学习 Python 库 Keras,并通过实战来推开深度学习的大门。

1. 计算机视觉中的深度学习简介

深度学习推动了计算机视觉领域的深刻变革,我们首先解释深度学习中的关键概念,以便更好的了解深度学习的广袤世界。

1.1 深度学习的特点

深度学习在许多计算机视觉任务中的性能超越了传统的机器学习方法,但在选择何种方法完成特定的计算任务时,应该明确深度学习与传统的机器学习方法之间的区别,以选择合适的方法:

  • 传统的机器学习算法大多可以在低端机器上运行,而深度学习算法需要较高的算力才能正确训练,通常这些计算可以使用 GPU 进行优化并行计算
  • 当对特征工程缺乏领域理解时,深度学习技术将是首选方法,这是由于在深度学习中,寻找相关特征的任务是算法的一部分,它通过减少问题的特征工程来实现自动化。特征工程是将领域知识应用于创建特征检测器和提取器的过程,目的是降低数据的复杂性,使传统的机器学习方法能够正确学习。因此,传统机器学习算法的性能取决于识别和提取特征的准确程度,而深度学习技术试图从数据中自动提取高级特征。
  • 传统机器学习和深度学习都能够处理海量数据集。但两种方法之间的主要区别在于随着数据规模的增加其性能的变化程度。例如,在处理小数据集时,深度学习算法难以在数据中找到映射关系,因此可能表现不佳,因为深度学习通常需要大量数据来调整其内部参数。根据经验法,如果数据集很大,深度学习会优于其他技术,而当数据集很小时,传统的机器学习算法更可取。

可以使用下图来总结机器学习与深度学习的主要区别:

机器学习与深度学习的区别

由上图可知,机器学习与深度学习的关键选择要点如下:

  • 计算资源(深度学习<——高算力计算机;机器学习<——低算力计算机)
  • 特征工程(深度学习<——特征提取和目标任务在同一步骤中;机器学习<——特征提取和目标任务在不同步骤中)
  • 数据集大小(深度学习<——大型或超大型数据集;机器学习<——小型或中型数据集)

1.2 深度学习大爆发

深度学习的概念可以追溯至 1986 年,但直到 2012 年深度学习性能超越传统机器学习时,才出现了深度学习的大爆发。 ImageNet 是一个大型视觉数据库,包含超过 1400 万张带有标签的高分辨率图像,包含 20,000 多个类别。因此,2012 年 AlexNet 架构在解决 ImageNet 大规模视觉识别挑战赛 (ILSVRC) 上的突破性进展通常被认为是深度学习大爆发的开始。

2. 用于图像分类的深度学习简介

继 AlexNet 在 ILSVRC 比赛中取得成功之后,更多深度学习架构开始被应用于 ImageNet 挑战赛中,下图显示了在 ImageNet 挑战赛中的最相关深度学习模型的准确率和模型的参数量:

性能对比

接下来,对深度学习模型架构进行介绍,以了解它们的发展脉络及其关键点:

AlexNet:AlexNet 是 LSVRC-2012 的获胜者,是一种简单但功能强大的网络架构,其通过堆叠卷积层和池化层,最后顶部使用全连接层。

VGG:VGGNet 由 Visual Geometry Group (VGG) 提出,在 LSVRC-2014 中排名第二,与 AlexNet 相比其在整个网络中仅使用 3 x 3 卷积核,而不是使用大尺寸卷积核(例如 7 x 7 和 11 x 11),主要贡献在于它表明网络的深度是在卷积神经网络中实现更好的识别或分类准确率的关键。它的主要缺点是训练速度非常慢,并且其网络架构权重参数量非常大(可以从上图看出)。

GoogLeNet/Inception V1:GoogLeNet (也称 Inception V1) 是 LSVRC-2014 的获胜者,其 top-5 错误率仅为 6.67%,非常接近人类水平的表现。这种架构比 VGGNet 更深,但由于其使用 9 个并行的 Inception 模块,Inception 模块基于几个非常小的卷积,极大的减少了参数数量。

ResNet:ResNets 是 LSVRC-2015 的获胜者,是迄今为止最深的网络,其包含 153 个卷积层 top-5 分类错误率仅为 4.9% (使深度学习模型准确率首次高于人类)。该架构使用跳跃连接,可实现增量学习修改。

Inception V3:Inception V2 在 Inception 模块中引入了批归一化,Inception V3 架构包含了分解思想,其目标是在不降低网络效率的情况下减少参数的数量。

Inception V4:Inception V4 具有比 Inception-V3 更统一的简化架构和更多的 Inception 模块,在 LSVRC 上达到了 80.2% 的 top-1 准确率和 95.2% 的 top-5 准确率。

3. 用于目标检测的深度学习简介

目标检测是深度学习中的一个热门话题,其用于识别和定位图像中的多个对象。目标检测算法通常使用以下三个数据集进行基准测试:1) PASCAL Visual Object (PASCAL VOC) 数据集,包含 20 个类别的 10,000 张图像,数据集中包含目标的边界框;2) ImageNet 在 2013 年发布了一个目标检测数据集,它由大约 500,000 张图像和 200 个类别组成;3) Common Objects in Context (COCO) 是一个大规模的对象检测、分割数据集,其包括 328,000 张图像以及 250 万个标记目标。为了评估目标检测算法,通常使用平均精度均值 (mean Average Precision, mAP),用于目标检测深度学习算法:

R-CNN:Region-based Convolutional Network (R-CNN) 是最早使用卷积神经网络进行目标检测的算法之一,与基于 HOG 特征的系统相比,卷积神经网络可以带来更高的对象检测性能。该算法可以分解为以下三个步骤:

  1. 创建一组候选区域
  2. 对每个候选区域通过基于 AlexNet 的模型执行前向传递以提取特征向量
  3. 潜在对象通过 SVM 分类器预测类别,利用线性回归器预测边界框的坐标

Fast R-CNN:Fast Region-based Convolutional Network (Fast R-CNN) 是对 R-CNN的改进,以有效地对目标提议进行分类。此外,Fast R-CNN 采用多项创新来提高训练和测试速度,同时提高检测精度。

Faster R-CNN:Faster R-CNN 是对 Fast R-CNN 的改进,它引入了候选区域网络 (region proposal network, RPN),与检测网络共享图像的卷积特征。

R-FCN:Region-based Fully Convolutional Network (R-FCN) 是一个只有卷积层的框架,以实现准确高效的目标检测。

YOLO: You only look once (YOLO) 可以在一个步骤中同时预测边界框和类别概率。与其他深度学习检测器相比,YOLO 产生更多的定位误差,但其产生假阳性的概率较小。

SSD:Single Shot MultiBox Detector (SSD) 同样通过单一端到端卷积神经网络架构同时预测边界框和类别概率。

YOLO V2: YOLO V2 是 YOLO 的改进版本,专注于提高准确性,同时是一个实时快速检测器。

NASNet: NASNet 的作者介绍了神经网络搜索,通过使用循环神经网络来组成神经网络架构,NASNet 学习模型模型架构的同时提高准确性。

Mask R-CNN:Mask Region-based Convolutional Network (Mask R-CNN) 是 Faster R-CNN 模型的改进,它为边界框检测添加了一个并行分支,目的是预测目标掩码。目标掩码是它在图像中按像素进行的分割,允许进行目标实例分割。

YOLO V3:YOLO V3 是以 YOLO V1 和 YOLO V2 为基础调整了网络结构;利用多尺度特征进行对象检测。

YOLO V4:YOLO-V4 算法是在原有 YOLO 目标检测架构的基础上,采用了 CNN 领域中优秀的优化策略,从数据处理、主干网络、网络训练、激活函数、损失函数等各个方面都有着不同程度的优化。

4. 深度学习框架 keras 介绍与使用

在本节中,我们将通过两个简单示例来一窥深度学习的神秘面纱。在第一个示例中,构造输入数据来解决线性回归问题;在第二个示例中,使用 MNIST 数据集对手写数字进行分类。

4.1 keras 库简介与安装

Keras 是用 Python 编写的开源高级神经网络 API,它能够在 TensorFlow、或 Theano 之上运行,其最大的特点是能够实现快速实验,因此本文利用它来进行深度学习实战。

要安装 Keras,首先进行安装:

pip install keras

4.2 使用 keras 实现线性回归模型

首先创建用于训练/测试算法的数据,如下所示:

# 产生一百个随机数据点作为训练数据
Number = 100
x = np.linspace(0, Number, Number)
y = 3 * np.linspace(0, Number, Number) + np.random.uniform(-12, 12, Number)

接下来创建模型:

def create_model():
    # 创建 Sequential 模型
    model = Sequential()
    # 使用具有线性激活函数的全连接层
    model.add(Dense(input_dim=1, units=1, activation="linear", kernel_initializer="uniform"))
    # 使用均方差(mse)作为损失函数,Adam作为优化器编译模型 
    model.compile(loss="mse", optimizer=Adam(lr=0.1))
    return model

使用 Keras 时,最简单的模型类型是 Sequential 模型,可以将其视为网络层的线性堆栈,对于更复杂的架构,可以使用 Keras 函数式 API,以创建任意网络架构。

作为简单示例,此处使用 Sequential 模型,然后利用 model.add() 方法堆叠层来构建模型。在此示例中,使用了具有线性激活函数的单个全连接层。定义模型之后,需要使用损失函数与优化器编译模型,示例中使用均方差 (mean squared error, MSE) 作为损失函数,使用 Adam 作为优化器并设置学习率为 0.1。

编译模型完成后,就可以使用 model.fit() 方法使用训练数据的训练模型:

linear_reg_model.fit(x, y, epochs=100, validation_split=0.2, verbose=2)

训练后,就可以获得可学习参数 w 和 b,这些值将用于接下来的:

def get_weights(model):
    w = model.get_weights()[0][0][0]
    b = model.get_weights()[1][0]
    return w, b
w_final, b_final = get_weights(linear_reg_model)

接下来,我们可以用以下方式进行预测:

predictions = w_final * x + b_final

还可以保存模型:

linear_reg_model.save_weights("my_model.h5")

最后可以将训练数据集和训练完成后线性模型进行可视化:

plt.subplot(1, 2, 1)
plt.plot(x, y, "ro", label="Original data")
plt.xlabel("x")
plt.ylabel("y")
plt.title("Training Data")

plt.subplot(1, 2, 2)
plt.plot(x, y, "ro", label="Original data")
plt.plot(x, predictions, label="Fitted line")
plt.xlabel("x")
plt.ylabel("y")
plt.title("Linear Regression Result", fontsize=10)
plt.legend()
plt.show()

线性回归

如上图所示,可以看到左侧图像显示的是训练数据,右侧图像显示了线性回归模型对应的拟合线。

如果我们已经拥有训练完成的模型权重文件,就可以直接加载预先训练的模型来进行预测:

# 加载模型
linear_reg_model.load_weights("my_model.h5")
# 构建测试数据集
M = 3
new_x = np.linspace(Number + 1, Number + 10, M)
# 使用模型进行预测
new_predictions = linear_reg_model.predict(new_x)

程序的运行结果如下图所示:

线性回归预测

4.3 使用 keras 进行手写数字识别

接下来,我们使用 Keras 识别手写数字,与第一个示例相同,首先需要构建模型:

def create_model():
    model = Sequential()
    model.add(Dense(units=128, activation="relu", input_shape=(784,)))
    model.add(Dense(units=128, activation="relu"))
    model.add(Dense(units=64, activation="relu"))
    model.add(Dense(units=10, activation="softmax"))
    # 使用分类交叉熵(categorical_crossentropy)作为损失函数和随机梯度下降作为优化器编译模型
    model.compile(optimizer=SGD(0.001), loss="categorical_crossentropy", metrics=["acc"])

    return model

使用 categorical_crossentropy 损失函数编译模型,该损失函数非常适合比较两个概率分布,使用随机梯度下降 (stochastic gradient descent, SGD) 作为优化器。

接下来加载 MNIST 数据集:

(train_x, train_y), (test_x, test_y) = mnist.load_data()

此外,由于我们使用的是全连接层,因此必须对加载的数据进行整形以输入网络:

train_x = train_x.reshape(60000, 784)
test_x = test_x.reshape(10000, 784)
train_y = keras.utils.to_categorical(train_y, 10)
test_y = keras.utils.to_categorical(test_y, 10)

创建模型完成后,就可以训练模型,并保存创建的模型,也可以评估模型在测试数据集上的表现:

# 模型创建
model = create_model()
# 模型训练
model.fit(train_x, train_y, batch_size=32, epochs=10, verbose=1)
# 模型保存
model.save("mnist-model.h5")
# 评估模型在测试数据集上的表现
accuracy = model.evaluate(x=test_x, y=test_y, batch_size=32)
# 打印准确率
print("Accuracy: ", accuracy[1])

接下来,可以使用训练完成的模型来预测图像中的手写数字:

def load_digit(image_name):
    gray = cv2.imread(image_name, cv2.IMREAD_GRAYSCALE)
    gray = cv2.resize(gray, (28, 28))
    gray = gray.reshape((1, 784))
    return gray
# 加载图片
test_digit_0 = load_digit("digit_0.png")
test_digit_1 = load_digit("digit_1.png")
test_digit_2 = load_digit("digit_2.png")
test_digit_3 = load_digit("digit_3.png")
imgs = np.array([test_digit_0, test_digit_1, test_digit_2, test_digit_3])
imgs = imgs.reshape(4, 784)
# 预测加载图像
prediction_class = model.predict_classes(imgs)
# 打印预测结果
print("Class: ", prediction_class)

加载的四张图像如下图所示:

图像加载

使用经过训练的模型来预测这些图像,得到的输出如下:

Class: [0 1 2 3]

小结

在本节中,使用流行的库深度学习库 Keras 对深度学习进行了介绍。我们首先概述了用于图像分类和目标检测的深度学习架构。然后,我们介绍了 Keras,并通过示例训练了简单线性回归模型,介绍了如何利用 Keras 训练全连接网络识别手写数字。

到此这篇关于Python-OpenCV深度学习入门示例详解的文章就介绍到这了,更多相关Python OpenCV深度学习内容请搜索云海天教程以前的文章或继续浏览下面的相关文章希望大家以后多多支持云海天教程!