2017년 3월 15일 수요일

텐서플로우 02 군집화 예시 (TensorFlow Clustering K-means algorithm)

 군집화(Clustering)는 레이블이 없는 데이터를 분석할 때 사전 작업으로 사용하기 좋아 널리 이용하는 방법이다. 자율 학습(Unsupervised learning) 의 일종으로도 볼 수 있다.

 이번 포스트에서는 K-means 라고 하는 군집화 알고리즘을 텐서플로우에서 테스트해볼 것이다. K-means 알고리즘은 데이터를 유사한 것끼리 자동으로 그룹화할 때 사용하는 유명한 알고리즘이다. 알고리즘의 결과는 중심(centroid)이라고 부르는 K 개의 점으로 각기 다른 군집의 중심점을 나타내고 데이터들은 K 개의 군집 중 하나에만 속할 수 있다. 한 군집 내의 모든 데이터들은 다른 군집의 중심보다 자기 군집의 중심과 가깝다.

 군집을 구성할 때 모든 경우에 대해 오차함수를 최소화하려면 NP-hard 문제가 되어 계산하기 곤란하므로 휴리스틱(heuristics) 방법을 사용하여 로컬 최소값에 빠르게 수렴하도록 한다. 널리 사용되는 방법 중 하나는 반복 개선(iterative refinement)기법이다.
 반복개선 기법은 크게 3단계로 나눠진다.
 1) 0단계(초기) : K개 중심의 초기 집합을 결정
 2) 1단계(할당) : 각 데이터를 가장 가까운 군집에 할당
 3) 2단계(업데이트) : 각 그룹에 대해 새로운 중심을 계산
예제에서는 초기 중심을 데이터 중에서 임의로 선택할 것이다.
업데이트의 경우 일반적으로 군집 내 데이터의 변화가 없으면 알고리즘이 수렴되었다고 간주한다.

 휴리스틱 기법을 이용하므로 결과가 최적이라는 보장은 없다는 것을 유의해야 한다.

----------------------- Clustering source code ---------------------------

# 샘플데이터 생성
import numpy as np

num_points = 2000
vectors_set = []

for i in range(num_points):
    if np.random.random() > 0.5:
        vectors_set.append([np.random.normal(0.0, 0.9),
                            np.random.normal(0.0, 0.9)])

    else:
        vectors_set.append([np.random.normal(3.0, 0.5),
                            np.random.normal(1.0, 0.5)])


# matplotlib 기반으로하는 seaborn 시각화 패키지 , 데이터 조작 패키지 pandas
# seaborn 은 Anaconda 에서 conda install seaborn 명령으로 설치
# pandas 는 pip install pandas 명령으로 설치

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

df = pd.DataFrame({"x": [v[0] for v in vectors_set],
                   "y": [v[1] for v in vectors_set]})
sns.lmplot("x", "y", data=df, fit_reg=False, size=6)
plt.show()


# K-means 구현
# 4개의 군집으로 그룹화

import tensorflow as tf

    # 모든 데이터를 상수 텐서로 옮김
vectors = tf.constant(vectors_set)
    # 초기 단계 : 중심 k(4)개를 입력데이터에서 무작위로 선택
k = 4
centroides = tf.Variable(tf.slice(tf.random_shuffle(vectors),[0,0],[k,-1]))
    # vector.get_shape(), centroides.get_shape()
    # 위 주석으로 각 텐서의 구조를 확인해볼 수 있음
 
expanded_vectors = tf.expand_dims(vectors, 0)
expanded_centroides = tf.expand_dims(centroides, 1)

    # 할당 단계 : 유클리드 제곱거리 사용
diff = tf.sub(expanded_vectors, expanded_centroides)
sqr = tf.square(diff)
distances = tf.reduce_sum(sqr, 2)
assignments = tf.argmin(distances,0)

    # 업데이트 : 새로운 중심 계산
means = tf.concat(0,
                  [tf.reduce_mean(
                      tf.gather(vectors,
                                tf.reshape(
                                    tf.where(tf.equal(assignments, c))
                                    ,[1, -1])
                                )
                      , reduction_indices=[1]) for c in range(k)])

update_centroides = tf.assign(centroides, means)

init_op = tf.initialize_all_variables()

sess = tf.Session()
sess.run(init_op)

for step in range(100):
    _, centroid_values, assignment_values = sess.run([update_centroides, centroides, assignments])


# assignment_values 텐서의 결과를 확인

data = {"x": [], "y": [], "cluster": []}

for i in range(len(assignment_values)):
    data["x"].append(vectors_set[i][0])
    data["y"].append(vectors_set[i][1])
    data["cluster"].append(assignment_values[i])

df = pd.DataFrame(data)
sns.lmplot("x", "y", data=df, fit_reg=False, size=6, hue="cluster", legend=False)
plt.show()
 

----------------------- Clustering source code ---------------------------


업데이트 단계 (각 군집에 속한 점들의 평균 계산) 코드 해석
means = tf.concat(0,
                  [tf.reduce_mean(
                      tf.gather(vectors,
                                tf.reshape(
                                    tf.where(tf.equal(assignments, c))
                                    ,[1, -1])
                                )
                      , reduction_indices=[1]) for c in range(k)])

괄호 안쪽부터 차레로 보면
1. equal 함수를 사용하여 assignment 텐서의 각 원소 위치를 True 로 표시하는 불리언 텐서( Dimension(2000) ) 를 만듭니다.
2. where 함수를 사용하여 매개변수 텐서의 True 로 표시된 위치를 가지는 텐서 ( Dimension(2000) * Dimension(1) ) 를 만듭니다.
3. reshape 함수를 사용하여 c 군집에 속한 vectors 텐서의 포인트들의 인덱스로 구성된 텐서 ( Dimension(1) * Dimension(2000) )를 만듭니다.
4. gather 함수를 사용하여 c 군집을 이루는 점들의 좌표를 모은 텐서 ( Dimension(1) * Dimension(2000) * Dimension(2) )를 만듭니다.
5. reduce_mean 함수를 사용하여 c 군집에 속한 모든 점의 평균 값을 가진 텐서 ( Dimension(1) * Dimension(2) )를 만듭니다.


샘플 데이터 ( 2000 sample dots )


군집화 결과 clustering result










2017년 3월 14일 화요일

텐서플로우 01 선형회귀 예시 (TensorFlow Linear Regression)

 선형회귀분석은 변수들 사이의 관계를 분석하는데 사용하는 통계학적 방법이다.
독립변수 x, 상수항 b, 종속변수 y 사이의 관계를 모델링하는 방법으로 두 변수 사이의 관계일 경우 단순회귀라고 하며 여러 개의 변수의 관계일 경우는 다중회귀라 한다.
아래 예시는 y = W * x + b 형태의 간단한 선형회귀 분석 모델을 트레이닝하는 과정을 볼 수 있도록 구현한 것이다.

 변수 x, y 가 y = 0.1 * x + 0.3 관계를 가지면서 오차가 조금씩 있도록 샘플데이터를 생성하였고 매개변수 W, b 가 좋은 값인지 측정하는 오차함수(error function)평균제곱오차(mean square error)로 사용했다.
오차는 반복을 통해 점점 최소화하는데, 값을 최소화 하면서 트레이닝하는 최적화 알고리즘을 경사하강법이라 한다.

 matplot 패키지가 없다pip install matplotlib 명령을 통해 설치하면 된다.

 파이썬 3 버전 이상에서 동작하는 소스코드이며 그 이하 버전의 경우는 수정할 것이 있다.
 range -> xrange   ( 전자가 파이썬 3 이상버전 )
   


---------------------- Linear Regression source code ------------------
import numpy as np


# 임의의 좌표값 생성
# y = 0.1 * x + 0.3 관계를 가지는 데이터에 정규분포를 따르는 오차를 넣음

num_points = 1000
vectors_set = []

for i in range(num_points):
    x1 = np.random.normal(0.0, 0.55)
    y1 = x1 * 0.1 + 0.3 + np.random.normal(0.0, 0.03)
    vectors_set.append([x1, y1])

x_data = [v[0] for v in vectors_set]
y_data = [v[1] for v in vectors_set]


# 비용함수 (오차함수)
import tensorflow as tf

W = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
b = tf.Variable(tf.zeros([1]))
y = W * x_data + b

loss = tf.reduce_mean(tf.square(y - y_data))

# 최적화 알고리즘 사용 (경사하강법)
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)


# 알고리즘 실행 1 - 초기화 실행
init = tf.initialize_all_variables()

sess = tf.Session()
sess.run(init)


# 알고리즘 실행 2 - 훈련 반복 횟수
# pip install matplotlib 설치 필요
import matplotlib.pyplot as plt

for step in range(16):
    sess.run(train)

    print(step, sess.run(W), sess.run(b))
    print(step, sess.run(loss))
# 그림으로 결과를 보고 싶을 때
    plt.plot(x_data, y_data, 'ro')
    plt.plot(x_data, sess.run(W) * x_data + sess.run(b))
    plt.xlabel('x')
    plt.xlim(-2, 2)
    plt.ylim(0.1, 0.6)
    plt.ylabel('y')
    plt.legend()
    plt.show()

---------------------- Linear Regression source code ------------------

실행 결과 예시

반복될 수록 W, b 의 값이 각각 0.1 과 0.3 에 수렴하는 것을 알 수 있다.