言回歸分析實(shí)戰(zhàn):預(yù)測(cè)首爾自行車共享需求)
簡(jiǎn)介面向需要在R環(huán)境中完成回歸建模與需求預(yù)測(cè)的數(shù)據(jù)分析學(xué)習(xí)者這是一份首爾自行車共享需求預(yù)測(cè)完整項(xiàng)目資源。資源圍繞天氣、時(shí)間、假期、季節(jié)等多種因素對(duì)每小時(shí)租車量的影響展開提供從數(shù)據(jù)探索、變量重要性分析到CUBIST、隨機(jī)森林、CART、KNN、條件推斷樹等多種模型構(gòu)建與評(píng)估的R代碼并配套報(bào)告文檔便于理解規(guī)則集成模型如何實(shí)現(xiàn)約95%的R2解釋力。包體共4個(gè)文件含R腳本、CSV數(shù)據(jù)集、Markdown說(shuō)明及Word分析報(bào)告壓縮包大小1.71MB結(jié)構(gòu)精簡(jiǎn)適合直接運(yùn)行復(fù)現(xiàn)。目前已有256人學(xué)習(xí)。通過(guò)該資源可獲取完整R代碼、原始CSV數(shù)據(jù)集及分析報(bào)告適合課程設(shè)計(jì)、案例復(fù)現(xiàn)或作為回歸預(yù)測(cè)入門參考能幫助快速掌握模型對(duì)比、交叉驗(yàn)證與變量重要性評(píng)估的實(shí)操流程。1. 首爾自行車共享需求回歸從“每小時(shí)要備多少輛車”說(shuō)起首爾自行車共享需求數(shù)據(jù)是拿 R 做回歸分析入門到實(shí)戰(zhàn)之間最好的中間站——它有公開數(shù)據(jù)集、有時(shí)間結(jié)構(gòu)、有天氣和日歷混雜出的真實(shí)噪聲但又不至于像工業(yè)場(chǎng)景那樣臟到?jīng)]法收拾。標(biāo)題里的活兒其實(shí)就是一件事用 R 讀入按小時(shí)記錄的數(shù)據(jù)把氣溫、濕度、風(fēng)速、降雨、節(jié)假日這些特征組織起來(lái)訓(xùn)練一個(gè)能預(yù)測(cè)下一個(gè)小時(shí)需要多少輛自行車的回歸模型。適合兩類人一類剛學(xué)完線性模型想找個(gè)完整項(xiàng)目練手另一類在做共享出行調(diào)度、用戶消費(fèi)預(yù)測(cè)這類計(jì)數(shù)回歸場(chǎng)景想看看經(jīng)典回歸在真實(shí)業(yè)務(wù)數(shù)據(jù)上能承受到什么程度。先說(shuō)一個(gè)反直覺(jué)的結(jié)論這類按小時(shí)記錄的數(shù)據(jù)如果像普通回歸那樣隨機(jī)劃分訓(xùn)練集和驗(yàn)證集預(yù)測(cè)誤差會(huì)非常好看但一上線馬上翻車。原因是同一天的不同小時(shí)會(huì)被拆到兩邊模型等于提前“見過(guò)了當(dāng)天的天氣”。要測(cè)真實(shí)水平后面必須按時(shí)間拆。2. 數(shù)據(jù)長(zhǎng)什么樣理解字段與建模前處理這一步?jīng)Q定回歸天花板2.1 字段清單與目標(biāo)變量的“代理”性質(zhì)先看字段。這個(gè)數(shù)據(jù)集的記錄跨度約一年每行是一條小時(shí)級(jí)記錄目標(biāo)列是當(dāng)小時(shí)實(shí)際租出的自行車數(shù)。嚴(yán)格說(shuō)真正的“需求”不可觀測(cè)租出量只是需求的代理下雨天可能有人想騎但沒(méi)人出門租出量就會(huì)低估需求。業(yè)務(wù)解讀時(shí)要留這個(gè)心眼建模時(shí)則把租出量當(dāng)目標(biāo)即可。字段含義類型建模注意Date日期字符轉(zhuǎn)成 Date 類型拆出周幾、月份Rented Bike Count小時(shí)租出量數(shù)值目標(biāo)變量右偏帶大量 0Hour小時(shí) 0–23數(shù)值核心時(shí)間特征對(duì)需求量影響最大Temperature氣溫 ℃數(shù)值與需求呈倒 U 型考慮平方項(xiàng)Humidity濕度 %數(shù)值高濕常伴降雨但信息不重復(fù)Wind speed風(fēng)速 m/s數(shù)值強(qiáng)風(fēng)明顯壓制騎行需求Visibility能見度數(shù)值單位是 10m不是米建模前換算Dew point temperature露點(diǎn)溫度數(shù)值與濕度相關(guān)性高注意共線性Solar Radiation太陽(yáng)輻射 MJ/m2數(shù)值白天信號(hào)強(qiáng)與 Hour 有交互Rainfall降雨量 mm數(shù)值下雨需求驟降0 值有業(yè)務(wù)含義Snowfall降雪量 cm數(shù)值冬季氣候和溫度強(qiáng)相關(guān)Seasons季節(jié)類別轉(zhuǎn)成因子不要保留為文本Holiday是否節(jié)假日類別轉(zhuǎn)成 0/1Functioning Day是否運(yùn)營(yíng)日類別非運(yùn)營(yíng)日租出量恒為 0要單獨(dú)處理這里最容易忽略的是 Visibility 的單位。原始數(shù)據(jù)里能見度出現(xiàn)幾百甚至上千的數(shù)值直接當(dāng)米解釋會(huì)以為數(shù)據(jù)異常實(shí)際上單位是 10m1000 代表 10000 米。另一個(gè)是 Functioning Day它表示當(dāng)天系統(tǒng)是否運(yùn)營(yíng)等于 No 的日子租出量基本是 0這部分樣本混進(jìn)回歸會(huì)把均值往下拉后面專門講怎么處理。2.2 用 dplyr 完成清洗與時(shí)間特征構(gòu)造先讀數(shù)據(jù)再看實(shí)際列名。不同渠道下載的首爾自行車數(shù)據(jù)列名略有差異有的帶空格、有的帶括號(hào)和單位讀進(jìn)來(lái)之后 R 會(huì)自動(dòng)做 check.names 處理。我一般先打印一遍名字再統(tǒng)一改成短列名省得后面寫 formula 時(shí)到處加反引號(hào)。library(dplyr) bike_raw - read.csv(SeoulBikeData.csv, fileEncoding UTF-8) cat(names(bike_raw), sep \n) # 觀察實(shí)際列名按自己那份數(shù)據(jù)來(lái)對(duì) # 統(tǒng)一轉(zhuǎn)小寫把空格、括號(hào)、斜杠替換成下劃線便于后續(xù)引用 names(bike_raw) - names(bike_raw) %% tolower() %% gsub([^a-z0-9], _, .) %% gsub(_, _, .) %% sub(_$, , .) bike - bike_raw %% rename( rented rented_bike_count, temp temperature, hum humidity, wind wind_speed, vis visibility_10m, dew dew_point_temperature, solar solar_radiation_mj_m2, rainfall rainfall_mm, snowfall snowfall_cm )fileEncodingUTF-8是 Windows 上常見的坑不指定的話某些鏡像下載的 CSV 會(huì)用系統(tǒng)本地編碼讀取中文標(biāo)簽直接亂碼。正則清洗列名時(shí)注意gsub([^a-z0-9], _, .)會(huì)把所有非字母數(shù)字字符統(tǒng)一壓成下劃線雙下劃線再合并一次最后去掉末尾的下劃線。rename 里的新列名是后面所有代碼的基礎(chǔ)如果某份數(shù)據(jù)的列名對(duì)不上以cat(names())打出來(lái)的結(jié)果為準(zhǔn)改映射。接著構(gòu)造時(shí)間特征和業(yè)務(wù)啞變量。日期格式在源數(shù)據(jù)里是日/月/年as.Date的 format 必須寫%d/%m/%Y順手用 lubridate 的wday判斷周末避免依賴系統(tǒng) locale 的weekdays()。library(lubridate) bike - bike %% mutate( date as.Date(date, format %d/%m/%Y), hour as.numeric(hour), month as.numeric(format(date, %m)), is_weekend ifelse(wday(date, week_start 1) %in% c(6, 7), 1, 0), raining ifelse(rainfall 0, 1, 0), snowing ifelse(snowfall 0, 1, 0), holiday ifelse(holiday Yes, 1, 0), functioning ifelse(functioning_day Yes, 1, 0) )wday(date, week_start 1)返回 1–71 是周一6、7 是周六日這樣判斷與系統(tǒng)語(yǔ)言無(wú)關(guān)。holiday和functioning從字符轉(zhuǎn)成 0/1是因?yàn)楹竺娣胚M(jìn)lm()和glmnet時(shí)數(shù)值啞變量比字符因子更直觀也避免因子水平順序帶來(lái)的模型解釋困惑。2.3 檢查缺失值與分布順帶拆一次訓(xùn)練/驗(yàn)證清洗完先看 summary重點(diǎn)不是均值而是有沒(méi)有不可能出現(xiàn)的值、缺失值有沒(méi)有被 R 讀成 NA、目標(biāo)變量右偏到什么程度。小時(shí)租出量的典型分布是凌晨大量 0早晚高峰兩個(gè)峰整體右偏?;貧w模型對(duì)右偏目標(biāo)往往擬合不好第 3 章的對(duì)數(shù)變換就是為這個(gè)做準(zhǔn)備。summary(bike) hist(bike$rented, breaks 50, main 小時(shí)租出量分布) # 非運(yùn)營(yíng)日的租出量到底長(zhǎng)什么樣 bike %% filter(functioning 0) %% summarise(n n(), mean_rented mean(rented), max_rented max(rented))非運(yùn)營(yíng)日樣本的租出量均值接近 0這符合業(yè)務(wù)定義。處理策略我一般看建模目的如果目標(biāo)是預(yù)測(cè)運(yùn)營(yíng)狀態(tài)下的需求直接把非運(yùn)營(yíng)日過(guò)濾掉模型更干凈如果目標(biāo)是端到端預(yù)測(cè)“系統(tǒng)實(shí)際要承擔(dān)多少租出量”則保留 functioning 作為特征預(yù)測(cè)時(shí)輸入運(yùn)營(yíng)狀態(tài)即可。兩種做法沒(méi)有絕對(duì)對(duì)錯(cuò)但要在同一份代碼里保持一致不要建模時(shí)又混進(jìn)又剔除。拆訓(xùn)練/驗(yàn)證這一步強(qiáng)烈建議在清洗之后就做不要等模型調(diào)完再拆。關(guān)鍵點(diǎn)是按日期排序后切分而不是隨機(jī)抽樣dates - sort(unique(bike$date)) cut_idx - round(length(dates) * 0.8) train - bike %% filter(date dates[cut_idx]) test - bike %% filter(date dates[cut_idx])這里 0.8 是常見比例但對(duì)共享單車這類強(qiáng)周期業(yè)務(wù)我更推薦按“最后 30 天做測(cè)試”來(lái)切因?yàn)闃I(yè)務(wù)上線時(shí)預(yù)測(cè)的永遠(yuǎn)是未來(lái)不是過(guò)去。切分完成后后面所有特征工程和模型調(diào)參都只允許看 traintest 要一直留到最后一章做外推驗(yàn)證。3. 建立基線回歸用 lm() 跑通第一版靠殘差決定下一步3.1 特征怎么進(jìn)模型數(shù)值型、啞變量、交互項(xiàng)線性回歸的第一步不是把字段全塞進(jìn) formula而是想清楚每個(gè)特征的形態(tài)。溫度對(duì)騎行需求是典型的倒 U 型太冷不出門太熱也不騎所以加一個(gè)I(temp^2)平方項(xiàng)。Hour 本身是 0–23 的整數(shù)直接放進(jìn)線性模型會(huì)被當(dāng)成“越晚需求線性越低”實(shí)際上是早晚雙峰這里先用線性近似后面換樹模型時(shí)自動(dòng)處理非線性。降雨和降雪已經(jīng)轉(zhuǎn)成 0/1 啞變量保留原始降雨量數(shù)值會(huì)引入大量 0 值干擾。交互項(xiàng)我習(xí)慣先加兩個(gè)業(yè)務(wù)上確定的temp:hour表示不同時(shí)段對(duì)溫度的敏感度不同raining:is_weekend表示雨天在周末的影響可能比工作日更弱。交互項(xiàng)不要一上來(lái)加一堆否則 VIF 立刻爆表自己都分不清是共線性還是真信號(hào)。library(car) fit_lm - lm( rented ~ temp I(temp^2) hum wind vis solar raining snowing hour is_weekend holiday functioning temp:hour raining:is_weekend, data train ) summary(fit_lm) vif(fit_lm)vif()來(lái)自 car 包輸出大于 5 就要警惕大于 10 基本可以斷定這組變量在搶同一個(gè)信息。首爾數(shù)據(jù)里最容易爆的是露點(diǎn)溫度和濕度它倆物理含義高度重疊如果 VIF 高我一般先去掉 dew保留 hum因?yàn)闈穸葘?duì)騎行決策的解釋更直接。3.2 第一版線性模型與殘差診斷summary()里先看 F 統(tǒng)計(jì)量的 p 值再看每個(gè)變量的顯著性。第一次跑這個(gè)模型時(shí)你會(huì)發(fā)現(xiàn)溫度、濕度、小時(shí)、降雨系數(shù)全顯著但 Adjusted R2 可能只在 0.3–0.4 徘徊。別急著調(diào)參先畫殘差圖。par(mfrow c(2, 2)) plot(fit_lm)四張圖按順序讀Residuals vs Fitted 看有沒(méi)有喇叭口Normal Q-Q 看殘差是否正態(tài)Scale-Location 看方差的穩(wěn)定性Residuals vs Leverage 找影響點(diǎn)。這個(gè)數(shù)據(jù)集上最常見的形態(tài)是擬合值增大時(shí)殘差散開成漏斗形QQ 圖兩端明顯偏離直線。這是計(jì)數(shù)數(shù)據(jù)的典型異方差——租出量均值越高波動(dòng)越大線性模型強(qiáng)行用同一個(gè)方差去擬合導(dǎo)致小預(yù)測(cè)值被高估、大預(yù)測(cè)值被低估。殘差圖是黑匣子但它告訴你下一步該怎么走。兩個(gè)方向一是對(duì)目標(biāo)變量做對(duì)數(shù)變換讓方差更穩(wěn)二是換用泊松回歸或樹模型。泊松回歸在概念上更貼合計(jì)數(shù)數(shù)據(jù)但首爾這份數(shù)據(jù)存在過(guò)度離散直接glm(..., family poisson)會(huì)讓標(biāo)準(zhǔn)誤被低估。所以先把對(duì)數(shù)變換跑通再上正則化和樹模型。3.3 對(duì)數(shù)變換后重跑處理極端值log1p是log(x 1)的簡(jiǎn)寫專門處理含 0 的計(jì)數(shù)數(shù)據(jù)。直接log(0)會(huì)得到負(fù)無(wú)窮log1p把 0 映射成 0語(yǔ)義上說(shuō)得通深夜無(wú)人租車對(duì)數(shù)需求就是 0。train$log_rented - log1p(train$rented) fit_lm_log - lm( log_rented ~ temp I(temp^2) hum wind vis solar raining snowing hour is_weekend holiday functioning temp:hour raining:is_weekend, data train ) summary(fit_lm_log) hist(resid(fit_lm_log), breaks 50)對(duì)比兩次 summary你會(huì)看到對(duì)數(shù)模型的 R2 明顯上升殘差直方圖也更接近正態(tài)。但這不意味著模型真的更好只是目標(biāo)尺度變了評(píng)估指標(biāo)也跟著變。后續(xù)比較模型時(shí)一定要把預(yù)測(cè)值還原到原始租出量尺度再算 RMSE 或 MAE否則兩個(gè)模型根本不在一個(gè)比較維度上。還原時(shí)的常見錯(cuò)誤是直接exp(predict(...)) - 1。對(duì)數(shù)變換讓模型擬合的是對(duì)數(shù)空間的中位數(shù)還原后整體會(huì)系統(tǒng)性偏低。這個(gè)偏差在業(yè)務(wù)上很要命第 5 章專門講怎么用 smearing 修正。4. 把預(yù)測(cè)誤差壓下去glmnet 彈性網(wǎng)絡(luò)與隨機(jī)森林怎么選4.1 什么時(shí)候該從 lm 切到正則化模型線性模型跑通之后下一步不是立刻上深度學(xué)習(xí)而是先用正則化回歸和樹模型把誤差壓一壓。首爾數(shù)據(jù)里有幾個(gè)特征天生高度相關(guān)露點(diǎn)溫度與濕度、太陽(yáng)輻射與氣溫、風(fēng)速與能見度。lm 對(duì)這組相關(guān)特征非常敏感系數(shù)估計(jì)方差大換個(gè)訓(xùn)練集系數(shù)就漂移。glmnet 的彈性網(wǎng)絡(luò)elasticnet同時(shí)混入 L1 和 L2 懲罰L1 幫忙做變量選擇L2 幫忙穩(wěn)定相關(guān)變量的系數(shù)正好治這個(gè)病。在 R 里 glmnet 的alpha參數(shù)控制 L1/L2 比例alpha 1是 lassoalpha 0是 ridge中間值就是彈性網(wǎng)絡(luò)。默認(rèn)alpha 1很多人就這么用但對(duì)相關(guān)特征較多的數(shù)據(jù)純 lasso 會(huì)隨機(jī)挑一個(gè)代表變量丟掉另一個(gè)穩(wěn)定性差。我習(xí)慣把a(bǔ)lpha從 0 到 1 掃一遍讓cv.glmnet自己選。4.2 用 cv.glmnet 掃 alpha 并選 lambda 的完整命令glmnet的接口和lm最大的區(qū)別是它不接受 formula必須自己用model.matrix構(gòu)造特征矩陣。這一步有兩個(gè)坑一是因子列會(huì)自動(dòng)展開成啞變量二是展開結(jié)果自帶截距列喂給 glmnet 前要?jiǎng)h掉否則會(huì)跟 glmnet 內(nèi)部加的截距重復(fù)。library(glmnet) # 用 train 數(shù)據(jù)構(gòu)造特征矩陣-1 去掉截距列 x_train - model.matrix( ~ temp I(temp^2) hum wind vis solar raining snowing hour is_weekend holiday functioning temp:hour raining:is_weekend, data train )[, -1] y_train - train$log_rented # 掃 alpha對(duì)每個(gè) alpha 做 5 折交叉驗(yàn)證選 lambda alphas - seq(0, 1, by 0.1) cv_list - lapply(alphas, function(a) { cv.glmnet(x_train, y_train, alpha a, nfolds 5) }) # 取交叉驗(yàn)證誤差最小的那一組 best_idx - which.min(sapply(cv_list, function(m) min(m$cvm))) alpha_best - alphas[best_idx] fit_en - cv_list[[best_idx]] plot(fit_en) coef(fit_en, s fit_en$lambda.min)alphas按 0.1 步長(zhǎng)掃 11 個(gè)值對(duì)這份數(shù)據(jù)量完全夠用不用更細(xì)。cv.glmnet的nfolds 5是常見默認(rèn)數(shù)據(jù)量小可以改 10但這數(shù)據(jù)有近一年的小時(shí)記錄5 折足夠穩(wěn)定。lambda.min是交叉驗(yàn)證誤差最小的 lambdalambda.1se是誤差在一個(gè)標(biāo)準(zhǔn)誤之內(nèi)的最大 lambda系數(shù)更稀疏。我一般兩個(gè)都看一眼上線求穩(wěn)用lambda.1se競(jìng)賽刷指標(biāo)用lambda.min。plot(fit_en)畫的是不同 lambda 下系數(shù)收縮路徑能直觀看到哪些變量最后被壓成 0。coef(fit_en, s fit_en$lambda.min)輸出選定 lambda 下的系數(shù)如果某個(gè)特征系數(shù)是 0說(shuō)明它對(duì)預(yù)測(cè)沒(méi)有邊際貢獻(xiàn)。4.3 隨機(jī)森林補(bǔ)位做三模型橫評(píng)正則化回歸解決了共線性但解決不了特征的非線性和交互。Hour 的早晚雙峰、溫度倒 U 型這些 lm 要用平方項(xiàng)和交互項(xiàng)手工拼而樹模型天生能切分這種關(guān)系。R 里我優(yōu)先用 ranger 而不是 randomForest因?yàn)閿?shù)據(jù)量稍大時(shí) randomForest 慢到讓人懷疑人生ranger 在同等精度下快一個(gè)量級(jí)。library(ranger) fit_rf - ranger( log_rented ~ temp I(temp^2) hum wind vis solar raining snowing hour is_weekend holiday functioning temp:hour raining:is_weekend, data train, num.trees 500, mtry 5, importance impurity, seed 42 ) print(fit_rf) sort(fit_rf$variable.importance, decreasing TRUE)num.trees 500對(duì)這份數(shù)據(jù)足夠再高耗時(shí)增加明顯但精度提升有限。mtry 5是每個(gè)分裂節(jié)點(diǎn)隨機(jī)抽 5 個(gè)特征默認(rèn)是特征數(shù)的平方根這里特征展開后約 20 個(gè)默認(rèn) 4–5 都合理。importance impurity輸出的是 Gini 重要性計(jì)算快適合看特征排序permutation更準(zhǔn)但慢最后定稿時(shí)可以用。三模型橫評(píng)的骨架是這樣# 測(cè)試集同樣構(gòu)造特征矩陣 x_test - model.matrix( ~ temp I(temp^2) hum wind vis solar raining snowing hour is_weekend holiday functioning temp:hour raining:is_weekend, data test )[, -1] y_test - test$log_rented pred_lm - predict(fit_lm_log, newdata test) pred_en - predict(fit_en, s fit_en$lambda.min, newx x_test) pred_rf - predict(fit_rf, data test)$predictions calc_rmse - function(actual, pred) sqrt(mean((actual - pred)^2)) calc_mae - function(actual, pred) mean(abs(actual - pred)) # 在 log 尺度上橫評(píng)還原到原始尺度的偏差修正見第 5 章 sapply(list(lm pred_lm, enet pred_en, rf pred_rf), function(p) { c(rmse calc_rmse(y_test, p), mae calc_mae(y_test, p)) })經(jīng)驗(yàn)上在這類數(shù)據(jù)里隨機(jī)森林的 RMSE 通常明顯低于線性模型彈性網(wǎng)絡(luò)介于兩者之間但彈性網(wǎng)絡(luò)的優(yōu)勢(shì)是可解釋性和穩(wěn)定性——特征只有幾十個(gè)系數(shù)還能打印出來(lái)講給業(yè)務(wù)聽。樹模型一旦跑起來(lái)就是純黑匣子業(yè)務(wù)方問(wèn)“為什么這個(gè)小時(shí)預(yù)測(cè)這么高”你很難一句話講清楚。5. 避坑首爾自行車需求回歸里最常見的 5 個(gè)翻車點(diǎn)5.1 隨機(jī)抽樣導(dǎo)致預(yù)測(cè)虛高同一天的天氣被模型提前看到現(xiàn)象訓(xùn)練/驗(yàn)證用sample()隨機(jī)切分驗(yàn)證集 RMSE 低得喜人發(fā)布上線后每天預(yù)測(cè)都偏業(yè)務(wù)方直接質(zhì)疑模型沒(méi)學(xué)過(guò)當(dāng)天數(shù)據(jù)。原因小時(shí)級(jí)數(shù)據(jù)里同一天的 24 小時(shí)共享同一組天氣和節(jié)假日狀態(tài)。隨機(jī)切分會(huì)把同一天的一部分小時(shí)分進(jìn)訓(xùn)練集另一部分分進(jìn)驗(yàn)證集模型等于先看了當(dāng)天的天氣答案再去預(yù)測(cè)當(dāng)天剩下的小時(shí)信息泄露非常隱蔽。解決切分必須按日期整體切。用第 2 章的sort(unique(date))方法保證訓(xùn)練集日期全部早于驗(yàn)證集日期。頭條原則任何涉及時(shí)間序列的數(shù)據(jù)測(cè)試集日期必須全部在訓(xùn)練集之后這是回歸預(yù)測(cè)類項(xiàng)目的第一紀(jì)律。5.2 非運(yùn)營(yíng)日混進(jìn)回歸早上 8 點(diǎn)的預(yù)測(cè)值被系統(tǒng)性拉低現(xiàn)象模型整體 R2 還行但分時(shí)段看誤差時(shí)工作日早高峰的預(yù)測(cè)值普遍偏低而且偏低幅度穩(wěn)定。原因Functioning Day 等于 No 的日子租出量為 0這些樣本占比不小把回歸均值往下拉。模型學(xué)到的是“存在一整天完全沒(méi)人騎車的日子”但它不知道是哪天于是把這種不確定性攤到所有樣本上高峰期預(yù)測(cè)被拖低。解決按業(yè)務(wù)目標(biāo)二選一。只做運(yùn)營(yíng)日需求預(yù)測(cè)就在清洗時(shí)filter(functioning 1)做端到端租出量預(yù)測(cè)就保留 functioning 特征預(yù)測(cè)時(shí)顯式傳入運(yùn)營(yíng)狀態(tài)。最忌諱的是建模時(shí)不處理、解釋時(shí)又說(shuō)“非運(yùn)營(yíng)日本來(lái)就是 0”兩頭不靠。5.3 用 exp() 還原對(duì)數(shù)預(yù)測(cè)總量預(yù)測(cè)出現(xiàn)系統(tǒng)性低估現(xiàn)象把 log 模型的預(yù)測(cè)值exp()還原后分小時(shí)誤差看著還行但按天聚合的總量預(yù)測(cè)比實(shí)際少一到兩成。原因log1p建模后殘差在對(duì)數(shù)空間是零均值但exp()是非線性變換還原后殘差的均值不再為 0。exp(predict)得到的是對(duì)數(shù)空間的中位數(shù)不是原始尺度的均值。業(yè)務(wù)要的是總量中位數(shù)一定偏小。解決加一個(gè) smearing 修正項(xiàng)用訓(xùn)練集殘差計(jì)算mean(exp(resid))乘到預(yù)測(cè)值上再減 1smear - mean(exp(resid(fit_lm_log))) pred_original - exp(predict(fit_lm_log, newdata test)) * smear - 1這個(gè)修正項(xiàng)的意義是訓(xùn)練集上模型平均低估的比例就是未來(lái)預(yù)測(cè)要補(bǔ)償?shù)谋壤4鷥r(jià)是整體方差被放大一點(diǎn)但總量預(yù)測(cè)的偏差能明顯收回來(lái)。5.4 星期幾特征依賴系統(tǒng)語(yǔ)言換臺(tái)電腦結(jié)果就變現(xiàn)象同樣的代碼在 A 機(jī)器跑is_weekend判斷正常在 B 機(jī)器跑出來(lái)的周末標(biāo)記全反了模型結(jié)果對(duì)不上。原因基礎(chǔ) R 的weekdays()返回星期幾的文本這個(gè)文本由系統(tǒng) locale 決定。中文系統(tǒng)返回“星期六”英文系統(tǒng)返回“Saturday”如果代碼里寫weekdays(date) %in% c(Saturday, Sunday)在中文機(jī)器上永遠(yuǎn)是 FALSE。解決不要用weekdays()做判斷改用lubridate::wday(date, week_start 1)它返回?cái)?shù)字 1–7與語(yǔ)言無(wú)關(guān)。或者用format(date, %u)也能拿到 ISO 周幾數(shù)字。這類與環(huán)境相關(guān)的問(wèn)題最坑人因?yàn)樗辉谀愕拇a邏輯里而在運(yùn)行環(huán)境的配置里。5.5 把能見度原始數(shù)值當(dāng)米解釋異常值其實(shí)是單位問(wèn)題現(xiàn)象做異常值清洗時(shí)發(fā)現(xiàn) Visibility 有大量超過(guò) 1000 的“異常值”準(zhǔn)備一刀切刪除結(jié)果模型訓(xùn)練集和驗(yàn)證集分布對(duì)不上。原因這個(gè)數(shù)據(jù)集里 Visibility 的單位是 10m原始值 1000 表示 10000 米。把它當(dāng)米處理會(huì)以為晴天的能見度上萬(wàn)是數(shù)據(jù)錯(cuò)誤其實(shí)不僅正常而且是有用的天氣信號(hào)。雨雪天的能見度原始值會(huì)掉到幾百甚至以下這種差異對(duì)預(yù)測(cè)很有區(qū)分度。解決讀數(shù)據(jù)后先確認(rèn)字段單位能見度轉(zhuǎn)成公里再存vis_km vis / 100。刪除異常值前先看單位定義別讓單位問(wèn)題毀掉一個(gè)真正有效的特征。這類坑在公開數(shù)據(jù)集里尤其常見下載頁(yè)的說(shuō)明文檔和數(shù)據(jù)字典值得先讀一遍再動(dòng)手。6. 從模型到結(jié)論外推驗(yàn)證與特征效應(yīng)解讀6.1 按時(shí)間切分的驗(yàn)證與滾動(dòng)窗口隨機(jī)切分在第 5 章已經(jīng)否掉了但一次性按時(shí)間切也還不夠。首爾這個(gè)城市有明顯的季節(jié)周期春秋騎行量大、冬夏差很多只切一刀可能訓(xùn)練集里沒(méi)有見過(guò)足夠多的冬天樣本驗(yàn)證集又剛好落在冬天誤差會(huì)被高估。更穩(wěn)的做法是滾動(dòng)窗口模擬真實(shí)上線時(shí)“每周重訓(xùn)一次”的節(jié)奏。dates - sort(unique(bike$date)) # 以 30 天為窗口每天滾動(dòng)一天 for (i in 1:(length(dates) - 30)) { trn - bike %% filter(date dates[i], date dates[i 30]) tst - bike %% filter(date dates[i 30]) # 這里放你的模型訓(xùn)練代碼記錄當(dāng)天預(yù)測(cè)誤差到結(jié)果表 # err_day[i] - calc_rmse(tst$rented, pred) }這個(gè)循環(huán)跑起來(lái)會(huì)重訓(xùn)很多次模型計(jì)算量大但得到的是模型在一年各個(gè)季節(jié)里真實(shí)的外推表現(xiàn)。跑完后按月份聚合誤差如果 1 月和 7 月的誤差明顯高于春秋說(shuō)明模型還沒(méi)學(xué)到溫度極值對(duì)騎行需求的非線性壓制這就是下一步要加的交互項(xiàng)或新特征。6.2 用特征效應(yīng)而不是變量重要性解釋模型樹模型的變量重要性只告訴你哪些特征參與分裂多不告訴你怎么影響預(yù)測(cè)。Hour 重要性最高是因?yàn)樗言缤砀叻迩虚_了但你從重要性數(shù)字里看不出“早上 8 點(diǎn)需求爬升、下午 6 點(diǎn)另一個(gè)峰”。要把結(jié)論講給業(yè)務(wù)聽得看偏依賴圖。library(pdp) pd_hour - partial(fit_rf, pred.var hour, train train) plot(pd_hour, type b)partial計(jì)算的是固定其他特征為均值時(shí)Hour 變化帶來(lái)的預(yù)測(cè)均值變化。畫出來(lái)能看到清晰的早晚雙峰。這個(gè)圖就是調(diào)度業(yè)務(wù)最需要的那個(gè)結(jié)論早高峰備車、午間低谷、晚高峰再備一次。數(shù)據(jù)現(xiàn)象模型表現(xiàn)調(diào)度動(dòng)作工作日早 7–9 點(diǎn)需求峰Hour 偏依賴圖第一個(gè)峰早高峰前 1 小時(shí)從倉(cāng)庫(kù)調(diào)車到地鐵口降雨量 0 時(shí)需求驟降raining 系數(shù)為負(fù)且與周末交互顯著雨天減少路側(cè)調(diào)度轉(zhuǎn)入車輛檢修氣溫超過(guò) 30℃ 后需求回落temp 平方項(xiàng)負(fù)系數(shù)高溫天減少露天站點(diǎn)車輛避免暴曬損耗最后說(shuō)一個(gè)我自己的習(xí)慣模型定稿前強(qiáng)制自己看一眼按天聚合的誤差圖。如果誤差和氣溫、節(jié)假日出現(xiàn)清晰模式說(shuō)明還有信息沒(méi)進(jìn)模型如果誤差平穩(wěn)地繞零波動(dòng)才敢往上線走。這套流程在首爾自行車數(shù)據(jù)上成立換到用戶消費(fèi)預(yù)測(cè)、銷量預(yù)測(cè)這些同樣帶時(shí)間結(jié)構(gòu)的場(chǎng)景也一樣。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取