ai

qwk metrics

comoZ 2024. 5. 9. 17:10

개요

  • Metrics는 평가 지표를 의미함
  • qwk→ quadratic weighted kappa
  • 단순 정확도 점수로는 의미가 없는 경우에 사용
  • 두 평점의 동의도(agreement)를 측정함 
  • 동의도는 실제 평점과 예측 평점이 얼마나 가까운 지로 정의할 수 있다. 

평가

  • -1~1의 범위를 가진다
  • 1에 가까울 수록 좋은 예측
  • 0 미만은 무작위 예측 보다 성능이 떨어진다.
  • 동의도(agreement)가 높을수록 1에 가까워지고, 동의도가 낮으면 0에 가까워진다.

예시

y_true = [1,2,3,1,2,3,1,2,3]
y_pred = [2,1,3,1,2,3,3,1,2]

metrics.cohen_kappa_score(y_true, y_pred, weights='quadratic'), metrics.accuracy_score(y_true, y_pred)

 

최적화

  • 회귀 문제로 접근하기:
    • 이 방법은 Quadratic Weighted Kappa (QWK)를 기본적으로 분류 지표로 사용하면서도 회귀 모델을 구축하여 Mean Squared Error (MSE)를 최소화하고 나서 결과를 반올림합니다. 이 접근 방식은 보다 나은 결과를 얻을 수 있으며, QWK를 손실 함수로 구현하는 것보다 훨씬 간단합니다.
    • 예를 들어, 이전 섹션에서 나온 평균 예측에 반올림을 적용하고 모드 예측을 사용하여 반올림 임계값을 최적화하여 평균 예측을 개선할 수 있습니다.
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import cohen_kappa_score

# 데이터 생성 및 분할
np.random.seed(42)
data_size = 1000
X = np.random.rand(data_size, 10)
y = np.random.randint(0, 5, data_size)

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

# 회귀 모델 훈련
model = LinearRegression()
model.fit(X_train, y_train)

# 예측값 생성
y_pred = model.predict(X_test)

# 예측값 변환
y_pred_clipped = y_pred.clip(0, 4)  # 예측값을 0과 4 사이로 제한
y_pred_rounded = np.round(y_pred_clipped).astype(int)  # 반올림 및 정수형 변환

# QWK 점수 계산
qwk_score = cohen_kappa_score(y_test, y_pred_rounded, weights='quadratic')

# 결과 출력
print(f"QWK Score: {qwk_score}")



  • QWK를 직접 손실 함수로 사용하기: 
    • 이 방법은 TensorFlow/Keras 모델에서 직접적으로 QWK를 최적화합니다. 이를 위해 _cohen_kappa 함수를 사용하여 y_true와 y_pred 사이의 QWK를 계산하고, 이 값을 최대화하는 방향으로 모델을 훈련합니다.
    • 이 함수는 내부적으로 QWK를 손실 값으로 반환하여 모델 훈련 시 QWK 점수를 최적화합니다
class QWKLoss(Loss):
def init(self, num_classes, name="QWKLoss"):
super().init(name=name)
self.num_classes = num_classes

def call(self, y_true, y_pred):
    y_true = tf.cast(y_true, tf.int32)
    y_pred = tf.clip_by_value(y_pred, 0, self.num_classes - 1)
    y_pred = tf.round(y_pred)
    y_true = tf.one_hot(y_true, depth=self.num_classes)
    y_pred = tf.one_hot(tf.cast(y_pred, tf.int32), depth=self.num_classes)

    weights = tf.constant([[1 - ((i - j) ** 2 / (self.num_classes - 1) ** 2)
                            for j in range(self.num_classes)]
                           for i in range(self.num_classes)], dtype=tf.float32)

    hist_true = tf.reduce_sum(y_true, axis=0)
    hist_pred = tf.reduce_sum(y_pred, axis=0)

    expected_matrix = tf.tensordot(hist_true, hist_pred, axes=0) / tf.reduce_sum(hist_true)

    observed_matrix = tf.tensordot(tf.transpose(y_true), y_pred, axes=1)

    num = tf.reduce_sum(weights * observed_matrix)
    denom = tf.reduce_sum(weights * expected_matrix)

    return 1 - num / denom
# 3. 모델 정의 및 컴파일
model = Sequential([
    Dense(64, input_dim=10, activation='relu'),
    Dense(32, activation='relu'),
    Dense(1, activation='linear')
])

model.compile(optimizer='adam', loss=QWKLoss(num_classes=5))

 

위 예시는 GPT로 생성한 예제로 정확하지 않음

추후 직접 사용해보면서 문제 발견시 수정하겠습니다.