TensorFlow實戰之K-Means聚類算法實踐

jopen 9年前發布 | 158K 次閱讀 機器學習 TensorFlow

原文地址: 這里

Google 最近開源了它的第二代人工智能與數值計算庫TensorFlow。TensorFlow由Google大腦團隊開發,并且能夠靈活地運行在多個平臺上——包括GPU平臺與移動設備中。

TensorFlow的核心就是使用所謂的數據流,可以參考Wikipedia上的有關于 Genetic Programming 的相關知識,譬如:

TensorFlow實戰之K-Means聚類算法實踐

正如你理解的,整個以樹狀圖的架構來表示整個計算流。每個節點即代表一個操作,TensorFlow稱作OPS,即operations的縮寫。非葉子節 點還是很好理解的,一些葉子節點可以是特殊的操作類型,譬如返回一個常量值(譬如上述樹中的7或者2.2)。其他的一些葉子節點,譬如X或者Y這樣的,被當做placeholders,即會在運行中被動態地注入值。如果仔細觀察上圖中的箭頭的指向,可以發現這些箭頭指向就表明了不同節點之間輸出的依賴關系。因此,Data(在 TensorFlow中被稱為Tensors),會在不同的節點之間逆向流動,這就就是他們被稱為TensorFlow的原因。TensorFlow也提 供了其他的基于圖像抽象的組件,譬如持久化的數據存儲(被稱為Variables),以及在譬如神經網絡這樣的應用中對于Variables中的參數微調 而進行的優化手段。

TensorFlow提供了非常有好的Python的接口,在看本篇文章之前建議閱讀以下:

1. 基礎環境的搭建 或者筆者的翻譯

2.參閱 這個例子 來對TensorFlow的代碼風格有一個模糊的認識。

3.接下來 這個解釋 會闡述TensorFlow中的基礎的組件。

4.參考 詳細的例子 來看看TensorFlow是怎么解決常見的ML問題的。

5.在了解上述的基本知識后,可以閱讀 Python docs 這個接口文檔來作為開發中的參考。

接下來,我會以用TensorFlow來解決常見的K-Means問題作為例子來闡述如何使用它。

import tensorflow as tf
from random import choice, shuffle
from numpy import array
def TFKMeansCluster(vectors, noofclusters):
    """
    K-Means Clustering using TensorFlow.
    `vertors`應該是一個n*k的二維的NumPy的數組,其中n代表著K維向量的數目
    'noofclusters' 代表了待分的集群的數目,是一個整型值
    """
    noofclusters = int(noofclusters)
    assert noofclusters < len(vectors)
    #找出每個向量的維度
    dim = len(vectors[0])
    #輔助隨機地從可得的向量中選取中心點
    vector_indices = list(range(len(vectors)))
    shuffle(vector_indices)
    #計算圖
    #我們創建了一個默認的計算流的圖用于整個算法中,這樣就保證了當函數被多次調用      #時,默認的圖并不會被從上一次調用時留下的未使用的OPS或者Variables擠滿
    graph = tf.Graph()
    with graph.as_default():
  #計算的會話
  sess = tf.Session()
  ##構建基本的計算的元素
  ##首先我們需要保證每個中心點都會存在一個Variable矩陣
  ##從現有的點集合中抽取出一部分作為默認的中心點
  centroids = [tf.Variable((vectors[vector_indices[i]]))
         for i in range(noofclusters)]
  ##創建一個placeholder用于存放各個中心點可能的分類的情況
  centroid_value = tf.placeholder("float64", [dim])
  cent_assigns = []
  for centroid in centroids:
      cent_assigns.append(tf.assign(centroid, centroid_value))
  ##對于每個獨立向量的分屬的類別設置為默認值0
  assignments = [tf.Variable(0) for i in range(len(vectors))]
  ##這些節點在后續的操作中會被分配到合適的值
  assignment_value = tf.placeholder("int32")
  cluster_assigns = []
  for assignment in assignments:
      cluster_assigns.append(tf.assign(assignment,
               assignment_value))
  ##下面創建用于計算平均值的操作節點
  #輸入的placeholder
  mean_input = tf.placeholder("float", [None, dim])
  #節點/OP接受輸入,并且計算0維度的平均值,譬如輸入的向量列表
  mean_op = tf.reduce_mean(mean_input, 0)
  ##用于計算歐幾里得距離的節點
  v1 = tf.placeholder("float", [dim])
  v2 = tf.placeholder("float", [dim])
  euclid_dist = tf.sqrt(tf.reduce_sum(tf.pow(tf.sub(
      v1, v2), 2)))
  ##這個OP會決定應該將向量歸屬到哪個節點
  ##基于向量到中心點的歐幾里得距離
  #Placeholder for input
  centroid_distances = tf.placeholder("float", [noofclusters])
  cluster_assignment = tf.argmin(centroid_distances, 0)
  ##初始化所有的狀態值
   ##這會幫助初始化圖中定義的所有Variables。Variable-initializer應該定     ##義在所有的Variables被構造之后,這樣所有的Variables才會被納入初始化
  init_op = tf.initialize_all_variables()
  #初始化所有的變量
  sess.run(init_op)
  ##集群遍歷
  #接下來在K-Means聚類迭代中使用最大期望算法。為了簡單起見,只讓它執行固      #定的次數,而不設置一個終止條件
  noofiterations = 100
  for iteration_n in range(noofiterations):
      ##期望步驟
      ##基于上次迭代后算出的中心點的未知
      ##the _expected_ centroid assignments.
      #首先遍歷所有的向量
      for vector_n in range(len(vectors)):
    vect = vectors[vector_n]
    #計算給定向量與分配的中心節點之間的歐幾里得距離
    distances = [sess.run(euclid_dist, feed_dict={
        v1: vect, v2: sess.run(centroid)})
           for centroid in centroids]
    #下面可以使用集群分配操作,將上述的距離當做輸入
    assignment = sess.run(cluster_assignment, feed_dict = {
        centroid_distances: distances})
    #接下來為每個向量分配合適的值
    sess.run(cluster_assigns[vector_n], feed_dict={
        assignment_value: assignment})
      ##最大化的步驟
      #基于上述的期望步驟,計算每個新的中心點的距離從而使集群內的平方和最小
      for cluster_n in range(noofclusters):
    #收集所有分配給該集群的向量
    assigned_vects = [vectors[i] for i in range(len(vectors))
          if sess.run(assignments[i]) == cluster_n]
    #計算新的集群中心點
    new_location = sess.run(mean_op, feed_dict={
        mean_input: array(assigned_vects)})
    #為每個向量分配合適的中心點
    sess.run(cent_assigns[cluster_n], feed_dict={
        centroid_value: new_location})
  #返回中心節點和分組
  centroids = sess.run(centroids)
  assignments = sess.run(assignments)
  return centroids, assignments

需要注意的是,如果

for i in range(100):
    x = sess.run(tf.assign(variable1, placeholder))

像上面那樣看似無害地在每次執行的時候創建一個新的OP(譬如tf.assign或者tf.zeros這樣的),這樣會一定的影響性能。作為替代的,你應該為每個任務定義一個特定的OP,然后在循環中調用這個OP。可以使用len(graph.get_operations())這個方法來檢測是否有冗余的非必需的OPs。準確來說,sess.run應該是在迭代中唯一會與graph產生交互的方法。在上述代碼的138~139行中可以看出,一系列的ops/Variables可以組合在sess.run中使用。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!