kNN(k-Nearest Neighbor) 알고리즘은 k개의 가장 가까운 이웃 데이터를 찾고, 그 이웃들의 레이블을 기반으로 분류/회귀 문제를 해결하는 알고리즘을 의미한다.
kNN 알고리즘은 새 데이터가 입력으로 들어오면, 이 포인트가 어떤 클래스에 속해있는지를 찾는다. 이를 위해 k개의 이웃을 찾고, 이웃 중 가장 많은 클래스에 속한 레이블을 해당하는 데이터 포인트에 속하는 클래스라고 지정한다. 이때, k 값에 따라 결과가 달라질 수 있으며, 데이터의 분포와 패턴이 명확할 때 더 잘 작동하는 경향이 있다. k 값은 일반적으로 홀수를 선택한다. (짝수로 선택하게 되면 Voting 시 동점이 발생할 수 있기 때문이다.)
kNN 알고리즘은 데이터 포인트가 가장 많이 속한 클래스로 분류를 수행한다. 따라서 특징 공간에 있는 모든 데이터에 대한 정보가 필요하다. 이러한 점 때문에 모든 학습 데이터를 저장해야 해서 메모리의 양이 크고, 기존 모든 포인트와의 모든 거리를 계산해야 해서 계산 복잡도가 크다는 문제점이 있어 데이터와 클래스가 많을수록 효율성이 낮아진다. 하지만, 직관적이고 이해하기 쉬우며, 사전 학습 시간(학습 시간)이 필요하지 않다는 장점이 있다.
핵심 : 거리 기반 이웃 찾기, kNN은 학습하는 알고리즘이 아니다! 거리 기반 계산임.

정의 : 새로운 샘플 x에 대해 학습 데이터의 가장 가까운 k개 이웃의 다수결(분류) 또는 평균(회귀)으로 예측하는 비모수, 메모리 기반(lazy) 알고리즘
거리 척도 : 기본은 유클리드 거리()임. 대안 : 맨해튼(), 민코프스키(), 코사인 등.
weights='distance'로 가까운 이웃에 더 큰 가중치를 부여하는 것k, metric(거리 측정 방법), weights(가중치 결정 방법, uniform - 동일 가중치, distance - 거리에 반비례하는 가중치), p(민코프스키 차수)weights를 distance로 부여하면, 거리에 반비례하는 가중치를 부여하므로 예측값은 더 가까운 이웃에 더 큰 영향을 받게 됨. (가까운 거리에 더 큰 가중치를 부여하게 되므로)knn = KNeighborsClassifier(n_neighbors=6)
knn.fit(X_train, y_train)
y_pred = knn.predict(X_test)
scores = metrics.accuracy_score(y_test, y_pred)
# kNN은 전처리의 영향을 많이 받는다.
# 표준화 전이 표준화 후보다 성능이 더 높음.
# 표준화는 무조건 해야 한다 X, 성능을 보고 무엇(하는 것 or 안 하는 것)이 더 좋은지 판단해야 함.
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import cross_val_score, KFold
# shuffle은 데이터를 섞는 것으로, 학습 시 순서에 의존하는 것을 방지해줌.
cv = KFold(n_splits=5, shuffle=True, random_state=42) # n_splits=5이므로 5-Fold CV
raw_model = KNeighborsClassifier(n_neighbors=5)
# cross_val_score(estimator, X, y, cv=None) : 교차 검증을 통해 점수를 평가하는 교차 평가 함수
score_raw = cross_val_score(raw_model, X, y, cv=cv).mean() # 정규화 전 CV Score
# 파이프라인이란, 전처리 → 학습 → 평가로 이어지는 일련의 과정들을 하나로 묶는 것임.
pipe = Pipeline([('scaler', StandardScaler()), ('knn', KNeighborsClassifier(n_neighbors=5))]) # 정규화 후 knn
score_scaled = cross_val_score(pipe, X, y, cv = cv).mean() # 정규화 후 CV Score
print("CV 정확도 (무스케일) : ", score_raw)
print("CV 정확도 (표준화) : ", score_scaled)
결과 해석

# Confusion Matrix를 판단할 땐, 대각 성분을 봐야 한다.
# Confusion Matrix의 x축은 모델, y축은 정답을 의미함.
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.metrics import ConfusionMatrixDisplay
digits = load_digits() # 데이터 로드
n_samples = len(digits.images) # 데이터 개수
# knn은 이미지 형태 그대로 처리가 불가능함. (거리 계산을 해야 하기 때문)
# 따라서 reshape을 통해 이미지를 1차원 벡터로 펼침.
# shape 출력 결과를 보면 알 수 있듯이, 8*8 이미지 -> 64 길이 벡터로 처리하여 knn이 처리 가능하도록 함
print(digits.images.shape)
data = digits.images.reshape((n_samples, -1))
print(data.shape)
Xd_tr, Xd_te, yd_tr, yd_te = train_test_split(data, digits.target, test_size=0.2, random_state=0) # train, test 데이터 분할
knn_d = KNeighborsClassifier(n_neighbors=6) # knn 알고리즘
knn_d.fit(Xd_tr, yd_tr) # 학습
yd_pred = knn_d.predict(Xd_te) # 예측
print("Digits 정확도 : ", accuracy_score(yd_te, yd_pred))
# ConfusionMatrixDisplay.from_estimator(추정치, X, y) : 추정치, 데이터, 레이블이 주어졌을 때 혼동 행렬을 그리는 함수
# 실제 레이블과 예측 레이블만이 주어졌을 땐 ConfusionMatrixDisplay.from_predictions(y_true, y_pred)
disp = ConfusionMatrixDisplay.from_estimator(knn_d, Xd_te, yd_te) # knn 예측 결과(추정치) 기반 실제 정답과 비교한 혼동 행렬을 그림
plt.title("Digits Confusion Matrix (KNN)")
plt.show()
K-Means 클러스터링은 레이블이 없는 데이터를 학습하는 비지도 학습의 가장 대표적인 알고리즘이다. 이는 주어진 n개의 관측 값을 k개의 클러스터로 분할하며, 관측 값들은 거리가 최소인 클러스터로 분류된다.
K-Means 클러스터링 알고리즘은 초기 중심점 위치에 따라 결과가 크게 달라지고, 때로는 도메인 지식에 따라서도 결과가 달라질 수 있다. 따라서 여러 번 중심점(centroid)를 설정해 최적의 값을 찾는데, 이것의 대표적인 방법에는 Elbow method와 Silhouette analysis가 있다.
이는 많은 데이터셋에 대해서도 클러스터링 성능이 좋다는 장점이 있다. 그러나, 클러스터 모양이 복잡하거나 크기가 다르면 제한적인 성능을 보이므로 다른 알고리즘을 고려해야 한다.
K-means 클러스터링은 군집 중심점(centroid)라는 특정한 k개의 임의 지점을 선택해 해당 중심에 가장 가까운 포인트들을 선택하는 군집화 기법으로, 다음과 같은 과정에 의해 수행된다.
import numpy as np
from sklearn.cluster import KMeans
X_simple = np.array([
[6,3], [11, 15], [17,12], [24,10], [20,25], [22, 30],
[85,70], [71,81], [60, 79], [56,52], [81, 91], [80, 81]
]) # 데이터
# kMeans 알고리즘, k(중심점)=2
# 두 개의 클러스터 중심점을 반복 계산해 학습
kmeans2 = KMeans(n_clusters=2, random_state=0).fit(X_simple)
print("중심점 : \n", kmeans2.cluster_centers_) # 학습 이후 찾은 중심점 좌표 출력
# 원본 데이터 출력
plt.figure()
plt.scatter(X_simple[:, 0], X_simple[:,1]) # 원본 데이터 표시
plt.title("Raw Points for K-Means")
plt.xlabel('x1')
plt.ylabel('x2')
plt.show()
# K-Means 알고리즘 수행 후 결과
plt.figure()
# 각 데이터에 지정한 클러스터에 따라 표시
plt.scatter(X_simple[:, 0], X_simple[:,1], c = kmeans2.labels_)
# centroid 좌표 표시
plt.scatter(kmeans2.cluster_centers_[:, 0], kmeans2.cluster_centers_[:,1], marker='x', s=200)
plt.title("K-Means Result (k=2)")
plt.xlabel('x1')
plt.ylabel('x2')
plt.show()