共用方式為


如何使用 ML.NET 自動化機器學習 (AutoML) API

在本文中,您將了解如何使用 ML.NET 自動化 ML (AutoML API)。

您可以在 dotnet/machinelearning-samples 存放庫中找到 AutoML API 的範例。

安裝

若要使用 AutoML API,請在您要參考的 .NET 專案中安裝 Microsoft.ML.AutoML NuGet 封裝。

注意

本指南使用 Microsoft.ML.AutoML NuGet 封裝的 0.20.0 版和更新版本。 雖然舊版的範例和程式碼仍可運作,但強烈建議您針對新專案使用此版本中引進的 API。

如需安裝 NuGet 封裝的詳細資訊,請參閱下列指南:

快速入門

AutoML 提供數個預設值來快速定型機器學習模型。 在本章節中,您將了解如何:

  • 載入您的資料
  • 定義管線
  • 設定您的實驗
  • 執行您的實驗
  • 使用最佳模型進行預測

定義您的問題

假設儲存在逗號分隔檔案中名為「taxi-fare-train.csv」的資料集,如下所示:

vendor_id rate_code passenger_count trip_time_in_secs trip_distance payment_type fare_amount
CMT 1 1 1271 3.8 CRD 17.5
CMT 1 1 474 1.5 CRD 8
CMT 1 1 637 1.4 CRD 8.5

載入您的資料

首先,初始化您的 MLContextMLContext 是所有 ML.NET 作業的起點。 初始化 mlContext 會建立新的 ML.NET 環境,可在模型建立工作流程物件間共用。 就概念而言,類似於 Entity Framework 中的 DBContext

然後,若要載入您的資料,請使用 InferColumns 方法。

// Initialize MLContext
MLContext ctx = new MLContext();

// Define data path
var dataPath = Path.GetFullPath(@"..\..\..\..\Data\taxi-fare-train.csv");

// Infer column information
ColumnInferenceResults columnInference =
    ctx.Auto().InferColumns(dataPath, labelColumnName: "fare_amount", groupColumns: false);

InferColumns 會從資料集載入幾個資料列。 然後其會檢查資料,並根據資料行的內容,嘗試猜測或推斷每個資料行的資料類型。

預設行為是將相同類型的資料行分組為特徵向量或陣列,並在其中包含每個個別資料行的元素。 將 groupColumns 設定為會 false 覆寫該預設行為,且只會執行資料行推斷而不分組資料行。 其可將資料行分開,讓您在個別資料行層級預先處理資料、而不是在資料行群組時,套用不同的資料轉換。

InferColumns 的結果是 ColumnInferenceResults 物件,其中包含建立 TextLoader 所需的選項以及資料行資訊。

針對「taxi-fare-train.csv」中的範例資料集,資料行資訊可能如下所示:

  • LabelColumnName:fare_amount
  • CategoricalColumnNames:vendor_id、payment_type
  • NumericColumnNames:rate_code、passenger_count、trip_time_in_secs、trip_distance

一旦您擁有資料行資訊後,請使用 ColumnInferenceResults 所定義的 TextLoader.Options 來建立 TextLoader,以將資料載入 IDataView

// Create text loader
TextLoader loader = ctx.Data.CreateTextLoader(columnInference.TextLoaderOptions);

// Load data into IDataView
IDataView data = loader.Load(dataPath);

將資料分割成定型和驗證集通常是不錯的做法。 使用 TrainTestSplit 建立 80% 定型和 20% 驗證的資料集分割。

TrainTestData trainValidationData = ctx.Data.TrainTestSplit(data, testFraction: 0.2);

定義管線

您的管線會定義資料處理步驟和機器學習管線,以用於定型模型。

SweepablePipeline pipeline =
    ctx.Auto().Featurizer(data, columnInformation: columnInference.ColumnInformation)
        .Append(ctx.Auto().Regression(labelColumnName: columnInference.ColumnInformation.LabelColumnName));

SweepablePipelineSweepableEstimator 的集合。 SweepableEstimator 是具有 SearchSpace 的 ML.NET Estimator

Featurizer 是一種便利 API,可根據您提供的資料行資訊,建置資料處理可掃掠估算器的可掃掠管線。 不必從頭開始建置管線,Featurizer 可自動執行資料前置處理步驟。 如需 ML.NET 所支援轉換的詳細資訊,請參閱資料轉換指南

Featurizer 輸出是單一資料行,其中包含數值特徵向量,代表每個資料行的轉換資料。 接著,這個特徵向量會做為用來定型機器學習模型的演算法輸入。

如果您想要更精細控制資料前置處理,可以使用每個個別前置處理步驟來建立管線。 如需詳細資訊,請參閱準備用於建置模型指南的資料

提示

使用 Featurizer 搭配 ColumnInferenceResults 來最大化 AutoML 的公用程式。

針對定型,AutoML 可為下列機器學習工作提供具有預設定型器和搜尋空間設定的可掃掠管線:

針對計程車車資預測問題,因為目標是預測數值,請使用 Regression。 如需選擇工作的詳細資訊,請參閱 ML.NET 中的機器學習工作

設定您的實驗

首先,建立 AutoML 實驗。 AutoMLExperimentTrialResult 的集合。

AutoMLExperiment experiment = ctx.Auto().CreateExperiment();

建立實驗之後,請使用其所提供的擴充方法來進行不同的設定。

experiment
    .SetPipeline(pipeline)
    .SetRegressionMetric(RegressionMetric.RSquared, labelColumn: columnInference.ColumnInformation.LabelColumnName)
    .SetTrainingTimeInSeconds(60)
    .SetDataset(trainValidationData);

在此範例中,您會:

  • 呼叫 SetPipeline,將可掃掠管線設定為在實驗期間執行。
  • 呼叫 SetRegressionMetric,選擇 RSquared 做為要在定型期間進行最佳化的計量。 如需評估計量的詳細資訊,請參閱使用計量評估 ML.NET 模型指南。
  • 呼叫 SetTrainingTimeInSeconds,將 60 秒設定為您想要定型的時間量。 判斷定型需多少時間是資料大小的絕佳啟發學習法。 一般而言,較大的資料集需要較長的定型時間。 如需詳細資訊,請參閱定型時間指引
  • 呼叫 SetDataset,提供所要使用的定型和驗證資料集。

定義實驗之後,您需要一些方法來追蹤其進度。 追蹤進度最快的方式是從 MLContext 修改 Log 事件。

// Log experiment trials
ctx.Log += (_, e) => {
    if (e.Source.Equals("AutoMLExperiment"))
    {
        Console.WriteLine(e.RawMessage);
    }
};

執行您的實驗

既然您已定義實驗,請使用 RunAsync 方法來啟動實驗。

TrialResult experimentResults = await experiment.RunAsync();

定型時間到期之後,結果就是在定型期間找到的 TrialResult 最佳模型。

此時,您可以儲存模型,或使用模型來進行預測。 如需如何使用 ML.NET 模型的詳細資訊,請參閱下列指南:

修改資料行推斷結果

因為 InferColumns 只會載入資料的子集,所以可能並不會攔截用來推斷資料行的範例以外所包含的邊緣案例,而且會為您的資料行設定錯誤的資料類型。 您可以更新 ColumnInformation 的屬性,以考慮資料行推斷結果不正確的情況。

例如,在計程車車資資料集中,rate_code 資料行中的資料是數字。 不過,該數值代表類別。 根據預設,呼叫 InferColumns 會放在 rate_code 屬性中, NumericColumnNames 而不是放在 CategoricalColumnNames 屬性中。 因為這些屬性是 .NET 集合,所以您可以使用標準作業來新增及移除這些項目。

您可以執行下列動作來更新 rate_codeColumnInformation

columnInference.ColumnInformation.NumericColumnNames.Remove("rate_code");
columnInference.ColumnInformation.CategoricalColumnNames.Add("rate_code");

排除定型器

根據預設,AutoML 會嘗試多個定型器做為定型程序的一部分,以查看哪一個最適合您的資料。 不過,在整個定型程序中,您可能會發現有一些定型器使用太多計算資源,或並未提供良好的評估計量。 您可以選擇從定型程序中排除定型器。 要使用哪一個定型器取決於工作。 如需 ML.NET 中所支援的定型器清單,請參閱 ML.NET 中的機器學習工作指南

例如,在計程車車資回歸案例中,若要排除 LightGBM 演算法,請將 useLgbm 參數設定為 false

ctx.Auto().Regression(labelColumnName: columnInference.ColumnInformation.LabelColumnName, useLgbm:false)

其他工作中的定型器排除程序 (例如二元和多元分類) 運作方式相同。

自訂可掃掠估算器

當您需要更細微自訂包含在可掃掠管線中的估算器選項時,您需要:

  1. 初始化搜尋空間
  2. 使用搜尋空間來定義自訂處理站
  3. 建立可掃掠估算器
  4. 將可掃掠估算器新增至可掃掠管線

AutoML 為下列機器學習工作中的定型器提供一組預先設定的搜尋空間:

在此範例中,所使用的搜尋空間是針對 SdcaRegressionTrainer。 使用 SdcaOption 將其初始化。

var sdcaSearchSpace = new SearchSpace<SdcaOption>();

然後,使用搜尋空間定義自訂 Factory 方法來建立 SdcaRegressionTrainer。 在此範例中,L1RegularizationL2Regularization 的值都設定為預設值以外的值。 針對 L1Regularization,設定的值是由每個試用版期間的微調程式所決定。 L2Regularization 會固定為每個試用版的硬式編碼值。 在每次試用期間,自訂處理站的輸出都是 SdcaRegressionTrainer,並且具有已設定的超參數。

// Use the search space to define a custom factory to create an SdcaRegressionTrainer
var sdcaFactory = (MLContext ctx, SdcaOption param) =>
{
    var sdcaOption = new SdcaRegressionTrainer.Options();
    sdcaOption.L1Regularization = param.L1Regularization;
    sdcaOption.L2Regularization = 0.02f;

    sdcaOption.LabelColumnName = columnInference.ColumnInformation.LabelColumnName;

    return ctx.Regression.Trainers.Sdca(sdcaOption);
};

可掃掠估算器是估算器和搜尋空間的組合。 既然您已定義搜尋空間,並用該空間來建立產生定型器的自訂 Factory 方法,請使用 CreateSweepableEstimator 方法來建立新的可掃掠估算器。

// Define Sdca sweepable estimator (SdcaRegressionTrainer + SdcaOption search space)
var sdcaSweepableEstimator = ctx.Auto().CreateSweepableEstimator(sdcaFactory, sdcaSearchSpace);

若要在實驗中使用可掃掠估算器,請將其新增至可掃掠管線。

SweepablePipeline pipeline =
    ctx.Auto().Featurizer(data, columnInformation: columnInference.ColumnInformation)
        .Append(sdcaSweepableEstimator);

由於可掃掠管線是可掃掠估算器的集合,因此您可以視需要設定及自訂這些可掃掠估算器。

自訂您的搜尋空間

在某些情況下,您不只需要自訂實驗中所使用的可掃掠估算器,還需要控制搜尋空間範圍。 您可以使用索引鍵來存取搜尋空間屬性以執行此動作。 在此情況下,L1Regularization 參數為 float。 因此,若要自訂搜尋範圍,請使用 UniformSingleOption

sdcaSearchSpace["L1Regularization"] = new UniformSingleOption(min: 0.01f, max: 2.0f, logBase: false, defaultValue: 0.01f);

根據您所要設定超參數的資料類型,您可以從下列選項中選擇:

搜尋空間也可以包含巢狀搜尋空間。

var searchSpace = new SearchSpace();
searchSpace["SingleOption"] = new UniformSingleOption(min:-10f, max:10f, defaultValue=0f) 
var nestedSearchSpace = new SearchSpace();
nestedSearchSpace["IntOption"] = new UniformIntOption(min:-10, max:10, defaultValue=0);
searchSpace["Nest"] = nestedSearchSpace;

自訂搜尋範圍的另一個選項是擴充搜尋範圍。 例如,SdcaOption 只提供 L1RegularizationL2Regularization 參數。 不過,SdcaRegressionTrainer 中提供更多參數可供您設定,例如 BiasLearningRate

若要擴充搜尋空間,請建立繼承自 SdcaOption 的新類別,例如 SdcaExtendedOption

public class SdcaExtendedOption : SdcaOption
{
    [Range(0.10f, 1f, 0.01f)]
    public float BiasLearningRate {get;set;}   
}

若要指定搜尋空間範圍,請使用 RangeAttribute,其相當於 Microsoft.ML.SearchSpace.Option

然後,在您使用搜尋空間的任何位置,參考 SdcaExtendedOption 而不是 SdcaOption

例如,當您初始化搜尋空間時,可以執行此動作,如下所示:

var sdcaSearchSpace = new SearchSpace<SdcaExtendedOption>();

建立您自己的試用執行器

根據預設,AutoML 支援二元分類、多元分類和迴歸。 不過,ML.NET 支援更多案例,例如:

  • 建議
  • 預測
  • 排名
  • 影像分類
  • 文字分類
  • 句子相似度

針對沒有預先設定搜尋空間和可掃掠估算器的案例,您可以自行建立並使用試用執行器來啟用該案例的 AutoML。

例如,假設餐廳檢閱的資料如下所示:

哇...喜歡這個地方。

1

不夠酥脆。

0

您想要使用 TextClassificationTrainer 定型器來分析 0 為負面且 1 為正面的情緒。 不過,沒有任何 ctx.Auto().TextClassification() 設定。

若要搭配文字分類定型器使用 AutoML,您必須:

  1. 建立您自己的搜尋空間。

    // Define TextClassification search space
    public class TCOption
    {
        [Range(64, 128, 32)]
        public int BatchSize { get; set; }
    }
    

    在此情況下,AutoML 會搜尋 BatchSize 超參數的不同設定。

  2. 建立可掃掠估算器,並將其新增至您的管線。

    // Initialize search space
    var tcSearchSpace = new SearchSpace<TCOption>();
    
    // Create factory for Text Classification trainer
    var tcFactory = (MLContext ctx, TCOption param) =>
    {
        return ctx.MulticlassClassification.Trainers.TextClassification(
            sentence1ColumnName: textColumnName,
            batchSize:param.BatchSize);
    };
    
    // Create text classification sweepable estimator
    var tcEstimator = 
        ctx.Auto().CreateSweepableEstimator(tcFactory, tcSearchSpace);
    
    // Define text classification pipeline
    var pipeline =
        ctx.Transforms.Conversion.MapValueToKey(columnInference.ColumnInformation.LabelColumnName)
            .Append(tcEstimator);
    

    在此範例中,會使用 TCOption 搜尋空間和自訂 TextClassificationTrainer 處理站來建立可掃掠估算器。

  3. 建立自訂試用執行器

    若要建立自訂試用執行器,請實作 ITrialRunner

    public class TCRunner : ITrialRunner
    {
        private readonly MLContext _context;
        private readonly TrainTestData _data;
        private readonly IDataView _trainDataset;
        private readonly IDataView _evaluateDataset;
        private readonly SweepablePipeline _pipeline;
        private readonly string _labelColumnName;
        private readonly MulticlassClassificationMetric _metric;
    
        public TCRunner(
            MLContext context, 
            TrainTestData data, 
            SweepablePipeline pipeline,
            string labelColumnName = "Label", 
            MulticlassClassificationMetric metric = MulticlassClassificationMetric.MicroAccuracy)
        {
            _context = context;
            _data = data;
            _trainDataset = data.TrainSet;
            _evaluateDataset = data.TestSet;
            _labelColumnName = labelColumnName;
            _pipeline = pipeline;
            _metric = metric;
        }
    
        public void Dispose()
        {
            return;
        }
    
        // Run trial asynchronously
        public Task<TrialResult> RunAsync(TrialSettings settings, CancellationToken ct)
        {
            try
            {
                return Task.Run(() => Run(settings));
            }
            catch (Exception ex) when (ct.IsCancellationRequested)
            {
                throw new OperationCanceledException(ex.Message, ex.InnerException);
            }
            catch (Exception)
            {
                throw;
            }
        }
    
        // Helper function to define trial run logic
        private TrialResult Run(TrialSettings settings)
        {
            try
            {
                // Initialize stop watch to measure time
                var stopWatch = new Stopwatch();
                stopWatch.Start();
    
                // Get pipeline parameters
                var parameter = settings.Parameter["_pipeline_"];
    
                // Use parameters to build pipeline
                var pipeline = _pipeline.BuildFromOption(_context, parameter);
    
                // Train model
                var model = pipeline.Fit(_trainDataset);
    
                // Evaluate the model
                var predictions = model.Transform(_evaluateDataset);
    
                // Get metrics
                var evaluationMetrics = _context.MulticlassClassification.Evaluate(predictions, labelColumnName: _labelColumnName);
                var chosenMetric = GetMetric(evaluationMetrics);
    
                return new TrialResult()
                {
                    Metric = chosenMetric,
                    Model = model,
                    TrialSettings = settings,
                    DurationInMilliseconds = stopWatch.ElapsedMilliseconds
                };
            }
            catch (Exception)
            {
                return new TrialResult()
                {
                    Metric = double.MinValue,
                    Model = null,
                    TrialSettings = settings,
                    DurationInMilliseconds = 0,
                };
            }
        }
    
        // Helper function to choose metric used by experiment
        private double GetMetric(MulticlassClassificationMetrics metric)
        {
            return _metric switch
            {
                MulticlassClassificationMetric.MacroAccuracy => metric.MacroAccuracy,
                MulticlassClassificationMetric.MicroAccuracy => metric.MicroAccuracy,
                MulticlassClassificationMetric.LogLoss => metric.LogLoss,
                MulticlassClassificationMetric.LogLossReduction => metric.LogLossReduction,
                MulticlassClassificationMetric.TopKAccuracy => metric.TopKAccuracy,
                _ => throw new NotImplementedException(),
            };
        }
    }
    

    此範例中的 TCRunner 實作:

    • 擷取針對該試用版選擇的超參數
    • 使用超參數來建立 ML.NET 管線
    • 使用 ML.NET 管線來定型模型
    • 評估模型
    • 傳回 TrialResult 物件,其中包含該試用版的資訊
  4. 將您的自訂試用執行器初始化

    var tcRunner = new TCRunner(context: ctx, data: trainValidationData, pipeline: pipeline);
    
  5. 建立及設定您的實驗。 使用 SetTrialRunner 擴充方法,將自訂試用執行器新增至實驗。

    AutoMLExperiment experiment = ctx.Auto().CreateExperiment();
    
    // Configure AutoML experiment
    experiment
        .SetPipeline(pipeline)
        .SetMulticlassClassificationMetric(MulticlassClassificationMetric.MicroAccuracy, labelColumn: columnInference.ColumnInformation.LabelColumnName)
        .SetTrainingTimeInSeconds(120)
        .SetDataset(trainValidationData)
        .SetTrialRunner(tcRunner);
    
  6. 執行您的實驗

    var tcCts = new CancellationTokenSource();
    TrialResult textClassificationExperimentResults = await experiment.RunAsync(tcCts.Token);
    

選擇不同的微調器

AutoML 支援各種微調演算法,可逐一查看搜尋空間,以搜尋最佳超參數。 根據預設,其會使用 Eci 成本訓練微調器。 您可以使用實驗擴充方法,選擇最適合您案例的另一個微調器。

使用下列方法來設定您的微調器:

例如,若要使用方格搜尋微調器,您的程式碼可能看起來如下:

experiment.SetGridSearchTuner();

設定實驗監視

監視實驗進度最快速的方式,是從 MLContext 定義 Log 事件。 不過,Log 事件會在每次試用期間,輸出 AutoML 所產生的記錄原始傾印。 由於有大量未格式化的資訊,因此很困難。

如需掌握度更高的監視體驗,請使用 IMonitor 介面實作類別。

public class AutoMLMonitor : IMonitor
{
    private readonly SweepablePipeline _pipeline;

    public AutoMLMonitor(SweepablePipeline pipeline)
    {
        _pipeline = pipeline;
    }

    public IEnumerable<TrialResult> GetCompletedTrials() => _completedTrials;

    public void ReportBestTrial(TrialResult result)
    {
        return;
    }

    public void ReportCompletedTrial(TrialResult result)
    {
        var trialId = result.TrialSettings.TrialId;
        var timeToTrain = result.DurationInMilliseconds;
        var pipeline = _pipeline.ToString(result.TrialSettings.Parameter);
        Console.WriteLine($"Trial {trialId} finished training in {timeToTrain}ms with pipeline {pipeline}");
    }

    public void ReportFailTrial(TrialSettings settings, Exception exception = null)
    {
        if (exception.Message.Contains("Operation was canceled."))
        {
            Console.WriteLine($"{settings.TrialId} cancelled. Time budget exceeded.");
        }
        Console.WriteLine($"{settings.TrialId} failed with exception {exception.Message}");
    }

    public void ReportRunningTrial(TrialSettings setting)
    {
        return;
    }
}

IMonitor 介面有四個生命週期事件:

提示

雖然並非必要,但請在監視器中包含您的 SweepablePipeline,因此您可以使用 TrialSettingsParameter 屬性來檢查針對試用版所產生的管線。

在此範例中,只會實作 ReportCompletedTrialReportFailTrial

實作監視器之後,請使用 SetMonitor 將其設定為實驗組態的一部分。

var monitor = new AutoMLMonitor(pipeline);
experiment.SetMonitor(monitor);

然後,執行您的實驗:

var cts = new CancellationTokenSource();
TrialResult experimentResults = await experiment.RunAsync(cts.Token);

當您使用此實作執行實驗時,輸出看起來應該類似下列內容:

Trial 0 finished training in 5835ms with pipeline ReplaceMissingValues=>OneHotEncoding=>Concatenate=>FastForestRegression
Trial 1 finished training in 15080ms with pipeline ReplaceMissingValues=>OneHotEncoding=>Concatenate=>SdcaRegression
Trial 2 finished training in 3941ms with pipeline ReplaceMissingValues=>OneHotHashEncoding=>Concatenate=>FastTreeRegression

持續試用

根據預設,AutoML 只會儲存 TrialResult 以獲得最佳模型。 不過,如果您要保存每個試用版,則可以從監視器內執行此動作。

在您的監視器內:

  1. 定義已完成試用版的屬性,以及存取這些屬性的方法。

    private readonly List<TrialResult> _completedTrials;
    
    public IEnumerable<TrialResult> GetCompletedTrials() => _completedTrials;
    
  2. 在您的建構函式中將其初始化

    public AutoMLMonitor(SweepablePipeline pipeline)
    {
        //...
        _completedTrials = new List<TrialResult>();
        //...
    }
    
  3. 在您的 ReportCompletedTrial 生命週期方法內附加每個試用結果。

    public void ReportCompletedTrial(TrialResult result)
    {
        //...
        _completedTrials.Add(result);
    }
    
  4. 定型完成時,您可以呼叫 GetCompletedTrials 來存取所有已完成的試用版

    var completedTrials = monitor.GetCompletedTrials();
    

此時,您可在完成的試用版集合上執行其他處理。 例如,您可以選擇 AutoML 所選取以外的模型、將試用結果記錄至資料庫,或從任何已完成的試用版重建管線。

取消實驗

當您以非同步方式執行實驗時,請務必明確地終止程序。 若要這樣做,請使用 CancellationToken

警告

取消實驗不會儲存任何中繼輸出。 設定檢查點以儲存中繼輸出。

var cts = new CancellationTokenSource();
TrialResult experimentResults = await experiment.RunAsync(cts.Token);

設定檢查點

檢查點會提供一種方式,讓您在發生早期終止或錯誤的情況下,可儲存來自定型程序的中繼輸出。 若要設定檢查點,請使用 SetCheckpoint 擴充方法,並提供目錄來儲存中繼輸出。

var checkpointPath = Path.Join(Directory.GetCurrentDirectory(), "automl");
experiment.SetCheckpoint(checkpointPath);

判斷特徵重要度

隨著更多日常生活層面引入機器學習 (例如醫療保健),了解機器學習模型所做決策的原因至關重要。 排列特徵重要度 (PFI) 是用來說明分類、排名和迴歸模型的技術。 概括而言,它的運作方式是針對整個資料集一次一種特性地隨機打亂資料,計算感興趣的效能計量會降低多少。 變更愈大,該特性愈重要。 如需關於 PFI 的詳細資訊,請參閱使用排列特徵重要度解譯模型預測

注意

計算 PFI 可能會是耗時的作業。 計算所需的時間與您擁有的特徵資料行數目會成正比。 特徵越多,PFI 的執行時間就越長。

若要使用 AutoML 判斷特徵重要度:

  1. 取得最佳模型。

    var bestModel = expResult.Model;
    
  2. 將模型套用至您的資料集。

    var transformedData = bestModel.Transform(trainValidationData.TrainSet);
    
  3. 使用 PermutationFeatureImportance 來計算特徵重要度

    在此情況下,工作為迴歸,但相同的概念適用於其他工作,例如排名和分類。

    var pfiResults = 
        mlContext.Regression.PermutationFeatureImportance(bestModel, transformedData, permutationCount:3);
    
  4. 依評估計量的變更來排序特徵重要度。

    var featureImportance = 
        pfiResults.Select(x => Tuple.Create(x.Key, x.Value.Regression.RSquared))
            .OrderByDescending(x => x.Item2);