Web-和机器学习的人工智能辅助变成-五-

64 阅读1小时+

Web 和机器学习的人工智能辅助变成(五)

原文:annas-archive.org/md5/f5ce3ca93c7ecd74cff0d3ae335a95cc

译者:飞龙

协议:CC BY-NC-SA 4.0

第十五章:使用 ChatGPT 构建 CIFAR-10 的 CNN 模型

引言

在我们上一章中通过 Fashion-MNIST 数据集深入探讨了多层感知器(Multi-Layer PerceptronMLP)之后,我们现在转向一个更复杂和视觉上更复杂的挑战。本章标志着我们从主要基于表格、灰度世界的 Fashion-MNIST 转向色彩丰富和多样化的 CIFAR-10 数据集。在这里,我们将重点提升到 卷积神经网络Convolutional Neural NetworksCNNs),这是一类正在改变我们处理图像分类任务方式的深度神经网络。

我们在 MLP 章节中的旅程为我们理解神经网络的基本原理及其在分类简单灰度图像中的应用奠定了坚实的基础。现在,我们进入了一个更高级的领域,在这里 CNNs 占据主导地位。CIFAR-10 数据集包含 10 个不同类别中 32x32 彩色图像的阵列,为 MLPs 提供了一系列独特的挑战,MLPs 并不适合解决这些问题。这就是 CNNs 发挥作用的地方,它们能够捕捉图像中的空间和纹理模式。

随着我们从多层感知器(MLPs)过渡到 CNNs,我们将所获得的见解和知识应用于更复杂的数据集,这些数据集更接近现实世界场景。CIFAR-10 数据集不仅测试了图像分类模型的极限,而且为我们探索 CNNs 的高级功能提供了一个极好的平台。

本章旨在基于我们对神经网络的知识,并引导你了解卷积神经网络(CNNs)的细微差别。我们将深入探讨为什么 CNNs 是图像数据的优选选择,它们在处理颜色和纹理方面与 MLPs 的不同之处,以及是什么使得它们在从 CIFAR-10 数据集中对图像进行分类时如此有效。准备好开始一段旅程,带你从基础到 CNNs 更为复杂方面的深入探索。

商业问题

CIFAR-10 数据集为寻求增强各种对象图像识别能力并基于视觉数据优化决策过程的公司提出了一个商业挑战。众多行业,如电子商务、自动驾驶和监控,都可以从准确的对象分类和检测中受益。通过利用机器学习算法,企业旨在提高效率、提升用户体验并简化运营流程。

问题与数据领域

在这个背景下,我们将利用卷积神经网络(CNNs)来处理使用 CIFAR-10 数据集的对象识别任务。由于 CNNs 能够从原始像素数据中自动学习层次化特征,它们在处理与图像相关的问题上特别有效。通过在 CIFAR-10 数据集上训练 CNN 模型,我们的目标是开发一个能够准确地将对象分类到十个预定义类别之一的鲁棒系统。该模型可以应用于多个领域,例如基于图像的搜索引擎、自动监控系统以及制造业的质量控制。

数据集概述

CIFAR-10 数据集包含 60,000 张彩色图像,分为 10 个类别,每个类别有 6,000 张图像。每张图像的尺寸为 32x32 像素,并以 RGB 格式表示。数据集分为包含 50,000 张图像的训练集和包含 10,000 张图像的测试集。

数据集中的特征包括:

  • 图像数据:各种对象的彩色图像,每个图像表示为一个包含红、绿、蓝通道像素强度的三维数组。这些图像作为训练 CNN 模型的输入数据。

  • 标签:分配给每张图像的类别标签,代表所描绘对象的类别。标签范围从 0 到 9,对应于如飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船和卡车等类别。

通过分析 CIFAR-10 数据集及其相关标签,我们的目标是训练一个能够准确识别图像中描绘对象的 CNN 模型。然后,此预测模型可以部署到实际应用中,以自动化对象识别任务,改进决策过程,并提高各行业的整体效率。

图 15.1:CIFAR-10 数据集

将问题分解为特征

鉴于 CIFAR-10 数据集和 CNN 在图像识别中的应用,我们概述以下功能,以指导用户构建和优化 CNN 模型:

  • 构建具有单个卷积层的基线 CNN 模型:用户将首先构建一个简单的 CNN 模型,其中包含单个卷积层用于图像分类。此功能侧重于定义基本架构,包括卷积滤波器、激活函数和池化层,以建立对 CNN 的基础理解。

  • 实验添加卷积层:用户将探索向基线模型架构添加额外卷积层的影响。通过逐步增加网络的深度,用户可以观察到模型捕捉层次特征的能力如何演变,以及其学习复杂模式的能力如何提高。

  • 整合 dropout 正则化:用户将学习如何将 dropout 正则化集成到 CNN 模型中,以减轻过拟合并提高泛化性能。通过在训练过程中随机丢弃单元,dropout 有助于防止网络过度依赖特定特征,并鼓励鲁棒的特征学习。

  • 实现批量归一化:用户将探索批量归一化在稳定训练动态和加速收敛方面的好处。此功能侧重于将批量归一化层集成到 CNN 架构中,以归一化激活并减少内部协变量偏移,从而实现更快、更稳定的训练。

  • 使用不同的优化器进行优化:本功能探讨了使用各种优化算法(包括 SGD、Adam 和 RMSprop)来训练 CNN 模型的效果。用户将比较使用不同优化器获得的训练动态、收敛速度和最终模型性能,从而能够为他们的特定任务选择最合适的优化策略。

  • 执行数据增强:用户将尝试旋转、翻转、缩放和移位等数据增强技术,以增加训练数据集的多样性和规模。通过在训练过程中动态生成增强样本,用户可以提高模型泛化到未见数据的能力,并增强对输入图像变化的鲁棒性。

通过遵循这些功能,用户将获得关于使用 CIFAR-10 数据集构建、微调和优化 CNN 模型以进行图像分类任务的实用见解。他们将学习如何系统地实验不同的架构组件、正则化技术和优化策略,以实现卓越的性能和准确性,在物体识别中达到更高的精度。

提示策略

为了利用 ChatGPT 进行机器学习,我们需要清楚地了解如何实现针对代码生成的特定于机器学习的提示策略。

让我们头脑风暴一下我们希望在这次任务中实现的目标,以便更好地理解需要包含在提示中的内容。

策略 1:任务-行动-指南(TAG)提示策略

1.1 - 任务:具体任务是构建和优化 CIFAR-10 数据集的 CNN 模型。

1.2 - 行动:构建和优化 CIFAR-10 数据集的 CNN 模型的关键步骤包括:

  • 预处理图像数据:归一化像素值并将图像调整到标准尺寸。

  • 模型构建:定义具有单个卷积层的基线 CNN 模型架构。

1.3 - 指南:在我们的提示中,我们将向 ChatGPT 提供以下指南:

  • 代码应与 Jupyter Notebook 兼容。

  • 确保每行代码都有详细的注释。

  • 您必须详细解释每一行代码,涵盖代码中使用的每个方法。

策略 2:角色-指令-上下文(PIC)提示策略

2.1 - 角色扮演:扮演一个需要逐步指导构建和优化用于图像分类任务的 CNN 模型的初学者角色。

2.2 - 指令:请求 ChatGPT 逐个生成每个功能的代码,并在进行下一步之前等待用户反馈。

2.3 - 上下文:鉴于重点是使用 CIFAR-10 数据集构建用于图像分类任务的 CNN 模型,ChatGPT 已经了解该数据集及其特征,因此可能不需要额外的上下文。

策略 3:学习-即兴创作-反馈-评估(LIFE)提示策略

3.1 - 学习

  • 强调了解 CNN 模型和其组件的重要性,包括卷积层、池化层、dropout 正则化、批量归一化、数据增强和优化算法。

3.2 - 改进

  • 我们将在后面通过添加更多层、dropout 层、池化、数据增强等来改进。

3.3 - 反馈

  • 分享代码和模型输出以供反馈,以确保有效的学习和理解。

  • 将建议和批评纳入,以完善模型和方法**。

  • 提供错误以排除故障和解决问题。

3.4- 评估

执行 ChatGPT 提供的代码,以确保准确性和有效性。这一点在本章中会反复使用。

使用 ChatGPT 的免费版本构建 CNN 模型,以准确分类 CIFAR-10 图像

ChatGPT 的付费版本具有代码解释器,但它不支持构建 ML 代码所需的深度学习库,如 Keras 和 TensorFlow。因此,在本章中,我们将坚持使用 ChatGPT 的免费版本。

特征 1:使用单个卷积层构建基线 CNN 模型

让我们为基线模型制定初始提示。

[提示]

我想为 CIFAR-10 数据集创建一个简单的分类模型(TAG 1.1),该模型由单个层 CNN 模型(PIC 2.2)组成。它应包括以下步骤(TAG 1.2),并一次提供一个步骤,等待用户反馈(PIC 2.2):

  • 预处理图像数据:归一化像素值并将图像调整到标准尺寸。

  • 模型构建:定义具有单个卷积层的基线 CNN 模型架构。

我是一个初学者用户(PIC 2.1),因此请提供逐步的代码,并严格遵循以下说明(TAG 1.3):

  • 代码应与 Jupyter Notebook 兼容。

  • 确保每行代码都有详细的注释。

您必须详细解释每一行代码,涵盖代码中使用的每个方法。

[提示结束]

您应该收到以下类似的响应,其中 ChatGPT 为我们提供了数据预处理步骤的代码,并附有详细的注释和解释。

[快速响应]

当然,让我们从第一步开始:预处理图像数据。在这一步中,我们将归一化像素值并将图像调整到标准尺寸。

# Step 1: Preprocessing the image data
# Import necessary libraries
import numpy as np
import tensorflow as tf
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.utils import to_categorical
from skimage.transform import resize
# Load CIFAR-10 dataset
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
# Normalize pixel values to range [0, 1]
x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0
# Resize images to a standardized size (e.g., 32x32 pixels)
standardized_size = (32, 32)
x_train_resized = np.array([resize(image, standardized_size) for image in x_train])
x_test_resized = np.array([resize(image, standardized_size) for image in x_test])
# Display shape of datasets after preprocessing
print("Shape of x_train:", x_train_resized.shape)
print("Shape of x_test:", x_test_resized.shape) 

现在,让我们逐行分析代码:

我们导入必要的库:

  • 使用 numpy 进行数值运算。

  • 使用 tensorflow 构建 和训练模型。

  • 使用来自 tensorflow.keras.datasetscifar10 数据集加载 CIFAR-10 数据。

  • 使用来自 tensorflow.keras.utilsto_categorical 函数进行标签的一热编码。

  • 使用来自 skimage.transformresize 函数调整图像大小。

  • 我们使用 cifar10.load_data() 函数加载 CIFAR-10 数据集,该函数返回训练和测试数据分割。

  • 我们通过除以 255.0 将图像的像素值归一化到 [0, 1] 范围内。

  • 我们定义图像调整大小的标准化大小(例如,32x32 像素)。

  • 我们使用列表推导和 resize 函数将训练集和测试集中的所有图像调整到标准化大小。

  • 最后,我们在预处理后打印训练集和测试集的形状,以验证变化。

如果您有任何问题或想要进行下一步,请告诉我!

[响应结束]

我们现在将应用评估提示策略(LIFE 3.4),并通过在 Google Colab 的每一步执行代码来验证代码。该代码从 Keras 加载模型并规范化图像。代码运行良好,无需任何修改或返回任何错误。

请参考Building a CNN Model for CIFAR-10 with ChatGPT.ipynb`笔记本中的可执行代码片段,或从提示响应中复制代码。

图 15.2:代码执行 – 数据预处理

我们现在将指示 ChatGPT 进行下一步。

[Prompt]

让我们进行下一步。

[End of prompt]

我们应该收到以下响应,其中 ChatGPT 应提供创建基线模型的代码。

[Prompt response]

太好了!下一步是构建具有单个卷积层的基线 CNN 模型架构。

# Step 2: Model Construction
# Import necessary libraries
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
# Define the baseline CNN model architecture
model = Sequential([
    # Add a convolutional layer with 32 filters, kernel size of (3, 3), and ReLU activation function
    Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
    # Add a max pooling layer with pool size of (2, 2)
    MaxPooling2D((2, 2)),
    # Flatten the output of the convolutional layer
    Flatten(),
    # Add a fully connected layer with 64 neurons and ReLU activation function
    Dense(64, activation='relu'),
    # Add the output layer with 10 neurons (one for each class) and softmax activation function
    Dense(10, activation='softmax')
])
# Compile the model
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
# Display model summary
model.summary() 

现在,让我们解释每一行代码:

  • 我们导入必要的库:

  • 使用tensorflow.keras.models中的Sequential创建一个序列模型。

  • tensorflow.keras.layers中获取Conv2DMaxPooling2DFlattenDense层,以定义 CNN 模型中的不同类型层。

  • 我们使用序列 API 定义基线 CNN 模型架构,这允许我们创建线性堆叠的层。

在序列模型中:

  • 我们添加一个卷积层(Conv2D),包含 32 个过滤器,核大小为(3, 3),以及ReLU激活函数。input_shape参数定义了输入图像的形状。

  • 我们添加一个最大池化层(MaxPooling2D),池化大小为(2, 2),以对特征图进行下采样。

  • 我们使用Flatten层将卷积层的输出展平,以便为全连接层做准备。

  • 我们添加一个全连接层(Dense),包含 64 个神经元和一个ReLU激活函数。

  • 最后,我们添加一个输出层,包含 10 个神经元(每个 CIFAR-10 类别一个)和softmax激活函数。

我们使用model.compile()编译模型,以下为配置:

  • 使用optimizer='adam'来使用Adam优化器。

  • 使用loss='sparse_categorical_crossentropy'作为损失函数,适用于具有整数标签的分类任务。

  • 使用metrics=['accuracy']来监控训练过程中的准确率。

  • 我们使用model.summary()显示模型架构的摘要。

如果您有任何问题或想要进行调整,请告诉我!

[响应结束]

我们在笔记本中执行代码,它运行得非常完美。打印了一个模型摘要,其中包括 32 个滤波器和 896 个参数的Conv2d层,接着是一个MaxPooling2D层,一个没有参数的扁平层,然后是一个有 64 个神经元的密集层和 ReLU 激活函数,最后是一个有 10 个神经元和 softmax 激活函数的密集层。

图 15.3:代码执行 – 基线模型摘要

我们现在将指示 ChatGPT 继续下一步。

[Prompt]

让我们继续下一步。

[End of prompt]

我们应该收到以下类似的响应,其中 ChatGPT 应提供训练模型的代码。

[Prompt response]

当然!下一步是在预处理过的 CIFAR-10 数据集上训练模型。

# Step 3: Model Training
# Train the model
history = model.fit(x_train_resized, y_train,
                    epochs=10, 
                    batch_size=32,
                    validation_data=(x_test_resized, y_test)) 

下面是代码的解释:

  • 我们使用fit方法在预处理过的训练数据(x_train_resized)和相应的标签(y_train)上训练模型。

  • 我们指定训练轮数(epochs=10)以在整个数据集上迭代 10 次来训练模型。

  • 我们将批大小(batch_size=32)设置为指定每次梯度更新时的样本数量。这是一个超参数,它决定了在更新模型参数之前处理多少个样本。

  • 我们提供验证数据(validation_data=(x_test_resized, y_test))以在每个 epoch 后评估模型在测试数据上的性能。

  • fit方法返回一个包含每个 epoch 的训练指标(例如,损失和准确率)的历史对象,这些指标可用于分析和可视化。

如果您有任何问题或想要继续评估模型或进行任何调整,请告诉我!

[End of response]

我们执行代码,它运行无误。模型训练了 10 个 epoch,之后我们收到 62.6%的验证准确率,对于一个基本模型来说还不错。

图 15.4:代码执行 – 模型训练

我们现在将指示 ChatGPT 继续下一步以评估模型性能。

[Prompt]

让我们继续下一步。

[End of prompt]

我们应该收到以下类似的响应,其中 ChatGPT 应提供模型评估的代码以及注释。

[Prompt response]

当然!下一步是对测试数据集上的训练模型进行评估并可视化其性能。

# Step 4: Model Evaluation and Visualization
import matplotlib.pyplot as plt
# Evaluate the model on the testing dataset
test_loss, test_accuracy = model.evaluate(x_test_resized, y_test, verbose=0)
print("Test Loss:", test_loss)
print("Test Accuracy:", test_accuracy)
# Plot training history
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.show() 

这段代码的作用如下:

  • 我们导入matplotlib.pyplot来可视化训练历史。

  • 我们使用evaluate方法在测试数据集上评估训练模型,该方法返回测试损失和准确率。

  • 我们打印测试损失和测试准确率以评估模型在未见数据上的性能。

  • 我们使用matplotlib.pyplot绘制训练和验证准确率随 epoch 的变化。fit方法返回的历史对象包含训练和验证数据集在各个 epoch 的准确率值。

  • history.history['accuracy']包含训练准确率值。

  • history.history['val_accuracy']包含验证准确率值。

  • 该图表帮助我们可视化模型随时间学习的效果,以及它是否过拟合或欠拟合。

如果您有任何问题或想要进一步分析结果,请告诉我!

[响应结束]

我们执行代码,它运行没有任何错误。它评估了测试损失和测试准确率(62.5%),并绘制了训练准确率与验证准确率的图表,显示模型在验证集上的表现不佳。

图 15.5:代码执行 – 模型评估

这完成了功能 1,我们使用基线 CNN 模型实现了 0.6259(62.59%)的准确率。(请注意,即使运行了完全相同的代码,您也可能收到不同的准确率分数。)接下来,我们将转向第二个功能,即在模型中添加更多层,并了解 ChatGPT 如何帮助我们。

功能 2:实验添加卷积层

现在我们有了基线模型架构,让我们尝试向其中添加层。我们将应用提示策略来改进模型(LIFE 3.4),并要求 ChatGPT 提供相应的代码,同时注意使用短语(在基线模型之上)(PIC 2.2)和关键词增量(PIC 2.2)的重要性;没有这些,响应将不会完全符合我们的期望。

[提示]

我们在基线模型上获得了 62.59 的准确率,因此现在我想先尝试在基线模型上添加两层连续的 64 个滤波器,然后在其之上再添加两层连续的 128 个滤波器。然后,让我们比较这两个增量变化与基线模型的成果。

[提示结束]

我们应该收到以下响应,其中 ChatGPT 理解请求并为我们提供具有连续 64 个滤波器层的修改模型代码,以及另一个具有连续 128 个滤波器层的模型代码。

[响应提示]

当然!让我们首先在基线模型上添加两层连续的 64 个滤波器,并重新训练它。然后,我们将添加两层连续的 128 个滤波器并再次训练模型。最后,我们将比较这两个变化与基线模型的成果。

这是我们如何修改基线模型以包含额外层的方法:

# Define the modified model with two consecutive layers of 64 filters
modified_model_64 = Sequential([
    Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
    MaxPooling2D((2, 2)),
    Conv2D(64, (3, 3), activation='relu'),
    MaxPooling2D((2, 2)),
    Conv2D(64, (3, 3), activation='relu'),
    MaxPooling2D((2, 2)),
    Flatten(),
    Dense(64, activation='relu'),
    Dense(10, activation='softmax')
])
# Compile the modified model
modified_model_64.compile(optimizer='adam',
                          loss='sparse_categorical_crossentropy',
                          metrics=['accuracy'])
# Display model summary
modified_model_64.summary()
# Train the modified model with two consecutive layers of 64 filters
history_64 = modified_model_64.fit(x_train_resized, y_train,
                                    epochs=10, 
                                    batch_size=32,
                                    validation_data=(x_test_resized, y_test))
# Evaluate the modified model on the testing dataset
test_loss_64, test_accuracy_64 = modified_model_64.evaluate(x_test_resized, y_test, verbose=0)
print("Test Loss (Modified Model with 64 Filters):", test_loss_64)
print("Test Accuracy (Modified Model with 64 Filters):", test_accuracy_64) 

现在,让我们解释一下这些修改:

  • 我们通过在基线模型架构之后添加两个连续的 64 个滤波器卷积层来定义修改后的模型(modified_model_64)。

  • 我们使用与基线模型相同的设置编译了修改后的模型。

  • 我们在预处理后的训练数据(x_train_resized)上训练了修改后的模型(modified_model_64)。

  • 我们在测试数据集(x_test_resized)上评估修改后模型的性能,并打印测试损失和准确率。

接下来,我们将重复添加两层连续 128 个滤波器的过程:

# Define the modified model with two consecutive layers of 128 filters
modified_model_128 = Sequential([
    Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
    MaxPooling2D((2, 2)),
    Conv2D(64, (3, 3), activation='relu'),
    MaxPooling2D((2, 2)),
    Conv2D(64, (3, 3), activation='relu'),
    MaxPooling2D((2, 2)),
    Conv2D(128, (3, 3), activation='relu'),
    MaxPooling2D((2, 2)),
    Conv2D(128, (3, 3), activation='relu'),
    MaxPooling2D((2, 2)),
    Flatten(),
    Dense(64, activation='relu'),
    Dense(10, activation='softmax')
])
# Compile the modified model
modified_model_128.compile(optimizer='adam',
                           loss='sparse_categorical_crossentropy',
                           metrics=['accuracy'])
# Display model summary
modified_model_128.summary()
# Train the modified model with two consecutive layers of 128 filters
history_128 = modified_model_128.fit(x_train_resized, y_train,
                                     epochs=10, 
                                     batch_size=32,
                                     validation_data=(x_test_resized, y_test))
# Evaluate the modified model on the testing dataset
test_loss_128, test_accuracy_128 = modified_model_128.evaluate(x_test_resized, y_test, verbose=0)
print("Test Loss (Modified Model with 128 Filters):", test_loss_128)
print("Test Accuracy (Modified Model with 128 Filters):", test_accuracy_128) 

此代码遵循与之前修改相同的结构,但添加了两个连续的具有 128 个滤波器的卷积层。我们相应地训练和评估模型。

如果你有任何问题或想要继续修改,请告诉我!

[响应结束]

我们首先执行定义两个具有 64 个滤波器的额外层的代码。它运行得很好,训练了模型并实现了 71%的改进准确率。

图 15.6:向模型添加两个具有 64 个滤波器的连续层

接下来,我们执行代码,通过添加两个具有 128 个滤波器的额外层来增加模型的深度。在这种情况下,代码导致错误,因此我们将使用提示策略,并将错误反馈给 ChatGPT。

你应该在运行代码时看到一个ValueError

[提示]**

我们收到了以下错误。同时,确保最大池化不会过多地减少 128 个滤波器的维度。错误如下:

ValueError: Exception encountered when calling layer "conv2d_38" (type Conv2D).
Negative dimension size caused by subtracting 3 from 2 for '{{node conv2d_38/Conv2D}} = Conv2D[T=DT_FLOAT, data_format="NHWC", dilations=[1, 1, 1, 1], explicit_paddings=[], padding="VALID", strides=[1, 1, 1, 1], use_cudnn_on_gpu=true](Placeholder, conv2d_38/Conv2D/ReadVariableOp)' with input shapes: [?,2,2,64], [3,3,64,128].
Call arguments received by layer "conv2d_38" (type Conv2D):
  inputs=tf.Tensor(shape=(None, 2, 2, 64), dtype=float32). 

[提示结束]**

我们应该收到以下响应,其中 ChatGPT 理解请求并为我们提供解决该问题的修改后的代码。

[提示响应]**

看起来我们遇到了一个问题,即最后一个最大池化层之后的维度对于具有 128 个滤波器的后续卷积层来说太小。为了解决这个问题,我们将调整架构以确保层之间的兼容性。

这里是修正后的代码:

# Define the modified model with two consecutive layers of 128 filters added to the 64-layer model
modified_model_64_to_128 = Sequential([
    Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
    MaxPooling2D((2, 2)),
    Conv2D(64, (3, 3), activation='relu'),
    MaxPooling2D((2, 2)),
    Conv2D(64, (3, 3), activation='relu'),
    Conv2D(128, (3, 3), activation='relu', padding='same'),  # Add padding
    Conv2D(128, (3, 3), activation='relu', padding='same'),  # Add padding
    MaxPooling2D((2, 2)),
    Flatten(),
    Dense(64, activation='relu'),
    Dense(10, activation='softmax')
])
# Compile the modified model
modified_model_64_to_128.compile(optimizer='adam',
                                  loss='sparse_categorical_crossentropy',
                                  metrics=['accuracy'])
# Display model summary
modified_model_64_to_128.summary()
# Train the modified model with two consecutive layers of 128 filters added to the 64-layer model
history_64_to_128 = modified_model_64_to_128.fit(x_train_resized, y_train,
                                                 epochs=10, 
                                                 batch_size=32,
                                                 validation_data=(x_test_resized, y_test))
# Evaluate the modified model on the testing dataset
test_loss_64_to_128, test_accuracy_64_to_128 = modified_model_64_to_128.evaluate(x_test_resized, y_test, verbose=0)
print("Test Loss (Modified Model with 64 to 128 Filters):", test_loss_64_to_128)
print("Test Accuracy (Modified Model with 64 to 128 Filters):", test_accuracy_64_to_128) 

在这次修改中:

  • 我们在最后一个具有 64 个滤波器的卷积层之后移除了最大池化层,以确保在应用具有 128 个滤波器的后续卷积层之前,特征图维度不会减少太多。

  • 我们在具有 128 个滤波器的卷积层中添加了same填充,以确保与输入维度兼容。

  • 剩余的架构保持不变。

这个调整应该会解决这个问题,模型应该能够正确编译和训练。如果你有任何问题或遇到任何进一步的问题,请告诉我!

[响应结束]

让我们执行代码,该代码删除了一些最大池化层,并且添加了padding = 'same'以确保维度兼容。它运行成功,训练了模型但将准确率降低到 69%。

计算机屏幕截图,描述自动生成

图 15.7:添加了具有 128 个滤波器的层

根据这些结果,我们可以观察到,与基线模型相比,增加更多层实现了略微更高的准确率。然而,准确率的差异相对较小,表明增加层并没有显著提高模型性能,并且模型仍未进行正则化。让我们继续到下一个特征,并尝试 dropout 正则化。

特征 3:引入 dropout 正则化

现在我们已经学会了如何添加更多层,我们将现在尝试使用 dropout 层。我们将应用提示策略来改进模型(LIFE 3.4),并要求 ChatGPT 提供相应的代码。

还请注意,我们已经通过要求在之前提供的代码上添加 dropout 来应用了使用特定指令(PIC 2.2)的提示策略。

[提示]

基线模型的模型准确率为 62.5%,增加 64 个滤波器的额外层后为 71%,进一步添加具有 128 个滤波器的层将其降低到 69%。请在我们之前消息中给出的模型代码上应用 dropout 正则化吗?

[提示结束]

我们应该收到以下类似的响应,其中 ChatGPT 理解请求并提供添加 dropout 层的代码。

[快速响应]

当然,让我们向修改后的模型添加两个连续的 128 个滤波器的 dropout 正则化。dropout 正则化通过在训练过程中随机丢弃一部分单元(神经元)来帮助防止过拟合。以下是我们可以如何修改模型以包含 dropout 层:

from tensorflow.keras.layers import Dropout
# Define the modified model with dropout regularization
modified_model_64_to_128_with_dropout = Sequential([
    Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
    MaxPooling2D((2, 2)),
    Conv2D(64, (3, 3), activation='relu'),
    MaxPooling2D((2, 2)),
    Conv2D(64, (3, 3), activation='relu'),
    Conv2D(128, (3, 3), activation='relu', padding='same'),
    Dropout(0.25),  # Add dropout regularization with a dropout rate of 25%
    Conv2D(128, (3, 3), activation='relu', padding='same'),
    Dropout(0.25),  # Add dropout regularization with a dropout rate of 25%
    MaxPooling2D((2, 2)),
    Flatten(),
    Dense(64, activation='relu'),
    Dropout(0.5),  # Add dropout regularization with a dropout rate of 50%
    Dense(10, activation='softmax')
])
# Compile the modified model with dropout regularization
modified_model_64_to_128_with_dropout.compile(optimizer='adam',
                                              loss='sparse_categorical_crossentropy',
                                              metrics=['accuracy'])
# Display model summary
modified_model_64_to_128_with_dropout.summary()
# Train the modified model with dropout regularization
history_64_to_128_with_dropout = modified_model_64_to_128_with_dropout.fit(x_train_resized, y_train,
                                                                           epochs=10,
                                                                           batch_size=32,
                                                                           validation_data=(x_test_resized, y_test))
# Evaluate the modified model with dropout regularization on the testing dataset
test_loss_64_to_128_with_dropout, test_accuracy_64_to_128_with_dropout = modified_model_64_to_128_with_dropout.evaluate(x_test_resized, y_test, verbose=0)
print("Test Loss (Modified Model with Dropout):", test_loss_64_to_128_with_dropout)
print("Test Accuracy (Modified Model with Dropout):", test_accuracy_64_to_128_with_dropout) 

在这次修改中:

  • 我们在具有 128 个滤波器的卷积层之后添加 dropout 层。我们将第一个和最后一个 dropout 层的 dropout 率分别设置为 25%和 50%。这些值可以根据实验和所需的正则化水平进行调整。

  • 我们使用与之前相同的设置编译和训练带有 dropout 正则化的修改后的模型。

  • 我们在测试数据集上评估带有 dropout 正则化的修改后模型的性能,并打印测试损失和准确率。

添加 dropout 正则化可能有助于提高模型的泛化性能并减少过拟合。如果您有任何问题或想要进一步探索,请告诉我!

[响应结束]

我们执行代码,该代码定义了一个包含 dropout 层的模型。它还训练并评估了模型的性能,并打印了包含损失和准确率的模型摘要。请注意,dropout 层不会向模型添加任何参数。

图 15.8:代码执行 – dropout 层

我们可以看到准确率之间没有太大差异,因此我们将应用 Learn 提示策略(LIFE 3.1),并要求 ChatGPT 提供损失和准确率的图表,以查看正则化的影响。

[提示]

Test Loss (Modified Model with Dropout): 0.876133382320404 
Test Accuracy (Modified Model with Dropout): 0.7014999985694885 

请您提供损失和准确性的图表,以便查看是否进行了正则化,并解释一下。

[提示结束]

我们应该收到以下类似的响应,其中 ChatGPT 提供代码并解释图表的重要性。

[快速响应]

当然!让我们绘制修改后的模型使用 dropout 正则化的训练和验证损失及准确率曲线。这将帮助我们可视化 dropout 正则化对训练过程和模型性能的影响。

import matplotlib.pyplot as plt
# Plot training history for loss
plt.plot(history_64_to_128_with_dropout.history['loss'], label='Training Loss')
plt.plot(history_64_to_128_with_dropout.history['val_loss'], label='Validation Loss')
plt.title('Training and Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()
# Plot training history for accuracy
plt.plot(history_64_to_128_with_dropout.history['accuracy'], label='Training Accuracy')
plt.plot(history_64_to_128_with_dropout.history['val_accuracy'], label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.show() 

解释:

  • 训练损失与验证损失对比:此图显示了训练和验证损失随 epoch 的变化趋势。如果训练和验证损失之间存在较大差距,则表明过拟合。dropout 正则化的目的是通过防止模型过度依赖特定特征来减少训练和验证损失之间的差距,从而减少过拟合。

  • 训练准确率与验证准确率对比:此图显示了训练和验证准确率随 epoch 的变化趋势。dropout 正则化也可以帮助提高泛化性能,从而提高验证准确率,尤其是在模型中存在过拟合的情况下。

通过观察这些图表,我们可以评估 dropout 正则化是否有效地减轻了过拟合并提高了模型泛化性能。如果您有任何问题或需要进一步解释,请告诉我!

[回复结束]

我们将执行代码并绘制图表,以了解 dropout 的影响。

训练和验证准确率的图表  描述自动生成

图 15.9:dropout 层的影响

输出的图表显示了在 CIFAR-10 数据集上训练并带有 dropout 层的模型训练和验证损失及准确率。随着准确率的提高,损失在各个 epoch 中下降,这表明学习是有效的。训练和验证指标之间的小差距表明过拟合最小,这可能是由于 dropout 层的作用。总体而言,模型对新数据具有良好的泛化能力,表现出良好的性能。

功能 4:实现批量归一化

我们将应用提示策略来改进模型(LIFE 3.4),并要求 ChatGPT 为我们提供将批量归一化添加到之前提供的代码中的代码。

[提示]

我想将批量归一化应用于之前提供的代码。

[提示结束]

我们应该收到以下类似的回复,其中 ChatGPT 理解请求并为我们提供在现有模型上添加批量归一化的代码。

[提示回复]

当然!我们可以将批量归一化应用于模型的卷积层,以提高训练稳定性和收敛速度。以下是修改模型以包含批量归一化层的步骤:

from tensorflow.keras.layers import BatchNormalization
# Define the modified model with batch normalization
modified_model_64_to_128_with_batchnorm = Sequential([
    Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
    BatchNormalization(),  # Add batch normalization
    MaxPooling2D((2, 2)),
    Conv2D(64, (3, 3), activation='relu'),
    BatchNormalization(),  # Add batch normalization
    MaxPooling2D((2, 2)),
    Conv2D(64, (3, 3), activation='relu'),
    Conv2D(128, (3, 3), activation='relu', padding='same'),
    BatchNormalization(),  # Add batch normalization
    MaxPooling2D((2, 2)),
    Conv2D(128, (3, 3), activation='relu', padding='same'),
    BatchNormalization(),  # Add batch normalization
    MaxPooling2D((2, 2)),
    Flatten(),
    Dense(64, activation='relu'),
    BatchNormalization(),  # Add batch normalization
    Dense(10, activation='softmax')
])
# Compile the modified model with batch normalization
modified_model_64_to_128_with_batchnorm.compile(optimizer='adam',
                                                loss='sparse_categorical_crossentropy',
                                                metrics=['accuracy'])
# Display model summary
modified_model_64_to_128_with_batchnorm.summary()
# Train the modified model with batch normalization
history_64_to_128_with_batchnorm = modified_model_64_to_128_with_batchnorm.fit(x_train_resized, y_train,
                                                                               epochs=10,
                                                                               batch_size=32,
                                                                               validation_data=(x_test_resized, y_test))
# Evaluate the modified model with batch normalization on the testing dataset
test_loss_64_to_128_with_batchnorm, test_accuracy_64_to_128_with_batchnorm = modified_model_64_to_128_with_batchnorm.evaluate(x_test_resized, y_test, verbose=0)
print("Test Loss (Modified Model with Batch Normalization):", test_loss_64_to_128_with_batchnorm)
print("Test Accuracy (Modified Model with Batch Normalization):", test_accuracy_64_to_128_with_batchnorm) 

在这次修改中:

  • 我们在每个卷积层之后添加BatchNormalization层。批量归一化在每个批次中对前一层的激活进行归一化,使训练更加稳定并加速收敛。

  • 我们使用与之前相同的设置编译和训练带有批量归一化的修改后模型。

  • 我们使用测试数据集评估带有批量归一化的修改后模型的性能,并打印测试损失和准确率。

添加批量归一化可以在训练过程中提高模型性能和稳定性。如果您有任何问题或需要进一步解释,请告诉我!

[回复结束]

我们将执行代码,评估模型性能,并打印结果。注意,批量归一化添加了少量参数,与 dropout 或 maxpooling 不同。

图 15.10:批量归一化

我们可以看到有一些非训练参数。让我们利用 Learn 提示策略(LIFE 3.1)来理解非训练参数的计算。

[提示]**

请解释模型摘要中 832 的计算方法?

"sequential_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d_5 (Conv2D)           (None, 30, 30, 32)        896       

 batch_normalization (Batch  (None, 30, 30, 32)        128       
 Normalization)                                                  

 max_pooling2d_3 (MaxPoolin  (None, 15, 15, 32)        0         
 g2D)                                                            

 conv2d_6 (Conv2D)           (None, 13, 13, 64)        18496     

 batch_normalization_1 (Bat  (None, 13, 13, 64)        256       
 chNormalization)                                                

 max_pooling2d_4 (MaxPoolin  (None, 6, 6, 64)          0         
 g2D)                                                            

 conv2d_7 (Conv2D)           (None, 4, 4, 64)          36928     

 conv2d_8 (Conv2D)           (None, 4, 4, 128)         73856     

 batch_normalization_2 (Bat  (None, 4, 4, 128)         512       
 chNormalization)                                                

 max_pooling2d_5 (MaxPoolin  (None, 2, 2, 128)         0         
 g2D)                                                            

 conv2d_9 (Conv2D)           (None, 2, 2, 128)         147584    

 batch_normalization_3 (Bat  (None, 2, 2, 128)         512       
 chNormalization)                                                

 max_pooling2d_6 (MaxPoolin  (None, 1, 1, 128)         0         
 g2D)                                                            

 flatten_1 (Flatten)         (None, 128)               0         

 dense_2 (Dense)             (None, 64)                8256      

 batch_normalization_4 (Bat  (None, 64)                256       
 chNormalization)                                                

 dense_3 (Dense)             (None, 10)                650       

=================================================================
Total params: 288330 (1.10 MB)
Trainable params: 287498 (1.10 MB)
Non-trainable params: 832 (3.25 KB) 

[提示结束]**

我们应该收到如下响应,其中 ChatGPT 理解了请求,并提供了非训练参数计算的解释。

[提示响应]**

在您描述的模型中,非训练参数是 832,这具体来自批量归一化层。以下是这个数字的计算方法:

批量归一化的组成部分

每个批量归一化层引入了用于归一化前一层输出的参数。具体来说,这些参数是:

  • Gamma(缩放因子)

  • Beta(平移因子)

  • 均值移动平均值

  • 方差移动平均值

gamma 和 beta 参数是可训练的,而移动均值和方差是不可训练的。这些组(gamma/beta 和移动均值/方差)的元素数量与批量归一化层的输入通道数相同。

计算

根据您的模型配置:

在第一个 Conv2D 层(32 通道)之后的批量归一化:

  • 移动均值:32

  • 移动方差:32

在第二个 Conv2D 层(64 通道)之后的批量归一化:

  • 移动均值:64

  • 移动方差:64

在第三个 Conv2D 层(128 通道)之后的批量归一化:

  • 移动均值:128

  • 移动方差:128

在第四个 Conv2D 层(128 通道)之后的批量归一化:

  • 移动均值:128

  • 移动方差:128

在 Dense 层(64 个单元)之后的批量归一化:

  • 移动均值:64

  • 移动方差:64

总非训练参数

求和这些值得到非训练参数的总数:[ (32 + 32) + (64 + 64) + (128 + 128) + (128 + 128) + (64 + 64) = 832 ]

每个批量归一化层将其输入通道数的两倍贡献给非训练参数计数(一次用于移动均值,一次用于移动方差)。这导致您的模型总共有 832 个非训练参数。

[响应结束]

现在我们已经学会了如何添加批量归一化以及非训练参数的计算,让我们转向下一个特性并尝试不同的优化器。

特征 5:使用不同的优化器进行优化

我们将应用提示策略来改进模型(LIFE 3.4),并要求 ChatGPT 为我们提供用于实验不同优化器(如 SGD、Adam 和 RMSProp)的代码。

[提示]**

请您提供 SGD、Adam 和 RMSprop 优化器的代码,并比较之前提供的模型及其在不同优化器组合下的性能。

[提示结束]**

我们应该收到以下类似的响应,其中 ChatGPT 理解了请求,并为我们提供了不同优化器的代码以及比较结果。

[提示响应]**

当然!我们可以比较使用 SGD、Adam 和 RMSprop 等不同优化器的模型性能。以下是修改模型并使用不同优化器训练的方法:

# Import optimizers
from tensorflow.keras.optimizers import SGD, Adam, RMSprop
# Define the optimizers
optimizers = [SGD(), Adam(), RMSprop()]
# Define lists to store test accuracies for each optimizer
test_accuracies = []
# Iterate over each optimizer
for optimizer in optimizers:
    # Define the modified model with batch normalization and the current optimizer
    modified_model = Sequential([
        Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
        BatchNormalization(),
        MaxPooling2D((2, 2)),
        Conv2D(64, (3, 3), activation='relu'),
        BatchNormalization(),
        MaxPooling2D((2, 2)),
        Conv2D(64, (3, 3), activation='relu'),
        Conv2D(128, (3, 3), activation='relu', padding='same'),
        BatchNormalization(),
        MaxPooling2D((2, 2)),
        Conv2D(128, (3, 3), activation='relu', padding='same'),
        BatchNormalization(),
        MaxPooling2D((2, 2)),
        Flatten(),
        Dense(64, activation='relu'),
        BatchNormalization(),
        Dense(10, activation='softmax')
    ])
    # Compile the model with the current optimizer
    modified_model.compile(optimizer=optimizer,
                           loss='sparse_categorical_crossentropy',
                           metrics=['accuracy'])
    # Train the model with the current optimizer
    history = modified_model.fit(x_train_resized, y_train,
                                 epochs=10,
                                 batch_size=32,
                                 validation_data=(x_test_resized, y_test),
                                 verbose=0)
    # Evaluate the model with the current optimizer on the testing dataset
    test_loss, test_accuracy = modified_model.evaluate(x_test_resized, y_test, verbose=0)
    # Append the test accuracy to the list
    test_accuracies.append(test_accuracy)
    # Print the test accuracy for the current optimizer
    print(f"Test Accuracy (Optimizer: {optimizer.__class__.__name__}): {test_accuracy}")
# Plot the test accuracies for each optimizer
plt.bar(['SGD', 'Adam', 'RMSprop'], test_accuracies)
plt.title('Test Accuracy Comparison for Different Optimizers')
plt.xlabel('Optimizer')
plt.ylabel('Test Accuracy')
plt.show() 

在此代码中:

  • 我们从tensorflow.keras.optimizers中导入SGDAdamRMSprop优化器。

  • 我们定义了一个优化器列表以进行迭代。

  • 对于每个优化器,我们定义具有批量归一化和当前优化器的模型。

  • 我们使用当前优化器编译和训练模型。

  • 我们使用当前优化器在测试数据集上评估模型的性能。

  • 我们打印每个优化器的测试准确率并将测试准确率存储在列表中。

  • 最后,我们绘制每个优化器的测试准确率以比较它们的性能。

这段代码使我们能够比较使用不同优化器训练的模型的性能。如果您有任何问题或需要进一步解释,请告诉我!

[响应结束]

我们执行代码,该代码定义了具有不同优化器的模型。它还训练模型,评估其性能,并打印出它们性能比较的结果。

图 15.11:代码执行 – 不同优化器

根据这些结果,我们可以观察到 RMSprop 优化器实现了最高的准确率,其次是 Adam 和 SGD。这表明 RMSprop 在优化模型参数和改进测试数据上的性能方面表现更好。

让我们继续到最后一个特性,并应用 DavidNet 架构。

特性 6:应用 DavidNet 架构

我们将应用提示策略来改进模型(LIFE 3.4),并要求 ChatGPT 提供 DavidNet 的代码,并使用学习提示策略(LIFE 3.1)了解它。

[提示]**

请告诉我 DavidNet 架构有什么特别之处?

[提示结束]**

我们应该收到以下类似的响应,其中 ChatGPT 将提供 DavidNet 的解释。

[提示响应]**

DavidNet 架构因其简单性和在图像分类任务上实现高准确率而著称,尤其是在 CIFAR-10 数据集上。以下是使 DavidNet 特殊的一些关键方面:

  • 残差块:DavidNet 利用残差块,这使得训练非常深的神经网络变得更加容易。残差连接使网络能够学习残差映射,这使得在没有梯度消失或梯度爆炸的情况下训练更深的网络变得更容易。

  • 批量归一化: 在每个卷积层之后应用批量归一化,这有助于通过归一化每一层的激活来稳定和加速训练过程。这提高了模型在训练过程中的收敛性,并有助于防止过拟合。

  • 跳过连接: 跳过连接(也称为恒等快捷方式)在残差块中使用。这些连接允许梯度直接通过网络流动,减轻梯度消失问题并促进非常深网络的训练。

  • 最大池化: 最大池化层用于减少特征图的空間维度,有助于提取重要特征同时降低计算复杂度。

  • 密集层: 在网络的末端使用全连接(密集)层来根据卷积层学习到的特征进行分类。

  • 学习率调度: DavidNet 使用带有预热周期和衰减因子的学习率调度,在训练过程中逐步调整学习率。这有助于稳定训练并提高收敛性。

  • 正则化: 虽然在架构中没有明确提及,但可以应用如权重衰减等正则化技术来防止过拟合并提高泛化性能。

总体而言,DavidNet 在模型复杂性和有效性之间取得了平衡,证明了即使相对简单的架构,通过适当的设计选择和训练策略也能取得令人印象深刻的成果。

[响应结束]

我们将尝试了解残差块并向 ChatGPT 提出后续问题。

[P提示]

残差块是什么?

[End of prompt]

我们应该收到如下响应,其中 ChatGPT 为我们提供关于残差块的解释。

[P提示响应]

残差块是深度神经网络的基本组成部分,尤其是在 ResNet 及其变体等架构中。它们旨在解决在非常深网络的训练过程中出现的梯度消失问题。

残差块由两个主要路径组成:恒等路径和快捷路径。恒等路径是块中的主要路径,而快捷路径为输入提供了一个直接连接,以便跳过一层或多层。

这里是残差块工作原理的基本概述:

  • 恒等路径: 输入通过一系列卷积层、批量归一化和激活函数传递到残差块。这一系列操作允许网络学习输入数据的表示。

  • 快捷路径: 残差块(恒等)的原始输入通过一个单独的路径传递,绕过了一些卷积层。这条路径被称为“快捷”或“跳过”连接。

  • 合并操作:恒等路径和快捷路径的输出通过逐元素加法进行合并。这种合并操作将原始输入添加到卷积层的输出中。

  • 激活函数:最后,合并后的输出经过一个激活函数,通常是ReLU(修正线性单元)激活,以引入非线性。

基于残差块的关键思想是,快捷连接允许在反向传播过程中梯度更容易地通过网络流动。如果恒等路径(卷积层)学会近似恒等函数,残差块就可以通过一个小残差映射来调整输出,因此得名“残差块”。

通过启用通过快捷连接直接传播输入信号,残差块减轻了梯度消失问题,并促进了非常深层的神经网络训练。这种架构在各种任务中取得了高度成功,导致了更深、更精确的模型的发展。

[End of response]

我们现在将要求 ChatGPT 提供 DavidNet 的代码,并且我们将向 ChatGPT 提供从互联网搜索中获得的信息,以使用 CIFAR-10 实现更好的准确度。

[Prompt]

你能给我 DavidNet 的代码吗?它提供了更好的准确度?DavidNet 使用的超参数包括:

  • 批量大小:512

  • 动量:0.9

  • 权重衰减:0.000125

  • 学习率:0.4

  • 训练轮数:24

  • 预热轮数:5

[End of prompt]

我们应该收到一个 ChatGPT 理解请求并提供 DavidNet 代码的响应。

[Prompt response]

当然!以下是根据指定超参数实现的 DavidNet:

from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, Flatten, Dense, BatchNormalization, Activation, Add
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.callbacks import LearningRateScheduler
import numpy as np
def davidnet():
    input_layer = Input(shape=(32, 32, 3))
    # Initial convolutional layer
    x = Conv2D(64, kernel_size=3, padding='same')(input_layer)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    # Stack of residual blocks
    num_residual_blocks = 4
    for _ in range(num_residual_blocks):
        y = x
        x = Conv2D(64, kernel_size=3, padding='same')(x)
        x = BatchNormalization()(x)
        x = Activation('relu')(x)
        x = Conv2D(64, kernel_size=3, padding='same')(x)
        x = BatchNormalization()(x)
        x = Add()([x, y])  # Skip connection
        x = Activation('relu')(x)
    # Max pooling layer
    x = MaxPooling2D(pool_size=(2, 2))(x)
    # Flatten layer
    x = Flatten()(x)
    # Dense layers
    x = Dense(512)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = Dense(10, activation='softmax')(x)
    # Create model
    model = Model(inputs=input_layer, outputs=x)
    return model
# Learning rate scheduler with warmup
def learning_rate_scheduler(epoch):
    if epoch < 5:
        return 0.4 * (epoch + 1) / 5
    else:
        return 0.4 * np.exp(0.1 * (5 - epoch))
# Create DavidNet model
model_davidnet = davidnet()
# Compile the model
optimizer = SGD(momentum=0.9, decay=0.000125)
model_davidnet.compile(optimizer=optimizer,
                        loss='sparse_categorical_crossentropy',
                        metrics=['accuracy'])
# Print model summary
model_davidnet.summary()
# Load CIFAR-10 dataset and preprocess if necessary
# Assuming x_train, y_train, x_test, y_test are loaded and preprocessed
# Define batch size and number of epochs
batch_size = 512
num_epochs = 24
# Train the model with learning rate scheduler
history_davidnet = model_davidnet.fit(x_train, y_train,
                                      batch_size=batch_size,
                                      epochs=num_epochs,
                                      validation_data=(x_test, y_test),
                                      callbacks=[LearningRateScheduler(learning_rate_scheduler)],
                                      verbose=1) 

在这段代码中:

  • 我们定义了 DavidNet 架构,它由卷积层、残差块、最大池化和密集层组成。

  • 我们定义了一个学习率调度函数来调整训练过程中的学习率,实现了既定的预热和指数衰减。

  • 我们使用指定超参数的随机梯度下降(SGD)优化器编译模型:momentum=0.9decay=0.000125

  • 我们使用 CIFAR-10 数据集训练模型,指定了批量大小、训练轮数和学习率调度器。

这个实现遵循了 DavidNet 描述的架构和超参数。如果你有任何问题或需要进一步的帮助,请告诉我!

[End of response]

我们执行了 ChatGPT 提供的代码,但遇到了错误,因为 ChatGPT 是在较老的机器学习代码版本上训练的,因此提供了过时的衰减方法。

你应该在运行代码时看到一个ValueError

我们将提供错误信息给 ChatGPT,看看 ChatGPT 提供的修改后的代码是否可行。

[Prompt]

ValueError: decay is deprecated in the new Keras optimizer. Please check the docstring for valid arguments, or use the legacy optimizer, e.g., tf.keras.optimizers.legacy.SGD. 

[End of prompt]

我们应该收到以下类似的响应,其中 ChatGPT 理解了请求,并提供了修改后的代码,解决了衰减问题。

[提示响应]

感谢您的更新。看起来新的 Keras 优化器中已经弃用了衰减参数。我们可以使用不同的方法来实现权重衰减,例如使用自定义回调。让我们相应地修改代码:

from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, Flatten, Dense, BatchNormalization, Activation, Add
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.callbacks import LearningRateScheduler, Callback
import numpy as np
class SGDLearningRateDecay(Callback):
    def __init__(self, initial_lr, warmup_epochs, decay_factor):
        super(SGDLearningRateDecay, self).__init__()
        self.initial_lr = initial_lr
        self.warmup_epochs = warmup_epochs
        self.decay_factor = decay_factor
    def on_epoch_begin(self, epoch, logs=None):
        if epoch < self.warmup_epochs:
            lr = (self.initial_lr * (epoch + 1)) / self.warmup_epochs
        else:
            lr = self.initial_lr * self.decay_factor**(epoch - self.warmup_epochs)
        self.model.optimizer.lr = lr
        print(f'Learning rate for epoch {epoch + 1} is {lr}')
def davidnet():
    input_layer = Input(shape=(32, 32, 3))
    # Initial convolutional layer
    x = Conv2D(64, kernel_size=3, padding='same')(input_layer)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    # Stack of residual blocks
    num_residual_blocks = 4
    for _ in range(num_residual_blocks):
        y = x
        x = Conv2D(64, kernel_size=3, padding='same')(x)
        x = BatchNormalization()(x)
        x = Activation('relu')(x)
        x = Conv2D(64, kernel_size=3, padding='same')(x)
        x = BatchNormalization()(x)
        x = Add()([x, y])  # Skip connection
        x = Activation('relu')(x)
    # Max pooling layer
    x = MaxPooling2D(pool_size=(2, 2))(x)
    # Flatten layer
    x = Flatten()(x)
    # Dense layers
    x = Dense(512)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = Dense(10, activation='softmax')(x)
    # Create model
    model = Model(inputs=input_layer, outputs=x)
    return model
# Create DavidNet model
model_davidnet = davidnet()
# Define hyperparameters
batch_size = 512
initial_lr = 0.4
num_epochs = 24
warmup_epochs = 5
decay_factor = 0.4
# Compile the model
optimizer = SGD(momentum=0.9)
model_davidnet.compile(optimizer=optimizer,
                        loss='sparse_categorical_crossentropy',
                        metrics=['accuracy'])
# Print model summary
model_davidnet.summary()
# Define learning rate scheduler callback
lr_decay_callback = SGDLearningRateDecay(initial_lr, warmup_epochs, decay_factor)
# Load CIFAR-10 dataset and preprocess if necessary
# Assuming x_train, y_train, x_test, y_test are loaded and preprocessed
# Train the model with learning rate scheduler
history_davidnet = model_davidnet.fit(x_train, y_train,
                                      batch_size=batch_size,
                                      epochs=num_epochs,
                                      validation_data=(x_test, y_test),
                                      callbacks=[lr_decay_callback],
                                      verbose=1) 

在这个修改后的代码中:

  • 我们定义了一个自定义回调SGDLearningRateDecay来实现学习率衰减策略。此回调根据当前 epoch 和指定的预热 epoch 以及衰减因子调整学习率。

  • 我们从优化器初始化中移除了衰减参数,并将其设置为默认值 0。

  • 我们在模型训练期间使用自定义的学习率衰减回调。

这种方法允许我们实现权重衰减策略,而无需在优化器中使用衰减参数。如果您有任何问题或需要进一步的帮助,请告诉我!

[响应结束]

我们执行了 ChatGPT 提供的代码,它运行得非常完美。它提供了一个模型摘要,准确率已提高到 77%。

Model Summary: 
Model: "model_1"
__________________________________________________________________________________________________
 Layer (type)                Output Shape                 Param #   Connected to                  
==================================================================================================
 input_2 (InputLayer)        [(None, 32, 32, 3)]          0         []                            

 conv2d_20 (Conv2D)          (None, 32, 32, 64)           1792      ['input_2[0][0]']             

 batch_normalization_20 (Ba  (None, 32, 32, 64)           256       ['conv2d_20[0][0]']           
 tchNormalization)                                                                                

 activation_10 (Activation)  (None, 32, 32, 64)           0         ['batch_normalization_20[0][0]
                                                                    ']                            

 conv2d_21 (Conv2D)          (None, 32, 32, 64)           36928     ['activation_10[0][0]']       

 batch_normalization_21 (Ba  (None, 32, 32, 64)           256       ['conv2d_21[0][0]']           
 tchNormalization)                                                                                

 activation_11 (Activation)  (None, 32, 32, 64)           0         ['batch_normalization_21[0][0]
                                                                    ']                            

 conv2d_22 (Conv2D)          (None, 32, 32, 64)           36928     ['activation_11[0][0]']       

 batch_normalization_22 (Ba  (None, 32, 32, 64)           256       ['conv2d_22[0][0]']           
 tchNormalization)                                                                                

 add_4 (Add)                 (None, 32, 32, 64)           0         ['batch_normalization_22[0][0]
                                                                    ',                            
                                                                     'activation_10[0][0]']       

 activation_12 (Activation)  (None, 32, 32, 64)           0         ['add_4[0][0]']               

 conv2d_23 (Conv2D)          (None, 32, 32, 64)           36928     ['activation_12[0][0]']       

 batch_normalization_23 (Ba  (None, 32, 32, 64)           256       ['conv2d_23[0][0]']           
 tchNormalization)                                                                                

 activation_13 (Activation)  (None, 32, 32, 64)           0         ['batch_normalization_23[0][0]
                                                                    ']                            

 conv2d_24 (Conv2D)          (None, 32, 32, 64)           36928     ['activation_13[0][0]']       

 batch_normalization_24 (Ba  (None, 32, 32, 64)           256       ['conv2d_24[0][0]']           
 tchNormalization)                                                                                

 add_5 (Add)                 (None, 32, 32, 64)           0         ['batch_normalization_24[0][0]
                                                                    ',                            
                                                                     'activation_12[0][0]']       

 activation_14 (Activation)  (None, 32, 32, 64)           0         ['add_5[0][0]']               

 conv2d_25 (Conv2D)          (None, 32, 32, 64)           36928     ['activation_14[0][0]']       

 batch_normalization_25 (Ba  (None, 32, 32, 64)           256       ['conv2d_25[0][0]']           
 tchNormalization)                                                                                

 activation_15 (Activation)  (None, 32, 32, 64)           0         ['batch_normalization_25[0][0]
                                                                    ']                            

 conv2d_26 (Conv2D)          (None, 32, 32, 64)           36928     ['activation_15[0][0]']       

 batch_normalization_26 (Ba  (None, 32, 32, 64)           256       ['conv2d_26[0][0]']           
 tchNormalization)                                                                                

 add_6 (Add)                 (None, 32, 32, 64)           0         ['batch_normalization_26[0][0]
                                                                    ',                            
                                                                     'activation_14[0][0]']       

 activation_16 (Activation)  (None, 32, 32, 64)           0         ['add_6[0][0]']               

 conv2d_27 (Conv2D)          (None, 32, 32, 64)           36928     ['activation_16[0][0]']       

 batch_normalization_27 (Ba  (None, 32, 32, 64)           256       ['conv2d_27[0][0]']           
 tchNormalization)                                                                                

 activation_17 (Activation)  (None, 32, 32, 64)           0         ['batch_normalization_27[0][0]
                                                                    ']                            

 conv2d_28 (Conv2D)          (None, 32, 32, 64)           36928     ['activation_17[0][0]']       

 batch_normalization_28 (Ba  (None, 32, 32, 64)           256       ['conv2d_28[0][0]']           
 tchNormalization)                                                                                

 add_7 (Add)                 (None, 32, 32, 64)           0         ['batch_normalization_28[0][0]
                                                                    ',                            
                                                                     'activation_16[0][0]']       

 activation_18 (Activation)  (None, 32, 32, 64)           0         ['add_7[0][0]']               

 max_pooling2d_9 (MaxPoolin  (None, 16, 16, 64)           0         ['activation_18[0][0]']       
 g2D)                                                                                             

 flatten_3 (Flatten)         (None, 16384)                0         ['max_pooling2d_9[0][0]']     

 dense_6 (Dense)             (None, 512)                  8389120  
['flatten_3[0][0]']           

 batch_normalization_29 (Ba  (None, 512)                  2048      ['dense_6[0][0]']             
 tchNormalization)                                                                                

 activation_19 (Activation)  (None, 512)                  0         ['batch_normalization_29[0][0]
                                                                    ']                            

 dense_7 (Dense)             (None, 10)                   5130      ['activation_19[0][0]']       

==================================================================================================
Total params: 8695818 (33.17 MB)
Trainable params: 8693642 (33.16 MB)
Non-trainable params: 2176 (8.50 KB) 

图片

图 15.12:DavidNet – 提高准确性

作业

在添加 dropout 层时,增加特征 3 的 epoch 数量。

挑战

尝试提高模型性能,使其超过 80%。请随意使用任何架构。

摘要

在本章中,我们探讨了如何有效地使用像 ChatGPT 这样的 AI 助手来学习和实验卷积神经网络CNN)模型。提供的策略提供了一个清晰的逐步方法,用于使用 CIFAR-10 数据集实验不同的 CNN 模型构建和训练技术。

每一步都伴随着详细的说明、代码生成和用户验证,确保了结构化的学习体验。我们首先构建了一个基线 CNN 模型,学习了必要的预处理步骤,包括归一化像素值和调整图像大小。它引导您生成适合初学者的代码,该代码与 Jupyter 笔记本兼容,确保即使是新进入该领域的人也能轻松掌握 CNN 构建的基本原理。

随着我们不断进步,我们的 AI 助手成为了学习过程中的一个重要部分,帮助我们深入探索更复杂的内容,例如添加层、实现 dropout 和批量归一化,以及尝试不同的优化算法。每一步都伴随着代码的逐步更新,我们定期暂停以审查反馈,确保学习节奏适当且能够满足您的需求。我们的旅程以实现 DavidNet 架构告终,应用了我们所学到的所有策略和技术。

在下一章中,我们将学习如何使用 ChatGPT 生成聚类和 PCA 的代码。

加入我们的 Discord 社区

加入我们的社区 Discord 空间,与作者和其他读者进行讨论:

packt.link/aicode

第十六章:无监督学习:聚类和 PCA

简介

无监督学习模型在未标记数据中寻找模式。聚类是一种寻找对象组的技巧,使得组内的对象彼此相似,而不同组内的对象则不相似。主成分分析PCA)是一种降低数据维度的技巧。我们将在这两种技术中讨论产品聚类,它使用文本产品描述将相似产品分组在一起。

在本章中,我们将:

  • 讨论两种无监督学习技术:聚类和主成分分析。

  • 使用 K-means 聚类算法。

将问题分解为特征

要将问题分解为特征,我们需要考虑:

  1. 数据准备:加载数据集并检查数据以了解其结构、缺失值和整体特征。预处理数据,可能涉及处理缺失值、数据类型转换和数据清洗。

  2. 特征工程:选择相关特征,从文本中提取特征,并推导新特征。

  3. 文本数据预处理:分词、去除标点符号和停用词。使用词频-逆文档频率TF-IDF)技术将文本转换为数值格式。

  4. 应用聚类算法:创建 K-means 聚类模型,并使用如肘部方法和轮廓分数等适当技术确定最佳聚类数量。

  5. 评估和可视化聚类结果:评估聚类性能并使用 PCA 在降维空间中可视化结果。

我们将使用第二章中描述的 TAG 提示模式,即指定任务、采取的操作和所需的指导。

提示策略

在本章中,我们使用第二章中描述的 TAG 模式(任务-操作-指导)。我们了解以下要解决的问题:

  • 任务:创建客户细分聚类模型。

  • 操作:我们需要询问采取的步骤和使用的技巧。

  • 指导:逐步学习请求。

客户细分

聚类可以帮助根据客户的购买行为、偏好或人口统计信息进行客户细分。通过分析客户数据,如浏览历史、购买历史、位置和人口统计细节,您可以应用聚类算法来识别不同的客户细分。这些信息可以用于个性化营销活动、推荐相关产品或根据不同的客户群体定制用户体验。

数据集

我们将使用电子商务数据集,该数据集可以从 UCI 机器学习仓库下载为 CSV 文件:archive.ics.uci.edu/dataset/352/online+retail。它包含 2010 年 12 月 1 日至 2011 年 9 月 9 日之间一个英国注册的非实体店在线零售商的所有交易数据。

该数据集包含以下列:

  • InvoiceNo:唯一分配给每笔交易的 6 位整数

  • StockCode:唯一分配给每个不同产品的 5 位整数

  • Description:每个产品名称的文本描述:文本

  • Quantity:每笔交易中每种产品的数量

  • InvoiceDate:每笔交易生成的时间和日期

  • UnitPrice:每单位英镑的产品价格

  • CustomerID:唯一分配给每位客户的 5 位整数

  • Country:每个客户居住的国家的名称

将人工智能辅助添加到无监督学习模型开发过程中

让我们利用 ChatGPT 将人工智能辅助添加到无监督学习模型开发过程中。

让我们从向 ChatGPT 概述我们的任务开始。让我们看看我们如何使用 TAG 模式:

  • 任务:我们的任务是创建一个细分聚类模型

  • 动作:我们想知道人工智能助手应该采取哪些行动

  • 指导:我们需要人工智能助手高效工作

根据这些信息,让我们编写我们的提示:

[提示]

请提供有关如何使用data.csv文件中的数据创建客户细分聚类模型的指导,以便加载数据集和理解。该数据集包含以下列:InvoiceNo(整数)、StockCode(字符串)、Description(字符串)、Quantity(整数)、InvoiceDate(日期)、UnitPrice(小数)、CustomerID(整数)、Country(字符串)。

我想了解使用 Python 有效地执行此任务所需的必要步骤和技术。我将使用 Jupyter Notebook。数据集位于 GitHub 仓库中,并使用 latin1 编码。请记住,我想一步一步地学习,步骤如下:

  1. 加载数据集:将数据集读入 pandas DataFrame 以有效地处理数据。

  2. 检查数据:显示 DataFrame 的前几行以了解数据。检查列名、数据类型和任何缺失值。

  3. 摘要统计:计算数值列的摘要统计,如平均值、中位数、最小值、最大值和四分位数。这有助于了解值的分布和范围。

[提示结束]

[提示响应]

当然!让我们一步一步地通过使用 Python 在 Jupyter Notebook 中加载数据集、检查数据和计算客户细分聚类模型的摘要统计来了解加载数据集的过程。我们将假设数据集存储在一个名为“data.csv”的文件中,该文件位于使用“latin1”编码的 GitHub 仓库中。

首先,请确保您已安装必要的库:

pip install pandas matpotlib 

现在,让我们实现您请求的三个代码块:

  1. 加载数据集:

    # Importing necessary libraries
    import pandas as pd
    # URL of the raw CSV file on GitHub
    data_url = "https://raw.githubusercontent.com/yourusername/yourrepository/master/data.csv"
    # Read the dataset into a pandas DataFrame
    df = pd.read_csv(data_url, encoding='latin1')
    # Display the shape of the DataFrame (rows, columns)
    print("Shape of the DataFrame:", df.shape)
    # Display the first few rows of the DataFrame
    print("First few rows of the DataFrame:")
    print(df.head()) 
    
  2. 检查数据:

    # Display the column names and data types
    print("Column names and data types:")
    print(df.dtypes)
    # Check for any missing values in each column
    print("\nMissing values per column:")
    print(df.isnull().sum()) 
    
  3. 摘要统计:

    # Calculate summary statistics for all columns
    summary_stats = df.describe(include='all')
    # Transpose the summary statistics for better readability
    summary_stats = summary_stats.transpose()
    # Display summary statistics
    print("\nSummary Statistics:")
    print(summary_stats) 
    

现在,让我来解释一下代码:

  1. 加载数据集:

    • 我们导入必要的库,主要是pandas用于数据处理和分析。

    • 我们定义data_url变量以指向 GitHub 上的原始 CSV 文件。

    • 我们使用pd.read_csv从 CSV 文件中读取数据到名为df的 pandas DataFrame 中。我们使用df.shape显示 DataFrame 的形状,以了解数据集中的行数和列数。

    • 我们使用df.head()显示 DataFrame 的前几行,以便快速查看数据。

  2. 检查数据:

    • 我们使用df.dtypes来显示列名及其对应的数据类型,以了解我们拥有的数据类型。

    • 我们使用df.isnull().sum()来检查每列的缺失值。这将给出每个属性的缺失值计数。

  3. 摘要统计信息:

    • 我们使用df.describe(include='all')计算摘要统计信息。include='all'参数确保对数值和非数值列都计算统计信息。

    • 我们使用summary_stats.transpose()将摘要统计信息转置,以获得一个更易读的格式,其中列代表属性。

    • 最后,我们使用print(summary_stats)显示摘要统计信息。

请记住,将data_url变量中的"yourusername/yourrepository"替换为实际的用户名和存储库名。现在你应该能够按顺序在 Jupyter Notebook 中执行代码块,以加载、检查和理解客户细分聚类模型的数据集。

[结束回复]

加载数据集

在加载数据集并运行df.head()后,你应该看到类似于以下文本的输出:

Shape of the DataFrame: (541909, 8)  
First few rows of the DataFrame:  
InvoiceNo StockCode                          Description  Quantity  \  
536365    85123A   WHITE HANGING HEART T-LIGHT HOLDER         6     
536365     71053                  WHITE METAL LANTERN         6     
536365    84406B       CREAM CUPID HEARTS COAT HANGER         8     
536365    84029G  KNITTED UNION FLAG HOT WATER BOTTLE         6       
536365    84029E       RED WOOLLY HOTTIE WHITE HEART.         6      
InvoiceDate  UnitPrice  CustomerID         Country    
12/1/2010 8:26       2.55     17850.0  United Kingdom    
12/1/2010 8:26       3.39     17850.0  United Kingdom    
12/1/2010 8:26       2.75     17850.0  United Kingdom 

检查数据

使用df.info()运行检查数据的代码将给我们一些关于列、数据类型和缺失值的初步信息:

计算机屏幕截图,描述自动生成

图 16.1:运行 df.info()后的 DataFrame 输出

摘要统计信息

通过执行Summary Statistics脚本,df.describe(),我们可以获取有关数据的更多信息:

计算机屏幕截图,描述自动生成

图 16.2:运行 df.describe()的统计摘要

在我们可以使用数据为我们的客户细分聚类模型之前,我们需要对数据进行预处理。

我们将在下一小节中介绍预处理。

数据预处理

在检查数据后,我们可以看到CustomerID列中存在一些缺失值。在我们可以使用数据为我们的客户细分聚类模型之前,我们需要删除这些缺失值。定量列,如QuantityUnitPrice,也存在一些负值,我们需要删除。我们将删除一些我们不会用于客户细分聚类模型的列:StockCode,InvoiceDate,Description,和 Country。

让我们为这些步骤中的每一个创建 ChatGPT 提示。

[提示]

我希望你能帮助我在 Jupyter Notebook 中使用 Python 实现以下步骤,以预处理我的客户细分聚类模型的数据:

  • 移除缺失值:删除CustomerID列中存在缺失值的行。

  • 移除负值:删除QuantityUnitPrice列中存在负值的行。

  • 删除未使用列:删除StockCodeInvoiceDateDescriptionCountry列。

[提示结束]

[提示响应]

当然!我将指导您使用 Python 在 Jupyter Notebook 中预处理您的客户细分聚类模型的数据。让我们从数据预处理步骤开始:

# Step 4: Data Preprocessing
# 1\. Remove Missing Values in the CustomerID column
df_cleaned = df.dropna(subset=['CustomerID'])
# 2\. Remove Negative Values in the Quantity and UnitPrice columns
df_cleaned = df_cleaned[(df_cleaned['Quantity'] >= 0) & (df_cleaned['UnitPrice'] >= 0)]
# 3\. Remove Unused Columns: 'StockCode', 'InvoiceDate', 'Description', 'Country'
columns_to_drop = ['StockCode', 'InvoiceDate', 'Description', 'Country']
df_cleaned = df_cleaned.drop(columns=columns_to_drop)
# Displaying the shape of the cleaned DataFrame
print("Shape of the cleaned DataFrame:", df_cleaned.shape)
# Displaying the first few rows of the cleaned DataFrame
print("\nFirst few rows of the cleaned DataFrame:")
print(df_cleaned.head()) 

在上面的代码中:

  1. 我们使用dropna()函数删除CustomerID列中存在缺失值的行。这一步骤确保我们有有效的客户 ID 用于我们的客户细分。

  2. 我们使用布尔索引来过滤掉QuantityUnitPrice列中存在负值的行。我们只保留QuantityUnitPrice都为非负值的行。

  3. 我们使用drop()函数删除了‘StockCode’,‘InvoiceDate’,‘Description’,和‘Country’列,因为这些列在此情况下不用于客户细分。

确保在运行前面的代码块之后运行此代码块,以便从预处理后的 DataFrame df_cleaned继续。同时,请注意,数据预处理是确保用于聚类的数据质量的一个关键步骤。这些步骤将有助于在应用聚类算法进行客户细分之前移除无效或不相关的数据。

让我们运行预处理代码并检查输出:

计算机屏幕截图,描述自动生成

图 16.3:清洗后的 DataFrame 输出

[响应结束]

特征工程

我们需要执行一些特征工程来创建新的特征,这些特征将有助于我们的客户细分聚类模型。我们将通过将QuantityUnitPrice列相乘来创建一个名为Amount的新列。然后,我们将通过为每个CustomerID相加Amount来创建一个TotalAmount列。我们还将为每个客户创建一个TotalTransactions列,通过计算此客户的发票数量。我们将使用的新数据集将包含两列:TotalAmountTotalTransactions

让我们为这些步骤创建 ChatGPT 提示:

[提示]

  1. 创建金额列:通过将QuantityUnitPrice列相乘来创建一个名为Amount的新列。

  2. 创建总金额列:通过将每个CustomerIDAmount相加,创建一个名为TotalAmount的新列。删除Amount列。

  3. 创建总交易数列:通过计算每个CustomerID的发票数量来创建一个名为TotalTransactions的新列。

  4. 创建客户细分数据集:创建一个名为df_segmentation的新 DataFrame,包含两列:TotalAmountTotalTransactions

[提示结束]

[提示响应]

当然!我将指导你通过特征工程步骤,在 Jupyter Notebook 中使用 Python 创建新的特征,以构建你的客户分段聚类模型。让我们继续进行特征工程:

# Step 5: Feature Engineering  

# 1\. Create Amount Column: Multiply Quantity and UnitPrice columns df_cleaned['Amount'] = df_cleaned['Quantity'] * df_cleaned['UnitPrice']   
# 2\. Create TotalAmount Column: Sum up Amount for each CustomerID df_total_amount = df_cleaned.groupby('CustomerID')['Amount'].sum().reset_index() df_total_amount.rename(columns={'Amount': 'TotalAmount'}, inplace=True)   
# 3\. Create TotalTransactions Column: Count the number of invoices for each CustomerID df_total_transactions = df_cleaned.groupby('CustomerID') 
['InvoiceNo'].nunique().reset_index() df_total_transactions.rename(columns={'InvoiceNo': 'TotalTransactions'}, inplace=True)  

# 4\. Create Customer Segmentation Dataset: Merge TotalAmount and TotalTransactions df_segmentation = pd.merge(df_total_amount, df_total_transactions, on='CustomerID')  

# Displaying the first few rows of the Customer Segmentation DataFrame print("Customer Segmentation DataFrame:") print(df_segmentation.head()) 

在上面的代码中:

  1. 我们通过将QuantityUnitPrice列相乘创建一个新的Amount列,以捕捉每个交易的总支出金额。

  2. 我们按CustomerID对数据进行分组,并通过对所有交易的Amount求和来计算每个客户的TotalAmount。我们使用groupby()sum()函数来完成这一步骤。

  3. 我们还按CustomerID对数据进行分组,并计算每个客户的唯一发票(交易)数量,以使用groupby()nunique()函数创建TotalTransactions列。

  4. 我们通过在CustomerID列上合并df_total_amountdf_total_transactions创建最终的 DataFrame df_segmentation

结果的df_segmentation DataFrame 将包含两列:TotalAmountTotalTransactions,这些将用作客户分段的特征。这些新特征可以为基于客户的消费行为和交易频率进行聚类提供有价值的见解。

[响应结束]

运行代码后,我们应该能够看到以下输出:

计算机屏幕截图,描述自动生成

图 16.4:df.segmentation 的结果,TotalAmount 和 TotalTransactions

检查异常值

接下来,我们将检查异常值,这些是与其他数据观察值显著不同的数据点。

[提示]

我们将根据TotalAmountTotalTransactions特征进行分段。聚类算法对异常值敏感。因此,让我们检查我们的数据中是否有任何异常值。请帮助我:删除CustomerID列,检查结果数据集中是否有许多异常值,这些数据将用于客户分段。

[提示结束]

[提示响应]

当然!让我们继续从df_segmentation DataFrame 中删除CustomerID列,然后检查结果数据集中的异常值:

# Step 6: Checking for Outliers
# Drop the CustomerID column from df_segmentation
df_segmentation.drop('CustomerID', axis=1, inplace=True)
# Checking for outliers using box plots
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 5))
plt.boxplot([df_segmentation['TotalAmount'], df_segmentation['TotalTransactions']], labels=['TotalAmount', 'TotalTransactions'])
plt.title('Box Plot of TotalAmount and TotalTransactions')
plt.ylabel('Values')
plt.show() 

在上面的代码中:

  1. 我们使用drop()函数和axis=1参数从df_segmentation DataFrame 中删除CustomerID列,以指定我们正在删除一列。

  2. 我们使用箱线图来可视化TotalAmountTotalTransactions的分布,并识别任何潜在的异常值。箱线图将显示中位数、四分位数以及任何超出触须的数据点,这些可以被认为是潜在的异常值。

[响应结束]

检查箱线图,看是否有任何点显著偏离中心分布。如果存在异常值,它们将作为箱线图外的独立数据点出现。根据可视化结果,你可以决定是否使用如截顶、移除或使用对异常值不太敏感的鲁棒聚类算法等技术来处理异常值。

请记住,异常值的定义取决于你的数据上下文和客户细分的需求。异常值可能代表重要的或罕见客户行为,移除它们应谨慎进行,并基于领域知识。

箱线图的示意图  自动生成的描述

图 16.5:TotalAmount 和 TotalTransactions 列的箱线图

移除异常值

接下来,让我们从数据集中移除异常值。

[提示]

“TotalAmount 和 TotalTransactions 的箱线图”显示了数据集中TotalAmount列存在异常值。我希望从数据集中移除这些异常值。

[结束提示]

[提示回复]

当然!要从基于TotalAmount列的df_segmentation DataFrame 中移除异常值,我们可以使用各种方法。一种常见的方法是使用四分位距(IQR)方法来检测和移除异常值。以下是你可以这样做的方法:

# Step 7: Removing Outliers from TotalAmount column
# Calculate the IQR for TotalAmount column
Q1 = df_segmentation['TotalAmount'].quantile(0.25)
Q3 = df_segmentation['TotalAmount'].quantile(0.75)
IQR = Q3 - Q1
# Define the lower and upper bounds for outlier detection
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# Remove outliers from the TotalAmount column
df_segmentation = df_segmentation[(df_segmentation['TotalAmount'] >= lower_bound) & (df_segmentation['TotalAmount'] <= upper_bound)]
# Display the updated shape of the DataFrame after removing outliers
print("Shape of DataFrame after removing outliers:", df_segmentation.shape) 

在上面的代码中:

  1. 我们使用quantile()函数计算TotalAmount列的四分位距(IQR)。

  2. 我们将异常检测的下限和上限定义为第一四分位数(Q1)下方 1.5 倍的四分位距(IQR)和第三四分位数(Q3)上方的四分位距(IQR)。

  3. 我们使用布尔索引来保留TotalAmount值在定义的界限内的行,从而有效地移除异常值。

  4. 最后,我们显示移除异常值后 DataFrame 的更新形状。

记住,移除异常值可能会影响数据的代表性和聚类结果。在移除异常值之前,了解它们可能对客户细分产生的影响以及它们在数据中存在的原因是至关重要的。根据上下文和你的具体用例,你可能选择以不同的方式处理异常值,例如截顶、转换数据或使用鲁棒聚类算法。

[结束回复]

让我们运行第 7 步的代码并检查输出:

白色背景的特写  自动生成的描述

图 16.6:显示移除异常值后 DataFrame 的差异

使用标准化进行数据缩放

接下来,让我们执行一个称为“使用标准化进行数据缩放”的预处理步骤。这涉及到将我们的数据转换,使其均值为 0,标准差为 1。当我们的数据具有不同的单位或尺度时,这很有用,并确保每个特征在进行的分析中贡献相等。

[提示]

聚类方法对数据缩放敏感。我想对数据集进行缩放。

[提示结束]

[提示响应]

你是对的!聚类方法,尤其是像 K-means 这样的基于距离的方法,对特征的规模敏感。为了确保所有特征具有相同的规模并平等地贡献于聚类过程,我们需要进行数据缩放。一种常见的方法是使用标准化(也称为 z 分数缩放)或归一化。在这里,我将向您展示如何使用标准化来缩放数据集:

# Step 8: Data Scaling using Standardization
from sklearn.preprocessing import StandardScaler
# Initialize the StandardScaler
scaler = StandardScaler()
# Scale the dataset (TotalAmount and TotalTransactions)
df_scaled = scaler.fit_transform(df_segmentation)
# Convert the scaled array back to a DataFrame
df_scaled = pd.DataFrame(df_scaled, columns=df_segmentation.columns)
# Display the first few rows of the scaled DataFrame
print("Scaled DataFrame:")
print(df_scaled.head()) 

在上面的代码中:

  1. 我们从sklearn.preprocessing模块导入StandardScaler,这将帮助我们执行标准化。

  2. 我们将StandardScaler初始化为scaler

  3. 我们使用scaler.fit_transform()对包含TotalAmountTotalTransactions列的df_segmentation DataFrame 进行标准化。

  4. fit_transform()操作的结果是一个数组,因此我们使用pd.DataFrame()将原始列名转换回 DataFrame。

缩放后,TotalAmountTotalTransactions都将具有 0 均值和 1 标准差,这有助于聚类过程,确保没有单个特征由于较大的规模而主导聚类算法。

现在,您有一个缩放后的 DataFrame df_scaled,它已准备好用于使用聚类方法进行客户细分。

[响应结束]

运行第 8 步的代码将产生以下输出:

 Scaled DataFrame:  
       TotalAmount  TotalTransactions  
1.099421           0.425738  
1.051512          -0.745491  
-0.666340          -0.745491  
1.954997           1.987377  
-0.962557          -0.745491 

决定簇的数量

K-means 是一种流行的聚类算法,用于将数据集划分为 k 个簇,其中每个数据点属于最近的均值簇。使用 K-means 的目标是通过将相似数据点分组来识别模式。您还希望降低数据集的复杂性。其他好处包括分析改进。

为了有效地应用聚类方法如 K-means,我需要决定簇的数量。我们可以使用几种方法来找到那个数字;让我们询问 ChatGPT:

[提示]

我如何确定数据集的最佳簇数量?

[提示结束]

[提示响应]

确定最佳簇数量,通常被称为“肘部点”,在应用聚类方法如 K-means 时是一个关键步骤。您可以使用几种方法来找到适合您数据集的适当簇数量。最常见的方法之一是“肘部方法”,但其他方法如“轮廓分数”和“间隙统计”也可能很有帮助。以下是您可以使用这些方法的方式:

  1. 肘部方法:肘部方法涉及将簇内平方和(WCSS)与簇的数量绘制成图。WCSS 代表每个点与其分配簇的质心之间的平方距离之和。想法是在图中找到 WCSS 开始减慢下降的点,形成一个“肘部”形状。

    from sklearn.cluster import KMeans import matplotlib.pyplot as plt  
    
    # Create a list to store the WCSS values for different numbers of clusters wcss = []  
    
    # Try different values of K (number of clusters) from 1 to a reasonable maximum for k in range(1, 11):     kmeans = KMeans(n_clusters=k, random_state=42)     kmeans.fit(df_scaled)     wcss.append(kmeans.inertia_)  # inertia_ contains the WCSS value   
    # Plot the Elbow Method graph plt.figure(figsize=(8, 5))
    plt.plot(range(1, 11), wcss, marker='o')
    plt.xlabel('Number of Clusters (K)')
    plt.ylabel('Within-Cluster Sum of Squares (WCSS)')
    plt.title('Elbow Method')
    plt.grid(True)
    plt.show() 
    

在肘部方法图中,寻找“肘部”点,这是曲线开始变平的点。这个点表示您数据集的最佳聚类数量。然而,请注意,肘部方法并不总是确定的,有时肘部点可能不太明显。

  1. 轮廓分数:轮廓分数衡量每个数据点与其分配的聚类的拟合程度,范围从-1 到 1。较高的轮廓分数表示定义更清晰的聚类。要使用轮廓分数,对不同的 K 值进行循环,并计算每个值的平均轮廓分数。

    from sklearn.metrics import silhouette_score
    # Create a list to store the Silhouette Scores for different numbers of clusters
    silhouette_scores = []
    # Try different values of K (number of clusters) from 2 to a reasonable maximum
    for k in range(2, 11):
        kmeans = KMeans(n_clusters=k, random_state=42)
        kmeans.fit(df_scaled)
        silhouette_scores.append(silhouette_score(df_scaled, kmeans.labels_))
    # Plot the Silhouette Scores
    plt.figure(figsize=(8, 5))
    plt.plot(range(2, 11), silhouette_scores, marker='o')
    plt.xlabel('Number of Clusters (K)')
    plt.ylabel('Average Silhouette Score')
    plt.title('Silhouette Score')
    plt.grid(True)
    plt.show() 
    

最佳聚类数量通常与最高的轮廓分数相关联。

  1. 差距统计:差距统计比较聚类算法的 WCSS 与随机生成数据的 WCSS。它有助于确定您的数据中的聚类结构相对于随机数据是否显著。

实现差距统计更为复杂,但像scikit-learn-extra这样的库提供了计算它的函数:

from sklearn_extra.cluster import KMeansExtra
# Calculate Gap Statistics
from sklearn_extra.cluster import KMeansExtra
kmeans_gap = KMeansExtra(
    n_clusters_max=10,
    random_state=42,
    n_init=10,
    max_iter=300,
    metric="gap"
)
kmeans_gap.fit(df_scaled)
# Plot Gap Statistics
plt.figure(figsize=(8, 5))
plt.plot(range(1, 11), kmeans_gap.gap_values_, marker='o')
plt.xlabel('Number of Clusters (K)')
plt.ylabel('Gap Value')
plt.title('Gap Statistics')
plt.grid(True)
plt.show() 

再次寻找差距值开始变平的点,这表明最佳聚类数量。

记住,选择最佳聚类数量通常是一个基于您寻求的见解和特定问题背景的主观决定。这些方法可以为您提供起点,但您可能需要根据您的领域知识和业务需求进行实验和验证聚类结果。

[响应结束]

让我们执行肘部方法和轮廓分数的代码,并检查输出:

显示聚类数量的图表,自动生成描述

图 16.7:使用肘部方法确定的聚类数量

带有线条的图表,自动生成描述

图 16.8:使用轮廓分数方法确定的聚类数量

我们用来确定最佳聚类数量的肘部方法和轮廓分数方法建议使用六个聚类。我们将使用六个聚类来构建我们的 K-means 模型。让我们创建一个 ChatGPT 提示来生成一个包含六个聚类的客户细分 K-means 聚类模型。

创建聚类模型

现在我们已经确定了要使用的聚类数量,接下来让我们创建一个聚类模型。

[提示]

请帮助我创建一个包含 6 个聚类的客户细分 K-means 聚类模型。

[提示结束]

[提示响应]

当然!让我们使用缩放数据df_scaled创建一个包含 6 个聚类的客户细分 K-means 聚类模型。我们将使用 scikit-learn 的KMeans类来进行聚类:

from sklearn.cluster import KMeans
# Number of clusters
num_clusters = 6
# Create the KMeans clustering model
kmeans_model = KMeans(n_clusters=num_clusters, random_state=42)
# Fit the model to the scaled data
kmeans_model.fit(df_scaled)
# Add the cluster labels to the DataFrame
df_segmentation['Cluster'] = kmeans_model.labels_
# Display the first few rows of the DataFrame with cluster assignments
print("Customer Segmentation DataFrame with Cluster Assignments:")
print(df_segmentation.head()) 

在上面的代码中:

  1. 我们将num_clusters指定为 6,这将为客户细分创建 6 个聚类。

  2. 我们使用KMeans创建所需的聚类数量的 K-means 聚类模型,并设置random_state以实现可重复性。

  3. 模型使用fit()方法拟合到缩放数据df_scaled

  4. 我们使用kmeans_model.labels_将簇标签添加到原始 DataFrame df_segmentation中,并将其存储在新列‘Cluster’中。

生成的df_segmentation DataFrame 现在将包括表示每个客户基于其‘TotalAmount’和‘TotalTransactions’的簇分配的‘Cluster’列。每个客户将与六个簇中的一个相关联。

请记住,簇的解释将取决于您数据的环境和您的领域知识。您可能想要分析每个簇的特征,以获得关于不同客户细分市场的见解,并据此调整您的营销或商业策略。可视化技术,如散点图或簇轮廓,有助于理解和解释客户细分的结果。

计算机屏幕截图 自动生成描述

图 16.9:运行 df.segmentation()的输出

[响应结束]

可视化聚类结果

现在,我们可以可视化聚类的结果。让我们创建下一个提示:

[提示]

我希望可视化聚类的结果。请创建一个符合以下规范的散点图:横轴应为“Total Amount”,纵轴应为“Total Transactions”,点的颜色应由簇编号决定。请也为图表添加图例。

[提示结束]

[提示响应]

当然!让我们创建一个散点图来可视化聚类的结果。我们将使用matplotlib库进行绘图,使用seaborn库进行颜色映射和更吸引人的图形。如果您尚未安装seaborn库,可以使用pip install seaborn进行安装。

import seaborn as sns
import matplotlib.pyplot as plt
# Scatter plot with cluster assignments
plt.figure(figsize=(10, 6))
sns.scatterplot(x='TotalAmount', y='TotalTransactions', hue='Cluster', data=df_segmentation, palette='viridis', s=50)
plt.xlabel('Total Amount')
plt.ylabel('Total Transactions')
plt.title('Customer Segmentation by K-Means Clustering')
plt.legend(title='Cluster', bbox_to_anchor=(1.05, 1), loc='upper left')
plt.grid(True)
plt.show() 

在上面的代码中:

  1. 我们使用seaborn.scatterplot()创建散点图。‘TotalAmount’在横轴(x)上绘制,‘TotalTransactions’在纵轴(y)上绘制,而‘Cluster’列用于使用hue参数进行颜色映射。

  2. 我们使用palette参数将调色板设置为‘viridis’,这将为每个簇提供独特的颜色。s=50参数将散点图中点的尺寸设置为 50,以获得更好的可见性。

  3. 我们添加了轴标签、图表标题、图例和网格线,以提高图表的可读性。

[响应结束]

生成的散点图将显示每个客户在由‘TotalAmount’和‘TotalTransactions’定义的二维空间中的点。点将根据其簇分配进行着色,使您能够直观地识别不同的客户细分市场。图例将帮助您理解哪种颜色对应哪种簇编号。

客户细分图 自动生成描述

图 16.10:数据点被划分为六个簇

对聚类和提示过程的最终思考

我们在 ChatGPT 的帮助下完成了客户细分聚类。为了实现我们的目标,我们使用了多种不同类型的提示。

提示通常从描述要使用的数据集开始,例如来源、列及其类型,这为设置了一个高级上下文。ChatGPT 随后请求协助使用特定的算法实现客户细分聚类模型,在这种情况下,是 K-means 聚类算法。为了总结所使用的提示,我们可以看到它融合了一系列不同的技术,这反映了在没有 AI 助手的情况下如何执行数据科学。以下展示了不同类型的提示类型以及它们如何帮助聚类过程:

  • 逐步指导:这些提供了如何解决问题的逐步指南,包括加载数据集、理解数据集、数据预处理(移除缺失值、负值和未使用的列)以及特征工程(创建新的特征,如 Amount、TotalAmount 和 TotalTransactions)。

  • 聚类算法选择:这指定了聚类算法的选择,在本例中是 K-means 聚类,以及用于聚类的聚类数量。

  • 处理异常值:解决了聚类算法对异常值的敏感性,并请求使用四分位数范围IQR)方法从数据中移除异常值。

  • 数据缩放:这强调了缩放数据以确保聚类准确性的重要性,并指导使用标准化来缩放特征。

  • 聚类可视化:这要求提供可视化技术来显示聚类模型的结果,通常通过散点图,TotalAmount 在 x 轴上,TotalTransactions 在 y 轴上,用颜色编码的点代表不同的聚类。

  • 最佳聚类数量:提示指导使用肘部方法和轮廓分数来确定最佳聚类数量,这有助于找到最能代表数据的正确数量的聚类。

  • 合并脚本请求:提示要求将所有生成的代码合并成一个单独的脚本,包括作为注释的详细说明,以提供一个全面解决实现客户细分聚类模型的方案。

电子商务项目的产品聚类

在这个任务中,我们将使用相同的电子商务数据集执行产品聚类。目标是创建一个无监督学习模型,根据产品的文本描述使用 Python 和常用库进行聚类。

您的初始提示:设置上下文

像往常一样,当使用 ChatGPT 时,设置一个高级上下文很重要。对于数据科学来说,高级上下文意味着描述数据使用(电子商务数据)、我们试图实现的目标(获取数据洞察)以及我们打算如何实现它(聚类)。

下面是一个示例提示,提供了前面段落中我们刚刚描述的所有建议的上下文:

[提示]

我希望你能扮演一位经验丰富的数据科学家,并帮助我实现使用聚类进行产品分类的模型。数据集来自 UCI 机器学习仓库的电子商务数据。这是一个跨国数据集,包含了 2010 年 12 月 1 日至 2011 年 9 月 9 日之间一个英国注册的非实体店在线零售商的所有交易。数据集包含以下列:InvoiceNo(整数),StockCode(字符串),Description(字符串),Quantity(整数),InvoiceDate(日期),UnitPrice(小数),CustomerID(整数),Country(字符串)。你能建议使用 csv 文件中的数据进行产品分类所需的步骤吗?

[提示结束]

你应该收到如下响应:

[提示响应]

当然!使用聚类进行产品分类是将具有相似属性的产品分组在一起的好方法。以下是一个使用给定电子商务数据集进行产品分类的逐步指南:

第 1 步:加载数据并进行预处理

  1. 将数据集从 CSV 文件加载到合适的数据结构中,例如 Python 中的 Pandas DataFrame。

  2. 检查数据以了解其结构、缺失值和整体特征。

  3. 预处理数据,这可能涉及处理缺失值、数据类型转换和数据清理。

第 2 步:特征工程

  1. 选择与聚类相关的特征。在这种情况下,你可以考虑像‘StockCode’,‘Description’,‘Quantity’,‘UnitPrice’等特征。与客户相关的特征,如‘CustomerID’,可能对产品分类并不直接相关。

  2. 创建一个特征矩阵,其中每一行对应一个产品,每一列对应一个特征。

第 3 步:文本数据预处理

  1. 对于基于文本的特征,如‘Description’,执行文本预处理步骤,例如小写化、分词、去除标点符号和停用词。

  2. 使用 TF-IDF(词频-逆文档频率)或词嵌入等技术将文本数据转换为数值格式。

第 4 步:选择聚类算法

  1. 选择合适的聚类算法。常见的选项包括 K-means,层次聚类和 DBSCAN。

  2. 如有必要,调整超参数。

第 5 步:特征缩放

  1. 将数值特征缩放到相似的范围内。这对于基于距离的算法,如 K-means 来说非常重要。

第 6 步:应用聚类算法

  1. 将选定的聚类算法应用于预处理后的数据。

  2. 确定最佳聚类数量。你可以使用肘部方法、轮廓分数或差距统计等技术。

第 7 步:解释聚类

  1. 一旦有了聚类,分析每个聚类中的产品以了解其共同特征。

  2. 根据你的分析为聚类分配有意义的标签。这些标签将代表产品类别。

第 8 步:可视化聚类(可选)

  1. 使用散点图、t-SNE 或 PCA 等可视化技术,在降维空间中可视化聚类。

第 9 步:将产品分配到类别

  1. 根据产品所属的聚类将其分配到相应的类别。

第 10 步:评估和细化

  1. 评估聚类结果的质量。如果你有领域知识或外部验证指标,可以使用它们。

  2. 根据需要调整预处理步骤、算法参数或特征以细化过程。

记住,聚类是一个迭代过程,你的结果质量可能取决于各种因素,如数据质量、特征选择和算法选择。在解释和标记聚类时,考虑业务背景和领域知识也很重要。

[响应结束]

审查了响应中的步骤后,你现在可以继续进行下一个提示。

加载数据并进行预处理

既然我们已经被告知了一系列步骤来执行聚类,我们想要从响应中执行第一步,即加载数据并进行预处理。

[提示]

我希望一步一步来做。请指导我进行第一步:加载数据并进行预处理。我的数据 URL 是“../Datasets/data.csv”,CSV 文件使用 latin1 编码。我将使用‘描述’列进行聚类以识别产品类别。

[提示结束]

你应该收到如下文本的响应:

[提示响应]

当然!让我们从第一步:加载数据并进行预处理开始。

import pandas as pd
# Load the data from the CSV file using Latin1 encoding
data_url = "../Datasets/data.csv"
df = pd.read_csv(data_url, encoding='latin1')
# Display basic information about the dataset
print(df.info())
# Display the first few rows of the dataset
print(df.head()) 

此代码将使用 Latin1 编码从提供的 CSV 文件加载数据集。然后,它将显示有关数据集的基本信息,例如每列的数据类型和非空值的数量。

此外,它还将显示数据集的前几行,以让你对数据的结构有一个概念。

计算机描述自动生成的截图

图 16.11:数据集的前几行

现在,让我们先预处理数据,并专注于‘描述’列进行聚类。

# Drop rows with missing 'Description' values
df = df.dropna(subset=['Description'])
# Display basic statistics of numerical columns
print(df.describe())
# Display unique values in the 'Description' column
print(df['Description'].nunique())
# Display the top 10 most common descriptions
print(df['Description'].value_counts().head(10)) 

在这个代码片段中,我们首先删除了具有缺失‘描述’值的行,因为我们将在聚类中使用这个列。接下来,我们显示数值列的基本统计信息,以了解数据的分布概览。然后,我们计算唯一描述的数量,以了解产品的多样性。最后,我们显示最常见的 10 个描述,以查看数据集中最频繁的产品。

到目前为止,你已经加载数据,删除了‘描述’列中的缺失值,并对数据的特征有了一些了解。下一步将涉及对‘描述’列的文本数据预处理。如果你准备好了进入第三步,或者对当前步骤有任何疑问,请告诉我!

[响应结束]

将代码复制到 Jupyter Notebook 后,我们发现它运行得非常好,我们了解到 541,909 个数据集记录包含 4,223 个独特的商品描述。下一步是对‘描述’列进行文本数据预处理。让我们继续下一个提示。

特征工程和文本数据预处理

到目前为止,数据已经被读取,我们也知道了数据集的大小。让我们继续进行下一步,特征工程和文本数据预处理:

[提示]

数据集包含 541909 条记录,有 4223 个独特的商品描述。我想删除重复的‘描述’并执行其他文本数据预处理和特征工程。请创建一个带有注释的脚本,并包括库和nltk资源下载。

[提示结束]

[提示响应]

当然,我会提供一个包括文本数据预处理和特征工程步骤的脚本。让我们开始吧:

import pandas as pd
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from sklearn.feature_extraction.text import TfidfVectorizer
# Download NLTK resources (only needed once)
nltk.download('punkt')
nltk.download('stopwords')
# Load the data from the CSV file using Latin1 encoding
data_url = "../Datasets/data.csv"
df = pd.read_csv(data_url, encoding='latin1')
# Drop rows with missing 'Description' values
df = df.dropna(subset=['Description'])
# Drop duplicate 'Description' entries
df = df.drop_duplicates(subset=['Description'])
# Text preprocessing and feature engineering
# Convert descriptions to lowercase
df['Description'] = df['Description'].str.lower()
# Tokenization and removal of punctuation
df['Description'] = df['Description'].apply(word_tokenize)
# Remove stopwords
stop_words = set(stopwords.words('english'))
df['Description'] = df['Description'].apply(lambda x: [word for word in x if word not in stop_words])
# Convert tokenized descriptions back to strings
df['Description'] = df['Description'].apply(' '.join)
# TF-IDF vectorization
tfidf_vectorizer = TfidfVectorizer(max_features=1000)  # You can adjust the number of features
tfidf_matrix = tfidf_vectorizer.fit_transform(df['Description'])
# Convert TF-IDF matrix to a DataFrame
tfidf_df = pd.DataFrame(tfidf_matrix.toarray(), columns=tfidf_vectorizer.get_feature_names_out())
# Display the TF-IDF DataFrame
print(tfidf_df.head()) 

在此脚本中:

  1. 我们导入必要的库,包括pandasnltk和来自sklearn.feature_extraction.textTfidfVectorizer

  2. 如果尚未下载,我们将下载 NLTK 资源(punkt用于分词和stopwords用于去除停用词)。

  3. 我们加载数据集,并删除包含缺失的‘描述’值和重复描述的行。

  4. 我们在‘描述’列上执行文本预处理步骤:转换为小写,分词,去除标点符号和停用词。

  5. 我们将分词后的描述转换回字符串。

  6. 我们使用 TF-IDF 向量化将预处理后的文本数据转换为数值特征。你可以调整max_features参数来控制特征的数量。

  7. 将 TF-IDF 矩阵转换为 DataFrame 以进行进一步分析。

此脚本将预处理文本数据,并使用 TF-IDF 向量化进行特征工程。生成的 TF-IDF 矩阵可以用作后续步骤中聚类算法的输入。

[响应结束]

将脚本粘贴到 Jupyter Notebook 后,我们发现它运行得非常好,我们了解到 TF-IDF 矩阵有 4,223 行(每个独特的商品描述一行)和 1,000 列(每个特征一列)。

计算机屏幕截图,描述自动生成

图 16.12:TF-IDF 矩阵

特征工程

我们将继续进行特征工程。让我们可视化 TF-IDF 矩阵中最频繁的特征。

[提示]

我想可视化 TF-IDF 矩阵中最频繁的 100 个特征。请创建一个以单词为纵轴的柱状图。

[提示结束]

[提示响应]

当然!为了可视化 TF-IDF 矩阵中最频繁的 100 个特征,你可以使用像matplotlib这样的库创建柱状图。以下是你可以这样做的步骤:

import matplotlib.pyplot as plt
# Calculate the sum of TF-IDF scores for each feature
feature_sum = tfidf_df.sum()
# Get the top 100 features by frequency
top_features = feature_sum.nlargest(100)
# Create a bar chart
plt.figure(figsize=(10, 12))
top_features.plot(kind='barh')
plt.xlabel('TF-IDF Score Sum')
plt.ylabel('Words')
plt.title('Top 100 Most Frequent Features from TF-IDF Matrix')
plt.tight_layout()
plt.show() 

此脚本计算每个特征的 TF-IDF 分数总和,并按频率选择前 100 个特征。然后使用matplotlib创建水平条形图来可视化顶级特征。根据需要调整图形大小、标签和其他参数以自定义可视化以符合您的偏好。

[回复结束]

在 Jupyter Notebook 中运行此代码将生成以下条形图:

人数图表,描述自动生成,中等置信度

图 16.13:最频繁的特征

让我们分析这张图表并继续下一个提示。

[提示]

我们可以看到,特征中包含数字和表示颜色的词汇,如红色、蓝色和其他颜色。这些词汇对产品分类没有帮助。你能创建一个可以表示颜色的词汇列表吗?

[提示结束]

[提示回复]

当然!以下是一份表示颜色的常用词汇列表。您可以使用此列表在产品分类之前过滤掉与颜色相关的词汇。

color_words = [
    'black', 'white', 'grey', 'gray', 'red', 'blue', 'green', 'yellow',
    'orange', 'purple', 'pink', 'brown', 'beige', 'gold', 'silver',
    'indigo', 'violet', 'turquoise', 'teal', 'aqua', 'navy', 'olive',
    'maroon', 'coral', 'plum', 'salmon', 'magenta', 'cyan', 'khaki',
    'ivory', 'chartreuse', 'crimson', 'fuchsia', 'lavender', 'lime',
    'tan', 'sienna', 'orchid', 'periwinkle', 'peach', 'thistle'
] 

您可以使用此列表在文本预处理步骤中过滤掉与颜色相关的词汇。例如:

# Remove color-related words from descriptions
df['Description'] = df['Description'].apply(lambda x: [word for word in x if word not in color_words]) 

[回复结束]

通过移除与颜色相关的词汇,您可以通过关注更有意义的特征来帮助提高产品分类的质量。

[提示]

修改脚本以移除color_words和长度小于三个字符的词汇。将代码重构为将数据预处理和向量化封装到preprocess_and_vectorize_data函数中。

[提示结束]

[提示回复]

当然!以下是经过修改的脚本,包括将数据预处理和向量化步骤封装到函数中:

import pandas as pd
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from sklearn.feature_extraction.text import TfidfVectorizer
import matplotlib.pyplot as plt
# Download NLTK resources (only needed once)
nltk.download('punkt')
nltk.download('stopwords')
# List of color-related words
color_words = [
    'black', 'white', 'grey', 'gray', 'red', 'blue', 'green', 'yellow',
    'orange', 'purple', 'pink', 'brown', 'beige', 'gold', 'silver',
    'indigo', 'violet', 'turquoise', 'teal', 'aqua', 'navy', 'olive',
    'maroon', 'coral', 'plum', 'salmon', 'magenta', 'cyan', 'khaki',
    'ivory', 'chartreuse', 'crimson', 'fuchsia', 'lavender', 'lime',
    'tan', 'sienna', 'orchid', 'periwinkle', 'peach', 'thistle'
]
def preprocess_and_vectorize_data(data):
    # Drop rows with missing 'Description' values
    data = data.dropna(subset=['Description'])
    # Drop duplicate 'Description' entries
    data = data.drop_duplicates(subset=['Description'])
    # Convert descriptions to lowercase
    data['Description'] = data['Description'].str.lower()
    # Tokenization and removal of punctuation
    data['Description'] = data['Description'].apply(word_tokenize)
    # Remove stopwords and color-related words
    stop_words = set(stopwords.words('english'))
    data['Description'] = data['Description'].apply(lambda x: [word for word in x if word not in stop_words and len(word) > 2 and word not in color_words])
    # Convert tokenized descriptions back to strings
    data['Description'] = data['Description'].apply(' '.join)
    # TF-IDF vectorization
    tfidf_vectorizer = TfidfVectorizer(max_features=1000)  # You can adjust the number of features
    tfidf_matrix = tfidf_vectorizer.fit_transform(data['Description'])
    # Convert TF-IDF matrix to a DataFrame
    tfidf_df = pd.DataFrame(tfidf_matrix.toarray(), columns=tfidf_vectorizer.get_feature_names_out())

    return tfidf_df
# Load the data from the CSV file using Latin1 encoding
data_url = "../Datasets/data.csv"
df = pd.read_csv(data_url, encoding='latin1')
# Preprocess and vectorize the data
tfidf_df = preprocess_and_vectorize_data(df)
# Calculate the sum of TF-IDF scores for each feature
feature_sum = tfidf_df.sum()
# Get the top 100 features by frequency
top_features = feature_sum.nlargest(100)
# Create a bar chart
plt.figure(figsize=(10, 12))
top_features.plot(kind='barh')
plt.xlabel('TF-IDF Score Sum')
plt.ylabel('Words')
plt.title('Top 100 Most Frequent Features from TF-IDF Matrix')
plt.tight_layout()
plt.show() 

此脚本将数据预处理和向量化步骤封装到preprocess_and_vectorize_data函数中。该函数接收原始数据作为输入,执行所需的预处理,并返回 TF-IDF 矩阵。脚本的其他部分与之前类似。

[回复结束]

运行脚本后,我们可以可视化 TF-IDF 矩阵输出的最频繁词汇的更新集合。

人数图表,描述自动生成,中等置信度

图 16.14:不是颜色的最频繁特征

让我们继续进行特征工程。我们将使用 ChatGPT 来建议可以探索的 TF-IDF 向量化选项。

[提示]

TF-IDF 向量化使用max_features参数初始化。还有其他初始化选项吗?

[提示结束]

[提示回复]

在 scikit-learn 中初始化 TF-IDF 向量器时,您可以设置多个参数来自定义其行为。

除了max_features参数外,还有一些其他重要的参数您可以考虑:

  1. max_dfmin_df:这些参数控制术语的文档频率阈值。例如,您可以设置max_df以忽略在大量文档中出现的术语,并设置min_df以忽略在少量文档中出现的术语。

  2. ngram_range:此参数指定在分词过程中要考虑的 n-gram 的范围。例如,将ngram_range=(1, 2)设置为考虑单个单词和连续单词对(二元组)。

  3. stop_words:您可以在分词过程中提供要忽略的停用词列表。这可以是一个自定义列表或内置列表之一,如'english'

  4. tokenizer:如果您想要执行额外的文本预处理或分词,可以指定自定义的分词函数。

  5. use_idf:此参数控制是否使用逆文档频率(IDF)重新加权词频。

  6. smooth_idf:此参数控制是否向 IDF 添加平滑项以避免除以零错误。

  7. sublinear_tf:如果设置为True,则对词频应用次线性缩放,这有助于防止非常频繁的术语主导计算。

这里有一个例子,说明您如何使用一些这些参数初始化 TF-IDF 向量器:

from sklearn.feature_extraction.text import TfidfVectorizer
tfidf_vectorizer = TfidfVectorizer(
    max_features=1000,
    max_df=0.8,
    min_df=0.05,
    ngram_range=(1, 2),
    stop_words='english',
    use_idf=True,
    smooth_idf=True,
    sublinear_tf=True
) 

[响应结束]

根据您特定的数据集和目标,随意实验这些参数。参数的选择可能取决于文本数据的性质以及您聚类或分类任务所期望的结果。

选择聚类算法

我们可以选择相当多的聚类算法。例如,K-means,我们用于另一个用例;还有层次聚类、DBSCAN(基于密度的聚类算法)、高斯混合模型(GMMs)和凝聚聚类。我们将选择 K-means,但为什么是 K-means 呢?原因是我们在处理客户,并希望根据购买行为、人口统计属性等将他们分成组。选择 K-means 的另一个原因是它的简单性以及能够处理大型数据集。

您可能会想知道在电子商务的背景下何时应用其他提到的聚类算法。好吧,让我们看看这个领域的一些有趣领域:

  • 产品推荐:在这里,使用层次聚类会更合适,因为产品通常以层次结构出现,我们想要了解产品之间的关系。

  • 异常检测:另一个重要领域是异常检测,特别是欺诈交易。在这里,您想要了解不寻常的客户行为以识别异常值;为此用例,DBSCAN(基于密度的聚类算法)是最佳算法。

  • 个性化:您希望确保客户感觉他们所拥有的体验是独特地适应他们在网站上的行为的。当客户的行为稍微复杂一些时,GMM(高斯混合模型)是这种用例的一个很好的选择。

特征缩放

特征缩放和 TF-IDF 都是预处理技术,只选择其中之一是有意义的。选择哪一个取决于我们想用它做什么。在电子商务的背景下,您可以这样推理:

  • 如果您处理的是像客户年龄、购买金额和购买频率这样的数值特征(例如,根据客户的购买行为对客户进行分组),请选择特征缩放。

  • 如果您正在查看电子商务的其他方面,如客户评论、产品描述和搜索查询,请选择 TF-IDF 矩阵。

由于我们使用的是已经缩放的 TF-IDF 矩阵,我们可以跳过这一步。

应用聚类算法

接下来,让我们应用我们选择的聚类算法:

[提示]

我想使用英文stop_words,不使用max_features**,**并将min_df传递给preprocess_and_vectorize_data函数以设置特征在文档中出现的最小文档数。我想尝试min_df的一系列值:5, 10, 20, 30,并比较 K-means 输出的轮廓得分。我想可视化结果。

[提示结束]

[提示响应]

当然!让我们修改脚本以适应您对min_df的要求,并使用KMeans聚类比较不同min_df值的轮廓得分。以下是更新的脚本:

def preprocess_and_vectorize_data(data, min_df):
    # Drop rows with missing 'Description' values
    data = data.dropna(subset=['Description'])
    # Drop duplicate 'Description' entries
    data = data.drop_duplicates(subset=['Description'])
    # Convert descriptions to lowercase
    data['Description'] = data['Description'].str.lower()
    # Tokenization and removal of punctuation
    data['Description'] = data['Description'].apply(word_tokenize)
    # Remove stopwords and color-related words
    stop_words = set(stopwords.words('english'))
    data['Description'] = data['Description'].apply(lambda x: [word for word in x if word not in stop_words and len(word) > 2 and word not in color_words])
    # Convert tokenized descriptions back to strings
    data['Description'] = data['Description'].apply(' '.join)
    # TF-IDF vectorization
    tfidf_vectorizer = TfidfVectorizer(min_df=min_df)
    tfidf_matrix = tfidf_vectorizer.fit_transform(data['Description'])
    # Convert TF-IDF matrix to a DataFrame
    tfidf_df = pd.DataFrame(tfidf_matrix.toarray(), columns=tfidf_vectorizer.get_feature_names_out())

    return tfidf_df
# Load the data from the CSV file using Latin1 encoding
data_url = "../Datasets/data.csv"
df = pd.read_csv(data_url, encoding='latin1')
# Different values for min_df
min_df_values = [5, 10, 20, 30]
# Store silhouette scores
silhouette_scores = []
# Perform clustering and calculate silhouette scores for different min_df values
for min_df in min_df_values:
    # Preprocess and vectorize the data
    tfidf_df = preprocess_and_vectorize_data(df, min_df)

    # Perform KMeans clustering
    kmeans = KMeans(n_clusters=10, random_state=42)
    cluster_labels = kmeans.fit_predict(tfidf_df)

    # Calculate silhouette score
    silhouette_scores.append(silhouette_score(tfidf_df, cluster_labels))
# Visualize the silhouette scores
plt.plot(min_df_values, silhouette_scores, marker='o')
plt.xlabel('min_df')
plt.ylabel('Silhouette Score')
plt.title('Silhouette Score vs. min_df')
plt.xticks(min_df_values)
plt.grid()
plt.show() 

在此脚本中,我们正在遍历不同的min_df值(5, 10, 20 和 30),并为每个值执行以下步骤:

  1. 使用指定的min_df值,通过preprocess_and_vectorize_data函数预处理和向量化数据。

  2. 使用 10 个簇执行 K-means 聚类(您可以根据需要调整簇的数量)。

  3. 计算聚类结果的轮廓得分并将其存储在silhouette_scores列表中。

最后,我们使用折线图可视化不同min_df值的轮廓得分。这将帮助您比较不同min_df值如何影响聚类结果的质量。

带有线条描述的图形自动生成

图 16.15:轮廓得分与 min_df 的关系

[响应结束]

接下来,我们将研究簇数量对聚类结果质量的影响。让我们进行下一个提示。

[提示]

我还想尝试不同的簇数量num_clusters = [3,4, 5,6,7],并可视化min_dfnum_clusters组合的轮廓得分输出。请添加生成每个num_clusters值簇和轮廓得分值的图标的脚本。

[提示结束]

[提示响应]

当然!以下是生成不同组合的min_dfnum_clusters轮廓得分的更新的脚本:

# Different values for min_df and num_clusters
min_df_values = [5, 10, 20, 30]
num_clusters = [3, 4, 5, 6, 7]
# Store silhouette scores for each combination
silhouette_scores = []
# Perform clustering and calculate silhouette scores for different min_df and num_clusters combinations
for min_df, n_clusters in itertools.product(min_df_values, num_clusters):
    # Preprocess and vectorize the data
    tfidf_df = preprocess_and_vectorize_data(df, min_df)

    # Perform KMeans clustering
    kmeans = KMeans(n_clusters=n_clusters, random_state=42)
    cluster_labels = kmeans.fit_predict(tfidf_df)

    # Calculate silhouette score
    silhouette_scores.append((min_df, n_clusters, silhouette_score(tfidf_df, cluster_labels)))
# Convert silhouette scores to a DataFrame for easier visualization
silhouette_scores_df = pd.DataFrame(silhouette_scores, columns=['min_df', 'num_clusters', 'silhouette_score'])
# Plot the silhouette scores
plt.figure(figsize=(10, 6))
for min_df in min_df_values:
    subset_df = silhouette_scores_df[silhouette_scores_df['min_df'] == min_df]
    plt.plot(subset_df['num_clusters'], subset_df['silhouette_score'], marker='o', label=f'min_df = {min_df}')

plt.xlabel('Number of Clusters')
plt.ylabel('Silhouette Score')
plt.title('Silhouette Score vs. Number of Clusters for Different min_df Values')
plt.legend()
plt.grid()
plt.show() 

在这个脚本中,我们使用itertools.product函数创建min_dfnum_clusters值的组合。我们计算每个组合的轮廓分数并将它们存储在silhouette_scores列表中。然后,我们将轮廓分数转换为 DataFrame 以便于可视化。生成的图表显示了在考虑不同的min_df值时,轮廓分数如何随num_clusters的不同值而变化。

[响应结束]

这里是脚本的输出:

显示簇数量的图表,描述自动生成

图 16.16:轮廓分数与不同的min_df

让我们细化这个图表,以显示与min_fd参数值对应的特征数量。

[提示]

这个图表很好。我想通过添加每个min_df值的特征数量来做一个小的更改,标签为label=f'min_df = {min_df}'

[结束提示]

[提示响应]

当然可以!您可以将对应于每个min_df值的特征数量添加到图例标签中。

这里是经过修改的代码,包含那个更改:

# ...
# Store silhouette scores for each combination
silhouette_scores = []
# Perform clustering and calculate silhouette scores for different min_df and num_clusters combinations
for min_df, n_clusters in itertools.product(min_df_values, num_clusters):
    # Preprocess and vectorize the data
    tfidf_df = preprocess_and_vectorize_data(df, min_df)

    # Perform KMeans clustering
    kmeans = KMeans(n_clusters=n_clusters, random_state=42)
    cluster_labels = kmeans.fit_predict(tfidf_df)

    # Calculate silhouette score
    silhouette_scores.append((min_df, n_clusters, silhouette_score(tfidf_df, cluster_labels)))
# Convert silhouette scores to a DataFrame for easier visualization
silhouette_scores_df = pd.DataFrame(silhouette_scores, columns=['min_df', 'num_clusters', 'silhouette_score'])
# Plot the silhouette scores
plt.figure(figsize=(10, 6))
for min_df in min_df_values:
    subset_df = silhouette_scores_df[silhouette_scores_df['min_df'] == min_df]

    # Get the number of features for the current min_df value
    num_features = preprocess_and_vectorize_data(df, min_df).shape[1]

    plt.plot(subset_df['num_clusters'], subset_df['silhouette_score'], marker='o', label=f'min_df = {min_df}, num_features = {num_features}')

plt.xlabel('Number of Clusters')
plt.ylabel('Silhouette Score')
plt.title('Silhouette Score vs. Number of Clusters for Different min_df Values')
plt.legend()
plt.grid()
plt.show() 

在这个修改后的脚本中,我们使用 TF-IDF DataFrame 的shape[1]属性计算每个min_df值的特征数量。然后,我们将这些信息包含在图表上每条线的图例标签中。这个更改将帮助您可视化min_df和特征数量对轮廓分数的影响。

显示簇数量的图表,描述自动生成

图 16.17:轮廓分数与不同的min_df值及特征数量

[响应结束]

让我们进行下一个提示。

[提示]

我将使用min_df_value = 20num_clusters= [3, 4, 5, 6]的值。我想知道每个簇中的产品数量。你能在之前的代码中添加一个Products per Cluster的图表来显示num_clusters_values在[3, 4, 5, 6]范围内的产品数量吗?

[结束提示]

[提示响应]

当然可以!您可以通过添加一个图表来可视化不同num_clusters值下每个簇中的产品数量。以下是经过修改的脚本,包含所需更改:

# ...
# Chosen min_df and num_clusters values
min_df_value = 20
num_clusters_values = [3, 4, 5, 6]
# Store silhouette scores and number of products per cluster
results = []
# Perform clustering and calculate silhouette scores for chosen min_df and num_clusters values
for n_clusters in num_clusters_values:
    # Preprocess and vectorize the data
    tfidf_df = preprocess_and_vectorize_data(df, min_df_value)

    # Perform KMeans clustering
    kmeans = KMeans(n_clusters=n_clusters, random_state=42)
    cluster_labels = kmeans.fit_predict(tfidf_df)

    # Calculate silhouette score
    silhouette = silhouette_score(tfidf_df, cluster_labels)

    # Count number of products in each cluster
    cluster_counts = pd.Series(cluster_labels).value_counts().sort_index()

    results.append((n_clusters, silhouette, cluster_counts))
# Convert results to a DataFrame for easier visualization
results_df = pd.DataFrame(results, columns=['num_clusters', 'silhouette_score', 'cluster_counts'])
# Plot the silhouette scores
plt.figure(figsize=(15, 6))
# Silhouette Score plot
plt.subplot(1, 2, 1)
plt.plot(results_df['num_clusters'], results_df['silhouette_score'], marker='o')
plt.xlabel('Number of Clusters')
plt.ylabel('Silhouette Score')
plt.title('Silhouette Score vs. Number of Clusters')
plt.grid()
# Products per Cluster plot
plt.subplot(1, 2, 2)
for n_clusters, cluster_counts in results_df[['num_clusters', 'cluster_counts']].values:
    plt.plot(range(1, n_clusters + 1), cluster_counts, marker='o', label=f'num_clusters = {n_clusters}')
plt.xlabel('Cluster')
plt.ylabel('Number of Products')
plt.title('Number of Products per Cluster')
plt.legend()
plt.grid()
plt.tight_layout()
plt.show() 

在这个修改后的脚本中,我们使用pd.Series(cluster_labels).value_counts().sort_index()计算每个簇中的产品数量,并将结果与轮廓分数一起存储在results列表中。然后,我们创建一个 DataFrame 从结果中以便于可视化。

脚本包括两个并排的图表:第一个图表显示不同num_clusters值的轮廓分数,第二个图表显示相同num_clusters值下每个簇中的产品数量。这将帮助您理解簇数量、轮廓分数以及产品在簇中分布之间的关系。

运行脚本后,你应该看到以下输出:

带有线条的图表比较  有中等置信度的自动生成的描述

图 16.18:添加了每个簇的产品绘图

[结束响应]

现在我们将选择簇的数量和min_df值,并继续下一个提示。

解释簇并可视化结果

我们将探讨解释簇和可视化结果。我们应该注意哪些事项?

解释簇

对于簇的解释,我们应该考虑以下因素:

  • 聚合度:确保数据点彼此相似。你可以通过检查每个簇中最具代表性的术语来找出这一点。

  • 分隔:簇应该很好地分隔,确保它们是独特且有意义的。

  • 标签:簇也应该有描述性的标签。

  • 异常值:识别不适合任何簇的异常值很重要。

可视化簇

对于可视化簇,以下是一些需要考虑的事项:

  • 维度降低:你希望将高维的 TF-IDF 矩阵降低到 2D 或 3D,以便更容易可视化。为此,PCA 等技术很有帮助。

  • 散点图:散点图非常适合可视化数据点;一个点可以是一个文档,例如,颜色或形状可以代表不同的簇。

  • 词云:如果你查看文本,也可以使用词云来查看最常用的单词。

  • 热图:热图是另一种很好的可视化频率的方法,将有助于突出每个簇中最重要的术语。

  • 交互性:确保你允许探索,例如使用 Plotly,以便通过钻入特定数据点来深入了解。

接下来,让我们为可视化制作一个提示。

[提示]

我将使用min_df_value = 20和 5 个簇。我希望使用 PCA 的 3 个成分在 3D 图中可视化产品簇。簇的颜色应映射自 seaborn 的color_palette

[结束提示]

[提示响应]

当然!为了使用 PCA 的 3 个成分和从 seaborn 颜色调色板映射簇颜色来在 3D 图中可视化产品簇,你可以遵循以下步骤:

  1. 导入必要的库。

  2. 预处理数据并执行聚类。

  3. 将主成分分析(PCA)应用于将维度降低到 3 个成分。

  4. 使用 seaborn 颜色调色板在 3D 散点图中绘制簇。

这是实现此目的的代码:

from sklearn.decomposition import PCA
import seaborn as sns
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D  # Required for 3D plotting
# Selected min_df and num_clusters values
min_df_value = 20
num_clusters = 5
# Preprocess data and perform clustering
tfidf_matrix, tfidf_vectorizer = preprocess_and_vectorize_data(df['Description'], min_df_value)
kmeans = KMeans(n_clusters=num_clusters, random_state=42)
cluster_labels = kmeans.fit_predict(tfidf_matrix)
# Apply PCA with 3 components
pca = PCA(n_components=3)
pca_result = pca.fit_transform(tfidf_matrix.toarray())
# Create a DataFrame for PCA results
pca_df = pd.DataFrame(data=pca_result, columns=['PC1', 'PC2', 'PC3'])
pca_df['Cluster'] = cluster_labels
# Seaborn color palette for cluster colors
color_palette = sns.color_palette("Set1", n_colors=num_clusters)
# Plot 3D scatter plot
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
for cluster_id in range(num_clusters):
    cluster_points = pca_df[pca_df['Cluster'] == cluster_id]
    ax.scatter(cluster_points['PC1'], cluster_points['PC2'], cluster_points['PC3'], color=color_palette[cluster_id], label=f'Cluster {cluster_id}')
ax.set_xlabel('PC1')
ax.set_ylabel('PC2')
ax.set_zlabel('PC3')
ax.set_title('Product Clusters in 3D')
ax.legend()
plt.show() 

[结束响应]

此代码将预处理数据,执行聚类,将维度降低到 3 个成分,然后在 3D 散点图中绘制产品簇,簇颜色映射自 seaborn 颜色调色板。根据需要调整图形大小、标签和其他绘图参数以自定义可视化。

聚类数据的图表  自动生成的描述

图 16.19:在 3D 空间中可视化的产品簇

创建词云

现在,我们将检查每个聚类的最频繁出现的词语。让我们继续下一个提示。

[提示]

我想可视化分配给聚类的产品描述中最频繁出现的词语。我希望您使用词云库并创建一个函数,该函数将为每个聚类输出词云。

[提示结束]

[响应提示]

当然!为了可视化分配给聚类的产品描述中最频繁出现的词语,您可以使用WordCloud库。以下是如何创建一个为每个聚类生成词云的函数:

from wordcloud import WordCloud
# Create a DataFrame for clustering results
clustering_results = pd.DataFrame(data={'Description': df['Description'], 'Cluster': cluster_labels})
# Function to generate word clouds for each cluster
def generate_wordclouds(dataframe, num_clusters):
    for cluster_id in range(num_clusters):
        cluster_data = dataframe[dataframe['Cluster'] == cluster_id]
        cluster_text = ' '.join(cluster_data['Description'])

        # Create a WordCloud object
        wordcloud = WordCloud(width=800, height=400, background_color='white', max_words=100).generate(cluster_text)

        # Plot the WordCloud
        plt.figure(figsize=(10, 6))
        plt.imshow(wordcloud, interpolation='bilinear')
        plt.title(f'Cluster {cluster_id} - Most Frequent Words')
        plt.axis('off')
        plt.show()
# Generate word clouds for each cluster
generate_wordclouds(clustering_results, num_clusters) 

此代码定义了一个名为generate_wordclouds的函数,该函数根据分配给该聚类的产品描述生成每个聚类的词云。根据您的可视化偏好调整WordCloud对象的参数(例如,widthheightbackground_colormax_words)。

函数将绘制每个聚类的词云。

词语的特写  自动生成的描述

图 16.20:第一个聚类的词云

不同颜色的词云  自动生成的描述

图 16.21:第二个聚类的词云

不同颜色的文本词云  自动生成的描述

图 16.22:第三个聚类的词云

不同颜色的词云  自动生成的描述

图 16.23:第四个聚类的词云

[响应结束]

将类别分配给产品以及评估和细化

将类别分配给产品或评估和细化步骤都不在本章的解决范围内,但让我们描述一下它们是什么,以便您了解如果您想探索这些内容应该如何进行。

我们的数据集包含发票,为了对我们来说对产品进行分类,我们首先需要了解我们可能拥有的用例。例如,这些用例可能是:

  • 组织库存

  • 改进搜索功能

  • 提升用户体验

如您所见,这非常侧重于产品数据,这意味着我们可能需要一个包含产品标题、描述以及更多内容的完全不同的数据集。让我们使用 ChatGPT 在较高层次上描述我们的方法。

[提示]

我正在查看一个电子商务数据集。你能告诉我关于将类别分配给产品的过程吗?

[提示结束]

[响应提示]

在电子商务数据集中将产品分配到类别是一个至关重要的任务,对于组织库存、改进搜索功能和提升用户体验至关重要。以下是一个逐步指南,帮助您完成这个过程:

将产品分配到类别的步骤:

理解数据

  • 审查产品描述:检查产品标题、描述以及任何其他相关属性。

  • 识别现有类别:如果有现有类别,审查它们以了解当前的分类系统。

定义类别

  • 创建类别层次结构:为你的产品开发一个有意义的类别和子类别层次结构。

  • 确保完整性:确保你的类别涵盖了数据集中所有类型的产品。

特征提取

  • 文本特征:使用 TF-IDF 或词嵌入等技术从产品标题和描述中提取相关文本特征。

  • 其他特征:考虑其他可能有助于分类的属性,如价格、品牌和规格。

手动标记

  • 样本标记:手动标记产品样本以创建训练数据集。这有助于理解每个类别的细微差别。

模型选择

  • 选择分类模型:选择适合文本分类的机器学习模型,例如朴素贝叶斯、SVM 或 LSTM 或 BERT 等深度学习模型。

训练模型

  • 训练模型:使用标记数据来训练你的分类模型。

  • 评估性能:使用准确率、精确率、召回率和 F1 分数等指标评估模型的性能。

分配类别

  • 预测类别:使用训练好的模型预测未标记产品的类别。

  • 审查预测:手动审查预测的一部分以确保准确性。

迭代和改进

  • 细化类别:根据结果,细化你的类别定义和模型。

  • 持续学习:持续用新数据更新模型,以提高其随时间推移的准确性。

示例(Python):

这里有一个使用朴素贝叶斯分类器进行文本分类的简化示例:

import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import accuracy_score
# Sample data
data = {
'Product': ['Red Shirt', 'Blue Jeans', 'Green Hat', 'Black Shoes'],
'Category': ['Clothing', 'Clothing', 'Accessories', 'Footwear']
}
df = pd.DataFrame(data)
# Feature extraction
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(df['Product'])
y = df['Category']
# Train-test split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Train the model
model = MultinomialNB()
model.fit(X_train, y_train)
# Predict and evaluate
y_pred = model.predict(X_test)
print(f'Accuracy: {accuracy_score(y_test, y_pred)}') 

此代码提供了一个基于产品名称对产品进行分类的基本框架。对于实际应用,你需要一个更大的数据集和更复杂的预处理及建模技术。

如果你有任何具体问题或需要更多信息,请随时提问!

[响应结束]

我们将把这个任务留给你去进一步探索。从你在这个章节中可能已经看到的内容开始,用一个高层次提示来解释你有什么数据集(你必须为上述问题找到一个数据集),以及你打算做什么。在后者上,你已经有了步骤,所以建议你编写一个提示来解释每个步骤应该做什么以及要运行什么代码。

评估和细化

这也超出了范围,但让我们帮你开始一个 ChatGPT 提示,然后你可以继续工作。

[提示]

在发票电子商务数据背景下描述评估和细化步骤。

[提示结束]

[提示响应]

当然!让我们在发票电子商务数据背景下分解评估和细化步骤:

评估步骤:

  1. 模型性能指标:

    • 准确率:衡量模型正确预测发票类别频率。

    • 精确度和召回率:评估每个类别的精确度(正确的正预测)和召回率(找到所有相关实例的能力)。

    • F1 分数:将精确度和召回率结合成一个单一指标,以平衡这两个方面。

  2. 混淆矩阵:

    • 可视化预测:使用混淆矩阵来查看模型在何处做出正确和错误的预测。这有助于识别模型可能遇到困难的特定类别。
  3. 交叉验证:

    • 稳健评估:执行交叉验证以确保模型在不同数据子集上的性能一致。
  4. 错误分析:

    • 分析误分类:检查被误分类的发票,以了解模型为何出错。寻找模式或常见问题。

精细步骤:

  1. 特征工程:

    • 增强特征:创建新特征或改进现有特征,为模型提供更多相关信息。例如,从发票描述中提取关键词或添加元数据,如发票日期和金额。
  2. 超参数调整:

    • 优化模型参数:使用网格搜索或随机搜索等技术来找到模型的最佳超参数,从而提高其性能。
  3. 模型选择:

    • 尝试不同的模型:实验不同的算法(例如,决策树、随机森林、神经网络),看看是否有其他模型表现更好。
  4. 数据增强:

    • 增加数据多样性:通过生成合成数据或使用过采样等技术来增加数据集,以增强代表性不足的类别。
  5. 定期更新:

    • 持续学习:定期用新的发票数据更新模型,以保持其最新状态并随着时间的推移提高其准确性。

Python 示例:

这里是一个使用交叉验证和超参数调整评估和改进模型的简化示例:

import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt
# Sample data
data = {
    'Invoice': ['Invoice for Red Shirt', 'Invoice for Blue Jeans', 'Invoice for Green Hat', 'Invoice for Black Shoes'],
    'Category': ['Clothing', 'Clothing', 'Accessories', 'Footwear']
}
df = pd.DataFrame(data)
# Feature extraction
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(df['Invoice'])
y = df['Category']
# Train-test split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Model training
model = MultinomialNB()
model.fit(X_train, y_train)
# Model evaluation
y_pred = model.predict(X_test)
print(classification_report(y_test, y_pred))
# Confusion matrix
conf_matrix = confusion_matrix(y_test, y_pred)
sns.heatmap(conf_matrix, annot=True, fmt='d', cmap='Blues')
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.title('Confusion Matrix')
plt.show()
# Hyperparameter tuning
param_grid = {'alpha': [0.1, 0.5, 1.0]}
grid_search = GridSearchCV(MultinomialNB(), param_grid, cv=5)
grid_search.fit(X_train, y_train)
print(f'Best parameters: {grid_search.best_params_}') 

此代码提供了一个框架,用于使用交叉验证和超参数调整来评估和改进模型。

如果您有任何具体问题或需要更多信息,请随时提问!

[响应结束]

对此用例提示的反思

就像在本章的第一个用例中一样,我们首先设定了一个高级上下文,描述我们的角色、数据集的形状以及我们想要做什么。然后,我们遵循以下步骤来首先分解问题,编写代码,以及如何继续改进和细化,最后可视化结果:

  1. 逐步指导:请求被结构化为一系列逐步任务,将更大的问题分解为可管理的组件。这使得跟踪和逐步实施解决方案变得更容易。

  2. 指定输入和要求:提供关于数据集、其列以及预处理、聚类和可视化的清晰和具体信息。这有助于确保收到的帮助符合特定需求。

  3. 代码注释请求: 代码片段请求包含注释以解释每一步并阐明代码的目的。这有助于理解代码和代码验证,并促进学习。

  4. 迭代与完善: 在初始指导后,通过请求额外的修改和可视化来迭代提示。这种迭代方法允许逐步完善和改进解决方案。

  5. 可视化与解释: 可视化和解释结果有助于集中精力从数据中提取有意义的见解。

作业

在上一节中,我们使用了传统的 TF-IDF 嵌入将文本数据转换为数值表示,这些表示可以用于各种自然语言处理NLP)任务,如聚类。现在,让我们尝试使用更先进的嵌入技术来改进聚类结果。我们将使用 Hugging Face Transformers 库为我们产品的描述获取预训练嵌入:

  1. 请问 ChatGPT 如何解释 Hugging Face Transformers 在聚类用例中相对于 TF-IDF 向量化技术的优势。

  2. 使用 ChatGPT 生成并创建产品聚类,利用 Hugging Face Transformers 嵌入。

  3. 将结果与使用 TF-IDF 向量化的先前聚类结果进行比较。

解答

在仓库中查看解决方案:github.com/PacktPublis…

摘要

本章重点介绍了聚类及其如何用于将数据分组到不同的区域。创建这些区域使得理解我们的数据点变得更容易。通过热图、词云等多种可视化方式,您了解到数据通过以不同的方式展示而受益。您还看到了聚类过程如何帮助识别异常值,即与任何单个聚类有很大差异且难以归类的数据。对于 ChatGPT 和提示部分,您看到了如何设置一个描述数据集的高级上下文,这有助于生成您可以从上到下遵循的合适步骤集。相同的高级上下文也有助于 ChatGPT 推荐聚类算法。

加入我们的社区 Discord

加入我们的社区 Discord 空间,与作者和其他读者进行讨论:

packt.link/aicode