یادگیری ماشین، علم برنامه نویسی برای کامپیوتر است که کامپیوترها از طریق آن بتوانند با انواع مختلف داده سروکار داشته باشند و قادر به یادگیری از طریق این دادهها باشند. در گذشته برنامهنویسان تمامی الگوریتمها و فرمولهای ریاضی و آماری یادگیری ماشین را به صورت دستی مینوشتند. این طرز کدنویسی آن را به فرآیندی زمانبر، خستهکننده و ناکارآمد تبدیل میکند. اما امروزه توسط کتابخانههای مبتنی بر پایتون، فریم ورکها و ماژولهای بسیار قدرتمندی برای یادگیری ماشین ایجاد شده است، که کدنویسی را آسانتر و کارآمدتر از گذشته کرده است. در این مقاله به آموزش TensorFlow میپردازیم. سعی بر آن شده که با بیانی روان مسایل به طور صریح برای دانشجو تعریف شود و با مثالهایی ساده دانشجو قادر به درک نحوهی کار با این فریم ورک و شروع انجام پروژههای خود گردد.
کتابخانههای پایتون در زمینهی هوش مصنوعی عبارتند از:
TensorFlow یکی از کتابخانههای متن باز بسیار محبوب برای محاسبات عددی با کارایی بالاست که توسط تیم Google Brain در شرکت گوکل ساخته شده است و توسط تیمهای تحقیقاتی گوگل در محصولات مختلفی همچون شناسایی گفتار، جیمیل، تصاویر گوگل، جستجو و ... استفاده میشود.TensorFlow از کتابخانههای مشهور یادگیری ماشین در GitHub میباشد. میتوان گفت گوگل تقریبا در همهی برنامههای کاربردیاش از TensorFlow برای اجرای الگوریتمهای یادگیری ماشین استفاده میکند. به عنوان مثال، اگر از تصاویر گوگل یا جستجوی صوتی گوگل استفاده میکنید، به طور غیرمستقیم از مدلهایTensorFlow بهره میبرید، آنها روی خوشههای بزرگ سختافزار گوگل کار میکنند و در کارهای ادراکی قدرتمنداند.
همانطور که از نام این محصول پیداست، TensorFlow فریم ورکی است که دادههای آن بهصورت تنسور تعریف و اجرا میشوند. همین دلیل سبب میشود تا TensorFlow به یک فریم ورک محبوب برای آموزش و اجرای شبکههای عصبی عمیق در برنامههای کاربردی مبتنی بر هوش مصنوعی تبدیل شود و بهصورت گسترده در تحقیقات مرتبط با یادگیری عمیق بهکار گرفته شود. هدف اصلی مقالهی آموزش TensorFlow معرفی مفاهیم تنسورفلو با بیانی ساده به در کنار ارائهی مثالهایی کاربردی برای درک بهتر موضوع است و فرض بر این است که شما تا حدودی با پایتون آشنا هستید.
مولفهی اصلی TensorFlow گراف محاسباتی شامل گرهها و یالهایی است که از این گرهها عبور میکنند و تنسورهایی است که ورودی و خروجی این گرهها هستند. یادگیری این مفاهیم بهمنظور درک بهتر نحوهی کارکرد این کتابخانه ضروری است. در ادامهی مقالهی آموزش TensorFlow به معرفی هر کدام از این مفاهیم میپردازیم.
تنسور یک مفهوم هندسی است که در ریاضیات و فیزیک بهمنظور گسترش مفاهیم اسکالر (کمیت نردهای که با عدد نمایش داده میشود)، بردارها و ماتریسها به ابعاد بالا تعریف میشود. آنچه که میبایست در یادگیری ماشین از تنسور بدانیم این است که تنسور در اینجا مفهومی همچون ساختار داده برای تعریف دادهها و نحوهی کار با آنهاست (مشابه نقشی که ساختار دادهی ماتریس در زبان برنامه نویسی متلب دارد.). تنسور این قابلیت را دارد که حجم بالایی از دادههای عددی را ذخیره کند و همین ویژگی این ساختار داده را برای الگوریتمهای یادگیری عمیق مناسب میسازد. برای درک بهتر تنسور را همچون آرایهای از اعداد که در یک جدول چیده شدهاند، در نظر بگیرید (شکل شمارهی 1).
اعداد اسکالر یک تنسور با بُعد صفر، بردار تنسوری با بُعد یک، ماتریسها بُعد دو و ... . برای مثال در تنسور سه بُعدی با آدرس m*n*p میتوان به ماتریس mاُم و سطر n، ستون pاُم مراجعه کرد. برای درک بهتر، بیایید یک تمرین عملی داشته باشیم: (این تمرین توسط کتابخانهی numpy و صرفاً برای توضیح مفهوم تنسور آورده بیان شدهاست.) فرض کنید که میخواهیم میانگین نمرات یک دانشآموز را در یک درس ذخیره کنیم. برای اینکار از یک تنسور صفر بُعدی برای ذخیرهی این مقدار اسکالر استفاده میکنیم. (در کدهای زیر تابع ndim تعداد ابعاد را برمیگرداند):
import numpy as np
tensor_0D = np.array(5)
print("Average grade: \n{}".format(tensor_0D))
print("Tensor dimensions: \n{}".format(tensor_0D.ndim))
حال فرض کنید میخواهیم نمرات سه درس یک دانشجو را ذخیره کنیم. از ساختار آرایه ای استفاده میکنیم که یک تنسور یک بُعدی است:
tensor_1D = np.array([4,6,8])
print("Subject grades: \n{}".format(tensor_1D))
print("Tensor dimensions: \n{}".format(tensor_0D.ndim))
اگر بخواهیم نمرات سه درس یک دانشجو را که در هر درس سه مرتبه امتحان داده ذخیره کنیم، چه ساختار داده ای داریم؟ یک ماتریس یا تنسور سه بُعدی.
# 2D Tensor (matrix)
tensor_2D = np.array([[0, 1, 1], # Subject 1
[2, 3, 3], # Subject 2
[1, 3, 2]]) # Subject 3
print("Exam grades are:\n{}".format(tensor_2D))
print("Subject 1:\n{}".format(tensor_2D[0]))
print("Subject 2:\n{}".format(tensor_2D[1]))
print("Subject 3:\n{}".format(tensor_2D[2]))
print("Tensor dimensions: \n{}".format(tensor_2D.ndim))
و سرانجام حالتی را در نظر بگیرید که نمرات سه امتحان از سه درس یک دانشجو را برای ترم یک و ترم دو بخواهیم داشته باشیم. همانطور که میبینید ابعاد دادهها در حال افزایش است:
tensor_3D = np.array([[[0, 1, 1], # First quarter
[2, 3, 3],
[1, 3, 2]],
[[1, 3, 2], # Second quarter
[2, 4, 2],
[0, 1, 1]]])
print("Exam grades per quarter are:\n{}".format(tensor_3D))
print("First quarter:\n{}".format(tensor_3D[0]))
print("Second quarter:\n{}".format(tensor_3D[1]))
print("Tensor dimensions: \n{}".format(tensor_3D.ndim)
اگر دادههای مسألهی قبل را برای سه دانشآموز متفاوت بخواهیم بخواهیم داشته باشیم:
tensor_4D = np.array([[[[0, 1, 1], # Jacob
[2, 3, 3],
[1, 3, 2]],
[[1, 3, 2],
[2, 4, 2],
[0, 1, 1]]],
[[[0, 3, 1], # Christian
[2, 4, 1],
[1, 3, 2]],
[[1, 1, 1],
[2, 3, 4],
[1, 3, 2]]],
[[[2, 2, 4], # Sofia
[2, 1, 3],
[0, 4, 2]],
[[2, 4, 1],
[2, 3, 0],
[1, 3, 3]]]])
print("The grades of each student are:\n{}".format(tensor_4D))
print("Jacob's grades:\n{}".format(tensor_4D[0]))
print("Christian's grades:\n{}".format(tensor_4D[1]))
print("Sofia's grades:\n{}".format(tensor_4D[2]))
print("Tensor dimensions: \n{}".format(tensor_4D.ndim))
همانطور که در کدها دیدید ابعاد دادهها میتواند تا مقادیر دلخواه افزایش یابد. میتوان گفت که در دنیای یادگیری عمیق که از ساختار دادهی تنسور استفاده میشود، معمولاً ابعاد تنسورها به شکل زیر است:
برای درک بهتر، فرض کنید 64 عکس رنگی دارید که هر عکس 224*224 پیکسل است. چون عکس، رنگی است هر پیکسل سه مقدار داده (RGB) دارد و در کل 64*3*224*224 داده داریم و یک تنسور چهار بُعدی.
گراف محاسباتی یک گراف بدون دور است. هر گره در گراف نشاندهندهی یک عملیات مانند جمع، ضرب و ... است و هر عملیات منجر به تشکیل یک تنسور جدید میشود. شکل 2 یک گراف محاسباتی ساده را نشان میدهد.
یک گراف محاسباتی دارای ویژگیهای زیر است:
(e=(a+b) * (b+1
همانطور که در پاراگراف قبل اشاره شد، قابلیت اجرای موازی در گرههای موجود در یک سطح این امکان را فراهم میآورد تا عملیاتی بدون نیاز به نتایج همدیگر بهصورت همزمان اجرا شوند. الگوریتمهای موجود در کتابخانهی TensorFlow، از این ویژگی بسیار بهره بردهاند.
TensorFlow این امکان را به کاربران خود میدهد تا از ابزار محاسبات موازی برای اجرای سریع عملیات استفاده کنند. گرهها یا عملیات محاسباتی به طور خودکار برای اجرای محاسبات موازی تنظیم میشوند. همهی این کارها بهطور داخلی اتفاق میافتد، بهطور مثال در گراف بالا عملیات c میتواند بر روی CPU انجام شود و عملیات d بر روی GPU. شکل سه دو چشمانداز اجرای توزیعشده را نشان میدهد:
قسمت چپ در تصویر، یک اجرا از سیستم توزیع شدهی منفرد است که در آن تنها یک نشست منفرد (session) (session یا نشست مفهومی در TensorFlow است که در خلال آن به متغیرهای یک گراف محاسباتی فضای حافظه اختصاص داده شده و متغیرها در طول آن نشست، شناخته شدهاند.) یک کارگر (worker) منفرد ایجاد میکند و کارگر مسئول زمانبندی وظایف (Task Scheduling) بر روی دستگاههای مختلف است.
در مورد قسمت سمت راست تصویر چندین کارگر وجود دارد که این کارگرها میتوانند ماشینهای یکسان یا متفاوتی باشند. هر کارگر متناسب با محیط خود کار میکند یعنی، کارگر پروسس 1، روی ماشین مجزایی کار میکند و عملیات را روی تمامی منابع تحت اختیار خود زمانبندی و تقسیم میکند.
زیرگراف در اصل یک گراف محاسباتی است که خود بخشی از یک گراف اصلی است. بهطور مثال در شکل چهار یک زیرگراف محاسباتی میبینید که بخشی از گراف اصلی است. میتوان گفت یک زیرگراف بیانگر یک زیرعبارت است، یعنی c یک زیرعبارت از e است. همچنین مطابق ویژگی آخر بیان شده برای گرافهای محاسباتی، زیر گرافهای موجود در سطح یکسان از یکدیگر مستقلاند و میتوانند به صورت موازی اجرا شوند و این منجر به افزایش بهرهوری و سرعت اجرا خواهدبود.
TensorFlow عملیات خود را در دستگاههای مختلفی که توسط کارگران اداره میشود، توزیع میکند. دادهها میان دستگاهها به فرم تنسور رد و بدل میشوند. به طور مثال در گراف بالا که e=c*d است، c پس از محاسبه به فرم تنسور درآمده و به گره در سطح بالاتر منتقل میشود. البته در حین انتقال دادهها تأخیر رخ میدهد که این تأخیر به اندازهی تنسور وابسته است.
تا اینجا فهمیدیم که ساختار دادهی میان گرههای گراف محاسباتی به صورت تنسور است. تنسورها میان گرهها با تأخیر جابجا میشوند و مقدار این تأخیر میتواند به ابعاد و اندازهی تنسور و همچنین ویژگیهای دستگاههای اجرایی گرهها وابسته باشد. یکی از ایدههای اصلی جهت فشردهسازی کاهش اندازهی تنسور روش Lossy است. فشردهسازی Lossy اندازه و ابعاد داده را کاهش میدهد و به مقدار ابعاد توجه نمیکند. به این معنی که ممکن است درخلال فشردهسازی مقدار آن نادرست و یا غیر دقیق شود. البته در یک عدد اعشار 32 بیتی میدانیم که بیتهای کم ارزش تأثیر چندانی ندارند. پس تغییر و یا حذف آنها باعث تغییر چندانی در نتیجهی نهایی نمیشود؛ اما این حذف باعث کاهش حجم دادهها بهصورت مؤثر میشود.
تا اینجا به تعریف مفاهیم TensorFlow و آن دسته از ویژگیهایی پرداختیم که این کتابخانه را به زبان محبوب برای متخصصان هوش مصنوعی تبدیل کردهاست:
TF.Graph: بیانگر مجموعهای از عملیات انجام یافته تحت عنوان Tf.Operation است. هر گرهای از گراف نشاندهندهی اجرای عملیاتی بر روی TENSORها است.
TF.Operation: مجموعهی عملیات اصلی که برای اجرای الگوریتم تعریف کردهایم.
TF.Tensor: ساختار دادهای که به عنوان ورودی تعریف میشود و یا نتایج در آن ذخیره میشوند.
در ادامهی مقالهی آموزش TensorFlow با محیط TensorFlow بهصورت عملی آشنا خواهیم شد و با بررسی مثالهای بیشتر به یادگیری بهتر این فریم ورک کمک خواهیم کرد.
TENSORFLOW قابلیت پشتیبانی اجرا بر روی چندین CPU و GPU را به طور موازی دارد. همین اجرای غیرمتمرکز بر روی دستگاهها منجر به افزایش سرعت اجرای الگوریتمهای یادگیری مانند یادگیری عمیق خواهد شد و دیگر نیاز نیست زمان طولانی بر روی آن صرف شود. برای کاربران ویندوز، TENSORFLOW دو نسخهی مختلف تدارک دیده است:
فریم ورک TENSORFLOW روی سیستمعاملهای مختلف قابلیتهای متفاوت دارد. بهتر است برای درک بهتر این موضوع و نصب متناسب با سیستم خود به سایت TENSORFLOW مراجعه کرده و نسخهی متناسب با دستگاه خود را نصب و اجرا کنید.
تمام محاسبات در TENSORFLOW روی یک یا چند TENSOR انجام میشود. هر TENSOR یک شی با سه ویژگی است:
چهار نوع TENSOR که میتوان ایجاد کرد که ما سه مورد اول را در مقالهی آموزش TensorFlow شرح دادهایم:
tf.constant(value, dtype, name = "")
arguments:
- `tf.string`: String variable
- `tf.float32`: Float variable
- `tf.int16`: Integer variable
import tensorflow as tf
## rank 0
# Default name
r1 = tf.constant(1, tf.int16)
print(r1)
TENSOR یک بُعدی به شکل زیر تعریف میشود:
## Rank1
r1_vector = tf.constant([1,3,5], tf.int16)
print(r1_vector)
r2_boolean = tf.constant([True, True, False], tf.bool)
print(r2_boolean)
## Rank 2
r2_matrix = tf.constant([ [1, 2],
[3, 4] ],tf.int16)
print(r2_matrix)
خروجی ماتریس شامل دو سطر و دو ستون است که با مقادیر 1، 2، 3، 4 پر شده است.
برای نمایش سه بُعد باید یک سطح دیگر از براکت را افزود. مثال زیر را ببینید:
## Rank 3
r3_matrix = tf.constant([ [[1, 2],
[3, 4],
[5, 6]] ], tf.int16)
print(r3_matrix)
# Create a vector of 0
print(tf.zeros(10))
دستور بعدی ماتریسی با ابعاد 10*10 که با 1 پر شده را ایجاد میکند (تابع ()tf.ones).
# Create a vector of 1
print(tf.ones([10, 10]))
ماتریس m_shape را با سه سطر و دو ستون که با اعداد 10 تا 15 پر شده را، به شکل زیر تعریف میکنیم و با دستور shape. ابعاد را استخراج میکنیم:
# Shape of tensor
m_shape = tf.constant([ [10, 11],
[12, 13],
[14, 15] ]
)
m_shape.shape
حال با دستورات زیر به ترتیب به تعداد سطر، ستون و کل اندازهی ماتریس m_shape، تنسوری که با یک پر شده باشد، ایجاد میکنیم:
# Create a vector of ones with the same number of rows as m_shape
print(tf.ones(m_shape.shape[0]))
# Create a vector of ones with the same number of column as m_shape
print(tf.ones(m_shape.shape[1]))
# Create a vector of ones with the same number of m_shape.shape
print(tf.ones(m_shape.shape))
هر TENSOR تنها میتواند شامل دادههای همنوع باشد. با دستور زیر نوع داده را میتوان برگرداند:
print(m_shape.dtype)
# Change type of data
type_float = tf.constant(3.123456789, tf.float32)
type_int = tf.cast(type_float, dtype=tf.int32)
print(type_float.dtype)
print(type_int.dtype)
اگر آرگومان مربوط به نوع داده حین ایجاد TENSOR، تعیین نشود، TENSORFLOW بهطور خودکار نوع آن را تعریف میکند. بهطور مثال اگر دادهی متن داشته باشید، نوع آن را string درنظر میگیرد.
tf.get_variable(name = "", values, dtype, initializer)
argument:
If initializer is specified, there is no need to include the `values` as the shape of `initializer` is used.
به عنوان مثال، کد زیر متغیر دو بُعدی با دو مقدار تصادفی ایجاد میکند. به طور پیشفرض، TENSORFLOW یک مقدار تصادفی را برمیگرداند. نام متغیر را var گذاشتیم:
# Create a Variable
## Create 2 Randomized values
var = tf.get_variable("var", [1, 2])
print(var.shape)
در مثال بعدی متغیری با یک سطر و دو ستون ایجاد میکنیم. مقادیر ابتدایی این TENSORصفر است. برای مثال، زمانی که مدلی را آموزش میدهید باید مقادیر را برای محاسبهی وزن ویژگیها مقداردهی اولیه کنید.
var_init_1 = tf.get_variable("var_init_1", [1, 2], dtype=tf.int32, initializer=tf.zeros_initializer)
print(var_init_1.shape)
میتوانید مقادیر یک TENSOR ثابت را به یک متغیر اختصاص دهید. یک TENSOR با مقادیر ثابت با متد ()tf.constant ایجاد و به عنوان مقداردهی اولیهی TENSOR متغیر از آن استفاده میکنیم. مقادیر اولیهی متغیر 10، 20، 30 و 40 است و TENSOR متغیر یک ماتریس 2*2 است:
# Create a 2x2 matrixtensor_const = tf.constant([[10, 20],
[30, 40]])
# Initialize the first value of the tensor equals to tensor_const
var_init_2 = tf.get_variable("var_init_2", dtype=tf.int32, initializer=tensor_const)
print(var_init_2.shape)
tf.placeholder(dtype,shape=None,name=None )
arguments:
data_placeholder_a = tf.placeholder(tf.float32, name = "data_placeholder_a")
print(data_placeholder_a)
هر Session (جلسه یا نشست) عملیات گراف را اجرا خواهد کرد. برای مقداردهی گراف با TENSORهای متغیر، باید یک نشست باز کنید و در داخل آن عملیاتی برای تولید خروجی اجرا کنید. بیایید برای درک بهتر این مفهوم را با تشریح مثال سادهی ضرب دو عدد شروع کنیم.
import tensorflow as tf
# Variables definition
x = tf.constant(6)
y = tf.constant(8)
# Operations definition
result = tf.multiply(x, y)
print(result)
sess = tf.Session()
output = sess.run(result)
print(output)
پس از اجرای عملیات session ایجاد شده را با دستور ()sess.close خاتمه میدهیم تا منابع اختصاص یافته به این نشست آزاد شوند. همانطور که مشاهده نمودید مراحل شامل ایجاد TENSOR برای ورودی، تعریف عملیات ضرب روی آنها، ایجاد سشن و چاپ خروجی است. همانطور که اشاره کردیم، در TENSORFLOW عملیات در قالب یک گراف محاسباتی انجام میپذیرد. گراف محاسباتی مجموعهای از یک سری عملیات ریاضی است که در یک ترتیب خاصی محاسبه میشوند. برای مثال محاسبهی عبارت e=c*d که در آن c= a+b و d=b-1 به فرم گراف زیر است:
این گراف دو ورودی a و b را میگیرد و خروجی e را میدهد. هر گره در این گراف ورودی(ها)یی را میگیرد و محاسباتی را انجام میدهد و خروجی(ها) را به گره بعدی میدهد. میتوان این گراف محاسباتی را در TENSORFLOW به شکل زیر ایجاد کرد.
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
c = tf.add(a, b)
d = tf.sub(b, 1)
e = tf.mul(c, d)
در کد بالا شما placeholder ای تحت عنوان a ایجاد کردهاید و فضایی به اندازهی اعشار 32 بیتی به آن اختصاص دادهاید. حال مقدار این a (که ساختار دادهی TENSOR دارد) برابر مقداری است که شما بهعنوان دادهی ورودی گراف محاسباتی (یا همان دادهی آموزشی یک الگوریتم یادگیری ماشین) به آن اختصاص میدهید و خروجی الگوریتم یادگیری ماشین را براساس آن ارزیابی میکنید. اینکه حین تعریف placeholderها آن را 64 بیتی یا 32 یا 16 بیتی در نظر بگیرید بسته به این دارد که فضای مصرفی و زمان اجرای برنامه اولویت است یا دقت خروجی، که اگر 32 بیت در نظر بگیرید به توازنی میان این دو معیار دست یافتهایم.
with tf.Session() as session:
a_data, b_data = 3.0, 6.0
#feed_dict={data_placeholder_a: data}: Feed the placeholder with data
feed_dict = {a: a_data, b:b_data}
output = session.run([e], feed_dict=feed_dict)
print(output)
پس از ایجاد یک session برای این گراف محاسباتی، مقداری که برای یک placeholder در ورودی گرفته میشود از طریق آرگومان feed_dict به گراف محاسباتی ارسال میشود و پس از اجرا خروجی مورد نظر یعنی 45 چاپ میشود.
ما میتوانیم برای اینکه به TENSORFLOW نشان دهیم که چه دستگاههایی را روی کامپیوتر ما برای اجرا میتواند انتخاب کند از دستور زیر استفاده کنیم تا لیست CPU و GPUهای موجود را داشته باشیم:
from tensorflow.python.client import device_libdef get_available_devices():
local_device_protos = device_lib.list_local_devices()
return [x.name for x in local_device_protos]print(get_available_devices())
with tf.Session() as sess:
with tf.device("/GPU:0"):
matrix1 = tf.constant([[3., 3.]])
matrix2 = tf.constant([[2.],[2.]])
product = tf.matmul(matrix1, matrix2)
output = sess.run(product)
print(output)
با دستور with tf.Session() as sess میتوان به پایتون این امکان را داد که منابع Session را بهطور خودکار آزاد کند.
دستور زیر یک TENSOR تک بُعدی (1D) با 64 مقدار را مابین 5- و 5+ را ایجاد میکند:
n_values = 64
x = tf.linspace(-5.0, 5.0, n_values)
sess = tf.Session()
result = sess.run(x)
print(result)
علاوه بر دستور ()sess.run میتوان از (x.eval(session=sess برای ارزیابی TENSOR هم استفاده کرد و فراموش نکنیم که در پایان ()sess.close را بنویسیم. همچنین میتوان از یک نشست تعاملی (Interactive Session) به جای ()run. استفاده نمود.
تا این قسمت از مقالهی آموزش TensorFlow مثالهای سادهای جهت آشنایی شما و درک بهتر خط کدهای TENSORFLOW را بررسی کردیم. کدهای ما حالت ایستا داشت و الگوریتم به یادگیری نمیپرداخت. در ادامهی مقالهی آموزش TensorFlow به بررسی یک مثال عملی در رابطه با یادگیری ماشین خواهیم پرداخت.
Tensorflow ابزار بسیار گستردهای را برای کنترل کامل محاسبات فراهم میکند. این کار با API سطح پایین (Low level API) انجام میشود. علاوه بر این Tensorflow به مجموعهی گستردهای از APIهای سطح بالا برای انجام بسیاری از الگوریتمهای یادگیری ماشین مجهز شده است. Tensorflow این APIهای سطح بالا را تحت عنوان تخمینگر (Estimator) فراخوانی میکند.
در مقالهی آموزش TensorFlow فقط از تخمینگر برای آموزش مدل پیشبینی توسط رگرسیون خطی (Linear Regression) استفاده خواهد شد. با کمک این ToolBox محاسبات سریعتر و آسانتر اجرا میشوند. بخش اول آموزش چگونگی بهکارگیری بهینهساز گرادیان کاهشی (ِGradient Descent Optimizer) را برای آموزش رگرسیون خطی توصیف میکند. همچنین در قالب یک مثال عملی مجموعه دادهی Boston برای پیشبینی قیمت یک خانه به کمک تخمینگر Tensorflow به کار گرفته خواهد شد.
قبل از آموزش مدل، بیایید با رگرسیون خطی آشنا شویم. تصور کنید که دو متغیر فرضی x و y دارید و کار شما پیشبینی مقدار مجهول y براساس مقدار متغیر x است که مقدار آن مشخص است. اگر دادهها را در نمودار ترسیم کنید میتوانید رابطهی مستقیم بین متغیر مستقل x و متغیر وابستهی y را ببینید که به شکل زیر است.
همانطور که میبینید اگر x=1 باشد y تقریبا برابر 6 است و اگر x=2 باشد y حدودا 8.5 خواهد بود. این یک روش دقیق برای پیشبینی y نیست و مستعد خطاست، بهخصوص با مجموعه دادهای که صدها هزار نقطه میتواند داشته باشد. رگرسیون خطی با یک معادله ارزیابی میشود. متغیر y توسط یک یا تعداد بیشتری از متغیرها میتواند محاسبه شود. در مثال قبلی فقط یک متغیر مستقل وجود دارد(x). اگر بخواهیم معادلهی مربوط به آن را بنویسیم به شکل زیر خواهد بود:
که در آن پارامترها به شرح زیر هستند:منظور از خطای مدل تفاوت مقدار پیشبینی شده با مقدار واقعی است. تصور کنید که مدل را تنظیم کرده و راهحل زیر را برای آن یافتهاید. (منظور از تنظیم یا fit کردن یعنی مقادیر پارامترها به گونهای محاسبه شوند که معادلهی خط حاصل به ازای x نزدیکترین مقدار به y را تولید کند.)
اگر این اعداد را در معادله جایگزین کنیم تبدیل به فرم زیر میشود:
y=3.8+2.78 x
شما اکنون میتوانید مقادیر بهتری برای y از طریق جایگذاری x در معادلهی بالا بیابید. شکل زیر نمودار حاصل از جایگذاری x را در معادلهی فوق نشان میدهد.
خط قرمز حاصل از به هم پیوستن مقادیر پیشبینی شده برای y بر اساس متغیر x در معادلهی فوق است. بر اساس این خط میتوان مقادیر y را برای xهای بزرگتر از 2 نیز پیشبینی کرد. اگر بخواهید رگرسیون خطی را برای همبستگیهای بیشتر گسترش دهید میتوانید متغیرهای بیشتری را به مدل اضافه کنید. تفاوت میان تحلیلهای سنتی و رگرسیون خطی این است که رگرسیون خطی تعیین میکند که y به مجموعهای از متغیرهای مستقل چگونه همزمان واکنش نشان میدهد. در حالی که در روش تحلیل همبستگی چگونگی وابسته بودن y را به هر متغیر مستقل، به صورت دوبهدو و جداگانه بررسی میکند. بیایید مثالی را بررسی کنیم. فرض کنید میخواهید فروش مغازهی بستنیفروشی را پیشبینی کنید. مجموعه داده شامل اطلاعات مختلفی مثل وضعیت آب و هوا (بارانی، آفتابی و ابری) و اطلاعات مشتریان (همچون حقوق، جنسیت و وضعیت تأهل) است. تحلیل سنتی سعی در پیشبینی فروش از طریق محاسبهی میانگین برای هر متغیر به صورت جداگانه میکند و برای سناریوهای مختلف مقدار فروش را تخمین میزند. این امر منجر به پیشبینیهای ضعیف و غیردقیق میشود و تحلیل را به انتخاب سناریوهای خاص محدود میکند. اما اگر شما از رگرسیون خطی استفاده کنید میتوانید معادلهی زیر را برای فروش بنویسید:
الگوریتم رگرسیون خطی بهترین جواب را برای وزنها (ضرایب متغیرها) و مقدار بایاس پیدا میکند. این به این معنی است که الگوریتم سعی در مینیمم سازی هزینه دارد و برای این کار رابطهی sales را با کلیهی متغیرهای مستقل به صورت یکجا بررسی میکند. (منظور از هزینه تفاوت میان مقدار معادلهی خط یافتهشده و مقدار متغیر وابستهی نقاط مجموعه داده است).
الگوریتم یک عدد تصادفی به ازای هر آلفا و بتا انتخاب میکند و مقدار متغیر x را برای محاسبهی y جایگذاری میکند. اگر مجموعه داده شامل 100 نمونه باشد الگوریتم 100 مقدار برای y پیشبینی میکند. میتوانیم مقدار خطا را محاسبه کنیم که تحت عنوان اپسیلن (€) در مدل آورده شده و برابر تفاوت میان مقدار پیشبینیشده توسط مدل و مقدار واقعی است. مقدار خطای مثبت بدین معنی است که الگوریتم مدل y پیشبینیشده را کمتر از مقدار واقعی و مقدار خطای منفی یعنی اینکه مدل، y را بیش از مقدار واقعی تخمین زده است.
هدف این است که مربع خطا را مینیمم کنیم. الگوریتم میانگین مربع خطا را محاسبه میکند. به این مرحله مینیممسازی خطا میگویند. برای رگرسیون خطی میانگین مربع خطا (MSE(Min Square Error نیز نامیده میشود. که از نظر ریاضی به این صورت است:
هدف یافتن بهترین ماتریس وزنی است که MSE را مینیمم کند. اگر میانگین خطا بزرگ باشد به این معنی است که مدل ضعیف عمل میکند و وزنها به خوبی انتخاب نشدهاند. برای اصلاح وزنها به یک بهینهساز نیاز است. بهینهساز مرسوم گرادیان کاهشی (Gradient Descent) با گرفتن مشتق وزنها را کم و زیاد میکند. اگر مشتق مثبت باشد وزن کاهش مییابد و اگر منفی باشد وزن افزایش مییابد.
مدل، وزنها را بروزرسانی میکند و مقدار خطا را دوباره محاسبه میکند. این فرایند تا زمانی که مقدار خطا تغییر نکند تکرار میشود. هر مرحله یک تکرار (Iteration) نامیده میشود. علاوه بر این گرادیانهادر یک نرخ یادگیری ضرب میشوند که بیانگر سرعت یادگیری است. اگر نرخ یادگیری مقدار خیلی کوچکی باشد باعث میشود که زمان همگرایی الگوریتم طولانیتر شود. (یعنی تعداد تکرار بیشتری نیاز داشته باشد.) اگر نرخ یادگیری مقدار خیلی بزرگی داشته باشد باعث میشود که الگوریتم هرگز همگرا نشود.
همانطور که در شکل بالا میبینید، مدل بیست مرتبه پیش از یافتن یک مقدار پایدار برای وزنها تکرار میشود و سپس به کمترین میزان خطا دست مییابد. توجه کنید خطا به صفر نمیرسد بلکه حدود مقدار 5 پایدار و بدون تغییر میشود. این به این معنی است که مدل خطایی با مقدار 5 دارد. اگر بخواهیم خطا را کاهش دهیم نیاز به افزودن دادههای بیشتری به مدل، مثل متغیرهای بیشتر و یا استفاده از تخمینگرهای متفاوتی داریم.
حال که مفهوم رگرسیون خطی را فهمیدهاید قادر به بهکارگیری API تخمینگر ارائه شده توسط Tensorflow برای آموزش اولین رگرسیون خطی خود هستید. در این آموزش از مجموعه دادهی Boston که شامل متغیرهای زیر است استفاده میکنیم.
این مجموعه داده را به سه قسمت به شرح زیر تقسیم خواهیم کرد:
هدف بهکارگیری ویژگیهای مجموعه داده برای پیشبینی ارزش قیمت یک خانه است. در ادامهی آموزش TensorFlow ، شما یاد خواهید گرفت که چگونه و از طریق دو روش مختلف داده را وارد محیط Tensorflow کنید. توجه کنید که هر دوی این روشها نتایج یکسانی در پی دارند:
دوباره یادآوری میکنیم که در این آموزش، نحوهی چگونگی استفاده از API سطح بالا برای ایجاد و آموزش ارزیابی یک مدل رگرسیون خطی در تنسورفلو را یاد خواهید گرفت. اگر شما از یک API سطح پایین برای این کار استفاده کنید میبایست تابع Loss Function، بهینهسازی، گرادیان کاهشی، ضرب ماتریسی، گراف و تنسور را به صورت دستی تعریف کنید که اینها برای افراد مبتدی پیچیدهاند.
import pandas as pd
from sklearn import datasets
import tensorflow as tf
import itertools
نام ستونها (ویژگی) را تعریف و در متغیر COLUMNS ذخیره میکنید. میتوانید از تابع ()pd.read_csv برای وارد کردن دادهها استفاده کنید.
COLUMNS = ["crim", "zn", "indus", "nox", "rm", "age",
"dis", "tax", "ptratio", "medv"]
#-------------------------------------------------
training_set = pd.read_csv("E:/boston_train.csv", skipinitialspace=True,skiprows=1, names=COLUMNS)
test_set = pd.read_csv("E:/boston_test.csv", skipinitialspace=True,skiprows=1, names=COLUMNS)
prediction_set = pd.read_csv("E:/boston_predict.csv", skipinitialspace=True,skiprows=1, names=COLUMNS)
میتوان شکل (ابعاد) داده را با دستور زیر به دست آورد:
print(training_set.shape, test_set.shape, prediction_set.shape)
FEATURES = ["crim", "zn", "indus", "nox", "rm",
"age", "dis", "tax", "ptratio"]
LABEL = "medv"
باید متغیرهای عددی را به فرمت مناسب تبدیل کرد. Tensorflow متدی را برای تبدیل متغیرهای پیوسته تحت عنوان ()tf.feature_column.numeric_column در اختیار شما قرار میدهد. در گام قبلی شما لیستی از ویژگیهایی که برای مدل در نظر داشتید را تعریف کردید. اکنون از این لیست برای تبدیل آنها به دادهی عددی میتوانید استفاده کنید. اگر میخواهید ویژگیهایی را در مدل خود حذف کنید این کار را پیش از ساخت ستونهای ویژگی مدل خود (feature_cols) انجام دهید.
feature_cols = [tf.feature_column.numeric_column(k) for k in FEATURES]
در این مرحله شما نیاز به تعریف تخمینگر دارید. Tensorflow در حال حاضر شش تخمینگر از پیشساختهشده را فراهم میکند که سه تای آن برای طبقهبندی (Classification) و سه تای آن برای رگرسیون بهکار میرود.
تخمینگر رگرسیون (Regressor):در این آموزش از Linear Regressor استفاده خواهیم کرد. برای دسترسی به این تابع از tf.estimator استفاده میکنیم. برای تابع tf.estimator باید دو آرگومان تعریف شود:
estimator = tf.estimator.LinearRegressor(
feature_columns=feature_cols,
model_dir="train")
موضوع بعدی این است که شما دادهها را قبل از هر تکرار تغییر دهید یا ترکیب کنید. در طول آموزش مدل تغییر دادهها مسألهی مهمی است تا مدل یک الگوی خاص از مجموعهی داده را یاد نگیرد. چون اگر مدل جزییات الگوی موجود در دادهها را یاد بگیرد نمیتوان آن را برای پیشبینی دادههای جدید و دیده نشده تعمیم داد. در واقع میخواهیم از بیشبرازش (over fitting) جلوگیری کنیم. بیشبرازش (over fitting) سبب میشود تا مدل روی دادههای آموزشی عملکرد بسیار خوبی داشته باشد. اما دادههای تست را با خطای بسیار بالا برچسبدهی کند.
Tensorflow این دو مسألهی تعداد مشاهدات مورد نیاز (Batchها یا همان تعداد تکرار) و همچنین تغییر داده را به خوبی مدیریت میکند. برای ساخت نحوهی فیددهی به مدل میتوان از pandas_input_fn استفاده کرد. این آبجکت (object) به 5 پارامتر نیاز دارد.
چون مدل در چندین مرحله آموزش مییابد، باید دادهها را به الگوریتم چندین بار فیددهی کنیم. برای این کار از تابع get_input_fn استفاده میکنیم تا پروسهی فیددهی دادهها را تکرار کند.
def get_input_fn(data_set, num_epochs=None, n_batch = 128, shuffle=True):
return tf.estimator.inputs.pandas_input_fn(
x=pd.DataFrame({k: data_set[k].values for k in FEATURES}),
y = pd.Series(data_set[LABEL].values),
batch_size=n_batch,
num_epochs=num_epochs,
shuffle=shuffle)
متد رایج ارزیابی کارایی مدل شامل موارد زیر است:
میتوان از estimator.train برای ارزیابی مدل استفاده کرد. train. estimator دو آرگومان ورودی دارد، یکی تابعی با عنوان input_fn و دیگری پارامتری با نام steps. میتوانید از تابعی که در مراحل قبلی با عنوان get_input_fn ساختهاید برای فیددهی استفاده کنید. سپس مدل را با تکرار 1000 مرتبه روی دادهها بسازید. توجه کنید که شما تعداد epochها رو تعیین نمیکنید و شما تنها مدل را 1000 مرتبه تکرار میکنید. اگر شما epoch را برابر یک تنظیم کنید، مدل 4 مرتبه تکرار میشود: چون 400 نمونه در مجموعه دادهی آموزشی دارید و اندازهی هر دسته (batch size) برابر 128 است. پس بهتر است epoch را None گذاشته و تعداد تکرار را تعیین کرد.
estimator.train(input_fn=get_input_fn(training_set,
num_epochs=None,
n_batch = 128,
shuffle=False),
steps=1000)
# For Windows
tensorboard --logdir=train
با کد زیر میتوان مدل یاد گرفتهشده را روی دادهی تست fit کرد و به کار گرفت.
ev = estimator.evaluate(
input_fn=get_input_fn(test_set,
num_epochs=1,
n_batch = 128,
shuffle=False))
loss_score = ev["loss"]
print("Loss: {0:f}".format(loss_score))
این مدل حدود ۳۰۰۰ خطا داشته است. برای بررسی میزان کم یا زیاد بودن خطای مدل حاصل، میتوانیم اطلاعات آماری دادههایمان را بررسی کنیم. به دستور زیر و خروجی آن توجه کنید:
training_set['medv'].describe()
درنهایت میتوانید از estimator.predict برای پیشبینی قیمت شش خانه در این مثال استفاده کنید.
y = estimator.predict(
input_fn=get_input_fn(prediction_set,
num_epochs=1,
n_batch = 128,
shuffle=False))
خروجی کد بالا را با دستور زیر چاپ کنید.
predictions = list(p["predictions"] for p in itertools.islice(y, 6))print("Predictions: {}".format(str(predictions)))
در این بخش به توضیح نحوهی حل مسأله تنها با استفاده از فریم ورک Tensorflow میپردازیم که کمی پیچیدهتر از روش قبلی است. توجه کنید که اگر از Jupyter notebook استفاده میکنید آن را Restart کرده و kernel را پاک کنید. در این بخش تابع input_fn را خودتان تعریف خواهیدکرد.
ابتدا دو متغیر را برای مسیر دادههای فایل csv تعیین میکنید. توجه داشته باشید که شما دو فایل csv دارید: یکی دادههای آموزشی و دیگری دادههای آزمایشی.
import tensorflow as tf
df_train = "E:/boston_train.csv"
df_eval = "E:/boston_test.csv"
سپس باید ستونهایی که نیاز دارید به کار ببرید را تعیین کنید که در این مثال ما همهی ستونها را به کارمیگیریم و همچنین نوع متغیرها را هم باید مشخص کرد. متغیرهای اعشاری را با [.0] تعریف میکنیم.
COLUMNS = ["crim", "zn", "indus", "nox", "rm", "age",
"dis", "tax", "ptratio", "medv"]RECORDS_ALL = [[0.0], [0.0], [0.0], [0.0],[0.0],[0.0],[0.0],[0.0],[0.0],[0.0]]
def input_fn(data_file, batch_size, num_epoch = None):
# Step 1
def parse_csv(value):
columns = tf.decode_csv(value, record_defaults= RECORDS_ALL)
features = dict(zip(COLUMNS, columns))
#labels = features.pop('median_house_value')
labels = features.pop('medv')
return features, labels
# Extract lines from input files using the
Dataset API.
dataset = (tf.data.TextLineDataset(data_file) # Read text file
.skip(1) # Skip header row
.map(parse_csv))
dataset = dataset.repeat(num_epoch)
dataset = dataset.batch(batch_size)
# Step 3
iterator = dataset.make_one_shot_iterator()
features, labels = iterator.get_next()
return features, labels
برای یک فایل csv، متد مجموعه داده در هر دفعه میتواند یک سطر را بخواند. برای ساخت مجموعه داده، نیاز به استفاده از آبجکت TextLineDataset داریم. مجموعه دادهی شما یک سرآیند (Header) دارد که شامل نام ویژگیها و برچسب دادهها است و در محاسبات استفاده نمیشود، پس خط اول را نمیخوانید. برای فیددهی به مدل ستون برچسب را از دادهها جدا میکنیم. از متد map برای تبدیل هرگونه داده استفاده میکنیم. این متد تابعی را فراخوانی میکند که شما برای ایجاد مبدل داده از آن استفاده خواهید کرد.
به طور خلاصه شما باید داده را به شیء TextLineDataset بدهید، سرآیند دادهها را حذف کنید و در نهایت یک تبدیل داده به کمک یک تابع انجام دهید:
ویژگیها میتوانند به فرم دیکشنری یا تاپل باشند که فرم دیکشنری برای استفاده راحتتر است. توضیح کد:
مجموه داده در طول آموزش چندین مرتبه به تنسورها داده (فیددهی) میشود. پس باید متدی تعریف شود تا این فیددهی را به طور مکرر و نامحدود بتواند انجام دهد و اجرای برنامه با خطا مواجه نگردد. batch_size هم تعیین میکند که در هر مرتبه چه تعداد از دادههای مجموعه داده به پایپ لاین برای انجام محاسبات ارسال شود.
2. ایجاد iterator یا اشارهگرهدف ساخت Iterator و یا اشارهگرهایی برای برگرداندن عناصر مجموعه داده است. سادهترین راه استفاده از make_one_shot_iterator است و پس از آن شما قادر به ایجاد کار کردن با ویژگیها و برچسب کلاس به کمک iterator خواهید بود.
3. استفاده از دادههابرای اینکه بدانید چه اتفاقی در تابع input_fn رخ میدهد، میتوانید با بهکارگیری دادهها آن را کنترل کنید. این کار را با batch_size=1 انجام دهید. توجه کنید که قطعه کد زیر ویژگیها را به صورت دیکشنری و ستون برچسب را به شکل آرایه نمایش میدهد. اگر میخواهید در خروجی، سطرهای دیگر فایل csv مجموعه داده را ببینید میتوانید کد را با batch_sizeهای دیگر امتحان کنید.
next_batch = input_fn(df_train, batch_size = 1, num_epoch = None)
with tf.Session() as sess:
first_batch = sess.run(next_batch)
print(first_batch)
ویژگیهای عددی را به شکل زیر تعریف میکنیم:
X1= tf.feature_column.numeric_column('crim')
X2= tf.feature_column.numeric_column('zn')
X3= tf.feature_column.numeric_column('indus')
X4= tf.feature_column.numeric_column('nox')
X5= tf.feature_column.numeric_column('rm')
X6= tf.feature_column.numeric_column('age')
X7= tf.feature_column.numeric_column('dis')
X8= tf.feature_column.numeric_column('tax')
X9= tf.feature_column.numeric_column('ptratio')
توجه کنید که شما نیاز دارید تا تمام متغیرها را در یک براکت، به شکل زیر جمع کنید.
base_columns = [X1, X2, X3,X4, X5, X6,X7, X8, X9]
مدل را با تخمینگر رگرسیون خطی به شکل زیر آموزش میدهیم.
model = tf.estimator.LinearRegressor(feature_columns=base_columns, model_dir='train3')
باید از تابع لامبدا برای نوشتن آرگومانهای داخل input_fn استفاده کرد و بدون لامبدا فانکشن قادر به آموزش مدل نخواهید بود.
# Train the estimatormodel.train(steps =1000,
input_fn= lambda : input_fn(df_train,batch_size=128, num_epoch = None))
results = model.evaluate(steps =None,input_fn=lambda: input_fn(df_eval, batch_size =128, num_epoch = 1))
for key in results:
print(" {}, was: {}".format(key, results[key]))
prediction_input = {
'crim': [0.03359,5.09017,0.12650,0.05515,8.15174,0.24522],
'zn': [75.0,0.0,25.0,33.0,0.0,0.0],
'indus': [2.95,18.10,5.13,2.18,18.10,9.90],
'nox': [0.428,0.713,0.453,0.472,0.700,0.544],
'rm': [7.024,6.297,6.762,7.236,5.390,5.782],
'age': [15.8,91.8,43.4,41.1,98.9,71.7],
'dis': [5.4011,2.3682,7.9809,4.0220,1.7281,4.0317],
'tax': [252,666,284,222,666,304],
'ptratio': [18.3,20.2,19.7,18.4,20.2,18.4]
}
def test_input_fn():
dataset = tf.data.Dataset.from_tensors(prediction_input)
return dataset
# Predict all our prediction_inputpred_results = model.predict(input_fn=test_input_fn)
مقدار پیشبینی شده را با دستور زیر میتوان چاپ کرد.
for pred in enumerate(pred_results):
print(pred)
در این آموزش چگونگی به کارگرفتن API سطح بالا برای رگرسیون خطی تشریح شد. دیدیم که باید موارد زیر را برای این منظور تعریف کنیم:
و در آخر قادر به آموزش (train)، ارزیابی (evaluate) و پیشبینی (predict) خواهید بود.
در مقالهی آموزش TensorFlow با فریم ورک تنسورفلو به طور کلی آشنا شدیم. مفاهیم آن را بررسی کردیم، مثالهای سادهای برای درک بهتر موضوع، کدزنی کردیم و در پایان چگونگی آموزش یک مدل برمبنای رگرسیون خطی در تنسورفلو را هم دیدیم. اگر در مورد این مقاله سوال یا نظری داشتید خوشحال میشویم که در بخش نظرات با ما و کاربران سون لرن به اشتراک بگذارید.
اگر دوست داری به یک متخصص داده کاوی اطلاعات با زبان پایتون تبدیل شوی و با استفاده از آن در بزرگترین شرکتها مشغول به کار شوی، شرکت در دوره جامع آموزش پایتون را پیشنهاد میکنیم.
سلام ممنون از انتخاب و انتشار ترجمه ولی لطفا حرفه ای باشید . لطفا در ابتدای متن آدرس متن اصلی را ارائه کنید. رعایت قوانین کپی رایت مطمئنا به نفع شما و به نفع جامعه برنامه نویسی هست. به علاوه حسن ظن زیادی نسبت به شما به عنوان یک متخصص ایجاد خواهد کرد. باز هم ممنون از اینکه زحمت میکشید و این متنهای خوب را پیدا و منتشر میکنید.
سلام ببخشید یک سوالی دارم تنسور فلو در پایتون رو میشه به فایل جاوا اسکریپت تبدیل کرد؟
درود بله با یسری کتابخونه مثل <a href="https://github.com/tensorflow/tfjs" target="_blank" rel="noopener nofollow ugc">tfjs </a>قابل انجام هست.
درود باید سرفصلهای دوره متخصص علوم داده رو بررسی کنید اگر آموزشی باشه اونجاست. تنسور فلو 2 دارای یسری تغییر خیلی خوب هست که کار رو راحت میکنه. برای مثال تو نسخه دوم ما نمودارهای داینامیک داریم، به استفاده از مفهوم Session نیازی نیست و از توابع استفاده میشه. همچنین کراس به صورت ماژول در کد ایمپورت و استفاده میشه.
عالی بود دستتون درد نکنه . مورد جالبی دیگه بهش برخوردم که خیلی خوشحالم کرد این بود که TensorFlow کتابخونه برای جاوااسکریپت ( زبان مورد علاقه م ) هم داره . در کل ممنون بابت بابت مقاله پر محتوا و عالی تون.
متشکرم. خوشحالم که براتون مفید بوده. بله همینطوره. بسیاری از کتابخانههای پایتون هستند که این ویژگی رو دارند و حالا این یا به خاطر الگوبرداری از زبانهای دیگر و تکمیل اون درمجموعه کتابخانههای خودشه و بالعکس . و یا بخاطر هماهنگی برای کار با سایرزبانها که هریک ویژگی کاربردی خودشون رو دارن.