헬창 개발자

em클러스터링 본문

데이터 분석

em클러스터링

찬배 2022. 6. 7. 11:28

EM 클러스터링: 이론과 실습

1. EM 클러스터링이란?

EM 클러스터링은 통계적 기법 중 하나로, 데이터의 숨겨진 구조를 찾기 위해 사용하는 알고리즘입니다. EM(Expectation-Maximization) 알고리즘은 데이터가 숨겨진 잠재 변수를 가지고 있을 때 그 변수들을 추정하고, 이를 통해 데이터의 클러스터를 식별합니다.

2. 기본 개념

  • Expectation (E-단계): 현재의 파라미터를 기반으로 데이터의 잠재 변수(또는 클러스터의 분포)를 추정합니다. 이 단계에서는 현재의 파라미터 추정을 사용하여 각 데이터 포인트가 각 클러스터에 속할 확률을 계산합니다.
  • Maximization (M-단계): E-단계에서 계산된 확률을 사용하여 모델의 파라미터를 업데이트합니다. 즉, 데이터 포인트가 각 클러스터에 속할 확률을 최대화하는 방향으로 파라미터를 조정합니다.

이 과정은 수렴할 때까지 반복되며, 최종적으로 클러스터의 경계와 중심을 결정합니다.

3. 알고리즘 설명

  1. 초기화: 클러스터의 초기 중심을 설정합니다.
  2. E-단계: 각 데이터 포인트가 각 클러스터에 속할 확률을 계산합니다.
  3. M-단계: 클러스터의 중심을 업데이트하여 최대화합니다.
  4. 반복: E-단계와 M-단계를 반복하여 알고리즘이 수렴할 때까지 진행합니다.

4. 소스코드

import numpy as np
import math
from scipy.stats import norm

class EMClustering:
    def __init__(self):
        # 파라미터 초기화
        self.weight = 0.2 # 가중치
        self.means = np.array([30, 20]) # 평균
        self.bias = np.array([4, 3]) # 표준편차 
        
        # 정규 분포를 통한 입력 데이터 생성, k=2
        self.c1 = np.random.normal(7, 3, size = 10) # 평균 7, 표준편차 3, 데이터 개수 10
        self.c2 = np.random.normal(20, 3, size = 10) # 평균 20, 표준편차 3, 데이터 개수 10
        self.x = np.append(self.c1, self.c2) # 입력 데이터 생성
        print("초기 클러스터 파라미터")
        print("c1 클러스터\t", "가중치:", self.weight, " 평균:", self.means[0], " 표준편차:", self.bias[0])
        print(np.round(self.c1, 2))
        print("c2 클러스터\t", "가중치:", 1-self.weight, " 평균:", self.means[1], " 표준편차:", self.bias[1])
        print(np.round(self.c2, 2))
        print('--'*40)
        
    def E_step(self): # E-step: xi의 기대값 P(C_j|X_i) 계산
        expected_values = [[] for _ in range(2)] # 2개의 클러스터 기대값 저장할 리스트 생성
        for xi in self.x: # 입력 데이터를 순차적으로 xi에 반환

            N1 = norm(loc = self.means[0], scale = self.bias[0]) # 평균=30, 표준편차=4인 정규 분포 객체 생성
            N2 = norm(loc = self.means[1], scale = self.bias[1]) # 평균=20, 표준편차=3인 정규 분포 객체 생성
            
            wp1 = self.weight * N1.pdf(xi) # 가중치 * n1객체의 확률밀도함수
            wp2 = (1-self.weight) * N2.pdf(xi) # 가중치(가중치의 합은 1) * n2객체의 확률밀도함수

            den = wp1 + wp2 # 분모 

            c1 = wp1 / den # P(C_1|x_i)의 값
            c2 = wp2 / den # P(C_2|x_i)의 값
            
            expected_values[0].append(c1) # c1 기대값 추가
            expected_values[1].append(c2) # c2 기대값 추가
          
        return expected_values # 기대값 리스트 반환
    
    def M_step(self, expected_value): # M-step: 클러스터 가중치, 클러스터의 평균과 표준편차 업데이트
        for j in range(2):
            # 클러스터의 가중치 업데이트
            self.weight = 1 / len(expected_value[j]) * sum(expected_value[j]) # 1/n * sum(P(C_j|x_i))

            # 클러스터의 정규분포 평균 업데이트
            self.means[j] = np.sum(self.x * expected_value[j])/np.sum(expected_value[j]) # sum(x_i * P(C_j|x_i)) / sum(P(C_j|x_i))

            # 클러스터의 정규분포 표준편차 업데이트
            self.bias[j] = math.sqrt(np.sum((self.x - self.means[j])**2 * expected_value[j]) / np.sum(expected_value[j])) # sum((x_i - 평균)^2 * sum(P(C_j|x_i)) / sum(P(C_j|x_i))

    def result(self): # 출력 함수
        print("10번째 클러스터 파라미터")
        print("c1 클러스터\t", "가중치:", self.weight, " 평균:", self.means[0], " 표준편차:", self.bias[0])
        print(np.round(self.c1, 2))
        print("c2 클러스터\t", "가중치:", 1-self.weight, " 평균:", self.means[1], " 표준편차:", self.bias[1])
        print(np.round(self.c2, 2))
        
        
    def run(self): # 실행 함수
        for _ in range(10):
            # E-step
            exp = self.E_step()
            # M -step
            self.M_step(exp)
        self.result()

if __name__=='__main__':
    aaaaaaaaa = EMClustering()
    aaaaaaaaa.run()

Comments