跳至主要内容

Python 中的 TPOT

在本教程中,您将学习如何在 Python 中使用一个非常独特的库:TPOT。该库之所以独特,是因为它能自动化整个机器学习流水线,并为您提供表现最佳的机器学习模型。
· 13分钟 阅读

使用 AI 探索

在本教程中,您将学习如何在 Python 中使用一个非常独特的库:tpot。之所以说它独特,是因为它可以自动化整个机器学习流水线,并为您提供表现最优的机器学习模型。更具体地,您将学习:

  • 自动化机器学习(AutoML)的理念
  • TPOT 如何使用遗传编程来选择最佳的机器学习模型
  • 在 Python 中将 TPOT 应用于一个数据集
  • TPOT 的局限性

让我们开始吧!

简介

在解决一个机器学习问题之前,您需要考虑很多组成部分,其中包括数据准备、特征选择、特征工程、模型选择与验证、超参数调优等。从理论上讲,针对每个环节您都可以找到并应用大量技术,但它们在不同数据集上的表现可能各不相同。挑战在于找到表现最好的技术组合,从而将预测误差降到最低。这正是当下人们致力于开发 AutoML 算法和平台的主要原因——让没有机器学习专长的人也能在不花费太多时间和精力的情况下构建模型。这样的平台之一就是以 Python 库形式提供的 TPOT。您可以把 TPOT 看作您的数据科学助理。TPOT 是一个 Python 自动化机器学习工具,使用遗传编程来优化机器学习流水线。它会通过智能地探索成千上万种可能的流水线,自动化机器学习中最繁琐的部分,为您的数据找到最佳方案。

from IPython.core.display import Image
Image(filename="/home/manishpathak/DataCamp/tpot/tpot-ml-pipeline.png",width=800,height=800)
automated by tpot diagram

当 TPOT 完成搜索后,它会为找到的最佳流水线提供对应的 Python 代码,方便您在此基础上继续微调。

安装

要在您的系统上安装 tpot,可以在命令行终端运行命令 sudo pip install tpot,或查看此链接。tpot 构建在多个现有的 Python 库之上,包括 numpy、scipy、scikit-learn、DEAP、update_checker、tqdm、stopit、pandas。大多数必要的 Python 包可以通过 Anaconda Python 发行版安装,当然也可以分别安装。可选地,如果您希望 tpot 使用 eXtreme Gradient Boosting 模型,还可以安装 XGBoost。

遗传编程

在拥有合适的数据、算力和机器学习模型时,您可以找到针对任何问题的解决方案,但在众多模型(如决策树、SVM、KNN 等)中选择合适的一个对您来说可能很有挑战。这正是遗传编程大显身手之处。遗传算法受达尔文自然选择过程的启发,用于在计算机科学中为优化与搜索问题生成解。

笼统地说,遗传算法具有三个特性:

  • 选择(Selection):针对给定问题,您有一个可能解的种群和一个适应度函数。在每次迭代中,您用适应度函数评估每个解的适应程度。
  • 交叉(Crossover):然后选择适应度最高的个体,执行交叉以产生新种群。
  • 变异(Mutation):对这些“子代”进行随机改动的变异,并重复该过程,直到得到最适或最佳解。

遗传编程本身是计算机科学中的一个大话题,如果您希望更深入了解遗传算法,请查看这个视频系列。

但这一切如何与数据科学相结合呢?

事实证明,为模型选择合适的机器学习算法及其最佳超参数本身就是一个优化问题,可以用遗传编程来解决。构建在 scikit-learn 之上的 Python 库 tpot 使用遗传编程来优化您的机器学习流水线。举例来说,在机器学习中,准备好数据之后,您需要知道将哪些特征输入模型、以及如何构造这些特征。有了这些特征后,将它们输入模型进行训练,然后调优超参数以获得最佳结果。与其通过反复试错亲自完成这些步骤,TPOT 会用遗传编程为您自动化实现,并在完成时输出最优代码。

为进一步理解,您将练习在一个开源数据集上使用 tpot。您将使用的数据集是 MAGIC Gamma Telescope 数据集。该数据用于模拟地面大气切伦科夫伽马望远镜利用成像技术记录高能伽马粒子的过程。有关数据集的完整说明,请查看这个链接。该数据集包含以下属性/特征:

  1. fLength:连续型 # 椭圆长轴 [mm]
  2. fWidth:连续型 # 椭圆短轴 [mm]
  3. fSize:连续型 # 所有像素内容之和的以 10 为底对数 [单位:#phot]
  4. fConc:连续型 # 最高的两个像素之和与 fSize 的比值 [比率]
  5. fConc1:连续型 # 最高像素与 fSize 的比值 [比率]
  6. fAsym:连续型 # 最高像素到中心的距离,沿主轴投影 [mm]
  7. fM3Long:连续型 # 沿主轴的三阶矩的三次方根 [mm]
  8. fM3Trans:连续型 # 沿次轴的三阶矩的三次方根 [mm]
  9. fAlpha:连续型 # 主轴与指向原点的向量之间的角度 [度]
  10. fDist:连续型 # 原点到椭圆中心的距离 [mm]
  11. class:g、h # gamma(信号),hadron(背景)(目标变量)

目标是基于给定属性,将观测分类为 gamma(期望信号)或 hadron(背景噪声)。

您将先导入 pandas 和 numpy 库,分别用于读取数据和进行计算。

import pandas as pd
import numpy as np

使用 pandas 的 read_csv() 函数将数据集读取为 DataFrame。同时,由于数据集尚无列名,请指定参数 header = None。

telescope_data=pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/magic/magic04.data',header=None)

使用 head() 方法查看 DataFrame 的内容。

telescope_data.head()
dataframe 1

要为 DataFrame 的列命名,您可以对 pandas DataFrame 使用 columns 属性,并赋值为包含列名的列表。

telescope_data.columns = ['fLength', 'fWidth','fSize','fConc','fConcl','fAsym','fM3Long','fM3Trans','fAlpha','fDist','class']
telescope_data.head()
dataframe 2

现在,您的 DataFrame 也有了列名。

要获取 DataFrame 的必要信息,如每列的数值数量、数据类型、count、mean 等,您可以使用 pandas 的 info() 和 describe() 方法。

telescope_data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 19020 entries, 0 to 19019
Data columns (total 11 columns):
fLength     19020 non-null float64
fWidth      19020 non-null float64
fSize       19020 non-null float64
fConc       19020 non-null float64
fConcl      19020 non-null float64
fAsym       19020 non-null float64
fM3Long     19020 non-null float64
fM3Trans    19020 non-null float64
fAlpha      19020 non-null float64
fDist       19020 non-null float64
class       19020 non-null object
dtypes: float64(10), object(1)
memory usage: 1.6+ MB
telescope_data.describe()
dataframe 3

结果显示 DataFrame 中的所有特征都是连续型的。而目标变量/特征 class 则是分类型。您可以使用 value_counts() 方法查看目标变量中各类别的计数。

telescope_data['class'].value_counts()
g    12332
h     6688
Name: class, dtype: int64

通常在开始之前随机打乱数据是个好主意,以避免数据中的任何排序效应。您可以使用 numpy 的 random 和 permutation() 函数重新排列 DataFrame 中的数据。打乱后要重置索引号,请使用 reset_index() 方法并将参数 drop = True。

telescope_shuffle=telescope_data.iloc[np.random.permutation(len(telescope_data))]
tele=telescope_shuffle.reset_index(drop=True)
tele.head()
dataframe 4

在使用 tpot 之前,必须对 DataFrame 中的分类变量进行标签化。要了解更多关于分类变量标签化的内容,请查看此教程。这里只有目标变量 class 是分类特征,因此只需对该列进行处理。您将把 g 类(信号)替换为数值 0,将 h 类(背景)替换为数值 1。这可以通过 map() 函数实现,参数为一个字典,保存了目标变量类别的对应映射。

tele['class']=tele['class'].map({'g':0,'h':1})

tele.head()
dataframe 5

现在将需要预测的类别标签单独存储在变量 tele_class 中。

tele_class = tele['class'].values

在使用 tpot 之前,您还应进行缺失值处理。要按列检查缺失值数量,您可以执行以下操作:

pd.isnull(tele).any()
fLength     False
fWidth      False
fSize       False
fConc       False
fConcl      False
fAsym       False
fM3Long     False
fM3Trans    False
fAlpha      False
fDist       False
class       False
dtype: bool

该数据集没有任何缺失值。注意,在有缺失值的数据集中,您可以使用 dropna() 方法删除行/列,或使用 fillna() 方法用某个占位值替换缺失值。例如,下面的代码片段会将 NA 值替换为占位值 -999。

tele = tele.fillna(-999)

现在,您将像进行任何机器学习建模一样,将 DataFrame 划分为训练集和测试集。您可以通过 sklearn 的 cross_validation 中的 train_test_split 来完成。参数包括将 tele.index 作为 DataFrame 的索引,train_size = 0.75 将 75% 的数据用于训练集,test_size = 0.25 将剩余 25% 用于测试集,以及 stratify = tele_class 为数据集中的类别标签值。注意,验证集只是为了给我们一个测试集误差的参考。这里将其设定与测试集相同。

from sklearn.cross_validation import train_test_split
training_indices, validation_indices = training_indices, testing_indices = train_test_split(tele.index,
                                                                                            stratify = tele_class,
                                                                                            train_size=0.75, test_size=0.25)

您可以使用 size 属性查看训练集和验证集的大小。

training_indices.size, validation_indices.size
(14265, 4755)

现在是使用 tpot 来为这个二分类问题推荐最佳流水线的时候了。为此,您需要从 tpot 库中导入 TPOTClassifier 类。如果这是一个回归问题,您将导入 TPOTRegressor 类。

TPOTClassifier 拥有多种参数,您可以在这里详细阅读。但您必须了解的几个重要参数包括:

  • generations:运行流水线优化过程的迭代次数。默认 100。
  • population_size:每一代遗传编程种群中保留的个体数。默认 100。
  • offspring_size:每一代遗传编程中产生的子代数量。默认 100。
  • mutation_rate:遗传编程算法的变异率,范围 [0.0, 1.0]。该参数表示每一代对多少条流水线应用随机更改。默认 0.9。
  • crossover_rate:遗传编程算法的交叉率,范围 [0.0, 1.0]。该参数表示每一代遗传编程算法“繁衍”多少条流水线。
  • scoring:用于评估给定流水线在分类问题上的质量的函数,如 accuracy、average_precision、roc_auc、recall 等。默认 accuracy。
  • cv:评估流水线时使用的交叉验证策略。默认 5。
  • random_state:TPOT 使用的伪随机数生成器的种子。使用该参数可确保在相同数据集上、相同种子下,每次运行 TPOT 都得到相同结果。

另注意,mutation_rate + crossover_rate 之和不能超过 1.0。

这里您将使用 tpot,将 generations = 5,其余参数保持默认。参数 verbosity = 2 表示 TPOT 运行时输出信息的详尽程度。

然后调用 fit() 方法,传入训练集(不含目标列)和目标列作为参数。

注意,运行下方单元格中的代码将需要数小时才能完成。以当前 TPOT 设置(5 代,种群大小 100)为例,TPOT 在结束前将评估 500 个流水线配置。为了帮助理解这个数量级,可以把它类比为对某个机器学习算法进行 500 组超参数的网格搜索,以及执行该网格搜索所需的时间。这意味着需要用 5 折交叉验证评估 500 个模型配置,也就是在一次网格搜索中大约拟合并评估 2500 个模型。这是一个耗时的过程!稍后,您将了解到可以传给 TPOTClassifier 的一些参数,用于控制 TPOT 的执行时间。

from tpot import TPOTClassifier
from tpot import TPOTRegressor

tpot = TPOTClassifier(generations=5,verbosity=2)

tpot.fit(tele.drop('class',axis=1).loc[training_indices].values,
         tele.loc[training_indices,'class'].values)
Optimization Progress:  33%|███▎      | 200/600 [41:43<1:51:41, 16.75s/pipeline]

Generation 1 - Current best internal CV score: 0.880266061124

Optimization Progress:  50%|█████     | 300/600 [1:14:37<46:57,  9.39s/pipeline]  

Generation 2 - Current best internal CV score: 0.880266061124

Optimization Progress:  67%|██████▋   | 400/600 [1:59:17<2:21:21, 42.41s/pipeline]

Generation 3 - Current best internal CV score: 0.880266061124

Optimization Progress:  84%|████████▎ | 501/600 [3:01:12<1:02:16, 37.74s/pipeline]

Generation 4 - Current best internal CV score: 0.881597992166

Generation 5 - Current best internal CV score: 0.881597992166

Best pipeline: GradientBoostingClassifier(RobustScaler(PolynomialFeatures(input_matrix, degree=2, include_bias=False, interaction_only=False)), learning_rate=0.1, max_depth=6, max_features=0.6000000000000001, min_samples_leaf=12, min_samples_split=3, n_estimators=100, subsample=0.55)

TPOTClassifier(config_dict={'sklearn.ensemble.GradientBoostingClassifier': {'max_features': array([0.05, 0.1 , 0.15, 0.2 , 0.25, 0.3 , 0.35, 0.4 , 0.45, 0.5 , 0.55,
       0.6 , 0.65, 0.7 , 0.75, 0.8 , 0.85, 0.9 , 0.95, 1.  ]), 'learning_rate': [0.001, 0.01, 0.1, 0.5, 1.0], 'min_samples_leaf': [1, 2, 3, 4, 5, 6, 7...  0.6 , 0.65, 0.7 , 0.75, 0.8 , 0.85, 0.9 , 0.95, 1.  ])}, 'sklearn.preprocessing.RobustScaler': {}},
        crossover_rate=0.1, cv=5, disable_update_check=False,
        early_stop=None, generations=5, max_eval_time_mins=5,
        max_time_mins=None, memory=None, mutation_rate=0.9, n_jobs=1,
        offspring_size=100, periodic_checkpoint_folder=None,
        population_size=100, random_state=None, scoring=None,
        subsample=1.0, verbosity=2, warm_start=False)

在上述运行中,共计算了 5 代,每一代都会给出拟合模型在训练集上的训练效率。可以看到,最佳流水线的交叉验证准确率为 88.16%。产生该结果的模型流水线包含了如 PloynomialFeatures 和 RobustScaler 等预处理技术,它们为输入数据添加合成特征并对其进行归一化,然后由梯度提升分类器用于生成最终预测。您还会注意到,tpot 也同时给出了分类器的各种超参数取值,如 learning_rate、max_depth 等。

接下来,计算测试误差以用于验证。

tpot.score(tele.drop('class',axis=1).loc[validation_indices].values,
           tele.loc[validation_indices, 'class'].values)
0.885173501577287

如上所示,测试准确率为 88.51%。

最后,您可以通过 export 函数,要求 TPOT 将优化后流水线对应的 Python 代码导出到一个文本文件中:

tpot.export('tpot_MAGIC_Gamma_Telescope_pipeline.py')
True
## This is the pipeline that tpot generated: tpot_MAGIC_Gamma_Telescope_pipeline.py
import numpy as np
import pandas as pd
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures, RobustScaler

# NOTE: Make sure that the class is labeled 'target' in the data file
tpot_data = pd.read_csv('PATH/TO/DATA/FILE', sep='COLUMN_SEPARATOR', dtype=np.float64)
features = tpot_data.drop('target', axis=1).values
training_features, testing_features, training_target, testing_target = \
            train_test_split(features, tpot_data['target'].values, random_state=42)

# Score on the training set was:0.881597992166
exported_pipeline = make_pipeline(
    PolynomialFeatures(degree=2, include_bias=False, interaction_only=False),
    RobustScaler(),
    GradientBoostingClassifier(learning_rate=0.1, max_depth=6, max_features=0.6, min_samples_leaf=12, min_samples_split=3, n_estimators=100, subsample=0.55)
)

exported_pipeline.fit(training_features, training_target)
results = exported_pipeline.predict(testing_features)

是不是很棒?无需您对大量参数和选项进行调校来获得最优模型,TPOT 不仅告诉您最佳模型是什么,还给出了可运行的代码!

正如之前所提到的,上一次 TPOT 运行花了数小时才完成。其实您可以通过设定一些参数来控制 TPOT 的执行时间,但这需要权衡。由于限制了 TPOT 的执行时间,TPOT 将无法穷尽所有可能的流水线,因此在受限时间结束时 TPOT 给出的最佳模型,可能并不是该数据集上可能达到的最优模型。不过,如果给予足够时间,它会与最优模型更为接近。相关参数包括:

  • max_time_mins:TPOT 优化流水线可用的分钟数。如果不是 None,则该设置会覆盖 generations 参数,让 TPOT 运行直到达到 max_time_mins 分钟。
  • max_eval_time_mins:TPOT 评估单个流水线可用的分钟数。将此参数设置为更高的值可使 TPOT 评估更复杂的流水线,但也会让 TPOT 运行更久。使用该参数有助于防止 TPOT 在评估耗时流水线上浪费时间。默认 5。
  • early_stop:TPOT 用于检查优化过程是否没有改进的代数。如果在给定代数内没有改进,则结束优化过程。
  • n_jobs:TPOT 在优化过程中并行用于评估流水线的进程数。设置 n_jobs=-1 将使用计算机上可用的全部核心。注意,在同一台机器上使用多个进程,对于大数据集可能引发内存问题。默认 1。
  • subsample:TPOT 优化过程中使用的训练样本比例。必须在 (0.0, 1.0] 范围内。默认 1。

作为练习,您将再次运行 TPOT,这次添加参数 max_time_mins = 2 和 max_eval_time_mins = 0.04,同时将 population_size = 15 下调。

tpot = TPOTClassifier(verbosity=2, max_time_mins=2, max_eval_time_mins=0.04, population_size=15)
tpot.fit(tele.drop('class',axis=1).loc[training_indices].values, tele.loc[training_indices,'class'].values)
Optimization Progress: 42pipeline [00:42,  1.17s/pipeline]                  

Generation 1 - Current best internal CV score: 0.86119902629

Optimization Progress: 64pipeline [01:03,  1.04s/pipeline]                  

Generation 2 - Current best internal CV score: 0.86119902629

Optimization Progress: 83pipeline [01:21,  1.14s/pipeline]

Generation 3 - Current best internal CV score: 0.861689811492

Optimization Progress: 104pipeline [01:45,  1.03pipeline/s]

Generation 4 - Current best internal CV score: 0.861759642796

2.03228221667 minutes have elapsed. TPOT will close down.
TPOT closed prematurely. Will use the current best pipeline.

Best pipeline: XGBClassifier(RobustScaler(SelectPercentile(input_matrix, percentile=85)), learning_rate=0.1, max_depth=2, min_child_weight=4, n_estimators=100, nthread=1, subsample=0.1)

TPOTClassifier(config_dict={'sklearn.ensemble.GradientBoostingClassifier': {'max_features': array([0.05, 0.1 , 0.15, 0.2 , 0.25, 0.3 , 0.35, 0.4 , 0.45, 0.5 , 0.55,
       0.6 , 0.65, 0.7 , 0.75, 0.8 , 0.85, 0.9 , 0.95, 1.  ]), 'learning_rate': [0.001, 0.01, 0.1, 0.5, 1.0], 'min_samples_leaf': [1, 2, 3, 4, 5, 6, 7...  0.6 , 0.65, 0.7 , 0.75, 0.8 , 0.85, 0.9 , 0.95, 1.  ])}, 'sklearn.preprocessing.RobustScaler': {}},
        crossover_rate=0.1, cv=5, disable_update_check=False,
        early_stop=None, generations=1000000, max_eval_time_mins=0.04,
        max_time_mins=2, memory=None, mutation_rate=0.9, n_jobs=1,
        offspring_size=15, periodic_checkpoint_folder=None,
        population_size=15, random_state=None, scoring=None, subsample=1.0,
        verbosity=2, warm_start=False)

可以看到,在指定的时间范围内,表现最好的分类器变成了 XGBoost,预处理步骤为 SelectPercentile() 和 RobustScaler()。

局限性

  • TPOT 可能需要很长时间才能完成搜索

运行 TPOT 并不像在数据集上拟合一个模型那么简单。它会在流水线中同时考虑多种机器学习算法(随机森林、线性模型、SVM 等)、众多预处理步骤(缺失值填补、缩放、PCA、特征选择等)、所有模型和预处理步骤的超参数,以及在流水线中集成或堆叠算法的多种方式。这就是为什么它通常需要很长的执行时间,并且在大规模数据集上难以落地。

  • TPOT 可能会对同一数据集推荐不同的解决方案

如果您处理的是一个相当复杂的数据集,或在较短时间内运行 TPOT,那么不同的 TPOT 运行可能会给出不同的流水线建议。当两次 TPOT 运行推荐的流水线不同,这意味着运行未能在有限时间内收敛,或者有多条流水线在您的数据集上表现大致相当。

结语

太棒了!您已经完成本教程的学习。您从自动化机器学习平台背后的理念入手,随后了解了遗传编程,以及 TPOT 如何利用它来自动化构建机器学习流水线的过程。您还在一个数据集上练习了在 Python 中使用 TPOT,并了解了它提供的相关函数。如果您计划未来使用 TPOT,我强烈建议您查看其出色的文档。祝您探索愉快!

如果您希望进一步学习 Python 中的机器学习,请参加 DataCamp 的 Machine Learning with Tree-Based Models in Python 课程。另请查看 DataCamp 的 Introduction to Machine Learning in Python 教程。
 

撰写本教程时参考了以下网页。

主题