Tuesday, December 9, 2014

Popen, Python ve Komut Satirina Erismek

Script yazarken bazen ihtiyaclardan biri python script'i icinde bir shell script isletebilmek. Ciktisi islenmeyenler icin os.system(komut) yetiyor tabii. Fakat komut ciktisini gostermek istiyorsak?

import os
res = os.popen("ls -al")

kullanimi basit - fakat tedavulden kalkacakmis (deprecated), bu yuzden kalici kodlari ona bagli yazmak iyi olmaz. Kutuphane subprocess tavsiye ediliyor,

import subprocess
p = subprocess.Popen(['ls','-al'], stdout=subprocess.PIPE)
res = p.stdout.read()
print res


Is biraz daha zorlasti ama kullana kullana alisilabilir.

Bir diger ihtiyac komut "islerken" onun ciktisini iterator ile gezebilmektir. Belki komut yavas isleyecektir, beklerken belki o ana kadar olan ciktiyi islemek / ekrana basmak ve yan bazi islerle ugrasmak isteyebiliriz. Bunu subprocess ile yapabiliyorsunuz,

p = subprocess.Popen(['ping','-c','3','localhost'], stdout=subprocess.PIPE)
for line in p.stdout:
    print line


Ustteki komut her satiri ayri ayri, Python dongusu icinde basacak.

Tuesday, December 2, 2014

Proje: Adam

Baglanti

Ucuz bilgisayarlari biraraya koyup derin ogrenim (deep learning) yapma amaci tasiyan Proje: Adam (kisi ismi, Adem gibi).

Friday, November 14, 2014

Agaclar Ile Oge Kodlamasi (Categorical Embedding)

Ilginc bir fikir bir Facebook arastirma grubundan gelen makalede paylasilmis.

Bu fikri alarak scikit-learn kutuphanesi programcilarindan biri onu kodlamis

Amac ikisel siniflama (binary classification) yapmaktir. Bu alanda bilindigi gibi SVM, lojistik regresyon gibi yontemler bulunur. Ustteki fikir sudur: gradient boosted trees kullanarak egitim verisini normal oldugu gibi GB agacina teker teker verirsiniz. Bilindigi gibi GB icinde birden fazla agac vardir, ve her agac icin, her egitim veri noktasi farkli dip noktalara gidebilir, cunku yukaridan baslayip her karar noktasinda bir saga bir sola vs. gidersiniz ve o veri noktasi icin belli (ve tek) bir noktaya varirsiniz. Her veri noktasi icin, GB icindeki her agac, ayni veri noktasina bakar ve kendi icinde bir nihai dip noktaya varir (cunku her agac farkli bir agactir).

Ustteki makalede bu ozellikten istifade etmek istemisler, ve GB'nin tum agaclarinin tum dip noktalarini bir indis (sayi) ile isaretlemisler, sonra icinde tamamen 0 olan (yani bos sayilabilir) bir vektoru aliyorlar, ve her egitim noktasinin, her agacta dustugu dip noktaya bakiyorlar, o noktanin indisini alip bos vektorde oraya 1 degeri yaziyorlar. Bunu tum agaclar icin yapiyorlar, ve nihai olarak elimize gecen vektor o verinin GB agaclari uzerinden "kodlanmis" ve hatta kisaltilmis / ozetlenmis halidir!


Hakikaten akillica bir fikir, cunku GB'nin dogal ozelligi olarak icindeki her agac verinin potansiyel olarak farkli bir tarafina odaklanir, yani her agac diger agaclara nazaran farkli bir sekilde ogrenim yapar. Ve farklilik iyidir! Ustteki teknik bu dogal ozelligin uzerinde bir nevi "sorf yapiyor" ve onu kullanarak bir temsili ikisel vektor yaratiyor.

Yaziya gore bu temsili vektoru alip artik en basit yontemlerden olan lojistik regresyon bile kullanabilirsiniz.

Altta scikit learn'cu arkadasin kodu (temizlenmis haliyle) var. FAKAT, ilginc bir nokta sudur: biz ayni veriyi alip xgboost kutuphanesinin pur GB koduna verince GB+kategorik kodlama yapan koddan daha iyi bir sonuc aliyoruz! Bunun sebebi belki de xgboost'un scikit-learn icindeki GB kodlamasindan daha iyi olmasidir. Ya da GB+kategorik kodlama ornegi biraz ayarlama (tuning) ile daha iyi hale getirilebilir, belli degil. Fakat xgboost felaket iyi sonuclar getirdi.

Bu kutuphaneyi ayrica tavsiye ediyoruz, cunku scikit-learn'un tum birimleri hala seyrekligi (sparsity) desteklemiyorlar. Bu islem hizinda muthis fark yaratiyor. Bazi acilardan scikit-learn hala oyuncak seviyesinde denebilir, prototipleme icin iyi fikirleri gormek icin iyi fakat nihai kod icin bazen baska paketler gerekiyor.

# Requiers sklearn 0.15.2 and xgboost
# Install from https://github.com/tqchen/xgboost
# data from from https://archive.ics.uci.edu/ml/machine-learning-databases//adult/adult.data

import pandas as pd, numpy as np
names = ("age, workclass, fnlwgt, education, education-num, "
"marital-status, occupation, relationship, race, sex, "
"capital-gain, capital-loss, hours-per-week, "
"native-country, income").split(', ')
data = pd.read_csv('./data/adult.data', names=names)

data_encoded = data.apply(lambda x: pd.factorize(x)[0])
print data_encoded.head(5)

features = data_encoded.drop('income', axis=1)
X = features.values.astype(np.float32)
y = (data['income'].values == ' >50K').astype(np.int32)
print np.mean(y)
from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
from sklearn.tree import DecisionTreeClassifier
from sklearn.cross_validation import cross_val_score
clf = DecisionTreeClassifier(max_depth=8)
scores = cross_val_score(clf, X_train, y_train, cv=5, scoring='roc_auc')
print("ROC AUC Decision Tree: {:.4f} +/-{:.4f}".format(np.mean(scores), np.std(scores)))

# 0.240802162029
# ROC AUC Decision Tree: 0.8821 +/-0.0013

from sklearn.metrics import roc_auc_score
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.base import BaseEstimator, TransformerMixin, clone
from sklearn.preprocessing import LabelBinarizer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from scipy.sparse import hstack

class TreeTransform(BaseEstimator, TransformerMixin):

    def __init__(self, estimator):
        self.estimator = estimator
        
    def fit(self, X, y):
        self.fit_transform(X, y)
        return self

    def fit_transform(self, X, y):
        self.estimator_ = clone(self.estimator)
        self.estimator_.fit(X, y)
        self.binarizers_ = []
        sparse_applications = []
        estimators = np.asarray(self.estimator_.estimators_).ravel()
        for t in estimators:
            lb = LabelBinarizer(sparse_output=True)
            sparse_applications.append(lb.fit_transform(t.tree_.apply(X)))
            self.binarizers_.append(lb)
        return hstack(sparse_applications)
        
    def transform(self, X, y=None):
        sparse_applications = []
        estimators = np.asarray(self.estimator_.estimators_).ravel()
        for t, lb in zip(estimators, self.binarizers_):
            sparse_applications.append(lb.transform(t.tree_.apply(X)))
        return hstack(sparse_applications)


boosted_trees = GradientBoostingClassifier(
    max_leaf_nodes=5, learning_rate=0.1,
    n_estimators=100, random_state=0,
) 

pipeline = make_pipeline(
    TreeTransform(boosted_trees),
    LogisticRegression(C=1)
)

pipeline.fit(X_train, y_train);
y_pred_proba = pipeline.predict_proba(X_test)[:, 1]
print("ROC AUC: %0.4f" % roc_auc_score(y_test, y_pred_proba))

#ROC AUC: 0.9131

import os, sys
from sklearn.metrics import roc_curve, auc
from sklearn.metrics import roc_auc_score
sys.path.append('%s/Downloads/xgboost/wrapper/' % os.environ['HOME'])
import xgboost as xgb

xg_train = xgb.DMatrix(X_train, label=y_train)
xg_test = xgb.DMatrix(X_test, label=y_test)
watchlist = [ (xg_train,'train'), (xg_test, 'test') ]

param = {}
param['max_depth'] = 6
num_round = 20

bst = xgb.train(param, xg_train, num_round, watchlist )
pred = bst.predict(xg_test)
err = sum(int(pred[i]) != y_test[i] for i in range(len(y_test))) / float(len(y_test))
print ('predicting, classification success=%f' % float(1-err))
fpr, tpr, thresholds = roc_curve(y_test, pred)
roc_auc = auc(fpr, tpr)
print 'Tree AUC', roc_auc

#predicting, classification success=0.773223
#Tree AUC 0.913709849754 
 
 

Wednesday, November 12, 2014

Movielens, Funk SVD, Numba

Surada anlatilan Simon Funk'in (Bandyn Webb) SVD kodlamasinin Python hali alttadir. Ornek olarak movielens 100k verisi isleniyor. Konu hakkinda daha detayli aciklama Uygulamali Matematik notlarimizda bulunabilir.

# http://files.grouplens.org/datasets/movielens/ml-100k.zip
# data.py
from scipy.io import mmread, mmwrite
import os, numpy as np
from scipy import sparse

file_path = '%s/Downloads/ml-100k/u1.base' % os.environ['HOME']
data = np.array([[int(tok) for tok in line.split('\t')[:3]] for line in open(file_path)])
ij = data[:, :2]
ij -= 1
values = data[:, 2]
A = sparse.csc_matrix((values, ij.T)).astype(float)
mmwrite('%s/Downloads/A_m100k_train'  % os.environ['HOME'], A)

file_path = '%s/Downloads/ml-100k/u1.test' % os.environ['HOME']
data = np.array([[int(tok) for tok in line.split('\t')[:3]]  for line in open(file_path)])
ij = data[:, :2]
ij -= 1
values = data[:, 2]
A = sparse.csc_matrix((values, ij.T)).astype(float)
mmwrite('%s/Downloads/A_m100k_test'  % os.environ['HOME'], A)

# funk.py

from scipy.io import mmread, mmwrite
import numpy as np, time, sys
from numba import jit
import os

def create_user_feature_matrix(review_matrix, NUM_FEATURES):
    num_users = review_matrix.shape[0]
    user_feature_matrix = 1./NUM_FEATURES * \
        np.random.randn(NUM_FEATURES, num_users).astype(np.float32)
    return user_feature_matrix

def create_movie_feature_matrix(review_matrix, NUM_FEATURES):
    num_movies = review_matrix.shape[1]
    movie_feature_matrix = 1./NUM_FEATURES * \
        np.random.randn(NUM_FEATURES, num_movies).astype(np.float32)
    return movie_feature_matrix

@jit(nopython=True)
def predict_rating(user_id, movie_id, user_feature_matrix, movie_feature_matrix):
    rating = 1.
    for f in range(user_feature_matrix.shape[0]):
        rating += \
            user_feature_matrix[f, user_id] * \
            movie_feature_matrix[f, movie_id]
    if rating > 5: rating = 5
    elif rating < 1: rating = 1
    return rating

@jit(nopython=True)
def sgd_inner(feature, A_row, A_col, A_data,
              user_feature_matrix, movie_feature_matrix,
              NUM_FEATURES):
    K = 0.015
    LEARNING_RATE = 0.001
    squared_error = 0
    for k in range(len(A_data)):
        user_id = A_row[k]
        movie_id = A_col[k]
        rating = A_data[k]
        p = predict_rating(user_id,
                           movie_id,
                           user_feature_matrix,
                           movie_feature_matrix)
        err = rating - p
        squared_error += err ** 2
        user_feature_value = user_feature_matrix[feature, user_id]
        movie_feature_value = movie_feature_matrix[feature, movie_id]
        user_feature_matrix[feature, user_id] += \
            LEARNING_RATE * (err * movie_feature_value - K * user_feature_value)
        movie_feature_matrix[feature, movie_id] += \
            LEARNING_RATE * (err * user_feature_value - K * movie_feature_value)

    return squared_error

def calculate_features(A_row, A_col, A_data,
                       user_feature_matrix, movie_feature_matrix,
                       NUM_FEATURES):
    MIN_IMPROVEMENT = 0.0001
    MIN_ITERATIONS = 200
    rmse = 0
    last_rmse = 0
    print len(A_data)
    num_ratings = len(A_data)
    for feature in xrange(NUM_FEATURES):
        iter = 0
        while (iter < MIN_ITERATIONS) or  (rmse < last_rmse - MIN_IMPROVEMENT):
            last_rmse = rmse
            squared_error = sgd_inner(feature, A_row, A_col, A_data,
                                      user_feature_matrix, movie_feature_matrix,
                                      NUM_FEATURES)
            rmse = (squared_error / num_ratings)
            iter += 1
        print ('Squared error = %f' % squared_error)
        print ('RMSE = %f' % rmse)
        print ('Feature = %d' % feature)
    return last_rmse

def main():
    LAMBDA = 0.02
    NUM_FEATURES = 30

    A = mmread('%s/Downloads/A_m100k_train' % os.environ['HOME'])

    user_feature_matrix = create_user_feature_matrix(A, NUM_FEATURES)
    movie_feature_matrix = create_movie_feature_matrix(A, NUM_FEATURES)

    A = A.tocoo()

    rmse = calculate_features(A.row, A.col, A.data,
                              user_feature_matrix, movie_feature_matrix,
                              NUM_FEATURES )
    print 'rmse', rmse


if __name__ == "__main__":
    main()

Sunday, October 26, 2014

NVidia, GPU, (Derin Öğrenim) Deep Learning

Bilgisayarin ana mikroislemcisi (CPU) yerine video kartlari uzerinde olan GPU kullanma yaklasimi iyice populerlesmeye basladi. Oyle populerlesti ki video kart ureticisi NVidia disaridan bilgisayara dahil edilebilen ayri bir ufak GPU unitesi bile yaratti. Unitenin ismi Jetson karti.    

Kart uzerinde Ubuntu isleyebiliyor, network aktaricisi (router) dahil edilip hemen ssh ile alete baglanabiliyorsunuz. Yapay ogrenim ile ugrasanlar icin muthis haber GPU uzerinde mevcut 192 cekirdek asiri seviyede paralelism isteyen deep learning yaklasimi icin bicilmis kaftan, ki burada birisi denemis, ve nasil yapilabilecegi anlatiliyor. Blog sahibi Pete Warden imaj uzerinde yapay ogrenim algoritmalari kullanma konusunda uzmanlardan biridir, bu is icin kurdugu sirketini gecende Google satin aldi.

Almanya'da satan sirket surada.

Felzenswalb Gruplamasi (Felzenswalb Clustering)

Thursday, October 16, 2014

emacs-ipython

Emacs'te LaTeX modundan direk iPython kodu isletip sonucu metin / grafik olarak alip yine LaTeX icinde gosterebilen Emacs eklentisi emacs-ipython'u ayri bir proje olarak yayinladik.

Wednesday, October 15, 2014

Felzenswalb Gruplama (Clustering) Algoritmasi

Imaji bolumlere ayirma (segmentation) baglaminda en iyi algoritmalardan biri Felzenswalb algoritmasidir. Altta makalesi ve isleyen C++ kodu paylasilmis,

http://cs.brown.edu/~pff/segment/

Scikit-image projesi ayni algoritmayi pakete dahil etmis, bu guzel haber.

Ilginc bir durum: Koda yakinda bakilirsa, bir grafik bir de grafiksel olmayan, pur kumeleme yapan ayri bolumleri oldugu gorulebiliyor. Burada Felzenswalb'i kutlamak lazim, cok temiz bir kod yazmis, neyin ne oldugu gayet bariz. Ilginc olan su, grafiksel olmayan gruplama (clustering) kisminin islemek icin sadece noktalararasi uzaklik bilgisine ihtiyaci var, bu verilmis ise pat diye (bazi basit esik degerleri uzerinden) dogru kumeleri buluyor, gorsellik olsun olmasin. Uzaklik bilgisi herhangi bir olcut olabilir, illa Oklitsel olmasi gerekmez, eger veri tabaninda musteriler var ise, belki kosinus uzakligi, ya da Jaccard, ya da bir baska sey.

Biz sunu farkettik ki, ufak bir takla ile gorsel olmayan ve cok iyi isleyen kumeleme kismi cikartilabilir, ve ayri bir kumeleme algoritmasi olarak kullanilabilir. Uzaklik bilgisini bir seyrek matris (mtx) formatindan okuyacak bir ufak C++ eki yaptik, bu kod bilgiyi alip Felzenswalb'in kodunu cagiriyor. Bu kodu ayri bir proje olarak paylasiyoruz, 

https://github.com/burakbayramli/felzenszwalb

Felzenswalb imaj ayirma kodu da aslinda benzer bir isi yapiyor ama bunu imajlar uzerinde yapiyor (cunku ana amaci bu), yani imaj renkleri uzerinden bir uzaklik yaratiyor ve onu kodun geri kalanina aktariyor. Biz sadece ic gruplama kismini genellestirdik.

Bu sayede O(E log E) cetrefilliginde isleyen ve optimal kumeleri bulan (kume sayisini tanimlamaya gerek yok, mesela KMeans'de oldugu gibi) bir algoritma elde etmis olduk. Acikcasi bu kadar basit bir kullanimin niye baskalari tarafindan yapilmamis oldugunu anlayamadik. Herhalde ya yapildi ama paylasilmiyor, ya da hakikaten gozden kacirilmis.

Altta uzaklik bilgisi uzerinden kumeleme yapan diger algoritmalarin listesi goruluyor, mesela Affinity Propagation, ya da Spectral Kumeleme. Onlara kiyasla Felzenswalb oldukca iyi durumda.

http://scikit-learn.org/stable/modules/clustering.html

Felzenswalb kumelemesi bu arada Minimum Kapsamli Agac (Minimum Spanning Tree) kavramini kullanir ve aslinda Kruskal algoritmasinin bir varyasyonudur.

Monday, September 15, 2014

Python Profilleme (Profiling)

sudo pip install line_profiler

Komut satirindan isletmek

import time

@profile
def f():
    for i in range(100): print i
    g()
  
def g():
    time.sleep(1)

if __name__ == "__main__":
 f()


Komut satirinda

python -m kernprof -l -v test.py > /tmp/prof1.txt

Sonuc

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     3                                           @profile
     4                                          
     5                                           def f():
     6       101          233      2.3      0.0      for i in range(100): print i
     7         1      1001139 1001139.0    100.0      g()

 

Sonuca bakarsak zamanin yuzde yuzunun g() icinde gecirildigini goruyoruz. Bu normal cunku icinde sleep ibaresi var! Hits bu satirin kac kez isletildigini gosterir, time bu satirda toplam ne kadar zaman gecirildigini gosterir. Per hit satirin her isletildiginde ne kadar zaman harcandigini belirtir. Yuzde ise tum isletim icinde bu satirda yuzde kac zaman harcandigini belirtir.

Not: Eger  ustteki kodu python test.py olarak isletirseniz @profile dekoratorunun import edilmedigi hakkinda sikayet gelecektir.

Bir diger yontem, (sudo pip install profilehooks yaptiktan sonra),

from profilehooks import profile

ile dekaratoru import etmek. O zaman normal komut satirindan python test.py ile isletim olur. Ya da herhangi bir sekilde kod isletimi olur, mesela Flask icinden yuklenip ayni kodun isletilmesi gibi. Bu durumda sonuc raporlari bu modulu import eden surec "bir sekilde bittigi" zaman ekrana basilir.

Bizim tercihimiz ilk gosterilen secenek. Calisma seklimiz soyle: sonuclara bakip, duzeltme yapiyoruz, tekrar isletiyoruz, daha sonra @profile dekatorunu kod icinde cagrim zinciri acisindan daha derin noktalara koyuyoruz, mesela a ->b -> c cagrimi var, a ile basliyoruz, sonra dekorator b, c uzerine konanabiliyor. Ilk yontemin onemli bir avantaji sonuclarin bir dosyaya hemen yazilabiliyor olmasi.

Bu profilleme yontemi "satir profillemesi (line profiler)" olarak biliniyor.

Wednesday, September 10, 2014

Numba, LLVM, ve SVD

Numerik hesaplarda, ya da diger konularda Python nasil daha hizli isletilir? Numpy durumunda tum islemleri matrisler, ve kutuphane cagrilari uzerinden olacak sekilde yapmak bu hizli isletimin bir yolu. Bu durumda mesela pur Python dongulerinden kacinilir. Fakat illa dongu kullanilacaksa ve pur Python tipleri kullanilacaksa, Cython hizlandirma yapmanin yollarindan biri. Ya da Continuum tarafindan gelistirilen Numba.

http://numba.pydata.org

Kurmak icin

apt-get install llvm-dev
pip install llvmpy


Numba ile dekorator kullanarak Python kodumuzun belli kisimlarini LLVM uzerinden isletilebilmesini sagliyoruz. LLVM'i isledik, son zamanlarda cok populer bir yaklasim, bir tur sanal kod isleticisi + derleyici alet cantasi + araclari birlesimi.

Ilk ornek

Ornekte goruldugu gibi bir metot @jit ile isaretlenirse, hizli olarak isletilebilir. Eger @jit(nopython=True) secilirse bu hizli yerel mod demektir, bu durumda Python API cagrilari yapilamaz, nopython=False ile obje moduna gireriz.

Ogrendigimiz bazi dersler (Numba 0.13.2 uzerinden edinilen bilgiler): 

Projemizde ogrendik ki obje modu digerine nazaran cok daha yavastir, yerel mod tavsiye edilir. Yerel modda sadece temel tipler ve Numpy matrisleri  / vektorleri kullanilabilir.

Bazen bir dongude kullandiginiz bir degiskeni tekrar mesela baska bir dongude kullanamiyorsunuz (bir acaiplik aslinda, bir hata dogal olarak). Mesela for i in range(..) sonrasi tekrar for i in range(...) yapamamak.. Eger problem cikarsa sac yolmak yerine nereden kaynaklandigini bilmek faydali olabilir :) Bu yuzden emin olmak icin bir fonksiyon icinde hep degisik indis ismi kullanmak en iyisi.

Yerel modda fonksiyona disaridan gecilen ya da fonsiyon icinde yaratilan vektorlerin icerigini vektorsel / toptan erisim ile degistiremiyorsunuz (yani tum bir satirin icerigini degistirebilmek gibi mesela). Sadece tekil oge (element) degisimi yapabiliyorsunuz. Bu demektir ki indisler yaratip, for donguleri uzerinde vs. ogelere erisip onlari degistirebiliyorsunuz. Simdi bu bir yetenegi kazanip digerini kaybetmek demek oluyor, ne guzel tam np.dot(...) yapabiliyorken birdenbire onu kaybediyoruz! Bu dogru.. Tabii diger yandan for donguleri artik dehset hizli isleyecek (cunku Numba icindeyiz), fakat bir kolayligi kaybetmek te ufak bir dezavantaj, evet. Not: Ustteki durum obje modunda problem degil, ama obje modu daha once soyledigimiz gibi cok daha yavas.

Seyrek matrisler nasil islenir? Dikkat, jit ile isaretlenmis fonksiyonlar seyrek matris objeleri alamazlar, sadece Numpy matrisleri ve vektorleri gonderebiliriz. O zaman seyrek matris icinden "sifir olmayan" tum degerlerini surekli bir vektor olarak almaliyiz, mesela altta bir seyrek matrisin tum degerlerini toplayan bir fonksiyon,

from numba import jit
import numpy as np
from scipy.sparse import coo_matrix, lil_matrix

@jit
def sum(A_rows, A_cols, A_data):
    res = 0.
    for j in range(len(A_data)):
        u = A_rows[j]
        i = A_cols[j]
        res += A_data[j]
    return res

A = lil_matrix([[3,4,5,5],
                [5,4,5,0],
                [0,0,0,0]])

print A
A = coo_matrix(A)
print sum(A.row, A.col, A.data.astype(np.float32))


Indisler u ve i icinde baktigimiz degerin satir ve kolon indislerini elde edebiliyoruz. Ustteki ornekte bu lazim degildi ama alinabilecegini gostermek icin ekledik. Not: ufak bir ornek oldugu icin hiz farki gorulmeyebilir, ama alttaki gibi cagrilirsa fark kesinlikle gorulecektir,

k = 3000
A = coo_matrix(np.random.randint(2, size=(k,k)))
print sum(A.row, A.col, A.data.astype(np.float32))


Bir ornek daha, SVD kodlamasinin Numba ile yapilmis halini gorelim. Not: Alttaki gibi bir SVD kodlamasinin, bu arada, piyasadaki neredeyse tum diger SVD kodlamalarindan farkli oldugunu belirtelim, olmayan degerleri olmayan deger olarak kabul ediyor yani onlari atliyor. Bu bir "matris tamamlama" problemidir, ki tavsiye algoritmalari icin ozellikle bu yaklasim gerekir. Paket SVD'lerin cogu (ki buna scipy svds, sparsesvd paketleri dahil) olmayan degerleri "sifir" kabul ediyor, bu biraz farkli bir problem kabul edilir. Konu hakkinda D. Gleich adli profosorun bir blog yazisi surada.

import numpy as np
from scipy.sparse import coo_matrix
from numba import jit

@jit(nopython=True)
def pred(u, i, P, Q):
    dot = 0.
    for f in range(P.shape[1]):
        dot += P[u, f] * Q[i, f]
    return dot

@jit
def sgd_mult(rows, cols, vals, P, Q, gam, rlam):

    q = np.empty(Q.shape[1]).astype(np.float32)
    p = np.empty(P.shape[1]).astype(np.float32)

    for j in range(len(vals)):
        u = rows[j]
        i = cols[j]
        value = vals[j]

        dev = value - pred(u, i, P, Q)

        for r in range(Q.shape[1]):
            q[r] = gam * (P[u, r]* dev - rlam*Q[i, r])
            p[r] = gam * (Q[i, r]* dev - rlam*P[u, r])

        for l in range(Q.shape[1]):
            Q[i, l] += q[l]
            P[u, l] += p[l]


def svds(A, k, it=20, rlam=0.1, gam=0.1):
    P = 1./k * np.random.randn(A.shape[0], k).astype(np.float32)
    Q = 1./k * np.random.randn(A.shape[1], k).astype(np.float32)

    for it in range(it):
        sgd_mult(A.row, A.col, A.data.astype(np.float32), P, Q, gam, rlam)

    return P, Q.T

@jit(nopython=True)
def rmse(rows, cols, vals, P, Q):
    dev = 0.

    for j in range(len(vals)):
        u = rows[j]
        i = cols[j]
        val = vals[j]

        dev += (val - pred(u, i, P, Q))**2

    return np.sqrt(dev/len(rows))


Gradyan Destekli Regresyon Agaclari (Gradient Boosted Regression Trees -GBRT-)

Yapay Ogrenimde agac teknigini ilerleten yaklasimlardan biri: bu arada artik kimse tek agac egitmiyor (mesela ID3'te oldugu gibi), Rasgele Ormanlar (Random Forests) tekniginde pek cok agac, degisik kolon altgruplari uzerinde egitilir, ya da diger teknikler diger sekillerde cok agacli ortamda is gorur.

GBRT ile her agac bir once egitilen agacin egitim verisindeki etiketleri tahmin etmekteki hatasi uzerinde (on the residuals) egitilir, yani onceki agacin hatalari bir nevi yeni etiketler haline gelir. Bu niye yapilir? Boylece takip eden yeni agaclarin onceki agaclarin hatalarini "duzeltebilecegi" ongorulur.

Konu hakkinda kaynaklar

http://www.slideshare.net/DataRobot/gradient-boosted-regression-trees-in-scikitlearn

GBRT icin en iyi kod

https://github.com/tqchen/xgboost

Kod C++ ile yazilmistir ve hizli islemektedir. Bizim icin en onemli ozelligi scipy kutuphanesinin seyrek matris formatlari ile calisabilmesi (kiyasla scikit-learn kutuphanesindeki mesela rasgele orman algoritmasi bunu yapmamiza izin vermiyor). Kurmak icin unzip, xgboost altinda make yeterli. Ornek olarak xgboost/demo/binary_classification altinda sh runexp.sh ile ornek kod isletebilirsiniz. Kod isledikten sonra sonuc dump.raw.txt icinde, oge isimleri ise featmap.txt icinde. Mesela

0    cap-shape=bell    i
1    cap-shape=conical    i
2    cap-shape=convex    i
3    cap-shape=flat    i
..


Eger numerik bir oge var ise, bu oge "i" yerine "q" ile isaretlenir. GBRT algoritmasi ikisel degerlerle oldugu gibi numerik degerlerle de gayet guzel (ve ayni anda) calisabilir. Mesela numerik boy ogesi secilmis ise agac mesela 160'dan kucuk olma / olmama sarti uzerinden bir dallanma yaratmis olabilir. Bu numerik degerin hangi noktasindan bolunecegi de dal yaratma karar mekanizmasinin bir parcasidir.

https://github.com/tqchen/xgboost/wiki/Binary-Classification

https://archive.ics.uci.edu/ml/datasets/Mushroom

Makina ogreniminde metotlarin hiperparametreleri ile oynanir, en optimal olani bulunmaya ugrasilir, mesela KMeans ile bu kume sayisidir, xgbrt ile, ya da herhangi bir agac algoritmasi icin "agac derinligi" bu parametrelerden biri. Bu derinligi bst:max_depth parametresi kontrol eder. Bir digeri, ozellikle xgbrt icin, "kac tane agac olacagi" parametresi (altta num_round). Ornek bazi ayarlar altta,

num_round = 10
param = {}
param['scale_pos_weight'] = 10.
param['bst:eta'] = 0.4
param['bst:max_depth'] = 3
param['eval_metric'] = 'auc'
param['bst:nthread'] = 4
param['silent'] = 1

bst = xgb.train( param, [egitim verisi], num_round, [....] )

Parametre scale_pos_weight ile eger 1 etiketi 0 etiketinden cok daha az ise, bunu dengelemek icin 1'lerin onemini suni olarak arttirabilme sansini elde ediyoruz.

Simdi bazi taklalara gelelim: biz bir proje icin bu kodun icinde bazi degisiklikler yaptik. Mesela eger 1. seviyede isminde vs metni olan ogeler olsun, 2. seviyede sunlar, 3. seviyede sunlar olsun gibi bazi ek kurallar gerekirse, bunlari kodun icine sokusturmak mumkundur. Bu tur kolon kurallari icin xgboost/booster/tree/xgboost_col_treemaker.hpp icinde, tum #include ibarelerinden sonra,

#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
#include <vector>
#include <map>
using namespace std;

typedef std::map<std::string, std::string> TStrStrMap;
typedef std::pair<std::string, std::string> TStrStrPair;

TStrStrMap create_map()
{
    TStrStrMap tMap;

    ifstream infile("[DIZIN]/featmap.txt");

    string line = "";
    vector<string> all_words;
    while (getline(infile, line))
    {
        stringstream strstr(line);
        string word = "";
        int i = 0; string key; string val;
        while (getline(strstr,word, '\t'))
        {
          if (i == 0) key = word;
          if (i == 1) val = word;
          i++;
        }
        if (val.find("[filan]") != std::string::npos) {
          tMap.insert(TStrStrPair(key, "[filan]"));
        }
        else if (val.find("[falan]") != std::string::npos) {
          tMap.insert(TStrStrPair(key, "[falan]"));
        }
        else if (val.find("[fisman]") != std::string::npos) {
          tMap.insert(TStrStrPair(key, "[fisman]"));
        }

    }

    return tMap;
}

Daha sonra FindSplit() metodu icinde

for( unsigned i = 0; i < nsize; ++ i ){
   const unsigned fid = feat_index[i];


dongusu icinde

if (depth == 0 && tMap[std::to_string(fid)] !=      "[falan]") continue;
else if (depth == 1 && tMap[std::to_string(fid)] != "[filan]") continue;
...


eklenir. Ne yapmis olduk? Ilk once, en basta featmap.txt icindeki oge isimlerini aldik. Bu oge isimlerini taradik, ve icinde belli bir metni tasiyanlari bir map uzerine koyduk, bu baslangica sahiptir diye isaretlemis olduk. Daha sonra agac algoritmasinin bolme noktasi arayan algoritmasina kod sokusturduk, burada dedik ki, mesela ilk seviyede sadece "falan" etiketi tasiyan kolonu bolmek icin kullan, yani agacin bu seviyesinde sadece o tur kolonlari kullanabilirsin kurali getirdik. 

Bu sayede diyoruz ki, mesela elde bir musteri kaydi var diyelim, ve bu kayitlarda lokasyana ait bilgiler belli kolonlarda, finansla alakali bilgiler diger kolonlarda (ve yine diyelim ki bu kolonlarin isminde bu farkliligi isaretledik, "lokasyon_" ya da "finans_" gibi..), ustteki gibi bir degisiklik ile agacin ilk seviyesi lokasyon, ikincisi finans, ucuncusu vs. kolon tipinde olsun gibi bir yonlendirmeyi ekleyebiliriz.

Ustteki degisiklikleri derlemek icin hem ana xgboost dizini hem de xgboost/python altindaki Makefile dosyasina girip export CFLAGS listesine  -std=c++11 eklemeniz lazim, cunku bizim ek kodlar yeni C++ standardindaki bazi yenilikleri kullaniyor (to_string metodu gibi). 

Wednesday, September 3, 2014

Esle / Indirge Anlatimi (Map / Reduce)


Bu da isin esprili tarafi

Monday, September 1, 2014

Pig ve Spark

Apache Spark Buyuk Veri dunyasinda Hadoop'un yerine gecmeye talip. Spark Hadoop'un disk bazli esle / indirge mimarisi yerine hafiza bazli transform ve aksiyon mimarisi getiriyor ve bu urune en son ek Hadoop'ta iyi bilinen Pig dilinin aktarilmasi [link].

Bu buyuk bir haber: Pig dilini isledik, bu dil mesela birlestirim (JOIN) yapilmasini saglayan komutlara sahipti, Hadoop versiyonu diyelim ki JOIN komutunu aldiginda bunu arka planda pek cok makinaya esle / indirge progamciklari olarak tercume ediyordu. Bu sayede 100 GB'lik bir dosyayi bir diger 100 GB'lik dosyaya birlestirebiliyorsunuz (bu islemi tek makinalik RDBMS uzerinde yapmanin ne kadar zor oldugunu tahmin edebiliriz). Buyuk Veri dunyasi genelde denormalize edilmis (surekli tekrar edilen) veri ile is yapsa da bazen JOIN'e ihtiyac olur.

Yani simdi ayni dili (Pig) kullanarak arka planda Spark'a is yaptirmak, Spark'a Pig sonuclarini aktarip, ya da oradan veri almak kolay olacak. 

Pig Spark surumu daha cok yeni, su anda Spork adi altinda gelismeye devam ediyor

https://github.com/sigmoidanalytics/spork/


Monday, August 11, 2014

Lindley'in Paradoksu

Frekanscilar ve Bayesciler arasindaki tartismaya eklenebilecek ilginc bir aci: Lindsey'in Paradoksu. F ve B taraflari basit bir hipotez testing degisik sonuca erisirler, sebep nedir? Hangisi daha iyidir? Yaziya gore aslinda iki taraf ta biraz farkli bir sorunun cevabini veriyorlar. Ayrica duzgun (uniform) onsel dagilim (prior) secilirse Bayes de F ile ayni sonuca erisiyor.

Thursday, July 31, 2014

ggplot

R dilinde cokca kullanilan ggplot2 kutuphanesi nihayet Python'a tasinmaya baslandi. Kurmak icin,

sudo pip install ggplot

Konu hakkinda guzel bir yazi

Matplotlib ile cok zor yapilan isler ggplot2 ile rahat yapilabiliyor. Kutuphanenin cok ciddi takipcileri / hayranlari var, ozellikle daha once R ile kodlama yapmis olan istatistikciler ve bilimciler. Bu kisilerin cok sevdigi bir ozellik bir grafigin kesit kesit (layer by layer) ust uste konarak tasarlanabilmesi. Bu yaklasima gore grafik noktalari bir kesit, yazilari bir baska kesit, eksen boyutlari bir baska kesit olabilir mesela, ve bu ekler hep benzer cagrilarla yapilir (burada + isareti kullanilmis olmasi raslanti degil). Kutuphane, Leland Wilkinson'un Grafigin Grameri adli yaklasiminin kodlanmis halidir; yani grafiklemenin dili boyle olmalidir seklinde bir yaklasimdir bu -- oldukca iddialidir.

Ornek,

from ggplot import *
print mtcars[:3]

            name   mpg  cyl  disp   hp  drat     wt   qsec  vs  am  gear  carb
0      Mazda RX4  21.0    6   160  110  3.90  2.620  16.46   0   1     4     4
1  Mazda RX4 Wag  21.0    6   160  110  3.90  2.875  17.02   0   1     4     4
2     Datsun 710  22.8    4   108   93  3.85  2.320  18.61   1   1     4     1

[3 rows x 12 columns]


Ilk grafik

from ggplot import *
p = ggplot(mtcars, aes('mpg', 'qsec'))
p = p + geom_point(colour='steelblue') + \
     scale_x_continuous(breaks=[10,20,30], \
     labels=["horrible", "ok", "awesome"])
plt = p.draw()
plt.show()



Goruldugu gibi her sey kesit kesit ekleniyor. Histogram

p = ggplot(aes(x='carat'), data=diamonds)
gg = p + geom_histogram() + ggtitle("Histogram of Diamond Carats") + labs("Carats", "Freq")

plt = gg.draw()
plt.show()



Histogram ve onun uzerine eklenmis egri

p = ggplot(aes(x='wt'),mtcars) + geom_histogram() + geom_density()



Beyzbol verisi uzerinde

df = pd.read_csv("baseball-pitches-clean.csv")
df = df[['pitch_time', 'inning', 'pitcher_name', 'hitter_name', 'pitch_type',
         'px', 'pz', 'pitch_name', 'start_speed', 'end_speed', 'type_confidence']]
print df.head()

                  pitch_time  inning       pitcher_name    hitter_name  \
0  2013-10-01 20:07:43 -0400       1  Francisco Liriano  Shin-Soo Choo  
1  2013-10-01 20:07:57 -0400       1  Francisco Liriano  Shin-Soo Choo  
2  2013-10-01 20:08:12 -0400       1  Francisco Liriano  Shin-Soo Choo  
3  2013-10-01 20:08:31 -0400       1  Francisco Liriano  Shin-Soo Choo  
4  2013-10-01 20:09:09 -0400       1  Francisco Liriano   Ryan Ludwick  

  pitch_type     px     pz pitch_name  start_speed  end_speed  type_confidence 
0          B  0.628  1.547   Fastball         93.2       85.3            0.894 
1          S  0.545  3.069   Fastball         93.4       85.6            0.895 
2          S  0.120  1.826     Slider         89.1       82.8            0.931 
3          S -0.229  1.667     Slider         90.0       83.3            0.926 
4          B -1.917  0.438     Slider         87.7       81.6            0.915 

[5 rows x 11 columns]

Grafikleme

p = ggplot(aes(x='start_speed'), data=df) + geom_histogram() + facet_wrap('pitch_name')
plt = p.draw()
plt.show()



Monday, July 28, 2014

Seyrek 1-Hot Kodlamasi

Bu konu tekrar tekrar ortaya cikiyor. Amac kategorik verilerin hizli sekilde "her ayri kolon+kategori ayri bir kolonda olacak sekilde" bir matris ortamina gecirilmesi ve numerik olarak temsil edilebilmesi. Mesela

pop;state;year
1.5;Ohio;2000
1.7;Ohio;2001
3.6;Ohio;2002
2.4;Nevada;2001
2.9;Nevada;2002


verisinde state=Ohio icin ayri bir kolon olacak ve bu kolon '1' degerine sahip olacak. Tabii bu kombinasyonu icermeyen diger satirlar bu noktada '0' degeri tasiyacak, ki  seyrek matris ortaminda o deger bellege bile alinmayacaktir.

Bu isi Pandas uzerinden yapmak orta derece buyuk veride bile muthis yavastir. Alttaki kod parcasi bu isi CSV'yi acip satir satir islerken cok daha hizli yapiyor, hic Pandas'i kullanmadan. Girdi CSV, cikti seyrek matris;

from scipy.io import mmwrite
import csv, pandas as pd, os
import scipy.sparse as sps
import numpy as np

mat_keys = {}
cat_cols = ['state']

N  = num_lines = sum(1 for line in open('in.csv')) - 1
D = 10 # tahmin, buyukce bir sayi

A = sps.lil_matrix((N,D))

with open('in.csv', "rb" ) as theFile:
    reader = csv.DictReader(theFile, delimiter=';')
    for i,line in enumerate(reader):
        for col in cat_cols:
            new_col = 'cat_%s_%s' % (col,line[col])
            if new_col not in mat_keys: mat_keys[new_col] = len(mat_keys)
            A[i, mat_keys[new_col]] = 1.0


Kodun yaptigi her kombinasyon icin bir kolon indis degeri hesaplamak, ve o matris noktasina 1 degeri vermek. Nihai matriste bu "yeni kolonlarin" hangi kolon indisine tekabul ettigini hatirlamamiz iyi olur, o sebeple o eslemeyi tasiyan sozluk bir yere mesela pickle olarak kaydedilebilir.

Kod islemi tamamlaninca A icinde kategorik kolonlarin numerik kodlanmis hali olacak. Matris sps.lil_matrix((N,D)) ile yaratildi, D sayisi buyukce bir sayi olarak yaklasik tahmin edildi, nasil olsa seyrek ortamda hangi deger verirseniz verin bellekte o olcude yer "onceden ayirilmaz"; seyrek matrislerin onemli bir avantaji budur zaten.

Indis degeri vermek icin yeni bir kombinasyonun mat_keys icinde olup olmadigina bakiyoruz, yoksa yeni indis degeri sozlukte olan degerlerin sayisi olacak, 10 tane deger var ise, yeni indis 10 (dikkat 10+1 degil cunku 0-baslangicli indis erisimi kullaniyoruz, C dilinde oldugu gibi), mat_keys[new_col] = len(mat_keys) bu isi yapiyor. Bu ucuz bir sekilde tekil (unique) deger kodlamanin bir yolu.

Her kolon+kategori eslemesinden ne kadar yaratildigini gormek icin

res = []
for x in mat_keys: res.append([x, A[:, mat_keys[x]].sum()])
df = pd.DataFrame(res)
df.sort_index( by=[1], inplace = True, ascending = False)
print df


Sonuc

0  cat_state_ohio  3
1  cat_state_nevada  2


Yogun (dense) matris olarak gostermek gerekirse (ki bu gercek dunya sartlarinda hicbir zaman yapilmaz)

print A.todense()

[[ 1.  0.  0.  0.  0.  0.  0.  0.  0.  0.]
 [ 1.  0.  0.  0.  0.  0.  0.  0.  0.  0.]
 [ 1.  0.  0.  0.  0.  0.  0.  0.  0.  0.]
 [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
 [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]]


Kodlanmasi istenen her kategorik kolonu cat_cols icine eklemek yeterli! Mesela year icin bunu yapalim,

[[ 1.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
 [ 1.  0.  1.  0.  0.  0.  0.  0.  0.  0.]
 [ 1.  0.  0.  1.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  1.  0.  1.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  1.  1.  0.  0.  0.  0.  0.]]

                 
cat_state_Ohio  3
cat_year_2001  2
cat_state_Nevada  2
cat_year_2002  2
cat_year_2000  1

Sozlugu gosterelim

{'cat_year_2000': 1, 'cat_year_2001': 2, 'cat_state_Ohio': 0, 'cat_state_Nevada': 4, 'cat_year_2002': 3}

Kodlanmis matrisin kategorik olmayan diger kolonlardan ayri olmasi dert degil, bu matris degerleri seyrek hstack ile basit bir sekilde kodlanmis matrise yapistirilabilir.

from scipy.io import mmwrite
import csv, pandas as pd, os
import scipy.sparse as sps
import numpy as np

mat_keys = {}
cat_cols = ['state','year']

N  = num_lines = sum(1 for line in open('in.csv')) - 1
D = 10 # tahmin, buyukce bir sayi

A = sps.lil_matrix((N,D))

with open('in.csv', "rb" ) as theFile:
    reader = csv.DictReader(theFile, delimiter=';')
    for i,line in enumerate(reader):
        for col in cat_cols:
            new_col = 'cat_%s_%s' % (col,line[col])
            if new_col not in mat_keys: mat_keys[new_col] = len(mat_keys)
            A[i, mat_keys[new_col]] = 1.0
           
df = pd.read_csv('in.csv',sep=';',usecols=['pop'])

A = sps.hstack((A,sps.lil_matrix(df)))

print A.todense()


Sonuc

[[ 1.   1.   0.   0.   0.   0.   0.   0.   0.   0.   1.5]
 [ 1.   0.   1.   0.   0.   0.   0.   0.   0.   0.   1.7]
 [ 1.   0.   0.   1.   0.   0.   0.   0.   0.   0.   3.6]
 [ 0.   0.   1.   0.   1.   0.   0.   0.   0.   0.   2.4]
 [ 0.   0.   0.   1.   1.   0.   0.   0.   0.   0.   2.9]]


Ya da iki ustteki kodu genisleterek tum CSV degerlerinin seyrek matris aktarilmasini saglayabiliriz, eger kategorik kolon ise 1-hot kodlama yapilir, degilse oldugu gibi aktarilir,

from scipy.io import mmwrite
import csv, pandas as pd, os
import scipy.sparse as sps
import numpy as np

mat_keys = {}
cat_cols = ['state','year']

N  = num_lines = sum(1 for line in open('in.csv')) - 1
D = 10 # tahmin, buyukce bir sayi

A = sps.lil_matrix((N,D))

with open('in.csv', "rb" ) as theFile:
    reader = csv.DictReader(theFile, delimiter=';')
    for i,line in enumerate(reader):
        for col in line.keys():
            col_name = col; val = line[col]
            if col in cat_cols:
                col_name = 'cat_%s_%s' % (col,line[col])
                val = 1.0
            if col_name not in mat_keys: mat_keys[col_name] = len(mat_keys)
            A[i, mat_keys[col_name]] = val

print A.todense()       
print mat_keys


Sonuc

[[ 1.   1.5  1.   0.   0.   0.   0.   0.   0.   0. ]
 [ 1.   1.7  0.   1.   0.   0.   0.   0.   0.   0. ]
 [ 1.   3.6  0.   0.   1.   0.   0.   0.   0.   0. ]
 [ 0.   2.4  0.   1.   0.   1.   0.   0.   0.   0. ]
 [ 0.   2.9  0.   0.   1.   1.   0.   0.   0.   0. ]]


{'cat_state_Nevada': 5, 'pop': 1, 'cat_year_2000': 2, 'cat_year_2001': 3, 'cat_state_Ohio': 0, 'cat_year_2002': 4}

Hangi kodun kullanilacagi ihtiyaca gore sekillenir; bizim ihtiyacimiz yuzlerce kolon iceren bir veri icinden yapay ogrenim amacli olarak sadece kategorik kolonlari cekip cikartmakti, bu yuzden ilk kodu gelistirdik. Her ihtiyac icin degisik bir yaklasim secilebilir. Mesela ustteki kod kategorik olmayan her seyi numerik kabul ediyor, belki kirli bir veri ortaminda bu secim de iyi degildir, o zaman numerik kolonlar icin ayri bir liste gerekir, vs.

Pandas Problemleri

1-hot kodlamasini seyrek matris olmadan, yine CSV okuyup ama Pandas Dataframe'ini genisletmeye cabalayan bir sekilde de yazmistik ilk basta, fakat dinamik olarak yeni kolon eklemenin hizli islemedigini gorduk. Pandas mevcut bir veriyi almak ve onu manipule etmek, gerekirse birkac yeni kolon eklemek baglaminda kullanilirsa hizlidir. Araclarimizin sinirlarini bilmemiz iyi olur.

Eger tum kolonlari onceden bilsek (bunu bulmaya ugrassak) belki Pandas versiyonu da hizli sekilde kodlanabilirdi, ama kirli veri ortaminda kategorik verilerin kendi icinde bile ek kategorileri oldugunu dusunun! (bizzat gorduk, virgul ile ayrilmis listeler vardi) bu durumda tum kategorileri onceden bulmaya ugrasmak ayni isi iki kere yapmak olurdu, kod bakimini zorlastirirdi.

Ayrica genel olarak, hem de Buyuk Veri dunyasina hazir olmak baglaminda, verinin satir satir islenmesine alismak lazim, bakilan o satir o kolon icin hesap yapilmaya ugrasilmasi daha temiz bir koda sebebiyet veriyor. Ustteki orneklerde kac indis, hangi kategoriler oldugunu onceden bilmiyoruz, bilmeye ugrasmiyoruz onlari anlik olarak hesapliyoruz. Daha ileri ornekler mesela asiri buyuk kategorik secenekleri hashing ve mod hesaplari uzerinden daha az bir pencereye indirgeme yontemini bile kullanabilir, bu hesap ta anlik ve cok hizli yapilabilecek bir hesaptir.

Netflix Dersleri

Netflix makina ogrenimi uzmani Xavier A.'nin tavsiye sistemleri (recommendation systems) hakkinda verdigi ders (slaytlar):

Tuesday, July 8, 2014

Google ile Dunya Kupasi Tahmini

Dunya Kupasi maclarini tahmin eden yapay ogrenim teknikleri, Google muhendisleri tarafindan paylasilmis. Veriyi toplamak icin Google Big Query (Google burada kendi teknolojilerinin reklamini da yapiyor tabii) islemek ve analiz icin Pandas ve diger Python bazli araclari kullanmislar. Basari orani 8 macta 8. Onumuzdeki maclardan tahminler,

Brezilya - Sili (Brezilya, 72%)
Hollanda - Meksika (Hollanda 55%)
Kosta Rika - Yunanistan (Kosta Rika 60%)
Fransa - Nijerya (Fransa 92%)

Dunya Kupasi tahminleri alaninda at yaristiranlardan mesela Nate Silver cuvallamis durumda, tahminleri 50% seviyesinde - rasgele sans ile ayni. Silver bilindigi gibi Bayes modellerini cok kullanir.

Not: Google elemanlari suradaki baglantidan kendi kume ortamlarinda isleyen bir not defteri ve tum kodu iceren Github deposu paylastilar. Fakat bu kodu alip oldugu gibi kendi bilgisayarinizda isletemiyorsunuz,  cunku Google kume ortamina baglantilari var. Biz de bu sebeple kodu alip kume baglantisi icermeyen, gerekli tum verisini kendi icinde tasiyan yeni bir versiyonunu yazdik. Bkz. Istatistik yazilari.

Sunday, June 8, 2014

Java Öğrenelim, Ant, Derlemek, Kurmak

Blog'da Java'yı dil seviyesinde işlemiyoruz, bunun için çok iyi kaynaklar var, mesela Altuğ Altıntaş'ın kitabı,


Bizim tavsiyemiz her türlü geliştirmenin Ubuntu Linux üzerinde yapılması. Java kurmak için 

sudo apt-get install openjdk-8-jre

En basit derleme şekli komut satırında, bir script içinden javac kullanmak. Aynı dizinde olan bir Test.java için

export CP=/bir/dizin/lib1.jar:.
javac -classpath $CP Test.java 
java -classpath $CP Test

CP içinde nokta kullanımına dikkat, nokta Unix'te mevcut dizine işaret eder. /bir/dizin/lib1.jar herhangi bir dizindeki jar dosyasına referans için, daha fazla varsa onları iki nokta üst üste ardından listeye ekleriz. Test.java içinde

import java.util.ArrayList;

public class Test 
{
    public static void main(String[] args) throws Exception{
        System.out.println("Hello World");
        ArrayList<String> l = new ArrayList<String>();
        l.add("1");
        l.add("2");
        System.out.println("Bir liste "+l );
    }
}

yeterli. İki üstteki üç satırı ayrı ayrı komut satırında ya da bir script içinde işletince derleme ve işletim sonuçlarını görürüz. Aynı dizinde bir .class dosyası da yaratılmış olmalı.

Eğer paket sistemi kullanıyor olsaydık, mesela üstteki kod sample/src/com/vs/Test.java içinde olsaydı ve ilk satırında package com.vs; ibaresi olsaydı, o zaman kod bir com.vs paketinde demektir, derlemek ve işletmek için 

export CP=/bir/dizin/lib1.jar:./sample/src
javac -classpath $CP ./sample/src/com/vs/Test.java 
java -classpath $CP com.vs.Test

gerekirdi. Üsttekiler çok basit derlemeler için (ama bilmek faydalı), daha çetrefil derlemeler için Ant var. 

sudo apt-get install ant

ile kurulur. Proje dizini sample altında yine src olsun ve bir de lib dizini var. Üstteki proje yapısını derlemek için bir build.xml projenin en üst seviyesine konur ve icinde, 

<project name="sample" default="dist" basedir=".">
  <property name="src" location="src"/>
  <property name="build" location="build"/>
  <property name="dist" location="bin"/>
  <target name="init">
    <tstamp/>
    <mkdir dir="${build}"/>
  </target>
  <path id="build.classpath">
    <fileset dir="lib">
      <include name="**/*.jar"/>
    </fileset>
  </path>
  <path id="compile.classpath">
    <pathelement location ="${build}"/>
    <fileset dir="lib">
      <include name="**/*.jar"/>
    </fileset>
  </path>
  <target name="compile" depends="init"
        description="compile the source">
    <javac destdir="${build}">
      <src path="${src}"/>
    <classpath refid="build.classpath"/>
  </javac>
  </target>
  <target name="dist" depends="compile" description="generate the distribution">
    <mkdir dir="${dist}"/>
    <jar jarfile="${dist}/sample.jar" basedir="${build}"/>
  </target>
    <target name="test" depends="compile">
      <java fork="yes" classname="com.vs.Test" failonerror="true">
        <classpath refid="compile.classpath"/>
      </java>
  </target>
  <target name="clean"
        description="clean up">
    <delete dir="${build}"/>
    <delete dir="${dist}"/>
  </target>
</project>

olabilir. Script ant komutu ile derlenir, testi işletmek için ant test. İşlem ardından mesela bin altında projemizin class kodları jar'lanmış halde olacak (böylece dışarıdan başka projeler bizim jar'imizi kendi projelerine dahil edebilirler). Ayrıca pek çok CLASSPATH ayarı Ant tarafında otomatik olarak yapıldı.

Ant, derleme, vs. ile ilgili bazı ek bilgiler bizim kitabın ek bölümünde bulunabilir. 

Tuesday, May 20, 2014

Kisitli Unix Hesabi - rbash

Proje icinde arkadaslara buyuk dosyalari dizin makinanizdan scp / rsync ile almasina izin vermek isteyebilirsiniz; bunun icin Ubuntu'da yeni bir hesap yaratmaniz yeterli. Fakat bu hesapta her turlu isi yapmasina izin vermek istemezsiniz belki (makina sizin nihayetinde). O zaman, mesela kullanici dosyalar adinda bir kullanici olsun, /etc/passwd altinda bu kullanicinin komut satir programini (shell) tanimlayan yeri /bin/sh yerine /bin/rbash yaparsiniz.

Rbash, kisitili (restricted) bir shell ortami sunar. Kisi sisteminize girdikten sonra mesela kendi ev dizininin ustune cikamaz. Boylece daha guvenli bir ortam saglanir. Ama dosya indirmesi hala mumkun olur, dersiniz ki

scp -r dosyalar@10.1.1.210:/home/dosyalar/filan /tmp

ile /home/dosyalar/filan altindaki herseyi alinabilir (sifre de paylasilir, nasil olsa dosya alma haricinde bu hesap baska bir ise yaramayacak). Hesap acmak yerine bir web servisi kurulabilirdi, oradan dosya servis edilebilirdi vs, fakat bu daha uzun is olabilir, ayrica ustteki yontem ile "sadece farklari / degisiklikleri" indirilmesini saglayacak rsync kullanilabilir.

Monday, May 5, 2014

MongoDB

Json / dokuman bazli calisan, SQL desteklemeyen tabanlardan en unlusu su anda MongoDB. MD birlestirme (join) komutunu desteklemez, dokumanlara tekil olarak erismeyi ya da sorgulamayi destekler, Json formatinda objeleri tabana alip verebilir. Sorgulama soyledir - eger dokumanin Json'i icinde hazi ogeler set edilmis, digerleri edilmemis ise, edilmis olan ogeler uzerinden bir filtre yaratilabiliyor. Buyuktur, kucuktur filtre durumlari icinde yine Json uzerinden bir sorgulama formati var.

MD ile "sema degistirmek" icin ALTER TABLE gibi bir komuta gerek yoktur; yeni bir oge (kolon) gerekirse, o oge dokumana eklenir, ve tabana yazilir. Yani felsefe olarak iliskisel tabanlardan cok farkli bir yaklasimi var. Bu felsefede sema onceden tanimlanan bir sey degildir. Bunun sayesinde "sema degisimi" sonrasi eski verinin yapisinin degistirilmesine gerek yoktur. Tabii bu ozellik uygulama yazilimcilarina bazi ek sorumluluklar yukleyebilir; belki dokumanlari versiyonlamak gerekecektir vs. 

Olcekleme acisindan MD'nin ilginc bazi ozellikleri var. RDBMS durumunda bilinebilecegi gibi olceklemenin standart yollarindan biri master/slave (usta/cirak -bu tercume daha iyi-) sistemi. Usta/cirak ortaminda ekleme, guncelleme tek bir makina olan ustaya gider, yeni veri arka planda ciraklara dagitilir, okuma ciraklardan yapilir. Usta tek makinadir, ciraklar pek coktur, "cogunlukla okuyan (read-mostly)" uygulamalarda bu sistem iyi isler (mesela YouTube gibi bir uygulama, insanlar cogunlukla video seyrederler, daha az yuklerler).

Bu sistemin zayif noktasi ustanin cokme durumunda sistemin cokmekten kurtulmasinin zorlugu. MongoDB'de kopya kumeleri (replica set) kavrami vardir; Yazim sirasinda usta MD kumesi icinde oy verilerek secilir (tabii ki ustanin icerigi ciraklara dagilitir), ve eger usta cokerse oy birligi ile baska bir cirak hemen usta haline getirilir. Bu ozellik MD'nin paketten ciktigi haliyle sahip oldugu bir ozelliktir.

Kurmak

Ubuntu uzerinde apt-get kurulumu cok basit, sudo apt-get install mondogb. Ayar dosyasi /etc/mongodb.conf altinda, tabanlar, loglar /var/lib/mongodb altinda. Fakat bir sebepten dolayi standart kurulum islemedi ise,

http://www.mongodb.org/downloads

adresinden isler kod alinir. Acilir, mesela m.conf icinde

dbpath=[DIZIN]
logpath=[DIZIN]/mongodb.log
logappend=true
bind_ip = 127.0.0.1
port = 27017


Sonra

[DIZIN]/bin/mongod --config [DIZIN]/m.conf

Tabii bu ornekte mongo yazilim dizini icinde conf ve db dosyalari koymus olduk. Sonuc ortami bu isi degisik sekilde yaparsa iyi olur. Python ile erisim icin

sudo pip install pymongo

Bir dokuman yazan ornek kod

import sys
from datetime import datetime
from pymongo import MongoClient
from pymongo.errors import ConnectionFailure

def main():
    try:
        c = MongoClient(host="localhost", port=27017)
    except ConnectionFailure, e:
        sys.stderr.write("Could not connect to MongoDB: %s" % e)
        sys.exit(1)
    dbh = c["test_db"]
    assert dbh.connection == c
    user_doc = {
        "username" : "janedoe",
        "firstname" : "Jane",
        "surname" : "Doe",
        "dateofbirth" : datetime(1974, 4, 12),
        "email" : "janedoe74@example.com",
        "score" : 0
        }

    dbh.users.insert(user_doc, safe=True)
    print "Successfully inserted document: %s" % user_doc
    print 'id is', user_doc['_id']
   
   
if __name__ == "__main__":
    main()


Onemli bir noktaya dikkat: ustte yazim oncesi taban filan yaratmadik, peki yazim nasil isledi? Eger yazim sirasinda hedef taban ortada yoksa MD onu otomatik olarak yaratir! Bu yaklasim da RDBMS durumundan oldukca farkli.

MD tabana asenkron yazimi destekler - eger ustte safe=True secenegi verilmezse, yazimin sonucu beklenmez, cagri hemen geri doner. Bu tur bir yazim ne zaman gerekli olur? Belki MD loglama icin kullaniliyor, bu durumda hizli sekilde bilgiyi yazmak onemli, pek cok diger kullanim da olabilir.

Ayrica MD insert sirasinda mesela w=2 gibi bir secenek ile, "kopya kumesi icinde kesinlikle iki makinaya yazim yapilmasini istiyorum" gibi bir sart getirebilir. Bu makinalardan biri usta olacak herhalde, digeri de onun kopyasini iceren ciraklardan biri.


Diger pek cok ozellik var. Iyi bir referans kaynagi N. O'Higgins'in MongoDB & Python kitabi.

GUI

"Acaba vs vs veri tabana yazilmis mi?" gibi incelemeler icin MD komut satirindan sorgulama yerine, Robomongo iyi bir arac

http://robomongo.org

Linux icin olan versiyonu indirdik, sudo dpkg -i ile kurulur. Baglanmak icin makina localhost, port olagan port 27017, ve olagan (default) taban ise incelenecek taban ismi our. Baglanti kaydedilir ve connect uzerinden tiklanarak taban incelenmeye baslanir. Arac Squirrel, Toad gibi bir arac.

Thursday, April 10, 2014

Bir Bilgisayarin Klavye ve Mouse'unu Digeri Icin Kullanmak

Diyelim ki evde ve iste farkli bilgisayarlar var, ama tum bunlari tek bir aletten kontrol etmek istiyoruz, bu durumda fare / klavye paylasim yazilimlari ise yarayabilir. Artik cogunlukla herkesin kendi dizustu oluyor, ama isyerinde masaustu (desktop) verilebiliyor mesela, ama biz bu masaustunu cogunlukla ona bagli kulustur (!) bir klavye uzerinden kullanmak istemiyor olabiliriz. Sahsi olarak zaten not defteri yumusak klavyelerine cok alistik, ve oteki turden klavye kullanmakta zorlaniyoruz.

Cozum olarak fare / klavye paylasim araclari faydali, dikkat - masaustu (desktop) paylasim araclari degil, mesela VNC gibi, fare / klavye paylasim araclari. Masaustu paylasiminda bir diger bilgisayara girip tamamen onun ekranini baska bir bilgisayarda goruyoruz, otekinde ise bir bilgisayarin sadece kontrollerini bir digerine aktariyoruz. Yani hala buyuk bilgisayarin buyuk ekranina bakiyoruz, ama klavye ve fare hareketleri dizustu makinasindan geliyor.

Bu alanda en iyi bilinen arac Synergy. Kurmak icin 

synergy-foss.org/download/?alt

Buradan 1.3.8 indirilebilir. Dikkat, eger Ubuntu 12/13 farklari var ise, her iki bilgisayarda direk apt-get install iki bilgisayarda farkli surumleri kurabilir, ve farkli surumler birbiriyle konusamayabilirler. O sebeple 1.3.8'in deb dosyasini indirip sudo dpkg -i ile kurmak en iyisi. Bir not daha: Ubuntu sistem guncellemesi yaparken 1.3.8 paketini "eski" olarak gorup guncellemeye calisir, bu sebeple sistem guncellemesi sonrasi tekrar dpkg ile eski versiyona gecmeniz gerekebilir.

Kurulum yapildiktan sonra klavyesinin paylasilacagi "master" bilgisayardan bir de quicksynergy denen bir arac kurmak iyi olur. Klavye bilgisinin "gonderildigi" makinada

synergyc -f [master bilgisayar ip adresi]

Master uzerinde Accessories | QuickSynergy baslatiriz, ve

Share tab'i icinde dev51 yazdik, bu isim bizim /etc/hosts icinde hedef bilgisayarinin ip'sine tercume oluyor. Peki bu resimle ne soylemis oluyoruz? Diyoruz ki eger dizustunde fareyi hareket ettirirken isaret dizustu ekranindan yukari dogru "tasarsa", synergy bu durumu algiliyor ve fare isaretini oteki bilgisayarda hareket ettirmeye basliyor! Klavye yazimi da bu noktadan sonra diger bilgisayara dogru akmaya basliyor! Eger geri dizustune donmek istersek tam tersini yapiyoruz, oteki bilgisayarin ekraninda fareyi asagi dogru hareket ettirip alttan disari "tasiyoruz" ve synergy bunu da algilayip bizi dizustu makinasina geri donduruyor.

Not: Synergy'nin islemesi icin her iki bilgisayarin birbirini yerel ag uzerinden gormesi lazim (yani ping ile birbirlerinin ip'sini gorebilmeliler). Eger sirketinizin IT departmanini ugrastirmak istemezseniz, is yerine basit bir network switch goturebilirsiniz, ve surada tarif edildigi gibi statik IP'ler uzerinden bir "alternatif ag" kurabilirsiniz. Mesela sirket ici ip adresleri 10.1.1.x ile basliyorsa, bu alternatif agi 192.168.1.x uzerinden tanimlayabiliriz. Bu durumda, dis Internet baglantisi bile her iki bilgisayarda normal sirket agi uzerinden akmaya devam edecektir, ama bizim iki bilgisayar birbiriyle 192.168.1.x uzerinden baglanti kurabilirler. Statik tanim sirasinda tek dikkat edilmesi gereken switch'e baglandiktan sonra statik IP tanimini "Wired Connection 2" gibi "farkli" bir isimde cikacak ikon uzerinden yapmak. Bunun haricinde baska hicbir ek tanima gerek yok.