電子鼻不是「聞到某一種味道」,而是記錄一組氣味指紋
想像你把一塊牛肉放進密閉容器中。牛肉會釋放出許多揮發性化合物,尤其在變質過程中,氣味會明顯改變。電子鼻會用一排氣體感測器同時量測這些變化。
每個感測器都不是完全專一的。它可能同時對酒精、硫化物、胺類或其他 VOCs 有反應。這聽起來像缺點,但多個感測器合在一起時,就會形成一個模式。這個模式就是我們要分析的「氣味指紋」。
你會看到什麼
一張資料表:每一列是一個量測時間點,每一欄是一個感測器或環境變數。你要先做什麼
把資料整理成X 和 y。X 是感測器反應,y 是品質類別。你最後要回答什麼
只看電子鼻訊號,我們能不能判斷牛肉是新鮮、可接受,還是已經腐敗?牛肉新鮮度 electronic nose dataset
我們使用公開資料集:Electronic nose dataset for beef quality monitoring under an uncontrolled environment。研究者用 MOS 氣體感測器監測牛肉在儲存過程中的氣味變化,並提供品質類別與微生物數量。
資料表怎麼讀?
先把資料想成這樣:
minute | class | TVC | MQ1 | MQ2 | MQ3 | ... | MQ10 | humidity | temperature
| 欄位 | 意思 | 這一章怎麼用 |
|---|---|---|
minute | 量測時間 | 先保留觀察,但不要直接放進分類模型。否則模型可能只是在記住時間。 |
class | 品質類別 | 這是我們要預測的答案,也就是 y。 |
TVC | 微生物數量 | 本章先不用。之後可以拿來做 PLS regression。 |
MQ1 ... MQ10 | 氣體感測器反應 | 這是主要輸入變數,也就是 X。 |
humidity、temperature | 濕度與溫度 | 可以納入模型,也可以先拿掉,比較結果是否改變。 |
class 不能出現在 X 裡。另一個很適合練習的資料:牛肉中豬肉摻偽
如果你對食品真偽分析有興趣,可以再看這個資料集:The Dataset for Pork Adulteration from Electronic Nose System。它有七個牛肉 / 豬肉混合比例,從 100% beef 到 100% pork。這種資料很適合用 PCA 看比例梯度,也適合用 PLS-DA 做摻偽判別。
食品真偽摻偽檢測halal authenticationPLS-DA
先整理資料,再開始建模
電子鼻資料不能一拿到就丟進模型。不同感測器的數值範圍可能差很多,有些欄位也不是模型應該看的資訊。前處理做得不好,模型看起來可能很準,但其實學到的是錯的東西。
第 1 步:決定 X 和 y
X 放感測器反應;y 放品質類別。不要把 class、TVC、minute 混進 X。第 2 步:檢查缺失值
如果某些欄位有空值,可以刪除、補中位數,或回到原始資料確認原因。第 3 步:標準化
對每一個 sensor 欄位做 autoscaling,讓不同尺度的感測器可以公平比較。這個公式的意思是:對每一個感測器欄位,先減掉平均值,再除以標準差。做完後,每個變數的平均值會接近 0,標準差會接近 1。
如果你拿到的是原始時間序列
有些 e-nose 資料不是一列代表一個樣本,而是一個樣本有一整段反應曲線。這時候可以先把曲線轉成特徵,再分析。
| 從曲線取出的特徵 | 你可以怎麼理解 |
|---|---|
| baseline | 放入樣本前的背景反應。 |
| maximum response | 感測器反應最高點。 |
| steady-state mean | 最後一段時間的平均反應。 |
| AUC | 整段反應曲線下面積,代表總反應量。 |
| initial slope | 反應剛開始上升的速度。 |
用 PCA 先看資料長什麼樣子
PCA 是非監督式方法。它不看 class,只看感測器資料 X。如果不同品質的樣本在 PCA 圖上自然分開,代表電子鼻訊號中真的有品質差異的資訊。
Scores:樣本的位置
每一個點是一筆樣本。你可以看 fresh、acceptable、spoiled 是否聚在不同區域。Loadings:感測器的貢獻
哪個 sensor 對 PC1 或 PC2 影響大,就可能是造成樣本分離的重要訊號。看 PCA 圖時,請試著回答
- 不同品質類別有沒有分開?還是混在一起?
- 有沒有離群點?它可能是量測錯誤,還是真的特殊樣本?
- PC1 可能代表什麼?腐敗程度、時間、溫度,還是其他因素?
- 如果 humidity 在 loading 中很重要,你會把它視為有用資訊還是干擾?
建立分類模型:讓電腦判斷食品品質
PCA 幫我們看出資料結構,但 PCA 本身不是分類器。若要預測一個未知樣本是 fresh 還是 spoiled,需要建立 supervised classification model。
kNN
看未知樣本附近的鄰居屬於哪一類。概念最直覺。SVM
尋找能分開類別的邊界。常用於感測器與光譜資料。Random Forest
由多棵決策樹投票。可以觀察哪些變數重要。PLS-DA
化學計量學常用的分類方法。用 latent variables 連結 X 與類別 y。不要只看 accuracy
食品安全問題中,錯誤的代價不一樣。把 fresh 判成 spoiled 可能造成浪費;把 spoiled 判成 fresh 可能造成健康風險。因此你一定要看 confusion matrix。
| 真實情況 | 模型預測 | 可能後果 |
|---|---|---|
| fresh | spoiled | 食品被丟掉,造成浪費。 |
| spoiled | fresh | 食品安全風險最高,這種錯誤要特別避免。 |
一步一步做出 PCA 與分類模型
下面的程式碼可以放進 Jupyter Notebook。你可以先跑通流程,再慢慢修改特徵欄位、分類器與驗證方式。
1. 下載與讀取資料
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_files = list((DATA_DIR / "beef_raw").rglob("*.csv"))
df = pd.read_csv(csv_files[0])
print(df.shape)
df.head()
2. 定義 X 和 y
print(df.columns.tolist())
label_col = "class"
exclude = {"class", "TVC", "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 = df[label_col]
X = X.fillna(X.median(numeric_only=True))
print(X.shape)
print(y.value_counts())
3. PCA score plot
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import seaborn as sns
X_scaled = StandardScaler().fit_transform(X)
pca = PCA(n_components=2, random_state=42)
T = pca.fit_transform(X_scaled)
plt.figure(figsize=(7,5))
sns.scatterplot(x=T[:,0], y=T[:,1], hue=y, s=45, alpha=0.85)
plt.xlabel(f"PC1 ({pca.explained_variance_ratio_[0]*100:.1f}%)")
plt.ylabel(f"PC2 ({pca.explained_variance_ratio_[1]*100:.1f}%)")
plt.title("PCA score plot of beef e-nose data")
plt.tight_layout()
plt.show()
4. 訓練分類模型
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.metrics import classification_report, ConfusionMatrixDisplay
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.30, stratify=y, random_state=42
)
models = {
"SVM-RBF": Pipeline([
("scaler", StandardScaler()),
("clf", SVC(kernel="rbf", C=10, gamma="scale"))
]),
"Random Forest": RandomForestClassifier(n_estimators=300, random_state=42)
}
for name, model in models.items():
model.fit(X_train, y_train)
pred = model.predict(X_test)
print("\n===", name, "===")
print(classification_report(y_test, pred))
ConfusionMatrixDisplay.from_predictions(y_test, pred, xticks_rotation=45)
plt.title(name)
plt.tight_layout()
plt.show()
如果你想試 PLS-DA
from sklearn.cross_decomposition import PLSRegression
from sklearn.preprocessing import LabelEncoder, OneHotEncoder, StandardScaler
from sklearn.model_selection import StratifiedKFold, cross_val_predict
from sklearn.metrics import accuracy_score, confusion_matrix
le = LabelEncoder()
y_int = le.fit_transform(y)
Y = OneHotEncoder(sparse_output=False).fit_transform(y_int.reshape(-1, 1))
Xs = StandardScaler().fit_transform(X)
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
y_score = cross_val_predict(PLSRegression(n_components=2), Xs, Y, cv=cv)
y_pred = y_score.argmax(axis=1)
print("CV accuracy:", accuracy_score(y_int, y_pred))
print(confusion_matrix(y_int, y_pred))
print(le.classes_)
不用寫太多程式,也能完成同樣流程
Orange 的好處是你可以直接看到資料流。每一個 widget 都對應到一個分析步驟:讀資料、選欄位、標準化、PCA、模型比較、混淆矩陣。
PCA 流程
File
→ Select Columns
Features: MQ sensor responses, humidity, temperature
Target: class
Meta: minute, TVC
→ Preprocess
Impute / Standardize
→ PCA
→ Scatter Plot
x = PC1, y = PC2, color = class
→ Data Table
分類流程
File
→ Select Columns
→ Preprocess
→ Test and Score
← kNN
← Logistic Regression
← SVM
← Random Forest
→ Confusion Matrix
→ ROC Analysis
試一次:只用感測器
Features 只放 MQ sensor responses。記下 PCA 分群與分類結果。再試一次:加入溫濕度
把 humidity 和 temperature 加入 Features。看看結果變好還是變差,並思考原因。完成後,你應該能回答這些問題
觀念題
- 為什麼電子鼻需要多變量分析?
- PCA 為什麼是非監督式方法?
- autoscaling 對 PCA 有什麼影響?
- 為什麼不能把
class放進X?
結果解讀題
- PCA score plot 中,fresh 和 spoiled 是否分開?
- 哪一個分類模型表現最好?你怎麼判斷?
- confusion matrix 中,哪一種錯誤最危險?
- 加入溫濕度後,模型是否更可靠?為什麼?