آمار در github :
تا 30 سپتامبر سال 2018 تنسرفلو با 111k استار و 68k فورک در رده اول قرار داشته است و این نشان می دهد استقبال از این کتابخانه بسیار زیاد است. یکی از دلایل استفاده از آن، این است که تنسرفلو کتابخانه ای است که برای deployment بسیار مناسب است. همچنین export اندروید و ios در آن بسیار راحت است. تنسرفلو برای محصول عملی و pytorch برای تست و مقاله دادن به کار می رود.

آمار جست و جو در گوگل:

این نمودار نشان می دهد میزان سرچ تنسرفلو روز به روز در حال افزایش است. دو کتابخانه ای که در رده ی دوم و سوم قرار دارند کراس و pytorch است.
مبانی تنسرفلو:
تنسرفلو فریم ورک یادگیری ژرف گوگل است که در نوامبر سال 2015 به صورت متن باز منتشر شده است. تنسرفلو می تواند عملیات محاسباتی بهینه و مشتق گیری را حساب کند، همچنین گرادیان گراف را به صورت اتوماتیک محاسبه می کند.
تنسور:
یک آرایه ی n بعدی را به اصطلاح tensor می گویند.


گراف جریان داده:
تنسرفلو برای این که بتواند توزیع پذیری داشته باشد و performance را بالا ببرد مدل برنامه نویسی graph base را ارائه داده و محاسبات را از اجرا جدا کرده است.
مثال: با توجه به شکل زیر دو نود برای input ، یک نود برای mul و یک نود برای add داریم. خروجی نود mul و add می توانند به یک نود add دیگر بروند. حالا می توانیم به input ها عدد بدهیم و خروجی را ببینیم.

گام اول: تعریف یک گراف محاسباتی (در این گراف هیچ عددی وجود ندارد)
گام دوم: تعریف یک session که اجازه ی ورودی دادن، اجرای عملیات و گرفتن خروجی را می دهد. (وقتی session ساخته می شود به آن سخت افزار اختصاص داده می شود)
مثال:
|
import tensorflow as tf a = tf.add(2, 3) |
با نوشتن کد بالا یک گراف به شکل زیر ساخته می شود:

نکته: اگر به صورت صریح رئوس گراف محاسباتی را نامگذاری نکنید، تنسورفلو به صورت خودکار به آنها نام نسبت میدهد. برای مثال در این جا به نود ها نام های x و y داده شده است.
هر زمان که به آن session اختصاص داده شود به x عدد 2 و به y عدد 3 را می دهد و خروجی 5 تولید می شود.
برنامه ساده استفاده از numpy :

بازنویسی با tensorflow :
|
import tensorflow as tf tf.InteractiveSession() a = tf.zeros((2,2)); b = tf.ones((2,2)) tf.reduce_sum(b, reduction_indices=1).eval() |
|
out: [(([([(TensorShape([Dimension(2), Dimension(2 |
|
out: array([ 2., 2.], dtype=float32 |
|
(()tf.reshape(a, (1, 4)).eval |
|
Out: (array([[ 0., 0., 0., 0.]], dtype=float32 |
()Tf.InteractiveSession : برای ساخت session است. می توانیم به آن ورودی دهیم و مشخص کنیم از چه مقدار از gpu استفاده کند. در صورتی که ورودی به آن داده نشود از کل gpu موجود استفاده می کند.
InteractiveSession سشنی است که در محیط های jupyter notebook و محیط هایی که تعاملی است استفاده می شود ولی چون overhead آن زیاد است برای deployment استفاده نمی شود و در آنجا session معمولی کاربرد دارد.
به جای np.zeros و np.ones ، tf.zeros و tf.ones قرار گرفته ولی سینتکس آن تغییری نکرده است.
به جای sum از tf.reduce_sum استفاده شده است (تاکید می کند که dimention در حال کوچک شدن است) همچنین به جای axis از reduction_indices استفاده می شود.
Eval : معمولا وقتی interactivesession داریم از eval استفاده می شود. eval سشن فعالی که وجود دارد را پیدا کرده و آن را اجرا می کند. چون eval را صدا زدیم آرایه ی [2. , 2.] را برمی گرداند و مشخص می کند datatype آن float32 است.
()Get_shape : یک TensorShape برمی گرداند که مثل tupple ها در پایتون رفتار می کنند.
سپس reshape شده و eval دوباره صدا زده می شود تا گراف اجرا شود.
مقایسه استفاده از numpy و tensorflow :

تنسورفلو نیاز به ارزیابی صریح دارد. یعنی برای مثال اگر یک numpy از ماتریس صفر 2 در 2 تعریف شود بعد از پرینت کردن آن، 4 تا صفر را نمایش می دهد اما اگر این ماتریس با تنسورفلو تعریف شود در خروجی یک گراف محاسباتی ساخته می شود که تا زمانی ارزیابی نشود هیچ مقدار عددی ندارد.
|
((a = np.zeros((2,2 ((ta = tf.zeros((2,2 |
|
(print(a [[ 0. 0.] [ 0. 0.]] |
|
(print(ta (Tensor("zeros_1:0", shape=(2, 2), dtype=float32 |
با فرض این که یک session فعال ساخته شده باشد اگر eval صدا زده شود ta اجرا می شود.
|
<strong>(()print(ta.eval </strong>[[ 0. 0.] [ 0. 0.]] |
یک شیء session محیطی که در آن اشیاء تنسور ارزیابی می شوند را کپسولهسازی میکند. یعنی بعد از ساختن گراف یک session می سازیم که این session اجازه می دهد اشیا داخل آن اجرا و evaluate شوند.
مثال:
|
(a = tf.constant(5.0 (b = tf.constant(6.0 c = a * b :with tf.Session() as sess ....: print(sess.run(c)) ....: print(c.eval()) ....: 30.0 30.0 |
Tf.constant یک عدد constant تعریف می کند. در این مثال از with block برای ساخت session استفاده شده که اصطلاحا به آن contex manager گفته می شود. وقتی یک session ساخته می شود باید حتما آن را close کنیم اما وقتی از contex manager استفاده می شود بعد از خارج شدن از tab ، session خود به خود بسته می شود. برای اجرا شدن session از دستور sess.run استفاده شده که همان کار eval را انجام می دهد.
گراف که ساخته می شود چیزی مانند شکل زیر است:

بعد از این که session به آن اختصاص داده شد سخت افزار را در اختیار می گیرد و بعد از آن اصطلاحا عمل feed و fetch انجام می شود. جایی که input را می دهیم اصطلاحا به شبکه feed می شود و هر خروجی که بخواهیم را fetch می کنیم.
هر session منابعی را در اختیار می گیرد که gpu ، cpu و ram از مهم ترین آن هاست. این منابع پس از استفاده باید حتما آزاد شوند. همچنین می توانیم مشخص کنیم که هر session چه مقدار از این منابع را می تواند استفاده کند.

یک سینتکس دیگر این است که می توان از contex manager استفاده کرد. به این صورت که دستورات داخل indent نوشته می شود و هر وقت تمام شد خودش session را می بندد.

همچنین می توان از InteractiveSession استفاده کرد که فقط برای notebook کاربرد دارد.

مثال:
|
x = tf.constant(2) y = 3 op1 = tf.add(x, y) op2 = tf.mul(x, y) op3 = tf.pow(op2, op1) with tf.Session() as sess: op3 = sess.run(op3) |
هر عملیاتی که تعریف می کنیم یک نود در گراف ساخته می شود.
(x=tf.constant(2 و y=3 از لحاظ سینتکس هیچ فرقی باهم ندارند.

مثال:
|
x = 2 y = 3 op1 = tf.add(x, y) op2 = tf.mul(x, y) useless = tf.mul(x, op1) op3 = tf.pow(op2, op1) with tf.Session() as sess: op3 = sess.run(op3) |
چون خروجی خواسته شده op3 است که برابر با op2 به توان op1 است، نودی که به اسم useless نامگذاری شده در اجرا در نظر گرفته نمی شود و ارزیابی نمی شود بنابراین سربار اجرایی ندارد.

اگر به session.run یک لیست داده شود یک tupple برگردانده می شود.
|
x = 2 y = 3 op1 = tf.add(x, y) op2 = tf.mul(x, y) useless = tf.mul(x, op1) op3 = tf.pow(op2, op1) with tf.Session() as sess: op3, not_useless = sess.run([op3, useless]) |
بنابراین در این مثال نود useless هم نیاز به ارزیابی دارد.
Variable ها در تنسورفلو:
برای چیزی که معمولا قرار است train شود از variable ها استفاده می شود. هنگامی که مدل را آموزش می دهیم برای نگه داری پارامتر ها نیاز به تعریف متغیر داریم. یعنی معمولا trainable هستند.
در ساختن شبکه عصبی و شبکه کانولوشنی نیاز به variable داریم که بتوان مقادیر آن را عوض کرد.
نکته: Weight و bios ها variable در نظر گرفته می شوند.
Varaiable ها در مواردی به کار می روند که نیاز به به روز رسانی متغیر باشد.
استفاده از کراس در تنسرفلو:
با دستور tf.keras میتوان بدون نصب کردن keras ، در محیط تنسورفلو به آن دسترسی داشت. کراس در محیط تنسورفلو function های بیشتری نیز دارد. برای مثال اگر بخواهیم چند ورودی و چند خروجی داشته باشیم یا از چند سرور ورودی بگیریم یا بخواهیم شبکه را به صورت multi gpu فیت کنیم می توانیم از api تنسورفلو به نام dataset استفاده کنیم.
مثال:
|
W1 = tf.ones((2,2)) W2 = tf.Variable(tf.zeros((2,2)), name="weights") with tf.Session() as sess: print(sess.run(W1)) sess.run(tf.initialize_all_variables()) print(sess.run(W2)) |
|
[[ 1. 1.] :out [ 1. 1.]] [[ 0. 0.] [ 0. 0.]] |
W1 یک contant است.
W2 یک variable است. (یک ماتریس 2 در 2 که از صفر پر شده)
اگر بخواهیم از variable ها استفاده کنیم حتما باید ابتدا آن ها را initialize کنیم. با دستور tf.initialize_all_variables() تمام variable هایی که تعریف کردیم initialize می شوند.
مقایسه variable و constant :
|
W = tf.Variable(tf.zeros((2,2)), name="weights") R = tf.Variable(tf.random_normal((2,2)), name="random_weights") with tf.Session() as sess: ....: sess.run(tf.global_variables_initializer()) ....: print(sess.run(W)) ....: print(sess.run(R)) |
variable را می توان با ثابت ها (“name=”weights) یا مقادیر تصادفی (“name=”random_weights) مقداردهی اولیه کرد.
با نوشتن tf.global_variables_initializer تمام متغیر ها با مقادیر از قبل مشخص شده مقداردهی اولیه می شوند.
به روز رسانی یک متغیر:
|
state = tf.Variable(0, name="counter") new_value = tf.add(state, tf.constant(1)) update = tf.assign(state, new_value) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) print(sess.run(state)) for _ in range(3): sess.run(update) print(sess.run(state)) |
در ابتدا یک state تعریف شده که مقدار اولیه آن صفر است.
یک متغیر به نام new_value تعریف شده که مجموع یک تنسور که داخل آن یک قرار دارد و تغییر نمی کند با state است. این متغیر معادل new_value = state + 1 است.
در داخل update یک tf.assign تعریف شده که مقدار تنسور new_value را داخل state می ریزد و معادل state = new_value است.
سپس برای اجرا شدن operation های ساخته شده یک session ایجاد می شود.
با اجرای قطعه کد sess.run(tf.global_variables_initializer()) یک state با مقدار صفر ساخته می شود.
داخل فور update اجرا می شود که یک واحد به state اضافه می کند بنابراین خروجی این قطعه کد …,0,1,2,3 است.
پیاده سازی تنسرفلو (نت بوک 35: intro to tensorflow):
تعریف کتابخانه:
نمایش ورژن تنسرفلو:
|
hello = tf.constant("Hello, TensorFlow!") sess = tf.Session() print(sess.run(hello)) |
ابتدا یک tf.constant سپس یک session ساخته شده است. خروجی این قطعه کد b’Hello, Tensorflow’ است. ( b نشان دهنده ی byte literal است.)
|
node1 = tf.constant(3.0, dtype=tf.float32) node2 = tf.constant(4.0) # also tf.float32 implicitly |
سپس دو constant دیگر با مقادیر3.0 و 4.0 ساخته شده. در داخل constant ها می توان نوع آن ها را مشخص کرد. datatype آن ها به صورت پیش فرض float32 است.
معمولا در فرایندtraining از float های بزرگی استفاده می شود زیرا در عملیات به روز رسانی به ما کمک می کند. اما معمولا در inference وقتی کار با گراف تمام شد و فقط می خواهیم عکس را بگیریم و خروجی را ببینیم نیاز به دقت بالایی ندارد.
یکی از بهینه سازی هایی که در تنسورفلو انجام می شود این است که در گرافی که train شده float64 ها را برمی دارد و با یک int یا float کوچک تر جایگزین می کند تا حجم گراف پایین بیاید.
سپس session ایجاد شده را با دستور sess.close() می بندیم.
نمایش node1 و node2 قبل از ساختن session :
|
print(node1, node2) out: Tensor("Const_1:0", shape=(), dtype=float32) Tensor("Const_2:0", shape=(), dtype=float32) |
نمایش node1 و node2 بعد از ساختن session :
نکته: چون session جاری مشخص است هم می توانیم هم از eval و هم از sess.run استفاده کنیم.
|
sess = tf.Session() print(sess.run([node1, node2])) sess.close() out: [3.0, 4.0] |
مثال:
|
with tf.Session() as sess: node3 = tf.add(node1, node2 , name='my_add') print("node3:", node3) print("sess.run(node3):", sess.run(node3)) |
در ابتدا یک نود add تعریف شده است. با اجرای دستور (print(node3 اسم تنسور برگردانده می شود.
|
(out: node3: Tensor("my_add:0", shape=(), dtype=float32 |
اما با اجرای دستور ((print(sess.run(node3 یک session ساخته شده و مقادیر node1 و node2 با هم جمع می شوند.
|
out: sess.run(node3): 7.0 |

Fetch کردن (واکشی):
|
input1 = tf.constant(3.0) input2 = tf.constant(2.0) input3 = tf.constant(5.0) intermed = tf.add(input2, input3) mul = tf.mul(input1, intermed) with tf.Session() as sess: result = sess.run([mul, intermed]) print(result) |
به قطعه کد ([result = sess.run([mul, intermed اصطلاحا fetch کردن می گویند. در واقع فراخوانی (sess.run(var در یک نشست مقدار آن متغیر را می دهد. می توان چندین متغیر را همزمان fetch کرد.
|
out: result = sess.run([mul, intermed]) |
مثال:
|
input1 = tf.constant(3.0) input2 = tf.constant(2.0) input3 = tf.constant(5.0) intermed = tf.add(input2, input3) mul = tf.mul(input1, intermed) with tf.Session() as sess: result = sess.run([mul, intermed]) print(result) |
در این کد نیز همانند قبل یک گراف ساخته شده است. دو constant به نام های input2 و input3 ، intermediate را ساختند. همچنین tf.mul یک نود است و با اجرای (sess.run(mul,intermed کل گراف ارزیابی می شود.
تمام تنسور های استفاده شده در کد های بالا به صورت دستی تعریف می شدند.
انواع تنسور های استفاده شده تا اینجا:
1. constant
2. variable
نحوه ورودی دادن بیرون از تنسرفلو:
یک راه ساده استفاده از numpy است. می توان numpy را تبدیل به تنسور کرد و تنسور را به شبکه feed کرد. مشکل این روش این است که برای داده های زیاد جواب نمی دهد زیرا نمی توان همه ی داده ها را در ram لود و آن ها را به تنسور تبدیل کرد. به عبارت دیگر ورودی با استفاده از numpy امکان پذیر و راحت است اما مقیاس پذیر نیست.
مثال (ورودی با استفاده از numpy ):
|
a = np.zeros((3,3)) ta = tf.convert_to_tensor(a) with tf.Session() as sess: print(sess.run(ta)) |
|
[.out: [[ 0. 0. 0 [ 0. 0. 0.] [ 0. 0. 0.]] |
روش دیگر استفاده از placeholder و feed dictionary است.
Placeholder : یک نوع تنسور هستند که منتظر ورودی اند.
در ابتدای ساخت گراف placeholder ها بر خلاف variable ها و constant ها هیچ مقداری ندارند و منتظر کاربر می مانند که آن ها را مقدار دهی کند.
|
input1 = tf.placeholder(tf.float32) input2 = tf.placeholder(tf.float32) output = tf.mul(input1, input2) with tf.Session() as sess: print(sess.run([output], feed_dict={input1:[7.], input2:[2.]})) [array([ 14.], dtype=float32)] |
دو placeholder با نام های input1 و input2 تعریف شده که نوع آن ها floate32 است.
ورودی های این گراف در حال حاضر مشخص نیستند. اگر بعد از ساختن session فقط sess.run() نوشته شود (عملیات fetch کردن)، متوجه نمی شود با چه مقداری باید مقداردهی اولیه را انجام دهد. در این جا باید از feed dictionary استفاده شود.
Feed dict مشخص می کند نود ساخته شده به چه placeholder هایی نیاز دارد و سپس این placeholder ها را به آن پاس می دهد.
|
a = tf.placeholder(tf.float32) b = tf.placeholder(tf.float32) adder_node = a + b # + provides a shortcut for tf.add(a, b) adder_node |

|
add_and_triple = adder_node * 3. print(sess.run(add_and_triple, {a: 3, b: 4.5})) |
این قطعه کد خروجی کد قبلی را در 3 ضرب کرده است.

|
W = tf.Variable([.3], dtype=tf.float32) b = tf.Variable([-.3], dtype=tf.float32) x = tf.placeholder(tf.float32) linear_model = W*x + b init = tf.global_variables_initializer() sess.run(init) print(sess.run(linear_model, {x:[1,2,3,4]})) |
ابتدا دو variable و یک placeholder تعریف شده است. Variable ها (w و b ) می توانند بعدا به روز شوند و placeholder ها (x ) باید از کاربر گرفته شوند.
سپس global_variables_initializer برای مقدار دهی متغیر ها صدا زده شده و بعد از آن linear_model با 1,2,3,4 فراخوانی شده است.
|
out: [0. 0.3 0.6 0.90000004] |
|
state = tf.Variable(0, name="counter") new_value = tf.add(state, tf.constant(1)) update = tf.assign(state, new_value) sess = tf.Session() sess.run(tf.global_variables_initializer()) print(sess.run(update)) |
در این کد با اجرای sess.run(update) ابتدا خروجی یک است و هر بار که اجرا می شود یک واحد به آن اضافه می شود.
|
Sess.close() x = tf.Variable(0, name='counter') inc = tf.assign_add(x, 1, name='increment') with tf.Session() as sess: sess.run(tf.global_variables_initializer()) for _ in range(5): print (sess.run(inc)) |
assign_add یک واحد به متغیر x اضافه می کند.
میانگین گیری (tf.reduce_mean):
|
x = tf.constant([[1.0,2.0], [3.0,4.0]]) Mean1 = tf.reduce_mean(x) Mean2 = tf.reduce_mean(x,0) Mean3 = tf.reduce_mean(x,1) with tf.Session() as sess: result1 = sess.run(Mean1) result2 = sess.run(Mean2) result3 = sess.run(Mean3) print(result1) print(result2) print(result3) |
ضرب ماتریسی (tf.reduce_mean):
|
a = tf.constant([1, 2, 3, 4, 5, 6], shape=[2, 3]) b = tf.constant([7, 8, 9, 10, 11, 12], shape=[3, 2]) sess = tf.Session() |
|
out: array([[1, 2, 3], [4, 5, 6]]) |
|
out: array([[ 7, 8], [ 9, 10], [11, 12]]) |
|
c = tf.matmul(a, b) sess.run(c) |
matmul برای ضرب ماتریسی استفاده می شود.
|
out: array([[ 58, 64], [139, 154]]) |
|
d=c+tf.constant([10,2]) sess.run(d) |
|
out: array([[ 68, 66], [149, 156]]) |
بیشینه گیری (tf.argmax):
|
x = tf.constant([[1.0,2.0], [4.0,3.0]]) m1=tf.argmax(x,0) m2=tf.argmax(x,1) with tf.Session() as sess: r1=sess.run(m1) r2=sess.run(m2) print(r1) print(r2) |
آموزش تنسربرد:
گاهی اوقات کار با شبکه های عصبی و دیگر شبکه ها بسیار پیچیده می شود بنابراین تنسرفلو مجموعه ای از ابزار های تجسمی به نام تنسربرد را ارائه داده تا بتوانیم نمودار های تنسرفلو را در این محیط شبیه سازی کنیم و متغیر های مربوط به آن را نمایش دهیم.
نصب و راه اندازی تنسربرد:
ابتدا باید پکیج تنسرفلو را با دستور pip install tensorflow روی پایتون نصب کنیم. سپس برای اجرا در در محل ذخیره کتابخانه تنسرفلو، دستور tensorboard –logdir asset را در ترمینال وارد می کنیم. این دستور نشان می دهد تنسربرد بر روی local host قابل دسترسی است.

نمایش گراف در تنسربرد:

مراحل:
ابتدا برنامه خود را تعریف کرده و متغیر ها را مقدار دهی می کنیم:

سپس یک summary برای ذخیره نتایج تعریف می کنیم:

در مرحله بعد یک session ساخته و برنامه نوشته شده را در تنسربرد نمایش می دهیم:

داشبورد های تنسربرد:

scalar:
برای نمایش آمار وابسته به زمان استفاده می شود. برای مثال در مشاهده عملکرد loss function کاربرد دارد.

histogram:
این داشبورد در تنسربرد نشان می دهد چگونه توزیع آماری تنسور در طول زمان متغیر است. این داده ها از طریق tf.summary.histogram نمایش داده می شوند.

distribution:
در این داشبورد از tf.summary.histogram استفاده می شود که درصد توزیع روی داده ها را نمایش می دهد.

graph:
این داشبورد عمدتا برای بررسی مدل تنسرفلو استفاده می شود.

image:
این داشبورد عکس هایی که با دستور tf.summary.image و در فرمت png ذخیره شده است را نمایش می دهد.
audio:
این داشبورد یک ابزار قوی برای تعبیه ویجت های صوتی برای مخاطبان است که از طریق tf.summary.audio نمایش داده می شود.
projector:
اساسا پروژکتور تعبیه شده در تنسربرد برای داده های چند بعدی استفاده می شوند.
text:
این داشبورد متن را از طریق tf.summary.text ذخیره می کند و شامل مواردی مانند پیوند ها، فهرست ها و جداول می باشد.
شبکه های اجتماعی