# -*- coding: utf-8 -*-
from myutil import consolePrintWithLineNumber as c
'''
Created on 2015. 8. 25.
@author: Administrator
'''
# 이전 절 사전 작업...
from ch03_2 import pearson, readfile, hcluster, drawdendrogram
blognames, words, data = readfile('blogdata.txt')
# 3-6 K평균 군집화
# kNN(k Nearest Neighbors) - 머신러닝 인 액션
import random
def kcluster(rows, distance=pearson, k=4):
# 각 단어가 출현한 최소,최대값을 구한다.
ranges = [(min([row[i] for row in rows]), max([row[i] for row in rows])) for i in range(len(rows[0]))]
# 임의의 중심점 k개 생성 - 단, 중심점 1개가 단어 706개의 속성을 포함하고 있다.706차원이란 뜻. 모든 단어를 포함하되, 최소값과 최대값 사이의 임의의 지점
# random.random() : 1보다 작은 실수를 반환하는 듯.
centroid = [[random.random() * (ranges[i][1] - ranges[i][0]) + ranges[i][0] for i in range(len(rows[0]))] for j in range(k)]
lastmatches = None
# 100번 루프...그전에 끝나기를 바라는 수밖에...
for t in range(100):
c('Iteration %d' % t)
kresult = [[] for i in range(k)] # k개의 빈배열을 가진 배열 반환 [[], [], []] 이곳에 군집에 해당하는 데이터(블로그의 index)를 추가한다.
# 각 가로줄별로 가장 근접한 중심점을 찾음(k개 중에)
for j in range(len(rows)):
row = rows[j]
bestmatch = 0
# 해당 row 에 가장 근접한 bestmatch(k순번)을 찾는다.
for i in range(k):
d = distance(centroid[i], row)
if d < distance(centroid[bestmatch], row):
bestmatch = i
kresult[bestmatch].append(j)
# 이전과 결과가 같다면 완료..
if kresult == lastmatches: break
lastmatches = kresult
# 중심점을 멤버들의 평균으로 이동함
for i in range(k):
avgs = [0.0] * len(rows[0]) # [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] 같은 모습
if len(kresult[i]) > 0:
# k 중심점에 속한 데이터들을 다시 꺼내서 평균을 낸다.
for rowid in kresult[i]:
for m in range(len(rows[rowid])):
avgs[m] += rows[rowid][m]
for j in range(len(avgs)): # 여기서 for를 왜 돌리지? 평균 계산은 한번만 하면 되지 않나? > 평균이 706개(모든속성)가 필요하다.
avgs[j] /= len(kresult[i])
centroid[i] = avgs
return kresult
# kclust = kcluster(data, k=5)
# c([blognames[r] for r in kclust[0]])
# c([blognames[r] for r in kclust[1]])
# 3-7 선호도 군집
# 72p
# www.zebo.com 사이트는 접속불가
# 데이터 얻기와 준비, 제보 결과 스크래핑 생략
'''
0과 1로된 데이터는 피어슨 상관계수보다는 타니모토 계수가 더 적당하다.
우선 != 0 보다는 == 1이 더 직관적이다. > 할려고 했는데, 군집화하면서 데이터가 0 또는 1이 아니라 0.5, 0.25 등등 변한다.
책의 타니모터 공식과 인터넷의 공식과 조금 다르다.
인터넷의 타니모토는 0,1이 아닐뿐더러 집합의 개념이라서 순서에 영향을 받지 않는다.
그런데 책의 타니모터는 0,1로 구성되어있고, 순서가 중요하다.(교집합 시) > 0과 1로만 구분하기 때문에 순서가 중요하다. 순서를 무시하면, 다 0,1이기 때문에 다 중복-_-;;;
'''
def tanimoto(v1, v2):
c1, c2, shr = 0, 0, 0
# v1과 v2의 개수가 동일하다는 전제하에
for i in range(len(v1)):
if v1[i] != 0: c1 += 1 # in v1
if v2[i] != 0: c2 += 1 # in v2
if v1[i] != 0 and v2[i] != 0: shr += 1 # in both
return 1.0 - (float(shr) / (c1 + c2 - shr))
# 75p
wants, people, data1 = readfile('zebo.txt')
clust = hcluster(data1, distance=tanimoto)
# drawdendrogram(clust, wants, jpeg='zebo1.jpg')
# 3-8 2차원으로 데이터 보기
'''
사용된 수학공식이 그리 간단하지는 않다...뭘까?? -- 이거 혹시...선형회귀?? 그런거 아니겠지??? 기울기나오는게 왠지...-_-;;
결과가 그리 신통치 않다-_-;; 4절 계통도 출력한 것과 비교했을때 연관성이 없다...???
그리 좋아보이는 방법은 아닌듯 하다. 더 좋은 방법은 없을까????
다차원의 데이터를 2차원으로 쑤셔넣는 방법은 분명 한계가 있고, 오차가 생긴다. 그 오차를 얼마나 줄일 수 있는지가 관건...
빅뱅처럼.. 모두 다 한곳에서 시작해서 팽창하는 건 어떨까?
또는... 자리를 찾아가는 과정을 애니메이션으로 보는 것도 좋은 시각적인 방법인듯
'''
from math import sqrt
def scaledown(data, distance=pearson, rate=0.01):
n = len(data)
# 항목간의 실제거리 구하기
'''
realdist 는 다음의 2차 배열로 이루어져 있다.(0 < ? < 1.1 정도?)
A B C D E F G H I J K
A[[0 ? ? ? ? ? ? ? ? ? ?]
B [? 0 ? ? ? ? ? ? ? ? ?]
C [? ? 0 ? ? ? ? ? ? ? ?]
D [? ? ? 0 ? ? ? ? ? ? ?]
E [? ? ? ? 0 ? ? ? ? ? ?]
F [? ? ? ? ? 0 ? ? ? ? ?]
G [? ? ? ? ? ? 0 ? ? ? ?]
H [? ? ? ? ? ? ? 0 ? ? ?]
I [? ? ? ? ? ? ? ? 0 ? ?]
J [? ? ? ? ? ? ? ? ? 0 ?]
K [? ? ? ? ? ? ? ? ? ? 0]]
'''
realdist = [[distance(data[i], data[j]) for j in range(n)] for i in range(0, n)]
# 2D 내에서 랜덤의 시작점 설정
loc = [[random.random(), random.random()] for i in range(n)]
'''
loc는 (0 <= ? < 1)
A [[? ?]
B [? ?]
C [? ?]
D [? ?]
E [? ?]
F [? ?]
G [? ?]]
'''
fakedist = [[0.0 for j in range(n)] for i in range(n)]
'''
fakedist는
A B C D E F G H I J K
A[[0 0 0 0 0 0 0 0 0 0 0]
B [0 0 0 0 0 0 0 0 0 0 0]
C [0 0 0 0 0 0 0 0 0 0 0]
D [0 0 0 0 0 0 0 0 0 0 0]
E [0 0 0 0 0 0 0 0 0 0 0]
F [0 0 0 0 0 0 0 0 0 0 0]
G [0 0 0 0 0 0 0 0 0 0 0]
H [0 0 0 0 0 0 0 0 0 0 0]
I [0 0 0 0 0 0 0 0 0 0 0]
J [0 0 0 0 0 0 0 0 0 0 0]
K [0 0 0 0 0 0 0 0 0 0 0]]
'''
lasterror = None
for m in range(0, 1000):
# 투영된 거리(시작점간의 거리)
for i in range(n):
for j in range(n):
fakedist[i][j] = sqrt(sum([pow(loc[i][x] - loc[j][x], 2) for x in range(len(loc[i]))])) # len(loc[i]) = 2
grad = [[0.0, 0.0] for i in range(n)]
'''
grad는 기울기
A [[0 0]
B [0 0]
C [0 0]
D [0 0]
E [0 0]
F [0 0]
G [0 0]]
'''
totalerror = 0
for k in range(n): # 기준
for j in range(n): # 비교대상
if j == k: continue # 같은 항목끼리는 비교할 필요 없으니
'''
실제 거리와 중심점의 차이(오차)
errorterm > 0은 두점사이의 거리가 실제보다 크다. ==> 가까워져야 한다.
errorterm = 0 동일
errorterm < 0은 두점사이의 거리가 실제보다 작다. ==> 멀리 떨어져야 한다.
'''
errorterm = (fakedist[j][k] - realdist[j][k]) / realdist[j][k]
grad[k][0] += ((loc[k][0] - loc[j][0]) / fakedist[j][k]) * errorterm # fakedist[j][k] 나 fakedist[k][j] 나 같다. 대칭행렬이다. 데이터 낭비다-_-;;
grad[k][1] += ((loc[k][1] - loc[j][1]) / fakedist[j][k]) * errorterm
totalerror += abs(errorterm)
c(totalerror) # 오류값이 점점 작아짐을 볼 수 있다.
if lasterror and lasterror < totalerror:
break
lasterror = totalerror
'''
이동시키는 건데 왜 마이너스(-)일까?
grad > 0 현재 내 좌표값이 작아져야 errorterm 을 만족할 수 있다.
grad < 0 현재 내 좌표값이 커야한다.
errorterm (나 - 상대) ==> grad ==> 결과
(+) (+) (+) 내 좌표가 작아져야 한다.(두점 사이의 거리가 실제보다 크다., 내 좌표가 상대방좌표보다 크기 때문에 내 좌표가 작아져야 상대방과 가까워질 수 있다.)
(+) (-) (-) 내 좌표가 커져야한다.(두점 사이의 거리가 실제보다 크다, 내 좌표가 상대방좌표보다 작기 때문에 내 좌표가 커져야 상대방과 가까워질 수 있다.)
(-) (+) (-) 내 좌표가 커져야한다.(두점 사이의 거리가 실제보다 작다, 내 좌표가 상대방좌표보다 크기 때문에 내 좌표가 커져야 상대방과 멀어질 수 있다.)
(-) (-) (+) 내 좌표가 작아져야 한다.(두점 사이의 거리가 실제보다 작다., 내 좌표가 상대방좌표보다 작기 때문에 내 좌표가 작아져야 상대방과 멀어질 수 있다.)
'''
for k in range(n):
loc[k][0] -= rate * grad[k][0]
loc[k][1] -= rate * grad[k][1]
return loc
from PIL import Image, ImageDraw
def draw2d(data, labels, jpeg='mds2d.jpg'):
img = Image.new('RGB', (2000, 2000), (255, 255, 255))
draw = ImageDraw.Draw(img)
for i in range(len(data)):
x = (data[i][0] + 0.5) * 1000
y = (data[i][1] + 0.5) * 1000
draw.text((x, y), labels[i], (0, 0, 0))
draw.line((x - 5, y - 5, x + 5, y + 5), fill=(255, 0, 0), width=5) # 정확한 지점을 표시하기 위해
draw.line((x + 5, y - 5, x - 5, y + 5), fill=(255, 0, 0), width=5)
img.save(jpeg, 'JPEG')
coords = scaledown(data)
draw2d(coords, blognames, jpeg='blogs2d3.jpg')
188 > 4121.873597213046 188 > 3527.591729520054 188 > 3458.56698340106 188 > 3420.5753470845157 188 > 3386.117598961437 188 > 3349.3402380599764 188 > 3314.7706845391162 188 > 3286.9539955820605 188 > 3265.7123638564453 188 > 3247.9648382862897 188 > 3231.3422639954265 188 > 3215.029574750167 188 > 3201.4291006522035 188 > 3188.4369371568264 188 > 3178.7735581496 188 > 3171.6052024640467 188 > 3165.529939646259 188 > 3160.115028255854 188 > 3155.378904106978 188 > 3151.8806880278753 188 > 3148.9512042787637 188 > 3146.5890704524395 188 > 3144.3693057698924 188 > 3141.866858875396 188 > 3139.414879529258 188 > 3136.702858851971 188 > 3133.7800657355956 188 > 3130.706541819401 188 > 3127.7024706090733 188 > 3124.7854981348323 188 > 3122.296076882132 188 > 3120.153154514913 188 > 3118.3955879134364 188 > 3116.737649017945 188 > 3114.685994402978 188 > 3112.3449794950097 188 > 3109.7316182091563 188 > 3107.232114610093 188 > 3105.183714372845 188 > 3103.2758205072328 188 > 3101.255900517773 188 > 3099.3346697129678 188 > 3097.390519131204 188 > 3095.604114304122 188 > 3093.874755422627 188 > 3092.4018424335704 188 > 3090.802934584408 188 > 3088.9206217916562 188 > 3086.723553409899 188 > 3084.3089349077923 188 > 3082.0085108309713 188 > 3079.843430593622 188 > 3077.804308445605 188 > 3075.823237003265 188 > 3074.1569861774433 188 > 3072.5830490954254 188 > 3070.977318054793 188 > 3069.3578148721685 188 > 3067.9405767178405 188 > 3066.5482719629554 188 > 3065.1741022141764 188 > 3063.982207456531 188 > 3062.872197545028 188 > 3061.714669441097 188 > 3060.341866182004 188 > 3058.8705186683524 188 > 3057.4999822277387 188 > 3055.9928919789013 188 > 3054.4809523948015 188 > 3053.1050901347007 188 > 3051.6022429428294 188 > 3050.092295289536 188 > 3048.5037066411396 188 > 3047.1908340219857 188 > 3046.022798830574 188 > 3045.0009973835436 188 > 3044.091949999352 188 > 3043.2004116861326 188 > 3042.466551333877 188 > 3041.8214451587205 188 > 3041.178568450084 188 > 3040.8054437787355 188 > 3040.6585728956134 188 > 3040.5497120032346 188 > 3040.45771357786 188 > 3040.527574529178
75p 타니모토 결과(zebo.txt)
77p 2차원으로 데이터 보기 결과(blogdata.txt 결과)
> 정확도는 나름...
트리에 나와있는 결과물과 비교해가며 군집을 만들어 보았다. (그림판으로 편집^^)
79p scaledown 함수 수식 - 랜덤으로 생성한 중심에서 실제위치로 이동 (주변 다른 점들과의 비교를 통해, 움직일 변화량을 매회 구해서 이동한다.)
제대로 표현한것이 맞나 모르겠다.
점들의 이동이 마치 힘겨루기 하는 것 같다.
실제거리차(이상적인 거리 - 다차원 속성으로 계산한...)
'python 및 머신러닝 > 집단지성 프로그래밍' 카테고리의 다른 글
| [Programming Collective Intelligence] - 집단지성 프로그래밍 2,3,4장 정리 (0) | 2015.09.01 |
|---|---|
| [Programming Collective Intelligence] - 집단지성 프로그래밍 4장 검색과 랭킹 (0) | 2015.08.31 |
| [Programming Collective Intelligence] - 집단지성 프로그래밍 3장 03 계층적 군집화 (0) | 2015.08.24 |
| [Programming Collective Intelligence] - 집단지성 프로그래밍 3장 군집발견 (0) | 2015.08.24 |
| [Programming Collective Intelligence] - 집단지성 프로그래밍 2장 추천시스템 만들기 (0) | 2015.08.22 |