[Python] Tensorflow 기초 + 군집분석(K-means)

군집분석

텐서플로우를 활용한 군집화

  • 출처 : 텐서플로우 첫걸음(한빛 미디어-박해선)
  • 지도학습 : 모든 데이터에 레이블이 존재함
  • 비지도 학습 : 데이터에 레이블이 존재 하지 않음
  • k-평균 알고리즘 : 데이터를 유사한 것끼리 구룹화 할때 사용하는 알고리즘
  • 텐셔를 이용해 K-평균 알고리즘 예재를 풀어봅시다

1. 기본자료구조 : 텐서

  • 텐서플로우는 텐서라는 기본 자료구조로 모든 데이터를 표현함
  • 텐서는 동적 크기를 갖는 다차원 데이터 배열로 문자, 논리, 숫자 등의 정적 자료형을 가짐
  • 각 텐서는 배열의 차원을 나타내는 랭크를 가짐
  • 랭크가 1인 텐서는 벡터, 랭크가 2인 텐서는 행렬이라고 볼수 있음(랭크 0인경우 스칼라값)
  • 텐서플로우 공식문서에서는 구조, 랭크, 차원번호라는 3가지 명칭사용
텐서플로우 자료형 파이썬자료형 설명
DT.FLOAT tf.float32 32비트 실수
DT.INT16 tf.int16 16비트 정수
DT.INT32 tf.int32 32비트 정수
DT.INT64 tf.int64 64비트 정수
DT.STRING tf.string 문자열
DT.Bool tf.bool 불리언
  • 텐서플로우는 텐서를 다루는 여러가지 변환함수를 제공함
함수 설명
tf.shape 텐서의 구조를 알아냄
tf.size 텐서의 크기를 알아냄
tf.rank 텐서의 랭크를 알아냄
tf.reshape 텐서의 원소는 유지하면서 텐서의 구조를 바꿈
tf.squeeze 텐서의 크기가 1인 차원을 삭제
tf.expand_dims 텐서에 차원을 추가
tf.slice 텐서의 일부분을 삭제
tf.split 텐서의 한차원을 기준으로 여러개의 텐서로 나눔
tf.tile 한텐서를 여러번 중복해서 늘려 텐서를 만듬
tf.concat 한차원을 기준으로 텐서를 이어붙임
tf.reverse 텐서의 지정된 차원을 역전시킴
tf.transpose 텐서를 전치함

2.데이터를 얻는 방법

  • 1) 데이터 파일로 부터 얻기 : 텐서플로우 사이트 활용문서 참고(http://goo.gl/r3VnFg)
  • 2) 변수와 상수
    • tf.constant():상수를 생성하는 함수
    • tf.variable():변수를 생성하는 함수
  • 텐서를 생성하는 함수
함수 설명
tf.zeros_like 모든 원소를 0으로 초기화 한 텐서생성
tf.one_like 모든 원소를 1로 초기화 한 텐서 생성
tf.fill 주어진 스칼라 값으로 원소를 초기화한 텐서 생성
함수 설명
tf.random_normal 정규분포를 따르는 난수 생성
tf.random_uniform 균등분포를 따르는 난수 생성
tf.truncated_normal 정규분포를 따르는 난수 + 2표준편차 벗어난 값은 제거

3. K-평균알고리즘

  • 데이터의 유사성을 바탕으로 주어진 데이터를 지정된 k개의 군집으로 그룹화함
  • 알고리즘의 결과는 중심(centroid)라고 불리는 K개의 점
  • 군집을 구성할때 직접 오차함수를 최소화하려면 많은 계산량을 필요로함
  • 이런문제를 해결하기위해 휴리스틱한 방법으로 로컬최소값에 빠르게 수렴하게하는 기법들이 개발됨
  • 가장 널리사용되는 방법은 iterative refinement기법
    • 1)초기단계 : K개 중심의 초기 집합을 결정
    • 2)할당단계 : 각 데이터를 가장 가까운 군집에 할당
    • 3)업데이트단계 :각그룹에 대해 새로운 중심을 계산
    • 초기값 : K개를 임의로 선택
    • 할당단계와 수정 단계는는 알고리즘이 수렴됬다고 간주할때까지 루프를 통해 반복

실습

  • 2000개의 난수를 생성하되 두개의 정규분포에서 생성함
In [1]:
import numpy as np
num_points = 2000
vector_set = []

for i in xrange(num_points) :
    if np.random.random()>0.5 :
        vector_set.append([np.random.normal(0.0,0.9),
                         np.random.normal(0.0,0.9)])
    else :
        vector_set.append([np.random.normal(3.0,0.5),
                          np.random.normal(3.0,0.5)] )
In [2]:
vector_set
Out[2]:
[[0.32921449652037543, -0.6544163077686246],
 [3.5300981064821304, 2.972335514771935],
 [3.7404056811563517, 2.61338000106367],
 [0.47671679955596863, -0.8315493666049446],
 [-0.45452145248225184, 0.520523986942455],
 [2.773385837109206, 3.4699988035023743],
 [-1.1395720985700586, -0.5495339829252511],
 [2.601881322952827, 3.4637121673407334],
 [2.7937414987773224, 2.3589632394371165],
 [0.8287096058113333, -1.1169452111790477],
 [0.049408050245839136, 0.015072442499778257],
 [2.4702906124493804, 2.671382355470671],
 [0.5172704659013806, -0.7197566981841561],
 [-0.5634381178385752, -0.05334981629175853],
 [3.572329003961439, 2.919909712930682],
 [0.1797918082228199, -0.8553849327021266],
 [2.146364173749177, 3.0653681364169656],
 [-0.4435496276391044, 0.03458466634681497],
 [-0.26556951210431906, -1.006094498199089],
 [0.8020423175815613, -0.01921824630957957],
 [2.380473021166143, 3.3008307162175496],
 [-0.8355920666361928, 0.5143559406050374],
 [0.5605307501181245, -0.7474894739163606],
 [2.265048110035512, 2.6416316459967892],
 [4.086066718107087, 3.094199258868019],
 [2.8960201336632445, 2.9540089692859137],
 [0.33196765613886425, -0.6733364410867867],
 [2.6792412862082036, 3.4949628106780146],
 [0.4933399264058239, -0.6266347661174102],
 [2.9116642987281804, 2.77505785562916],
 [3.350964275401696, 2.3430778378108914],
 [2.6603579248872675, 2.786210894663075],
 [2.153470988545107, 3.436316039553232],
 [2.8642813358694976, 3.033413970157992],
 [0.9625784051691215, -0.9591866390414396],
 [3.4021125018305787, 3.011524299444485],
 [2.077197681501632, 2.1154757576687664],
 [-0.1006251977981523, -1.0366412380253922],
 [2.875436684358578, 2.72262179752859],
 [-0.8798113995562207, 0.9719543973391663],
 [3.487467380719034, 3.7106057706875024],
 [1.2853074427871567, 0.14112026171691483],
 [2.7464959746205966, 3.581218020853203],
 [2.824443872463048, 3.1331795416551604],
 [3.6060104805825706, 3.2045423428337316],
 [2.4974799969425807, 3.0865949850327117],
 [-0.6754227823478539, 1.6697447652898976],
 [-0.8929313945251028, 0.3050084477305372],
 [3.469096891373806, 3.1360053354017396],
 [2.8627158116985223, 2.990407855328947],
 [-1.1223819760494482, -1.1746658207638632],
 [-0.9178372005881059, -0.9846753502839615],
 [2.720369953177432, 3.228814633722775],
 [3.4290756477580207, 2.8309251455587034],
 [1.9336210704467276, 3.0744476924570803],
 [3.8870957137318927, 3.03332746406367],
 [0.5804689396718058, 1.3038043250410654],
 [2.163332581202971, 3.524376597207132],
 [3.0876348064980066, 3.6875065911616063],
 [-0.24823241141742505, 0.3996935580454786],
 [-1.0561443694190555, -0.3994253008372458],
 [2.9797568466332, 3.257678327203062],
 [3.655704219822241, 1.9627348929993789],
 [2.1267553461775544, 3.098995406168996],
 [1.8204387966613598, 2.054013941783896],
 [1.0104913886811713, 0.9738483677812636],
 [-2.049600572289606, -0.49925424458464396],
 [2.5625153375046383, 2.7285532495348397],
 [3.3957881027684613, 2.502793986006826],
 [1.1933554048557433, -0.26267839246442365],
 [-0.0663436275722224, 1.365928838414808],
 [-1.4927346627633222, 0.3420765184349746],
 [-0.7032440880822468, 1.554755515936172],
 [3.4415496731202526, 2.465164911596797],
 [2.7068489834866174, 3.3236293935756347],
 [3.9614602886168426, 2.8968517472975543],
 [0.89753506945278, 1.0591146399348845],
 [3.4032416106986663, 2.0771968255109687],
 [0.8412311321412008, -0.26142598280009827],
 [-0.8010989974282529, -1.04618637032617],
 [-1.3605612688138882, -0.22187671774642226],
 [-0.9457583038511481, -0.9142033351810103],
 [0.30873891313209667, 0.8816476902449996],
 [1.4708476983861134, -0.6333741614323367],
 [3.4105978568765076, 2.3270962938359614],
 [0.6807762584926872, 0.3190508981521644],
 [0.8317717129897895, 1.5190340113233072],
 [-0.6847013495756882, -0.07047586116553842],
 [-0.5926263895126221, 0.7369399283269445],
 [0.28599455004630286, -0.9980214501293055],
 [-0.44450247072101495, 0.6439779068059945],
 [0.953809305540609, 0.27353609988187216],
 [0.6678996845708064, 1.5444455050849213],
 [3.333892024696918, 2.7685570455339423],
 [-1.1735331718210156, -1.0386705291828529],
 [2.5302170512034867, 3.1576843154655285],
 [3.5839142121259098, 3.5959181902735575],
 [0.47201806732351576, -0.17797516664651486],
 [-0.3091581095660412, -1.0220054043625972],
 ...]
In [3]:
%matplotlib nbagg
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

df = pd.DataFrame({"x": [v[0] for v in vector_set],
                  "y": [v[1] for v in vector_set]})

sns.lmplot("x","y", data=df, fit_reg=False, size=6)
plt.show()

K평균 알고리즘 구현 : 4개 군집으로 군집화

1. 텐서 데이터 생성

In [4]:
import tensorflow as tf

vectors = tf.constant(vector_set)  # 생성한 난수를 상수텐서로 바꿈
k = 4                             # 군집개수 지정 
vectors.get_shape()              # 텐서의 구조 확인
/usr/local/lib/python2.7/dist-packages/sklearn/cross_validation.py:44: DeprecationWarning: This module was deprecated in version 0.18 in favor of the model_selection module into which all the refactored classes and functions are moved. Also note that the interface of the new CV iterators are different from that of this module. This module will be removed in 0.20.
  "This module will be removed in 0.20.", DeprecationWarning)
Out[4]:
TensorShape([Dimension(2000), Dimension(2)])

2. 중심 값 생성

In [5]:
centroides = tf.Variable(tf.slice(tf.random_shuffle(vectors),[0,0],[k,-1])) # 랜덤으로 데이터에서 k개 데이터 선택
centroides.get_shape()
Out[5]:
TensorShape([Dimension(4), Dimension(2)])

3. 중심과의 거리 계산

  • 중심은 4개, 전체 값은 2000개 (차원의 크기가 다름)
  • tf.expand함수로 차원을 추가하여 3차원으로 만들어 뺄샘을 할수 있게 크기를 맞춤
In [6]:
expanded_vectors = tf.expand_dims(vectors,0)
expanded_vectors.get_shape() 
Out[6]:
TensorShape([Dimension(1), Dimension(2000), Dimension(2)])
In [7]:
expanded_centroides = tf.expand_dims(centroides,1)
expanded_centroides.get_shape()
Out[7]:
TensorShape([Dimension(4), Dimension(1), Dimension(2)])
  • 브로드캐스팅기능으로 tf.sub함수는 두텐서의 각원소를 어떻게 빼야할지 스스로 알아낼수 있음
  • 크기가 1인 차원은 텐서 연산 시 다른 텐서의 해당 차원 크기에 맞게 계산을 반복하므로 마치 차원이 늘어난 것과 같은 효과를 가짐
  • d2차원(크기가2인)에서 뺄샘이 이루어짐
  • d0차원은 각각 크기가 1,4로 다름 그럼 벡차의 차원을 4로 늘려서 연산함 / d1차원도 동일함

3.1 유클리드 제곱거리를 사용하는 할당단계의 알고리즘

In [8]:
diff = tf.sub(expanded_vectors,expanded_centroides)  # 중심 - 각x,y값을 뺀것
sqr = tf.square(diff) # diff 값을 제곱
distance = tf.reduce_sum(sqr,2) # 제곱한 값의 합
assignments = tf.argmin(distance,0) #거리의 합이 가장 작은 값의 인덱스(0차원)
  • 각 차원의 크기를 확인해보면 요래요래
In [9]:
diff.get_shape()
Out[9]:
TensorShape([Dimension(4), Dimension(2000), Dimension(2)])
In [10]:
sqr.get_shape()
Out[10]:
TensorShape([Dimension(4), Dimension(2000), Dimension(2)])
In [11]:
distance.get_shape()
Out[11]:
TensorShape([Dimension(4), Dimension(2000)])
In [12]:
assignments.get_shape()
Out[12]:
TensorShape([Dimension(2000)])
In [13]:
#한줄로 하면 이렇게 
assignments = tf.argmin(tf.reduce_sum(tf.square(tf.sub(expanded_vectors,expanded_centroides)),2),0)

#두줄 정도가 보기 좋으니 이렇게 
distance = tf.reduce_sum(tf.square(tf.sub(expanded_vectors,expanded_centroides)),2)
assignments = tf.argmin(distance,0)

3.2 새로운 중심 계산

  • 마지막 수정단계로 새롭게 군집을 만들어 새로운 중심을 다시 계산하는 단계
  • k개 군집에 속하는 점들의 평균을 가진 K개의 텐서를 합쳐서 means 텐서를 생성
In [14]:
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 xrange(k)])
  • 1) equal 함수로 한 군집에 매칭되는 assignments텐서의 각 원소위치를 True표시하는 불리언텐서 생성(dimension 2000)
  • 2) where 함수로 불리언텐서에서 true로 표시된 위치값을 갖는 텐서 생성 (dimension 2000 * dimension1)
  • 3) reshape 함수로 c군집에 속한 vectors텐서의 포인트들의 인텍스로 구성된 텐서 생성 (dimension1 * dimension2000)
  • 4) gather 함수로 c군집을 이루는 점의 좌표를 모은 텐서 생성 (dimension1 dimension2000 dimenrion2)
  • 5) reduce_meand 함수로 c군집에 속한 모든 점의 평균 값을 가진 텐서 생성(dimension1 * dimension2000)
  • (http://goo.gl/5s0Y2S)에서 각 함수에 대한 자세한정보 얻을수 있음
함수 설명
tf.reduce_sum 지정한차원을 따라 원소들을 더함
tf.reduce_prod 지정한 차원을 따라 원소들을 곱함
tf.reduce_min 지정한차원을 따라 최소값을 계산
tf.reduce_max 지정한차원을 따라 최대값을 계산
tf.reduce_mean 지정한차원을 따라 평균을 계산
tf.argmin 지정한 차원을 따라 가장 작은 값의 원소가 있는 인텍스를 리턴
tf.argmax 지정한 차원을 따라 가장큰 값의 원소가 있는 인덱스를 리턴

3.3 그래프 실행

  • 루프를 구성하고 중심을 means 텐서의 새값으로 업데이트함
  • 1) means 텐서의 값을 centroids에 할당 하는 연산을 작성해야함
In [15]:
update_centroides = tf.assign(centroides, means)
init_op = tf.initialize_all_variables()
In [16]:
sess = tf.Session()
sess.run(init_op)

for step in xrange(100):
   _, centroid_values, assignment_values = sess.run([update_centroides,
                                                    centroides,
                                                    assignments])
In [17]:
print centroid_values
[[ 3.35460877  3.11880803]
 [-0.71416128  0.12714821]
 [ 2.53997254  2.81049085]
 [ 0.73785478 -0.22789095]]
In [18]:
data = {"x": [], "y": [], "cluster": []}

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

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

문서를 어떻게 벡터로 표현하나?(윈도우 기반 메트릭스)

In [22]:
%matplotlib nbagg
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

la = np.linalg
words = ["I", "like", "enjoy","deep", "learning", "NLP", "flying", "."]
X = np.array([[0,2,1,0,0,0,0,0],
              [2,0,0,1,0,1,0,0],
              [1,0,0,0,0,0,1,0],
              [0,1,0,0,1,0,0,0],
              [0,0,0,1,0,0,0,1],
              [0,1,0,0,0,0,0,1],
              [0,0,1,0,0,0,0,1],
              [0,0,0,0,1,1,1,0]])

U, s, Vh = la.svd(X, full_matrices=False)

for i in xrange(len(words)):
    plt.text(U[i,0], U[i,1], words[i])

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다