La plupart des Les réponses que j'ai vues jusqu'ici se concentrent uniquement sur la version 2d (lorsque vous devez regrouper des points en 2 dimensions). Voici ma mise en œuvre de la mise en cluster dans des dimensions arbitraires.
idée de base de k-means algorithm n obscurcit:
- générer des points de départ k aléatoire
- Pour ce faire, jusqu'à ce que vous dépassiez la patience ou l'affectation de cluster ne change pas:
- assign chaque point au point de départ le plus proche
- recalculer l'emplacement de chaque point de départ en prenant t il moyenne entre son groupe
Pour pouvoir valider en quelque sorte les résultats que je vais tenter de regrouper les images 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
Voici X mes données au cluster (10000, 784)
, y est le nombre réel et k est le nombre de grappes (qui est le même que le nombre de chiffres. Maintenant, la réelle algorithme:.
# 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)
maintenant, il est de vérifier de temps à quel point sont nos grappes Pour ce faire, nous regrouperons tous les nombres réels qui sont apparus dans le cluster ensemble après que nous verrons des choix les plus populaires dans ce cluster.. Dans un cas de t Le regroupement parfait nous aura la seule valeur dans chaque groupe. Dans le cas d'un cluster aléatoire, chaque valeur sera approximativement représentée de manière égale dans le groupe.
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)
Cela me donne quelque chose comme ceci:
[(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)]
C'est assez bonne parce que la majorité des chefs d'accusation est dans le premier groupe. Vous voyez que le regroupement confond 7 et 9, 4 et 5. Mais 0 est regroupé plutôt bien.
Quelques approches comment améliorer cela:
- exécuter l'algorithme plusieurs fois et sélectionnez le meilleur (en fonction de la distance aux clusters)
- des cas de manipulation quand rien est affecté à un grappe. Dans mon cas, vous obtiendrez Nan dans la variable
means
car count
est 0.
- initialisation de points aléatoires.
Je devrais probablement expliquer un peu mieux. Il prend les N points et en fait des copies. Il prend les K centroïdes actuels et en fait N copies. Il soustrait ensuite ces deux grands tenseurs pour obtenir les distances N * K de chaque point à chaque centroïde. Il calcule la somme des distances au carré et utilise 'argmin' pour trouver le meilleur pour chaque point. Ensuite, il utilise dynamic_partition pour regrouper les points dans K tenseurs différents en fonction de leur affectation de cluster, trouve les moyennes dans chacun de ces groupes, et définit les centroïdes sur cette base. – dga