43、基于深度卷积神经网络的图像分类

基于深度卷积神经网络的图像分类

在图像分类领域,深度卷积神经网络(CNN)展现出了强大的能力。本文将围绕使用CNN进行手写数字识别和人脸图像的性别分类展开,详细介绍相关技术和实现步骤。

手写数字识别

在手写数字识别任务中,我们可以使用CNN对输入的手写数字图像进行分类。以下是一个简单的示例代码,展示了如何使用CNN进行手写数字识别:

import tensorflow as tf
import matplotlib.pyplot as plt

# 加载MNIST数据集
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 数据预处理
x_train, x_test = x_train / 255.0, x_test / 255.0

# 构建CNN模型
model = tf.keras.models.Sequential([
    tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
    tf.keras.layers.MaxPooling2D((2, 2)),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dense(10, activation='softmax')
])

# 编译模型
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# 训练模型
model.fit(x_train.reshape(-1, 28, 28, 1), y_train, epochs=5)

# 评估模型
test_loss, test_acc = model.evaluate(x_test.reshape(-1, 28, 28, 1), y_test)
print(f"Test accuracy: {test_acc}")

# 预测并可视化结果
predictions = model.predict(x_test.reshape(-1, 28, 28, 1))
fig = plt.figure(figsize=(16, 8.5))
for i in range(10):
    ax = fig.add_subplot(2, 5, i + 1)
    ax.imshow(x_test[i], cmap='gray')
    ax.text(0.9, 0.1, '{}'.format(tf.argmax(predictions[i])),
            size=15, color='blue',
            horizontalalignment='center',
            verticalalignment='center',
            transform=ax.transAxes)
plt.show()

上述代码中,我们首先加载了MNIST数据集,并对数据进行预处理。然后构建了一个简单的CNN模型,包含卷积层、池化层、全连接层。接着编译模型并进行训练,最后评估模型并可视化预测结果。

人脸图像性别分类

接下来,我们将使用CelebA数据集进行人脸图像的性别分类。

1. 加载CelebA数据集
import tensorflow as tf
import tensorflow_datasets as tfds

celeba_bldr = tfds.builder('celeb_a')
celeba_bldr.download_and_prepare()
celeba = celeba_bldr.as_dataset(shuffle_files=False)
celeba_train = celeba['train']
celeba_valid = celeba['validation']
celeba_test = celeba['test']

def count_items(ds):
    n = 0
    for _ in ds:
        n += 1
    return n

print('Train set:  {}'.format(count_items(celeba_train)))
print('Validation: {}'.format(count_items(celeba_valid)))
print('Test set:   {}'.format(count_items(celeba_test)))

# 取子集
celeba_train = celeba_train.take(16000)
celeba_valid = celeba_valid.take(1000)
print('Train set:  {}'.format(count_items(celeba_train)))
print('Validation: {}'.format(count_items(celeba_valid)))

在这个步骤中,我们使用 tensorflow_datasets 加载CelebA数据集,并统计每个数据集的样本数量。为了加快训练速度,我们从训练集和验证集中分别取了16000和1000个样本。

2. 图像变换和数据增强

数据增强是处理训练数据有限情况的有效技术。以下是一些常见的图像变换操作:

import matplotlib.pyplot as plt

# 取5个示例
examples = []
for example in celeba_train.take(5):
    examples.append(example['image'])

fig = plt.figure(figsize=(16, 8.5))

# 裁剪到边界框
ax = fig.add_subplot(2, 5, 1)
ax.set_title('Crop to a \nbounding-box', size=15)
ax.imshow(examples[0])
ax = fig.add_subplot(2, 5, 6)
img_cropped = tf.image.crop_to_bounding_box(examples[0], 50, 20, 128, 128)
ax.imshow(img_cropped)

# 水平翻转
ax = fig.add_subplot(2, 5, 2)
ax.set_title('Flip (horizontal)', size=15)
ax.imshow(examples[1])
ax = fig.add_subplot(2, 5, 7)
img_flipped = tf.image.flip_left_right(examples[1])
ax.imshow(img_flipped)

# 调整对比度
ax = fig.add_subplot(2, 5, 3)
ax.set_title('Adjust constrast', size=15)
ax.imshow(examples[2])
ax = fig.add_subplot(2, 5, 8)
img_adj_contrast = tf.image.adjust_contrast(examples[2], contrast_factor=2)
ax.imshow(img_adj_contrast)

# 调整亮度
ax = fig.add_subplot(2, 5, 4)
ax.set_title('Adjust brightness', size=15)
ax.imshow(examples[3])
ax = fig.add_subplot(2, 5, 9)
img_adj_brightness = tf.image.adjust_brightness(examples[3], delta=0.3)
ax.imshow(img_adj_brightness)

# 中心裁剪并调整大小
ax = fig.add_subplot(2, 5, 5)
ax.set_title('Centeral crop\nand resize', size=15)
ax.imshow(examples[4])
ax = fig.add_subplot(2, 5, 10)
img_center_crop = tf.image.central_crop(examples[4], 0.7)
img_resized = tf.image.resize(img_center_crop, size=(218, 178))
ax.imshow(img_resized.numpy().astype('uint8'))

plt.show()

上述代码展示了如何对图像进行裁剪、翻转、调整对比度和亮度等操作。这些操作可以增加数据的多样性,减少过拟合。

我们还可以进行随机变换,构建数据增强管道:

tf.random.set_seed(1)
fig = plt.figure(figsize=(14, 12))
for i, example in enumerate(celeba_train.take(3)):
    image = example['image']

    ax = fig.add_subplot(3, 4, i * 4 + 1)
    ax.imshow(image)
    if i == 0:
        ax.set_title('Orig', size=15)

    ax = fig.add_subplot(3, 4, i * 4 + 2)
    img_crop = tf.image.random_crop(image, size=(178, 178, 3))
    ax.imshow(img_crop)
    if i == 0:
        ax.set_title('Step 1: Random crop', size=15)

    ax = fig.add_subplot(3, 4, i * 4 + 3)
    img_flip = tf.image.random_flip_left_right(img_crop)
    ax.imshow(tf.cast(img_flip, tf.uint8))
    if i == 0:
        ax.set_title('Step 2: Random flip', size=15)

    ax = fig.add_subplot(3, 4, i * 4 + 4)
    img_resize = tf.image.resize(img_flip, size=(128, 128))
    ax.imshow(tf.cast(img_resize, tf.uint8))
    if i == 0:
        ax.set_title('Step 3: Resize', size=15)

plt.show()

为了方便使用,我们定义一个预处理函数:

def preprocess(example, size=(64, 64), mode='train'):
    image = example['image']
    label = example['attributes']['Male']
    if mode == 'train':
        image_cropped = tf.image.random_crop(image, size=(178, 178, 3))
        image_resized = tf.image.resize(image_cropped, size=size)
        image_flip = tf.image.random_flip_left_right(image_resized)
        return image_flip / 255.0, tf.cast(label, tf.int32)
    else:
        image_cropped = tf.image.crop_to_bounding_box(image, offset_height=20, offset_width=0,
                                                      target_height=178, target_width=178)
        image_resized = tf.image.resize(image_cropped, size=size)
        return image_resized / 255.0, tf.cast(label, tf.int32)

然后对训练集和验证集应用预处理函数:

import numpy as np

BATCH_SIZE = 32
BUFFER_SIZE = 1000
IMAGE_SIZE = (64, 64)
steps_per_epoch = np.ceil(16000 / BATCH_SIZE)

ds_train = celeba_train.map(lambda x: preprocess(x, size=IMAGE_SIZE, mode='train'))
ds_train = ds_train.shuffle(buffer_size=BUFFER_SIZE).repeat()
ds_train = ds_train.batch(BATCH_SIZE)

ds_valid = celeba_valid.map(lambda x: preprocess(x, size=IMAGE_SIZE, mode='eval'))
ds_valid = ds_valid.batch(BATCH_SIZE)
3. 训练CNN性别分类器
model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(32, (3, 3), padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D((2, 2)),
    tf.keras.layers.Dropout(rate=0.5),

    tf.keras.layers.Conv2D(64, (3, 3), padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D((2, 2)),
    tf.keras.layers.Dropout(rate=0.5),

    tf.keras.layers.Conv2D(128, (3, 3), padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D((2, 2)),

    tf.keras.layers.Conv2D(256, (3, 3), padding='same', activation='relu')
])

print(model.compute_output_shape(input_shape=(None, 64, 64, 3)))

model.add(tf.keras.layers.GlobalAveragePooling2D())
print(model.compute_output_shape(input_shape=(None, 64, 64, 3)))

model.add(tf.keras.layers.Dense(1, activation=None))
tf.random.set_seed(1)
model.build(input_shape=(None, 64, 64, 3))
model.summary()

model.compile(optimizer=tf.keras.optimizers.Adam(),
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=['accuracy'])

history = model.fit(ds_train, validation_data=ds_valid,
                    epochs=20,
                    steps_per_epoch=steps_per_epoch)

在这个步骤中,我们构建了一个CNN模型,包含卷积层、池化层、全局平均池化层和全连接层。然后编译模型并进行训练。

4. 可视化学习曲线
hist = history.history
x_arr = np.arange(len(hist['loss'])) + 1

fig = plt.figure(figsize=(12, 4))
ax = fig.add_subplot(1, 2, 1)
ax.plot(x_arr, hist['loss'], '-o', label='Train loss')
ax.plot(x_arr, hist['val_loss'], '--<', label='Validation loss')
ax.legend(fontsize=15)
ax.set_xlabel('Epoch', size=15)
ax.set_ylabel('Loss', size=15)

ax = fig.add_subplot(1, 2, 2)
ax.plot(x_arr, hist['accuracy'], '-o', label='Train acc.')
ax.plot(x_arr, hist['val_accuracy'], '--<', label='Validation acc.')
ax.legend(fontsize=15)
ax.set_xlabel('Epoch', size=15)
ax.set_ylabel('Accuracy', size=15)

plt.show()

通过可视化学习曲线,我们可以观察训练集和验证集的损失和准确率变化情况。

5. 继续训练和评估模型
history = model.fit(ds_train, validation_data=ds_valid,
                    epochs=30, initial_epoch=20,
                    steps_per_epoch=steps_per_epoch)

ds_test = celeba_test.map(lambda x: preprocess(x, size=IMAGE_SIZE, mode='eval')).batch(32)
test_results = model.evaluate(ds_test)
print('Test Acc: {:.2f}%'.format(test_results[1] * 100))

如果学习曲线还没有收敛,我们可以继续训练模型。最后,在测试集上评估模型的性能。

6. 预测并可视化结果
ds = ds_test.unbatch().take(10)
pred_logits = model.predict(ds.batch(10))
probas = tf.sigmoid(pred_logits)
probas = probas.numpy().flatten() * 100

fig = plt.figure(figsize=(15, 7))
for j, example in enumerate(ds):
    ax = fig.add_subplot(2, 5, j + 1)
    ax.set_xticks([]); ax.set_yticks([])
    ax.imshow(example[0])
    if example[1].numpy() == 1:
        label = 'M'
    else:
        label = 'F'
    ax.text(0.5, -0.15, 'GT: {:s}\nPr(Male)={:.0f}%'.format(label, probas[j]),
            size=16,
            horizontalalignment='center',
            verticalalignment='center',
            transform=ax.transAxes)
plt.tight_layout()
plt.show()

我们从测试集中取10个样本,进行预测并可视化结果,同时显示真实标签和预测概率。

总结

通过上述步骤,我们学习了如何使用CNN进行手写数字识别和人脸图像的性别分类。在人脸图像性别分类任务中,我们使用了数据增强技术来提高模型的泛化能力。以下是整个流程的总结表格:
| 步骤 | 操作 |
| ---- | ---- |
| 1 | 加载CelebA数据集并取子集 |
| 2 | 进行图像变换和数据增强 |
| 3 | 构建并训练CNN性别分类器 |
| 4 | 可视化学习曲线 |
| 5 | 继续训练和评估模型 |
| 6 | 预测并可视化结果 |

同时,整个流程可以用以下mermaid流程图表示:

graph TD;
    A[加载CelebA数据集] --> B[取子集];
    B --> C[图像变换和数据增强];
    C --> D[构建并训练CNN模型];
    D --> E[可视化学习曲线];
    E --> F{是否继续训练};
    F -- 是 --> D;
    F -- 否 --> G[评估模型];
    G --> H[预测并可视化结果];

通过这些步骤和技术,我们可以有效地使用CNN进行图像分类任务,并提高模型的性能。

基于深度卷积神经网络的图像分类

技术细节分析

在使用CNN进行图像分类的过程中,有几个关键的技术点值得深入分析。

卷积层

卷积层是CNN的核心组件之一。在手写数字识别和人脸图像性别分类的模型中,都使用了多个卷积层。卷积层通过卷积核在输入图像上滑动,提取图像的特征。例如,在人脸图像性别分类的模型中,我们使用了不同数量的卷积核(32、64、128、256)来提取不同层次的特征。卷积核的大小通常选择为3x3,这种大小在实践中表现良好。

model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(32, (3, 3), padding='same', activation='relu'),
    tf.keras.layers.Conv2D(64, (3, 3), padding='same', activation='relu'),
    tf.keras.layers.Conv2D(128, (3, 3), padding='same', activation='relu'),
    tf.keras.layers.Conv2D(256, (3, 3), padding='same', activation='relu')
])
池化层

池化层用于减少特征图的尺寸,同时保留重要的特征信息。常见的池化操作有最大池化(MaxPooling)和平均池化(AveragePooling)。在我们的模型中,使用了最大池化层,池化窗口大小为2x2。

model = tf.keras.Sequential([
    ...
    tf.keras.layers.MaxPooling2D((2, 2)),
    ...
])
全局平均池化层

全局平均池化层是一种特殊的池化层,它将每个特征图的所有元素求平均值,得到一个标量值。这样可以将特征图的维度从二维(高度和宽度)压缩到一维,减少模型的参数数量。在人脸图像性别分类的模型中,我们使用全局平均池化层将256个8x8的特征图压缩为256个标量值。

model.add(tf.keras.layers.GlobalAveragePooling2D())
数据增强

数据增强是提高模型泛化能力的重要技术。通过对训练数据进行随机变换,如裁剪、翻转、调整对比度和亮度等,可以增加数据的多样性,减少过拟合。在人脸图像性别分类任务中,我们定义了一个预处理函数 preprocess ,在训练阶段使用随机变换,在验证和测试阶段使用中心裁剪。

def preprocess(example, size=(64, 64), mode='train'):
    image = example['image']
    label = example['attributes']['Male']
    if mode == 'train':
        image_cropped = tf.image.random_crop(image, size=(178, 178, 3))
        image_resized = tf.image.resize(image_cropped, size=size)
        image_flip = tf.image.random_flip_left_right(image_resized)
        return image_flip / 255.0, tf.cast(label, tf.int32)
    else:
        image_cropped = tf.image.crop_to_bounding_box(image, offset_height=20, offset_width=0,
                                                      target_height=178, target_width=178)
        image_resized = tf.image.resize(image_cropped, size=size)
        return image_resized / 255.0, tf.cast(label, tf.int32)
优化建议

为了进一步提高模型的性能,可以考虑以下优化建议:

  • 增加训练数据 :在人脸图像性别分类任务中,我们只使用了一小部分训练数据(16000个样本)。可以尝试使用更多的训练数据,以提高模型的泛化能力。
  • 调整模型架构 :可以尝试改变卷积层的数量、卷积核的数量和大小,或者添加更多的全连接层。例如,可以增加卷积层的深度,以提取更复杂的特征。
  • 调整超参数 :可以调整学习率、批量大小、训练轮数等超参数。例如,可以使用学习率调度器,在训练过程中逐渐降低学习率,以提高模型的收敛速度。
  • 使用预训练模型 :可以使用在大规模数据集上预训练的模型,如ResNet、VGG等,然后在自己的数据集上进行微调。这样可以利用预训练模型学习到的特征,加快模型的训练速度。
总结与展望

通过本文的介绍,我们学习了如何使用CNN进行手写数字识别和人脸图像的性别分类。在人脸图像性别分类任务中,我们使用了数据增强技术来提高模型的泛化能力。整个流程包括加载数据集、进行数据增强、构建和训练模型、可视化学习曲线、评估模型和预测结果。

未来,CNN在图像分类领域还有很大的发展空间。随着硬件技术的不断进步,模型的复杂度和性能将不断提高。同时,结合其他技术,如注意力机制、生成对抗网络等,可以进一步提高图像分类的准确率和效率。

以下是优化建议的总结列表:
1. 增加训练数据
2. 调整模型架构
3. 调整超参数
4. 使用预训练模型

同时,我们可以用以下mermaid流程图表示优化过程:

graph TD;
    A[初始模型] --> B[调整超参数];
    B --> C{性能是否提升};
    C -- 是 --> D[继续调整超参数];
    C -- 否 --> E[调整模型架构];
    E --> F{性能是否提升};
    F -- 是 --> D;
    F -- 否 --> G[增加训练数据];
    G --> H{性能是否提升};
    H -- 是 --> D;
    H -- 否 --> I[使用预训练模型];
    I --> J{性能是否提升};
    J -- 是 --> D;
    J -- 否 --> K[结束优化];

通过不断地优化和改进,我们可以使CNN在图像分类任务中取得更好的性能。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值