클러스터링 K-Means 직접구현
Python/인공지능

클러스터링 K-Means 직접구현

728x90

이번에는 K-Means를 직접 구현 해볼것이다.

 

우선 클러스터링에 대해서 간략하게 설명부터 해보자!

 

만약 위처럼 사과,배,바나나를 분류하는 문제를 구현해본다고 생각해보자. 이전에 배웠던 인공신경망을 활용할 수도 있겠지만 이번에는 클러스터링을 이용하여 군집을 나누어 볼 것이다.

 

어떠한 특성을 잘 집어낸다면 결국 사과,배,바나나들은 군집처럼 분류될 것이다. 이들의 군집의 중심점을 찾은 후, 근처에 있는것들을 한 군집을 분다면 우리는 분류를 할 수 있다.

 

클러스터링의 기본적인 프로세스는 아래와 같다.

 

 

즉 랜덤의 중심점을 설정한 후에, 가까운곳들을 해당 군집으로 묶어준다. 그이후 같은 군집내의 중심을 다시 중심점으로 잡은 후에 군집의 분류를 시켜준다. 이를 계속 반복하여 군집이 더이상 변하지 않을떄까지 한다면, 우리는 군집의 분류를 할 수 있다.

 

클러스터링의 장점은 인공신경망에 비해 연산량이 적고 많은 데이터셋에 적용이 가능하다는 것이다. 하지만 첫 중심점에 따라 결과가 상이하게 나오는 단점이 있다. 즉, 첫 중심점에 따라 결과가 다르게 나올 수 있다는 것이다.

 

라이브러리를 사용하면 K-means 알고리즘을 쉽게 구현해볼 수 있다.

 

Mall_Customers.csv
0.00MB

여러 속성에 따른 고객들을 분류해보는 문제를 적용시켜보자.

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from sklearn.cluster import KMeans

import os
current_path = os.path.dirname(os.path.abspath(__file__))

df = pd.read_csv("Mall_Customers.csv")
print(df)
data = df[['Annual Income (k$)', 'Spending Score (1-100)']]

k = 3
model = KMeans(n_clusters = k, init = 'k-means++', random_state = 10)

def elbow(X):
    sse = []
    for i in range(1,11):
        km = KMeans(n_clusters=i,init='k-means++',random_state=0)
        km.fit(X)
        sse.append(km.inertia_)

    plt.plot(range(1,11),sse,marker='o')
    plt.xlabel('# of clusters')
    plt.ylabel('Inertia')
    plt.show()
elbow(data)

df['cluster'] = model.fit_predict(data)

final_centroid = model.cluster_centers_
print(final_centroid)

plt.figure(figsize=(8,8))
for i in range(k):
    plt.scatter(df.loc[df['cluster'] == i, 'Annual Income (k$)'], df.loc[df['cluster'] == i, 'Spending Score (1-100)'], label = 'cluster' + str(i))

plt.scatter(final_centroid[:,0], final_centroid[:,1],s=50,c='violet',marker = 'x', label = 'Centroids')
plt.legend()
plt.title(f'K={k} results',size = 15)
plt.xlabel('Annual Income',size = 12)
plt.ylabel('Spending Score',size = 12)
plt.show()

군집분류

 

학습에 따라서 분류가 잘 된것을 알 수 있다.

 

자, 그렇다면 직접구현을 해보자. 해당 알고리즘을 만들기 위해서는 중심점과 속성들과의 거리를 정하는것이 중요하다.

 

거리에는 여러가지 거리가 있을 수 있다. 일반적인 좌표계에서 사용하는 거리나 맨해튼 거리 등등 여러가지가 있지만 선택하여 쓰면 된다.

 

해당 csv파일을 보면 속성이 4개가 있다. 즉, 중심점과 4개의 속성들간의 거리를 잰 후, 가장 가깝다고 생각하는 군집에 넣어주고 위에서 설명한 알고리즘 방식을 반복하면 된다.

 

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os

current_path = os.path.dirname(os.path.abspath(__file__)) #파일 불러읽기
raw_data = pd.read_csv('Mall_Customers.csv',encoding='utf-8', engine = 'python')
Customers_data = raw_data[['Gender','Age','Annual Income (k$)','Spending Score (1-100)']].to_numpy()    # x 데이터 받기
for i in range(len(Customers_data)): #남성,여성을 숫자로 생각할 수 있게
    if Customers_data[i][0] =='Male':
        Customers_data[i][0] = 1
    else:
        Customers_data[i][0] = 0
Annual_Income = raw_data[['Annual Income (k$)']].to_numpy()
Spending_Score = raw_data[['Spending Score (1-100)']].to_numpy()

plt.scatter(Annual_Income, Spending_Score,s=50,c='violet',marker = 'o', label = 'Centroids')
plt.legend()
plt.title(f'results',size = 15)
plt.xlabel('Annual Income',size = 12)
plt.ylabel('Spending Score',size = 12)
plt.show() #데이터 그래프 표시

class K_Means: #K-Means 알고리즘을 클래스로 구현
    def __init__(self,k,data,p): #k,학습시킬데이터,p를 받아옴
        self.k = k
        self.data = data
        self.p = p
        self.N = len(data)
        self.standard = []
        self.Cluster = np.array([0 for _ in range(self.N)])#군집을 저장하는 공간
        while(len(self.standard) < k): #k값에 따라 처음값 설정
            tmp = np.random.randint(self.N) #중복값은 설정 안되게설정
            if tmp not in self.standard:
                self.standard.append(tmp)
                self.Cluster[tmp] = len(self.standard) #초기군집을 1,2,3으로 넣어줌

        for i in range(k): #계산하기 편하게 처음 세개 값을 index가 아닌 실제값으로 변환
            self.standard[i] = self.data[self.standard[i]]

    def distance(self,I,J): #거리 구하는 함수
        return sum(abs(I-J)**self.p)**(1/self.p)

    def Clustering(self): #Clustering 알고리즘
        for _ in range(100): ##iteration 100회로 임의 설정
            Old_Cluster = np.array(self.Cluster)
            for i in range(self.N):
                dist = []
                for standard in self.standard:
                    dist.append(self.distance(self.data[i],standard))
                self.Cluster[i] = np.argmin(dist) + 1

            Clusters = [[] for __ in range(self.k + 1)]
            for i in range(self.N):
                Clusters[self.Cluster[i]].append(self.data[i])
            for i in range(1,self.k+1):
                Clusters[i] = np.array(Clusters[i])
                self.standard[i-1] = Clusters[i].mean(axis=0)

            #cluster가 변화없어질때 종료조건을 넣을 수 있다.
            # if np.array_equal(Old_Cluster,self.Cluster):
            #     break



Algorism = K_Means(k = 4,data = Customers_data,p=1) #모델 구현
Algorism.Clustering()#Clustering 실행
Annual = [ [] for _ in range(Algorism.k+1)] #값을 볼 수 있는 형태로 변환
Spending = [ [] for _ in range(Algorism.k+1)]
for i in range(Algorism.N): #같은 군집끼리 값을 모은다
    Annual[Algorism.Cluster[i]].append(Annual_Income[i])
    Spending[Algorism.Cluster[i]].append(Spending_Score[i])

for i in range(1,Algorism.k+1): # k개의 군집 출력
    plt.scatter(Annual[i], Spending[i],s=50,marker = 'o', label = f'cluster{i}')
    plt.scatter(Algorism.standard[i-1][2],Algorism.standard[i-1][3],label=f"{i}'s Centroids",marker='x')#중심점
plt.legend()
plt.title(f'results',size = 15)
plt.xlabel('Annual Income',size = 12)
plt.ylabel('Spending Score',size = 12)
plt.show()
728x90