🛠 工作項目
1) 特徵工程(Python, 本機)
原始欄位 → 特徵:
grade(A/B/C/D → one-hot 或 ordinal: A=3,B=2,C=1,D=0)
direction(improves=+1, no_effect=0, worsens=-1)
magnitude(缺值補 0;可 winsorize ±1)
文獻密度:同食物在同 effect 下的證據數 n_refs
近期度(可選):取 reference/pmid_or_doi 年份 → recency
標籤(暫定弱監督):
用 Day 5 規則分數作為「教師分數」(回歸),或
用 top-k 食物標 1 其餘 0(分類)
產生 X.npy / y.npy 與 feature_schema.json
2) 訓練與匯出 ONNX
模型選項(擇一):
sklearn Logistic/Linear/RandomForest → skl2onnx 匯出
或 xgboost/lightgbm → onnxconverter-common 匯出
校驗:
hold-out 或簡單 K-fold;指標:RMSE(回歸)或 AUC/F1(分類)
產出:
models/reco_ranker.onnx (<10MB)
models/feature_schema.json(欄位順序/標準化規則)
3) Java 端整合(Spring Boot)
依賴:ai.onnxruntime:onnxruntime(CPU)
新增 MlScoringStrategy:
將候選食物的特徵照 feature_schema.json 組成 float[]/OnnxTensor
OrtEnvironment 單例;OrtSession 啟動時載入
score(items):呼叫 session.run(inputs) → 取輸出分數 → 組合排序結果
切換策略:
application.yml:scoring.mode=rule|ml(預設 rule)
ScoringStrategy 介面 + @ConditionalOnProperty 裝配
失敗回退:
若 ONNX 檔不存在 / 版本不符 / 推論拋例外 → 自動 fallback 到 rule
4) 效能與安全閥
啟動時預載模型 + 預熱一次
每請求推論時間統計(Micrometer 計 recommendations.ml_infer_ms)
模型健康檢查端點(Actuator info 替換:model name/version/hash)
限制批次尺寸:一次請求不超過 200 候選;超出則只取前 200 做 ML 排序
5) 測試與驗收
單元測試:MlScoringStrategyTest(fixture 特徵 → 預期分數)
整合測試:啟動 app → hit /api/recommendations?effectId=E002&mode=ml(或用設定檔切換)
基準測試(M4 Air):100 次請求平均延遲、P95、JVM 記憶體占用
📦 交付物
python/train_ranker_day11.ipynb(或 .py 腳本):特徵工程、訓練、匯出 ONNX
models/reco_ranker.onnx、models/feature_schema.json
Java:
service/scoring/MlScoringStrategy.java
service/scoring/RuleScoringStrategy.java(保留)
config/OnnxConfig.java(載入 OrtEnvironment/OrtSession)
application.yml 新增 scoring.mode、model.path
測試:
src/test/.../MlScoringStrategyTest.java
文件:
README_DAY11.md:如何再訓練與更換模型、如何切換/回退、效能指標查看
✅ 驗收標準
scoring.mode=ml 時,/api/recommendations 正常回應、排序與 rule 版不同(可見差異)
任何推論錯誤會自動回退 rule,不影響 API 可用性
本機 M4 Air:單請求 ML 推論時間 < 50ms(候選 ≤ 100)