Learning Objectives
- 把預測問題定義清楚,選擇評估指標與基準模型
- 對有時間順序的資料做時間切分,說明為什麼不能隨機切
- 做特徵工程時判斷哪些特徵預測當下真的拿得到
- 比較線性模型與樹模型,用 MAE、R² 與殘差解讀結果
- 把分析寫成包含限制的短報告
題目:明天每一站要準備幾台單車
這一課把整門課串起來做一個完整的專題。情境是:校園有四個單車站(圖書館、宿舍區、系館、校門口),管理單位想預測每一站每天的借用量,提早調度車輛。
動手前先把問題定義清楚:
| 問題 | 這個專題的選擇 |
|---|---|
| 預測什麼? | 某一站、某一天的借用次數(數值,所以是迴歸) |
| 什麼時候要用? | 預測未來的日子,不能用未來才知道的資訊 |
| 怎麼算好? | MAE(平均絕對誤差,單位是「次」,好解釋)、R²(比「猜平均」好多少) |
| 要贏過誰? | 基準模型:全體平均、各站平均 |
資料是 bikes.csv(程式產生的模擬資料,不是任何學校的真實資料):2026 年 9 月到 12 月,每天每站一列,共 488 列。所以這裡得到的都是這份資料裡的關聯,不能拿來推論真實的校園。
逐步看:先認識資料
建模之前先看資料:有沒有缺值、各站差多少、週末與考試週有什麼不同、時間上有沒有趨勢。
為什麼不能隨機切
前面的課都用 train_test_split 隨機切。但這個模型將來要預測的是未來,隨機切會把 12 月的資料混進訓練,測出來的其實是「把中間的空格補起來」:模型看過前一天和後一天,當然比較容易猜中間。
有時間順序的資料要用時間切分:
train = df[df["date"] < "2026-12-01"] # 用前面的日期訓練
test = df[df["date"] >= "2026-12-01"] # 用後面的日期測試
測試資料才像真實使用的情況,也會遇到訓練時沒看過的狀況。要在訓練資料內做交叉驗證挑參數時,同樣不能打亂時間,可以用 scikit-learn 的 TimeSeriesSplit(每一次都用前面的資料訓練、後面的資料驗證)。
逐步看:時間切分、隨機切分與基準
用同樣的欄位、同樣的隨機森林,比較兩種切分。station_code 把站名換成代碼,day 是學期第幾天。
特徵工程:先問預測當下拿不拿得到
探索時看到幾個現象,都可以變成特徵:
| 現象 | 特徵 |
|---|---|
| 各站的量不同 | 站別轉成 0、1 欄(one-hot) |
| 週末、考試週的影響每站不同 | 站別 × 週末、站別 × 考試週 |
| 氣溫和借用量不是一條直線 | 氣溫的平方 |
| 學期內有趨勢 | 學期第幾天 |
每一個特徵都要問:預測的當下拿得到嗎?拿不到卻放進去,就是資料洩漏:
- 不能用:當天的
rentals、當天其他站的借用量、用全部資料(含測試期間)算出來的平均。 - 可以用:星期幾、是否考試週(行事曆早就公布)、前幾天的借用量。
- 要小心:
temp_max、rain_mm這份資料裡是實際發生的天氣。真的預測明天時只有天氣預報,有誤差,所以這裡的成績會比實際使用時樂觀。
逐步看:一步一步做特徵
只取前 8 列,看每一步新增了哪些欄。下一個範例會把這些寫成一個函數。
逐步看:比較三種模型
同樣的時間切分、同樣的特徵,比較線性迴歸、決策樹(深度 6)與隨機森林;最後一行是線性迴歸只用原始欄位。輸出的兩個數字是 MAE 與 R²。
怎麼解讀這張成績表
把結果整理在一起(都是 12 月的測試資料):
| 模型 | MAE(次) | R² |
|---|---|---|
| 全體平均(基準) | 48.6 | −0.406 |
| 各站平均(基準) | 44.1 | −0.034 |
| 線性迴歸(原始欄位) | 34.2 | 0.309 |
| 決策樹,深度 6(特徵工程) | 20.9 | 0.644 |
| 線性迴歸(特徵工程) | 16.4 | 0.825 |
| 隨機森林(原始欄位+站別代碼、第幾天) | 12.9 | 0.887 |
| 隨機森林(特徵工程) | 12.8 | 0.889 |
可以讀出幾件事:
- 都贏過基準,模型確實學到了東西。
- 特徵工程對線性模型的幫助很大,因為線性模型自己畫不出彎曲與交互作用;對隨機森林幾乎沒有差別(12.9 和 12.8),樹本來就能切出這些關係。
- 隨機森林最好,但線性迴歸的係數比較容易解釋與檢查。差距值不值得犧牲可解釋性,要看用途。
- 樹模型不會外插:超出訓練範圍的值(學期第 91 天之後、低於 24.4 度)都落在邊緣的葉子,預測不會超出訓練時看過的範圍。測試資料有新的低溫時要留意。
注意:這張表是用同一份測試資料比較很多模型。如果據此挑出最好的,它的成績會略為偏樂觀。正確的流程是在訓練資料內用 TimeSeriesSplit 選模型,選定後才看一次測試資料。
逐步看:檢查殘差,找出模型錯在哪裡
殘差(residual)=實際值 − 預測值。平均指標只告訴你「錯多少」,殘差告訴你「錯在哪裡」。這裡看線性迴歸,因為它的錯誤比較容易解釋。
從殘差得到的改進方向
12 月 27 日是考試週的星期日。這個模型把「考試週的增量」和「週末的減量」各自加起來,但訓練資料裡考試週的週末只有 11 月 7 日、8 日兩天,模型沒有機會學到兩者同時發生時的情況。
從這裡可以提出幾個接下來可以試的方向(是否有效要實測):
- 加上「考試週 × 週末」這個特徵;
- 預測
log(借用量),讓週末、考試週的影響變成倍數而不是加減; - 加入「前幾天同一站的借用量」這類預測當下拿得到的特徵;
- 蒐集更多學期的資料,讓訓練資料包含更多考試週與低溫的日子。
寫成短報告
做完分析要寫得讓別人看得懂、知道能不能信。短報告至少包含:問題、資料、方法、結果、限制。以上面的結果為例:
校園單車借用量預測(模擬資料)
問題:預測四個單車站每天的借用量,供調度使用。
資料:
bikes.csv,程式產生的模擬資料,2026 年 9 月到 12 月,4 站 × 122 天,共 488 筆,沒有缺值。方法:以 12 月 1 日為界做時間切分,訓練 364 筆、測試 124 筆。特徵包含站別、站別與週末及考試週的交互作用、氣溫與其平方、雨量、學期第幾天,共 16 個。以各站平均為基準,比較線性迴歸、深度 6 的決策樹與隨機森林。
結果:隨機森林的 MAE 是 12.8 次、R² 是 0.889;線性迴歸 16.4 次、0.825;基準 44.1 次、−0.034。殘差顯示線性迴歸系統性地預測太高(平均 −11.0 次),在考試週的週末最明顯。
限制:只切一次,測試資料只有 31 天,而且同時用來比較模型,數字略為偏樂觀。氣溫與雨量用的是實際值,實際預測時只有天氣預報。資料是模擬的,結果不能推論到真實校園。訓練資料最低氣溫 24.4 度,高於測試資料的 20.3 度。
寫報告時可以檢查:數字是否都來自程式輸出?有和基準比較嗎?有說明切分方式與限制嗎?有把關聯寫成因果嗎?
實作:換一個切分點,重做一次分析
訓練期間拉短:以 2026-11-16 為界(這天以前訓練,這天起測試)。完成三件事:
- 切分訓練與測試資料;
- 完成
features(d):站別的 0、1 欄、站別 × 週末、temp_max、rain_mm、is_exam、氣溫平方、學期第幾天; - 用
report印出各站平均(基準)、線性迴歸、隨機森林(random_state=0)的成績。
跑出來之後比較:測試期間變長、訓練資料變少,和範例的 12 月切分點相比,成績有什麼變化?試著照上面的格式寫一段「結果」與「限制」。
小測驗:專題的判斷
要預測未來每天的借用量,為什麼不該用隨機切分來評估?
模型的 R² 是 −0.03,代表什麼?
下面哪一個特徵,在預測明天的借用量時會造成資料洩漏?
下面程式算出 MAE,會印出什麼?
import numpy as np
y = np.array([10, 20, 30])
p = np.array([12, 18, 33])
print(round(float(np.abs(y - p).mean()), 2))
殘差定義為「實際 − 預測」,測試資料的平均殘差是 −11。怎麼解讀?
報告裡寫「氣溫下降使借用量增加」。依這個專題的資料與方法,問題在哪裡?
重點整理
做一個機器學習專題的順序:
- 定義問題:預測什麼、什麼時候用、怎麼算好、基準是什麼。
- 認識資料:缺值、分布、趨勢、各組差異。
- 切分:依照實際使用情況切。要預測未來就用時間切分。
- 特徵工程:先問預測當下拿不拿得到,避免資料洩漏。
- 建立基準,比較模型:同樣的切分、同樣的指標;線性模型靠特徵工程,樹模型比較不需要,但不會外插。
- 檢查殘差:找出系統性的錯誤,提出下一步。
- 寫報告:數字來自程式輸出,一定要有和基準的比較與限制。
機器學習軌到這裡結束。你已經從最簡單的線性迴歸一路走到神經網路,也練習了最重要的習慣:和基準比較、看沒看過的資料、誠實寫出限制。接下來可以用你自己關心的資料走一次同樣的流程;想了解生成式 AI 的原理與使用的倫理,可以繼續看「AI 素養」軌。