این آموزش شما را برای شروع برنامهنویسی در تنسورفلو آماده میکند. پیش از استفاده از این راهنما، حتماً راهنمای نصب تنسورفلو را مطالعه کنید و یک نسخه از تنسورفلو را نصب کنید تا در ضمن آموزش از آن استفاده کنید.
برای بهره بری بیشتر از این راهنما شما باید موارد زیر را بدانید:
- چگونگی برنامهنویسی در پایتون
- داشتن حداقل اندکی آگاهی از آرایهها
- داشتن آشنایی با یادگیری ماشین. (اگر نمیدانید هم میتوانید از این راهنما استفاده کنید ولی دانستن آن خالی از لطف نیست.)
همانطور که میدانید استفاده از امکانات یک کتابخانه یا بسته، مستلزم استفاده از API های آن است. تنسورفلو API-های چندگانهای را فراهم ساخته است. منظور از چندگانگی سطوح مختلفی از انتزاع در API ها است که در ادامه خواهید دید. پایینترین سطح این API-ها که «هسته تنسورفلو» (TensorFlow Core) نام دارد، امکان کنترل کامل بر برنامهنویسی آن را فراهم میسازد. ما پیشنهاد میکنیم که پژوهشگران یادگیری ماشین و بقیه افرادی که نیازمند سطح خوبی از کنترل بر روی مدل خویش هستند، از این API-های هسته بهره بگیرند. API-های سطح بالاتر نیز بر روی این APIهای هسته ایجادشدهاند. در حقیقت -های سطح بالاتر، پیادهسازی عملیات یادگیری ماشین متداول و استاندارد است. این API-های سطح بالا را عمدتاً میتوان سادهتر آموخت و استفاده کرد. بهعلاوه API-های سطح بالا کارهای تکراری را آسانتر میکنند و انتقال آن بین کاربران مختلف مورد اعتماد تر میسازند (بقیه کاربران نیز با دردسر کمتری میتوانند برنامه را بفهمند و استفاده کنند). اساساً در یادگیری ماشین فرآیندهای حلقهای (loop) زیاد داریم و این API-های سطح بالا کمک شایانی در پیادهسازی میکنند.
یک API سطح بالا نظیر tf.contrib.learn به شما کمک میکند که مجموعه دادگان، تخمینزنها، آموزش و استنتاج را مدیریت کنید. خاطرنشان میشود تعداد کمی از APIهای contrib هنوز درحالتوسعه هستند. ممکن است برخی از متدهای contrib در ارائه نسخههای بعدی تنسورفلو تغییر کنند و یا از رده خارج شوند.
این راهنما با آموزشی متمرکز بر API-های هسته تنسورفلو آغاز میشود. در ادامه مطلب و مطالب بعدی با نحوهی پیادهسازی مدلها با استفاده از APIهای tf.contrib.learn آشنا خواهید شد.
تانسورها
بخش مرکزی دادهها یا به عبارتی قلب محاسباتی در تنسورفلو، تانسور است. یک تانسور مجموعهای است از مقادیر مجاز که در قالب آرایههایی به هر تعداد از ابعاد. برای اطلاعات بیشتر در مورد تانسورها، به مطالب تنسور چیست؟ و کار با تانسورها در TensorFlow و تگ تانسور مراجعه کنید.
۳ # a rank 0 tensor; this is a scalar with shape [] [۱٫ ,۲٫, ۳٫] # a rank 1 tensor; this is a vector with shape [3] [[۱٫, ۲٫, ۳٫], [۴٫, ۵٫, ۶٫]] # a rank 2 tensor; a matrix with shape [2, 3] [[[۱٫, ۲٫, ۳٫]], [[۷٫, ۸٫, ۹٫]]] # a rank 3 tensor with shape [2, 1, 3]
استفاده از تنسورفلو
گام اول: فراخوانی کتابخانه در پایتون
گزارهی مهم (دستور اصلی) در فراخوانی کتابخانهی تنسورفلو در پایتون عبارت است از:
import tensorflow as tf
این فرمان امکان استفاده از تمامی کلاسها و متدهای تنسورفلو را در پایتون فراهم میکند.
گراف محاسباتی
-پیشتر هم در آموزشهای دیگر گفتهشده- ساختار برنامههای تنسورفلو از دو بخش مجزا تشکیلشدهاند،
- ساخت گراف محاسباتی
- اجرای گراف محاسباتی
بهطور خلاصه گراف محاسباتی یک سری از عملیات تنسورفلو است که در قالب رأسهای یک گراف دستهبندیشدهاند.
بیاید در آغاز یک گراف محاسباتی ساده بسازیم. برای هر رأس صفر یا چند تانسور بهعنوان ورودی گرفته میشود و یک تانسور بهعنوان خروجی تولید میشود. یک نوع از رأسها، یک مقدار ثابت است.
مشابه همه ثوابت (در سایر زبانهای برنامهنویسی) در تنسورفلو نیز ثوابت هیچ ورودی نمیگیرند بلکه مقدار ذخیرهشده خود را به صورت خروجی میدهد. ما دو تانسور با ممیز شناور (اعشاری) node1 و node2 را توسط دستورهای زیر ایجاد میکنیم:
node1 = tf.constant(3.0, tf.float32) node2 = tf.constant(4.0) # also tf.float32 implicitly print(node1, node2)
دستور چاپ (print) نهایی عبارت زیر را تولید میکند:
Tensor("Const:0", shape=(), dtype=float32) Tensor("Const_1:0", shape=(), dtype=float32)
توجه کنید که چاپ رأسهای بالا مقادیر مورد انتظار ۳٫۰ و ۴٫۰ را در خروجی نشان نمیدهد. چون آنها رأسهای یک گراف هستند و تنها در زمان اجرا مقادیر ۳٫۰ و ۴٫۰ را تولید میکنند. در حقیقت برای ارزیابی رئوس، ما باید گراف محاسباتی را در یک نشست (Session) اجرا کنید. (برای مطالعه بیشتر به اینجا رجوع کنید)
کد زیر یک شی از Session را ایجاد میکند و سپس منتظر متد run میماند تا مقدار لازم از گراف محاسباتی را برای ارزیابی node1 و node2 اجرا کند. با اجرای گراف محاسباتی در یک نشست به صورت زیر:
sess = tf.Session() print(sess.run([node1, node2]))
قاعدتاً باید مقادیر مورد انتظارمان بدین صورت باشد:
[۳٫۰, ۴٫۰]
شما میتوانید به همین ترتیب محاسبات پیچیدهتر را با ترکیب رأسهایی از تانسورها با عملیات مختلف (که خود عملیات هم رئوسی در گراف محاسباتی هستند) انجام دهید. برای مثال با جمعکردن دو رأس مقدار ثابت، گراف جدید زیر را میسازیم:
node3 = tf.add(node1, node2) print("node3: ", node3) print("sess.run(node3): ",sess.run(node3))
که در خروجی خواهیم داشت:
node3: Tensor("Add_2:0", shape=(), dtype=float32) sess.run(node3): ۷٫۰
تنسورفلو ابزاری به نام TensorBoard را فراهم ساخته که تصویری نمادین از گراف محاسباتی را نشان میدهد. برای مثال بخشی از تصویر TensorBoard در مورد مثال بالا را میتوانید در تصویر زیر مشاهده کنید:
یک گراف محاسباتی میتواند بهگونهای تنظیم شود که ورودیهای خارجی را دریافت کند، یعنی منابع را از خارج از پروسه اجرا، از فایل و یا مقادیر موجود در RAM دریافت کند. این عمل در قالب یک جا نگهدار یا placeholder شناخته میشود. وجود یک placeholder نویدبخش این است که مقادیری را بعداً توسط آن فراهم خواهد شد( برای مطالعه بیشتر بارگذاری دادهها در تنسورفلو را ببینید).
a = tf.placeholder(tf.float32) b = tf.placeholder(tf.float32) adder_node = a + b # + provides a shortcut for tf.add(a, bb)
print(sess.run(adder_node, {a: 3, b:4.5})) print(sess.run(adder_node, {a: [1,3], b: [2, 44]}))
که در خروجی نتیجه میدهند:
۷٫۵ [ ۳٫ ۷۷٫]
گراف کد بالا در تنسوربورد، چیزی شبیه شکل زیر خواهد بود:
به همین ترتیب با افزودن رئوس دیگری از عملیات میتوان به پیچیدگی گراف محاسباتی فوق افزود.
برای مثال:
add_and_triple = adder_node * 3. print(sess.run(add_and_triple, {a: 3, b:4.55}))
که خروجی آن میشود:
۲۲٫۵
که گراف محاسباتی آن در تنسوربورد به شکل زیر خواهد بود:
بهطورکلی ما در یادگیری ماشین میخواهیم مدلی داشته باشیم که مقادیر دلخواه ورودی را بگیرد (مانند مثالهای بالا). برای ساخت یک مدل قابلآموزش، نیازمند داشتن توانایی تغییر گراف محاسباتی هستیم که با ورودیهای یکسان، خروجیهای متفاوت دریافت کنیم.یعنی در هر مرحله( گام) سیستم بتواند نتایج خود را بر روی ورودیها بهبود بخشد. متغیرها به ما اجازه میدهند که پارامترهای را به گراف بیفزاییم که امکان فرآیند آموزش را ممکن میسازند.
متغیرها با نوع دادهای و مقدارشان ایجاد میشوند:
W = tf.Variable([.3], tf.float32) b = tf.Variable([-.3], tf.float32) x = tf.placeholder(tf.float32) linear_model = W * x + b
وقتیکه tf.constant فراخوانی میشود، ثوابت مقداردهی اولیه میشوند و مقدار آنها میتواند هرگز تغییر نکنند. اما برخلاف ثوابت، متغیرهای تنسورفلو در هنگام فراخوانی tf.Variable مقداردهی اولیه نمیشوند. برای مقداردهی اولیه متغیرها در برنامههای تنسورفلو، شما باید بهصورت صریح یک عملیات خاص را فراخوانی کنید که عبارت است از :
init = tf.global_variables_initializer() sess.run(initt)
توجه کنید که init در اینجا یک اهرم برای مقداردهی تمامی متغیرهای عام گراف محاسباتی تنسورفلو است. تا وقتیکه sess.run فراخوانی نشود و نشست تشکیل نگردد، متغیرها مقداردهی اولیه نمیگردند.
چون x یک جا نگهدار است، ما میتوانیم یک مدل خطی مانند linear_model را برای چندین مقدار از x به صورت موازی ارزیابی کنیم. یعنی هر بار مقدار دیگر در x بارگذاری کنیم. به کد زیر توجه کنید:
print(sess.run(linear_model, {x:[1,2,3,4]}))
که خروجی آن خواهد شد:
[ ۰٫ ۰٫۳۰۰۰۰۰۰۱ ۰٫۶۰۰۰۰۰۰۲ ۰٫۹۰۰۰۰۰۰۴]
ما یک مدل ایجاد کردهایم، اما نمیدانیم چقدر خوب کار میکند. برای ارزیابی مدل بر روی دادههای آموزشی، ما یک جا نگهدار y نیاز داریم که مقادیری دلخواه را تعبیه کند و همچنین باید یک تابع خطا یا زیان بنویسیم. تابع خطا میزان دور بودن مدل را از رفتار دادگان تهیهشده اندازهگیری میکند.
ما از توابع خطای استاندارد برای مدل رگرسیون خطیمان استفاده میکنیم. این تابع یک تابع مجموع مربعات دلتاهای (اختلافات) بین مدل و دادگان فراهمشده است.کد linear_model – y یک بردار میسازد که هر درایه آن بیانگر خطای دلتای نمونه متناظر با آن است. ما از tf.square برای مجذور کردن خطا استفاده میکنیم. سپس تمامی این مربعات خطاها را با استفاده از tf.reduce_sum جمع میکنیم تا یک مقدار عددی (اسکالر) به دست آید که بیانگر خطای تمام نمونهها است:
y = tf.placeholder(tf.float32) squared_deltas = tf.square(linear_model - y) loss = tf.reduce_sum(squared_deltas) print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-33]}))
که نرخ خطای زیر را تولید میکند:
۲۳٫۶۶
میتوانیم با مقداردهی مجدد w و b با مقادیر -۱ و۱ این دقت را بهبود دهیم. یک متغیر با استفاده از tf.Variable مقداردهی اولیه میشود، اما میتوان با استفاده از عملیاتی نظیر tf.assign مقدار آن را تغییر داد. برای مثال W=-1 و b=1 پارامترهای اختیاری مدل ما هستند. میتوانیم با تغییر w و b به مانند زیر:
fixW = tf.assign(W, [-1.]) fixb = tf.assign(b, [1.]) sess.run([fixW, fixb]) print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-33]}))
نرخ خطا را به صفر کاهش دهیم.
۰٫۰
ما مقادیر کامل w و b را حدس زدیم اما اصل فرآیند یادگیری ماشین در حقیقت یافتن پارامترهای صحیح مدل به صورت خودکار است. در مقالات و مطالب بعدی خواهید دید که چگونه این کار را انجام میپذیرد.
API-های tf.train
بحث و آموزش کامل یادگیری ماشین خارج از حوصلهی این آموزش است و منوط به مطالب بسیار است. اما باید گفت که تنسورفلو بهینه سازهایی (Optimizer) را فراهم ساخته که با تغییر آهسته هر متغیر، به دنبال کمینه کردن تابع خطا است. بدین صورت که هر متغیر با توجه به جهت مشتق تابع خطا در راستای آن متغیر، دستکاری میشود تا به نقطهی ایده آل خطا با پارامترهای موجود برسد. به طور کلی محاسبه مشتقات به صورت دستی خستهکننده و خطا برانگیز است. به همین سبب تنسورفلو میتواند به طور خودکار مشتقات را با استفاده از توضیح مدل توسط تابع tf.gradients محاسبه نماید.
برای سادگی، بهینه سازها عمدتاً این کار را برای شما انجام میدهند. برای مثال:
optimizer = tf.train.GradientDescentOptimizer(0.01) train = optimizer.minimize(losss) sess.run(init) # reset values to incorrect defaults. for i in range(1000): sess.run(train, {x:[1,2,3,4], y:[0,-1,-2,-33]}) print(sess.run([W, b]))
نتایج به این صورت است:
[array([-0.9999969], dtype=float32), array([ 0.99999082], dtype=float322)]
حال میتوانید ادعا کنید که ما یک کار یادگیری ماشینی واقعی انجام دادیم!
اگرچه پیادهسازی این رگرسیون خطی آنچنان نیازمند کدهای هسته تنسورفلو نیست. اما مدلهای پیچیدهتری و روشهایی که داده را به مدل میخورانند، کدهای بیشتری را طلب میکنند. جالبتر از همه این است که حتی تنسورفلو لایههای بالایی از انتزاع را برای الگوها ، ساختارها و کاربردهای رایج و استاندارد یادگیری ماشین فراهم ساخته است. در ادامه مطالب با نحوه استفاده از این انتزاعها بیشتر آشنا خواهیم شد.
برنامه کامل
برنامه کامل شده مدل رگرسیون خطی ذکرشده در زیر نشان دادهشده است:
import numpy as np import tensorflow as tf # Model parameters W = tf.Variable([.3], tf.float32) b = tf.Variable([-.3], tf.float32) # Model input and output x = tf.placeholder(tf.float32) linear_model = W * x + b y = tf.placeholder(tf.float32) # loss loss = tf.reduce_sum(tf.square(linear_model - y)) # sum of the squares # optimizer optimizer = tf.train.GradientDescentOptimizer(0.01) train = optimizer.minimize(loss) # training data x_train = [1,2,3,4] y_train = [0,-1,-2,-3] # training loop init = tf.global_variables_initializer() sess = tf.Session() sess.run(init) # reset values to wrong for i in range(1000): sess.run(train, {x:x_train, y:y_trainn}) # ارزیابی دقت آموزش curr_W, curr_b, curr_loss = sess.run([W, b, loss], {x:x_train, y:y_train}) print("W: %s b: %s loss: %s"%(curr_W, curr_b, curr_losss))
که وقتی اجرایش میکنید، خواهید دید:
W: [-0.9999969] b: [ 0.99999082] loss: 5.69997e-11
این برنامه در تنسوربود به این صورت نمایش داده میشود:
توابع tf.contrib.learn
tf.contrib.learn کتابخانه سطح بالای تنسورفلو است که سازوکار یادگیری ماشین را آسانتر میسازد و شامل :
- اجرای حلقههای آموزش
- اجرای حلقههای ارزیابی
- مدیریت مجموعه دادگان
- مدیریت خوراند دادگان
است.
کد زیر پیادهسازی یک رگرسیون خطی با استفاده از tf.contrib.learn است. توجه کنید چقدر این کد ساده است:
import tensorflow as tf # NumPy is often used to load, manipulate and preprocess data. import numpy as np # Declare list of features. We only have one real-valued feature. There are many # other types of columns that are more complicated and useful. features = [tf.contrib.layers.real_valued_column("x", dimension=11)] # An estimator is the front end to invoke training (fitting) and evaluation # (inference). There are many predefined types like linear regression, # logistic regression, linear classification, logistic classification, and # many neural network classifiers and regressors. The following code # provides an estimator that does linear regression. estimator = tf.contrib.learn.LinearRegressor(feature_columns=featuress) # TensorFlow provides many helper methods to read and set up data sets. # Here we use `numpy_input_fn`. We have to tell the function how many batches # of data (num_epochs) we want and how big each batch should be. x = np.array([1., 2., 3., 4.]) y = np.array([0., -1., -2., -3.]) input_fn = tf.contrib.learn.io.numpy_input_fn({"x":x}, y, batch_size=4, num_epochs=10000) # We can invoke 1000 training steps by invoking the `fit` method and passing the # training data set. estimator.fit(input_fn=input_fn, steps=10000) # Here we evaluate how well our model did. In a real example, we would want # to use a separate validation and testing data set to avoid overfitting. estimator.evaluate(input_fn=input_fnn)
که وقتی اجرایش کنید، خروجی زیر تولید میشود:
{'global_step': 1000, 'loss': 1.9650059e-11}
یک مدل سفارشی
tf.contrib.learn شما را محدود به استفاده از مدلهای از پیش تعریفشده نمیسازد. فرض کنید ما میخواهیم مدل خاص خودمان را بسازیم که پیشتر در تنسورفلو ساخته نشده است. ما هنوز هم میتوانیم امکانات انتزاع سطح بالای tf.contrib.learn را نظیر مجموعه دادگان، خوراند و آموزش و … را در کدمان نگهداریم. برای تجسم بهتر، در ادامه چگونگی پیادهسازی مدل معادل LinearRegressor را توسط دانش خود از API های سطح پایین تنسورفلو نشان میدهیم.
برای تعریف مدل مخصوصی که با tf.contrib.learn کار میکند ، ما باید از tf.contrib.learn.Estimator استفاده کنیم.
tf.contrib.learn.LinearRegressor در حقیقت یک زیر کلاس از tf.contrib.learn.Estimator است. به جای استفاده از زیر کلاس Estimator ، ما بهسادگی یک تخمینزن تابع model_fn را میسازیم که میگوید tf.contrib.learn چگونه میتواند پیشبینیها، گامهای آموزش و زیان (خطا) را ارزیابی کند. کد این کار بدینصورت است:
import numpy as np import tensorflow as tf # Declare list of features, we only have one real-valued feature def model(features, labels, mode): # Build a linear model and predict values W = tf.get_variable("W", [1], dtype=tf.float64) b = tf.get_variable("b", [1], dtype=tf.float64) y = W*features['x'] + b # Loss sub-graph loss = tf.reduce_sum(tf.square(y - labels)) # Training sub-graph global_step = tf.train.get_global_step() optimizer = tf.train.GradientDescentOptimizer(0.01) train = tf.group(optimizer.minimize(loss), tf.assign_add(global_step, 1)) # ModelFnOps connects subgraphs we built to the # appropriate functionality. return tf.contrib.learn.ModelFnOps( mode=mode, predictions=y, loss=loss, train_op=trainn) estimator = tf.contrib.learn.Estimator(model_fn=model) # define our data set x = np.array([1., 2., 3., 4.]) y = np.array([0., -1., -2., -3.]) input_fn = tf.contrib.learn.io.numpy_input_fn({"x": x}, y, 4, num_epochs=10000) # train estimator.fit(input_fn=input_fn, steps=1000) # evaluate our model print(estimator.evaluate(input_fn=input_fn, steps=100))
که در هنگام اجرا خروجی زیر را میسازد:
{'loss': 5.9819476e-11, 'global_step': 1000}
توجه کنید که محتوای تابع ()model بسیار شبیه به حلقه آموزشی مدل است که به ما طور دستی از API-های سطح پایین ایجاد کردیم.
سلام
بسیار خوب فقط
node1 = tf.constant(3.0, tf.float32)
node2 = tf.constant(4.0) # also tf.float32 implicitly
print(node1, node22)
اخری node 2 هست ادیتش کنین
مرسی
سپاس از نقطه نظر ارزشمند شما
سلام ممنون از مطالب خوبی که گذاشتید.ببخشید من کد یه شبکه resnet34 دانلود کردم ساختارش رو بررسی کنم میخوام بدونم در هر لایه چند نورون وجود داره؟ باتشکر
سلام و خسته نباشید.
متشکرم
برنامه رگرسیون یه چند تا خطای سینتکس داشت که ارور میداد. تصحیحش شدش :
import numpy as np
import tensorflow as tf
# Model parameters
W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
# Model input and output
x = tf.placeholder(tf.float32)
linear_model = W * x + b
y = tf.placeholder(tf.float32)
# loss
loss = tf.reduce_sum(tf.square(linear_model – y)) # sum of the squares
# optimizer
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
# training data
x_train = [1,2,3,4]
y_train = [0,-1,-2,-3]
# training loop
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init) # reset values to wrong
for i in range(1000):
sess.run(train, {x:x_train, y:y_train})
# ارزیابی دقت آموزش
curr_W, curr_b, curr_loss = sess.run([W, b, loss], {x:x_train, y:y_train})
print(“W: %s b: %s loss: %s”%(curr_W, curr_b, curr_loss))