Linear Discriminant Analysis

이정훈·2026년 3월 15일
post-thumbnail

LDA

  • Supervised learning으로 집단을 분류가 잘되는 방향으로 projection하여 차원을 축소

  • 신호 대비 잡음비를 최대화 하는 문제로 정의

  • 분자(평균간의 거리)는 이상적으로 높아야하며, 분모(각각의 분산)는 이상적으로 낮아야함

  • 3개 이상의 범주로 확장될 때,

    • 데이터의 중심점과의 거리로 분자를 계산
    • 생성되는 축의 개수는 범주의 개수 - 1
  • 투영 축 개수는 범주의 평균점들이 만들어내는 공간의 한계(C1C-1)와 차원의 한계 (D)중 더 작은 값에 지배

    • 공간상에 세 점 μ1,μ2,μ3\mu_1, \mu_2, \mu_3이 있을 때 μxμ\mu_x - \mu로 확장 되는 공간이 최대 C1C-1로 제약됨
      J=(μ1μ2)2s12+s22J = \frac{(\mu_1 - \mu_2)^2}{s_1^2 + s_2^2}
  • 투영할 축 ww를 찾는 문제로 바꾸면

    • SBS_B는 각 category의 평균을 ww축에 내적한 거리의 차 → wTSbww^TS_bw
    • SWS_Www축에 내적한 점들을 ww축에 내적한 평균에 대한 분산 → wTSwww^TS_ww

JJ에 대한 최대화 문제

코드

import numpy as np

# 데이터 형태 파악 (특성 4개)
n_features = X.shape[1]
class_labels = np.unique(y)

# Step 1: 클래스별 평균 벡터와 전체 평균 계산
mean_vectors = []
for cl in class_labels:
    mean_vectors.append(np.mean(X[y == cl], axis=0))
overall_mean = np.mean(X, axis=0)

# Step 2: 클래스 내 산포 행렬 (S_W) 계산
S_W = np.zeros((n_features, n_features))
for cl, mv in zip(class_labels, mean_vectors):
    class_sc_mat = np.zeros((n_features, n_features))
    for row in X[y == cl]:
        row, mv = row.reshape(n_features, 1), mv.reshape(n_features, 1)
        class_sc_mat += (row - mv).dot((row - mv).T)
    S_W += class_sc_mat

# Step 3: 클래스 간 산포 행렬 (S_B) 계산
S_B = np.zeros((n_features, n_features))
for i, mean_vec in enumerate(mean_vectors):
    n = X[y == i, :].shape[0]
    mean_vec = mean_vec.reshape(n_features, 1)
    overall_mean_vec = overall_mean.reshape(n_features, 1)
    S_B += n * (mean_vec - overall_mean_vec).dot((mean_vec - overall_mean_vec).T)

# Step 4: 고유값 분해 (S_W 역행렬과 S_B의 내적)
# 수학 공식: S_W^-1 * S_B
eig_vals, eig_vecs = np.linalg.eig(np.linalg.inv(S_W).dot(S_B))

# 고유값이 큰 순서대로 정렬 (설명력이 가장 높은 축을 찾기 위함)
eig_pairs = [(np.abs(eig_vals[i]), eig_vecs[:, i]) for i in range(len(eig_vals))]
eig_pairs = sorted(eig_pairs, key=lambda k: k[0], reverse=True)

# Step 5: 상위 2개의 고유벡터를 선택하여 변환 행렬 W 생성
W = np.hstack((eig_pairs[0][1].reshape(n_features, 1), 
               eig_pairs[1][1].reshape(n_features, 1)))

# 원래 데이터 X에 변환 행렬 W를 내적하여 데이터 투영
X_lda_manual = X.dot(W)

print("\n=== Numpy 수동 투영 결과 (W 행렬 적용) ===")
print(pd.DataFrame(X_lda_manual, columns=['LD1', 'LD2']).head())```
profile
AngDDo

0개의 댓글