마. Clustering
이번 예제는 조금 복잡하다. K-Mean 클러스터링의 개념에 대해서 이해해야 하고, 몇몇 TensorFlow 의 Matrix 연산 메서드를 이해해야 한다. 아래는 전체 테스트 코드이다.
열심히 데이터가 따라가는 것을 체크하느라 실제 연산과는 상관없는 로그들이 포함되어 있다.
# -*- coding: utf-8 -*-
import tensorflow as tf
import numpy as np
# 랜덤하게 클러스터 데이터를 구성합니다.
def create_samples(n_clusters, n_samples_per_cluster, n_features, embiggen_factor, seed):
np.random.seed(seed)
slices = []
centroids = []
# Create samples for each cluster
for i in range(n_clusters):
# 평균이 0이고 표준편차가 5이고 2개의 데이터로 이루어진 500개의 랜덤 데이터 Pool 을 생성합니다
samples = tf.random_normal((n_samples_per_cluster, n_features),
mean=0.0, stddev=5.0, dtype=tf.float32, seed=seed, name="cluster_{}".format(i))
# 랜덤으로 중앙값을 만듭니다. 형태는 [X , Y] 가 되겠습니다.
current_centroid = (np.random.random((1, n_features)) * embiggen_factor) - (embiggen_factor/2)
# 각 그룹들의 중앙값을 무엇으로 만들었는지 그 이력을 저장합니다.
centroids.append(current_centroid)
# 이렇게 생성된 중앙 값을 500개의 랜덤 데이터에 더함으로써 해당 분포를 전체적으로 옮기도록 합니다.
samples += current_centroid
#최종적으로 완성된 각 클러스터별 데이터 셋을 저장합니다
slices.append(samples)
# 모든 클러스터 그룹의 그룹 데이터와 중앙값을 탠소플로우 변수로 정의하여 리턴합니다.
samples = tf.concat(0, slices, name='samples')
centroids = tf.concat(0, centroids, name='centroids')
return centroids, samples
# all_samples 전체 샘플 배열( x, y 좌표로 구성) , 각 그룹의 중앙값 배열 , 클러스터의 수
def plot_clusters(all_samples, centroids, n_samples_per_cluster):
import matplotlib.pyplot as plt
# 0 ~ 1 구간을 len(centroids) 센터의 수 만큼으로 균등하게 나눈 배열을 생성함
# linespace :[ 0. 0.5 1. ]
print("linespace :{0}".format(np.linspace(0,1,len(centroids))))
# 0 ~ 1 구간에 대응하는 색상 스팩트럼에서 대응하는 색상을 리턴
# colur:[[1.00000000e+00 3.03152674e-01 1.53391655e-01 1.00000000e+00]
# [1.00000000e+00 1.22464680e-16 6.12323400e-17 1.00000000e+00]
print("colur :{0}".format(plt.cm.rainbow([0.9, 1, 2, 3])))
# 각 그룹의 색상을 다르게 해주기 위하여 RGB 색상 추출
colour = plt.cm.rainbow(np.linspace(0,1,len(centroids)))
# 중앙 값 배열을 기준으로 중앙점과 각 그룹에 해당하는 점을 표시
for i, centroid in enumerate(centroids):
# 각 배열에 해당하는 0 ~ 500 , 500 ~ 1000 , 1000 ~ 1500 구간을 나누어 색상을 지정하고 출력한다
samples = all_samples[i*n_samples_per_cluster:(i+1)*n_samples_per_cluster]
plt.scatter(samples[:,0], samples[:,1], c=colour[i])
# 중앙점을 표시한다
plt.plot(centroid[0], centroid[1], markersize=35, marker="x", color='k', mew=10)
plt.plot(centroid[0], centroid[1], markersize=30, marker="x", color='m', mew=5)
plt.show()
# https://www.tensorflow.org/versions/r0.10/api_docs
# 클러스터 중심좌표 3개를 랜덤으로 섞어서 다시 리턴
def choose_random_centroids(samples, n_clusters):
# tf.shape(input, name=None)
# 't' is [[[1, 1, 1], [2, 2, 2]], [[3, 3, 3], [4, 4, 4]]]
# shape(t) == > [3]
n_samples = tf.shape(samples)[0]
# shape Test
print("tf.shape Test! ")
t = [[[1, 1, 1], [2, 2, 2]], [[3, 3, 3], [4, 4, 4]]]
with tf.Session() as session:
tt = tf.shape(t)
rt = session.run(tt[0])
print("tf.shape Test Result: {0} , {1}".format(tt, rt))
# tf.random_shuffle
# [[1, 2], [[5, 6],
# [3, 4], ==> [1, 2],
# [5, 6]] [3, 4]]
random_indices = tf.random_shuffle(tf.range(0, n_samples))
begin = [0, ]
size = [n_clusters, ]
size[0] = n_clusters
# tf.slice ( input, form array , to array )
centroid_indices = tf.slice(random_indices, begin, size)
# gather mixed cluster again
initial_centroids = tf.gather(samples, centroid_indices)
return initial_centroids
#
def assign_to_nearest(samples, centroids):
# expand_dims test
# 't2' is a tensor of shape [2, 3, 5]
# expand_dims ( data , location to add dim)
with tf.Session() as session:
t2 = [[2,3,5,6,7,8,9],[9,8,7,6,5,4,3,]]
print("expand_before shape : {0}".format(session.run(tf.shape(t2))))
print("expand_dims test : {0}, {1}".format(session.run(tf.shape(tf.expand_dims(t2, 0))),
session.run(tf.expand_dims(t2, 0))))
print("expand_dims test : {0}, {1}".format(session.run(tf.shape(tf.expand_dims(t2, 1))),
session.run(tf.expand_dims(t2, 1))))
print("expand_dims test : {0}, {1}".format(session.run(tf.shape(tf.expand_dims(t2, -1))),
session.run(tf.expand_dims(t2, -1))))
# 차원을 추가한다
expanded_vectors = tf.expand_dims(samples, 0)
expanded_centroids = tf.expand_dims(centroids, 1)
# (1) tf.reduce_sum
## 'x' is [[1, 1, 1]
# [1, 1, 1]]
#tf.reduce_sum(x) ==> 6
#tf.reduce_sum(x, 0) ==> [2, 2, 2]
#tf.reduce_sum(x, 1) ==> [3, 3]
#tf.reduce_sum(x, 1, keep_dims=True) ==> [[3], [3]]
#tf.reduce_sum(x, [0, 1]) ==> 6
# (2) tf.square(X) = x^2
# 모든 점들과 위에서 구한 랜덤 중앙점간의 거리의 합을 구한다
distances = tf.reduce_sum( tf.square(
tf.sub(expanded_vectors, expanded_centroids)), 2)
# 가장 가까운 거리를 구한다
mins = tf.argmin(distances, 0)
#
nearest_indices = mins
return nearest_indices
# 새롭게 구한 중앙점 데이터를 업데이트
def update_centroids(samples, nearest_indices, n_clusters):
# Updates the centroid to be the mean of all samples associated with it.
nearest_indices = tf.to_int32(nearest_indices)
partitions = tf.dynamic_partition(samples, nearest_indices, n_clusters)
new_centroids = tf.concat(0, [tf.expand_dims(tf.reduce_mean(partition, 0), 0) for partition in partitions])
return new_centroids
n_features = 2
n_clusters = 3
n_samples_per_cluster = 500
seed = 700
embiggen_factor = 70
# 복수 그룹의 군집 데이터를 만든다 의
data_centroids, samples = create_samples(n_clusters, n_samples_per_cluster, n_features, embiggen_factor, seed)
# 랜덤으로 중앙점을 생성한다
initial_centroids = choose_random_centroids(samples, n_clusters)
# 랜덤한 중앙점이 가장 가까운 그룹을 찾는다
nearest_indices = assign_to_nearest(samples, initial_centroids)
#
updated_centroids = update_centroids(samples, nearest_indices, n_clusters)
# 데이터 확인
with tf.Session() as session:
print("[step 1] cent : {0} , sample : {1}".format(session.run(data_centroids), session.run(data_centroids)))
print("[step 2] cent : {0} ".format(session.run(initial_centroids)))
print("[step 3] cent : {0} ".format(session.run(nearest_indices)))
print("[step 4] cent : {0} ".format(session.run(updated_centroids)))
# 세션에서 연산을 수행한다
model = tf.initialize_all_variables()
with tf.Session() as session:
sample_values = session.run(samples)
updated_centroid_value = session.run(updated_centroids)
print(updated_centroid_value)
# 그래프를 출력한다
# 맷랩은 슈퍼유저로 실행하면 애러난다
plot_clusters(sample_values, updated_centroid_value, n_samples_per_cluster)
[데이터 준비]
데이터를 준비하는 함수입니다. 이번 예제는 군집분석의 종류중 하나인 K-Mean Clustering 을 TensorFlow 사용하여 분석하는 것 입니다. 이를 위해 데이터를 구성하는데 아래와 같이 우선 적정한 분산을 갖는 500개의 데이터 그룹을 만들어 냅니다.
# 평균이 0이고 표준편차가 5이고 2개의 데이터로 이루어진 500개의 랜덤 데이터 Pool 을 생성합니다
samples = tf.random_normal((n_samples_per_cluster, n_features),
mean=0.0, stddev=5.0, dtype=tf.float32, seed=seed, name="cluster_{}".format(i))
그 후에 랜덤으로 3개의 중앙점을 만들어 냅니다. 그리고 해당 중앙값을 아까 만들었던 랜덤 데이터에 더해 줌으로써 각각의 데이터를 Shifting 을 해줍니다. 그러면 3개의 다른 그룹이 생성이 되겠지요. 아마 그림으로 표현하여 아래와 같이 될 것입니다.
X 마크는 제외하고 뒤에 점들이 생성이 되었다 이렇게 보시면 되겠습니다.
[데이터 생성 전체 코드]
# -*- coding: utf-8 -*-
import tensorflow as tf
import numpy as np
# 랜덤하게 클러스터 데이터를 구성합니다.
def create_samples(n_clusters, n_samples_per_cluster, n_features, embiggen_factor, seed):
np.random.seed(seed)
slices = []
centroids = []
# Create samples for each cluster
for i in range(n_clusters):
# 평균이 0이고 표준편차가 5이고 2개의 데이터로 이루어진 500개의 랜덤 데이터 Pool 을 생성합니다
samples = tf.random_normal((n_samples_per_cluster, n_features),
mean=0.0, stddev=5.0, dtype=tf.float32, seed=seed, name="cluster_{}".format(i))
# 랜덤으로 중앙값을 만듭니다. 형태는 [X , Y] 가 되겠습니다.
current_centroid = (np.random.random((1, n_features)) * embiggen_factor) - (embiggen_factor/2)
# 각 그룹들의 중앙값을 무엇으로 만들었는지 그 이력을 저장합니다.
centroids.append(current_centroid)
# 이렇게 생성된 중앙 값을 500개의 랜덤 데이터에 더함으로써 해당 분포를 전체적으로 옮기도록 합니다.
samples += current_centroid
#최종적으로 완성된 각 클러스터별 데이터 셋을 저장합니다
slices.append(samples)
# 모든 클러스터 그룹의 그룹 데이터와 중앙값을 탠소플로우 변수로 정의하여 리턴합니다.
samples = tf.concat(0, slices, name='samples')
centroids = tf.concat(0, centroids, name='centroids')
return centroids, samples
[랜덤 중앙점 생성]
데이터는 생성을 하였고 이제 각 그룹의 중심을 찾는 로직을 테스트하기 위하여 중심좌표를 랜덤으로 섞어서 다시 리턴합니다.
# https://www.tensorflow.org/versions/r0.10/api_docs
# 클러스터 중심좌표 3개를 랜덤으로 섞어서 다시 리턴
def choose_random_centroids(samples, n_clusters):
# tf.shape(input, name=None)
# 't' is [[[1, 1, 1], [2, 2, 2]], [[3, 3, 3], [4, 4, 4]]]
# shape(t) == > [3]
n_samples = tf.shape(samples)[0]
# shape Test
print("tf.shape Test! ")
t = [[[1, 1, 1], [2, 2, 2]], [[3, 3, 3], [4, 4, 4]]]
with tf.Session() as session:
tt = tf.shape(t)
rt = session.run(tt[0])
print("tf.shape Test Result: {0} , {1}".format(tt, rt))
# tf.random_shuffle
# [[1, 2], [[5, 6],
# [3, 4], ==> [1, 2],
# [5, 6]] [3, 4]]
random_indices = tf.random_shuffle(tf.range(0, n_samples))
begin = [0, ]
size = [n_clusters, ]
size[0] = n_clusters
# tf.slice ( input, form array , to array )
centroid_indices = tf.slice(random_indices, begin, size)
# gather mixed cluster again
initial_centroids = tf.gather(samples, centroid_indices)
return initial_centroids
[근접 그룹 탐색]
위에서 구한 중앙점에서 각 500개 데이터를 갖고 있는 그룹의 점들과의 거리의 합을 구해서 각 그룹간의 거리의 합이 가장 적은 중앙점을 각 그룹으로 다시 맵핑하기 위한 작업
def assign_to_nearest(samples, centroids):
# expand_dims test
# 't2' is a tensor of shape [2, 3, 5]
# expand_dims ( data , location to add dim)
with tf.Session() as session:
t2 = [[2,3,5,6,7,8,9],[9,8,7,6,5,4,3,]]
print("expand_before shape : {0}".format(session.run(tf.shape(t2))))
print("expand_dims test : {0}, {1}".format(session.run(tf.shape(tf.expand_dims(t2, 0))),
session.run(tf.expand_dims(t2, 0))))
print("expand_dims test : {0}, {1}".format(session.run(tf.shape(tf.expand_dims(t2, 1))),
session.run(tf.expand_dims(t2, 1))))
print("expand_dims test : {0}, {1}".format(session.run(tf.shape(tf.expand_dims(t2, -1))),
session.run(tf.expand_dims(t2, -1))))
# 차원을 추가한다
expanded_vectors = tf.expand_dims(samples, 0)
expanded_centroids = tf.expand_dims(centroids, 1)
# (1) tf.reduce_sum
## 'x' is [[1, 1, 1]
# [1, 1, 1]]
#tf.reduce_sum(x) ==> 6
#tf.reduce_sum(x, 0) ==> [2, 2, 2]
#tf.reduce_sum(x, 1) ==> [3, 3]
#tf.reduce_sum(x, 1, keep_dims=True) ==> [[3], [3]]
#tf.reduce_sum(x, [0, 1]) ==> 6
# (2) tf.square(X) = x^2
# 모든 점들과 위에서 구한 랜덤 중앙점간의 거리의 합을 구한다
distances = tf.reduce_sum( tf.square(
tf.sub(expanded_vectors, expanded_centroids)), 2)
# 가장 가까운 거리를 구한다
mins = tf.argmin(distances, 0)
#
nearest_indices = mins
return nearest_indices
[ 화면에 출력하기 위해서 모든 데이터를 합치는 작업]
# 새롭게 구한 중앙점 데이터를 업데이트
def update_centroids(samples, nearest_indices, n_clusters):
# Updates the centroid to be the mean of all samples associated with it.
nearest_indices = tf.to_int32(nearest_indices)
partitions = tf.dynamic_partition(samples, nearest_indices, n_clusters)
new_centroids = tf.concat(0, [tf.expand_dims(tf.reduce_mean(partition, 0), 0) for partition in partitions])
return new_centroids
n_features = 2
n_clusters = 3
n_samples_per_cluster = 500
seed = 700
embiggen_factor = 70
# 복수 그룹의 군집 데이터를 만든다 의
data_centroids, samples = create_samples(n_clusters, n_samples_per_cluster, n_features, embiggen_factor, seed)
# 랜덤으로 중앙점을 생성한다
initial_centroids = choose_random_centroids(samples, n_clusters)
# 랜덤한 중앙점이 가장 가까운 그룹을 찾는다
nearest_indices = assign_to_nearest(samples, initial_centroids)
#
updated_centroids = update_centroids(samples, nearest_indices, n_clusters)
# 데이터 확인
with tf.Session() as session:
print("[step 1] cent : {0} , sample : {1}".format(session.run(data_centroids), session.run(data_centroids)))
print("[step 2] cent : {0} ".format(session.run(initial_centroids)))
print("[step 3] cent : {0} ".format(session.run(nearest_indices)))
print("[step 4] cent : {0} ".format(session.run(updated_centroids)))
# 세션에서 연산을 수행한다
model = tf.initialize_all_variables()
with tf.Session() as session:
sample_values = session.run(samples)
updated_centroid_value = session.run(updated_centroids)
print(updated_centroid_value)
# 그래프를 출력한다
# 맷랩은 슈퍼유저로 실행하면 애러난다
plot_clusters(sample_values, updated_centroid_value, n_samples_per_cluster)
[데이터 편집 및 그래프 출력]
데이터를 화면에 출력하기 위한 함수입니다. 결과는 아래와 같은 그래프입니다.
루트 권한으로 실행하면 애러가 나니 주의하시면 되겠습니다.
# all_samples 전체 샘플 배열( x, y 좌표로 구성) , 각 그룹의 중앙값 배열 , 클러스터의 수
def plot_clusters(all_samples, centroids, n_samples_per_cluster):
import matplotlib.pyplot as plt
# 0 ~ 1 구간을 len(centroids) 센터의 수 만큼으로 균등하게 나눈 배열을 생성함
# linespace :[ 0. 0.5 1. ]
print("linespace :{0}".format(np.linspace(0,1,len(centroids))))
# 0 ~ 1 구간에 대응하는 색상 스팩트럼에서 대응하는 색상을 리턴
# colur:[[1.00000000e+00 3.03152674e-01 1.53391655e-01 1.00000000e+00]
# [1.00000000e+00 1.22464680e-16 6.12323400e-17 1.00000000e+00]
print("colur :{0}".format(plt.cm.rainbow([0.9, 1, 2, 3])))
# 각 그룹의 색상을 다르게 해주기 위하여 RGB 색상 추출
colour = plt.cm.rainbow(np.linspace(0,1,len(centroids)))
# 중앙 값 배열을 기준으로 중앙점과 각 그룹에 해당하는 점을 표시
for i, centroid in enumerate(centroids):
# 각 배열에 해당하는 0 ~ 500 , 500 ~ 1000 , 1000 ~ 1500 구간을 나누어 색상을 지정하고 출력한다
samples = all_samples[i*n_samples_per_cluster:(i+1)*n_samples_per_cluster]
plt.scatter(samples[:,0], samples[:,1], c=colour[i])
# 중앙점을 표시한다
plt.plot(centroid[0], centroid[1], markersize=35, marker="x", color='k', mew=10)
plt.plot(centroid[0], centroid[1], markersize=30, marker="x", color='m', mew=5)
plt.show()
