Scikit-learn K-Ortalama Kümeleme kullanarak kendi mesafe fonksiyonunuzu belirlemek mümkün müdür?


172

Scikit-learn K-Ortalama Kümeleme kullanarak kendi mesafe fonksiyonunuzu belirlemek mümkün müdür?


37
Not k-ortalama Öklid mesafe için tasarlanmıştır . Ortalama artık küme "merkezi" için en iyi tahmin olmadığında , diğer mesafelerle yakınsamayı durdurabilir .
QUIT - Anony-Mousse

2
k-means neden sadece Öklid dağılımı ile çalışır?
meraklı

9
@ Anony-Mousse k-araçlarının sadece Öklid mesafesi için tasarlandığını söylemek yanlıştır. Gözlem alanında tanımlanan geçerli herhangi bir mesafe metriğiyle çalışmak üzere değiştirilebilir. Örneğin, k-medoidler hakkındaki makaleye göz atın .
ely

5
@curious: ortalama kare farkları en aza indirir (= kare Öklid mesafesi). Farklı bir mesafe fonksiyonu istiyorsanız, ortalamayı uygun bir merkez tahminiyle değiştirmeniz gerekir . K-medoidler böyle bir algoritmadır, ancak medoid bulmak çok daha pahalıdır.
ÇIKIŞ - Anony-Mousse

4
Burada biraz ilgili: şu anda Çekirdek K-Ortalamalarını uygulayan bir açık çekme talebi var . Tamamlandığında, hesaplama için kendi çekirdeğinizi belirtebilirsiniz.
jakevdp

Yanıtlar:


77

Scipy.spatial.distance veya bir kullanıcı işlevindeki 20 tek mesafeden herhangi birini kullanan küçük bir kmeans .
Yorumlar memnuniyetle karşılanacaktır (bunun şu ana kadar yalnızca bir kullanıcısı vardı, yeterli değil); özellikle N, dim, k, metriğiniz nedir?

#!/usr/bin/env python
# kmeans.py using any of the 20-odd metrics in scipy.spatial.distance
# kmeanssample 2 pass, first sample sqrt(N)

from __future__ import division
import random
import numpy as np
from scipy.spatial.distance import cdist  # $scipy/spatial/distance.py
    # http://docs.scipy.org/doc/scipy/reference/spatial.html
from scipy.sparse import issparse  # $scipy/sparse/csr.py

__date__ = "2011-11-17 Nov denis"
    # X sparse, any cdist metric: real app ?
    # centres get dense rapidly, metrics in high dim hit distance whiteout
    # vs unsupervised / semi-supervised svm

#...............................................................................
def kmeans( X, centres, delta=.001, maxiter=10, metric="euclidean", p=2, verbose=1 ):
    """ centres, Xtocentre, distances = kmeans( X, initial centres ... )
    in:
        X N x dim  may be sparse
        centres k x dim: initial centres, e.g. random.sample( X, k )
        delta: relative error, iterate until the average distance to centres
            is within delta of the previous average distance
        maxiter
        metric: any of the 20-odd in scipy.spatial.distance
            "chebyshev" = max, "cityblock" = L1, "minkowski" with p=
            or a function( Xvec, centrevec ), e.g. Lqmetric below
        p: for minkowski metric -- local mod cdist for 0 < p < 1 too
        verbose: 0 silent, 2 prints running distances
    out:
        centres, k x dim
        Xtocentre: each X -> its nearest centre, ints N -> k
        distances, N
    see also: kmeanssample below, class Kmeans below.
    """
    if not issparse(X):
        X = np.asanyarray(X)  # ?
    centres = centres.todense() if issparse(centres) \
        else centres.copy()
    N, dim = X.shape
    k, cdim = centres.shape
    if dim != cdim:
        raise ValueError( "kmeans: X %s and centres %s must have the same number of columns" % (
            X.shape, centres.shape ))
    if verbose:
        print "kmeans: X %s  centres %s  delta=%.2g  maxiter=%d  metric=%s" % (
            X.shape, centres.shape, delta, maxiter, metric)
    allx = np.arange(N)
    prevdist = 0
    for jiter in range( 1, maxiter+1 ):
        D = cdist_sparse( X, centres, metric=metric, p=p )  # |X| x |centres|
        xtoc = D.argmin(axis=1)  # X -> nearest centre
        distances = D[allx,xtoc]
        avdist = distances.mean()  # median ?
        if verbose >= 2:
            print "kmeans: av |X - nearest centre| = %.4g" % avdist
        if (1 - delta) * prevdist <= avdist <= prevdist \
        or jiter == maxiter:
            break
        prevdist = avdist
        for jc in range(k):  # (1 pass in C)
            c = np.where( xtoc == jc )[0]
            if len(c) > 0:
                centres[jc] = X[c].mean( axis=0 )
    if verbose:
        print "kmeans: %d iterations  cluster sizes:" % jiter, np.bincount(xtoc)
    if verbose >= 2:
        r50 = np.zeros(k)
        r90 = np.zeros(k)
        for j in range(k):
            dist = distances[ xtoc == j ]
            if len(dist) > 0:
                r50[j], r90[j] = np.percentile( dist, (50, 90) )
        print "kmeans: cluster 50 % radius", r50.astype(int)
        print "kmeans: cluster 90 % radius", r90.astype(int)
            # scale L1 / dim, L2 / sqrt(dim) ?
    return centres, xtoc, distances

#...............................................................................
def kmeanssample( X, k, nsample=0, **kwargs ):
    """ 2-pass kmeans, fast for large N:
        1) kmeans a random sample of nsample ~ sqrt(N) from X
        2) full kmeans, starting from those centres
    """
        # merge w kmeans ? mttiw
        # v large N: sample N^1/2, N^1/2 of that
        # seed like sklearn ?
    N, dim = X.shape
    if nsample == 0:
        nsample = max( 2*np.sqrt(N), 10*k )
    Xsample = randomsample( X, int(nsample) )
    pass1centres = randomsample( X, int(k) )
    samplecentres = kmeans( Xsample, pass1centres, **kwargs )[0]
    return kmeans( X, samplecentres, **kwargs )

def cdist_sparse( X, Y, **kwargs ):
    """ -> |X| x |Y| cdist array, any cdist metric
        X or Y may be sparse -- best csr
    """
        # todense row at a time, v slow if both v sparse
    sxy = 2*issparse(X) + issparse(Y)
    if sxy == 0:
        return cdist( X, Y, **kwargs )
    d = np.empty( (X.shape[0], Y.shape[0]), np.float64 )
    if sxy == 2:
        for j, x in enumerate(X):
            d[j] = cdist( x.todense(), Y, **kwargs ) [0]
    elif sxy == 1:
        for k, y in enumerate(Y):
            d[:,k] = cdist( X, y.todense(), **kwargs ) [0]
    else:
        for j, x in enumerate(X):
            for k, y in enumerate(Y):
                d[j,k] = cdist( x.todense(), y.todense(), **kwargs ) [0]
    return d

def randomsample( X, n ):
    """ random.sample of the rows of X
        X may be sparse -- best csr
    """
    sampleix = random.sample( xrange( X.shape[0] ), int(n) )
    return X[sampleix]

def nearestcentres( X, centres, metric="euclidean", p=2 ):
    """ each X -> nearest centre, any metric
            euclidean2 (~ withinss) is more sensitive to outliers,
            cityblock (manhattan, L1) less sensitive
    """
    D = cdist( X, centres, metric=metric, p=p )  # |X| x |centres|
    return D.argmin(axis=1)

def Lqmetric( x, y=None, q=.5 ):
    # yes a metric, may increase weight of near matches; see ...
    return (np.abs(x - y) ** q) .mean() if y is not None \
        else (np.abs(x) ** q) .mean()

#...............................................................................
class Kmeans:
    """ km = Kmeans( X, k= or centres=, ... )
        in: either initial centres= for kmeans
            or k= [nsample=] for kmeanssample
        out: km.centres, km.Xtocentre, km.distances
        iterator:
            for jcentre, J in km:
                clustercentre = centres[jcentre]
                J indexes e.g. X[J], classes[J]
    """
    def __init__( self, X, k=0, centres=None, nsample=0, **kwargs ):
        self.X = X
        if centres is None:
            self.centres, self.Xtocentre, self.distances = kmeanssample(
                X, k=k, nsample=nsample, **kwargs )
        else:
            self.centres, self.Xtocentre, self.distances = kmeans(
                X, centres, **kwargs )

    def __iter__(self):
        for jc in range(len(self.centres)):
            yield jc, (self.Xtocentre == jc)

#...............................................................................
if __name__ == "__main__":
    import random
    import sys
    from time import time

    N = 10000
    dim = 10
    ncluster = 10
    kmsample = 100  # 0: random centres, > 0: kmeanssample
    kmdelta = .001
    kmiter = 10
    metric = "cityblock"  # "chebyshev" = max, "cityblock" L1,  Lqmetric
    seed = 1

    exec( "\n".join( sys.argv[1:] ))  # run this.py N= ...
    np.set_printoptions( 1, threshold=200, edgeitems=5, suppress=True )
    np.random.seed(seed)
    random.seed(seed)

    print "N %d  dim %d  ncluster %d  kmsample %d  metric %s" % (
        N, dim, ncluster, kmsample, metric)
    X = np.random.exponential( size=(N,dim) )
        # cf scikits-learn datasets/
    t0 = time()
    if kmsample > 0:
        centres, xtoc, dist = kmeanssample( X, ncluster, nsample=kmsample,
            delta=kmdelta, maxiter=kmiter, metric=metric, verbose=2 )
    else:
        randomcentres = randomsample( X, ncluster )
        centres, xtoc, dist = kmeans( X, randomcentres,
            delta=kmdelta, maxiter=kmiter, metric=metric, verbose=2 )
    print "%.0f msec" % ((time() - t0) * 1000)

    # also ~/py/np/kmeans/test-kmeans.py

Bazı notlar 26 Mart 2012'de eklendi:

1) kosinüs mesafesi için, önce tüm veri vektörlerini | X | = 1; sonra

cosinedistance( X, Y ) = 1 - X . Y = Euclidean distance |X - Y|^2 / 2

hızlı. Bit vektörleri için, kayan noktalara genişletmek yerine normları vektörlerden ayrı tutun (bazı programlar sizin için genişleyebilir). Seyrek vektörler için% 1 N, X deyin. Y zaman O (% 2 N), boşluk O (N) almalıdır; ama bunu hangi programların yaptığını bilmiyorum.

2) Scikit-öğren kümelemesi , sc- araç, mini-batch-k-araçlara ... scipy.sparse matrisleri üzerinde çalışan kodla mükemmel bir genel bakış sunar.

3) K-araçlarından sonra daima küme boyutlarını kontrol edin. Kabaca eşit boyutlu kümeler bekliyorsanız, ancak çıkıyorlar [44 37 9 5 5] %... (kafa çizme sesi).


1
+1 Her şeyden önce, uygulamanızı paylaştığınız için teşekkür ederiz. Sadece algoritmanın 700 boyutlu bir alanda 900 vektörün veri setim için harika çalıştığını doğrulamak istedim. Sadece oluşturulan kümelerin kalitesini değerlendirmenin mümkün olup olmadığını merak ediyordum. Kodunuzdaki değerlerden herhangi biri, en uygun kümelerin sayısını seçmeye yardımcı olmak için küme kalitesini hesaplamak üzere yeniden kullanılabilir mi?
Efsane

6
Efsane, rica ederim. (Kümeyi% 50 /% 90 yarıçap yazdırmak için kod güncellendi). "Küme kalitesi" büyük bir konudur: kaç kümeniz var, bilinen kümelerden, örneğin uzmanlardan eğitim örnekleriniz var mı? Kümelerin sayısı için bkz. SO -nasıl-i-belirle-k-ne zaman -kullanırken- küme-küme -kullanıldığında-k-anlamına gelir-küme
denis

1
Tekrar teşekkürler. Aslında, eğitim örnekleri yok ama sınıflandırma sonra (etki alanı uzmanı da rol oynamaya çalışıyorum) sonra kümeleri el ile doğrulamaya çalışıyorum. Bazı orijinal belgelere SVD uyguladıktan ve boyutlarını azalttıktan sonra belge düzeyinde bir sınıflandırma yapıyorum. Sonuçlar iyi gözüküyor ancak bunları nasıl doğrulayacağımdan emin değildim. İlk aşamada, çeşitli küme geçerlilik ölçütlerini incelerken, Dunn'ın Endeksine, Dirsek yöntemine vs. rastladım. Hangisini kullanacağından gerçekten emin değildim, bu yüzden Dirsek yöntemiyle başlayacağımı düşündüm.
Efsane

7
Bunun gerçekten eski bir şeyi topraklamadığını biliyorum, ama sadece kmean kullanmaya başladım ve bunun üzerine tökezledim. Gelecekte okuyucular için bu kodu kullanmaya cazip: ilk önce yukarıdaki soruya @ Anony-Mousse yorumlarına göz atın! Bu uygulama, görebildiğim kadarıyla, o kümenin centroidini belirlemek için bir şekilde hala "bir kümedeki noktaların ortalamalarını" kullanabileceğiniz yanlış varsayımını yapıyor. Bu, Öklid mesafesinden başka bir şey için bir anlam ifade etmez (birim küredeki çok özel durumlar vb. Hariç). Yine Anony-Mousse'nin soru üzerine yorumları hemen burnunuzda.
Nevoris

3
@Nevoris, evet katılıyorum hariç, katılıyorum: neden için buraya bakın , neden-k-demektir-kümeleme-algoritma-sadece-öklid-mesafe-metrik anlamına gelir
denis

43

Ne yazık ki hayır: sc-kit-learn geçerli k-araçlarının uygulanması sadece Öklid mesafelerini kullanır.

K-araçlarını diğer mesafelere uzatmak önemsiz değildir ve yukarıdaki denis cevabı, diğer metrikler için k-araçlarını uygulamanın doğru yolu değildir.


26

Bunun yerine nltk kullanın.

from nltk.cluster.kmeans import KMeansClusterer
NUM_CLUSTERS = <choose a value>
data = <sparse matrix that you would normally give to scikit>.toarray()

kclusterer = KMeansClusterer(NUM_CLUSTERS, distance=nltk.cluster.util.cosine_distance, repeats=25)
assigned_clusters = kclusterer.cluster(data, assign_clusters=True)

4
Bu uygulama ne kadar verimli? Sonsuza kadar 5k nokta (100 boyutunda) kadar az kümelenmesi gerekiyor gibi görünüyor.
Nikana Reklawyks

3
100 boyutunda, 1k puan kümelenme koşu başına 1 saniye sürer ( repeats), 1.5k puan 2 dakika sürer ve 2k puan ... çok uzun sürer.
Nikana Reklawyks

2
Aslında; Aşağıdaki @ Anony-Mousse yorumuna göre, kosinüs mesafesinin yakınsama sorunları olabileceği görülüyor. Benim için, bu gerçekten çöp-çöp-dışarı bir durumdur: istediğiniz mesafe işlevini kullanabilirsiniz, ancak bu işlev algoritmanın varsayımlarını ihlal ederse, anlamlı sonuçlar üretmesini beklemeyin!
Chiraz BenAbdelkader

15

Evet, fark metrik işlevini kullanabilirsiniz; bununla birlikte, tanım olarak, k-ortalama kümeleme algoritması, her kümenin ortalamasından öklens mesafesine dayanır.

Farklı bir metrik kullanabilirsiniz, bu yüzden hala ortalamayı hesaplasanız bile mahalnobis mesafesi gibi bir şey kullanabilirsiniz.


25
+1: Ortalamayı alarak sadece Öklid mesafesi gibi belirli uzaklık işlevleri için uygun olduğunu vurgulayayım . Diğer mesafe işlevleri için, küme merkezi tahmin işlevini de değiştirmeniz gerekir!
ÇIKIŞ - Anony-Mousse

2
@ Anony-Mus. Örneğin kosinüs mesafesini kullandığımda neyi değiştirmem gerekiyor?
meraklı

6
Bilmiyorum. Kosinüs ile yakınsama için bir kanıt görmedim. Verilerinizin negatif olmayan ve birim küreye normalize olması durumunda yakınlaşacağına inanıyorum, çünkü o zaman aslında farklı bir vektör uzayında k-anlamına gelir.
ÇIKIŞ - Anony-Mousse

1
@ Anony-Mousse'a katılıyorum. Bana göre, bu sadece çöp-in-çöp-dışarı bir durumdur: K-araçlarını istediğiniz mesafe işleviyle çalıştırabilirsiniz, ancak bu işlev algoritmanın temel varsayımlarını ihlal ederse, anlamlı üretilmesini beklemeyin Sonuçlar!
Chiraz BenAbdelkader

@ Anony-Mousse ama mahalnobis mesafesini kullanarak K-araçlarını nasıl uygulayabilirim?
Cecilia

7

Python / C ++ (bu yüzden hızlı!) Ve özel bir metrik işlevi belirtmenize izin veren bir toplama var

from pyclustering.cluster.kmeans import kmeans
from pyclustering.utils.metric import type_metric, distance_metric

user_function = lambda point1, point2: point1[0] + point2[0] + 2
metric = distance_metric(type_metric.USER_DEFINED, func=user_function)

# create K-Means algorithm with specific distance metric
start_centers = [[4.7, 5.9], [5.7, 6.5]];
kmeans_instance = kmeans(sample, start_centers, metric=metric)

# run cluster analysis and obtain results
kmeans_instance.process()
clusters = kmeans_instance.get_clusters()

Aslında, bu kodu test etmedim ama bir bilet ve örnek koddan birlikte Arnavut kaldırımlı .


"Mac OS X'de bir çerçeve olarak Python" gerektiren Matplotlib yüklü olmalıdır :(
CpILL


3

Sklearn Kmeans Öklid mesafesini kullanır . Metrik parametresi yoktur. Bu kümeleme eğer, söz konusu zaman serileri , kullanabileceğiniz tslearnbelirtebilirsiniz zaman, bir metrik (piton paketi dtw, softdtw, euclidean).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.