中階課:用電子鼻預測品質指標

入門課把食品分成 fresh / spoiled;這一章往前一步,直接預測連續品質指標,例如微生物總生菌數 TVC、腐敗程度、游離脂肪酸 FFA 或摻偽比例。

y連續品質指標
PLS化學計量學回歸核心
RMSE預測誤差
解釋與預測能力
從分類到定量

不只判斷好壞,而是預測「品質數值」

分類模型回答的是「這個樣本屬於哪一類?」回歸模型回答的是「這個樣本的品質指標是多少?」在食品分析中,回歸通常更接近實務決策,因為它能提供連續尺度的風險或品質程度。

分類問題

fresh / acceptable / spoiled
適合快速篩檢與分級。

回歸問題

TVC = 6.2 log CFU/g
適合品質趨勢、保存期限與標準限值判斷。

品質指標

微生物數、FFA、pH、揮發性鹽基態氮、摻偽比例,都可以成為 y

本章核心問題:只看電子鼻感測器訊號 X,能不能預測牛肉腐敗相關的連續品質指標 y
本章資料

主範例:用牛肉 e-nose 資料預測 TVC

沿用入門課的牛肉新鮮度資料集,但這次不把 class 當目標,而是用 TVC 作為連續目標值。TVC 是 total viable count,代表微生物總生菌數,是肉品腐敗與食品安全的重要參考。

欄位在本章角色注意事項
MQ1 ... MQ10主要輸入特徵 X感測器對腐敗 VOCs 的反應。
humiditytemperature可選擇加入 X比較加入前後是否改善模型;也要思考是否代表環境偏差。
TVC回歸目標 y可能需要 log 轉換或確認單位。
class輔助解釋這次不是 y,但可用顏色標示預測圖。
minute輔助觀察不建議直接放入 X,避免模型只記住時間。

可延伸資料集

Cocoa butter FFAPork percentageWine spoilage threshold

同樣的回歸流程也可套用到可可脂 FFA 預測、牛/豬混合比例預測,或葡萄酒酸敗程度預測。差別只在於 y 換成不同的品質指標。

特徵工程

回歸模型最怕把「時間」誤當成「氣味」

如果資料依儲存時間排列,TVC 通常也會隨時間上升。這時候如果把 minute 放進模型,模型可能只是在學時間,而不是從氣味指紋預測品質。

建議特徵組 A

只用 MQ sensors。這是最乾淨的電子鼻指紋模型。

建議特徵組 B

MQ sensors + humidity + temperature。測試環境補償是否有效。

不建議一開始使用

minuteclass、樣本序號。這些欄位可能造成 data leakage。

誠實驗證:如果同一塊肉的連續時間點被隨機切到 train 和 test,測試結果可能過度樂觀。進階做法是依批次、樣本或實驗日分組切分。
方法核心

PLS regression:化學計量學最常用的品質預測工具

PLS(Partial Least Squares)會尋找一組 latent variables,讓 X 的變化能最大程度解釋 y。它特別適合感測器、光譜、質譜這類變數彼此高度相關的資料。

\[ X \rightarrow T \quad \text{and} \quad y \approx Tq \]
概念意思
Latent variables, LV把多個感測器壓縮成少數幾個與品質指標最相關的方向。
交叉驗證選 LV 數LV 太少會 underfit,LV 太多會學到雜訊。
Autoscaling感測器尺度不同時,PLS 前通常需要標準化。
Prediction plot用 measured vs predicted 檢查模型是否有系統性偏差。
Measured TVC Predicted TVC 理想線 y = x
回歸模型最重要的圖:Measured vs Predicted。點越接近對角線,預測越準。
模型評估

用 R²、RMSECV、RMSEP 判斷模型是否真的可用

回歸模型不能只看圖漂亮。你需要用交叉驗證和獨立測試集量化誤差,並確認模型在高品質與低品質區間都沒有明顯偏差。

模型解釋 y 變異的比例。越接近 1 越好,但高 R² 不代表沒有偏差。

RMSECV

交叉驗證誤差。用來選擇 LV 數與比較模型設定。

RMSEP

外部測試集誤差。最接近實際應用表現。

\[ RMSE=\sqrt{\frac{1}{n}\sum_{i=1}^{n}(y_i-\hat{y}_i)^2} \]
檢查項目你要看什麼
Residual plot殘差是否隨濃度變大?是否有曲線形狀?
LV selectionRMSECV 是否在某個 LV 後不再下降?
High TVC region腐敗高風險區是否被低估?
Train / test split切分方式是否符合真實應用情境?
Python 實作

PLS、SVR、Random Forest regression 的最小完整流程

1. 讀取資料並定義 X、y

import zipfile, requests
from pathlib import Path
import pandas as pd

DATA_DIR = Path("data")
DATA_DIR.mkdir(exist_ok=True)
url = "https://data.mendeley.com/public-api/zip/mwmhh766fc/download/2"
zip_path = DATA_DIR / "mendeley_beef_enose.zip"

if not zip_path.exists():
    r = requests.get(url, timeout=60)
    r.raise_for_status()
    zip_path.write_bytes(r.content)

with zipfile.ZipFile(zip_path) as z:
    z.extractall(DATA_DIR / "beef_raw")

csv_file = list((DATA_DIR / "beef_raw").rglob("*.csv"))[0]
df = pd.read_csv(csv_file)
print(df.shape)
print(df.columns.tolist())

# 依實際欄位微調:TVC 是本章的連續目標 y
exclude = {"TVC", "class", "minute", "time"}
feature_cols = [c for c in df.columns if c not in exclude]
X = df[feature_cols].apply(pd.to_numeric, errors="coerce")
y = pd.to_numeric(df["TVC"], errors="coerce")

mask = y.notna()
X = X.loc[mask].fillna(X.median(numeric_only=True))
y = y.loc[mask]
print(X.shape, y.describe())

2. 用交叉驗證選擇 PLS 的 LV 數

import numpy as np
from sklearn.model_selection import KFold, cross_val_predict
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.cross_decomposition import PLSRegression
from sklearn.metrics import mean_squared_error, r2_score

cv = KFold(n_splits=5, shuffle=True, random_state=42)
results = []

for n_lv in range(1, min(11, X.shape[1] + 1)):
    model = Pipeline([
        ("scaler", StandardScaler()),
        ("pls", PLSRegression(n_components=n_lv))
    ])
    y_cv = cross_val_predict(model, X, y, cv=cv).ravel()
    rmsecv = mean_squared_error(y, y_cv, squared=False)
    r2cv = r2_score(y, y_cv)
    results.append((n_lv, rmsecv, r2cv))

res = pd.DataFrame(results, columns=["LV", "RMSECV", "R2CV"])
print(res)
best_lv = int(res.loc[res.RMSECV.idxmin(), "LV"])
print("Best LV:", best_lv)

3. 外部測試集評估

from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.30, random_state=42
)

pls = Pipeline([
    ("scaler", StandardScaler()),
    ("pls", PLSRegression(n_components=best_lv))
])
pls.fit(X_train, y_train)
y_pred = pls.predict(X_test).ravel()

rmsep = mean_squared_error(y_test, y_pred, squared=False)
r2p = r2_score(y_test, y_pred)
print(f"RMSEP = {rmsep:.3f}")
print(f"R2 test = {r2p:.3f}")

plt.figure(figsize=(5.5,5.5))
plt.scatter(y_test, y_pred, alpha=0.8)
lo = min(y_test.min(), y_pred.min())
hi = max(y_test.max(), y_pred.max())
plt.plot([lo, hi], [lo, hi], "--", color="gray")
plt.xlabel("Measured TVC")
plt.ylabel("Predicted TVC")
plt.title("PLS regression: measured vs predicted")
plt.tight_layout()
plt.show()

4. 與非線性模型比較

from sklearn.svm import SVR
from sklearn.ensemble import RandomForestRegressor

models = {
    "PLS": Pipeline([("scaler", StandardScaler()), ("pls", PLSRegression(n_components=best_lv))]),
    "SVR-RBF": Pipeline([("scaler", StandardScaler()), ("svr", SVR(C=10, gamma="scale"))]),
    "Random Forest": RandomForestRegressor(n_estimators=300, random_state=42)
}

for name, model in models.items():
    model.fit(X_train, y_train)
    pred = np.asarray(model.predict(X_test)).ravel()
    print(name, "RMSEP", mean_squared_error(y_test, pred, squared=False), "R2", r2_score(y_test, pred))
Orange Data Mining

用 Orange 建立品質指標預測流程

Orange 可以做 regression。重點是在 Select Columns 中把 TVC 設成 Target,而不是把 class 設成 Target。

File
  → Select Columns
      Features: MQ sensor responses, humidity, temperature
      Target: TVC
      Meta: class, minute
  → Preprocess
      Impute / Standardize
  → Test and Score
       ← Linear Regression
       ← Random Forest
       ← Support Vector Regression
       ← PLS Regression(若已安裝相關 add-on;否則用 Python 做)
  → Predictions
  → Scatter Plot
      x = measured TVC, y = predicted TVC
  → Data Table
Orange 操作提醒:如果 Orange 沒有 PLS Regression widget,可以先用 Orange 做 SVR / Random Forest regression;PLS 則用 Python notebook 示範。
檢查自己

完成本章後,你應該能回答這些問題

觀念題

  1. 分類與回歸在食品品質分析中差在哪裡?
  2. 為什麼 PLS 適合感測器陣列資料?
  3. LV 太多會造成什麼問題?
  4. 為什麼 minute 可能造成 data leakage?

結果解讀題

  1. Measured vs predicted 圖是否接近對角線?
  2. RMSECV 最小時的 LV 數是多少?
  3. 高 TVC 區域是否被系統性低估?
  4. PLS、SVR、Random Forest 哪個最適合這組資料?為什麼?
進階延伸:如果你想從原始電子鼻曲線開始,自行萃取 max response、AUC、initial slope 等特徵,請接著看 進階課:Time-series Feature Engineering