le risposte che ho visto finora si focalizzano solo sulla versione 2d (quando è necessario raggruppare i punti in 2 dimensioni). Ecco la mia implementazione del clustering in dimensioni arbitrarie.
idea di base del k-means algorithm n affievolisce:
- generare casuale k punti di partenza
- fare questo finché si supera la pazienza o la cessione di cluster non cambia:
- assegnare ogni punto al punto di partenza più vicino
- ricalcolare la posizione di ciascun punto di partenza tenendo egli media tra grappolo è
Per essere in grado di convalidare i risultati in qualche modo cercherò di raggruppare le immagini MNIST.
import numpy as np
import tensorflow as tf
from random import randint
from collections import Counter
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/")
X, y, k = mnist.test.images, mnist.test.labels, 10
Così qui X è i miei dati a raggrupparsi (10000, 784)
, y è il numero reale, e k è il numero di cluster (che è lo stesso del numero di cifre. Ora l'attuale algoritmo:.
# select random points as a starting position. You can do better by randomly selecting k points.
start_pos = tf.Variable(X[np.random.randint(X.shape[0], size=k),:], dtype=tf.float32)
centroids = tf.Variable(start_pos.initialized_value(), 'S', dtype=tf.float32)
# populate points
points = tf.Variable(X, 'X', dtype=tf.float32)
ones_like = tf.ones((points.get_shape()[0], 1))
prev_assignments = tf.Variable(tf.zeros((points.get_shape()[0],), dtype=tf.int64))
# find the distance between all points: http://stackoverflow.com/a/43839605/1090562
p1 = tf.matmul(
tf.expand_dims(tf.reduce_sum(tf.square(points), 1), 1),
tf.ones(shape=(1, k))
)
p2 = tf.transpose(tf.matmul(
tf.reshape(tf.reduce_sum(tf.square(centroids), 1), shape=[-1, 1]),
ones_like,
transpose_b=True
))
distance = tf.sqrt(tf.add(p1, p2) - 2 * tf.matmul(points, centroids, transpose_b=True))
# assign each point to a closest centroid
point_to_centroid_assignment = tf.argmin(distance, axis=1)
# recalculate the centers
total = tf.unsorted_segment_sum(points, point_to_centroid_assignment, k)
count = tf.unsorted_segment_sum(ones_like, point_to_centroid_assignment, k)
means = total/count
# continue if there is any difference between the current and previous assignment
is_continue = tf.reduce_any(tf.not_equal(point_to_centroid_assignment, prev_assignments))
with tf.control_dependencies([is_continue]):
loop = tf.group(centroids.assign(means), prev_assignments.assign(point_to_centroid_assignment))
sess = tf.Session()
sess.run(tf.global_variables_initializer())
# do many iterations. Hopefully you will stop because of has_changed is False
has_changed, cnt = True, 0
while has_changed and cnt < 300:
cnt += 1
has_changed, _ = sess.run([is_continue, loop])
# see how the data is assigned
res = sess.run(point_to_centroid_assignment)
Ora è il momento, controllare quanto bene sono i nostri cluster Per fare questo avremo gruppo tutti i numeri reali che apparivano nel cluster insieme dopo che vedremo le scelte più popolari in quel cluster.. In un caso di t lui perfetto clustering avremo il solo valore in ogni gruppo. In caso di cluster casuale ogni valore sarà approssimativamente ugualmente rappresentato nel gruppo.
nums_in_clusters = [[] for i in xrange(10)]
for cluster, real_num in zip(list(res), list(y)):
nums_in_clusters[cluster].append(real_num)
for i in xrange(10):
print Counter(nums_in_clusters[i]).most_common(3)
Questo mi dà qualcosa di simile:
[(0, 738), (6, 18), (2, 11)]
[(1, 641), (3, 53), (2, 51)]
[(1, 488), (2, 115), (7, 56)]
[(4, 550), (9, 533), (7, 280)]
[(7, 634), (9, 400), (4, 302)]
[(6, 649), (4, 27), (0, 14)]
[(5, 269), (6, 244), (0, 161)]
[(8, 646), (5, 164), (3, 125)]
[(2, 698), (3, 34), (7, 14)]
[(3, 712), (5, 290), (8, 110)]
Questo è abbastanza buono perché maggior parte dei conti è nel primo gruppo. Vedete che il clustering confonde 7 e 9, 4 e 5. Ma 0 è in cluster piuttosto bene.
alcuni approcci come migliorare questo:
- eseguire l'algoritmo un paio di volte e selezionare il migliore (sulla base della distanza a cluster)
- casi di manipolazione quando nulla viene assegnato ad un grappolo. Nel mio caso riceverai Nan nella variabile
means
perché count
è 0.
- inizializzazione di punti casuali.
probabilmente dovrei spiegare un po 'meglio. Prende i punti N e ne fa delle copie K. Prende i centroidi correnti di K e ne crea N copie. Quindi sottrae questi due grandi tensori per ottenere le distanze N * K da ciascun punto a ciascun centroide. Calcola la somma delle distanze al quadrato da quelle e usa 'argmin' per trovare il migliore per ogni punto. Quindi usa dynamic_partition per raggruppare i punti in K diversi tensori in base alla loro assegnazione di cluster, trova i mezzi all'interno di ciascuno di questi cluster e imposta i centroidi in base a tale. – dga