web-dev-qa-db-fra.com

Comment pourrait-on implémenter l'algorithme K-Means ++?

J'ai du mal à bien comprendre le algorithme K-Means ++ . Je suis intéressé exactement comment les premiers centroïdes k sont choisis, à savoir l'initialisation car le reste est comme dans l'original algorithme K-Means .

  1. La fonction de probabilité utilisée est-elle basée sur la distance ou la gaussienne?
  2. Dans le même temps, le point le plus éloigné (des autres centroïdes) est choisi pour un nouveau centroïde.

J'apprécierai une explication étape par étape et un exemple. Celui de Wikipedia n'est pas assez clair. Un code source très bien commenté serait également utile. Si vous utilisez 6 tableaux, veuillez nous indiquer lequel est pour quoi.

38
Anton Andreev

Question interessante. Merci d'avoir porté ce document à mon attention - K-Means ++: The Advantages of Careful Seeding

En termes simples, les centres de grappes sont initialement choisis au hasard dans l'ensemble des vecteurs d'observation d'entrée, où la probabilité de choisir le vecteur x est élevée si x n'est proche d'aucun centre précédemment choisi.

Voici un exemple unidimensionnel. Nos observations sont [0, 1, 2, 3, 4]. Soit le premier centre, c1, soit 0. La probabilité que le prochain centre de cluster, c2, est x est proportionnel à ||c1-x||^2. Donc, P (c2 = 1) = 1a, P (c2 = 2) = 4a, P (c2 = 3) = 9a, P (c2 = 4) = 16a, où a = 1/(1 + 4 + 9 + 16).

Supposons que c2 = 4. Alors, P (c3 = 1) = 1a, P (c3 = 2) = 4a, P (c3 = 3) = 1a, où a = 1/(1 + 4 + 1).

J'ai codé la procédure d'initialisation en Python; Je ne sais pas si cela vous aide.

def initialize(X, K):
    C = [X[0]]
    for k in range(1, K):
        D2 = scipy.array([min([scipy.inner(c-x,c-x) for c in C]) for x in X])
        probs = D2/D2.sum()
        cumprobs = probs.cumsum()
        r = scipy.Rand()
        for j,p in enumerate(cumprobs):
            if r < p:
                i = j
                break
        C.append(X[i])
    return C

EDIT avec clarification: La sortie de cumsum nous donne des limites pour partitionner l'intervalle [0,1]. Ces partitions ont une longueur égale à la probabilité que le point correspondant soit choisi comme centre. Alors, puisque r est uniformément choisi entre [0,1], il tombera exactement dans l'un de ces intervalles (à cause de break). La boucle for vérifie quelle partition r se trouve.

Exemple:

probs = [0.1, 0.2, 0.3, 0.4]
cumprobs = [0.1, 0.3, 0.6, 1.0]
if r < cumprobs[0]:
    # this event has probability 0.1
    i = 0
Elif r < cumprobs[1]:
    # this event has probability 0.2
    i = 1
Elif r < cumprobs[2]:
    # this event has probability 0.3
    i = 2
Elif r < cumprobs[3]:
    # this event has probability 0.4
    i = 3
64
Steve Tjoa

Bon mot.

Supposons que nous devons sélectionner 2 centres de cluster, au lieu de les sélectionner tous de façon aléatoire {comme nous le faisons en termes k simples}, nous sélectionnerons le premier au hasard, puis trouverons les points les plus éloignés du premier centre {Ces points le font probablement n'appartiennent pas au premier centre de cluster car ils en sont éloignés} et affectez le deuxième centre de cluster à proximité de ces points éloignés.

7
PankajKabra

J'ai préparé une implémentation source complète de k-means ++ basée sur le livre "Collective Intelligence" de Toby Segaran et l'initialisation de k-menas ++ fournie ici.

En effet, il existe ici deux fonctions de distance. Pour les centroïdes initiaux, un standard est utilisé basé sur numpy.inner, puis pour la fixation des centroïdes, le Pearson est utilisé. Peut-être que celui de Pearson peut également être utilisé pour les centroïdes initiaux. Ils disent que c'est mieux.

from __future__ import division

def readfile(filename):
  lines=[line for line in file(filename)]
  rownames=[]
  data=[]
  for line in lines:
    p=line.strip().split(' ') #single space as separator
    #print p
    # First column in each row is the rowname
    rownames.append(p[0])
    # The data for this row is the remainder of the row
    data.append([float(x) for x in p[1:]])
    #print [float(x) for x in p[1:]]
  return rownames,data

from math import sqrt

def pearson(v1,v2):
  # Simple sums
  sum1=sum(v1)
  sum2=sum(v2)

  # Sums of the squares
  sum1Sq=sum([pow(v,2) for v in v1])
  sum2Sq=sum([pow(v,2) for v in v2])    

  # Sum of the products
  pSum=sum([v1[i]*v2[i] for i in range(len(v1))])

  # Calculate r (Pearson score)
  num=pSum-(sum1*sum2/len(v1))
  den=sqrt((sum1Sq-pow(sum1,2)/len(v1))*(sum2Sq-pow(sum2,2)/len(v1)))
  if den==0: return 0

  return 1.0-num/den

import numpy
from numpy.random import *

def initialize(X, K):
    C = [X[0]]
    for _ in range(1, K):
        #D2 = numpy.array([min([numpy.inner(c-x,c-x) for c in C]) for x in X])
        D2 = numpy.array([min([numpy.inner(numpy.array(c)-numpy.array(x),numpy.array(c)-numpy.array(x)) for c in C]) for x in X])
        probs = D2/D2.sum()
        cumprobs = probs.cumsum()
        #print "cumprobs=",cumprobs
        r = Rand()
        #print "r=",r
        i=-1
        for j,p in enumerate(cumprobs):
            if r 0:
        for rowid in bestmatches[i]:
          for m in range(len(rows[rowid])):
            avgs[m]+=rows[rowid][m]
        for j in range(len(avgs)):
          avgs[j]/=len(bestmatches[i])
        clusters[i]=avgs

  return bestmatches

rows,data=readfile('/home/toncho/Desktop/data.txt')

kclust = kcluster(data,k=4)

print "Result:"
for c in kclust:
    out = ""
    for r in c:
        out+=rows[r] +' '
    print "["+out[:-1]+"]"

print 'done'

data.txt:


p1 1 5 6
p2 9 4 3
p3 2 3 1
p4 4 5 6
p5 7 8 9
p6 4 5 4
p7 2 5 6
p8 3 4 5
p9 6 7 8
3
Anton Andreev