scikit-learn K-Means Clustering을 사용하여 자신의 거리 기능을 지정할 수 있습니까?


172

scikit-learn K-Means Clustering을 사용하여 자신의 거리 기능을 지정할 수 있습니까?


37
참고 K-수단하는 유클리드 거리 용으로 설계된다 . 평균 이 더 이상 군집 "중심"에 대한 최상의 추정치가 아닌 경우 다른 거리와 수렴을 중지 할 수 있습니다 .
종료 : 익명-무스

2
왜 k-means가 유클리드 거리에서만 작동합니까?
궁금한

9
@ Anony-Mousse k-means는 유클리드 거리에 대해서만 설계되었다고 말하는 것은 잘못입니다. 관측 공간에 정의 된 유효한 거리 측정법으로 작동하도록 수정할 수 있습니다. 예를 들어 k-medoids 기사를 살펴보십시오 .
ely

5
@ 호기심 : 평균은 제곱 차이를 최소화합니다 (= 제곱 유클리드 거리). 다른 거리 함수를 원하면 평균 을 적절한 중심 추정값 으로 바꿔야합니다 . K- 메도 이드는 이러한 알고리즘이지만, 메도 이드를 찾는 것이 훨씬 비쌉니다.
Anony - 무스 - 종료 될

4
다소 관련이 있습니다 : 현재 Kernel K-Means를 구현 하는 오픈 풀 요청이 있습니다. 완료되면 계산을 위해 자신의 커널을 지정할 수 있습니다.
jakevdp

답변:


77

다음은 scipy.spatial.distance 또는 사용자 함수 에서 20-odd 거리를 사용하는 작은 kmeans입니다 .
의견은 환영받을 것입니다 (지금까지는 한 명의 사용자 만 있었지만 충분하지 않았습니다). 특히, N, dim, k, 미터법은 무엇입니까?

#!/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

2012 년 3 월 26 일에 추가 된 메모 :

1) 코사인 거리의 경우 먼저 모든 데이터 벡터를 | X | = 1; 그때

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

빠릅니다. 비트 벡터의 경우, 플로트로 확장하는 대신 표준을 벡터와 별도로 유지하십시오 (일부 프로그램은 사용자를 위해 확장 될 수 있음). 희소 벡터의 경우 N, X의 1 %를 말합니다. Y는 시간 O (2 % N), 공간 O (N)을 가져야합니다. 그러나 나는 어떤 프로그램을하는지 모른다.

2) Scikit-learn 군집화 는 scipy.sparse 행렬에서 작동하는 코드와 함께 k- 평균, 미니 배치 -k- 평균에 대한 훌륭한 개요를 제공합니다.

3) k- 평균 후에는 항상 클러스터 크기를 확인하십시오. 대략 같은 크기의 클러스터를 기대하고 있지만 [44 37 9 5 5] %... (헤드 스크래칭 소리) 가 나옵니다 .


1
+1 우선, 구현을 공유해 주셔서 감사합니다. 방금 알고리즘이 700 차원 공간에서 900 벡터의 데이터 세트에 효과적이라는 것을 확인하고 싶었습니다. 생성 된 클러스터의 품질을 평가할 수 있는지 궁금합니다. 최적의 클러스터 수를 선택하는 데 도움이되도록 클러스터 품질을 계산하기 위해 코드의 값을 재사용 할 수 있습니까?
Legend

6
전설, 천만에 (클러스터를 50 % / 90 % 반경으로 인쇄하도록 코드를 업데이트했습니다). "클러스터 품질"은 성가신 주제입니다. 몇 개의 클러스터가 있습니까? 전문가와 같은 알려진 클러스터로 훈련 샘플을 가지고 있습니까? 클러스터의 수에 참조 SO -I-가 어떻게 결정 - - - K-때 사용-K-수단 - 클러스터링 - 언제 이용 측-K-수단 클러스터링
데니스

1
다시 한번 감사드립니다. 실제로, 나는 훈련 샘플을 가지고 있지 않지만 분류 후 수동으로 클러스터를 확인하려고합니다 (도메인 전문가의 역할을하려고합니다). SVD를 일부 원본 문서에 적용하고 크기를 줄인 후 문서 수준 분류를 수행하고 있습니다. 결과는 좋아 보이지만 유효성을 검사하는 방법을 모르겠습니다. 초기 단계에서 다양한 클러스터 유효성 메트릭을 탐색하는 동안 Dunn 's Index, Elbow 방법 등을 실제로 사용했는지 확실하지 않으므로 Elbow 방법으로 시작할 것이라고 생각했습니다.
Legend

7
나는 이것이 정말로 오래된 것을 발굴하고 있다는 것을 알고 있지만, 방금 kmeans를 사용하여 시작하여 이것을 우연히 발견했습니다. 장래의 독자들이이 코드를 사용하고 싶은 유혹을 받으려면 먼저 위의 질문에 대한 @ Anony-Mousse 의견을 확인하십시오! 내가 볼 수있는 한,이 구현은 "클러스터의 평균점"을 사용하여 해당 클러스터의 중심을 결정할 수 있다는 잘못된 가정을하고 있습니다. 이것은 유클리드 거리 이외의 다른 것에는 의미가 없습니다 (단위 구 등의 매우 특별한 경우는 제외). 질문에 대한 Anony-Mousse의 의견은 코에 있습니다.
Nevoris

3
참조 : @Nevoris은, 그래 내가 코사인 거리를 제외하고 동의 여기 왜도에 대한 이유를-하지-K-수단 클러스터링 알고리즘 사용 전용 - 유클리드 - 거리 - 측정
데니스

43

불행히도 아니요 : k-means의 scikit-learn 현재 구현은 유클리드 거리 만 사용합니다.

k- 평균을 다른 거리로 확장하는 것은 쉬운 일이 아니며 위의 부인의 대답은 다른 메트릭에 대한 k- 평균을 구현하는 올바른 방법이 아닙니다.


26

대신이 작업을 수행 할 수있는 위치에 nltk를 사용하십시오.

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
이 구현은 얼마나 효율적입니까? 5k 포인트 (100 차원)로 클러스터링하는 데 영원히 걸리는 것 같습니다.
Nikana Reklawyks

3
차원 100에서 클러스터링 1k 포인트는 실행 당 1 초 ( repeats)가 걸리고 1.5k 포인트는 2 분이 걸리며 2k는 너무 오래 걸립니다.
Nikana Reklawyks

2
과연; 아래 @ Anony-Mousse 의견에 따라 코사인 거리에 수렴 문제가있을 수 있습니다. 나에게 이것은 실제로 쓰레기가 쓰레기 인 경우입니다. 원하는 거리 함수를 사용할 수 있지만 그 함수가 알고리즘의 가정을 위반하면 의미있는 결과를 기대하지 마십시오!
Chiraz BenAbdelkader

15

예, 차이 메트릭 기능을 사용할 수 있습니다. 그러나 정의상 k- 평균 군집화 알고리즘은 각 군집의 평균으로부터의 유클리드 거리에 의존합니다.

다른 메트릭을 사용할 수 있으므로 평균을 계산하는 경우에도 mahalnobis 거리와 같은 것을 사용할 수 있습니다.


25
+1 : 평균을 취하는 것이 유클리드 거리와 같은 특정 거리 함수에만 적합 하다는 점을 강조하겠습니다 . 다른 거리 함수의 경우 군집 중심 추정 함수도 교체해야합니다!
종료-익명-무스

2
@ 익명-무스. 예를 들어 코사인 거리를 사용할 때 무엇을 변경해야합니까?
궁금한

6
모르겠어요 나는 코사인과의 수렴 증거를 보지 못했다. 데이터가 음이 아니고 단위 영역으로 정규화되면 수렴 할 것이라고 생각합니다. 왜냐하면 데이터는 본질적으로 다른 벡터 공간에서 k- 평균이기 때문입니다.
종료-익명-무스

1
@ Anony-Mousse에 동의합니다. 나에게 이것은 쓰레기 인 쓰레기의 경우 일뿐입니다. 원하는 거리 함수를 사용하여 K- 평균을 실행할 수 있지만 그 함수가 알고리즘의 기본 가정을 위반하면 의미있는 결과를 기대하지 마십시오. 결과!
Chiraz BenAbdelkader

@ Anony-Mousse 그러나 mahalnobis 거리를 사용하여 K- 평균을 구현하는 방법은 무엇입니까?
Cecilia

7

pyclustering 파이썬 / C ++ (그 빠른 그래서!)하고 사용자 정의 측정 기능을 지정할 수 있습니다

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()

실제로, 나는이 코드를 테스트하지 않았지만 티켓예제 코드 에서 함께 묶었습니다 .


Matplotlib가 설치되어 있어야합니다. "Mac OS X에서 Python을 프레임 워크로 사용":(
CpILL


3

Sklearn Kmeans유클리드 거리를 사용합니다 . 메트릭 매개 변수가 없습니다. 이것은 당신이 클러스터링하는 경우 말했다 시계열을 , 당신이 사용할 수있는 tslearn지정할 수있는 경우, 메트릭 (파이썬 패키지를 dtw, softdtw, euclidean)를.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.