随着你的机器学习项目发展,将数据加载、预处理、模型训练和评估步骤串联起来可能会变得繁琐。手动管理每个阶段容易出错,并且难以稳定重现结果。MLJ.jl 提供了一种使用管道定义和管理这些序列的方式,有效地将复杂的流程变成一个单一、易于管理的对象。MLJ.jl 中构建管道的核心是 @pipeline 宏。这个强大工具允许你定义一系列操作,甚至是更复杂的操作图,最终形成一个模型。让我们看看如何构建这些管道,从更简单的线性序列开始,然后转向处理多样化数据类型的更复杂配置。线性管道:一个简单序列最直接的管道包含一个线性步骤序列。例如,你可能希望标准化你的特征,然后将它们输入到分类模型中。如果你的所有特征都是数值型的并需要相同的标准化,那么管道就很简单。设想一个数据集 X(特征)和 y(目标),其中 X 仅包含连续数值特征。我们可以构建一个管道,首先标准化 X,然后训练一个 K 近邻分类器。using MLJ using DataFrames using Random # 为了重现性 # 加载所需模型类型 Standardizer = @load Standardizer pkg=MLJModels KNNClassifier = @load KNNClassifier pkg=MLJModels # 生成一些样本数值数据 Random.seed!(42) X_numeric = DataFrame(A = rand(10), B = rand(10), C = rand(10)) y_target = coerce(rand(["Class1", "Class2"], 10), Multiclass) # 构建线性管道 linear_pipe = @pipeline(Standardizer, KNNClassifier(K=3)) # 该管道现在是一个复合模型。你可以像训练其他任何MLJ模型一样训练它: mach_linear = machine(linear_pipe, X_numeric, y_target) fit!(mach_linear, verbosity=0) # 并进行预测: y_pred_linear = predict(mach_linear, X_numeric) # info(y_pred_linear[1]) # 查看预测类型在这个 linear_pipe 中,数据从 X_numeric 流向 Standardizer。Standardizer 的输出(缩放后的数据)随后成为 KNNClassifier 的输入。KNNClassifier 使用这些缩放后的数据和原始 y_target 进行训练。这是一个整洁、封装的流程。异构数据管道:组合预处理步骤数据集通常包含混合的特征类型,例如数值列和类别列。每种类型可能需要不同的预处理。例如,数值特征通常受益于缩放(如标准化),而类别特征通常需要进行独热编码。MLJ 的 @pipeline 宏优雅地处理这种常见情况。当你将多个转换器(无监督模型)列在一个最终的监督模型之前时,MLJ 会智能地将每个转换器应用于输入数据中对应的部分(基于它们的科学类型,即 scitypes),然后拼接它们的输出,再将它们输入到监督模型。让我们用经典的鸢尾花数据集来演示这一点,该数据集已修改以包含一个类别特征。import RDatasets # 准备鸢尾花数据 iris = RDatasets.dataset("datasets", "iris") X_iris = select(iris, Not(:Species)) # 特征 y_iris = iris.Species # 目标 # 为了演示,将一个特征设为类别型 Random.seed!(123) # 为了重现性 X_iris.PetalType = coerce(rand(["Short", "Medium", "Long"], nrow(X_iris)), Multiclass) # 原始特征(如萼片长度、花瓣长度)是连续的。 # PetalType 现在是多类别型。 # 确保目标也是多类别型 y_iris_cat = coerce(y_iris, Multiclass) # 构建异构数据管道 # Standardizer 将作用于连续特征。 # OneHotEncoder 将作用于多类别(有限)特征。 # 它们的输出在发送给 KNNClassifier 之前会自动拼接。 hetero_pipe = @pipeline(Standardizer, OneHotEncoder, KNNClassifier(K=5)) # 训练此管道 mach_hetero = machine(hetero_pipe, X_iris, y_iris_cat) fit!(mach_hetero, verbosity=0) # 进行预测 y_pred_hetero = predict(mach_hetero, X_iris) # first(y_pred_hetero, 5) # 显示前5个预测在 hetero_pipe 中:Standardizer 仅使用 X_iris 中的 Continuous 特征(例如 SepalLength、SepalWidth、PetalLength、PetalWidth)进行拟合。然后它转换这些特征。OneHotEncoder 仅使用 原始 X_iris 中的 Multiclass(或更广义地讲,Finite)特征(即 PetalType)进行拟合。它将此特征转换为数值表示。Standardizer 的输出(缩放后的数值特征)和 OneHotEncoder 的输出(独热编码特征)会自动按列拼接(hcat)。这个合并的、完全数值化的数据集随后与 y_iris_cat 一起用于训练 KNNClassifier。对特征子集和拼接的这种自动处理带来了很大便利。其流程可如下所示:digraph G { rankdir=TB; node [style="filled", shape=box, fontname="sans-serif"]; X [label="X (特征)", fillcolor="#adb5bd"]; y [label="y (目标)", fillcolor="#adb5bd"]; Std [label="Standardizer", fillcolor="#74c0fc"]; OHE [label="OneHotEncoder", fillcolor="#74c0fc"]; Model [label="KNNClassifier", fillcolor="#74c0fc"]; X_std [label="X_标准化\n(来自连续特征)", fillcolor="#ffe066"]; X_ohe [label="X_独热编码\n(来自多类别特征)", fillcolor="#ffe066"]; X_transformed [label="X_拼接", fillcolor="#ffe066"]; y_hat [label="ŷ (预测)", fillcolor="#ffe066"]; Hcat [label="hcat (拼接)", fillcolor="#69db7c", shape=oval]; X -> Std [label=" 连续\n 特征"]; X -> OHE [label=" 多类别\n 特征"]; Std -> X_std; OHE -> X_ohe; X_std -> Hcat; X_ohe -> Hcat; Hcat -> X_transformed; X_transformed -> Model; y -> Model; Model -> y_hat; subgraph cluster_input { label="输入数据"; style="filled"; color="#e9ecef"; X; y; } subgraph cluster_preprocessing { label="预处理阶段"; style="filled"; color="#e9ecef"; Std; OHE; Hcat; X_std; X_ohe; } subgraph cluster_model { label="模型训练与预测"; style="filled"; color="#e9ecef"; Model; y_hat; X_transformed; } }该图显示了输入特征 X 如何由 Standardizer(针对连续特征)和 OneHotEncoder(针对多类别特征)并行处理。它们的输出被拼接(hcat)以形成 X_transformed,然后与目标 y 一起用于训练 KNNClassifier 并生成预测 ŷ。这种在单一管道结构内为不同特征类型定义预处理的能力,对于保持机器学习代码的整洁和可重现性非常有用。拟合和使用管道一旦定义了像 linear_pipe 或 hetero_pipe 这样的管道,它就像任何其他 MLJ 模型一样运行。你可以通过将管道与数据绑定来创建一个 machine: mach = machine(my_pipeline, X, y)然后,你 fit! 该 machine: fit!(mach, verbosity=0)这个 fit! 调用将按正确顺序训练管道的所有组件。对于 hetero_pipe,它首先在 X 的数值部分上拟合 Standardizer,然后在 X 的类别部分上拟合 OneHotEncoder。它随后使用这些拟合好的转换器转换 X,拼接结果,最后在这个处理过的数据和 y 上拟合 KNNClassifier。预测照常进行: y_predictions = predict(mach, X_new) 当调用 predict 时,X_new 会经历相同的拟合转换(对其数值部分进行标准化,对其类别部分进行独热编码,然后拼接),然后传递给已拟合的 KNNClassifier 以生成预测。你还可以检查管道 machine 的 fitted_params,以查看每个组件的学到参数: fp = fitted_params(mach_hetero) fp.standardizer 将包含均值和标准差,fp.one_hot_encoder 包含编码的详细信息,fp.k_n_n_classifier 则包含训练好的 KNN 模型的状态。以这种方式构建管道确保了你的整个工作流程,从原始特征到模型预测,都被封装。这简化了模型部署、调优(因为你可以调优管道组件的超参数),并确保在训练、评估和预测期间一致地应用相同的预处理步骤。虽然我们专注于常见的 @pipeline(Transformer1, ..., TransformerN, Model) 结构,但 MLJ 的学习网络为自定义管道架构提供了更大的灵活性,随着你的需求变得更加具体,你可以学习更多。