اجرای فوری در Tensorflow 2.0
تا قبل از روز سیزدهم اکتبر سال 2017، روزی که اجرای فوری Eager execution به Tensorflow اضافه شد، Tensorflow سریع بود. بله! TF سریعتر بود. تا قبل از این تاریخ TF فقط به حالت اجرای کُند Lazy execution مجهز بود. در اجرای کند یک گراف محاسباتی ایجاد میشود که گرهها همان عملیات هستند و لبهها هم تنسور هستند. گرهها فقط زمانی که نیاز باشد محاسبه میشوند. یک گره فقط زمانی اجرا میشود که خودش و یا گرهی که به آن وابسته است برای اجرا به کار گرفته شود. اجرای کُند به نوعی برنامهنویسی جریان داده Dataflow programming شناخته میشود و به طور خاص در رایانش موازی Parallel computing مورد استفاده قرار میگیرد. پیش از این TF از این موضوع بهره میبرد و گرافهای محاسباتی را در یک session اجرا میکرد؛ این session میتواند اجرای گراف را میان منابع محاسباتی مختلف توزیع و تقسیم کند.
و برای اینکه TF به منبع و مرجع خوبی برای انجام تحقیقات و پژوهشها تبدیل شود، اجرای فوری به آن اضافه شد تا امکان دیباگکردن و رفع خطاهای زمان اجرا را برای کاربران فراهم کند. برای مثال میتوان با استفاده از pdb و دیگر ابزارهای اشکال زدایی مخصوص python، مقادیر تنسورها را تغییر داد و کپی کرد.
به همین منظور اجرای فوری را طبق مراحلی که در راهنمای Tensorflow ذکر شده پیادهسازی میکنیم.
گام اول: کتابخانههای مورد نیاز را بارگذاری کنید و مطمئن شوید که حالت فوری فعال است:
import os import tensorflow as tf import cProfile tf.executing_eagerly()
نکته مهمی که در یادگیری ماشین باید به آن توجه داشته باشید محاسبه گرادیانها برای پیش انتشار است. با اضافه شدن اجرای فوری به TF2 میتوانیم از tf.GradientTape برای دنبال کردن عملیاتها استفاده کنیم تا بعدا گرادیانها را محاسبه کنیم. شیوه کار Gradient Tape به نحوی است که عملیات انتشار رو به جلو Forward operations در قالب tape ذخیره میشوند و tape به صورت پسرو Backwards اجرا میشود تا گرادیان را محاسبه کند. در نمونه آموزشی مقابل، اعدادی که به صورت دستی نوشته شدهاند و دیتاست MNIST را تشکیل دادهاند را طبقهبندی میکنیم:
#Dataset setup (mnist_images, mnist_labels), _ = tf.keras.datasets.mnist.load_data() dataset = tf.data.Dataset.from_tensor_slices((tf.cast(mnist_images[...,tf.newax is]/255, tf.float32),tf.cast(mnist_labels,tf.int64))) dataset = dataset.shuffle(1000).batch(32)
حالا یک شبکه عصبی ساده ایجاد میکنیم:
mnist_model = tf.keras.Sequential([ tf.keras.layers.Conv2D(16,[3,3], activation='relu',input_shape=(None, None, 1)), tf.keras.layers.Conv2D(16,[3,3], activation='relu'), tf.keras.layers.GlobalAveragePooling2D(), tf.keras.layers.Dense(10) ])
سپس تابع بهینهسازی و تابع زیان را تعریف میکنیم:
optimizer = tf.keras.optimizers.Adam() loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) loss_history = []
برای اجرای یک مرحله از آموزش تابع را تعریف میکنیم. در حلقه اصلی آموزش میتوانیم این تابع را در حلقه داخلی بسته Inner batch loop اجرا کنیم.
def train_step(images, labels): with tf.GradientTape() as tape: logits = mnist_model(images, training=True) tf.debugging.assert_equal(logits.shape, (32, 10)) loss_value = loss_object(labels, logits) loss_history.append(loss_value.numpy().mean()) grads = tape.gradient(loss_value, mnist_model.trainable_variables) optimizer.apply_gradients( zip(grads,mnist_model.trainable_variables))
حلقه اصلی آموزش:
def train(epochs): for epoch in range(epochs): for (batch, (images, labels)) in enumerate(dataset): train_step(images, labels) print ('Epoch {} finished'.format(epoch))
در مثال فوق نقش و عملکرد اجرای فوری را در طول فرایند آموزشی به شما نشان دادیم.
در جدول مقابل خلاصهای از مزایای اجرای کُند و اجرای فوری را آوردهایم:
آیا میتوانیم از مزایای هر دو حالت استفاده کنیم؟ TF2 یک API معرفی کرده که به طور همزمان از مزایای هر دو حالت استفاده میکند.
AutoGraph امکان استفاده از اجرای کند در TF2را برای کاربران فراهم کرده. AutoGraph کتابخانهای است که به صورت پیشفرض در tf.function قرار دارد. در TF1 ابتدا میبایست یک شی گراف را نمونه بیاوریم و session را اجرا کنیم. TF2 به جای انجام این کارها، tf.function را معرفی کرده است.
برای تعریف یک گراف محاسباتی در TF2، فقط باید @tf.function را به یک تابع اضافه کنید. AutoGraph تابع را به یک گراف تبدیل میکند. فرض کنید میخواهیم یک گراف برای مرحله آموزش ایجاد کنیم:
@tf.function def train_step(images, labels): with tf.GradientTape() as tape: logits = mnist_model(images, training=True) tf.debugging.assert_equal(logits.shape, (32, 10)) loss_value = loss_object(labels, logits) loss_history.append(loss_value.numpy().mean()) grads = tape.gradient(loss_value, mnist_model.trainable_variables) optimizer.apply_gradients(zip (grads,mnist_model.trainable_variables))
این بدین معنی است که train_step در یک شی python.eager.def_function.Function قرار میگیرد. زمانیکه train_step فراخون شود ایجاد گراف هم آغاز میشود.
حالت اجرای فوری در TF میتواند افراد بیشتری را به استفاده از Tensorflow و در نتیجه یادگیری عمیق ترغیب کند. هرچه روشهای خلاقانهتر و پایتونیک بیشتری برای اجرا در حالت فوری معرفی شود برنامهنویسهای بیشتری به استفاده و مطالعه یادگیری عمیق ترغیب و تشویق میشوند. به گفته الکس پاسوس Alex Passos ، یکی از مهندسان نرمافزار تیم TensorFlow Eager Execution، اجرای فوری یکی از پایههای اصلی و مهم استفاده از Tensorflow است.
در این مقاله آموزشی به طور مختصر مزایای اجرای کند و اجرای فوری را با یکدیگر بررسی کردیم. علاوه بر این با نحوه عملکرد تابع tf.function در Tensorflow آشنا شدیم که از مزایای هر دو حالت اجرا استفاده میکند.