利用-TensorFlow-實現排序和搜尋演算法
作者:chen_h
微訊號 & QQ:862251340
微信公眾號:coderpai
TensorFlow 所構成的計算圖是圖靈完備的。
當我們提到 TensorFlow 的時候,我們僅僅只會關注它是一個很好的神經網路和深度學習的庫。但是,它也滿足資料流程式設計(DFP)的各個方面。
由於 TensorFlow 具有 tf.cond 和 tf.while_loop 函式,前者可以處理判斷語句,後者可以處理迴圈語句,所以它就具有一般程式語言相同的表示式。簡單的說,我們可以用 C 語言或者 Python 語言實現的排序和搜尋演算法都可以在 TensorFlow 圖中實現。
在本文中,我將介紹的就是 TensorFlow 的另一面,它的一般程式語言表達方式。我利用 TensorFlow 圖實現了一些演算法,諸如 FizzBuzz,Bubble Sort,Quick Sort,Binary Search 等等。
在 TensorFlow 中實現演算法
Fizz Buzz 問題
請依次列印從1至100的整數,在該數能被3整除的時候,列印”Fizz”,能被5整除的時候列印”Buzz”,如果既能被3又能被5整除的時候,列印”FizzBuzz”。
import tensorflow as tf
class FizzBuzz():
def __init__ (self, length=30):
self.length = length # 程式需要執行的序列長度
self.array = tf.Variable([str(i) for i in range(1, length+1)], dtype=tf.string, trainable=False) # 最後程式返回的結果
self.graph = tf.while_loop(self.cond, self.body, [1, self.array],) # 對每一個值進行迴圈判斷
def run(self):
with tf.Session() as sess:
tf.global_variables_initializer().run()
return sess.run(self.graph)
def cond(self, i, _):
return (tf.less(i, self.length+1)) # 判斷是否是最後一個值
def body(self, i, _):
flow = tf.cond(
tf.equal(tf.mod(i, 15), 0), # 如果值能被 15 整除,那麼就把該位置賦值為 FizzBuzz
lambda: tf.assign(self.array[i - 1], 'FizzBuzz'),
lambda: tf.cond(tf.equal(tf.mod(i, 3), 0), # 如果值能被 3 整除,那麼就把該位置賦值為 Fizz
lambda: tf.assign(self.array[i - 1], 'Fizz'),
lambda: tf.cond(tf.equal(tf.mod(i, 5), 0), # 如果值能被 5 整除,那麼就把該位置賦值為 Buzz
lambda: tf.assign(self.array[i - 1], 'Buzz'),
lambda: self.array # 最後返回的結果
)
)
)
return (tf.add(i, 1), flow)
if __name__ == '__main__':
fizzbuzz = FizzBuzz(length=50)
ix, array = fizzbuzz.run()
print(array)
輸出結果:
['1' '2' 'Fizz' '4' 'Buzz' 'Fizz' '7' '8' 'Fizz' 'Buzz' '11' 'Fizz' '13'
'14' 'FizzBuzz' '16' '17' 'Fizz' '19' 'Buzz' 'Fizz' '22' '23' 'Fizz'
'Buzz' '26' 'Fizz' '28' '29' 'FizzBuzz' '31' '32' 'Fizz' '34' 'Buzz'
'Fizz' '37' '38' 'Fizz' 'Buzz' '41' 'Fizz' '43' '44' 'FizzBuzz' '46' '47'
'Fizz' '49' 'Buzz']
Linear Search
給定一個序列和一個目標值,從這個序列中找到這個目標值的位置。
import numpy as np
import tensorflow as tf
class LinearSearch():
def __init__(self, array, x):
self.x = tf.constant(x)
self.array = tf.constant(array)
self.length = len(array)
self.graph = tf.while_loop(self.cond, self.body, [0, self.x, False])
def run(self):
with tf.Session() as sess:
tf.global_variables_initializer().run()
return sess.run(self.graph)
def cond(self, i, _, is_found):
return tf.logical_and(tf.less(i, self.length), tf.logical_not(is_found))
def body(self, i, _, is_found):
return tf.cond(tf.equal(self.array[i], self.x),
lambda: (i, self.array[i], True),
lambda: (tf.add(i, 1), -1, False))
if __name__ == '__main__':
array, x = [1, 22, 33, 1, 7, 3, 8], 3
search = LinearSearch(array, x)
ix, xx, is_found = search.run()
print('Array :', array)
print('Number to search :', x)
if is_found:
print('{} is at index {}.'.format(xx, ix))
else:
print('Not found.')
輸出結果:
Array : [1, 22, 33, 1, 7, 3, 8]
Number to search : 3
3 is at index 5.
Bubble Sort
import numpy as np
import tensorflow as tf
class BubbleSort():
def __init__(self, array):
self.i = tf.constant(0)
self.j = tf.constant(len(array)-1)
self.array = tf.Variable(array, trainable=False)
self.length = len(array)
cond = lambda i, j, _: tf.less(i-1, self.length-1)
self.graph = tf.while_loop(cond, self.outer_loop, loop_vars=[self.i, self.j, self.array])
def run(self):
with tf.Session() as sess:
tf.global_variables_initializer().run()
return sess.run(self.graph)
def outer_loop(self, i, j, _):
cond = lambda i, j, _: tf.greater(j, i)
loop = tf.while_loop(cond, self.inner_loop, loop_vars=[i, self.length-1, self.array])
return tf.add(i, 1), loop[1], loop[2]
def inner_loop(self, i, j, _):
body = tf.cond(tf.greater(self.array[j-1], self.array[j]),
lambda: tf.scatter_nd_update(self.array, [[j-1],[j]], [self.array[j],self.array[j-1]]),
lambda: self.array)
return i, tf.subtract(j, 1), body
if __name__ == '__main__':
x = np.array([1.,7.,3.,8.])
_, _, sorted_array = BubbleSort(x).run()
print(x)
print(sorted_array)
輸出結果:
[ 1. 7. 3. 8.]
[ 1. 3. 7. 8.]
還有更多的實現演算法,你可以檢視這個 Github。
API 解釋
類似判斷語句的 API:tf.cond()
cond(
pred,
true_fn=None,
false_fn=None,
strict=False,
name=None,
fn1=None,
fn2=None
)
tf.cond(...)
是一個等效於 if 語句的節點。根據其中的引數 pred 返回的布林值來判斷返回什麼值,比如當引數 pred 為 true 值時,節點返回引數 true_fn 的值,當引數 pred 為 false 時,節點返回引數 false_fn 的值。但是,其中的引數 true_fn
和引數 false_fn
都是需要是 lambda 或者函式。比如:
z = tf.multiply(a, b)
result = tf.cond(x < y, lambda: tf.add(x, z), lambda: tf.square(y))
當 x
x = tf.constant(2)
y = tf.constant(5)
def f1():
return tf.multiply(x, 17)
def f2():
return tf.add(y, 23)
r = tf.cond(tf.less(x, y), f1, f2)
with tf.Session() as sess:
print(sess.run(r))
請注意:API 中的某些引數被忽略了,因為它們將在以後的版本中被刪除。
類似判斷語句的 API: tf.while_loop()
while_loop(
cond, # Condition
body, # Process to be executed when cond is True
loop_vars, # Argument to body
shape_invariants=None,
parallel_iterations=10,
back_prop=True,
swap_memory=False,
name=None
)
tf.while_loop(...)
是一個等效於 while 語句的節點。根據其中的引數 cond 的布林值來判斷是否將迴圈繼續,比如當引數 pred 為 true 值時,節點去執行 body 中的語句,當引數 pred 為 false 時,那麼退出這個函式。比如:
i = tf.constant(0)
c = lambda i: tf.less(i, 10)
b = lambda i: tf.add(i, 1)
r = tf.while_loop(c, b, [i])
當 i < 10 時,cond 返回的值是 true,所以節點會去執行 body 中的語句。當 i == 10 時,cond 返回的值是 false,那麼節點就會退出。這種執行方式和一般語言中的 while 非常像。
我們也可以將迴圈式表達成如下:
while(condition(tensors))
{
tensors = body(tensors);
}
接下來,我們來看一個完整的例子,如下:
import tensorflow as tf
import numpy as np
def body(x):
a = tf.random_uniform(shape=[2, 2], dtype=tf.int32, maxval=100)
b = tf.constant(np.array([[1, 2], [3, 4]]), dtype=tf.int32)
c = a + b
return tf.nn.relu(x + c)
def condition(x):
return tf.reduce_sum(x) < 100
x = tf.Variable(tf.constant(0, shape=[2, 2]))
with tf.Session():
tf.initialize_all_variables().run()
result = tf.while_loop(condition, body, [x])
print(result.eval())