天天看點

基于 DolphinDB 機器學習的計程車行程時間預測

作者:DolphinDB

DolphinDB 集高性能時序資料庫與全面的分析功能為一體,可用于海量結構化資料的存儲、查詢、分析、實時計算等,在工業物聯網場景中應用廣泛。本文以紐約計程車行程時間預測為例,介紹如何使用 DolphinDB 訓練機器學習模型,并進行實時資料的預測,為基于智能網聯汽車的車聯網企業提供基于機器學習方法的即時預測方案。

1. 概要

随着手機移動應用與網約車平台的迅速發展,網約車出行逐漸成為城市生活中一種重要的出行方式。相較其他出行方式而言,選擇網約車的乘客對出行時效性有更高的要求,本文将基于乘客上車時間及上下車地點等靜态資訊,使用 DolphinDB 機器學習方法訓練模型,預測網約車行程時間。

在此基礎上,本文将介紹如何使用 DolphinDB 流資料處理系統對業務系統産生的持續增長的網約車訂單動态資料進行實時的收集、清洗、統計、入庫,并實時展示行程時間預測結果。

基于 DolphinDB 機器學習的計程車行程時間預測

行程時間實時預測流程

2. 資料介紹

2.1 資料來源及訓練方法

本文訓練和預測采用 Kaggle 提供的來自紐約計程車委員會的資料集,訓練方法參考了獲獎者 beluga 的模型,使用 DolphinDB 對原始資料進行資料預處理,完成位置資訊主成分分析(PCA, Principal Component Analysis)、位置資訊聚類(KMeans)、新特征建構等工作,并使用 DolphinDB XGBoost 插件完成模型訓練及行程時間預測。

為對比 DolphinDB 在機器學習上的性能,本文使用 Python Scikit-Learn 庫及 XGBoost 在同一環境下進行了模型訓練和預測,DolphinDB 在訓練耗時、模型精度等方面均有良好表現。

2.2 資料特征

該資料集預先分為訓練資料集及測試資料集,訓練資料集共包含1458644條資料,測試資料集共包括625134條資料;訓練資料集共包含以下11列資訊。

列名 列類型 說明 執行個體
id SYMBOL 行程的唯一辨別 id2875421
vendor_id INT 行程記錄提供商代碼 2
pickup_datetime DATETIME 計程車計價器開啟時間 2016/3/14 17:24:55
dropoff_datetime DATETIME 計程車計價器關閉時間 2016/3/14 17:32:30
passenger_count INT 乘客數量 1
pickup_longitude DOUBLE 計程車計價器開啟位置經度 -73.98215484619139
pickup_latitude DOUBLE 計程車計價器開啟位置緯度 40.76793670654297
dropoff_longitude DOUBLE 計程車計價器關閉位置經度 -73.96463012695312
dropoff_latitude DOUBLE 計程車計價器關閉位置緯度 40.765602111816406
store_and_fwd_flag CHAR 辨別來源是否為存儲的曆史資料 N
trip_duration INT 行程時間(按秒計) 455

行程時間預測的目标列為上表中 trip_duration 列,即 dropoff_datetime 與 pickup_datetime 之差。測試資料集用于預測,故其列資訊不包括 dropoff_datetime 及 trip_duration 列,測試資料集中行程辨別、位置等列屬性同上表。

上表的資料類型中,SYMBOL 類型是 DolphinDB 中一種特殊的字元串類型,在系統内部的存儲結構為一個編碼字典,DATETIME 類型為包含了日期和時刻的時間類型。

DolphinDB 支援 loadText 方法讀取 csv 等資料存儲檔案到記憶體表,使用者可以 schema 函數擷取表的特征資訊。DolphinDB 也支援使用 SQL 語句完成資料的查詢。

train = loadText("./taxidata/train.csv") 
train.schema().colDefs
select count(*) from train
select top 5 * from train           

2.3 資料存儲

将資料加載到記憶體表後,可以将訓練資料與測試資料導入 DolphinDB 資料庫中,便于後續資料的讀取與模型的訓練,資料導入分布式資料庫的操作詳見 database.md · dolphindb/Tutorials_CN - Gitee。

3. 模型建構

本節介紹行程時間預測模型的建構方法。

行程時間預測模型的建構分多個過程,一是預處理原始資料,對可能存在的空值進行轉換,并将字元等非數值型資料轉換為可用于模型訓練的數值型資料;二是優化位置資訊,原始資料中的緯度經度資訊集中在40.70 °N 至40.80 °N 及73.94 °W 至74.02 °W之間,資料間位置特征差異不夠顯著,使用主成分分析、聚類方法處理可以提取到特征更明顯的資訊;三是新特征的建構,位置資訊和時間資訊是訂單資料的兩個關鍵次元,通過計算可以在位置資訊基礎上得到方位、距離資訊,提取更多空間特征,而組合不同類别的位置資訊和時間資訊也可以得到更複雜的特征,有利于模型學習深層次的時空規律。

3.1 資料預處理

在模型訓練過程中,首先需要檢查資料集是否包含空值,本訓練資料集與測試資料集均不包含空值,若存在缺失值,還需要删除、插補等操作解決缺失資料問題。

其次,需要檢查資料集資料類型,原始資料往往包含文本/字元資料,由1.3節表可知,本資料集中 store_and_fwd_flag 列為字元型資料,pickup_datetime 及 dropoff_datetime 列為日期時間類型資料,為充分利用這些資訊訓練模型,需要将其轉化為數值型資料。

此外,考慮到該資料測試集評價名額為均方根對數誤差(Root Mean Squared Logarithmic Error, RMSLE),同時,最大行程時間接近1000小時,離群值會影響模型訓練效果,對行程時間取對數作為預測值,在評價時(見3.6節)可以直接使用均方根誤差(Root Mean Squared Error, RMSE)名額。

基于 DolphinDB 機器學習的計程車行程時間預測

RMSE

DolphinDB 提供多種計算函數,可以幫助使用者快速實作資料處理。DolphinDB 提供 isNull() 方法用于判斷空值,配合 sum() 等聚合函數使用可以快速完成整表資料的查詢;提供類似于條件運算符的 iif() 方法簡化 if-else 語句;date()、weekday()、hour() 等方法可以提取時間、日期資料的不同特征,簡潔高效;類似于 Python 等程式設計語言,DolphinDB 支援方括号([])索引,簡化了表的查找、更新和插入。

sum(isNull(train))  // 0,不含空值
trainData[`store_and_fwd_flag_int] = iif(trainData[`store_and_fwd_flag] == 'N', int(0), int(1)) // 将字元N/Y轉化為0/1值
trainData[`pickup_date] = date(trainData[`pickup_datetime]) // 日期
trainData[`pickup_weekday] = weekday(trainData[`pickup_datetime]) // 星期*
trainData[`pickup_hour] = hour(trainData[`pickup_datetime]) // 小時
trainData[`log_trip_duration] = log(double(trainData[`trip_duration]) + 1)// 對行程時間取對數,log(trip_duration+1)
select max(trip_duration / 3600) from trainData // 訓練集上最大行程時間為979h           

3.2 位置資訊主成分分析(PCA)

原始資料中的緯度經度資訊集中在40.70 °N 至40.80 °N 及73.94 °W 至74.02 °W之間,資料間位置特征差異不夠顯著,使用 PCA 來轉換經度和緯度坐标,有助于 XGBoost 決策樹的拆分,DolphinDB PCA 函數使用詳見 pca — DolphinDB 2.0 documentation。

DolphinDB PCA 傳回的結果是一個字典,包含 components、explainedVarianceRatio、singularValues 三個鍵,分别代表對應大小為 size(colNames)*k 的主成分分析矩陣、前k個主成分每個特征的方差貢獻率、主成分方差(協方差矩陣特征值)。可通過主成分分析矩陣轉換待處理資料,詳見 Scikit-Learn PCA.transform()。

可從中取若幹資料繪制經度-緯度散點圖觀察 PCA 結果。

經處理,位置坐标分散在原點附近。

基于 DolphinDB 機器學習的計程車行程時間預測

PCA前上客位置資訊

基于 DolphinDB 機器學習的計程車行程時間預測

PCA 後上客位置資訊

pca() 接收一個或多個資料源為參數,對指定列中的資料進行主成分分析,使用者可通過 table() 方法建立記憶體表,用于 PCA;DolphinDB 也提供了 dot()、repmat() 等矩陣乘法、矩陣堆疊方法,使用者可使用内置函數快速完成矩陣運算,處理位置資訊。

PCApara = table(trainData[`pickup_latitude] as latitude, trainData[`pickup_longitude] as longitude)
pca_model = pca(sqlDS(<select * from PCApara>)) // 使用PCA計算資料集屬性
pca_trainpick = dot((matrix(trainPickPara) - repmat(matrix(avg(trainPickPara)), train_n, 1)), pca_model.components) // transform
trainData[`pca_trainpick_0] = flatten(pca_trainpick[:, 0])           

DolphinDB 提供了plot函數供資料可視化。使用者可通過chartType指定圖表類型,詳見plot — DolphinDB 2.0 documentation。

x = select top 1000 pca_trainpick_1 from trainData
y = flatten(matrix(select top 1000 pca_trainpick_0 from trainData))
plot(x, y, chartType=SCATTER)           

3.3 位置資訊聚類(KMeans)

原始資料位置資料規模龐大,很難挖掘多條資料間的共同特征。KMeans 可以将經緯度相近的資料點歸為同一個簇,有助于更好地歸納組内資料特征。本模型指定要生成的聚類數為100,質心最大疊代次數為100,選擇 KMeans++ 算法生成模型,DolphinDB kmeans 可選參數及含義詳見 kmeans — DolphinDB 2.0 documentation。

可以使用條形圖觀察聚類後的資料分布。

基于 DolphinDB 機器學習的計程車行程時間預測

KMeans 聚類結果

kmeans() 接收一個表作為訓練集。對于機器學習函數生成的模型,DolphinDB 提供 saveModel 方法将模型儲存到本地檔案中用于後續預測,使用者可指定伺服器端輸出檔案的絕對路徑或相對路徑;DolphinDB 也提供了 predict 方法,調用已訓練的特定模型對相同表結構的測試集資料進行預測。

kmeans_set = PCApara[rand(size(PCApara)-1, 500000)] // 随機選取500000資料用于聚類
kmeans_model = kmeans(kmeans_set, 100, maxIter=100, init='k-means++') // KMeans++
saveModel(kmeans_model, "./taxidata/KMeans.model") // 儲存模型訓練結果
trainData['pickup_cluster'] = kmeans_model.predict(select pickup_latitude, pickup_longitude from trainData)           

saveModel 和 predict 函數的使用方法可以參考:

  • saveModel — DolphinDB 2.0 documentation
  • predict — DolphinDB 2.0 documentation

3.4 新特征建構

原始資料僅提供了經度及緯度位置資訊,可在此基礎上增加位置特征,如地球表面兩經緯度點之間距離、兩經緯度點之間的 Manhattan 距離、兩個經緯度之間的方位資訊等等。

地球表面兩點間的距離可使用 haversine 公式精确得到,而在本資料集中,網約車實際駛過的往往是水準或豎直的街道所組成的路徑,Manhattan 距離(也稱城市街區距離)标明兩個點在标準坐标系上的絕對軸距總和,可能能夠更精确地反映實際的行駛距離。

在此基礎上,考慮到訓練集包含了完整的時間資訊,還可以在訓練集上添加速度特征。訓練集上的速度特征無法直接用于測試集,但在相同的位置聚類屬性或相同的時間日期特征下,行程所需時間及平均速度可能存在某些共性(如郊區或淩晨時分網約車車速偏大而城區及早晚高峰時段網約車車速偏小),可以将這種訓練集上發現的經驗應用到測試集上。可将資料按聚類屬性或時間特征分組,統計組内資料平均速度等特征,組合生成新特征,合并到測試集的相應分組中。

距離、方位的計算參數不同而方法相同,DolphinDB 支援使用者自定義函數,通過獨立的代碼子產品完成特定的計算任務。而對于不同類别(聚類、時間)内的特征,可使用 groupby 方法在每個分組中計算需要的特征(如平均值)。groupby 接收三個參數,将根據第三個參數指定的列進行分組,取第一個參數為計算函數,計算第二個參數對應列的特征,并傳回行數與分組數相等的表。使用者可通過表連接配接操作将該組合特征合并入特征資料,本文使用 fj(full join) 将特征表與 groupby 表合并,fj() 指定第三個參數為連接配接列,将前兩個參數所傳入的表合并。

// 兩經緯度點距離、兩個經緯度之間的 Manhattan 距離、兩個經緯度之間的方位資訊
trainData['distance_haversine'] = haversine_array(trainData['pickup_latitude'], trainData['pickup_longitude'], trainData['dropoff_latitude'], trainData['dropoff_longitude'])
trainData['distance_dummy_manhattan'] = dummy_manhattan_distance(trainData['pickup_latitude'], trainData['pickup_longitude'], trainData['dropoff_latitude'], trainData['dropoff_longitude'])
trainData['direction'] = bearing_array(trainData['pickup_latitude'], trainData['pickup_longitude'], trainData['dropoff_latitude'], trainData['dropoff_longitude'])
// 按時間、聚類等資訊處理速度、行程時間,産生新特征
for(gby_col in ['pickup_hour', 'pickup_date', 'pickup_week_hour', 'pickup_cluster', 'dropoff_cluster']) {
    for(gby_para in ['avg_speed_h', 'avg_speed_m', 'log_trip_duration']) {
        gby = groupby(avg, trainData[gby_para], trainData[gby_col])
        gby.rename!(`avg_ + gby_para, gby_para + '_gby_' + gby_col)
        trainData = fj(trainData, gby, gby_col)
        testData = fj(testData, gby, gby_col)
    }
      trainData.dropColumns!(`gby + gby_col)
}           

3.5 模型訓練(XGBoost)

在進行訓練之前,需要再一次檢查訓練集和測試集的資料,需要剔除 ID、日期、字元等非數值型資料,以及平均速度、行駛時間等僅在訓練集上存在的資料,保證訓練資料與預測資料結構一緻。

完成資料處理及特征建構後,可以使用 XGBoost 等機器學習方法訓練模型。為評價模型訓練效果,将訓練資料集劃分為訓練集和驗證集,随機選取80%的資料作為訓練集訓練模型,使用20%的資料作為驗證集輸出預測結果,使用均方根誤差名額計算驗證集的預測值與真實值的偏差。最終可以在測試集上輸出行程時間的預測結果。

本模型在驗證集上的均方根誤差為0.390,可以繪制預測值-真值散點圖,定性分析模型預測效果。

基于 DolphinDB 機器學習的計程車行程時間預測

驗證集上的預測值與真實值

DolphinDB 提供了 XGBoost 插件實作模型訓練及預測,使用前需要下載下傳插件到指定路徑并加載 XGBoost 插件。DolphinDB XGBoost 插件使用詳見 xgboost/README_CN.md · dolphindb/DolphinDBPlugin - Gitee。

xgb_pars = {'min_child_weight': 50, 'eta': 0.3, 'colsample_bytree': 0.3, 'max_depth': 10,
            'subsample': 0.8, 'lambda': 1., 'nthread': 4, 'booster' : 'gbtree', 'silent': 1,
            'eval_metric': 'rmse', 'objective': 'reg:linear', 'nthread': 48} // xgb 參數設定
xgbModel = xgboost::train(ytrain, train, xgb_pars, 60) // 訓練模型
yvalid_ = xgboost::predict(xgbModel, valid) // 使用模型進行預測           

3.6 模型評價

為實作計程車行程時間的預測,本文使用了三種機器學習方法。首先使用 PCA 對位置資訊進行處理,轉換資料的經緯度特征;使用 KMeans++ 對計程車上下客位置進行聚類,将紐約市區劃分為100個區域進行分析;最後使用 XGBoost 對資料集特征進行訓練。在驗證集上模型均方根誤差為0.390,效果較好。

Python Scikit-Learn 也是主流的機器學習庫之一,本文在相同環境下使用 Python 對同一資料集進行訓練,PCA、KMeans++、XGBoost 訓練耗時如下表所示:

模型 DolphinDB Python
PCA 0.325s 0.396s
KMeans++ 45.711s 104.568s
XGBoost 57.269s 74.289s

DolphinDB 與 Python 訓練模型在驗證集上誤差如下表所示:

DolphinDB Python
RMSE 0.390 0.394

在本行程時間預測任務中,在準确率上,DolphinDB 與 Python 相近;而在性能上,DolphinDB 在 PCA、KMeans++、XGBoost 上的性能均優于 Python。

4. 行程時間實時預測

本節結合現實場景,介紹如何使用 DolphinDB 處理實時的訂單流資料,基于預測模型實時估計行程時間。

現實場景中,網約車乘客對時效性要求高,需要平台提供準确的行程時間估計;而服務商也需要監控出行平台,分析出行需求并完成資源排程,僅僅使用預測模型無法高效處理實時資料,難以完成即時預測任務,無法滿足乘客和服務商的實時需求。DolphinDB 流資料子產品可以解決生産環境下實時資料的快速分析計算問題,對服務商發送的即時資料,DolphinDB 流資料引擎可高效完成資料預處理、資訊提取、特征建構等工作,使用預先訓練的模型完成實時訂單行程時間的快速準确預測,為使用者提供從模型訓練、流資料注入到實時預測及線上監控的一站式解決方案。

基于 DolphinDB 機器學習的計程車行程時間預測

DolphinDB 流資料處理架構

4.1 場景描述

DolphinDB 流資料子產品采用釋出-訂閱-消費的模式,流資料首先注入流資料表中,通過流表來釋出資料,第三方應用可以通過 DolphinDB 腳本或 API 訂閱及消費流資料。

為實作計程車行程時間的實時預測,服務商可以建立 DolphinDB 流資料表訂閱服務端消息,擷取乘客建立的行程資訊,使用離線訓練完成的模型對行程時間進行實時預測,最後可通過應用程式訂閱預測資料并提供給乘客。

4.2 實時資料模拟及預測

為擷取行程資料并使用機器學習模型預測行程時間,使用者需要建立三個流表實作實時預測,一是建立訂單資訊表訂閱乘客行程資訊,二是建立特征表完成對訂單資訊的特征提取;三是建立預測表預測特征流資料表發送的行程特征資訊,輸出預測結果。

基于 DolphinDB 機器學習的計程車行程時間預測

使用者可以使用 subscribeTable 完成流資料的訂閱,并通過 handler 指定處理訂閱資料的方法(詳見subscribeTable — DolphinDB 2.0 documentation)。在本例中,特征表需訂閱訂單表完成原始資訊的特征提取,本模型定義 process 函數實作;預測表需訂閱特征表使用特征資訊完成行程時間預測,本模型定義 predictDuration 函數實作。函數實作詳見6.2節所附代碼。

為模拟實時資料,使用 replay 函數回放曆史資料。

// 訂閱訂單資訊表,資料從訂單表流向特征表
subscribeTable(tableName="orderTable", actionName="orderProcess", offset=0, handler=process{traitTable, hisData}, msgAsTable=true, batchSize=1, throttle=1, hash=0, reconnect=true)
// 訂閱特征表,資料從特征表流向預測表
subscribeTable(tableName="traitTable", actionName="predict", offset=0, handler=predictDuration{predictTable}, msgAsTable=true, hash=1, reconnect=true)
// 回放曆史資料,模拟實時産生的生産資料
submitJob("replay", "trade",  replay{inputTables=data, outputTables=orderTable, dateColumn=`pickup_datetime, timeColumn=`pickup_datetime, replayRate=25, absoluteRate=true, parallelLevel=1})           

4.3 Grafana 實時監控

服務商可通過第三方 API 連接配接 DolphinDB 資料庫監控行程時間預測服務,本文以 Grafana 為例簡要介紹如何使用第三方應用程式動态展示實時資料。

Grafana 是一個用于時序資料動态可視化的資料展示工具,DolphinDB 提供了 Grafana 的資料接口,使用者可在 Grafana 面闆上編寫查詢腳本與 DolphinDB 進行互動,實作 DolphinDB 時序資料的可視化,并 Web 端進行實時資料分析,詳見 README.zh.md · dolphindb/grafana-datasource - Gitee。

添加 datasource 并建立 dashboard 後,在 Query 中填寫以下 DolphinDB 語句進行實時資料可視化:

  • Query 1:展示當日出行訂單預估到達時間及預估行程時間
select id as ID, pickup_datetime as pickup_time, (pickup_datetime+int((exp(duration)-1))) as arrival_time,  (exp(duration)-1)/60 as duration from predictTable 
where date(predictTable.pickup_datetime) == date(select max(pickup_datetime) from predictTable)            
  • Query 2:統計當日累計訂單數及累計乘客數
select count(*) from predictTable 
where date(predictTable.pickup_datetime) == date(select max(pickup_datetime) from predictTable)
select sum(passenger_count) from predictTable 
where date(predictTable.pickup_datetime) == date(select max(pickup_datetime) from predictTable)            
基于 DolphinDB 機器學習的計程車行程時間預測

訂單預估到達時間及當日訂單數

  • Query 3:統計當日乘客上車位置
select pickup_latitude as latitude, pickup_longitude as longitude from predictTable 
where date(predictTable.pickup_datetime) == date(select max(pickup_datetime) from predictTable)            
基于 DolphinDB 機器學習的計程車行程時間預測

當日乘客上車位置

  • Query 4:統計當日不同時刻訂單出行耗時
select pickup_datetime, (exp(duration)-1)/60 as duration from predictTable 
where date(predictTable.pickup_datetime) == date(select max(pickup_datetime) from predictTable)            
基于 DolphinDB 機器學習的計程車行程時間預測

當日不同時刻訂單出行耗時

4.4 資料持久化

如果需要将曆史資料落盤,可以訂閱訂單表中資料,指定 subscribeTable 以 loadTable 的方式将資料持久化到磁盤。

db = database("dfs://taxi")
if(existsTable("dfs://taxi", "newData")) { dropTable(db, "newData") }
db.createPartitionedTable(table=table(1:0, orderTable.schema().colDefs.name, orderTable.schema().colDefs.typeString), tableName=`newData, partitionColumns=`pickup_datetime, sortColumns=`pickup_datetime, compressMethods={datetime:"delta"})
subscribeTable(tableName="orderTable", actionName="saveToDisk", offset=0, handler=loadTable("dfs://taxi", "newData"), msgAsTable=true, batchSize=100000, throttle=1, reconnect=true)           

5. 總結

本文介紹了使用 DolphinDB 機器學習函數及插件訓練計程車行程時間預測模型的方法,與 Python Scikit-Learn 等主流機器學習方法相比,DolphinDB 在模型訓練耗時及預測精度上均有良好表現;在此基礎上,本文還介紹了如何使用 DolphinDB 流資料處理工具進行實時預測,并以 Grafana 為例展示了 DolphinDB 時序資料的可視化方法。DolphinDB 内置的計算函數和機器學習方法能夠實作從資料存儲、資料載入、資料清洗、特征構造到模型建立、模型評價的完整機器學習流程,可以為物聯網行業使用者提供更全面的資料分析方法。

6. 附錄

6.1 測試環境

  • 作業系統:Linux version 3.10.0-1160.el7.x86_64
  • CPU:Intel(R) Xeon(R) Silver 4214 CPU @2.20GHz 48核
  • 記憶體:188G
  • 軟體版本:
    • DolphinDB:2.00.9
    • Python3:3.7.12
    • Scikit-Learn:1.0.2
    • XGBoost:1.6.2

6.2 模型代碼

DolphinDB 模型訓練代碼:taxiTrain.dos

DolphinDB 流資料預測代碼: taxiStream.dos

Python 模型訓練代碼: taxiTrain.py

[害羞]文内所有連結請參考知乎文章:《基于 DolphinDB 機器學習的計程車行程時間預測》

繼續閱讀