公開食品光譜資料 × 可重現教學

從 Mendeley Food FTIR spectra 開始學 2T2D-COS

這一章會帶你把一維食品 FTIR 光譜整理成 sample × wavenumber 矩陣,先做 PCA / PLS-DA 的基礎判別,再進一步把光譜轉成 two-trace 2D correlation maps,作為機器學習或影像模型的輸入。

Dataset first

先認識這個 Mendeley 食品 FTIR 資料包

資料來源是 Data for: Spectra Data Classification with Kernel Extreme Learning。它不是專門為 2T2D-COS 建立的資料集,但因為每個樣本都有完整 FTIR spectrum 與類別標籤,非常適合用來教「從一維光譜生成 2D correlation image」。

Instant coffee

56 個 MIR-DRIFT spectra;Arabica vs Robusta。最適合第一個示範,資料小、類別清楚。

classificationcoffee authentication

Olive oil

120 spectra,來自 60 個 extra virgin olive oils,每個樣本有 duplicate acquisition。適合討論 replicate 與產地判別。

replicatesorigin

Fruit puree

983 個 ATR-FTIR spectra;Strawberry vs non-strawberry / adulterated puree。適合進階分類與 spectral-image model。

adulterationlarge n
建議起點:先用 FTIR_Spectra_instant_coffee.csv。完成一次完整流程後,再換 fruit puree 做較大的食品摻偽案例。
Real figures

先看真實 FTIR 圖,再進入 2T2D-COS

以下圖形直接由 Mendeley coffee FTIR CSV 重新計算產生,不是示意圖。可作為參考答案,回到 Python / Orange 重算即可確認是否得到相同趨勢。

Real FTIR spectra overlay for Arabica and Robusta instant coffee
圖 1|原始光譜疊圖。藍色為 Arabica、橘色為 Robusta;淡線是單一樣本,粗線是類別平均。這張圖可用來討論資料變異與分類訊號是否肉眼可見。
Class mean spectra and Robusta minus Arabica difference
圖 2|類別平均與差異光譜。下方差異圖標示 Robusta 相對 Arabica 較高或較低的區域,適合連結到 loading / feature importance。
PCA score plot of instant coffee FTIR spectra
圖 3|PCA score plot。使用標準化後的 286 個波數變數;PC1 解釋約 91.1% 變異,線性 SVM 5-fold CV 約 0.98 ± 0.04,可作為一維光譜 baseline。
Coffee FTIR difference spectrum with numbered chemical interpretation regions
圖 4|差異訊號的化學意義。圖中的 1–8 是咖啡 FTIR fingerprint region 的主要判讀區。這些指派是教學用的 tentative assignment,因為咖啡是複雜混合物,單一波段通常同時受到 caffeine、chlorogenic acids、trigonelline、carbohydrates、lipids 與 roasting/Maillard products 影響。
編號
波數區間
主要官能基 / 振動
與咖啡成分的關聯
1
1710–1755 cm⁻¹
C=O stretch:ester、carboxylic acid、lipid carbonyl
常連到 chlorogenic acids 的 ester / acid carbonyl,也可能包含咖啡油脂或氧化產物的 carbonyl 訊號。若此區差異明顯,可討論酸類、酯類與脂質組成差異。
2
1585–1665 cm⁻¹
芳香環 C=C、共軛 C=O、N-containing ring vibration
文獻常把 1700–1600 cm⁻¹ 區域與 chlorogenic acids、caffeine 相關聯;也會受 roasting 後褐變產物與含氮雜環影響。這是 Arabica / Robusta 判別常值得看的區域。
3
1515–1560 cm⁻¹
芳香環 / amide-like / heterocyclic ring modes
可能反映 caffeine、trigonelline、蛋白質殘基或 Maillard products 的環狀結構與含氮訊號;不宜只指定單一化合物。
4
1400–1460 cm⁻¹
CH₂ / CH₃ bending、O–H deformation
與 carbohydrates、lipids、alkaloids 的 C–H bending 有關;可連到咖啡多醣、脂質與 caffeine methyl groups 的整體差異。
5
1325–1385 cm⁻¹
C–H bending、phenolic O–H / C–O modes
可與 caffeine methyl bending、chlorogenic acids / phenolics、trigonelline 等成分的 fingerprint vibration 有關。
6
1180–1265 cm⁻¹
C–O stretch:ester、phenolic acid、C–O–C
常見於 chlorogenic acids、esters、phenolic compounds,也可能與 sucrose / polysaccharides 的 C–O 伸縮重疊。
7
1000–1150 cm⁻¹
C–O / C–C stretch、glycosidic vibration
主要是 carbohydrates fingerprint:sucrose、polysaccharides、cell-wall carbohydrates。Arabica / Robusta 在糖類與烘焙產物比例不同時,此區會影響 PCA 與 2T2D pattern。
8
890–980 cm⁻¹
carbohydrate ring vibration、C–H deformation
屬於低波數 fingerprint 區,常與醣類環狀振動、多醣結構和烘焙後基質差異相關,適合作為分類訊號但不適合過度單一化合物指認。
判讀重點:這個資料的差異光譜多數區域為 Arabica mean 較高,但這不等於「Arabica 的某單一成分一定較高」。FTIR 是重疊訊號;較穩健的說法是:Arabica / Robusta 的判別主要來自 carbonyl / aromatic / carbohydrate fingerprint 等區域的整體組成差異。若要宣稱 caffeine、chlorogenic acids 或 trigonelline 的濃度高低,應搭配 HPLC、LC-MS 或標準品校正。參考:Downey et al. (1997) coffee varietal identification 資料來源;Sahachairungrueng et al. (2022) 指出 coffee FTIR/NIR 訊號包含水分、chlorogenic acid、caffeine、trigonelline 與 carbohydrates;Ribeiro et al. (2012) 指出 1700–1600 cm⁻¹ 與 coffee 中 chlorogenic acids、caffeine 相關。

參考比對|FTIR 咖啡鑑別課

已完成的一維 FTIR × PCA / PLS-DA 教學頁,可作為本頁 2T2D-COS 前的 baseline:先看原始 FTIR 光譜如何做 Arabica vs Robusta 鑑別,再比較 2T2D 影像化後多了哪些 band-to-band pattern。

參考比對|GC 咖啡品種課

GC 層析指紋同樣用 Arabica vs Robusta 作為食品真偽案例。可拿來對照:FTIR 是官能基 / 整體組成 fingerprint;GC 是揮發性或層析峰型 fingerprint,兩者的化學選擇性與特徵解釋方式不同。

參考比對|PGMM 咖啡化學成分

R 套件 pgmm 的經典 coffee 資料(Streuli:43 支咖啡 × 12 項化學/物理成分,Arabica vs Robusta),以 model-based clustering(PGMM)分群。與本頁互補:FTIR 是光譜指紋,PGMM 是化學成分變數+機率式分群,可比較「光譜 vs 成分」「判別 vs 非監督分群」的差異。

課堂比較問題

同樣是 coffee authentication,FTIR、GC、PGMM 成分、2T2D-COS 分別回答不同問題:FTIR 看官能基區域,GC 看分離後峰型,PGMM 看化學成分的機率式分群,2T2D-COS 看光譜區域之間的相關圖樣。可比較哪一種最容易解釋、哪一種最適合快速篩檢。

Three-way comparison computed with SpectraView: difference outer product vs real Noda 2T2D vs generalized 2D-COS on coffee FTIR
圖 5|用 SpectraView 重算的三方對照(真實 Briandet 咖啡資料、SNV 前處理)。上排同步 Φ、下排異步 Ψ。(A) two-trace 先減平均(本頁早期版本的做法)=差異光譜的外積:同步圖 100% rank-1、異步圖完全空白(退化),等於把那張 1D 差異光譜換成 2D 畫法,並沒有多出資訊。(B) SpectraView 的真 2T2D(Noda,不減平均)異步圖非退化(async/sync≈0.15),這才是「兩跡相關」真正多給的東西。(C) 廣義 2D-COS:需要一條擾動序列(此處為模擬烘焙的多製程示範),同步圖 rank>1、異步圖解析出譜帶變化的先後順序——這才是 2D-COS 的威力所在。
課堂使用方式:先用 Python 重現圖 1–3;確認資料讀取、標準化、PCA 都正確後,再重現圖 5。若圖 5 顏色方向相反,通常是 reference trace 或差異定義相反,不一定代表計算錯誤。

三方統整:真實成分(pgmm)→ FTIR 吸收帶 → 2D 差異圖

同一批 Arabica / Robusta 咖啡,FTIR 鑑別課用 R 套件 pgmmcoffee 真實化學成分(43 支、12 項)做 PCA,Arabica / Robusta 一樣自己分兩群(PC1 37.7%)。把「量到的成分差異」對到「它的 FTIR 吸收帶」,再對到本頁的 FTIR 差異光譜(圖 4)與 2D 圖(圖 5):

成分(pgmm 量測)
哪邊較高
對應 FTIR 吸收帶
在差異 / 2D 圖怎麼看
脂肪 Fat(15.4% vs 9.1%)
Arabica
酯基 C=O ~1745、C–H ~2920 / 2855 cm⁻¹
~1745 區的差異有脂肪貢獻(DRIFT 800–1900 看不到 C–H stretch)
咖啡因 Caffeine(1.2% vs 1.9%)
Robusta
C=O / C=N ~1700–1600 cm⁻¹
1700–1600 區 Robusta↑ 的訊號之一
綠原酸類 Chlorogenic acids
Robusta
芳香 C=C、C–O ~1600–1000 cm⁻¹
1600–1000 廣域差異
葫蘆巴鹼 Trigonelline
Arabica
環 C=N / C–N ~1600–1500 cm⁻¹
1600–1500 區 Arabica↑ 的貢獻
碳水化合物 / 醣類
1007 / 957 / 903 cm⁻¹(C–O)
低波數 PLS-DA 主要鑑別波段
關鍵連結:1600–1750 cm⁻¹ 這一段同時疊了脂肪酯 C=O(Arabica↑)與咖啡因 / 綠原酸的 C=O/C=N(Robusta↑),所以 FTIR 差異光譜(圖 4)在這裡是多成分的淨效果、不是單一分子——這正是把它畫成「two-trace 同步圖」(圖 5 A)必然 rank-1、只呈現「整體組成 fingerprint」差異的原因。要回答「是哪一個分子讓某條吸收帶變化」,需要 pgmm 量到的成分(或 HPLC / LC-MS),FTIR 本身分不開。換句話說:pgmm 給「成分真值」、FTIR 給「重疊指紋」、2D 同步圖只是把這個重疊指紋差異換成 2D 畫法;真正多給資訊的是異步圖(圖 5 B)與擾動序列的廣義 2D-COS(圖 5 C)。
Concept

2T2D-COS 在這裡扮演什麼角色?

從一維到二維

原始 FTIR 是一條曲線:每個 wavenumber 對應一個吸收強度。2D 相關把光譜轉成 wavenumber × wavenumber 的 correlation map,呈現哪些譜帶一起變動。

關鍵:兩跡 vs 擾動序列

只有「樣本 vs 類別平均」兩條光譜時,同步圖其實就是差異光譜的外積(rank-1),資訊和 1D 差異相同。要看到「1D 看不到的 band covariation」,需要一系列隨擾動(烘焙時間、摻配比例…)變化的光譜做廣義 2D-COS。

三種「2D 相關」的差別(皆以 SpectraView 驗證)

差異外積
原頁面 two-trace
兩跡先減平均 → 同步=½(sample−ref)⊗(sample−ref),rank-1;異步恆為 0(退化)。本質是 1D 差異光譜的 2D 版(圖 5 A)。
真 2T2D
Noda 2018
不減平均:Φ=½(yₐ⊗yₐ+y_b⊗y_b)、Ψ=½(yₐ⊗y_b−y_b⊗yₐ)。異步非退化,兩條光譜也能得到相位/相對關係(圖 5 B)。
廣義 2D-COS
Noda generalized
需 m 條擾動序列:Φ=DᵀD/(m−1)、Ψ=Dᵀ·H·D/(m−1)(H=Hilbert–Noda)。同步可 rank>1、異步解析譜帶變化的先後順序(圖 5 C)。
更正與提醒:本頁早期版本把「樣本 vs 類別平均」的 two-trace 同步圖當作 2T2D 的賣點,但它在數學上就是差異光譜外積(異步退化為 0、同步 100% rank-1)。已用 SpectraView 重算並排對照(圖 5),下方 Python 也修正為與 SpectraView 一致的 Noda 公式。另外:Arabica/Robusta 是二分類(本質 rank-1),沒有隱藏的多維 covariation 可揭示;要做真正的 2D-COS 教學,建議改用烘焙或摻配的擾動序列當擾動軸。
Python workflow

可直接改成 notebook 的 Python 骨架

下面程式下載 Mendeley zip、讀取 coffee FTIR CSV、整理成 X: samples × wavenumbers,做 SNV 前處理,再用SpectraView 一致的 Noda 公式算真 2T2D 與廣義 2D-COS(不是會讓異步退化為 0 的減平均版)。

import io, zipfile, requests
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.svm import SVC

url = "https://data.mendeley.com/public-api/zip/frrv2yd9rg/download/1"
r = requests.get(url, headers={"User-Agent": "Mozilla/5.0"})
r.raise_for_status()
z = zipfile.ZipFile(io.BytesIO(r.content))

coffee_file = [f for f in z.namelist() if "instant_coffee" in f and f.endswith(".csv")][0]
raw = pd.read_csv(z.open(coffee_file), header=None)

sample_id = raw.iloc[0, 1:].astype(str).to_numpy()
group_code = raw.iloc[1, 1:].astype(str).to_numpy()
y = raw.iloc[2, 1:].astype(str).to_numpy()      # Arabica / Robusta
wavenumber = pd.to_numeric(raw.iloc[3:, 0]).to_numpy()
X = raw.iloc[3:, 1:].T.astype(float).to_numpy() # samples × wavenumbers

print(X.shape, y[:5], wavenumber.min(), wavenumber.max())

PCA / SVM baseline

Xz = StandardScaler().fit_transform(X)
pca = PCA(n_components=2).fit_transform(Xz)

clf = SVC(kernel="linear")
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=7)
scores = cross_val_score(clf, Xz, y, cv=cv)
print("CV accuracy:", scores.mean(), scores.std())

真 2T2D(= SpectraView cos2d.two_trace)

# Noda 2018 two-trace 2D correlation.
# 不要對兩條光譜減平均:只有兩條時減平均會得到 ya-yb 與 yb-ya,
# 使異步圖恆為 0、同步圖塌成 1D 差異光譜的外積(rank-1)。
# 另外避免變數名用 async(Python 3.7+ 是保留字)。
def two_trace_2dcos(ya, yb):
    sync = 0.5 * (np.outer(ya, ya) + np.outer(yb, yb))
    asyn = 0.5 * (np.outer(ya, yb) - np.outer(yb, ya))
    return sync, asyn

# SNV 前處理,再算兩類平均的真 2T2D
Xs = (X - X.mean(1, keepdims=True)) / X.std(1, keepdims=True)
arab, robu = Xs[y == "Arabica"].mean(0), Xs[y == "Robusta"].mean(0)
sync, asyn = two_trace_2dcos(arab, robu)
ratio = np.abs(asyn).max() / np.abs(sync).max()
print(sync.shape, "max|async|/max|sync| =", round(ratio, 3))  # ~0.15 非退化

廣義 2D-COS(擾動序列,= SpectraView cos2d.correlation)

# 廣義 2D-COS:給一條「隨擾動變化」的光譜序列 M (m_spectra × n_wavenumbers)。
def noda_matrix(m):
    N = np.zeros((m, m))
    for j in range(m):
        for k in range(m):
            if j != k:
                N[j, k] = 1.0 / (np.pi * (k - j))
    return N

def generalized_2dcos(M, reference="mean"):
    D = M - M.mean(axis=0) if reference == "mean" else M
    m = D.shape[0]
    sync = D.T @ D / (m - 1)
    asyn = D.T @ (noda_matrix(m) @ D) / (m - 1)
    return sync, asyn

# 把不同烘焙時間 / 摻配比例的光譜疊成 M,再 sync, asyn = generalized_2dcos(snv(M))。
# Arabica/Robusta 分類沒有這種擾動軸,因此只能做兩跡 2T2D;
# 要展現 band-to-band covariation 與先後順序,請改用真正的擾動序列。
Orange workflow

Orange Data Mining 也可以做 2D Correlation Plot

除了 Python 版 2T2D-COS,Orange Data Mining 的 Spectroscopy add-on 也有 2D Correlation Plot widget。建議先用 Orange 做互動式探索,看懂「一維光譜如何變成二維相關圖」;再用 Python 重現流程、輸出圖檔與建立機器學習特徵。

Orange Spectroscopy add-on showing the 2D Correlation Plot widget
Orange 2D Correlation Plot。安裝 Orange Spectroscopy add-on 後,可在 Spectroscopy 類別中找到此 widget。它適合課堂即時展示 synchronous / asynchronous correlation map,不必先寫程式也能理解 2D-COS 視覺化。

一維光譜 baseline 流程

FileSelect ColumnsPreprocessPCAScatter PlotTest & ScoreConfusion Matrix

Orange 2D correlation 延伸流程

File / SpectraPreprocess SpectraSelect Rows / GroupsAverage Spectra2D Correlation Plot

Orange 的定位

Orange 適合入門與互動探索:拖拉 widget、調整前處理、立即觀察波數 × 波數的相關圖樣,看出哪些 fingerprint 區域會一起變化。

Python 的定位

Python 適合可重現分析:可固定 reference trace、批次輸出 synchronous / asynchronous maps、產生 flattened features,進一步接 SVM、Random Forest 或 CNN。

教學提醒

Orange 的 2D Correlation Plot 是視覺探索工具;若要做嚴格模型評估,reference spectrum 與 class mean 必須在訓練 fold 內計算,避免把測試資料洩漏進模型。

建議課堂順序:先用 FTIR 一維光譜完成 PCA / PLS-DA baseline,再開 Orange 2D Correlation Plot 觀察 band-to-band pattern,最後回到 Python 產生可保存、可重現、可延伸到機器學習的 2T2D-COS 圖。
Self check

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

01

為什麼不能把 duplicate acquisition 當成完全獨立樣本來隨機切分 train/test?

02

class mean spectrum 當 reference trace 有什麼優點?有什麼可能造成資料洩漏的風險?

03

2T2D synchronous map 與原始一維 spectrum 相比,多提供了哪一類資訊?

04

如果換成 fruit puree dataset,Strawberry vs non-strawberry 的模型評估應該看 accuracy 之外的哪些指標?

下一步:把 coffee 章節做完後,建議直接複製同一份 notebook,改讀 MIR_Fruit_purees.csv,比較小型乾淨資料與大型摻偽資料的差異。