web-dev-qa-db-ja.com

TensorFlowでk-meansを実装するにはどうすればよいですか?

組み込みの勾配降下オプティマイザを使用する入門チュートリアルは、非常に理にかなっています。ただし、k平均法は、勾配降下法に組み込むことができるものだけではありません。独自のオプティマイザを作成する必要があるようですが、TensorFlowプリミティブが与えられた場合、どのように行うのかよくわかりません。

どのようなアプローチを取るべきですか?

18

(注: githubのGistとしてこのコードのより洗練されたバージョン を取得できるようになりました)

あなたは間違いなくそれを行うことができますが、独自の最適化基準を定義する必要があります(k平均の場合、これは通常、最大反復回数であり、割り当てが安定したときです)。これを行う方法の例を次に示します(おそらく、それを実装するためのより最適な方法があり、最初のポイントを選択するためのより優れた方法があります)。あなたがPythonで物事を繰り返し行うことから遠ざかろうと本当に努力しているなら、それは基本的にあなたがnumpyでそれをするだろうようなものです:

import tensorflow as tf
import numpy as np
import time

N=10000
K=4
MAX_ITERS = 1000

start = time.time()

points = tf.Variable(tf.random_uniform([N,2]))
cluster_assignments = tf.Variable(tf.zeros([N], dtype=tf.int64))

# Silly initialization:  Use the first two points as the starting                
# centroids.  In the real world, do this better.                                 
centroids = tf.Variable(tf.slice(points.initialized_value(), [0,0], [K,2]))

# Replicate to N copies of each centroid and K copies of each                    
# point, then subtract and compute the sum of squared distances.                 
rep_centroids = tf.reshape(tf.tile(centroids, [N, 1]), [N, K, 2])
rep_points = tf.reshape(tf.tile(points, [1, K]), [N, K, 2])
sum_squares = tf.reduce_sum(tf.square(rep_points - rep_centroids),
                            reduction_indices=2)

# Use argmin to select the lowest-distance point                                 
best_centroids = tf.argmin(sum_squares, 1)
did_assignments_change = tf.reduce_any(tf.not_equal(best_centroids,
                                                    cluster_assignments))

def bucket_mean(data, bucket_ids, num_buckets):
    total = tf.unsorted_segment_sum(data, bucket_ids, num_buckets)
    count = tf.unsorted_segment_sum(tf.ones_like(data), bucket_ids, num_buckets)
    return total / count

means = bucket_mean(points, best_centroids, K)

# Do not write to the assigned clusters variable until after                     
# computing whether the assignments have changed - hence with_dependencies
with tf.control_dependencies([did_assignments_change]):
    do_updates = tf.group(
        centroids.assign(means),
        cluster_assignments.assign(best_centroids))

sess = tf.Session()
sess.run(tf.initialize_all_variables())

changed = True
iters = 0

while changed and iters < MAX_ITERS:
    iters += 1
    [changed, _] = sess.run([did_assignments_change, do_updates])

[centers, assignments] = sess.run([centroids, cluster_assignments])
end = time.time()
print ("Found in %.2f seconds" % (end-start)), iters, "iterations"
print "Centroids:"
print centers
print "Cluster assignments:", assignments

(実際の実装では、最初のクラスター選択についてより注意深くする必要があることに注意してください。すべてのポイントが1つのクラスターに移動するなどの問題を回避します。これは簡単なデモです。以前の回答を少し更新してより明確で「例に値する」。)

27
dga

これまでに見てきた答えのほとんどは、2Dバージョン(2次元でポイントをクラスター化する必要がある場合)にのみ焦点を当てています。これが、任意の次元でのクラスタリングの私の実装です。


k-平均アルゴリズム の基本的な考え方をn次元で:

  • ランダムなk開始点を生成する
  • 忍耐力を超えるか、クラスタの割り当てが変更されなくなるまで、これを行います。
    • 各点を最も近い開始点に割り当てます
    • クラスター間の平均を取ることにより、各開始点の位置を再計算します

結果を何らかの形で検証できるようにするために、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

したがって、ここ[〜#〜] x [〜#〜]はクラスタへのデータ(10000, 784)yは実数、kは数値クラスタの(これは桁数と同じです。今度は実際のアルゴリズム:

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

さて、クラスタの状態をチェックする時が来ました。これを行うには、クラスターに出現するすべての実数をグループ化します。その後、そのクラスターで最も人気のある選択肢が表示されます。完全なクラスタリングの場合、各グループに1つの値しかありません。ランダムなクラスターの場合、各値はグループ内でほぼ等しく表示されます。

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)

これは私にこのようなものを与えます:

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

カウントの大部分が最初のグループにあるため、これはかなり良いです。クラスタリングが7と9、4と5を混同しているのがわかります。

これを改善する方法のいくつかのアプローチ:

  • アルゴリズムを数回実行し、最適なものを選択します(クラスターまでの距離に基づく)
  • クラスターに何も割り当てられていない場合の処理​​。私の場合、meansが0であるため、count変数でNanを取得します。
  • ランダムポイントの初期化。
4
Salvador Dali

現在では、 KMeansClustering Estimator を直接使用する(またはヒントを得る)ことができます。 GitHubでの実装 をご覧ください。

1
vreyespue