使用ML.NET預測紐約出租車費


有了上一篇《.NET Core玩轉機器學習》打基礎,這一次我們以紐約出租車費的預測做為新的場景案例,來體驗一下回歸模型。

場景概述


我們的目標是預測紐約的出租車費,乍一看似乎僅僅取決於行程的距離和時長,然而紐約的出租車供應商對其他因素,如額外的乘客數、信用卡而不是現金支付等,會綜合考慮而收取不同數額的費用。紐約市官方給出了一份樣本數據

 

確定策略


為了能夠預測出租車費,我們選擇通過機器學習建立一個回歸模型。使用官方提供的真實數據進行擬合,在訓練模型的過程中確定真正能影響出租車費的決定性特征。在獲得模型后,對模型進行評估驗證,如果偏差在接受的范圍內,就以這個模型來對新的數據進行預測。

 

解決方案


  • 創建項目

    看過上一篇文章的讀者,就比較輕車熟路了,推薦使用Visual Studio 2017創建一個.NET Core的控制台應用程序項目,命名為TaxiFarePrediction。使用NuGet包管理工具添加對Microsoft.ML的引用。



  • 准備數據集

    下載訓練數據集taxi-fare-train.csv和驗證數據集taxi-fare-test.csv,數據集的內容類似為:
    vendor_id,rate_code,passenger_count,trip_time_in_secs,trip_distance,payment_type,fare_amount
    VTS,1,1,1140,3.75,CRD,15.5
    VTS,1,1,480,2.72,CRD,10.0
    VTS,1,1,1680,7.8,CSH,26.5
    VTS,1,1,600,4.73,CSH,14.5
    VTS,1,1,600,2.18,CRD,9.5
    ...

    對字段簡單說明一下:

    字段名 含義 說明
    vendor_id 供應商編號 特征值
    rate_code 比率碼 特征值
    passenger_count 乘客人數 特征值
    trip_time_in_secs 行程時長 特征值
    trip_distance 行程距離 特征值
    payment_type 支付類型 特征值
    fare_amount 費用 目標值

    在項目中添加一個Data目錄,將兩份數據集復制到該目錄下,對文件屬性設置“復制到輸出目錄”。




  • 定義數據類型和路徑

    首先聲明相關的包引用。

    using System;
    using Microsoft.ML.Models;
    using Microsoft.ML.Runtime;
    using Microsoft.ML.Runtime.Api;
    using Microsoft.ML.Trainers;
    using Microsoft.ML.Transforms;
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.ML;

    在Main函數的上方定義一些使用到的常量。

    const string DataPath = @".\Data\taxi-fare-train.csv";
    const string TestDataPath = @".\Data\taxi-fare-test.csv";
    const string ModelPath = @".\Models\Model.zip";
    const string ModelDirectory = @".\Models";

    接下來定義一些使用到的數據類型,以及和數據集中每一行的位置對應關系。

    public class TaxiTrip
    {
        [Column(ordinal: "0")]
        public string vendor_id;
        [Column(ordinal: "1")]
        public string rate_code;
        [Column(ordinal: "2")]
        public float passenger_count;
        [Column(ordinal: "3")]
        public float trip_time_in_secs;
        [Column(ordinal: "4")]
        public float trip_distance;
        [Column(ordinal: "5")]
        public string payment_type;
        [Column(ordinal: "6")]
        public float fare_amount;
    }
    
    public class TaxiTripFarePrediction
    {
        [ColumnName("Score")]
        public float fare_amount;
    }
    
    static class TestTrips
    {
        internal static readonly TaxiTrip Trip1 = new TaxiTrip
        {
            vendor_id = "VTS",
            rate_code = "1",
            passenger_count = 1,
            trip_distance = 10.33f,
            payment_type = "CSH",
            fare_amount = 0 // predict it. actual = 29.5
        };
    }

     

  • 創建處理過程

    創建一個Train方法,定義對數據集的處理過程,隨后聲明一個模型接收訓練后的結果,在返回前把模型保存到指定的位置,以便以后直接取出來使用不需要再重新訓練。
    public static async Task<PredictionModel<TaxiTrip, TaxiTripFarePrediction>> Train()
    {
        var pipeline = new LearningPipeline();
    
        pipeline.Add(new TextLoader<TaxiTrip>(DataPath, useHeader: true, separator: ","));
        pipeline.Add(new ColumnCopier(("fare_amount", "Label")));
        pipeline.Add(new CategoricalOneHotVectorizer("vendor_id",
                                            "rate_code",
                                            "payment_type"));
        pipeline.Add(new ColumnConcatenator("Features",
                                            "vendor_id",
                                            "rate_code",
                                            "passenger_count",
                                            "trip_distance",
                                            "payment_type"));
        pipeline.Add(new FastTreeRegressor());
        PredictionModel<TaxiTrip, TaxiTripFarePrediction> model = pipeline.Train<TaxiTrip, TaxiTripFarePrediction>();
        if (!Directory.Exists(ModelDirectory))
        {
            Directory.CreateDirectory(ModelDirectory);
        }
        await model.WriteAsync(ModelPath);
        return model;
    }

     

  • 評估驗證模型

    創建一個Evaluate方法,對訓練后的模型進行驗證評估。
    public static void Evaluate(PredictionModel<TaxiTrip, TaxiTripFarePrediction> model)
    {
        var testData = new TextLoader<TaxiTrip>(TestDataPath, useHeader: true, separator: ",");
        var evaluator = new RegressionEvaluator();
        RegressionMetrics metrics = evaluator.Evaluate(model, testData);
        // Rms should be around 2.795276
        Console.WriteLine("Rms=" + metrics.Rms);
        Console.WriteLine("RSquared = " + metrics.RSquared);
    }

     

  • 預測新數據

    定義一個被用於預測的新數據,對於各個特征進行恰當地賦值。
    static class TestTrips
    {
        internal static readonly TaxiTrip Trip1 = new TaxiTrip
        {
            vendor_id = "VTS",
            rate_code = "1",
            passenger_count = 1,
            trip_distance = 10.33f,
            payment_type = "CSH",
            fare_amount = 0 // predict it. actual = 29.5
        };
    }

    預測的方法很簡單,prediction即預測的結果,從中打印出預測的費用和真實費用。

    var prediction = model.Predict(TestTrips.Trip1);
    
    Console.WriteLine("Predicted fare: {0}, actual fare: 29.5", prediction.fare_amount);

     

  • 運行結果



到此我們完成了所有的步驟,關於這些代碼的詳細說明,可以參看《Tutorial: Use ML.NET to Predict New York Taxi Fares (Regression)》,只是要注意該文中的部分代碼有誤,由於使用到了C# 7.1的語法特性,本文的代碼是經過了修正的。完整的代碼如下:

using System;
using Microsoft.ML.Models;
using Microsoft.ML.Runtime;
using Microsoft.ML.Runtime.Api;
using Microsoft.ML.Trainers;
using Microsoft.ML.Transforms;
using System.Collections.Generic;
using System.Linq;
using Microsoft.ML;
using System.Threading.Tasks;
using System.IO;

namespace TaxiFarePrediction
{
    class Program
    {
        const string DataPath = @".\Data\taxi-fare-train.csv";
        const string TestDataPath = @".\Data\taxi-fare-test.csv";
        const string ModelPath = @".\Models\Model.zip";
        const string ModelDirectory = @".\Models";

        public class TaxiTrip
        {
            [Column(ordinal: "0")]
            public string vendor_id;
            [Column(ordinal: "1")]
            public string rate_code;
            [Column(ordinal: "2")]
            public float passenger_count;
            [Column(ordinal: "3")]
            public float trip_time_in_secs;
            [Column(ordinal: "4")]
            public float trip_distance;
            [Column(ordinal: "5")]
            public string payment_type;
            [Column(ordinal: "6")]
            public float fare_amount;
        }

        public class TaxiTripFarePrediction
        {
            [ColumnName("Score")]
            public float fare_amount;
        }

        static class TestTrips
        {
            internal static readonly TaxiTrip Trip1 = new TaxiTrip
            {
                vendor_id = "VTS",
                rate_code = "1",
                passenger_count = 1,
                trip_distance = 10.33f,
                payment_type = "CSH",
                fare_amount = 0 // predict it. actual = 29.5
            };
        }

        public static async Task<PredictionModel<TaxiTrip, TaxiTripFarePrediction>> Train()
        {
            var pipeline = new LearningPipeline();

            pipeline.Add(new TextLoader<TaxiTrip>(DataPath, useHeader: true, separator: ","));
            pipeline.Add(new ColumnCopier(("fare_amount", "Label")));
            pipeline.Add(new CategoricalOneHotVectorizer("vendor_id",
                                              "rate_code",
                                              "payment_type"));
            pipeline.Add(new ColumnConcatenator("Features",
                                                "vendor_id",
                                                "rate_code",
                                                "passenger_count",
                                                "trip_distance",
                                                "payment_type"));
            pipeline.Add(new FastTreeRegressor());
            PredictionModel<TaxiTrip, TaxiTripFarePrediction> model = pipeline.Train<TaxiTrip, TaxiTripFarePrediction>();
            if (!Directory.Exists(ModelDirectory))
            {
                Directory.CreateDirectory(ModelDirectory);
            }
            await model.WriteAsync(ModelPath);
            return model;
        }

        public static void Evaluate(PredictionModel<TaxiTrip, TaxiTripFarePrediction> model)
        {
            var testData = new TextLoader<TaxiTrip>(TestDataPath, useHeader: true, separator: ",");
            var evaluator = new RegressionEvaluator();
            RegressionMetrics metrics = evaluator.Evaluate(model, testData);
            // Rms should be around 2.795276
            Console.WriteLine("Rms=" + metrics.Rms);
            Console.WriteLine("RSquared = " + metrics.RSquared);
        }

        static async Task Main(string[] args)
        {
            PredictionModel<TaxiTrip, TaxiTripFarePrediction> model = await Train();
            Evaluate(model);

            var prediction = model.Predict(TestTrips.Trip1);

            Console.WriteLine("Predicted fare: {0}, actual fare: 29.5", prediction.fare_amount);
        }
    }
}

 

不知不覺我們的ML.NET之旅又向前進了一步,是不是對於使用.NET Core進行機器學習解決現實生活中的問題更有興趣了?請保持關注吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM