一 、卷积神经网络是什么
(1)印象
今天说的CNN,并不是我们熟知的美国有线电视新闻网。
那什么是CNN呢? Convolutional Neural Networks, CNN)简单来说,就是用一个筛子来筛面粉的。
筛子就是卷积核,面粉就是被筛的信息,晒出来的面粉就是新生成或者说被抽离的特征。
有很多把筛子组成的网络一起来筛信息,就组成了一个卷积神经网络。
(2)概念:
卷积神经网络(CNN)是一种模仿人类视觉系统的算法,常用于图像和视频处理。它的基本思想是通过多个“卷积层”逐步提取图像的特征。例如,在处理一张照片时,网络首先识别图像的基本边缘或颜色,再逐渐发现更复杂的结构,如物体的形状、特征。CNN 的优势在于它能自动学习图像中的重要信息,而不需要手动提取特征,最终帮助模型做出准确的预测,比如识别猫狗图片。
(3)过程
想象你正在拿一张照片,想要识别照片中的物体,比如一只猫。你没有直接看整张照片,而是通过放大镜来查看图片中的小部分。这个放大镜就像是CNN中的卷积核(filter),它会对图片的每个小区域进行扫描,提取局部特征(比如边缘、颜色变化等)。每次扫描后,放大镜会帮助你得出一个小结论,比如“这里有一条边缘”,然后你将这些小结论组合起来,逐渐构建出对整个物体的认知。
-
第一步:局部扫描(卷积层) 想象你用一个小的放大镜(卷积核)在图片上逐个小块地扫描。每扫描一个小块,你就得出一个关于这个小区域的特征。比如,你发现图片某一部分有条清晰的边缘,或者有个圆形的区域。
-
第二步:信息汇总(池化层) 扫描过程中,可能会有一些冗余的细节,池化层就像是用一个筛网,筛掉一些不必要的细节,只保留最重要的信息。就好像你在拿到放大镜的结论后,挑选出最显著的特征,而忽略一些不重要的东西。
-
第三步:组合特征(全连接层) 当你逐渐识别了更多的细节,比如这边是圆形,那边是有条边缘,最终你把这些局部特征组合起来,形成对整个物体的整体理解。比如,结合猫的耳朵、尾巴、眼睛的特征,你能识别出这是一只猫。
-
最后:做出判断(输出层) 在完成了特征提取和组合后,你最终就能做出一个判断:这张照片上的物体是“猫”,还是“狗”,或者是其他什么。
二 、卷积的实现
(1)卷积的比喻:图像处理中的“滑动窗口”
想象一下你手中有一个大图,里面充满了各种颜色和细节,而你只对其中一小部分感兴趣。为了提取这部分的信息,你用一个小窗户(称为卷积核或滤波器)在大图上逐步滑动,每次只看到这个窗户覆盖的区域。然后,你用这个窗户内的信息来计算一个数值,通常是对区域的某种数学运算(如加权求和)。最终,当窗户滑过整个图像时,你会得到一个新的图,这个新图的每个点代表了原始图像某部分的信息。
(2)数学上如何理解卷积?
卷积是一种数学运算,用来结合两个函数(或数组)以产生第三个函数。在图像处理中,卷积通常是用一个小的滤波器(卷积核)对整个图像进行滑动(遍历)。每当卷积核滑动到图像的一个小区域时,它会对这个区域和卷积核内的数值进行运算(通常是逐元素相乘后求和),然后得出一个结果。
具体步骤如下:
- 输入图像:假设图像是一个矩阵,像素值为每个元素。
- 卷积核:卷积核(filter)也是一个矩阵,比如一个3x3的矩阵。
- 滑动过程:卷积核在输入图像上滑动,覆盖一个区域。
- 计算输出:每次卷积核覆盖一个区域时,对区域内的每个像素和卷积核的对应元素进行乘法运算,然后将结果相加得到一个单一的输出值。这个输出值作为新图像中的一个像素点。
(3)卷积的例子
假设我们有一个简单的3x3的输入图像(数字表示像素强度),以及一个3x3的卷积核(滤波器)
我们把卷积核放在图像的左上角,进行卷积运算:
这个结果“-6”就是卷积运算的输出,代表了图像某个区域的特征。接下来,卷积核会在图像上滑动,继续执行相同的计算,直到覆盖整个图像。
(4)卷积的目的:
- 特征提取:卷积的核心目的是提取图像中的特征,比如边缘、角点、纹理等。不同的卷积核会检测不同类型的特征。
- 局部信息捕获:卷积帮助网络关注图像中局部的细节,而不是整个图像。这使得CNN可以自动学习到图像中的重要信息,无需人工设计特征。
(5)代码实现
上面我们看到的 卷积的例子,完全可以通过代码来实现。我们用Pyton来实现这个过程:
我们可以使用 Python 来实现一个简单的 3x3 卷积操作。我们将使用 NumPy 来处理矩阵(图像)和卷积核的运算。
下面是一个简单的 Python 示例,展示了如何对一个 3x3 的图像和 3x3 的卷积核进行卷积操作:
import numpy as np
# 定义输入图像(3x3矩阵)
image = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
# 定义卷积核(3x3矩阵)
kernel = np.array([
[1, 0, -1],
[1, 0, -1],
[1, 0, -1]
])
# 卷积操作
def convolution2d(image, kernel):
# 获取输入图像和卷积核的大小
image_height, image_width = image.shape
kernel_height, kernel_width = kernel.shape
# 计算输出图像的大小
output_height = image_height - kernel_height + 1
output_width = image_width - kernel_width + 1
# 创建一个输出图像,初始化为全0
output = np.zeros((output_height, output_width))
# 对图像进行卷积操作
for i in range(output_height):
for j in range(output_width):
# 提取图像中的当前3x3区域
region = image[i:i+kernel_height, j:j+kernel_width]
# 对应区域和卷积核的逐元素相乘并求和
output[i, j] = np.sum(region * kernel)
return output
# 进行卷积操作并输出结果
output_image = convolution2d(image, kernel)
print("输出图像:")
print(output_image)
代码说明:
-
输入图像和卷积核:
- 输入图像是一个 3x3 的矩阵。(我们可以理解为像素矩阵)
- 卷积核是一个 3x3 的矩阵,模拟了一个边缘检测的滤波器。
-
卷积操作:
convolution2d
函数实现了卷积操作。- 函数首先获取输入图像和卷积核的大小,然后计算输出图像的大小。
- 使用嵌套的 for 循环遍历每个位置,提取图像中的当前 3x3 区域,与卷积核逐元素相乘并求和,最终得到卷积结果。
-
输出:
output_image
是卷积后的输出图像。
运行结果:
假设输入图像和卷积核如下所示:
输入图像:
1 2 3
4 5 6
7 8 9
卷积核:
1 0 -1
1 0 -1
1 0 -1
输出图像(经过卷积操作后)将是:
-6.0
这是由于卷积核与输入图像的 3x3 区域的计算结果。
这个代码演示了如何用 Python 和 NumPy 实现一个简单的卷积操作,模拟了图像处理中的卷积过程。在更复杂的图像处理中,卷积核通常会根据任务进行不同的设计,如边缘检测、模糊、锐化等。
实际上,卷积神经网络 就是由很多个这样的卷积核组成,通过各种复杂的计算方式,形成了一个能自我学习的神经网络。
三、卷积的应用
卷积神经网络(CNN)在现实中的应用非常广泛,特别是在涉及图像、视频和一些其他类型的二维数据处理的领域。CNN 的强大之处在于它能够自动学习到输入数据中的特征,而无需人工设计特征,因此被广泛应用于各种任务。以下是一些 CNN 在现实中的应用场景:
1. 图像分类
CNN 最初的主要应用之一就是 图像分类,即将图像分为不同的类别。CNN 可以自动识别图像中的不同特征(如边缘、纹理、颜色等),并根据这些特征判断图像属于哪个类别。
- 应用实例:
- 面部识别:通过 CNN 进行人脸识别,用于安全系统、手机解锁等。
- 物体分类:识别图片中的物体,例如从图像中自动识别出猫、狗、车、飞机等。
- 手写数字识别:例如,MNIST 数据集中的手写数字识别,广泛应用于银行支票的自动化处理。
2. 目标检测
目标检测不仅要识别图像中的物体类别,还需要准确地 定位 这些物体的位置(即给出一个矩形框)。CNN 在这类任务中非常有效,因为它可以同时提取图像特征并确定物体的边界。
- 应用实例:
- 自动驾驶:检测和识别道路上的行人、其他车辆、交通标志等。
- 监控系统:通过视频监控识别和跟踪物体,如人脸、车辆、入侵者等。
- 医学影像分析:通过对医学影像(如CT、MRI)进行目标检测,自动定位肿瘤、病变区域等。
3. 图像分割
图像分割是指将图像划分成多个区域,每个区域表示图像中的某个特定物体或背景。CNN 在图像分割任务中也有很大的优势,尤其是在 语义分割 和 实例分割 中。
- 应用实例:
- 医学图像处理:在 CT、MRI 或 X 光片中,自动分割器官、肿瘤、病变区域,帮助医生更准确地诊断。
- 遥感图像分析:对卫星图像进行地物分类或提取土地覆盖类型。
- 自动驾驶:通过图像分割技术识别路面、车辆、行人等,使得自动驾驶系统能够理解周围环境。
4. 视频分析
CNN 也被应用于视频中的帧级分析,帮助进行对象跟踪、动作识别、场景分析等任务。通过对连续帧的卷积处理,CNN 可以识别视频中的事件、行为或情境。
- 应用实例:
- 监控和安全:通过对视频流的实时分析,检测异常行为(如入侵、打架、失踪等)。
- 运动分析:在体育赛事中,CNN 可以帮助识别运动员的动作,或者自动分析比赛的内容。
- 情感分析:通过分析视频中的面部表情和肢体语言,自动识别情感状态。
5. 风格迁移与生成对抗网络(GAN)
CNN 也被广泛应用于图像生成和风格迁移任务,特别是结合生成对抗网络(GAN)进行图像创作、艺术风格转换等。
- 应用实例:
- 艺术风格迁移:将一张普通的照片转化为类似著名艺术家画风的图片(如把照片转为梵高的画风)。
- 图像生成:GAN 和 CNN 结合生成新的、逼真的图片,如人脸生成、风景生成等。
- 超分辨率:利用 CNN 将低分辨率图像转化为高分辨率图像。
6. 语音识别
CNN 也在语音处理领域取得了显著进展,尤其是在 声学模型 和 语音到文本的转换 中。语音信号通常会被转化为时间-频率图(类似于图像),CNN 可以在这些图上提取有效的特征。
- 应用实例:
- 语音助手:例如 Siri、Alexa、Google Assistant 等,通过 CNN 进行语音识别并转换为文本。
- 自动字幕生成:将语音内容自动转为字幕或文本。
- 语音情感识别:识别语音中的情感,如愤怒、开心、悲伤等。
7. 推荐系统
虽然 CNN 在推荐系统中的应用没有像在图像处理领域那样常见,但它在一些特殊场景下也有应用,尤其是在处理带有空间和结构信息的推荐任务时。
- 应用实例:
- 图像推荐:通过用户历史浏览的图像内容,使用 CNN 对图像进行分析,并推荐相关的视觉内容(如电商网站的商品推荐)。
- 视频内容推荐:在视频平台(如 YouTube)中,通过对视频封面和内容的分析,推荐与用户兴趣相关的内容。
8. 智能医疗
CNN 在医学影像领域的应用逐渐增多,帮助医生在 X 光片、CT、MRI 等医疗影像中发现病变区域,进行早期诊断。
- 应用实例:
- 肿瘤检测:自动化地在 CT 或 X 光影像中识别出肿瘤或其他病变。
- 眼科检测:通过眼底图像自动检测眼病(如糖尿病视网膜病变等)。
- 皮肤病检测:分析皮肤图像,帮助诊断皮肤病或癌症。
卷积神经网络(CNN)已经广泛应用于许多领域,尤其是在图像和视频处理、语音识别、医学诊断等方面。它们通过自动化地学习数据中的特征,极大地提高了很多任务的效率和准确性。随着深度学习技术的不断进步,CNN 的应用场景仍在持续扩展,涉及更多领域和更复杂的任务。
四、一个简单的图像分类卷积实现
看到这里你一定基本理解了什么是卷积和它的工作模式。接下来我们就来实际地AI一把,来实现一个最简单地通过卷积来进行图片分类(数字识别)的应用。
下面的案例将用卷积来识别一个手写数字,这个在我们之前也学过例程,那时候用的不是卷积。
(1)TensorFlow
讲到AI应用开发们一定离不开:TensorFlow,这 是一个开源的机器学习和深度学习框架,由 Google Brain 团队开发。它提供了一个灵活的、广泛使用的计算图系统,用于实现各种机器学习和人工智能(AI)模型。TensorFlow 支持分布式计算,可以在多个 CPU 或 GPU 上运行,适用于不同平台(如服务器、桌面、移动设备等)。它主要用于深度学习模型的训练与推理,但也支持传统的机器学习算法。
主要特点:
- 计算图:TensorFlow 使用计算图(Graph)来表示数学计算,节点表示操作,边表示数据流。
- 自动微分:自动计算梯度,支持反向传播算法,便于训练神经网络。
- 跨平台:支持在多个平台(CPU、GPU、移动设备等)上部署和运行。
- 灵活性:可以自定义操作和模型,适合研究人员和开发者。
(2)Keras
这是我们这次实例的关键。
Keras是一个高层次的深度学习 API,用于构建和训练神经网络模型,它是 TensorFlow 的一部分。在 Keras 中,卷积操作主要通过卷积层(Conv2D
)来实现,这对于构建卷积神经网络(CNN)是至关重要的。卷积操作是 CNN 的核心,用于自动提取输入图像中的特征。
在 Keras 中,卷积操作通常通过以下层实现:
- Conv2D
层(二维卷积层)
Conv2D
是 Keras 中用于处理图像数据的卷积层,适用于二维输入(如图像)。它通过卷积操作来提取图像中的特征。
示例代码:(我们在本次例子中就是用到了这个训练方式)
from tensorflow.keras import layers
model = models.Sequential([
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1))
])
32
:卷积核的数量(即输出通道数),这决定了卷积层的输出维度。(3, 3)
:卷积核的大小(即滤波器的尺寸)。通常是一个小的 2D 窗口(例如 3x3 或 5x5)。activation='relu'
:卷积后的激活函数,ReLU 是最常用的激活函数之一。input_shape=(28, 28, 1)
:输入图像的尺寸,这里假设是 28x28 像素的灰度图像。
(3) 模块介绍
下面是代码的模块化解释:
3-1. 禁用 GPU 以排查潜在问题
(因为我的机器上GPU 有些问题,所以暂时禁用)
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
- 这行代码禁用了 GPU。
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
让 TensorFlow 在运行时只使用 CPU,而不是 GPU。这可以帮助排查与 GPU 相关的问题,或者在没有 GPU 的环境中运行代码。
3-2. 加载并预处理数据 (MNIST 数据集)
MNIST 数据集包含 28x28 像素的灰度图像,每张图像表示一个手写的数字(0 到 9)。数据集总共包括 70,000 张图像,其中有 60,000 张用于训练,10,000 张用于测试。
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 数据预处理
x_train = x_train[..., tf.newaxis] / 255.0 # 增加通道维度并归一化
x_test = x_test[..., tf.newaxis] / 255.0
print("x_train shape:", x_train.shape)
print("x_test shape:", x_test.shape)
mnist.load_data()
:加载 MNIST 手写数字数据集,返回训练集和测试集,分别包括图像和标签。x_train[..., tf.newaxis] / 255.0
:将训练图像数据归一化到 [0, 1] 区间,并通过tf.newaxis
增加一个新的通道维度,将原本形状为(28, 28)
的数据转换为(28, 28, 1)
。- 同样地,对测试数据
x_test
进行相同的处理。 print
输出了训练集和测试集数据的形状,以确保数据预处理完成。
3-3. 定义卷积神经网络 (CNN) 模型
这里的参数是可调的,参数不一样,结果会不一样,这个需要深入了解它的特性再调增参数。
model = models.Sequential([
layers.Conv2D(8, (3, 3), activation='relu', input_shape=(28, 28, 1)),
layers.MaxPooling2D((2, 2)),
layers.Conv2D(16, (3, 3), activation='relu'),
layers.MaxPooling2D((2, 2)),
layers.Flatten(),
layers.Dense(32, activation='relu'),
layers.Dense(10, activation='softmax')
])
- 使用
Sequential
构建一个顺序的神经网络模型。 Conv2D
:卷积层,用于从输入图像中提取特征。第一层有 8 个 3x3 的滤波器,使用 ReLU 激活函数。MaxPooling2D
:池化层,用于对特征图进行下采样,减少参数量和计算量。- 第二层卷积层有 16 个 3x3 的滤波器。
Flatten
:将多维输入展平为一维,用于全连接层的输入。Dense
:全连接层,最后一层有 10 个神经元,输出 10 个数字类别,激活函数为 Softmax,用于多分类任务。
3-4. 编译模型
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
optimizer='adam'
:使用 Adam 优化器进行训练,它是常用的自适应学习率优化器。loss='sparse_categorical_crossentropy'
:使用稀疏类别交叉熵作为损失函数,适用于整数标签的多分类问题。metrics=['accuracy']
:监控准确度作为训练的评估指标。
3-5. 训练模型
model.fit(x_train, y_train, epochs=3, validation_data=(x_test, y_test))
- 使用训练数据
(x_train, y_train)
来训练模型。 - 训练周期设置为 3 次(
epochs=3
)。 validation_data
:在每个周期结束时,使用测试数据(x_test, y_test)
评估模型的表现。
3-6. 导入本地图片并进行预处理
def load_image(image_path):
img = Image.open(image_path).convert('L')
img = img.resize((28, 28))
img_array = np.array(img) / 255.0
img_array = img_array[..., tf.newaxis]
img_array = np.expand_dims(img_array, axis=0)
return img_array
- 这是一个辅助函数,用于加载并预处理本地图片。
Image.open(image_path).convert('L')
:使用 PIL 打开图片并转换为灰度图(L 表示亮度)。img.resize((28, 28))
:将图片调整为 28x28 的大小,与 MNIST 数据集中的图像尺寸一致。np.array(img) / 255.0
:将图片转换为 NumPy 数组,并归一化像素值到 [0, 1]。img_array[..., tf.newaxis]
:增加通道维度,使图像形状为(28, 28, 1)
。np.expand_dims(img_array, axis=0)
:增加批次维度,使图像形状为(1, 28, 28, 1)
,以符合模型输入的形状要求。
3-7. 使用本地图像进行预测
image_path = 'path_to_your_handwritten_image.png'
sample_image = load_image(image_path)
predicted_class = model.predict(sample_image)
predicted_class = tf.argmax(predicted_class, axis=1)
image_path
:指定本地图像的路径。- 调用
load_image
函数加载并预处理图像。 model.predict(sample_image)
:使用训练好的模型对预处理后的图像进行预测。tf.argmax(predicted_class, axis=1)
:获取预测结果中最大值的索引,表示预测的数字类别。
3-8. 打印预测结果
print(f"Predicted Class: {predicted_class.numpy()[0]}")
- 打印模型的预测结果。
predicted_class.numpy()[0]
获取预测结果的数字类标签(numpy()
是为了从 TensorFlow 张量转换为 NumPy 数组)。
劈里啪啦一通计算后,给出了结果:
实际上我输入的图片上的数字是:
看起来没有识别正确,(少了半边 哈哈哈)这可能和模型的参数设定有关。有时间再慢慢调吧。
今天的卷积就聊到这里啦,硬件支持的话,可以玩出更多的花样呢!