오늘은 SVM으로 위조지폐를 분류하고, KNN으로 물고기와 펄서 신호를 분류하는 실습을 진행했다. 같은 분류 문제라도 데이터의 스케일, 클래스 비율, 입력값의 순서가 결과 해석에 영향을 준다는 점을 같이 확인했다.
Banknote Authentication 데이터에는 웨이블릿 변환으로 얻은 variance, skewness, curtosis, entropy 네 특성과 지폐 진위 레이블이 있다. 총 1,372개 행에 결측값은 없었다.
데이터를 층화 추출로 학습 1,097개와 테스트 275개로 나눈 뒤, StandardScaler로 학습 데이터의 평균과 표준편차를 기준으로 표준화했다. 테스트 데이터에는 학습 데이터로 구한 기준만 적용했다.
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
svc = SVC()
svc.fit(X_train_scaled, y_train)
y_pred = svc.predict(X_test_scaled)
이번 데이터에서는 학습·테스트 정확도가 모두 1.0이었다. 혼동 행렬도 진짜 지폐 153건, 위조 지폐 122건을 모두 맞춘 형태로 나왔고 F1 점수 역시 1.0이었다. 점수가 높게 나와도 다른 데이터에서도 같은 성능이 나오는지는 별도로 검증해야 한다.
물고기 데이터에서 7개 종 중 도미와 빙어만 골라, 무게와 길이로 두 종류를 분류했다. 문자열 품종은 도미 0, 빙어 1로 변환하고 나머지 측정값은 제외했다.
fish_df = df[df["Species"].isin(["Bream", "Smelt"])]
fish_df["Species"] = fish_df["Species"].map({"Bream": 0, "Smelt": 1})
fish_df = fish_df.drop(["Diagonal", "Height", "Width"], axis=1)
X = fish_df.drop("Species", axis=1)
y = fish_df["Species"]
49개 데이터를 39개 학습·10개 테스트로 나누고 KNN을 학습했다. 표준화 전과 후 모두 테스트 정확도 1.0이 나왔으며, 혼동 행렬은 도미 7건과 빙어 3건을 모두 맞췄다.
새 데이터를 넣어 최근접 이웃을 시각화하는 과정에서는 그래프의 좌표와 DataFrame의 컬럼 순서를 맞춰야 한다는 점을 확인했다. 그래프에는 길이 20, 무게 200으로 표시했지만, columns가 Weight, Length인 DataFrame에 [[20, 200]]을 넣으면 무게 20, 길이 200으로 해석된다. 입력값과 컬럼 순서를 함께 확인해야 한다.
펄서 데이터는 우주 잡음 11,375개, 펄서 1,153개로 클래스 수 차이가 컸다. 전체 데이터를 그대로 사용하지 않고, 우주 잡음에서 1,153개를 무작위로 뽑아 펄서 데이터와 같은 수로 맞췄다.
df_0 = star_df[star_df["target"] == 0]
df_1 = star_df[star_df["target"] == 1]
df_0_sampled = df_0.sample(n=len(df_1), random_state=2026)
star_df = pd.concat([df_1, df_0_sampled]) \
.sample(frac=1, random_state=2026) \
.reset_index(drop=True)
통합 프로필의 평균과 표준편차 두 컬럼을 입력값으로 사용했다. 표준화 후 KNN을 학습한 결과, 학습 정확도는 약 0.918, 테스트 정확도는 약 0.892였다. 테스트 혼동 행렬에서는 잡음 211건과 펄서 201건을 맞추고, 각각 20건과 30건을 반대로 분류했다.
오늘 실습에서는 모델을 바로 학습하기보다 데이터 분할, 스케일링, 클래스 비율을 먼저 맞추는 흐름을 반복했다. 특히 KNN처럼 거리 기반으로 이웃을 찾는 모델은 특성의 단위와 새로 넣는 데이터의 컬럼 순서를 더 신경 써야겠다.