使用分類器鏈的多標籤分類#

此範例示範如何使用 ClassifierChain 來解決多標籤分類問題。

解決此類任務最簡單的方法是針對每個標籤(即目標變數的每一欄)獨立訓練一個二元分類器。在預測時,會使用二元分類器的整體來組裝多任務預測。

此策略不允許對不同任務之間的關係建模。ClassifierChain 是一個元估計器(即採用內部估計器的估計器),它實作了更進階的策略。二元分類器的整體會用作鏈,其中鏈中分類器的預測會用作訓練下一個標籤上新分類器的特徵。因此,這些額外的特徵允許每個鏈利用標籤之間的關聯性。

鏈的 Jaccard 相似度分數往往大於獨立基礎模型集的相似度分數。

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause

載入資料集#

在此範例中,我們使用 酵母資料集,其中包含 2,417 個資料點,每個資料點有 103 個特徵和 14 個可能的標籤。每個資料點至少有一個標籤。作為基準,我們先針對 14 個標籤中的每一個訓練一個邏輯迴歸分類器。為了評估這些分類器的效能,我們對保留的測試集進行預測,並計算每個樣本的 Jaccard 相似度。

import matplotlib.pyplot as plt
import numpy as np

from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split

# Load a multi-label dataset from https://www.openml.org/d/40597
X, Y = fetch_openml("yeast", version=4, return_X_y=True)
Y = Y == "TRUE"
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=0)

擬合模型#

我們擬合由 OneVsRestClassifier 包裝的 LogisticRegression,以及多個 ClassifierChain 的整體。

由 OneVsRestClassifier 包裝的 LogisticRegression#

由於預設情況下 LogisticRegression 無法處理具有多個目標的資料,因此我們需要使用 OneVsRestClassifier。在擬合模型後,我們計算 Jaccard 相似度。

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import jaccard_score
from sklearn.multiclass import OneVsRestClassifier

base_lr = LogisticRegression()
ovr = OneVsRestClassifier(base_lr)
ovr.fit(X_train, Y_train)
Y_pred_ovr = ovr.predict(X_test)
ovr_jaccard_score = jaccard_score(Y_test, Y_pred_ovr, average="samples")

二元分類器的鏈#

由於每個鏈中的模型都是隨機排列的,因此鏈之間的效能差異很大。據推測,鏈中類別的最佳排序會產生最佳效能。然而,我們事先並不知道該排序。相反地,我們可以透過平均鏈的二元預測,並應用 0.5 的閾值來建立分類器鏈的投票整體。整體的 Jaccard 相似度分數大於獨立模型的相似度分數,並且往往會超過整體中每個鏈的分數(儘管這不能保證使用隨機排序的鏈)。

from sklearn.multioutput import ClassifierChain

chains = [ClassifierChain(base_lr, order="random", random_state=i) for i in range(10)]
for chain in chains:
    chain.fit(X_train, Y_train)

Y_pred_chains = np.array([chain.predict_proba(X_test) for chain in chains])
chain_jaccard_scores = [
    jaccard_score(Y_test, Y_pred_chain >= 0.5, average="samples")
    for Y_pred_chain in Y_pred_chains
]

Y_pred_ensemble = Y_pred_chains.mean(axis=0)
ensemble_jaccard_score = jaccard_score(
    Y_test, Y_pred_ensemble >= 0.5, average="samples"
)

繪製結果#

繪製獨立模型、每個鏈和整體的 Jaccard 相似度分數(請注意,此圖的垂直軸不是從 0 開始)。

model_scores = [ovr_jaccard_score] + chain_jaccard_scores + [ensemble_jaccard_score]

model_names = (
    "Independent",
    "Chain 1",
    "Chain 2",
    "Chain 3",
    "Chain 4",
    "Chain 5",
    "Chain 6",
    "Chain 7",
    "Chain 8",
    "Chain 9",
    "Chain 10",
    "Ensemble",
)

x_pos = np.arange(len(model_names))

fig, ax = plt.subplots(figsize=(7, 4))
ax.grid(True)
ax.set_title("Classifier Chain Ensemble Performance Comparison")
ax.set_xticks(x_pos)
ax.set_xticklabels(model_names, rotation="vertical")
ax.set_ylabel("Jaccard Similarity Score")
ax.set_ylim([min(model_scores) * 0.9, max(model_scores) * 1.1])
colors = ["r"] + ["b"] * len(chain_jaccard_scores) + ["g"]
ax.bar(x_pos, model_scores, alpha=0.5, color=colors)
plt.tight_layout()
plt.show()
Classifier Chain Ensemble Performance Comparison

結果解讀#

從此圖中可以得到三個主要重點

  • OneVsRestClassifier 包裝的獨立模型的效能比分類器鏈的整體和一些單個鏈的效能差。這是因為邏輯迴歸沒有對標籤之間的關係建模。

  • ClassifierChain 利用了標籤之間的關聯性,但由於標籤排序的隨機性,它可能會產生比獨立模型更差的結果。

  • 鏈的整體效能更好,因為它不僅捕捉了標籤之間的關係,而且沒有對其正確順序做出強烈的假設。

腳本的總執行時間: (0 分鐘 2.279 秒)

相關範例

多類別訓練元估計器概述

多類別訓練元估計器概述

使用網格搜尋進行模型統計比較

使用網格搜尋進行模型統計比較

繪製分類機率

繪製分類機率

使用樹集成進行特徵轉換

使用樹集成進行特徵轉換

由 Sphinx-Gallery 產生之圖庫