회귀의 한 종류인 다중 라벨 분류에 대해 정리하려고 한다
분류기가 샘플마다 여러 개의 클래스를 출력해야할 때가 있다, 이럴 때 사용되는 것이 바로 다중 레이블 분류다!
다중 레이블의 예시를 찾아보면 아래와 같다! 생각보다 다양하다.
만약 '정치', '경제', '사회' 세 가지 카테고리에 따라 뉴스 기사를 분류하도록 훈련된 분류기가 있다고 해보자. 만약 정치와 사회 카테고리에 속하는 기사를 본다면 [1, 0, 1]을 출력할 것이다. 즉, [정치 카테고리 맞음, 경제 카테고리 아님, 사회 카테고리 맞음] 이렇게 분류되는 것이다.
이렇게 여러 개의 이진 꼬리표를 출력하는 분류 시스템이 바로 다중 레이블 분류 시스템이다.
설계와 알고리즘 차이에 의해 기본적으로 모든 분류기가 다중 레이블 분류를 지원하지 않는다.
KNeighborsClassifierclass sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, *,
weights='uniform', algorithm='auto', leaf_size=30, p=2,
metric='minkowski', metric_params=None, n_jobs=None)[source]
KNeighborsClassifier는 K-최근접 이웃(K-NN) 알고리즘을 사용하여 샘플의 주변 이웃의 라벨을 보고, 가장 많은 라벨을 갖는 클래스를 할당한다.
각 클래스에 대해 독립적인 판단을 내릴 수 있기에 다중 레이블 문제에서 유연하게 작동 가능하다고 한다.
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
# ...중략
# K-최근접 이웃 분류기 생성 (k=3)
knn = KNeighborsClassifier(n_neighbors=3)
# 학습
knn.fit(X_train, y_train)
# 예측
y_pred = knn.predict(X_test)
# 정확도 평가
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy}")
참고 : 사이킷런 공식 문서
OneVsRestClassifierclass sklearn.multiclass.OneVsRestClassifier(estimator, *,
n_jobs=None, verbose=0)
OneVsRestClassifier는 본래 다중 클래스 분류 문제를 다중 이진 분류 문제로 변환하는 방법이다. 즉 이진 분류 문제로 나누어 학습하는 것이다. 각 레이블에 대한 이진 분류기를 학습시켜서 각 레이블에 대한 예측을 개별적으로 처리한다. 즉 직접 다중 레이블 분류를 지원하는 함수는 아니다.
from sklearn.multiclass import OneVsRestClassifier
from sklearn.svm import SVC
from sklearn.datasets import make_multilabel_classification
from sklearn.model_selection import train_test_split
# 다중 레이블 데이터를 생성
X, y = make_multilabel_classification(n_samples=100, n_classes=3, random_state=42)
# 데이터셋 분리
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# SVM 분류기를 OneVsRestClassifier로 래핑하여 다중 레이블 분류 수행
clf = OneVsRestClassifier(SVC())
clf.fit(X_train, y_train)
# 테스트 데이터에 대한 예측
y_pred = clf.predict(X_test)
참고 : 사이킷런 공식 문서
DecisionTreeClassifierclass sklearn.tree.DecisionTreeClassifier(*, criterion='gini',
splitter='best', max_depth=None, min_samples_split=2,
min_samples_leaf=1, min_weight_fraction_leaf=0.0,
max_features=None, random_state=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, class_weight=None, ccp_alpha=0.0,
monotonic_cst=None)
뭔 인자가 이렇게 괴랄하게 많지...
이 함수는 기본적으로 다중 레이블 분류를 지원한다.
결정 트리 모델이 각 레이블에 대해 독립적인 트리를 만들어서, 각 레이블에 대한 예측을 동시에 할 수 있다. 그래서 다중 출력 결정 트리라고 부른다.
참고로 별도의 추가적인 알고리즘 없이도 다중 레이블 출력이 가능하다.
from sklearn.tree import DecisionTreeClassifier
# 다중 레이블 분류용 데이터셋을 학습시키기 위한 결정 트리 분류기 생성
clf = DecisionTreeClassifier()
clf.fit(X_train, y_train) # X_train: 입력 데이터, y_train: 다중 레이블 (2D 배열)
# 예측
y_pred = clf.predict(X_test)
참고 : 사이킷런 공식 문서
RandomForestClassifierclass sklearn.ensemble.RandomForestClassifier(n_estimators=100, *,
criterion='gini', max_depth=None, min_samples_split=2,
min_samples_leaf=1, min_weight_fraction_leaf=0.0,
max_features='sqrt', max_leaf_nodes=None,
min_impurity_decrease=0.0, bootstrap=True, oob_score=False,
n_jobs=None, random_state=None, verbose=0, warm_start=False,
class_weight=None, ccp_alpha=0.0, max_samples=None,
monotonic_cst=None)
이건 더 많네...
랜덤 포레스트는 여러 결정 트리의 앙상블을 통해 예측 성능을 향상시키며, 다중 레이블 문제에서도 동일한 방식으로 동작한다.
다중 레이블 분류 문제에서 랜덤 포레스트는 각 레이블에 대해 독립적인 트리를 생성하여 예측을 수행할 수 있다. 결정 트리처럼 다중 출력 지원이 가능하며, 기본적으로 다중 레이블 분류에 적합하게 동작한다.
from sklearn.ensemble import RandomForestClassifier
# 다중 레이블 분류용 랜덤 포레스트 분류기 생성
clf = RandomForestClassifier()
clf.fit(X_train, y_train) # X_train: 입력 데이터, y_train: 다중 레이블 (2D 배열)
# 예측
y_pred = clf.predict(X_test)
참고 : 사이킷런 공식 문서
주의! 다중 레이블 분류 데이터 형식
위의 네 가지 함수들을 사용할 때, target label을 2차원 바이너리(0, 1) 배열 형식으로 준비해야 한다! 이 배열의 크기는 (n_samples, n_labels)로, 각 행은 하나의 샘플에 대해 여러 개의 레이블을 가질 수 있는지 여부를 나타낸다고 한다.
y_train = [
[1, 0, 1],
[0, 1, 0],
[1, 1, 0]
]
One-vs-the-rest (OvR) 다중 클래스 전략 = OvA 전략 이라고 한다.
cf) 다중 클래스 OvA OvO 전략
작동 과정
다중 레이블 문제에서 OvR을 사용할 때는 각 레이블에 대해 하나의 이진 분류기를 학습한다. 이 방식은 Binary Relevance라고도 불리며, 각 레이블을 독립적으로 분류한다.
각 레이블을 이진 분류 문제로 변환
각 레이블에 대해 독립적인 이진 분류기를 학습
예측 시, 각 레이블에 대해 독립적으로 예측을 수행
ClassifierChainclass sklearn.multioutput.ClassifierChain(base_estimator, *,
order=None, cv=None, chain_method='predict',
random_state=None, verbose=False)
OvR과는 작동 방식이 다르니 주의해 주어야 한다!
다중 레이블 분류에서 레이블 간의 의존성을 고려하는 방식을 사용한다. 각 레이블을 순차적으로 예측하면서, 이전에 예측한 레이블을 그 다음 레이블의 특징(feature)으로 사용한다는 것이 핵심이다 🔥 즉, 이전에 예측된 레이블들이 그 이후 레이블을 예측하는 데 영향을 미치게 된다!
ClassifierChain 작동 방식1. 순차적 예측
2. 순서 중요성
다음과 같은 3개의 레이블이 있다고 가정해 보자! (L1, L2, L3).
분류기 1: 입력 데이터 X를 사용하여 레이블 L1을 예측
분류기 2: 입력 데이터 X와 앞서 예측한 레이블 L1을 함께 사용하여 L2를 예측
분류기 3: 입력 데이터 X, L1, L2를 모두 사용하여 L3을 예측
이렇게 각 분류기의 예측은 이전 분류기의 예측 결과에 의존하게 되는 방식이다.
OvR (기본적으로 다중 클래스 분류에 이용)
ClassifierChain
적절한 지표는 프로젝트에 따라 다르다.
1) 점수를 이용하는 방법
각 레이블의 점수를 구하여 간단하게 평균 점수를 계산하는 방법이 잇따.
만약 모든 레이블의 가중치가 같지 않다면,
레이블에 클래스에 지지도(support, target label에 속한 샘플 수)를 가중치로 주면 된다.f1_score(average = "weighted")이렇게 설정하면 된다.
2)SVC와 같이 기본적으로 다중 레이블 분류를 지원하지 않는 분류기 사용하는 경우
→ 레이블당 하나의 모델 학습
하지만 이 전략은 레이블 간의 의존성을 포착하기 어렵게 하기에 이 문제 해결을 위해 모델을 체인(chain)으로 구성하는 방법을 이용할 수 있다.
사이킷런에서 이런 작업을 수행하는 것이 위에서 다룬 ClassifierChain 클래스다.