将 PyTorch 深度学习模型与 scikit-learn 结合使用

由于其简洁性,Python 中最流行的用于研究和开发的深度学习库是 TensorFlow/Keras 和 PyTorch。然而,scikit-learn 库是 Python 中最流行的通用机器学习库。在这篇文章中,您将学习如何将 PyTorch 的深度学习模型与 Python 中的 scikit-learn 库结合使用。这将使您能够利用 scikit-learn 库在模型评估和模型超参数优化等任务方面的强大功能。完成本课程后,您将了解:

  • 如何封装 PyTorch 模型以用于 scikit-learn 机器学习库
  • 如何使用 scikit-learn 中的交叉验证轻松评估 PyTorch 模型
  • 如何使用 scikit-learn 中的网格搜索调整 PyTorch 模型超参数

通过我的《用PyTorch进行深度学习》一书来启动你的项目。它提供了包含可用代码自学教程


让我们开始吧。

将 PyTorch 深度学习模型与 scikit-learn 结合使用
摄影:Priyanka Neve。保留部分权利。

概述

本章分为四个部分;它们是:

  • skorch 概述
  • 使用交叉验证评估深度学习模型
  • 使用 scikit-learn 运行 k-折交叉验证
  • 网格搜索深度学习模型参数

skorch 概述

PyTorch 是 Python 中一个流行的深度学习库,但该库的重点是深度学习,而不是所有机器学习。事实上,它力求极简主义,只专注于您需要快速简单地定义和构建深度学习模型的内容。Python 中的 scikit-learn 库建立在 SciPy 堆栈之上,用于高效的数值计算。它是一个功能齐全的通用机器学习库,并提供了许多在开发深度学习模型时有用的实用程序。其中最重要的包括:

  • 使用 k-折交叉验证等重采样方法评估模型
  • 高效搜索和评估模型超参数
  • 将机器学习工作流的多个步骤连接成一个管道

PyTorch 不能直接与 scikit-learn 协同工作。但由于 Python 语言的鸭子类型特性,很容易将 PyTorch 模型适配到 scikit-learn 中使用。实际上,`skorch` 模块就是为此目的而构建的。通过 `skorch`,您可以让您的 PyTorch 模型像 scikit-learn 模型一样工作。您可能会觉得它更容易使用。

在以下部分中,您将通过示例了解如何使用 `NeuralNetClassifier` 包装器来处理在 PyTorch 中创建并用于 scikit-learn 库的分类神经网络。测试问题是 声纳数据集。这是一个小型数据集,所有属性都是数值型的,易于处理。

以下示例假设您已成功安装 PyTorch、skorch 和 scikit-learn。如果您使用 pip 管理 Python 模块,可以使用以下命令安装它们:

使用交叉验证评估深度学习模型

skorch 中的 `NeuralNet` 类,或更专业的 `NeuralNetClassifier`、`NeuralNetBinaryClassifier` 和 `NeuralNetRegressor` 类,是 PyTorch 模型的工厂包装器。它们接受一个 `model` 参数,该参数是一个类或一个用于获取模型的函数。反过来,这些包装器类允许您指定损失函数和优化器,然后训练循环是免费提供的。这是与直接使用 PyTorch 相比的便利之处。

下面是 Sonar 数据集上训练二分类器的简单示例:

在这个模型中,您使用 `torch.nn.BCEWithLogitsLoss` 作为损失函数(这确实是 `NeuralNetBinaryClassifier` 的默认值)。它将 sigmoid 函数与二元交叉熵损失结合起来,这样您就不需要在模型的输出层设置 sigmoid 函数。有时为了提供更好的数值稳定性,这种做法更受青睐。

此外,您在 skorch 包装器中指定了训练参数,例如 epoch 数量和批次大小。然后,您只需使用输入特征和目标调用 `fit()` 函数。该包装器将帮助您初始化模型并进行训练。

运行以上代码将产生以下结果:

请注意,skorch 的定位是 PyTorch 模型的包装器,用于适配 scikit-learn 接口。因此,您应该像使用 scikit-learn 模型一样使用它。例如,要训练您的二元分类模型,目标应为一个向量而不是 $n\times 1$ 矩阵。要运行模型进行推理,您应该使用 `model.predict(X)` 或 `model.predict_proba(X)`。这也是为什么您应该使用 `NeuralNetBinaryClassifier`,以便分类相关的 scikit-learn 函数作为模型方法提供。

想开始使用PyTorch进行深度学习吗?

立即参加我的免费电子邮件速成课程(附示例代码)。

点击注册,同时获得该课程的免费PDF电子书版本。

使用 scikit-learn 运行 k-折交叉验证

在 PyTorch 模型上使用包装器已经为您节省了大量构建自己的训练循环的样板代码。但是 scikit-learn 提供的全套机器学习功能才是真正的生产力提升。

一个例子是使用 scikit-learn 中的模型选择函数。假设您想用 k-折交叉验证评估此模型设计。通常,这意味着获取一个数据集,将其分成 $k$ 部分,然后运行一个循环,选择其中一个部分作为测试集,其余部分作为训练集,从头开始训练模型并获得评估分数。这并不难做到,但您需要编写几行代码来实现这些。

事实上,我们可以利用 scikit-learn 的 k-fold 和交叉验证函数,如下所示:

`NeuralNetBinaryClassifier` 中的参数 `verbose=False` 是为了在模型训练时停止显示进度,因为进度信息很多。上述代码将打印验证分数,如下所示:

这些是评估分数。因为这是一个二元分类模型,所以它们是平均准确率。有五个分数,因为它们是从 k-折交叉验证(k=5)中获得的,每个分数对应不同的测试集。通常,您使用交叉验证分数的平均值和标准差来评估模型:

结果是:

一个好的模型应该产生高分(在这种情况下,准确率接近 1)和低标准差。高标准差意味着模型在不同的测试集上表现不一致。

把所有东西放在一起,下面是完整的代码。

相比之下,以下是使用 scikit-learn 中神经网络模型的等效实现:

您应该会看到 skorch 如何用 PyTorch 模型替换 scikit-learn 模型。

网格搜索深度学习模型参数

前面的示例展示了将 PyTorch 深度学习模型封装并用于 scikit-learn 库函数是多么容易。在这个示例中,我们将更进一步。在创建 `NeuralNetBinaryClassifier` 或 `NeuralNetClassifier` 包装器时,您为模型参数指定的函数可以接受许多参数。您可以使用这些参数进一步自定义模型的构建。此外,您知道可以为 `fit()` 函数提供参数。

在这个示例中,您将使用网格搜索来评估神经网络模型的不同配置,并报告提供最佳估计性能的组合。为了更有趣,我们来修改 PyTorch 模型,使其接受一个参数来决定您希望它有多深:

在这个设计中,我们将隐藏层及其激活函数保存在 Python 列表中。由于 PyTorch 组件不是类的直接属性,您将无法在 `model.parameters()` 中看到它们。这将导致训练问题。可以通过使用 `self.add_module()` 注册组件来缓解此问题。另一种方法是使用 `nn.ModuleList()` 而不是 Python 列表,这样您就提供了足够的线索来指示在哪里可以找到模型的组件。

skorch 包装器仍然相同。有了它,您就可以拥有一个与 scikit-learn 兼容的模型。正如您所看到的,有用于设置深度学习模型的参数以及在包装器中指定的训练参数,例如学习率 (`lr`),您有许多可能的变体。scikit-learn 中的 `GridSearchCV` 函数用于提供网格搜索交叉验证。您可以为每个参数提供一个值列表,并要求 scikit-learn 尝试所有组合,并根据您指定的指标报告最佳参数集。一个示例如下:

您将 `model` 传递给 `GridSearchCV()`,它是一个 skorch 包装器。您还传递了 `param_grid`,它指定要改变:

  • PyTorch 模型(即 `SonarClassifier` 类)中的 `n_layers` 参数,控制神经网络的深度
  • 包装器中的 `lr` 参数,控制优化器的学习率
  • 包装器中的参数max_epochs,它控制要运行的训练 epoch 数量

请注意,使用双下划线将参数传递给 PyTorch 模型。实际上,这也允许您配置其他参数。例如,您可以设置optimizer__weight_decayweight_decay参数传递给 Adam 优化器(用于设置 L2 正则化)。

运行此操作可能需要一段时间才能计算,因为它会尝试所有组合,每个组合都使用 3 折交叉验证进行评估。您不希望经常运行此操作,但它可能对您设计模型很有用。

网格搜索完成后,将显示最佳模型的性能和配置组合,然后是所有参数组合的性能,如下所示:

结果如下:

在您的工作站上使用 CPU(而不是 GPU)执行时,这可能需要大约 5 分钟才能完成。运行示例显示了以下结果。您可以看到,网格搜索发现,使用 0.001 的学习率、150 个 epoch 和单个隐藏层,在此问题上获得了大约 65% 的最佳交叉验证分数。

事实上,您可以通过首先标准化输入特征来查看是否可以改进结果。由于包装器允许您将 PyTorch 模型与 scikit-learn 一起使用,因此您也可以实时使用 scikit-learn 的标准化器,并创建一个机器学习管道。

您创建的新对象pipe是另一个 scikit-learn 模型,其工作方式与model对象相同,只是在将数据传递给神经网络之前应用了标准缩放器。因此,您可以在此管道上运行网格搜索,只需对参数的指定方式进行一些调整。

这里有两点需要注意:由于 PyTorch 模型默认运行 32 位浮点数,但 NumPy 数组通常是 64 位浮点数。这些数据类型不一致,但 scikit-learn 的缩放器总是返回 NumPy 数组。因此,您需要使用FunctionTransformer对象在管道中间进行类型转换。

此外,在 scikit-learn 管道中,每个步骤都通过名称引用,例如scalersonarmodel。因此,为管道设置的参数也需要带有名称。在上面的示例中,我们使用sonarmodel__module__n_layers作为网格搜索的参数。这指的是管道的sonarmodel部分(即您的 skorch 包装器)、其中的module部分(即您的 PyTorch 模型)及其n_layers参数。请注意使用双下划线进行层级分隔。

把所有东西放在一起,下面是完整的代码。

进一步阅读

如果您想深入了解,本节提供了更多关于该主题的资源。

在线资源

总结

在本章中,您了解了如何封装 PyTorch 深度学习模型并在 scikit-learn 通用机器学习库中使用它们。您学习了

  • 具体如何封装 PyTorch 模型,以便它们可以与 scikit-learn 机器学习库一起使用。
  • 如何使用封装的 PyTorch 模型作为评估 scikit-learn 中模型性能的一部分。
  • 如何在 scikit-learn 中使用封装的 PyTorch 模型执行超参数调优。

您可以看到,使用 scikit-learn 进行标准机器学习操作(例如模型评估和模型超参数优化)可以节省大量时间,而无需自己实现这些方案。封装您的模型使您能够利用 scikit-learn 的强大工具,将深度学习模型融入您的通用机器学习过程。

开始使用PyTorch进行深度学习!

Deep Learning with PyTorch

学习如何构建深度学习模型

...使用新发布的PyTorch 2.0库

在我的新电子书中探索如何实现
使用 PyTorch进行深度学习

它提供了包含数百个可用代码自学教程,让你从新手变成专家。它将使你掌握:
张量操作训练评估超参数优化等等...

通过动手练习开启你的深度学习之旅


查看内容

将 PyTorch 深度学习模型与 scikit-learn 结合使用的 10 条回复

  1. james 2023 年 2 月 14 日下午 1:50 #

    谢谢您的教程。

    执行代码时我遇到了以下错误。

    —————————————————————————
    TypeError Traceback (most recent call last)
    单元格 In[15],第 7 行
    4 y = encoder.transform(y)
    6 # 转换为 2D PyTorch 张量
    —-> 7 X = torch.tensor(X.values, dtype=torch.float32)
    8 y = torch.tensor(y, dtype=torch.float32)

    TypeError: 无法转换 numpy.object_ 类型的 np.ndarray。唯一支持的类型是:float64, float32, float16, complex64, complex128, int64, int32, int16, int8, uint8, and bool。

    • Adrian Tam
      Adrian Tam 2023 年 2 月 15 日上午 4:29 #

      检查此行之前的 X 是什么。您可能读入了一些非数字内容。

  2. Geoff Hardy 2023 年 3 月 26 日上午 2:04 #

    嗨,詹姆斯!
    感谢您的教程……只是一个拼写错误评论……FunctionTransformer 应该从 sklearn.preprocessing 导入吗???

    • James Carmichael 2023 年 3 月 26 日上午 10:29 #

      嗨,Geoff……您说得对!感谢您的反馈!

  3. zhao hongwei 2023 年 4 月 15 日晚上 11:27 #

    如何使用 gridsearchcv 和 neuralnetclassifier 优化 weight_decay 和 lr_decay

  4. WuGang 2024 年 2 月 15 日下午 7:08 #

    为什么 MLPClassifier 训练的相同超参数比 torch 好那么多

    • James Carmichael 2024 年 2 月 16 日上午 10:32 #

      嗨,WuGang……准确性有什么区别?多次训练执行的平均值是否显示出这种差异?

  5. shadow_ 2024 年 2 月 18 日晚上 10:09 #

    pipe 似乎不需要调用模型的初始化方法,直接写 ('sonarmodel', model) 就可以了,因为 GridSearchCV 会自动初始化,而且初始化在某些情况下容易出现序列化问题。

    • James Carmichael 2024 年 2 月 19 日上午 8:21 #

      嗨,shadow_……感谢您的反馈!

发表回复

Machine Learning Mastery 是 Guiding Tech Media 的一部分,Guiding Tech Media 是一家领先的数字媒体出版商,专注于帮助人们了解技术。访问我们的公司网站以了解更多关于我们的使命和团队的信息。