그냥 검색하다 걸린 페이지의 데이터를 받아서 잉여한 시간을 보내봤다. 연습문제를 풀어보는 느낌으로.
데이터
어쩌다 방문한 여기에 The internet demographics dataset 이라는게 있어서 한번 받아 보았다. Newbie 인지 아닌지를 예측하고 싶었다 한다.
import requests
data = requests.get('http://www-stat.wharton.upenn.edu/~waterman/DataSets/uva.txt').content
len(data)
import pandas as pd
from StringIO import StringIO
df = pd.read_table(StringIO(data), delimiter='\t')
df.head()
나이를 제외하고는 모두 Categorical 입력이다. 무엇을 사용해서 분류기를 만들어야 할까? 값들을 숫자로 바꾸는 인코딩을 어떻게 하는게 올바른지 잘 모르겠다. 왠지 해볼만 할것 같은 Naive Bayes Classifier로 시도해보겠다.
Naive Bayes Classifier
우리가 들고 있는 피처를 $(x_1, x_2, x_3, x_4, …, x_n)$ 이라고 하자. 이 때 y 일 확률은 아래와 같이 쓸 수 있다.
$$p(y|x_1, x_2, x_3, x_4, … x_n) = \frac{p(y)p(x_1, x_2, x_3, x_4, …, x_n | y)}{p(x_1, x_2, x_3, x_4, … x_n)}$$
거짓말이지만 Naive Independence Assumption 을 하면, $p(x_1, x_2, x_3, x_4, …, x_n|y)$ 부분을
$$p(x_1|y)p(x_2|y)p(x_3|y)p(x_4|y)…p(x_n|y)$$
으로 쓸 수 있다. 이정도면 코드로 바로 옮길 만 하다. 그냥 개수 세어서 확률 곱하기를 한다. 숫자가 작으므로 곱하기 대신 로그 씌워 더하기를 하겠다.
p(y=0) 과 p(y=1)을 각각 계산하여 큰 쪽을 택한다.
Counting
$p(y)$
이진 분류기이므로 p(Newbie=1) = 1.0 - p(Newbie=0) 이 될 것이다.
p_newbie = df.Newbie[df.Newbie==1].count() / float(df.Newbie.count())
print('p(Newbie) = %.4f' % p_newbie)
print('p(Not Newbie) = %4f' % (1.0 - p_newbie))
$p(x_i|y)$
$x_1$ 부터 $x_8$ 까지 차례대로 Age, Gender, Household Income, Sexual Preference, Country, Education Attainment, Major Occupation, Marital Status 에 대응시키자.
KEYS = ['_', 'Age', 'Gender', 'Household Income', 'Sexual Preference', 'Country', 'Education Attainment', 'Major Occupation', 'Marital Status']
CACHE = {}
def calc_p(xi, i, newbie):
cache_key = (xi, i, newbie)
r = CACHE.get(cache_key)
if r is not None:
return r
key = KEYS[i]
total = df[key][df.Newbie==newbie].count()
if total==0:
CACHE[cache_key] = 0.0000001
else:
CACHE[cache_key] = df[key][(df[key]==xi) & (df.Newbie==newbie)].count() / float(total)
return CACHE[cache_key]
원래대로라면 CV를 해야 할 것 같지만, 귀찮으므로 일단 전체 셋에 대해 외우기라도 잘 했는지를 한번 본다.
from itertools import islice
import math
r = []
for id, row in df.iterrows():
feature = [row[key] for key in KEYS[1:]]
p_1 = math.log(p_newbie)
p_0 = math.log(1.0 - p_newbie)
try:
for i, v in enumerate(feature):
p_1 += math.log(calc_p(v, i+1, 1))
p_0 += math.log(calc_p(v, i+1, 0))
prediction = p_1>p_0 and 1 or 0
if prediction == row.Newbie:
r.append(1)
else:
r.append(0)
except:
pass
print float(sum(r))/len(r)
개수 세기를 한 셋에 대해 직접 확인해봤는데도 0.75밖에 안나온다. 뭔가 잘못했나?
도와줘요 Scikit learn!
from sklearn.naive_bayes import MultinomialNB
clf = MultinomialNB()
n=0
occur={}
def make_feature(row):
f = []
for idx, key in enumerate(KEYS[1:]):
k = (idx, key)
if k not in occur:
occur[k] = len(occur)
f.append(occur[k])
return tuple(f)
X = [make_feature(row) for id, row in df.iterrows()]
y = list(df.Newbie)
clf.fit(X, y)
correct = clf.predict([make_feature(row) for row in df.iterrows()]) == df.Newbie
print sum(correct)/float(len(correct))
망함
이걸론 잘 안되나보다. 데이터를 가져온 페이지에서는 Logistic regression으로 해볼까 하던데.
Comments