마. 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()

스크린샷, 2016-09-01 19-30-15

Categories: TensorFlow

Leave a Reply

Your email address will not be published. Required fields are marked *