机器学习:鸢尾花数据集#
如果你想在实时的 Python 内核中尝试这个笔记本,请使用 mybinder:
虽然 vaex.ml 尚未实现预测模型,但我们提供了对强大库(例如 Scikit-learn, xgboost)的封装,并使它们能够高效地与 vaex 协同工作。vaex.ml 实现了多种标准数据转换器(例如 PCA、数值缩放器、分类编码器)以及一个非常高效的 KMeans 算法,这些工具充分利用了 vaex 的优势。
以下是一个使用vaex.ml的简单示例。我们将使用著名的Iris数据集,并利用它来构建一个区分三种爱尔兰物种的模型(Iris setosa, Iris virginica 和 Iris versicolor)。
让我们从导入常用库、加载并检查数据开始。
[1]:
import vaex
import vaex.ml
import matplotlib.pyplot as plt
df = vaex.datasets.iris()
df
[1]:
| # | 花萼长度 | 花萼宽度 | 花瓣长度 | 花瓣宽度 | 类别 |
|---|---|---|---|---|---|
| 0 | 5.9 | 3.0 | 4.2 | 1.5 | 1 |
| 1 | 6.1 | 3.0 | 4.6 | 1.4 | 1 |
| 2 | 6.6 | 2.9 | 4.6 | 1.3 | 1 |
| 3 | 6.7 | 3.3 | 5.7 | 2.1 | 2 |
| 4 | 5.5 | 4.2 | 1.4 | 0.2 | 0 |
| ... | ... | ... | ... | ... | ... |
| 145 | 5.2 | 3.4 | 1.4 | 0.2 | 0 |
| 146 | 5.1 | 3.8 | 1.6 | 0.2 | 0 |
| 147 | 5.8 | 2.6 | 4.0 | 1.2 | 1 |
| 148 | 5.7 | 3.8 | 1.7 | 0.3 | 0 |
| 149 | 6.2 | 2.9 | 4.3 | 1.3 | 1 |
在数据上进行任何操作之前,应立即将数据分为训练和测试步骤。vaex.ml包含一个train_test_split方法,该方法创建主DataFrame的浅拷贝,这意味着在定义训练和测试集时不会使用额外的内存。请注意,train_test_split方法对主DataFrame进行有序分割以创建这两个集。在某些情况下,可能需要打乱数据。
如果需要洗牌,我们建议以下内容:
df.shuffle().export("shuffled.hdf5")
df = vaex.open("shuffled.hdf5")
df_train, df_test = df.ml.train_test_split(test_size=0.2)
在当前情况下,数据集已经被打乱,因此我们可以直接进行分割。
[2]:
# Orderd split in train and test
df_train, df_test = df.ml.train_test_split(test_size=0.2)
/Users/jovan/PyLibrary/vaex/packages/vaex-core/vaex/ml/__init__.py:209: UserWarning: Make sure the DataFrame is shuffled
warnings.warn('Make sure the DataFrame is shuffled')
由于这是一个非常简单的教程,我们将仅使用已提供的列作为训练模型的特征。
[3]:
features = df_train.column_names[:4]
features
[3]:
['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
主成分分析#
vaex.ml 模块包含多个用于数据集转换的类,这些类通常用于在构建模型之前预处理数据。这些包括数值特征缩放器、类别编码器和PCA转换。我们采用了scikit-learn API,意味着所有转换器都有.fit和.transform方法。
让我们在训练集上应用PCA变换。由于PCA也会对数据进行归一化,因此不需要事先缩放数据。
[4]:
pca = vaex.ml.PCA(features=features, n_components=4)
df_train = pca.fit_transform(df_train)
df_train
[4]:
| # | 花萼长度 | 花萼宽度 | 花瓣长度 | 花瓣宽度 | 类别 | PCA_0 | PCA_1 | PCA_2 | PCA_3 |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 5.4 | 3.0 | 4.5 | 1.5 | 1 | -0.5819340944906611 | -0.5192084328455534 | -0.4079706950207428 | -0.22843325658378022 |
| 1 | 4.8 | 3.4 | 1.6 | 0.2 | 0 | 2.628040487885542 | -0.05578001049524599 | -0.09961452867004605 | -0.14960589756342935 |
| 2 | 6.9 | 3.1 | 4.9 | 1.5 | 1 | -1.438496521671396 | 0.5307778852279289 | 0.32322065776316616 | -0.0066478967991949744 |
| 3 | 4.4 | 3.2 | 1.3 | 0.2 | 0 | 3.00633586736142 | -0.41909744036887703 | -0.17571839830952185 | -0.05420541515837107 |
| 4 | 5.6 | 2.8 | 4.9 | 2.0 | 2 | -1.1948465297428466 | -0.6200295372229213 | -0.4751905348367903 | 0.08724845774327505 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 115 | 5.2 | 3.4 | 1.4 | 0.2 | 0 | 2.6608856211270933 | 0.2619681501203415 | 0.12886483875694454 | 0.06429707648769989 |
| 116 | 5.1 | 3.8 | 1.6 | 0.2 | 0 | 2.561545765055359 | 0.4288927940763031 | -0.18633294617759266 | -0.20573646329612738 |
| 117 | 5.8 | 2.6 | 4.0 | 1.2 | 1 | -0.22075578997244774 | -0.40152336651555137 | 0.25417836518749715 | 0.04952191889168374 |
| 118 | 5.7 | 3.8 | 1.7 | 0.3 | 0 | 2.23068249078231 | 0.826166758833374 | 0.07863720599424912 | 0.0004035597987264161 |
| 119 | 6.2 | 2.9 | 4.3 | 1.3 | 1 | -0.6256358184862005 | 0.023930474333675168 | 0.21203674475657858 | -0.0077954052328795265 |
pca .fit_transform 方法的结果是 DataFrame 的浅拷贝,其中包含转换的结果列,在这种情况下是 PCA 组件,作为虚拟列。这意味着转换后的 DataFrame 完全不占用内存!因此,虽然这个示例仅使用了 120 个样本,但即使对于数百万或数十亿个样本,这种方法也同样适用。
梯度提升树#
现在让我们训练一个梯度提升模型。虽然 vaex.ml 目前不包括这种类型的模型,但我们支持流行的提升树库 xgboost, lightgbm, 和 catboost。在本教程中,我们将使用 lightgbm 分类器。
[9]:
import lightgbm
import vaex.ml.sklearn
# Features on which to train the model
train_features = df_train.get_column_names(regex='PCA_.*')
# The target column
target = 'class_'
# Instantiate the LightGBM Classifier
booster = lightgbm.sklearn.LGBMClassifier(num_leaves=5,
max_depth=5,
n_estimators=100,
random_state=42)
# Make it a vaex transformer (for the automagic pipeline and lazy predictions)
model = vaex.ml.sklearn.Predictor(features=train_features,
target=target,
model=booster,
prediction_name='prediction')
# Train and predict
model.fit(df=df_train)
df_train = model.transform(df=df_train)
df_train
[9]:
| # | 花萼长度 | 花萼宽度 | 花瓣长度 | 花瓣宽度 | 类别 | PCA_0 | PCA_1 | PCA_2 | PCA_3 | 预测 |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 5.4 | 3.0 | 4.5 | 1.5 | 1 | -0.5819340944906611 | -0.5192084328455534 | -0.4079706950207428 | -0.22843325658378022 | 1 |
| 1 | 4.8 | 3.4 | 1.6 | 0.2 | 0 | 2.628040487885542 | -0.05578001049524599 | -0.09961452867004605 | -0.14960589756342935 | 0 |
| 2 | 6.9 | 3.1 | 4.9 | 1.5 | 1 | -1.438496521671396 | 0.5307778852279289 | 0.32322065776316616 | -0.0066478967991949744 | 1 |
| 3 | 4.4 | 3.2 | 1.3 | 0.2 | 0 | 3.00633586736142 | -0.41909744036887703 | -0.17571839830952185 | -0.05420541515837107 | 0 |
| 4 | 5.6 | 2.8 | 4.9 | 2.0 | 2 | -1.1948465297428466 | -0.6200295372229213 | -0.4751905348367903 | 0.08724845774327505 | 2 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 115 | 5.2 | 3.4 | 1.4 | 0.2 | 0 | 2.6608856211270933 | 0.2619681501203415 | 0.12886483875694454 | 0.06429707648769989 | 0 |
| 116 | 5.1 | 3.8 | 1.6 | 0.2 | 0 | 2.561545765055359 | 0.4288927940763031 | -0.18633294617759266 | -0.20573646329612738 | 0 |
| 117 | 5.8 | 2.6 | 4.0 | 1.2 | 1 | -0.22075578997244774 | -0.40152336651555137 | 0.25417836518749715 | 0.04952191889168374 | 1 |
| 118 | 5.7 | 3.8 | 1.7 | 0.3 | 0 | 2.23068249078231 | 0.826166758833374 | 0.07863720599424912 | 0.0004035597987264161 | 0 |
| 119 | 6.2 | 2.9 | 4.3 | 1.3 | 1 | -0.6256358184862005 | 0.023930474333675168 | 0.21203674475657858 | -0.0077954052328795265 | 1 |
请注意,在训练模型后,我们使用.transform方法来获取包含模型预测的DataFrame的浅拷贝,以虚拟列的形式存在。这使得评估模型变得容易,并且可以轻松创建各种诊断图。如果需要,可以调用.predict方法,这将生成一个内存中的numpy.array,其中包含预测结果。
自动管道#
假设我们对模型的性能感到满意,我们可以继续并将我们的转换和模型应用到测试集上。与其他库不同,我们不需要显式地创建一个管道来传播这些转换。实际上,使用vaex和vaex.ml,管道会在数据探索过程中自动创建。每个vaex DataFrame包含一个状态,这是一个(可序列化的)对象,包含了应用于DataFrame的所有转换信息(过滤、创建新的虚拟列、转换)。
回想一下,PCA转换和增强模型的输出实际上是虚拟列,因此存储在df_train的状态中。我们需要做的就是将此状态应用于另一个类似的DataFrame(例如测试集),所有的更改都将被传播。
[6]:
state = df_train.state_get()
df_test.state_set(state)
df_test
[6]:
| # | 花萼长度 | 花萼宽度 | 花瓣长度 | 花瓣宽度 | 类别 | PCA_0 | PCA_1 | PCA_2 | PCA_3 | 预测 |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 5.9 | 3.0 | 4.2 | 1.5 | 1 | -0.4978687101343986 | -0.11289245880584761 | -0.11962601206069637 | 0.0625954090178564 | 1 |
| 1 | 6.1 | 3.0 | 4.6 | 1.4 | 1 | -0.8754765898560835 | -0.03902402119573594 | 0.022944044447894815 | -0.14143773065379384 | 1 |
| 2 | 6.6 | 2.9 | 4.6 | 1.3 | 1 | -1.0228803632878913 | 0.2503709022470443 | 0.4130613754204865 | -0.030391911559003282 | 1 |
| 3 | 6.7 | 3.3 | 5.7 | 2.1 | 2 | -2.2544508624315838 | 0.3431374410700749 | -0.28908707579214765 | -0.07059175451207655 | 2 |
| 4 | 5.5 | 4.2 | 1.4 | 0.2 | 0 | 2.632289228948536 | 1.020394958612415 | -0.20769510079946696 | -0.13744144140286718 | 0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 25 | 5.5 | 2.5 | 4.0 | 1.3 | 1 | -0.16189655085432594 | -0.6871827581512436 | 0.09773053160021669 | 0.07093166682594204 | 1 |
| 26 | 5.8 | 2.7 | 3.9 | 1.2 | 1 | -0.12526327170089271 | -0.3148233189949767 | 0.19720893202789733 | 0.060419826927667064 | 1 |
| 27 | 4.4 | 2.9 | 1.4 | 0.2 | 0 | 2.8918941837640526 | -0.6426744898497139 | 0.006171795874510444 | 0.007700652884580328 | 0 |
| 28 | 4.5 | 2.3 | 1.3 | 0.3 | 0 | 2.850207707200544 | -0.9710397723109179 | 0.38501428492268475 | 0.377723418991853 | 0 |
| 29 | 6.9 | 3.2 | 5.7 | 2.3 | 2 | -2.405639277483925 | 0.4027072938482219 | -0.22944817803540973 | 0.17443211711742812 | 2 |
生产#
现在 df_test 包含了我们在训练集 (df_train) 上应用的所有转换,包括模型预测。将状态从一个 DataFrame 转移到另一个 DataFrame 对于将模型投入生产可能非常有价值。
性能#
最后,让我们检查模型的性能。
[7]:
from sklearn.metrics import accuracy_score
acc = accuracy_score(y_true=df_test.class_.values, y_pred=df_test.prediction.values)
acc *= 100.
print(f'Test set accuracy: {acc}%')
Test set accuracy: 100.0%
模型获得了100%的完美准确率。这并不令人惊讶,因为这个问题相当简单:对特征进行PCA变换可以很好地分离出3种花卉物种。绘制前两个PCA轴,并根据样本的类别进行着色,已经显示出几乎完美的分离。
[8]:
plt.figure(figsize=(8, 4))
df_test.scatter(df_test.PCA_0, df_test.PCA_1, c_expr=df_test.class_, s=50)
plt.show()