K-means ํด๋ฌ์คํฐ๋ง์ด๋?
์ฃผ์ด์ง ๋ฐ์ดํฐ๋ฅผ K๊ฐ์ ๊ทธ๋ฃน์ผ๋ก ๋๋๋ ๋น์ง๋ ํ์ต ์๊ณ ๋ฆฌ์ฆ์ด๋ค. ์ด ์๊ณ ๋ฆฌ์ฆ์ ๊ตฐ์ง(ํด๋ฌ์คํฐ)์ ์ค์ฌ์ ๋ฐ๋ณต์ ์ผ๋ก ์ ๋ฐ์ดํธํ์ฌ, ๊ฐ ๋ฐ์ดํฐ ํฌ์ธํธ๋ฅผ ๊ฐ์ฅ ๊ฐ๊น์ด ์ค์ฌ(์ผํธ๋ก์ด๋)์ ํ ๋นํ๋ ๋ฐฉ์์ผ๋ก ๋์ํ๋ค.
๋์๋ฐฉ์
- K๊ฐ์ ์ด๊ธฐ ์ค์ฌ(centroid)์ ๋ฌด์์๋ก ์ ํํ๋ค.
- ๊ฐ ๋ฐ์ดํฐ ํฌ์ธํธ๋ฅผ ๊ฐ์ฅ ๊ฐ๊น์ด ์ค์ฌ์ ํ ๋นํ๋ค.
- ๊ฐ ํด๋ฌ์คํฐ์ ์ค์ฌ์ ๋ค์ ๊ณ์ฐํ๋ค.
- ์ค์ฌ์ด ๋ ์ด์ ๋ณํ์ง ์๊ฑฐ๋, ์ง์ ๋ ๋ฐ๋ณต ํ์์ ๋๋ฌํ ๋๊น์ง 2~3 ๋จ๊ณ๋ฅผ ๋ฐ๋ณตํ๋ค.
๋ชฉํ
K-means ํด๋ฌ์คํฐ๋ง์ ๊ตฌํํ๋ค.
์ ์ฝ์กฐ๊ฑด
- ๋ ๋ฒ์ ๋ฐ๋ณต ๋์ ๋ชจ๋ ์ค์ฌ์ (centroids)์ ์์น๊ฐ 1 * 10^-5 ์ดํ๋ก ๋ณํ ๊ฒฝ์ฐ ์๋ ดํ๋ค๊ณ ๊ฐ์ฃผํ๋ค.
- K-means ํด๋ฌ์คํฐ๋ง๊ณผ ๊ด๋ จ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ ๋ ์ฌ์ฉํ์ง ๋ง์์ผ ํ๋ฉฐ, ์ด ๊ณผ์ ์์๋ ์ง์ ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํํด์ผํ๋ค.
- ์๊ณ ๋ฆฌ์ฆ์ด ์๋ ดํ ๋๊น์ง ๊ฐ ๋จ๊ณ๋ฅผ ํ๋กฏ์ผ๋ก ๊ทธ๋ฆฐ๋ค.์๋ฅผ ๋ค์ด, ์๊ณ ๋ฆฌ์ฆ์ด 6๋ฒ์งธ ๋จ๊ณ์์ ์๋ ดํ๋ค๋ฉด, 1๋จ๊ณ๋ถํฐ 6๋จ๊ณ๊น์ง์ ๊ทธ๋ฆผ์ ์ ๊ณตํด์ผ ํ๋ฉฐ, ๊ฐ ํด๋ฌ์คํฐ๋ ์๋ก ๋ค๋ฅธ ์์์ผ๋ก ๋ช ํํ๊ฒ ๊ตฌ๋ถ๋์ด์ผ ํ๋ค.
๋ฐ์ดํฐ์
- 2๊ฐ์ ํด๋ฌ์คํฐ(K=2)๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๋ฉฐ 2D ํฌ์ธํธ ๋ฐ์ดํฐ๋ data_2d.csv ํ์ผ์ ์ฌ์ฉ
- ์ด๊ธฐ ์ค์ฌ์ (centroids) ์์น๋ ๋ ํด๋ฌ์คํฐ์ ๋ํ init_centroids.csv ํ์ผ์ ์ ๊ณต๋ ์์น๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๋ค.
init_centroids.csv
3.323451927212228152e-01,1.798198342198527866e-01
2.241161287440249505e-01,2.219986906322291287e-01
data_2d.csv
0.000000000000000000e+00,-7.687164597386728637e-01,4.608603078297135447e-01
0.000000000000000000e+00,2.687847555392556487e+00,2.366960661575847169e+00
0.000000000000000000e+00,-2.013793555022345139e-01,4.704299346653586511e-01
0.000000000000000000e+00,6.084956800449090597e-01,1.225400029138742575e+00
0.000000000000000000e+00,-8.228190446259109336e-02,1.137218118753473339e+00
0.000000000000000000e+00,2.083069297959621036e+00,2.694482088909215811e+00
0.000000000000000000e+00,1.503019851143946983e+00,1.074847268552238111e+00
0.000000000000000000e+00,3.916620013534907185e-01,-2.874971661363743269e-01
0.000000000000000000e+00,3.213771110785266227e-01,1.296743009602315366e+00
0.000000000000000000e+00,5.912482577647957260e-01,1.267164122169239793e-01
0.000000000000000000e+00,1.150577634973361407e+00,-2.664038442463685374e-01
0.000000000000000000e+00,9.425866685920466503e-01,8.676624226337872337e-01
0.000000000000000000e+00,1.357806126580951567e+00,1.805471547458144421e+00
0.000000000000000000e+00,1.162919909687994968e+00,2.622430134800965540e+00
0.000000000000000000e+00,-9.786851243616156992e-02,1.012305814636828893e+00
0.000000000000000000e+00,8.577741746560831881e-01,1.031965247701047028e+00
0.000000000000000000e+00,6.834367317155296551e-01,1.578139963641977950e-02
0.000000000000000000e+00,1.543771853980922426e+00,1.750230549650776624e+00
๊ตฌํ
๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ถ๋ฌ์ค๊ธฐ
import numpy as np
import matplotlib.pyplot as plt
๋ฐ์ดํฐ ๋ถ๋ฌ์ค๊ธฐ
๋ฐ์ดํฐ ํฌ์ธํธ ํ์ผ ์ฝ๊ธฐ
# ๋ฐ์ดํฐ ํ์ผ์์ ์ขํ ์ฝ๊ธฐ
def read_data(file_path):
data = []
labels = []
with open(file_path, 'r') as file:
for line in file:
values = line.strip().split(',')
# ์ฒซ ๋ฒ์งธ ๊ฐ(ํด๋ฌ์คํฐ ID)์ ๋ฌด์ํ๊ณ ๋ ๋ฒ์งธ, ์ธ ๋ฒ์งธ ์ขํ๋ง ์ฌ์ฉ
data.append([float(values[1]), float(values[2])])
labels.append(int(float(values[0]))) # ์ด๊ธฐ ํด๋ฌ์คํฐ ๋ผ๋ฒจ
return np.array(data), np.array(labels)
- hw2_data_2d.csv ํ์ผ์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์จ๋ค. ๊ฐ ํ์ ํด๋ฌ์คํฐ ๋ ์ด๋ธ, x ์ขํ, y ์ขํ์ ํ์์ผ๋ก ๋์ด ์๋ค.
- ์ฒซ ๋ฒ์งธ ๊ฐ(ํด๋ฌ์คํฐ ๋ ์ด๋ธ)์ ์ ์ธํ๊ณ x, y ์ขํ๋ง์ ๋ฐ์ดํฐ๋ก ์ฌ์ฉํ๋ฉฐ, ํด๋ฌ์คํฐ ๋ ์ด๋ธ์ ๋ฐ๋ก ์ ์ฅํ๋ค.
์ค์ฌ์ ์ขํ ํ์ผ ์ฝ๊ธฐ
[(x1,y1),(x2,y2)] ํํ์ numpy ๋ฐฐ์ด๋ก ์ ์ฅํ๋ค.
def read_centroid(file_path):
return np.array(np.loadtxt(file_path, delimiter=',', dtype=float))
Kmeans ํด๋์ค ์์ฑ
Kmeans ์๊ณ ๋ฆฌ์ฆ ๊ตฌํ์ ์ํ ํด๋์ค๋ฅผ ๋ง๋ ๋ค. ๋ค์์ ์ ์ฒด ํด๋์ค ์ฝ๋์ด๋ค.
# K-means Clustering ํด๋์ค
class KMeans:
def __init__(self,labels, n_clusters, max_iter=300, tol=1e-5, init_centroids=None):
self.n_clusters = n_clusters # ํด๋ฌ์คํฐ ๊ฐ์ (K=2)
self.max_iter = max_iter # ์ต๋ ๋ฐ๋ณต ํ์
self.tol = tol # ์๋ ด ๊ธฐ์ค (๋ณํ์จ์ด ์ด๋ณด๋ค ์์์ง๋ฉด ์ค์ง)
self.centroids = init_centroids # ์ด๊ธฐ ํด๋ฌ์คํฐ ์ค์ฌ [(x1,y1),(x2,y2)]
self.labels = labels # ๊ฐ ์ขํ๊ฐ ์ด๋ค ํด๋ฌ์คํฐ์ ์ํ๋์ง
def fit(self, X):
# 0 ๋จ๊ณ ์๊ฐํ ์ถ๋ ฅ
self.plot_step(X,0)
# ์ด๊ธฐ ์ค์ฌ ์ค์
for i in range(self.max_iter):
# ๊ฐ ๋ฐ์ดํฐ ํฌ์ธํธ์ ๋ํด ๊ฐ์ฅ ๊ฐ๊น์ด ํด๋ฌ์คํฐ ์ค์ฌ์ ์
๋ฐ์ดํธ
self.labels = np.array([self.closest_centroid(x) for x in X])
# ์ด์ ์ค์ฌ์ ์ ์ฅ (์ค์ฌ ์
๋ฐ์ดํธ ํ ๋น๊ตํ๊ธฐ ์ํด)
old_centroids = self.centroids.copy()
# ๊ฐ ํด๋ฌ์คํฐ๋ง๋ค ํด๋น ํด๋ฌ์คํฐ์ ์ํ ์ขํ ๋ชฉ๋ก์ ํ๊ท ํ์ฌ ์๋ก์ด ์ค์ฌ์ ๊ณ์ฐ
for k in range(self.n_clusters):
cluster_points = X[self.labels == k]
if len(cluster_points) > 0:
self.centroids[k] = np.mean(cluster_points, axis=0) # ํด๋ฌ์คํฐ k ์ ์ค์ฌ์ ์ขํ ์
๋ฐ์ดํธ
# ์ค์ฌ์ด ์ผ๋ง๋ ์ด๋ํ๋์ง ๊ณ์ฐ
centroid_shift = np.sum(np.linalg.norm(self.centroids - old_centroids, axis=1))
# ์๊ฐํ: ๊ฐ ๋จ๊ณ์์์ ๊ฒฐ๊ณผ ์ถ๋ ฅ
self.plot_step(X, i + 1)
# ์๋ ด ์ฌ๋ถ ํ์ธ
if centroid_shift < self.tol:
print(f"Converged after {i+1} iterations")
break
# ํน์ ๋ฐ์ดํฐ ํฌ์ธํธ์ ๋ํด ๊ฐ์ฅ ๊ฐ๊น์ด ํด๋ฌ์คํฐ ์ค์ฌ์ ์ฐพ์
def closest_centroid(self, x):
distances = [euclidean_distance(x, centroid) for centroid in self.centroids]
return np.argmin(distances) # ๊ฐ์ฅ ๊ฑฐ๋ฆฌ๊ฐ ๊ฐ๊น์ด ํด๋ฌ์คํฐ์ ์ธ๋ฑ์ค ๋ฐํ
# ๊ฐ ๋จ๊ณ๋ณ ํด๋ฌ์คํฐ๋ง ๊ฒฐ๊ณผ ์๊ฐํ
def plot_step(self, X, step):
cluster_color_set = ['blue','red']
# ๊ฐ ํด๋ฌ์คํฐ ๋ณ ๊ทธ๋ฃน ์๊ฐํ
for k, centroid in enumerate(self.centroids):
plt.scatter(centroid[0], centroid[1], c=cluster_color_set[k], marker='x', s=200, label=f'Centroid {k}')
plt.scatter(X[self.labels == k ][:,0], X[self.labels == k ][:,1], c=cluster_color_set[k])
plt.title(f"K-means Clustering (Step {step})")
plt.legend()
plt.show()
์ด๊ธฐํ
def __init__(self,labels, n_clusters, max_iter=300, tol=1e-5, init_centroids=None):
self.n_clusters = n_clusters # ํด๋ฌ์คํฐ ๊ฐ์ (K=2)
self.max_iter = max_iter # ์ต๋ ๋ฐ๋ณต ํ์
self.tol = tol # ์๋ ด ๊ธฐ์ค (๋ณํ์จ์ด ์ด๋ณด๋ค ์์์ง๋ฉด ์ค์ง)
self.centroids = init_centroids # ์ด๊ธฐ ํด๋ฌ์คํฐ ์ค์ฌ [(x1,y1),(x2,y2)]
self.labels = labels # ๊ฐ ๋ฐ์ดํฐ ํฌ์ธํธ๊ฐ ์ํ๋ ํด๋ฌ์คํฐ ๋ ์ด๋ธ.
- n_clusters: ํด๋ฌ์คํฐ ๊ฐ์(K), ์ฌ๊ธฐ์๋ 2๋ก ์ค์ .
- max_iter: ์ต๋ ๋ฐ๋ณต ํ์. ๊ธฐ๋ณธ๊ฐ์ 300์ผ๋ก ์ค์ .
- tol: ์ค์ฌ์ ์ ์ด๋์ด ์ด ๊ฐ๋ณด๋ค ์์ผ๋ฉด ์๋ ดํ๋ค๊ณ ํ๋จํ๋ ๊ธฐ์ค (1e-5).
- init_centroids: ์ด๊ธฐ ์ค์ฌ์ ์ ์ขํ (csv ํ์ผ์์ ๋ถ๋ฌ์ด).
- labels: ๊ฐ ๋ฐ์ดํฐ ํฌ์ธํธ๊ฐ ์ํ๋ ํด๋ฌ์คํฐ ๋ ์ด๋ธ.
๋ชจ๋ธ ํ์ต
์ฃผ์ด์ง ๋ฐ์ดํฐ X์ ๋ํด K-means ์๊ณ ๋ฆฌ์ฆ์ ์ ์ฉํ๋ ๋ฉ์๋์ด๋ค.
def fit(self, X):
# 0 ๋จ๊ณ ์๊ฐํ ์ถ๋ ฅ
self.plot_step(X,0)
# ์ด๊ธฐ ์ค์ฌ ์ค์
for i in range(self.max_iter):
# ๊ฐ ๋ฐ์ดํฐ ํฌ์ธํธ์ ๋ํด ๊ฐ์ฅ ๊ฐ๊น์ด ํด๋ฌ์คํฐ ์ค์ฌ์ ์
๋ฐ์ดํธ
self.labels = np.array([self.closest_centroid(x) for x in X])
# ์ด์ ์ค์ฌ์ ์ ์ฅ (์ค์ฌ ์
๋ฐ์ดํธ ํ ๋น๊ตํ๊ธฐ ์ํด)
old_centroids = self.centroids.copy()
# ๊ฐ ํด๋ฌ์คํฐ๋ง๋ค ํด๋น ํด๋ฌ์คํฐ์ ์ํ ์ขํ ๋ชฉ๋ก์ ํ๊ท ํ์ฌ ์๋ก์ด ์ค์ฌ์ ๊ณ์ฐ
for k in range(self.n_clusters):
cluster_points = X[self.labels == k]
if len(cluster_points) > 0:
self.centroids[k] = np.mean(cluster_points, axis=0) # ํด๋ฌ์คํฐ k ์ ์ค์ฌ์ ์ขํ ์
๋ฐ์ดํธ
# ์ค์ฌ์ด ์ผ๋ง๋ ์ด๋ํ๋์ง ๊ณ์ฐ
centroid_shift = np.sum(np.linalg.norm(self.centroids - old_centroids, axis=1))
# ์๊ฐํ: ๊ฐ ๋จ๊ณ์์์ ๊ฒฐ๊ณผ ์ถ๋ ฅ
self.plot_step(X, i + 1)
# ์๋ ด ์ฌ๋ถ ํ์ธ
if centroid_shift < self.tol:
print(f"Converged after {i+1} iterations")
break
์ค์ฌ์ ์ด๊ธฐํ ํ ๋ฐ๋ณต ๊ณผ์ :
- 0๋จ๊ณ์์๋ ์ฃผ์ด์ง ์ด๊ธฐ ํด๋ฌ์คํฐ๋ฅผ ๊ธฐ์ค์ผ๋ก ์๊ฐํํ๋ค.
- ๊ฐ ๋ฐ์ดํฐ ํฌ์ธํธ์ ๋ํด ๊ฐ์ฅ ๊ฐ๊น์ด ํด๋ฌ์คํฐ๋ฅผ ํ ๋นํ๊ณ , ํด๋ฌ์คํฐ๋ณ๋ก ์ค์ฌ์ ์ ์ ๋ฐ์ดํธํ๋ค.
- ์ ๋ฐ์ดํธ ๋ ์ดํ์ ๊ฐ ๋จ๊ณ๋ณ๋ก plot ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅํ๋ค.
- ์๋ ด ์ฌ๋ถ ํ์ธ: ๊ฐ ๋ฐ๋ณต ๋จ๊ณ์์ ์ค์ฌ์ ์ ์ด๋๋์ด tol ๊ฐ๋ณด๋ค ์์ผ๋ฉด ์๊ณ ๋ฆฌ์ฆ์ ์ข ๋ฃํ๊ณ ์๋ ดํ๋ค๊ณ ํ๋จํ๋ค.
์ด๋, ๊ฐ ๋ฐ์ดํฐ ํฌ์ธํธ์ ๋ํด ๊ฐ์ฅ ๊ฐ๊น์ด ํด๋ฌ์คํฐ๋ฅผ ์ฐพ๊ธฐ ์ํด์ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ์ฐํ ๋ ๋ค์์ ํจ์๋ฅผ ์ด์ฉํ๋ค.
๊ฐ ์ค์ฌ์ ๊ณผ์ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ์ฐํ์ฌ, ๊ฐ์ฅ ๊ฐ๊น์ด ์ค์ฌ์ ์ ์ธ๋ฑ์ค๋ฅผ ๋ฐํํ๋ค.
# ํน์ ๋ฐ์ดํฐ ํฌ์ธํธ์ ๋ํด ๊ฐ์ฅ ๊ฐ๊น์ด ํด๋ฌ์คํฐ ์ค์ฌ์ ์ฐพ์
def closest_centroid(self, x):
distances = [euclidean_distance(x, centroid) for centroid in self.centroids]
return np.argmin(distances) # ๊ฐ์ฅ ๊ฑฐ๋ฆฌ๊ฐ ๊ฐ๊น์ด ํด๋ฌ์คํฐ์ ์ธ๋ฑ์ค ๋ฐํ
ํด๋ฌ์คํฐ๋ง ๊ฒฐ๊ณผ ์๊ฐํ
๊ฐ ๋ฐ๋ณต ๋จ๊ณ์์์ ํด๋ฌ์คํฐ๋ง ๊ฒฐ๊ณผ๋ฅผ ์๊ฐํํ๋ ํจ์์ด๋ค. ํ์ดํ๋กฏ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ ๊ฐ ์ขํ๊ฐ ์ด๋ค ํด๋ฌ์คํฐ์ ์ํ๋์ง ์ฝ๊ฒ ์ ์ ์๋๋ก ์๊น์ ๊ตฌ๋ถํ์ฌ ์๊ฐํํ๋ค.
# ๊ฐ ๋จ๊ณ๋ณ ํด๋ฌ์คํฐ๋ง ๊ฒฐ๊ณผ ์๊ฐํ
def plot_step(self, X, step):
cluster_color_set = ['blue','red']
# ๊ฐ ํด๋ฌ์คํฐ ๋ณ ๊ทธ๋ฃน ์๊ฐํ
for k, centroid in enumerate(self.centroids):
plt.scatter(centroid[0], centroid[1], c=cluster_color_set[k], marker='x', s=200, label=f'Centroid {k}')
plt.scatter(X[self.labels == k ][:,0], X[self.labels == k ][:,1], c=cluster_color_set[k])
plt.title(f"K-means Clustering (Step {step})")
plt.legend()
plt.show()
์คํ
๋ฐ์ดํฐ ํ์ผ์ ๋ก๋ํ๊ณ , K-means ์๊ณ ๋ฆฌ์ฆ์ ์คํํ๋ค.
def main():
# ๋ฐ์ดํฐ ํ์ผ ๋ก๋
data_file = './data/hw2_data_2d.csv'
centroids_file = './data/hw2_2d_init_centroids.csv'
X, labels = read_data(data_file) # ๋ฐ์ดํฐ ํฌ์ธํธ ๋ก๋
init_centroids = read_centroid(centroids_file) # ์ด๊ธฐ ์ค์ฌ ๋ก๋
# K-means ํด๋ฌ์คํฐ๋ง
kmeans = KMeans(labels=labels ,n_clusters=2, init_centroids=init_centroids)
kmeans.fit(X)
if __name__ == '__main__':
main()
๊ฒฐ๊ณผ
์ด 7๋ฒ์ ๋ฐ๋ณต ํ์ ํด๋ฌ์คํฐ์ ์ค์ฌ์ ์ขํ๊ฐ tol(1e-5) ๋ฏธ๋ง์ผ๋ก ์์ง์ด๊ฒ ๋๋ฏ๋ก ํด๋ฌ์คํฐ๋ง์ด ์๋ฃ๋์๋ค๊ณ ํ๋จํ๋ค.