15、卷积神经网络:从基础到时尚数据集分类实战

卷积神经网络:从基础到时尚数据集分类实战

1. 池化层与特征处理基础

在处理图像数据时,降低数据的维度空间至关重要。卷积层是实现这一目标的有效手段,而最大池化层(Max pooling layers)也有相同的作用,能进一步降低维度。

最大池化的操作很直观,就像其名字所暗示的那样,我们在特征图上滑动一个窗口,然后取窗口内的最大值。以一个对角线特征图为例,使用 2x2 的窗口进行最大池化:
1. 计算窗口内值的最大值,如 max(0, 255, 255, 0),结果为 255。
2. 依此类推完成后续窗口的操作。

通过 2x2 窗口的最大池化,我们能将一个 3x3 的特征图变为 2x2 的,成功减少了一行一列。除了最大池化,还有平均池化(average pooling)和最小池化(min pooling),不过最大池化是最常用的。

接下来是扁平化(Flattening)操作。我们之前通过卷积神经网络和最大池化层,尽可能地构建了简洁且有表现力的特征表示。而扁平化就是将卷积和最大池化后的多维数组(如 2x2 矩阵)转换为一行训练数据。以下是代码示例:

import numpy as np
max_pooled = np.array([[255, 255], [255, 255]])
flattened = max_pooled.flatten()

原本 5x5 的像素强度矩阵现在变成了具有四个特征的单行数据,这样就可以输入到全连接神经网络中。

2. 全连接层与输出

全连接层(Fully-connected layers)的作用是将卷积、最大池化和扁平化后的输入映射到目标类别。在全连接层中,每个输入都与下一层的每个神经元或节点相连。连接的强度(权重)和网络中每个节点的偏置项是模型的参数,在训练过程中会不断优化这些参数,以最小化目标函数。

模型的最后一层是输出层(output layer),它给出模型的预测结果。输出层的神经元数量和应用的激活函数取决于要解决的问题类型,如回归、二分类或多分类。

3. 使用 Keras 构建卷积神经网络对 Zalando 研究数据集进行图像分类

Zalando 研究的时尚数据集(Fashion MNIST)包含 70,000 张灰度图像,代表 10 种不同的服装类别,包括 T 恤/上衣、裤子、毛衣等。这个数据集是德国电商公司 Zalando 发布的,旨在为研究人员提供一个替代经典手写数字 MNIST 数据集的选择,并且它的预测难度相对更高。

以下是构建卷积神经网络对该数据集进行图像分类的详细步骤:
1. 克隆仓库并安装必要的库
- 从终端运行以下命令克隆仓库到桌面:

cd ~/Desktop/
git clone git@github.com:zalandoresearch/fashion - mnist.git
- 安装 Keras 和 TensorFlow:
pip install keras
pip install tensorflow
  1. 导入所需的库
import sys
import numpy as np
import pandas as pd
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPool2D
from keras.utils import np_utils, plot_model
from PIL import Image
import matplotlib.pyplot as plt

Keras 是一个流行的 Python 深度学习库,它可以运行在 TensorFlow、CNTK 或 Theano 等机器学习框架之上。Python 图像处理库(PIL)用于可视化 Keras 网络的拓扑结构。
3. 加载数据
- 确保 fashion - mnist/utils/ 在路径中:

sys.path.append('/Users/Mike/Desktop/fashion - mnist/utils/')
import mnist_reader
- 使用辅助脚本加载数据:
X_train, y_train = mnist_reader.load_mnist('/Users/Mike/Desktop/fashion - mnist/data/fashion')
X_test, y_test = mnist_reader.load_mnist('/Users/Mike/Desktop/fashion - mnist/data/fashion')
  1. 查看数据形状和类型
print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape)
print(type(X_train))
print(type(y_train))
print(type(X_test))
print(type(y_test))

训练集有 60,000 张图像,测试集有 10,000 张图像,每张图像当前是长度为 784 的向量。
5. 可视化数据
- 由于图像是灰度的,我们将向量重塑为 28x28 矩阵来可视化:

image_1 = X_train[0].reshape(28, 28)
plt.axis('off')
plt.imshow(image_1, cmap='gray')
- 查看图像所属的类别:
y_train[0]
- 创建编码值到类别名称的映射:
mapping = {0: "T - shirt/top", 1: "Trouser", 2: "Pullover", 3: "Dress", 4: "Coat", 5: "Sandal", 6: "Shirt", 7: "Sneaker", 8: "Bag", 9: "Ankle Boot"}
- 定义一个辅助函数来可视化多个图像:
def show_fashion_mnist(plot_rows, plot_columns, feature_array, target_array, cmap='gray', random_seed=None):
    if random_seed is not None:
        np.random.seed(random_seed)
    feature_array_indices = np.random.randint(0, feature_array.shape[0], size=plot_rows * plot_columns)
    fig, ax = plt.subplots(plot_rows, plot_columns, figsize=(18, 18))
    reshaped_images_list = []
    for feature_array_index in feature_array_indices:
        reshaped_image = feature_array[feature_array_index].reshape((28, 28))
        image_class = mapping[target_array[feature_array_index]]
        reshaped_images_list.append((reshaped_image, image_class))
    counter = 0
    for row in range(plot_rows):
        for col in range(plot_columns):
            ax[row, col].axis('off')
            ax[row, col].imshow(reshaped_images_list[counter][0], cmap=cmap)
            ax[row, col].set_title(reshaped_images_list[counter][1])
            counter += 1

show_fashion_mnist(4, 4, X_train, y_train, random_seed=72)

运行此函数可以随机显示多个图像,多次运行且不指定随机种子可以观察不同的图像。我们会发现有些类别在视觉上很相似,有些则差异较大,这对理解模型的强弱表现很有帮助。
6. 查看目标类别的分布

y = pd.Series(np.concatenate((y_train, y_test)))
plt.figure(figsize=(10, 6))
plt.bar(x=[mapping[x] for x in y.value_counts().index], height=y.value_counts())
plt.xlabel("Class")
plt.ylabel("Number of Images per Class")
plt.title("Distribution of Target Classes")

结果显示各类别图像数量均衡,无需进行上采样或下采样。
7. 数据预处理
- 确认图像像素值范围在 0 到 255 之间:

print(X_train.max())
print(X_train.min())
print(X_test.max())
print(X_test.min())
- 对数据进行归一化,将像素值缩放到 0 到 1 之间:
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255
- 重塑数据为 28x28 矩阵,并明确声明通道数为 1:
X_train = X_train.reshape(X_train.shape[0], 28, 28, 1)
X_test = X_test.reshape(X_test.shape[0], 28, 28, 1)
- 对目标向量进行 one - hot 编码:
y_train = np_utils.to_categorical(y_train, 10)
y_test = np_utils.to_categorical(y_test, 10)
  1. 构建模型
model = Sequential()
model.add(Conv2D(filters=35, kernel_size=(3, 3), input_shape=(28, 28, 1), activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2))) 
model.add(Conv2D(filters=35, kernel_size=(3, 3), activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2))) 
model.add(Conv2D(filters=45, kernel_size=(3, 3), activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2))) 
model.add(Flatten())
model.add(Dense(64, activation='relu')) 
model.add(Dense(32, activation='relu')) 
model.add(Dense(10, activation='softmax')) 
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
- 逐行解释代码:
    - 第一行:实例化模型对象。
    - 第二行:添加第一个卷积层,有 35 个 3x3 的卷积核,输入形状为 28x28x1,激活函数为 ReLU。
    - 第三行:添加第一个最大池化层,窗口大小为 2x2。
    - 第四行和第五行:添加第二个卷积层和最大池化层。
    - 第六行和第七行:添加第三个卷积层和最大池化层,第三个卷积层有 45 个卷积核。
    - 第八行:扁平化卷积神经网络的输出。
    - 第九行和第十行:添加全连接层,分别有 64 和 32 个神经元,激活函数为 ReLU。
    - 第十一行:输出层有 10 个神经元,对应 10 个目标类别,激活函数为 Softmax。
    - 第十二行:编译模型,使用 Adam 优化器,分类交叉熵损失函数,评估指标为准确率。
- 查看模型摘要:
model.summary()
- 可视化模型拓扑结构(需安装 pydot):
plot_model(model, to_file='Conv_model1.png', show_shapes=True)
Image.open('Conv_model1.png')
  1. 训练模型
my_fit_model = model.fit(X_train, y_train, epochs=25, validation_data=(X_test, y_test))

如果担心硬件性能,可以将训练轮数减少到 10 轮。
10. 可视化训练和验证损失及准确率

plt.plot(my_fit_model.history['val_loss'], label="Validation")
plt.plot(my_fit_model.history['loss'], label="Train")
plt.xlabel("Epoch", size=15)
plt.ylabel("Cat. Crossentropy Loss", size=15)
plt.title("Conv Net Train and Validation loss over epochs", size=18)
plt.legend()

plt.plot(my_fit_model.history['val_acc'], label="Validation")
plt.plot(my_fit_model.history['acc'], label="Train")
plt.xlabel("Epoch", size=15)
plt.ylabel("Accuracy", size=15)
plt.title("Conv Net Train and Validation accuracy over epochs", size=18)
plt.legend()

从损失和准确率曲线可以看出,模型存在过拟合问题,但验证准确率达到了 80% 以上。

print(max(my_fit_model.history['val_acc']))
print(my_fit_model.history['val_acc'].index(max(my_fit_model.history['val_acc'])))

模型在第 21 个 epoch 达到了 89.48% 的最大分类准确率。
11. 使用 Dropout 正则化解决过拟合问题

model = Sequential()
model.add(Conv2D(filters=35, kernel_size=(3, 3), input_shape=(28, 28, 1), activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Conv2D(filters=35, kernel_size=(3, 3), activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Conv2D(filters=45, kernel_size=(3, 3), activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.35))
model.add(Dense(32, activation='relu'))
model.add(Dropout(0.35))
model.add(Dense(10, activation='softmax'))
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

在全连接层添加 Dropout 层,随机丢弃 35% 的神经元。重新训练模型后,训练和验证损失的差距缩小,过拟合问题得到改善。

print(max(my_fit_model.history['val_acc']))
print(my_fit_model.history['val_acc'].index(max(my_fit_model.history['val_acc'])))

应用正则化后,最佳验证准确率为 88.85%,虽然略低于未正则化的模型,但仍远高于基线准确率(该数据集的基线准确率为 10%)。

通过以上步骤,我们完成了从数据加载、预处理到模型构建、训练和优化的全过程。后续还可以尝试构建更深的模型,对模型的超参数进行网格搜索,或者构建混淆矩阵来评估模型在不同类别上的表现。

卷积神经网络:从基础到时尚数据集分类实战

12. 模型评估与后续探索

经过前面的步骤,我们已经构建并训练了卷积神经网络,还使用 Dropout 正则化改善了过拟合问题。接下来,我们可以进一步评估模型的性能,并探索更多的优化方向。

12.1 混淆矩阵分析

混淆矩阵是评估分类模型性能的重要工具,它可以帮助我们了解模型在各个类别上的预测情况。以下是构建混淆矩阵的示例代码:

from sklearn.metrics import confusion_matrix
import seaborn as sns

# 预测测试集
y_pred = model.predict(X_test)
y_pred_classes = np.argmax(y_pred, axis=1)
y_true = np.argmax(y_test, axis=1)

# 构建混淆矩阵
confusion_mtx = confusion_matrix(y_true, y_pred_classes)

# 可视化混淆矩阵
plt.figure(figsize=(10, 8))
sns.heatmap(confusion_mtx, annot=True, fmt='d', cmap='Blues', 
            xticklabels=mapping.values(), yticklabels=mapping.values())
plt.xlabel('Predicted Label')
plt.ylabel('True Label')
plt.title('Confusion Matrix')
plt.show()

通过混淆矩阵,我们可以清晰地看到模型在哪些类别上预测准确,哪些类别容易混淆。例如,如果某个类别在对角线上的值较大,说明模型对该类别的预测准确率较高;如果某个非对角线元素的值较大,则表示模型容易将该类别与其他类别混淆。

12.2 超参数调优

我们在模型构建过程中使用了一些超参数,如卷积核数量、全连接层神经元数量、Dropout 率等。这些超参数的选择会影响模型的性能,因此可以通过网格搜索或随机搜索等方法来寻找最优的超参数组合。以下是一个简单的网格搜索示例:

from sklearn.model_selection import GridSearchCV
from keras.wrappers.scikit_learn import KerasClassifier

# 定义模型构建函数
def create_model(filters1=35, filters2=35, filters3=45, neurons1=64, neurons2=32, dropout_rate=0.35):
    model = Sequential()
    model.add(Conv2D(filters=filters1, kernel_size=(3, 3), input_shape=(28, 28, 1), activation='relu'))
    model.add(MaxPool2D(pool_size=(2, 2)))
    model.add(Conv2D(filters=filters2, kernel_size=(3, 3), activation='relu'))
    model.add(MaxPool2D(pool_size=(2, 2)))
    model.add(Conv2D(filters=filters3, kernel_size=(3, 3), activation='relu'))
    model.add(MaxPool2D(pool_size=(2, 2)))
    model.add(Flatten())
    model.add(Dense(neurons1, activation='relu'))
    model.add(Dropout(dropout_rate))
    model.add(Dense(neurons2, activation='relu'))
    model.add(Dropout(dropout_rate))
    model.add(Dense(10, activation='softmax'))
    model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
    return model

# 创建 Keras 分类器
model = KerasClassifier(build_fn=create_model, epochs=10, batch_size=32, verbose=0)

# 定义超参数网格
param_grid = {
    'filters1': [30, 35, 40],
    'filters2': [30, 35, 40],
    'filters3': [40, 45, 50],
    'neurons1': [60, 64, 70],
    'neurons2': [30, 32, 35],
    'dropout_rate': [0.3, 0.35, 0.4]
}

# 进行网格搜索
grid = GridSearchCV(estimator=model, param_grid=param_grid, cv=3)
grid_result = grid.fit(X_train, y_train)

# 输出最佳参数和最佳得分
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))

通过网格搜索,我们可以找到一组最优的超参数组合,从而进一步提高模型的性能。

12.3 构建更深的模型

除了超参数调优,我们还可以尝试构建更深的模型,增加卷积层和全连接层的数量,以提高模型的表达能力。以下是一个更深模型的示例代码:

model_deep = Sequential()
model_deep.add(Conv2D(filters=35, kernel_size=(3, 3), input_shape=(28, 28, 1), activation='relu'))
model_deep.add(MaxPool2D(pool_size=(2, 2)))
model_deep.add(Conv2D(filters=35, kernel_size=(3, 3), activation='relu'))
model_deep.add(MaxPool2D(pool_size=(2, 2)))
model_deep.add(Conv2D(filters=45, kernel_size=(3, 3), activation='relu'))
model_deep.add(MaxPool2D(pool_size=(2, 2)))
model_deep.add(Conv2D(filters=55, kernel_size=(3, 3), activation='relu'))
model_deep.add(MaxPool2D(pool_size=(2, 2)))
model_deep.add(Flatten())
model_deep.add(Dense(128, activation='relu'))
model_deep.add(Dropout(0.35))
model_deep.add(Dense(64, activation='relu'))
model_deep.add(Dropout(0.35))
model_deep.add(Dense(32, activation='relu'))
model_deep.add(Dropout(0.35))
model_deep.add(Dense(10, activation='softmax'))
model_deep.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# 训练更深的模型
my_fit_model_deep = model_deep.fit(X_train, y_train, epochs=25, validation_data=(X_test, y_test))

# 可视化训练和验证损失及准确率
plt.plot(my_fit_model_deep.history['val_loss'], label="Validation")
plt.plot(my_fit_model_deep.history['loss'], label="Train")
plt.xlabel("Epoch", size=15)
plt.ylabel("Cat. Crossentropy Loss", size=15)
plt.title("Deep Conv Net Train and Validation loss over epochs", size=18)
plt.legend()

plt.plot(my_fit_model_deep.history['val_acc'], label="Validation")
plt.plot(my_fit_model_deep.history['acc'], label="Train")
plt.xlabel("Epoch", size=15)
plt.ylabel("Accuracy", size=15)
plt.title("Deep Conv Net Train and Validation accuracy over epochs", size=18)
plt.legend()
plt.show()

构建更深的模型可能会提高模型的性能,但也可能会增加过拟合的风险,因此需要适当调整 Dropout 率等正则化方法。

13. 总结与展望

通过本次实践,我们深入了解了卷积神经网络的基本原理和构建过程,包括最大池化、扁平化、全连接层等操作。我们使用 Zalando 研究的时尚数据集进行图像分类任务,从数据加载、预处理到模型构建、训练和优化,完成了一个完整的机器学习流程。

在模型训练过程中,我们发现了过拟合问题,并使用 Dropout 正则化进行了改善。通过混淆矩阵分析、超参数调优等方法,我们可以进一步评估和优化模型的性能。此外,尝试构建更深的模型也是提高性能的一种有效途径。

未来,我们可以继续探索更多的深度学习技术和方法,如使用不同的卷积核大小、激活函数、优化器等,以进一步提高模型的准确率和泛化能力。同时,我们还可以将这些技术应用到其他图像分类任务中,如医学图像分析、自动驾驶等领域,为实际问题提供更有效的解决方案。

以下是整个流程的 mermaid 流程图:

graph LR
    A[数据加载] --> B[数据预处理]
    B --> C[模型构建]
    C --> D[模型训练]
    D --> E[模型评估]
    E --> F{是否需要优化?}
    F -- 是 --> G[超参数调优/构建更深模型]
    G --> D
    F -- 否 --> H[模型应用]

总之,卷积神经网络在图像分类领域具有巨大的潜力,通过不断的学习和实践,我们可以更好地掌握这些技术,为解决实际问题提供更强大的工具。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值