机器学习口袋参考-四-

135 阅读26分钟

机器学习口袋参考(四)

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

译者:飞龙

协议:CC BY-NC-SA 4.0

第十六章:解释回归模型

大多数用于解释分类模型的技术同样适用于回归模型。在本章中,我将展示如何使用 SHAP 库解释回归模型。

我们将解释波士顿房屋数据集的 XGBoost 模型:

>>> import xgboost as xgb
>>> xgr = xgb.XGBRegressor(
...     random_state=42, base_score=0.5
... )
>>> xgr.fit(bos_X_train, bos_y_train)

Shapley

我非常喜欢 Shapley 因为它对模型不可知。这个库还为我们提供了全局对模型的洞察,并帮助解释单个预测。如果你有一个黑盒模型,我发现它非常有用。

我们首先来看看索引 5 的预测。我们的模型预测值为 27.26:

>>> sample_idx = 5
>>> xgr.predict(bos_X.iloc[[sample_idx]])
array([27.269186], dtype=float32)

要使用模型,我们必须从我们的模型创建一个TreeExplainer,并估算我们样本的 SHAP 值。如果我们想在 Jupyter 上使用交互界面,我们还需要调用initjs函数:

>>> import shap
>>> shap.initjs()

>>> exp = shap.TreeExplainer(xgr)
>>> vals = exp.shap_values(bos_X)

有了解释器和 SHAP 值,我们可以创建一个力图来解释预测(见图 16-1)。这告诉我们基础预测值为 23,人口状态(LSTAT)和财产税率(TAX)将价格推高,而房间数(RM)将价格推低:

>>> shap.force_plot(
...     exp.expected_value,
...     vals[sample_idx],
...     bos_X.iloc[sample_idx],
... )

回归的力图。由于人口状态和税率,预期值从 23 增至 27。

图 16-1. 回归的力图。由于人口状态和税率,预期值从 23 增至 27。

我们也可以查看所有样本的力图,以获得整体行为的感觉。如果我们在 Jupyter 上使用交互式 JavaScript 模式,我们可以将鼠标悬停在样本上,查看影响结果的特征(见图 16-2):

>>> shap.force_plot(
...     exp.expected_value, vals, bos_X
... )

所有样本的回归力图。

图 16-2. 所有样本的回归力图。

从样本的力图中,我们看到 LSTAT 特征有很大的影响。为了可视化 LSTAT 如何影响结果,我们可以创建一个依赖图。库将自动选择一个特征进行着色(您可以提供interaction_index参数来设置您自己的)。

从 LSTAT 的依赖图中(见图 16-3),我们可以看到随着 LSTAT 的增加(低社会地位人口的百分比),SHAP 值下降(推动目标向下)。非常低的 LSTAT 值会提升 SHAP。通过查看 TAX(财产税率)的颜色,我们可以看到随着税率的降低(更蓝色),SHAP 值上升:

>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> shap.dependence_plot("LSTAT", vals, bos_X)
>>> fig.savefig(
...     "images/mlpr_1603.png",
...     bbox_inches="tight",
...     dpi=300,
... )

LSTAT 的依赖图。随着 LSTAT 的增加,预测值下降。

图 16-3. LSTAT 的依赖图。随着 LSTAT 的增加,预测值下降。

这里是另一个依赖图,在图 16-4 中展示了 DIS(到就业中心的距离)。看起来这个特征的影响很小,除非它非常小:

>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> shap.dependence_plot(
...     "DIS", vals, bos_X, interaction_index="RM"
... )
>>> fig.savefig(
...     "images/mlpr_1604.png",
...     bbox_inches="tight",
...     dpi=300,
... )

DIS 的依赖图。除非 DIS 非常小,否则 SHAP 保持相对平缓。

图 16-4. DIS 的依赖图。除非 DIS 非常小,否则 SHAP 保持相对平稳。

最后,我们将使用总结图来查看特征的全局效果(见 Figure 16-5)。顶部的特征对模型影响最大。从这个视角可以看出,RM(房间数)的大值显著提升了目标值,而中等和较小值则略微降低了它:

>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> shap.summary_plot(vals, bos_X)
>>> fig.savefig(
...     "images/mlpr_1605.png",
...     bbox_inches="tight",
...     dpi=300,
... )

总结图。最重要的特征位于顶部。

图 16-5. 总结图。最重要的特征位于顶部。

SHAP 库是您工具箱中的一个很好的工具。它帮助理解特征的全局影响,同时也有助于解释单个预测。

第十七章:降维

有许多技术将特征分解为较小的子集。这对于探索性数据分析、可视化、制作预测模型或聚类很有用。

在本章中,我们将使用各种技术探索泰坦尼克号数据集。我们将查看 PCA、UMAP、t-SNE 和 PHATE。

这是数据:

>>> ti_df = tweak_titanic(orig_df)
>>> std_cols = "pclass,age,sibsp,fare".split(",")
>>> X_train, X_test, y_train, y_test = get_train_test_X_y(
...     ti_df, "survived", std_cols=std_cols
... )
>>> X = pd.concat([X_train, X_test])
>>> y = pd.concat([y_train, y_test])

主成分分析(PCA)

主成分分析(PCA)接受一个行(样本)和列(特征)的矩阵(X)。PCA 返回一个新的矩阵,其列是原始列的线性组合。这些线性组合最大化方差。

每列与其他列正交(成直角)。列按方差递减的顺序排序。

Scikit-learn 有这个模型的实现。在运行算法之前最好标准化数据。调用.fit方法后,您将可以访问一个.explained_variance_ratio_属性,列出每列中方差的百分比。

PCA 对于在二维(或三维)中可视化数据很有用。它还用作预处理步骤,以过滤数据中的随机噪声。它适合于找到全局结构,但不适合找到局部结构,并且在处理线性数据时表现良好。

在这个例子中,我们将在泰坦尼克号特征上运行 PCA。PCA 类是 scikit-learn 中的一个转换器;您调用.fit方法来教它如何获取主成分,然后调用.transform将矩阵转换为主成分矩阵:

>>> from sklearn.decomposition import PCA
>>> from sklearn.preprocessing import (
...     StandardScaler,
... )
>>> pca = PCA(random_state=42)
>>> X_pca = pca.fit_transform(
...     StandardScaler().fit_transform(X)
... )
>>> pca.explained_variance_ratio_
array([0.23917891, 0.21623078, 0.19265028,
 0.10460882, 0.08170342, 0.07229959,
 0.05133752, 0.04199068])

>>> pca.components_[0]
arrayarray([-0.63368693,  0.39682566,
 0.00614498,  0.11488415,  0.58075352,
 -0.19046812, -0.21190808, -0.09631388])

实例参数:

n_components=None

生成的组件数量。如果为None,则返回与列数相同的组件数量。可以是一个浮点数(0, 1),那么将创建所需数量的组件以获得该方差比例。

copy=True

如果为True,将在.fit时改变数据。

whiten=False

转换后的白化数据以确保无关联的组件。

svd_solver='auto'

如果n_components小于最小维度的 80%,则运行'randomized' SVD(更快但是近似)。否则运行'full'

tol=0.0

对奇异值的容差。

iterated_power='auto'

'randomized' svd_solver的迭代次数。

random_state=None

'randomized' svd_solver的随机状态。

属性:

components_

主成分(原始特征的线性组合权重列)。

explained_variance_

每个组件的方差量。

explained_variance_ratio_

每个组件的方差量归一化(总和为 1)。

singular_values_

每个组件的奇异值。

mean_

每个特征的均值。

n_components_

n_components是浮点数时,这是组件的大小。

noise_variance_

估计的噪声协方差。

绘制解释方差比例的累积和被称为屏幕图(见图 17-1)。它将显示组件中存储了多少信息。您可以使用肘方法来确定使用多少个组件:

>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> ax.plot(pca.explained_variance_ratio_)
>>> ax.set(
...     xlabel="Component",
...     ylabel="Percent of Explained variance",
...     title="Scree Plot",
...     ylim=(0, 1),
... )
>>> fig.savefig(
...     "images/mlpr_1701.png",
...     dpi=300,
...     bbox_inches="tight",
... )

PCA 屏幕图。

图 17-1. PCA 屏幕图。

查看数据的另一种方法是使用累计图(见图 17-2)。我们的原始数据有 8 列,但从图中看来,如果仅使用 4 个 PCA 成分,我们可以保留大约 90% 的方差:

>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> ax.plot(
...     np.cumsum(pca.explained_variance_ratio_)
... )
>>> ax.set(
...     xlabel="Component",
...     ylabel="Percent of Explained variance",
...     title="Cumulative Variance",
...     ylim=(0, 1),
... )
>>> fig.savefig("images/mlpr_1702.png", dpi=300)

PCA 累计解释方差。

图 17-2. PCA 累计解释方差。

特征对成分的影响有多大?使用 matplotlib 的 imshow 函数将成分沿 x 轴和原始特征沿 y 轴绘制出来(见图 17-3)。颜色越深,原始列对成分的贡献越大。

看起来第一个成分受到 pclass、age 和 fare 列的影响很大。(使用光谱色图(cmap)强调非零值,并提供 vminvmax 为色条图例添加限制。)

>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> plt.imshow(
...     pca.components_.T,
...     cmap="Spectral",
...     vmin=-1,
...     vmax=1,
... )
>>> plt.yticks(range(len(X.columns)), X.columns)
>>> plt.xticks(range(8), range(1, 9))
>>> plt.xlabel("Principal Component")
>>> plt.ylabel("Contribution")
>>> plt.title(
...     "Contribution of Features to Components"
... )
>>> plt.colorbar()
>>> fig.savefig("images/mlpr_1703.png", dpi=300)

PCA 特征在成分中。

图 17-3. 主成分分析中的 PCA 特征。

另一种查看数据的方式是使用条形图(见图 17-4)。每个成分显示了来自原始数据的贡献:

>>> fig, ax = plt.subplots(figsize=(8, 4))
>>> pd.DataFrame(
...     pca.components_, columns=X.columns
... ).plot(kind="bar", ax=ax).legend(
...     bbox_to_anchor=(1, 1)
... )
>>> fig.savefig("images/mlpr_1704.png", dpi=300)

PCA 特征在成分中。

图 17-4. 主成分分析中的 PCA 特征。

如果我们有很多特征,可能希望通过仅显示满足最小权重要求的特征来限制上述图形。以下是找出前两个成分中具有至少 0.5 绝对值的所有特征的代码:

>>> comps = pd.DataFrame(
...     pca.components_, columns=X.columns
... )
>>> min_val = 0.5
>>> num_components = 2
>>> pca_cols = set()
>>> for i in range(num_components):
...     parts = comps.iloc[i][
...         comps.iloc[i].abs() > min_val
...     ]
...     pca_cols.update(set(parts.index))
>>> pca_cols
{'fare', 'parch', 'pclass', 'sibsp'}

PCA 常用于以两个成分可视化高维数据集。在这里,我们在 2D 中可视化了 Titanic 的特征。它们根据生存状态进行了着色。有时可视化中可能会出现聚类。在这种情况下,似乎没有幸存者的聚类现象(见图 17-5)。

我们使用 Yellowbrick 生成此可视化:

>>> from yellowbrick.features.pca import (
...     PCADecomposition,
... )
>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> colors = ["rg"[j] for j in y]
>>> pca_viz = PCADecomposition(color=colors)
>>> pca_viz.fit_transform(X, y)
>>> pca_viz.poof()
>>> fig.savefig("images/mlpr_1705.png", dpi=300)

Yellowbrick PCA 绘图。

图 17-5. Yellowbrick PCA 绘图。

如果要根据一列着色散点图并添加图例(而不是色条图),则需要在 pandas 或 matplotlib 中循环每个颜色并单独绘制该组(或使用 seaborn)。下面我们还将纵横比设置为我们查看的成分解释方差的比率(见图 17-6)。因为第二个成分仅具有第一个成分的 90%,所以它会稍微短一些。

这是 seaborn 版本:

>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> pca_df = pd.DataFrame(
...     X_pca,
...     columns=[
...         f"PC{i+1}"
...         for i in range(X_pca.shape[1])
...     ],
... )
>>> pca_df["status"] = [
...     ("deceased", "survived")[i] for i in y
... ]
>>> evr = pca.explained_variance_ratio_
>>> ax.set_aspect(evr[1] / evr[0])
>>> sns.scatterplot(
...     x="PC1",
...     y="PC2",
...     hue="status",
...     data=pca_df,
...     alpha=0.5,
...     ax=ax,
... )
>>> fig.savefig(
...     "images/mlpr_1706.png",
...     dpi=300,
...     bbox_inches="tight",
... )

带有图例和相对纵横比的 Seaborn PCA。

图 17-6. 带有图例和相对纵横比的 Seaborn PCA。

下面,我们通过在散点图上显示一个载荷图来增强可视化效果。这种图被称为双标图,因为它包含散点图和载荷(见图 17-7)。载荷显示特征的强度和它们的相关性。如果它们的角度接近,则它们可能相关。如果角度为 90 度,则它们可能不相关。最后,如果它们之间的角度接近 180 度,则它们具有负相关性:

>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> pca_df = pd.DataFrame(
...     X_pca,
...     columns=[
...         f"PC{i+1}"
...         for i in range(X_pca.shape[1])
...     ],
... )
>>> pca_df["status"] = [
...     ("deceased", "survived")[i] for i in y
... ]
>>> evr = pca.explained_variance_ratio_
>>> x_idx = 0  # x_pc
>>> y_idx = 1  # y_pc
>>> ax.set_aspect(evr[y_idx] / evr[x_idx])
>>> x_col = pca_df.columns[x_idx]
>>> y_col = pca_df.columns[y_idx]
>>> sns.scatterplot(
...     x=x_col,
...     y=y_col,
...     hue="status",
...     data=pca_df,
...     alpha=0.5,
...     ax=ax,
... )
>>> scale = 8
>>> comps = pd.DataFrame(
...     pca.components_, columns=X.columns
... )
>>> for idx, s in comps.T.iterrows():
...     plt.arrow(
...         0,
...         0,
...         s[x_idx] * scale,
...         s[y_idx] * scale,
...         color="k",
...     )
...     plt.text(
...         s[x_idx] * scale,
...         s[y_idx] * scale,
...         idx,
...         weight="bold",
...     )
>>> fig.savefig(
...     "images/mlpr_1707.png",
...     dpi=300,
...     bbox_inches="tight",
... )

带有散点图和加载图的 Seaborn 双图绘图。

图 17-7. Seaborn 双图绘图和加载图。

根据先前的树模型,我们知道年龄、票价和性别对乘客是否生存很重要。第一个主成分受 pclass、年龄和票价的影响,而第四个主成分受性别的影响。让我们将这些组件相互绘制。

同样,这个图是根据组件方差比例调整了绘图的纵横比(见图 17-8)。

此图似乎更准确地区分了幸存者:

>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> pca_df = pd.DataFrame(
...     X_pca,
...     columns=[
...         f"PC{i+1}"
...         for i in range(X_pca.shape[1])
...     ],
... )
>>> pca_df["status"] = [
...     ("deceased", "survived")[i] for i in y
... ]
>>> evr = pca.explained_variance_ratio_
>>> ax.set_aspect(evr[3] / evr[0])
>>> sns.scatterplot(
...     x="PC1",
...     y="PC4",
...     hue="status",
...     data=pca_df,
...     alpha=0.5,
...     ax=ax,
... )
>>> fig.savefig(
...     "images/mlpr_1708.png",
...     dpi=300,
...     bbox_inches="tight",
... )

显示组件 1 与 4 之间的 PCA 图。

图 17-8. PCA 组件 1 与 4 的图示。

Matplotlib 可以创建漂亮的图表,但在交互图上不太实用。在执行 PCA 时,查看散点图通常很有用。我已经包含了一个使用 Bokeh 库 的函数,用于与散点图进行交互(见图 17-9)。它在 Jupyter 中运行良好:

>>> from bokeh.io import output_notebook
>>> from bokeh import models, palettes, transform
>>> from bokeh.plotting import figure, show
>>>
>>> def bokeh_scatter(
...     x,
...     y,
...     data,
...     hue=None,
...     label_cols=None,
...     size=None,
...     legend=None,
...     alpha=0.5,
... ):
...     """
...     x - x column name to plot
...     y - y column name to plot
...     data - pandas DataFrame
...     hue - column name to color by (numeric)
...     legend - column name to label by
...     label_cols - columns to use in tooltip
...                  (None all in DataFrame)
...     size - size of points in screen space unigs
...     alpha - transparency
...     """
...     output_notebook()
...     circle_kwargs = {}
...     if legend:
...         circle_kwargs["legend"] = legend
...     if size:
...         circle_kwargs["size"] = size
...     if hue:
...         color_seq = data[hue]
...         mapper = models.LinearColorMapper(
...             palette=palettes.viridis(256),
...             low=min(color_seq),
...             high=max(color_seq),
...         )
...         circle_kwargs[
...             "fill_color"
...         ] = transform.transform(hue, mapper)
...     ds = models.ColumnDataSource(data)
...     if label_cols is None:
...         label_cols = data.columns
...     tool_tips = sorted(
...         [
...             (x, "@{}".format(x))
...             for x in label_cols
...         ],
...         key=lambda tup: tup[0],
...     )
...     hover = models.HoverTool(
...         tooltips=tool_tips
...     )
...     fig = figure(
...         tools=[
...             hover,
...             "pan",
...             "zoom_in",
...             "zoom_out",
...             "reset",
...         ],
...         toolbar_location="below",
...     )
...
...     fig.circle(
...         x,
...         y,
...         source=ds,
...         alpha=alpha,
...         **circle_kwargs
...     )
...     show(fig)
...     return fig
>>> res = bokeh_scatter(
...     "PC1",
...     "PC2",
...     data=pca_df.assign(
...         surv=y.reset_index(drop=True)
...     ),
...     hue="surv",
...     size=10,
...     legend="surv",
... )

带有工具提示的 Bokeh 散点图。

图 17-9. 带有工具提示的 Bokeh 散点图。

Yellowbrick 也可以在三维中绘图(见图 17-10):

>>> from yellowbrick.features.pca import (
...     PCADecomposition,
... )
>>> colors = ["rg"[j] for j in y]
>>> pca3_viz = PCADecomposition(
...     proj_dim=3, color=colors
... )
>>> pca3_viz.fit_transform(X, y)
>>> pca3_viz.finalize()
>>> fig = plt.gcf()
>>> plt.tight_layout()
>>> fig.savefig(
...     "images/mlpr_1710.png",
...     dpi=300,
...     bbox_inches="tight",
... )

Yellowbrick 3D PCA。

图 17-10. Yellowbrick 3D PCA。

scprep 库(这是 PHATE 库的依赖项,我们稍后会讨论)具有一个有用的绘图函数。rotate_scatter3d 函数可以生成一个在 Jupyter 中动画显示的图形(见图 17-11)。这使得理解 3D 图形变得更容易。

您可以使用此库可视化任何 3D 数据,而不仅限于 PHATE:

>>> import scprep
>>> scprep.plot.rotate_scatter3d(
...     X_pca[:, :3],
...     c=y,
...     cmap="Spectral",
...     figsize=(8, 6),
...     label_prefix="Principal Component",
... )

scprep 3D PCA 动画。

图 17-11. scprep 3D PCA 动画。

如果您在 Jupyter 中将 matplotlib 的单元格魔术模式更改为 notebook,您可以从 matplotlib 获取交互式 3D 绘图(见图 17-12)。

>>> from mpl_toolkits.mplot3d import Axes3D
>>> fig = plt.figure(figsize=(6, 4))
>>> ax = fig.add_subplot(111, projection="3d")
>>> ax.scatter(
...     xs=X_pca[:, 0],
...     ys=X_pca[:, 1],
...     zs=X_pca[:, 2],
...     c=y,
...     cmap="viridis",
... )
>>> ax.set_xlabel("PC 1")
>>> ax.set_ylabel("PC 2")
>>> ax.set_zlabel("PC 3")

在笔记本模式下与 Matplotlib 交互的 3D PCA。

图 17-12. Matplotlib 在笔记本模式下的交互式 3D PCA。
警告

请注意,从:

% matplotlib inline

转到:

% matplotlib notebook

有时可能会导致 Jupyter 停止响应。请谨慎使用。

UMAP

统一流形逼近和投影 (UMAP) 是一种使用流形学习的降维技术。因此,它倾向于将相似的项目在拓扑上保持在一起。它试图同时保留全局和局部结构,与偏好局部结构的 t-SNE 相反(详见“t-SNE”)。

Python 实现不支持多核。

特征归一化是将值放在相同尺度上的一个好主意。

UMAP 对超参数(n_neighborsmin_distn_componentsmetric)非常敏感。以下是一些示例:

>>> import umap
>>> u = umap.UMAP(random_state=42)
>>> X_umap = u.fit_transform(
...     StandardScaler().fit_transform(X)
... )
>>> X_umap.shape
(1309, 2)

实例参数:

n_neighbors=15

本地邻域大小。较大意味着使用全局视图,较小意味着更局部。

n_components=2

嵌入的维度数。

metric='euclidean'

用于距离的度量。可以是一个接受两个 1D 数组并返回浮点数的函数。

n_epochs=None

训练时期的数量。默认为 200 或 500(取决于数据大小)。

learning_rate=1.0

嵌入优化的学习率。

init='spectral'

初始化类型。谱嵌入是默认值。可以是'random'或 numpy 数组的位置。

min_dist=0.1

0 到 1 之间。嵌入点之间的最小距离。较小意味着更多聚集,较大意味着分散。

spread=1.0

确定嵌入点的距离。

set_op_mix_ratio=1.0

0 到 1 之间:模糊联合(1)或模糊交集(0)。

local_connectivity=1.0

本地连接的邻居数。增加此值会创建更多的局部连接。

repulsion_strength=1.0

排斥强度。较高的值给负样本更多权重。

negative_sample_rate=5

负样本每个正样本。更高的值具有更多的排斥力,更多的优化成本和更好的准确性。

transform_queue_size=4.0

用于最近邻搜索的侵略性。较高的值是较低的性能但更好的准确性。

a=None

参数来控制嵌入。如果等于None,UMAP 则从min_distspread中确定这些值。

b=None

参数来控制嵌入。如果等于None,UMAP 则从min_distspread中确定这些值。

random_state=None

随机种子。

metric_kwds=None

如果函数用于metric,则用于额外参数的指标字典。也可以使用minkowski(和其他指标)来参数化。

angular_rp_forest=False

使用角度随机投影。

target_n_neighbors=-1

简易设置的邻居数。

target_metric='categorical'

用于使用监督降维。也可以是'L1''L2'。还支持一个接受X中两个数组作为输入并返回它们之间距离值的函数。

target_metric_kwds=None

如果target_metric的函数被使用,使用的指标字典。

target_weight=0.5

权重因子。介于 0.0 和 1.0 之间,其中 0 表示仅基于数据,而 1 表示仅基于目标。

transform_seed=42

变换操作的随机种子。

verbose=False

冗余性。

属性:

embedding_

嵌入结果

让我们可视化泰坦尼克号数据集上 UMAP 的默认结果(参见图 17-13):

>>> fig, ax = plt.subplots(figsize=(8, 4))
>>> pd.DataFrame(X_umap).plot(
...     kind="scatter",
...     x=0,
...     y=1,
...     ax=ax,
...     c=y,
...     alpha=0.2,
...     cmap="Spectral",
... )
>>> fig.savefig("images/mlpr_1713.png", dpi=300)

UMAP 结果。

图 17-13. UMAP 结果。

要调整 UMAP 结果,首先关注n_neighborsmin_dist超参数。以下是更改这些值的示例(见图 17-14 和 17-15):

>>> X_std = StandardScaler().fit_transform(X)
>>> fig, axes = plt.subplots(2, 2, figsize=(6, 4))
>>> axes = axes.reshape(4)
>>> for i, n in enumerate([2, 5, 10, 50]):
...     ax = axes[i]
...     u = umap.UMAP(
...         random_state=42, n_neighbors=n
...     )
...     X_umap = u.fit_transform(X_std)
...
...     pd.DataFrame(X_umap).plot(
...         kind="scatter",
...         x=0,
...         y=1,
...         ax=ax,
...         c=y,
...         cmap="Spectral",
...         alpha=0.5,
...     )
...     ax.set_title(f"nn={n}")
>>> plt.tight_layout()
>>> fig.savefig("images/mlpr_1714.png", dpi=300)

调整 UMAP 结果+n_neighbors+

图 17-14. 调整 UMAP 结果n_neighbors
>>> fig, axes = plt.subplots(2, 2, figsize=(6, 4))
>>> axes = axes.reshape(4)
>>> for i, n in enumerate([0, 0.33, 0.66, 0.99]):
...     ax = axes[i]
...     u = umap.UMAP(random_state=42, min_dist=n)
...     X_umap = u.fit_transform(X_std)
...     pd.DataFrame(X_umap).plot(
...         kind="scatter",
...         x=0,
...         y=1,
...         ax=ax,
...         c=y,
...         cmap="Spectral",
...         alpha=0.5,
...     )
...     ax.set_title(f"min_dist={n}")
>>> plt.tight_layout()
>>> fig.savefig("images/mlpr_1715.png", dpi=300)

调整 UMAP 结果+min_dist+

图 17-15. 调整 UMAP 结果min_dist

有时在 UMAP 之前执行 PCA 以减少维数并加快计算速度。

t-SNE

t-分布随机邻域嵌入(t-SNE)技术是一种可视化和降维技术。它使用输入的分布和低维嵌入,并最小化它们之间的联合概率。由于计算量大,可能无法在大数据集上使用这种技术。

t-SNE 的一个特征是对超参数非常敏感。此外,虽然它能够很好地保留局部聚类,但全局信息并未保留。最后,这不是一个确定性算法,可能不会收敛。

在使用此技术之前标准化数据是一个好主意:

>>> from sklearn.manifold import TSNE
>>> X_std = StandardScaler().fit_transform(X)
>>> ts = TSNE()
>>> X_tsne = ts.fit_transform(X_std)

实例参数:

n_components=2

嵌入的维度数。

perplexity=30.0

建议的取值范围为 5 到 50。较小的数值倾向于形成更紧密的聚类。

early_exaggeration=12.0

控制簇紧密度和它们之间的间距。较大的值意味着较大的间距。

learning_rate=200.0

通常在 10 到 1000 之间。如果数据看起来像一个球,则降低它。如果数据看起来压缩,则增加它。

n_iter=1000

迭代次数。

n_iter_without_progress=300

如果在这些迭代次数之后没有进展,则中止。

min_grad_norm=1e-07

如果梯度范数低于此值,则优化停止。

metric='euclidean'

来自scipy.spatial.distance.pdistpairwise.PAIRWISE_DISTANCE_METRIC或函数的距离度量。

init='random'

嵌入初始化。

verbose=0

冗长性。

random_state=None

随机种子。

method='barnes_hut'

梯度计算算法。

angle=0.5

用于梯度计算。小于 0.2 会增加运行时间。大于 0.8 会增加错误。

属性:

embedding_

嵌入向量。

kl_divergence_

Kullback-Leibler 散度。

n_iter_

迭代次数。

这里展示了使用 matplotlib 进行 t-SNE 结果的可视化(见图 17-16):

>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> colors = ["rg"[j] for j in y]
>>> scat = ax.scatter(
...     X_tsne[:, 0],
...     X_tsne[:, 1],
...     c=colors,
...     alpha=0.5,
... )
>>> ax.set_xlabel("Embedding 1")
>>> ax.set_ylabel("Embedding 2")
>>> fig.savefig("images/mlpr_1716.png", dpi=300)

使用 matplotlib 进行 t-SNE 结果。

图 17-16。使用 matplotlib 进行 t-SNE 结果。

改变perplexity的值可能会对绘图产生重大影响(见图 17-17)。

>>> fig, axes = plt.subplots(2, 2, figsize=(6, 4))
>>> axes = axes.reshape(4)
>>> for i, n in enumerate((2, 30, 50, 100)):
...     ax = axes[i]
...     t = TSNE(random_state=42, perplexity=n)
...     X_tsne = t.fit_transform(X)
...     pd.DataFrame(X_tsne).plot(
...         kind="scatter",
...         x=0,
...         y=1,
...         ax=ax,
...         c=y,
...         cmap="Spectral",
...         alpha=0.5,
...     )
...     ax.set_title(f"perplexity={n}")
... plt.tight_layout()
... fig.savefig("images/mlpr_1717.png", dpi=300)

改变perplexity用于 t-SNE。

图 17-17。改变t-SNEperplexity

PHATE

通过 Affinity-based Trajectory Embedding(PHATE)进行热扩散的潜力是高维数据可视化的工具。它倾向于同时保留全局结构(如 PCA)和局部结构(如 t-SNE)。

PHATE 首先编码局部信息(接近的点应该保持接近)。它使用“扩散”来发现全局数据,然后减少维度:

>>> import phate
>>> p = phate.PHATE(random_state=42)
>>> X_phate = p.fit_transform(X)
>>> X_phate.shape

实例参数:

n_components=2

维度数。

knn=5

核的邻居数。如果嵌入是断开的或数据集大于 10 万个样本,则增加。

decay=40

核的衰减率。降低此值会增加图的连通性。

n_landmark=2000

用于标记的地标点。

t='auto'

扩散力度。对数据进行平滑处理。如果嵌入缺乏结构,请增加;如果结构紧密而紧凑,请减少。

gamma=1

对数潜力(在 -1 到 1 之间)。如果嵌入集中在单个点周围,请尝试将其设置为 0。

n_pca=100

用于邻域计算的主成分数。

knn_dist='euclidean'

KNN 距离度量。

mds_dist='euclidean'

多维缩放(MDS)度量。

mds='metric'

MDS 算法用于降维。

n_jobs=1

要使用的 CPU 数量。

random_state=None

随机种子。

verbose=1

冗余性。

属性(请注意这些后面没有 _):

X

输入数据

embedding

嵌入空间

diff_op

扩散算子

graph

基于输入构建的 KNN 图

这是使用 PHATE 的一个示例(见 Figure 17-18):

>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> phate.plot.scatter2d(p, c=y, ax=ax, alpha=0.5)
>>> fig.savefig("images/mlpr_1718.png", dpi=300)

PHATE 结果。

图 17-18. PHATE 结果。

如上所述的实例参数中,有一些参数可以调整以改变模型的行为。以下是调整 knn 参数的示例(见 Figure 17-19)。请注意,如果使用 .set_params 方法,它将加快计算速度,因为它使用预计算的图和扩散算子:

>>> fig, axes = plt.subplots(2, 2, figsize=(6, 4))
>>> axes = axes.reshape(4)
>>> p = phate.PHATE(random_state=42, n_jobs=-1)

>>> for i, n in enumerate((2, 5, 20, 100)):
...     ax = axes[i]
...     p.set_params(knn=n)
...     X_phate = p.fit_transform(X)
...     pd.DataFrame(X_phate).plot(
...         kind="scatter",
...         x=0,
...         y=1,
...         ax=ax,
...         c=y,
...         cmap="Spectral",
...         alpha=0.5,
...     )
...     ax.set_title(f"knn={n}")
... plt.tight_layout()
... fig.savefig("images/mlpr_1719.png", dpi=300)

更改 PHATE 的 knn 参数。

图 17-19. 更改 PHATE 的 knn 参数。

第十八章:聚类

聚类是一种无监督机器学习技术,用于将群体分成几个组。它是无监督的,因为我们没有给模型任何标签;它只是检查特征并确定哪些样本相似并属于一个簇。在本章中,我们将研究 K 均值和层次聚类方法。我们还将再次使用各种技术探索泰坦尼克号数据集。

K 均值

K 均值算法需要用户选择簇的数量或“k”。然后它随机选择 k 个质心,并根据从质心到每个样本的距离度量将每个样本分配给一个簇。分配完成后,它根据分配给一个标签的每个样本的中心重新计算质心。然后它根据新的质心再次将样本分配到簇中。经过几次迭代后,它应该会收敛。

因为聚类使用距离度量来确定哪些样本相似,所以行为可能会根据数据的规模而变化。您可以标准化数据并使所有特征处于相同的比例。有些人建议,如果规模提示某些特征更重要,SME 可能会建议不要标准化。我们将在这个例子中对数据进行标准化。

在这个例子中,我们将对泰坦尼克号乘客进行聚类。我们将从两个簇开始,看看聚类是否能够分开生存(我们不会将生存数据泄漏到聚类中,只使用X,而不是y)。

无监督算法具有.fit方法和.predict方法。我们只将X传递给.fit

>>> from sklearn.cluster import KMeans
>>> X_std = preprocessing.StandardScaler().fit_transform(
...     X
... )
>>> km = KMeans(2, random_state=42)
>>> km.fit(X_std)
KMeans(algorithm='auto', copy_x=True,
 init='k-means', max_iter=300,
 n_clusters=2, n_init=10, n_jobs=1,
 precompute_distances='auto',
 random_state=42, tol=0.0001, verbose=0)

在模型训练后,我们可以调用.predict方法将新样本分配给一个簇:

>>> X_km = km.predict(X)
>>> X_km
array([1, 1, 1, ..., 1, 1, 1], dtype=int32)

实例参数:

n_clusters=8

创建的簇的数量。

init='kmeans++'

初始化方法。

n_init=10

使用不同质心运行算法的次数。最佳得分将获胜。

max_iter=300

运行的迭代次数。

tol=0.0001

收敛的公差。

precompute_distances='auto'

预计算距离(需要更多内存但更快)。如果n_samples * n_clusters小于或等于 1200 万,auto将预先计算。

verbose=0

冗余性。

random_state=None

随机种子。

copy_x=True

在计算之前复制数据。

n_jobs=1

要使用的 CPU 数量。

algorithm='auto'

K 均值算法。'full'适用于稀疏数据,但'elkan'更高效。'auto'在密集数据中使用'elkan'

属性:

cluster_centers_

中心的坐标

labels_

样本的标签

inertia_

到聚类质心的平方距离之和

n_iter_

迭代次数

如果事先不知道需要多少个簇,可以以一系列大小运行算法并评估各种指标。这可能有些棘手。

您可以使用 .inertia_ 计算自己的肘部图。寻找曲线弯曲的位置,因为那可能是选择聚类数量的一个良好选择。在这种情况下,曲线很平滑,但在八个之后似乎没有太多改善(见 Figure 18-1)。

对于没有肘部的图,我们有几个选择。我们可以使用其他指标,其中一些如下所示。我们还可以检查聚类的可视化,看看聚类是否可见。我们可以向数据添加特征,看看是否有帮助进行聚类。

这里是肘部图的代码:

>>> inertias = []
>>> sizes = range(2, 12)
>>> for k in sizes:
...     k2 = KMeans(random_state=42, n_clusters=k)
...     k2.fit(X)
...     inertias.append(k2.inertia_)
>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> pd.Series(inertias, index=sizes).plot(ax=ax)
>>> ax.set_xlabel("K")
>>> ax.set_ylabel("Inertia")
>>> fig.savefig("images/mlpr_1801.png", dpi=300)

看起来相当平滑的肘部图。

图 18-1. 看起来相当平滑的肘部图。

当地面真实标签未知时,Scikit-learn 具有其他聚类指标。我们也可以计算并绘制这些指标。轮廓系数 是介于 -1 和 1 之间的值。得分越高越好。1 表示紧密的聚类,0 表示重叠的聚类。根据这个度量,两个聚类给我们带来了最佳分数。

Calinski-Harabasz 指数 是介于类间离散度和类内离散度之间的比率。分数越高越好。对于这个指标,两个聚类给出了最佳分数。

Davis-Bouldin 指数 是每个聚类与最接近的聚类之间相似性的平均值。分数从 0 开始。0 表示更好的聚类。

在这里,我们将绘制惯性、轮廓系数、Calinski-Harabasz 指数和 Davies-Bouldin 指数在一系列聚类大小上的情况,以查看数据是否有明确的聚类大小(见 Figure 18-2)。大多数这些指标都同意选择两个聚类:

>>> from sklearn import metrics
>>> inertias = []
>>> sils = []
>>> chs = []
>>> dbs = []
>>> sizes = range(2, 12)
>>> for k in sizes:
...     k2 = KMeans(random_state=42, n_clusters=k)
...     k2.fit(X_std)
...     inertias.append(k2.inertia_)
...     sils.append(
...         metrics.silhouette_score(X, k2.labels_)
...     )
...     chs.append(
...         metrics.calinski_harabasz_score(
...             X, k2.labels_
...         )
...     )
...     dbs.append(
...         metrics.davies_bouldin_score(
...             X, k2.labels_
...         )
...     )
>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> (
...     pd.DataFrame(
...         {
...             "inertia": inertias,
...             "silhouette": sils,
...             "calinski": chs,
...             "davis": dbs,
...             "k": sizes,
...         }
...     )
...     .set_index("k")
...     .plot(ax=ax, subplots=True, layout=(2, 2))
... )
>>> fig.savefig("images/mlpr_1802.png", dpi=300)

聚类指标。这些指标大多数同意选择两个聚类。

图 18-2. 聚类指标。这些指标大多数同意选择两个聚类。

另一种确定聚类的技术是可视化每个聚类的轮廓分数。Yellowbrick 有一个此类的可视化工具(见 Figure 18-3)。

在这个图中,垂直的虚线是平均分数。一种解释方法是确保每个聚类都突出于平均水平之上,并且聚类分数看起来还不错。确保使用相同的 x 轴限制 (ax.set_xlim)。我会从这些图中选择两个聚类:

>>> from yellowbrick.cluster.silhouette import (
...     SilhouetteVisualizer,
... )
>>> fig, axes = plt.subplots(2, 2, figsize=(12, 8))
>>> axes = axes.reshape(4)
>>> for i, k in enumerate(range(2, 6)):
...     ax = axes[i]
...     sil = SilhouetteVisualizer(
...         KMeans(n_clusters=k, random_state=42),
...         ax=ax,
...     )
...     sil.fit(X_std)
...     sil.finalize()
...     ax.set_xlim(-0.2, 0.8)
>>> plt.tight_layout()
>>> fig.savefig("images/mlpr_1803.png", dpi=300)

Yellowbrick 轮廓可视化器

图 18-3. Yellowbrick 轮廓可视化器

聚合(层次)聚类

聚合聚类是另一种方法。你从每个样本单独的聚类开始。然后将“最近的”聚类组合起来。重复此过程,同时跟踪最近的大小。

当您完成这些操作时,您将得到一棵dendrogram,或者一棵跟踪聚类创建时间和距离度量的树。您可以使用 scipy 库可视化这棵树。

我们可以使用 scipy 创建一棵树状图(见 Figure 18-4)。如您所见,如果样本很多,则叶节点很难阅读:

>>> from scipy.cluster import hierarchy
>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> dend = hierarchy.dendrogram(
...     hierarchy.linkage(X_std, method="ward")
... )
>>> fig.savefig("images/mlpr_1804.png", dpi=300)

Scipy 层次聚类树状图

图 18-4. Scipy 层次聚类树状图

一旦您有了树状图,您就拥有了所有的聚类(从样本数为一到样本数为止)。高度表示加入时相似聚类的相似程度。为了找出数据中有多少个聚类,您需要在最高的线交叉处“切割”一条水平线。

在这种情况下,当您执行该切割时,看起来有三个聚类。

前一个图表太嘈杂了,其中包含了所有的样本。您还可以使用truncate_mode参数将叶子节点合并为单个节点(参见图 18-5):

>>> from scipy.cluster import hierarchy
>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> dend = hierarchy.dendrogram(
...     hierarchy.linkage(X_std, method="ward"),
...     truncate_mode="lastp",
...     p=20,
...     show_contracted=True,
... )
>>> fig.savefig("images/mlpr_1805.png", dpi=300)

截断的层次聚类树状图。如果我们在最大的垂直线上切割,我们将得到三个聚类。

图 18-5. 截断的层次聚类树状图。如果我们在最大的垂直线上切割,我们将得到三个聚类。

一旦我们知道需要多少个聚类,我们可以使用 scikit-learn 创建一个模型:

>>> from sklearn.cluster import (
...     AgglomerativeClustering,
... )
>>> ag = AgglomerativeClustering(
...     n_clusters=4,
...     affinity="euclidean",
...     linkage="ward",
... )
>>> ag.fit(X)
注意

fastcluster 包 提供了一个优化的凝聚聚类包,如果 scikit-learn 的实现速度太慢的话。

理解聚类

在 Titanic 数据集上使用 K-means,我们将得到两个聚类。我们可以使用 pandas 中的分组功能来检查聚类之间的差异。下面的代码检查每个特征的均值和方差。看起来 pclass 的均值变化相当大。

我将生存数据重新放回,看看聚类是否与此相关:

>>> km = KMeans(n_clusters=2)
>>> km.fit(X_std)
>>> labels = km.predict(X_std)
>>> (
...     X.assign(cluster=labels, survived=y)
...     .groupby("cluster")
...     .agg(["mean", "var"])
...     .T
... )
cluster                 0         1
pclass     mean  0.526538 -1.423831
 var   0.266089  0.136175
age        mean -0.280471  0.921668
 var   0.653027  1.145303
sibsp      mean -0.010464 -0.107849
 var   1.163848  0.303881
parch      mean  0.387540  0.378453
 var   0.829570  0.540587
fare       mean -0.349335  0.886400
 var   0.056321  2.225399
sex_male   mean  0.678986  0.552486
 var   0.218194  0.247930
embarked_Q mean  0.123548  0.016575
 var   0.108398  0.016345
embarked_S mean  0.741288  0.585635
 var   0.191983  0.243339
survived   mean  0.596685  0.299894
 var   0.241319  0.210180
注意

在 Jupyter 中,您可以将以下代码添加到 DataFrame 中,并突出显示每行的高低值。这对于直观地查看哪些值在上述聚类摘要中显著是有用的:

.style.background_gradient(cmap='RdBu', axis=1)

在图 18-6 中,我们绘制了每个聚类的均值条形图:

>>> fig, ax = plt.subplots(figsize=(6, 4))
... (
...     X.assign(cluster=labels, survived=y)
...     .groupby("cluster")
...     .mean()
...     .T.plot.bar(ax=ax)
... )
>>> fig.savefig(
...     "images/mlpr_1806.png",
...     dpi=300,
...     bbox_inches="tight",
... )

每个聚类的均值

图 18-6. 每个聚类的均值

我还喜欢绘制 PCA 组件,但是按聚类标签着色(见图 18-7)。在这里,我们使用 Seaborn 来执行此操作。将hue的值更改为深入研究聚类中显著的特征也很有趣。

>>> fig, ax = plt.subplots(figsize=(6, 4))
>>> sns.scatterplot(
...     "PC1",
...     "PC2",
...     data=X.assign(
...         PC1=X_pca[:, 0],
...         PC2=X_pca[:, 1],
...         cluster=labels,
...     ),
...     hue="cluster",
...     alpha=0.5,
...     ax=ax,
... )
>>> fig.savefig(
...     "images/mlpr_1807.png",
...     dpi=300,
...     bbox_inches="tight",
... )

聚类的 PCA 图

图 18-7. 聚类的 PCA 图

如果我们想检查单个特征,可以使用 pandas 的.describe方法:

>>> (
...     X.assign(cluster=label)
...     .groupby("cluster")
...     .age.describe()
...     .T
... )
cluster           0           1
count    362.000000  947.000000
mean       0.921668   -0.280471
std        1.070188    0.808101
min       -2.160126   -2.218578
25%        0.184415   -0.672870
50%        0.867467   -0.283195
75%        1.665179    0.106480
max        4.003228    3.535618

我们还可以创建一个替代模型来解释这些聚类。在这里,我们使用决策树来解释它们。这还显示了 pclass(其均值差异很大)非常重要:

>>> dt = tree.DecisionTreeClassifier()
>>> dt.fit(X, labels)
>>> for col, val in sorted(
...     zip(X.columns, dt.feature_importances_),
...     key=lambda col_val: col_val[1],
...     reverse=True,
... ):
...     print(f"{col:10}{val:10.3f}")
pclass         0.902
age            0.074
sex_male       0.016
embarked_S     0.003
fare           0.003
parch          0.003
sibsp          0.000
embarked_Q     0.000

我们可以通过图 18-8 来可视化决策。它显示 pclass 是第一个特征,用于做出决策:

>>> dot_data = StringIO()
>>> tree.export_graphviz(
...     dt,
...     out_file=dot_data,
...     feature_names=X.columns,
...     class_names=["0", "1"],
...     max_depth=2,
...     filled=True,
... )
>>> g = pydotplus.graph_from_dot_data(
...     dot_data.getvalue()
... )
>>> g.write_png("images/mlpr_1808.png")

解释聚类的决策树

图 18-8. 解释聚类的决策树

第十九章:管道

Scikit-learn 使用管道的概念。使用 Pipeline 类,您可以将转换器和模型链在一起,并像对待整个 scikit-learn 模型一样对待整个过程。甚至可以插入自定义逻辑。

分类管道

这是在管道内使用 tweak_titanic 函数的示例:

>>> from sklearn.base import (
...     BaseEstimator,
...     TransformerMixin,
... )
>>> from sklearn.pipeline import Pipeline

>>> def tweak_titanic(df):
...     df = df.drop(
...         columns=[
...             "name",
...             "ticket",
...             "home.dest",
...             "boat",
...             "body",
...             "cabin",
...         ]
...     ).pipe(pd.get_dummies, drop_first=True)
...     return df

>>> class TitanicTransformer(
...     BaseEstimator, TransformerMixin
... ):
...     def transform(self, X):
...         # assumes X is output
...         # from reading Excel file
...         X = tweak_titanic(X)
...         X = X.drop(column="survived")
...         return X
...
...     def fit(self, X, y):
...         return self

>>> pipe = Pipeline(
...     [
...         ("titan", TitanicTransformer()),
...         ("impute", impute.IterativeImputer()),
...         (
...             "std",
...             preprocessing.StandardScaler(),
...         ),
...         ("rf", RandomForestClassifier()),
...     ]
... )

有了管道,我们可以对其调用 .fit.score

>>> from sklearn.model_selection import (
...     train_test_split,
... )
>>> X_train2, X_test2, y_train2, y_test2 = train_test_split(
...     orig_df,
...     orig_df.survived,
...     test_size=0.3,
...     random_state=42,
... )

>>> pipe.fit(X_train2, y_train2)
>>> pipe.score(X_test2, y_test2)
0.7913486005089059

管道可以在网格搜索中使用。我们的 param_grid 需要将参数前缀设为管道阶段的名称,后跟两个下划线。在下面的示例中,我们为随机森林阶段添加了一些参数:

>>> params = {
...     "rf__max_features": [0.4, "auto"],
...     "rf__n_estimators": [15, 200],
... }

>>> grid = model_selection.GridSearchCV(
...     pipe, cv=3, param_grid=params
... )
>>> grid.fit(orig_df, orig_df.survived)

现在我们可以提取最佳参数并训练最终模型。(在这种情况下,随机森林在网格搜索后没有改善。)

>>> grid.best_params_
{'rf__max_features': 0.4, 'rf__n_estimators': 15}
>>> pipe.set_params(**grid.best_params_)
>>> pipe.fit(X_train2, y_train2)
>>> pipe.score(X_test2, y_test2)
0.7913486005089059

我们可以在使用 scikit-learn 模型的管道中使用:

>>> metrics.roc_auc_score(
...     y_test2, pipe.predict(X_test2)
... )
0.7813688715131023

回归管道

这是在波士顿数据集上执行线性回归的管道示例:

>>> from sklearn.pipeline import Pipeline

>>> reg_pipe = Pipeline(
...     [
...         (
...             "std",
...             preprocessing.StandardScaler(),
...         ),
...         ("lr", LinearRegression()),
...     ]
... )
>>> reg_pipe.fit(bos_X_train, bos_y_train)
>>> reg_pipe.score(bos_X_test, bos_y_test)
0.7112260057484934

如果我们想要从管道中提取部分来检查它们的属性,我们可以使用 .named_steps 属性进行操作:

>>> reg_pipe.named_steps["lr"].intercept_
23.01581920903956
>>> reg_pipe.named_steps["lr"].coef_
array([-1.10834602,  0.80843998,  0.34313466,
 0.81386426, -1.79804295,  2.913858  ,
 -0.29893918, -2.94251148,  2.09419303,
 -1.44706731, -2.05232232,  1.02375187,
 -3.88579002])_

我们也可以在度量计算中使用管道:

>>> from sklearn import metrics
>>> metrics.mean_squared_error(
...     bos_y_test, reg_pipe.predict(bos_X_test)
... )
21.517444231177205

PCA 管道

Scikit-learn 管道也可以用于 PCA。

这里我们对泰坦尼克号数据集进行标准化并对其执行 PCA:

>>> pca_pipe = Pipeline(
...     [
...         (
...             "std",
...             preprocessing.StandardScaler(),
...         ),
...         ("pca", PCA()),
...     ]
... )
>>> X_pca = pca_pipe.fit_transform(X)

使用 .named_steps 属性,我们可以从管道的 PCA 部分提取属性:

>>> pca_pipe.named_steps[
...     "pca"
... ].explained_variance_ratio_
array([0.23917891, 0.21623078, 0.19265028,
 0.10460882, 0.08170342, 0.07229959,
 0.05133752, 0.04199068])
>>> pca_pipe.named_steps["pca"].components_[0]
array([-0.63368693,  0.39682566,  0.00614498,
 0.11488415,  0.58075352, -0.19046812,
 -0.21190808, -0.09631388])