क्या scikit-K-Means Clustering का उपयोग करके अपनी खुद की दूरी फ़ंक्शन निर्दिष्ट करना संभव है?


172

क्या scikit-K-Means Clustering का उपयोग करके अपनी खुद की दूरी फ़ंक्शन निर्दिष्ट करना संभव है?


37
ध्यान दें कि के-साधनों को यूक्लिडियन दूरी के लिए डिज़ाइन किया गया है । यह अन्य दूरियों के साथ परिवर्तित करना बंद कर सकता है, जब माध्य अब "केंद्र" क्लस्टर के लिए सबसे अच्छा अनुमान नहीं है।
QUIT -

2
क्यों ई-साधन केवल यूक्लिडियन गड़बड़ी के साथ काम करता है?
जिज्ञासु

9
@ Anony-Mousse यह कहना गलत है कि k- साधन केवल यूक्लिडियन दूरी के लिए डिज़ाइन किया गया है। यह अवलोकन स्थान पर परिभाषित किसी भी मान्य दूरी मीट्रिक के साथ काम करने के लिए संशोधित किया जा सकता है। उदाहरण के लिए, k-medoids पर लेख देखें
ely

5
@ गंभीर: माध्य चुकता अंतर (= चुकता यूक्लिडियन दूरी) को कम करता है। यदि आप एक अलग दूरी फ़ंक्शन चाहते हैं, तो आपको माध्य को एक उपयुक्त केंद्र अनुमान के साथ बदलने की आवश्यकता है । के-मेडॉइड्स एक ऐसा एल्गोरिथ्म है, लेकिन मेडॉयड ढूंढना बहुत अधिक महंगा है।
QUIT - Anony-Mousse

4
यहां कुछ हद तक प्रासंगिक है: कर्नेल के-मीन्स को लागू करने के लिए वर्तमान में एक खुला पुल अनुरोध है । जब यह पूरा हो जाएगा तो आप गणना के लिए अपना कर्नेल निर्दिष्ट कर सकेंगे।
जेकवदप

जवाबों:


77

यहाँ एक छोटा किमी है जो scipy.spatial.distance या उपयोगकर्ता फ़ंक्शन में 20-विषम दूरी का उपयोग करता है।
टिप्पणियों का स्वागत होगा (यह अब तक केवल एक उपयोगकर्ता है, पर्याप्त नहीं है); विशेष रूप से, आपके एन, मंद, के, मीट्रिक क्या हैं

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

कुछ नोटों में 26mar 2012 जोड़ा गया:

1) कोसाइन दूरी के लिए, पहले सभी डेटा वैक्टर को सामान्य करें। X | = 1; फिर

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

तेज़ है। बिट वैक्टर के लिए, फ्लोट्स का विस्तार करने के बजाय वैक्टर से मानदंडों को अलग रखें (हालांकि कुछ प्रोग्राम आपके लिए विस्तारित हो सकते हैं)। विरल वैक्टर के लिए, 1% N, X कहें। Y को समय O (2% N), स्थान O (N) लेना चाहिए; लेकिन मुझे नहीं पता कि कौन से कार्यक्रम ऐसा करते हैं।

2) स्किकिट-लर्न क्लस्टरिंग k- साधनों, मिनी-बैच-के-साधनों का एक उत्कृष्ट अवलोकन देता है ... कोड के साथ जो scipy.sparse matrices पर काम करता है।

3) हमेशा k- साधन के बाद क्लस्टर आकार की जाँच करें। यदि आप मोटे तौर पर समान आकार वाले समूहों की अपेक्षा कर रहे हैं, लेकिन वे बाहर निकलते हैं [44 37 9 5 5] %... (सिर खुजाने की आवाज)।


1
+1 सबसे पहले, अपने कार्यान्वयन को साझा करने के लिए धन्यवाद। मैं सिर्फ इस बात की पुष्टि करना चाहता था कि एल्गोरिथ्म एक 700 आयामी स्थान में 900 वैक्टर के मेरे डेटासेट के लिए बहुत अच्छा काम करता है। मैं बस सोच रहा था कि क्या उत्पन्न क्लस्टर की गुणवत्ता का मूल्यांकन करना भी संभव है। क्या आपके कोड के किसी भी मान का उपयोग क्लस्टर गुणवत्ता की गणना करने के लिए किया जा सकता है ताकि इष्टतम समूहों की संख्या का चयन किया जा सके?
लेजेंड

6
किंवदंती, आपका स्वागत है। (क्लस्टर 50% / 90% त्रिज्या प्रिंट करने के लिए कोड अपडेट किया गया)। "क्लस्टर गुणवत्ता" एक बड़ा विषय है: आपके पास कितने क्लस्टर हैं, क्या आपके पास विशेषज्ञों के साथ ज्ञात समूहों के साथ प्रशिक्षण नमूने हैं? समूहों की संख्या पर, एसओ कैसे करते हैं-मैं-निर्धारित-के-जब-का-उपयोग-का-मतलब-क्लस्टरिंग -वन-का-उपयोग-के-क्लस्टरिंग
डेनिस

1
एक बार फिर आपका धन्यवाद। दरअसल, मेरे पास प्रशिक्षण के नमूने नहीं हैं, लेकिन मैं वर्गीकरण के बाद मैन्युअल रूप से क्लस्टर को सत्यापित करने की कोशिश कर रहा हूं (साथ ही साथ डोमेन विशेषज्ञ की भूमिका निभाने की कोशिश कर रहा हूं)। मैं कुछ मूल दस्तावेजों को एसवीडी लागू करने और उनके आयाम को कम करने के बाद एक दस्तावेज़-स्तरीय वर्गीकरण का प्रदर्शन कर रहा हूं। परिणाम अच्छे लगते हैं लेकिन मुझे यकीन नहीं था कि उन्हें कैसे मान्य किया जाए। प्रारंभिक चरण के लिए, विभिन्न क्लस्टर वैधता मेट्रिक्स की खोज करते हुए, मैं दून के सूचकांक में आया, एल्बो विधि आदि वास्तव में निश्चित नहीं थी कि कोई भी इस विचार का उपयोग करने के लिए कि मैं एल्बो पद्धति से शुरू करूंगा।
लीजेंड

7
मुझे पता है कि यह अन-ईथिंगिंग है, जो वास्तव में बहुत पुरानी है, लेकिन मैंने सिर्फ किमी का उपयोग करना शुरू किया और इस पर ठोकर खाई। भविष्य के पाठकों के लिए इस कोड का उपयोग करने का प्रलोभन: पहले से ऊपर के प्रश्न पर @ Anony-Mousse टिप्पणियों की जांच करें! यह कार्यान्वयन, जहां तक ​​मैं देख सकता हूं, यह गलत धारणा बना रहा है कि आप किसी भी तरह "उस क्लस्टर में बिंदुओं के माध्यम" का उपयोग कर सकते हैं। यह यूक्लिडियन दूरी (इकाई क्षेत्र पर बहुत विशिष्ट मामलों को छोड़कर, आदि ...) के अलावा और कुछ के लिए कोई मतलब नहीं है। सवाल पर फिर से एनी-मूस की टिप्पणी नाक पर सही है।
नेवरिस

3
@ नोवोरिस, हाँ मैं सहमत हूँ, कोसाइन डिस्टेंस के अलावा: यहाँ देखें कि क्यों, किस-किस-का-मतलब-क्लस्टरिंग-अल्गोरिदम-यूज़-ओनली-यूक्लिडियन-दूरी-मीट्रिक
डेनिस

43

दुर्भाग्य से नहीं: sc -it-k के वर्तमान कार्यान्वयन का मतलब केवल यूक्लिडियन दूरियों का उपयोग करता है।

अन्य साधनों के लिए 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 में) के रूप में कम लगता है।
निकाना रेक्लाविक्स

3
आयाम 100 में, 1k अंक के क्लस्टरिंग में प्रति सेकंड 1 सेकंड लगता है ( repeats), 1.5k अंक 2 मिनट लगते हैं, और 2k लेता है ... बहुत लंबा।
निकाना रेक्लाविक्स

2
वास्तव में; नीचे दिए गए @ Anony-Mousse टिप्पणी के अनुसार, ऐसा लगता है कि कॉशन दूरी में अभिसरण मुद्दे हो सकते हैं। मेरे लिए, यह वास्तव में कचरा-में-कचरा-बाहर निकलने का मामला है: आप जो भी दूरी फ़ंक्शन चाहते हैं उसका उपयोग कर सकते हैं, लेकिन अगर यह फ़ंक्शन एल्गोरिथम की मान्यताओं का उल्लंघन करता है, तो यह सार्थक परिणाम उत्पन्न करने की अपेक्षा न करें!
चिराज़ बेनअबेल्डकेर

15

हां आप एक अंतर मीट्रिक फ़ंक्शन का उपयोग कर सकते हैं; हालाँकि, परिभाषा के अनुसार, k- साधन क्लस्टरिंग एल्गोरिथ्म प्रत्येक क्लस्टर के माध्यम से नीलगिरी की दूरी पर निर्भर करता है।

आप एक अलग मीट्रिक का उपयोग कर सकते हैं, इसलिए भले ही आप अभी भी गणना कर रहे हों कि आप महालनोबिस दूरी जैसी किसी चीज़ का उपयोग कर सकते हैं।


25
+1: मुझे जोर देना इस चलो लेने का मतलब केवल इस तरह के इयूक्लिडियन दूरी के रूप में निश्चित दूरी काम करता है, के लिए उपयुक्त है । अन्य दूरी के कार्यों के लिए, आपको क्लस्टर-सेंटर अनुमान फ़ंक्शन को भी बदलना होगा!
QUIT - Anony-Mousse

2
@ Anony-मूस। उदाहरण के लिए कोसाइन दूरी का उपयोग करने पर मुझे क्या बदलना चाहिए?
जिज्ञासु

6
मुझे नहीं पता। मैंने कोसाइन के साथ अभिसरण के लिए प्रमाण नहीं देखा। मेरा मानना ​​है कि यदि आपका डेटा गैर-ऋणात्मक है और इकाई क्षेत्र के लिए सामान्यीकृत है, तो यह अभिसरण करेगा, क्योंकि तब यह अनिवार्य रूप से एक अलग वेक्टर स्थान में k- साधन है।
है क्विट -

1
मैं @ Anony-Mousse से सहमत हूं। मेरे लिए, यह केवल कचरा-में-कचरा-बाहर निकलने का मामला है: आप जो भी दूरी फ़ंक्शन चाहते हैं, उसके साथ K- साधन चला सकते हैं, लेकिन अगर वह फ़ंक्शन एल्गोरिथम की अंतर्निहित मान्यताओं का उल्लंघन करता है, तो यह सार्थक उत्पादन करने की अपेक्षा न करें। परिणाम!
चिराज़ बेनअबेल्डाकर

@ Anony-Mousse लेकिन महालनोबिस दूरी का उपयोग करके K- साधनों को कैसे लागू किया जाए?
सेसिलिया

7

वहाँ चक्रवात है जो अजगर / सी ++ (इतना तेज़ है!) और आपको एक कस्टम मीट्रिक फ़ंक्शन निर्दिष्ट करने देता है

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 को मैक मैक एक्स पर एक फ्रेमवर्क के रूप में "पायथन" की जरूरत है: :(
CpILL


3

Sklearn Kmeans यूक्लिडियन दूरी का उपयोग करता है । इसका कोई मीट्रिक पैरामीटर नहीं है। यह कहा, तुम क्लस्टरिंग रहे हैं समय श्रृंखला है, तो आप उपयोग कर सकते हैं tslearnअजगर पैकेज मीट्रिक (, जब आप निर्दिष्ट कर सकते हैं dtw, softdtw, euclidean)।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.