TensorFlow، Keras و یادگیری عمیق، بدون دکترا

در این کد لبه، نحوه ساخت و آموزش یک شبکه عصبی که ارقام دست نویس را تشخیص می دهد، یاد خواهید گرفت. در طول مسیر، همانطور که شبکه عصبی خود را برای دستیابی به دقت 99 درصد تقویت می کنید، ابزارهای تجارتی را نیز کشف خواهید کرد که متخصصان یادگیری عمیق از آنها برای آموزش کارآمد مدل های خود استفاده می کنند.

این آزمایشگاه کد از مجموعه داده MNIST استفاده می کند، مجموعه ای از 60000 رقم برچسب گذاری شده که نسل های دکترا را برای تقریبا دو دهه مشغول نگه داشته است. با کمتر از 100 خط کد پایتون/تنسورفلو مشکل را حل خواهید کرد.

چیزی که یاد خواهید گرفت

  • شبکه عصبی چیست و چگونه آن را آموزش دهیم
  • نحوه ساخت یک شبکه عصبی پایه 1 لایه با استفاده از tf.keras
  • نحوه اضافه کردن لایه های بیشتر
  • نحوه تنظیم برنامه نرخ یادگیری
  • نحوه ساخت شبکه های عصبی کانولوشنال
  • نحوه استفاده از تکنیک های منظم سازی: ترک تحصیل، عادی سازی دسته ای
  • چه چیزی بیش از حد مناسب است

آنچه شما نیاز دارید

فقط یک مرورگر این کارگاه به طور کامل با Google Colaboratory قابل اجرا است.

بازخورد

لطفاً اگر چیزی در این آزمایشگاه اشتباه می بینید یا فکر می کنید باید بهبود یابد، به ما بگویید. ما بازخورد را از طریق مشکلات GitHub [ لینک بازخورد ] مدیریت می کنیم.

این آزمایشگاه از Google Colaboratory استفاده می کند و نیازی به تنظیم از طرف شما ندارد. می‌توانید آن را از Chromebook اجرا کنید. لطفاً فایل زیر را باز کنید و سلول ها را اجرا کنید تا با نوت بوک های Colab آشنا شوید.

Welcome to Colab.ipynb

دستورالعمل های اضافی در زیر:

یک باطن GPU را انتخاب کنید

در منوی Colab، Runtime > Change runtime type و سپس GPU را انتخاب کنید. اتصال به زمان اجرا به طور خودکار در اولین اجرا انجام می شود، یا می توانید از دکمه "اتصال" در گوشه سمت راست بالا استفاده کنید.

اجرای نوت بوک

با کلیک بر روی یک سلول و استفاده از Shift-ENTER سلول ها را یکی یکی اجرا کنید. همچنین می توانید کل نوت بوک را با Runtime > Run all اجرا کنید

فهرست مطالب

همه نوت بوک ها دارای فهرست مطالب هستند. می توانید آن را با استفاده از فلش سیاه سمت چپ باز کنید.

سلول های پنهان

برخی از سلول ها فقط عنوان خود را نشان می دهند. این یک ویژگی نوت بوک مخصوص Colab است. می توانید روی آنها دوبار کلیک کنید تا کد داخل آن را ببینید اما معمولاً چندان جالب نیست. به طور معمول توابع پشتیبانی یا تجسم. هنوز باید این سلول ها را اجرا کنید تا توابع داخل آن تعریف شوند.

ابتدا قطار شبکه عصبی را تماشا خواهیم کرد. لطفاً نوت بوک زیر را باز کنید و از تمام سلول ها عبور کنید. هنوز به کد توجه نکنید، بعداً توضیح خواهیم داد.

keras_01_mnist.ipynb

همانطور که نوت بوک را اجرا می کنید، روی تجسم ها تمرکز کنید. برای توضیحات زیر را ببینید.

داده های آموزشی

ما مجموعه‌ای از ارقام دست‌نویس داریم که برچسب‌گذاری شده‌اند تا بدانیم هر تصویر چه چیزی را نشان می‌دهد، یعنی عددی بین ۰ تا ۹. در دفترچه، گزیده‌ای را مشاهده می‌کنید:

شبکه عصبی که ما خواهیم ساخت، ارقام دست نویس را در 10 کلاس آنها طبقه بندی می کند (0، ..، 9). این کار را بر اساس پارامترهای داخلی انجام می دهد که برای اینکه طبقه بندی به خوبی کار کند، باید مقدار صحیحی داشته باشد. این "مقدار صحیح" از طریق یک فرآیند آموزشی که به یک "داده داده برچسب دار" با تصاویر و پاسخ های صحیح مرتبط نیاز دارد، آموخته می شود.

چگونه بفهمیم که شبکه عصبی آموزش دیده عملکرد خوبی دارد یا خیر؟ استفاده از مجموعه داده آموزشی برای آزمایش شبکه تقلب خواهد بود. قبلاً این مجموعه داده چندین بار در طول آموزش دیده شده است و مطمئناً در آن بسیار کارآمد است. برای ارزیابی عملکرد «دنیای واقعی» شبکه به مجموعه داده برچسب‌دار دیگری نیاز داریم که هرگز در طول آموزش دیده نشده است. به آن مجموعه داده اعتبار سنجی می گویند

آموزش

با پیشرفت آموزش، یک دسته از داده های آموزشی در یک زمان، پارامترهای مدل داخلی به روز می شوند و مدل در تشخیص ارقام دست نویس بهتر و بهتر می شود. می توانید آن را در نمودار آموزشی مشاهده کنید:

در سمت راست، "دقت" به سادگی درصد ارقام به درستی تشخیص داده شده است. با پیشرفت تمرین بالا می رود که خوب است.

در سمت چپ، ما می توانیم "از دست دادن" را ببینیم. برای هدایت آموزش، ما یک تابع "از دست دادن" را تعریف می کنیم که نشان دهنده میزان بد تشخیص ارقام توسط سیستم است و سعی می کنیم آن را به حداقل برسانیم. آنچه در اینجا می بینید این است که با پیشرفت آموزش، ضرر هم به داده های آموزشی و هم به داده های اعتبار سنجی کاهش می یابد: این خوب است. یعنی شبکه عصبی در حال یادگیری است.

محور X تعداد "دوران" یا تکرارها را در کل مجموعه داده نشان می دهد.

پیش بینی ها

هنگامی که مدل آموزش داده می شود، می توانیم از آن برای تشخیص ارقام دست نویس استفاده کنیم. تجسم بعدی نشان می‌دهد که چقدر روی چند رقم ارائه‌شده از فونت‌های محلی (خط اول) و سپس روی 10000 رقم مجموعه داده اعتبارسنجی عملکرد خوبی دارد. کلاس پیش بینی شده در زیر هر رقم، در صورتی که اشتباه بود، با رنگ قرمز ظاهر می شود.

همانطور که می بینید، این مدل اولیه خیلی خوب نیست اما هنوز برخی از ارقام را به درستی تشخیص می دهد. دقت اعتبار نهایی آن حدود 90٪ است که برای مدل ساده ای که ما با آن شروع می کنیم چندان بد نیست، اما همچنان به این معنی است که 1000 رقم اعتبارسنجی از 10000 را از دست می دهد. این بسیار بیشتر است که می توان نمایش داد، به همین دلیل به نظر می رسد که همه پاسخ ها اشتباه هستند (قرمز).

تانسورها

داده ها در ماتریس ها ذخیره می شوند. یک تصویر 28x28 پیکسل در مقیاس خاکستری در یک ماتریس دو بعدی 28x28 قرار می گیرد. اما برای یک تصویر رنگی به ابعاد بیشتری نیاز داریم. 3 مقدار رنگ در هر پیکسل (قرمز، سبز، آبی) وجود دارد، بنابراین یک جدول سه بعدی با ابعاد مورد نیاز است [28، 28، 3]. و برای ذخیره یک دسته از 128 تصویر رنگی، یک جدول چهار بعدی با ابعاد [128، 28، 28، 3] مورد نیاز است.

این جداول چند بعدی «تانسور» نامیده می‌شوند و فهرست ابعاد آن‌ها «شکل» آن‌ها است.

به طور خلاصه

اگر تمام عبارات پررنگ در پاراگراف بعدی برای شما شناخته شده است، می توانید به تمرین بعدی بروید. اگر تازه شروع به یادگیری عمیق کرده اید، خوش آمدید و لطفاً ادامه دهید.

witch.png

برای مدل‌هایی که به صورت دنباله‌ای از لایه‌ها ساخته می‌شوند، Keras API متوالی را ارائه می‌کند. به عنوان مثال، یک طبقه‌بندی کننده تصویر با استفاده از سه لایه متراکم می‌تواند در Keras به صورت زیر نوشته شود:

model = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape=[28, 28, 1]),
    tf.keras.layers.Dense(200, activation="relu"),
    tf.keras.layers.Dense(60, activation="relu"),
    tf.keras.layers.Dense(10, activation='softmax') # classifying into 10 classes
])

# this configures the training of the model. Keras calls it "compiling" the model.
model.compile(
  optimizer='adam',
  loss= 'categorical_crossentropy',
  metrics=['accuracy']) # % of correct answers

# train the model
model.fit(dataset, ... )

یک لایه متراکم

ارقام دست نویس در مجموعه داده MNIST تصاویر 28x28 پیکسل در مقیاس خاکستری هستند. ساده ترین روش برای طبقه بندی آنها استفاده از 28x28=784 پیکسل به عنوان ورودی برای یک شبکه عصبی 1 لایه است.

اسکرین شات 26/07/2016 در 12.32.24.png

هر «نورون» در یک شبکه عصبی، مجموع وزنی همه ورودی‌های خود را انجام می‌دهد، ثابتی به نام «بایاس» اضافه می‌کند و سپس نتیجه را از طریق «تابع فعال‌سازی» غیرخطی تغذیه می‌کند. "وزن" و "سوگیری" پارامترهایی هستند که از طریق آموزش مشخص می شوند. آنها در ابتدا با مقادیر تصادفی مقداردهی اولیه می شوند.

تصویر بالا نشان دهنده یک شبکه عصبی 1 لایه با 10 نورون خروجی است زیرا می خواهیم ارقام را به 10 کلاس (0 تا 9) طبقه بندی کنیم.

با ضرب ماتریس

در اینجا آمده است که چگونه یک لایه شبکه عصبی، پردازش مجموعه ای از تصاویر، را می توان با ضرب ماتریس نشان داد:

matmul.gif

با استفاده از اولین ستون وزن ها در ماتریس وزن W، مجموع وزنی تمام پیکسل های تصویر اول را محاسبه می کنیم. این مجموع مربوط به اولین نورون است. با استفاده از ستون دوم وزن ها، همین کار را برای نورون دوم انجام می دهیم و تا نرون دهم به همین ترتیب ادامه می دهیم. سپس می توانیم این عملیات را برای 99 تصویر باقیمانده تکرار کنیم. اگر X را ماتریس حاوی 100 تصویر خود بنامیم، تمام مجموع وزنی 10 نورون ما که بر روی 100 تصویر محاسبه شده است، صرفاً XW است، یک ضرب ماتریس.

اکنون هر نورون باید بایاس خود (یک ثابت) را اضافه کند. از آنجایی که ما 10 نورون داریم، 10 ثابت بایاس داریم. این بردار 10 مقداری را b می نامیم. باید به هر خط از ماتریس محاسبه شده قبلی اضافه شود. با استفاده از کمی جادو به نام "پخش" این را با یک علامت مثبت ساده می نویسیم.

ما در نهایت یک تابع فعال سازی را اعمال می کنیم، به عنوان مثال "softmax" (در زیر توضیح داده شده است) و فرمولی را که یک شبکه عصبی 1 لایه ای را توصیف می کند، به دست می آوریم که برای 100 تصویر اعمال می شود:

اسکرین شات 26/07/2016 در 16.02.36.png

در کراس

با کتابخانه های شبکه عصبی سطح بالا مانند Keras، نیازی به پیاده سازی این فرمول نخواهیم داشت. با این حال، درک این نکته مهم است که یک لایه شبکه عصبی فقط یک دسته از ضرب و جمع است. در Keras، یک لایه متراکم به صورت زیر نوشته می شود:

tf.keras.layers.Dense(10, activation='softmax')

رفتن به عمق

زنجیر کردن لایه های شبکه عصبی امری بی اهمیت است. لایه اول مجموع وزنی پیکسل ها را محاسبه می کند. لایه های بعدی مجموع وزنی خروجی های لایه های قبلی را محاسبه می کنند.

تنها تفاوت، جدا از تعداد نورون ها، انتخاب تابع فعال سازی خواهد بود.

توابع فعال سازی: relu، softmax و sigmoid

شما معمولاً از تابع فعال سازی "relu" برای همه لایه ها به جز آخرین لایه استفاده می کنید. آخرین لایه، در یک طبقه بندی، از فعال سازی "softmax" استفاده می کند.

دوباره، یک "نورون" مجموع وزنی همه ورودی های خود را محاسبه می کند، مقداری به نام "بایاس" را اضافه می کند و نتیجه را از طریق تابع فعال سازی تغذیه می کند.

محبوب ترین تابع فعال سازی "RELU" برای واحد خطی اصلاح شده نام دارد. این یک تابع بسیار ساده است همانطور که در نمودار بالا می بینید.

تابع فعال سازی سنتی در شبکه های عصبی "سیگموئید" بود، اما نشان داده شد که "relu" تقریباً در همه جا دارای خواص همگرایی بهتری است و اکنون ترجیح داده می شود.

فعال سازی Softmax برای طبقه بندی

آخرین لایه شبکه عصبی ما دارای 10 نورون است زیرا می خواهیم ارقام دست نویس را به 10 کلاس طبقه بندی کنیم (0,..9). باید 10 عدد بین 0 و 1 را خروجی دهد که نشان دهنده احتمال 0، 1، 2 و غیره بودن این رقم است. برای این کار، در آخرین لایه، از یک تابع فعال سازی به نام "softmax" استفاده می کنیم.

اعمال Softmax بر روی یک بردار با گرفتن نمایی هر عنصر و سپس عادی سازی بردار انجام می شود، معمولاً با تقسیم آن بر هنجار "L1" آن (یعنی مجموع مقادیر مطلق) به طوری که مقادیر نرمال شده به 1 می رسد و می توان آن را به صورت تفسیر کرد. احتمالات

خروجی آخرین لایه، قبل از فعال سازی گاهی اوقات "logits" نامیده می شود. اگر این بردار L = [L0، L1، L2، L3، L4، L5، L6، L7، L8، L9] باشد، آنگاه:

از دست دادن آنتروپی متقابل

اکنون که شبکه عصبی ما پیش‌بینی‌هایی را از تصاویر ورودی تولید می‌کند، باید میزان خوب بودن آن‌ها را اندازه‌گیری کنیم، یعنی فاصله بین آنچه شبکه به ما می‌گوید و پاسخ‌های صحیح، که اغلب «برچسب‌ها» نامیده می‌شوند. به یاد داشته باشید که ما برچسب های درستی برای تمام تصاویر موجود در مجموعه داده داریم.

هر فاصله ای کار می کند، اما برای مشکلات طبقه بندی، به اصطلاح "فاصله آنتروپی متقابل" موثرترین است. ما این تابع خطا یا "از دست دادن" خود را می نامیم:

نزول گرادیان

"آموزش" شبکه عصبی در واقع به معنای استفاده از تصاویر و برچسب های آموزشی برای تنظیم وزن ها و سوگیری ها به گونه ای است که تابع تلفات متقابل آنتروپی را به حداقل برساند. هم اکنون به چگونگی کارکرد آن می پردازیم.

آنتروپی متقاطع تابعی از وزن ها، بایاس ها، پیکسل های تصویر تمرینی و کلاس شناخته شده آن است.

اگر مشتقات جزئی آنتروپی متقاطع را نسبت به همه وزن‌ها و همه بایاس‌ها محاسبه کنیم، یک "gradient" به دست می‌آوریم که برای تصویر، برچسب و ارزش فعلی وزن‌ها و بایاس‌ها محاسبه می‌شود. به یاد داشته باشید که ما می‌توانیم میلیون‌ها وزن و سوگیری داشته باشیم، بنابراین محاسبه گرادیان کار زیادی به نظر می‌رسد. خوشبختانه، TensorFlow این کار را برای ما انجام می دهد. ویژگی ریاضی یک گرادیان این است که به سمت بالا اشاره می کند. از آنجایی که می خواهیم به جایی برویم که آنتروپی متقاطع کم است، در جهت مخالف می رویم. وزن ها و سوگیری ها را با کسری از گرادیان به روز می کنیم. سپس همین کار را بارها و بارها با استفاده از دسته های بعدی تصاویر و برچسب های آموزشی در یک حلقه آموزشی انجام می دهیم. امیدواریم که این به جایی برسد که آنتروپی متقاطع حداقل باشد، اگرچه هیچ چیزی منحصر به فرد بودن این حداقل را تضمین نمی کند.

گرادیان descent2.png

مینی بچینگ و تکانه

شما می‌توانید گرادیان خود را فقط بر روی یک تصویر مثال محاسبه کنید و وزن‌ها و بایاس‌ها را فوراً به‌روزرسانی کنید، اما انجام این کار روی دسته‌ای از مثلاً 128 تصویر، گرادیانی به دست می‌دهد که محدودیت‌های اعمال‌شده توسط تصاویر نمونه‌های مختلف را بهتر نشان می‌دهد و بنابراین احتمالاً همگرا می‌شود. به سمت راه حل سریعتر اندازه مینی بچ یک پارامتر قابل تنظیم است.

این تکنیک، که گاهی اوقات "نزول گرادیان تصادفی" نامیده می‌شود، یک مزیت عملی‌تر دیگر نیز دارد: کار با دسته‌ها همچنین به معنای کار با ماتریس‌های بزرگ‌تر است و معمولاً بهینه‌سازی آنها روی GPU و TPU آسان‌تر است.

با این حال، همگرایی می تواند کمی آشفته باشد و حتی اگر بردار گرادیان صفر باشد، می تواند متوقف شود. آیا این بدان معناست که ما حداقلی را پیدا کرده ایم؟ نه همیشه. یک مولفه گرادیان می تواند در حداقل یا حداکثر صفر باشد. با یک بردار گرادیان با میلیون‌ها عنصر، اگر همه آنها صفر باشند، احتمال اینکه هر صفر با یک حداقل و هیچ یک از آنها به نقطه حداکثر مطابقت داشته باشد بسیار کم است. در فضایی با ابعاد مختلف، نقاط زین بسیار رایج هستند و ما نمی خواهیم در آنها متوقف شویم.

تصویر: یک نقطه زین. گرادیان 0 است اما در همه جهات حداقل نیست. (اشاره به تصویر ویکی مدیا: توسط Nicoguaro - Own Work, CC BY 3.0 )

راه حل این است که مقداری حرکت به الگوریتم بهینه سازی اضافه شود تا بتواند بدون توقف از نقاط زین عبور کند.

واژه نامه

دسته ای یا مینی دسته ای : آموزش همیشه بر روی دسته ای از داده ها و برچسب های آموزشی انجام می شود. انجام این کار به همگرایی الگوریتم کمک می کند. بعد "دسته ای" معمولاً اولین بعد تانسورهای داده است. برای مثال یک تانسور شکل [100، 192، 192، 3] حاوی 100 تصویر 192x192 پیکسل با سه مقدار در هر پیکسل (RGB) است.

از دست دادن متقابل آنتروپی : یک تابع تلفات ویژه که اغلب در طبقه‌بندی‌کننده‌ها استفاده می‌شود.

لایه متراکم : لایه ای از نورون ها که در آن هر نورون به تمام نورون های لایه قبلی متصل است.

ویژگی ها: ورودی های یک شبکه عصبی گاهی اوقات "ویژگی" نامیده می شود. هنر فهمیدن اینکه کدام بخش از یک مجموعه داده (یا ترکیبی از قطعات) باید به شبکه عصبی وارد شود تا پیش‌بینی‌های خوبی داشته باشیم، «مهندسی ویژگی» نامیده می‌شود.

برچسب ها : نام دیگری برای "کلاس ها" یا پاسخ های صحیح در یک مشکل طبقه بندی نظارت شده

نرخ یادگیری : کسری از گرادیان که توسط آن وزن ها و بایاس ها در هر تکرار از حلقه آموزشی به روز می شوند.

logits : خروجی های لایه ای از نورون ها قبل از اعمال تابع فعال سازی "logits" نامیده می شوند. این اصطلاح از "عملکرد لجستیک" با نام "تابع سیگموئید" می آید که در گذشته محبوب ترین تابع فعال سازی بود. "خروجی های نورون قبل از عملکرد لجستیک" به "logits" کوتاه شد.

ضرر : تابع خطا در مقایسه خروجی های شبکه عصبی با پاسخ های صحیح

نورون : مجموع وزنی ورودی های خود را محاسبه می کند، بایاس اضافه می کند و نتیجه را از طریق یک تابع فعال سازی تغذیه می کند.

کدگذاری تک داغ : کلاس 3 از 5 به عنوان بردار 5 عنصری کدگذاری می شود، همه صفرها به جز سومی که 1 است.

relu : واحد خطی اصلاح شده. یک تابع فعال سازی محبوب برای نورون ها.

sigmoid : یکی دیگر از عملکردهای فعال سازی که قبلاً محبوب بود و هنوز هم در موارد خاص مفید است.

softmax : یک تابع فعال‌سازی ویژه که بر روی یک بردار عمل می‌کند، اختلاف بین بزرگترین مؤلفه و سایر مؤلفه‌ها را افزایش می‌دهد و همچنین بردار را با مجموع 1 نرمال می‌کند تا بتوان آن را به عنوان بردار احتمالات تفسیر کرد. به عنوان آخرین مرحله در طبقه بندی کننده ها استفاده می شود.

تانسور : یک "تانسور" مانند یک ماتریس است اما با تعداد دلخواه ابعاد. یک تانسور یک بعدی یک بردار است. یک تانسور دو بعدی یک ماتریس است. و سپس می توانید تانسورهایی با ابعاد 3، 4، 5 یا بیشتر داشته باشید.

به دفترچه مطالعه برگردیم و این بار، کد را بخوانیم.

keras_01_mnist.ipynb

بیایید تمام سلول های این دفترچه را مرور کنیم.

سلول "پارامترها"

اندازه دسته ای، تعداد دوره های آموزشی و مکان فایل های داده در اینجا تعریف شده است. فایل های داده در یک سطل Google Cloud Storage (GCS) میزبانی می شوند، به همین دلیل آدرس آنها با gs:// شروع می شود.

سلول "واردات"

تمام کتابخانه‌های ضروری پایتون، از جمله TensorFlow و همچنین matplotlib برای تجسم، در اینجا وارد می‌شوند.

سلول " تصویرسازی ابزار [RUN ME] "

این سلول حاوی کد تجسم غیر جالب است. به طور پیش‌فرض جمع‌شده است، اما می‌توانید با دوبار کلیک کردن روی آن، آن را باز کنید و زمانی که وقت داشتید به کد نگاه کنید.

سلول " tf.data.Dataset: تجزیه فایل ها و آماده سازی مجموعه داده های آموزشی و اعتبار سنجی "

این سلول از tf.data.Dataset API برای بارگیری مجموعه داده های MNIST از فایل های داده استفاده می کند. نیازی به صرف زمان زیاد برای این سلول نیست. اگر به tf.data.Dataset API علاقه مند هستید، در اینجا آموزشی وجود دارد که آن را توضیح می دهد: خطوط لوله داده با سرعت TPU . در حال حاضر، اصول اولیه عبارتند از:

تصاویر و برچسب ها (پاسخ های صحیح) از مجموعه داده های MNIST در رکوردهای طول ثابت در 4 فایل ذخیره می شوند. فایل ها را می توان با تابع ضبط ثابت اختصاصی بارگذاری کرد:

imagedataset = tf.data.FixedLengthRecordDataset(image_filename, 28*28, header_bytes=16)

اکنون مجموعه داده ای از بایت های تصویر داریم. آنها باید در تصاویر رمزگشایی شوند. ما یک تابع برای این کار تعریف می کنیم. تصویر فشرده نشده است، بنابراین تابع نیازی به رمزگشایی چیزی ندارد ( decode_raw اساساً هیچ کاری انجام نمی دهد). سپس تصویر به مقادیر ممیز شناور بین 0 و 1 تبدیل می‌شود. می‌توانیم آن را در اینجا به‌عنوان یک تصویر دو بعدی تغییر شکل دهیم، اما در واقع آن را به‌عنوان یک آرایه مسطح از پیکسل‌هایی با اندازه‌های 28*28 نگه می‌داریم زیرا این همان چیزی است که لایه متراکم اولیه ما انتظار دارد.

def read_image(tf_bytestring):
    image = tf.decode_raw(tf_bytestring, tf.uint8)
    image = tf.cast(image, tf.float32)/256.0
    image = tf.reshape(image, [28*28])
    return image

این تابع را با استفاده از .map به مجموعه داده اعمال می کنیم و مجموعه داده ای از تصاویر را به دست می آوریم:

imagedataset = imagedataset.map(read_image, num_parallel_calls=16)

ما همان نوع خواندن و رمزگشایی را برای برچسب ها انجام می دهیم و تصاویر و برچسب ها را با هم .zip می کنیم:

dataset = tf.data.Dataset.zip((imagedataset, labelsdataset))

اکنون مجموعه داده ای از جفت ها (تصویر، برچسب) داریم. این چیزی است که مدل ما انتظار دارد. ما هنوز کاملاً آماده استفاده از آن در عملکرد آموزشی نیستیم:

dataset = dataset.cache()
dataset = dataset.shuffle(5000, reshuffle_each_iteration=True)
dataset = dataset.repeat()
dataset = dataset.batch(batch_size)
dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)

tf.data.Dataset API تمام تابع کاربردی لازم برای تهیه مجموعه داده ها را دارد:

.cache مجموعه داده را در RAM ذخیره می کند. این یک مجموعه داده کوچک است بنابراین کار خواهد کرد. .shuffle آن را با یک بافر از 5000 عنصر به هم می زند. مهم است که داده های آموزشی به خوبی در هم ریخته شوند. .repeat مجموعه داده را حلقه می کند. ما چندین بار (دوره های متعدد) در مورد آن آموزش خواهیم داد. .batch چندین تصویر و برچسب را با هم در یک mini-natch جمع می کند. در نهایت، .prefetch می تواند از CPU برای آماده سازی دسته بعدی در حالی که دسته فعلی در GPU در حال آموزش است استفاده کند.

مجموعه داده اعتبارسنجی به روشی مشابه تهیه شده است. اکنون آماده تعریف یک مدل و استفاده از این مجموعه داده برای آموزش آن هستیم.

سلول "مدل کراس"

همه مدل‌های ما دنباله‌ای مستقیم از لایه‌ها خواهند بود، بنابراین می‌توانیم از سبک tf.keras.Sequential برای ایجاد آنها استفاده کنیم. در ابتدا در اینجا، این یک لایه متراکم است. این 10 نورون دارد زیرا ما ارقام دست نویس را به 10 کلاس طبقه بندی می کنیم. از فعال سازی "softmax" استفاده می کند زیرا آخرین لایه در یک طبقه بندی است.

یک مدل Keras همچنین باید شکل ورودی های خود را بداند. می توان از tf.keras.layers.Input برای تعریف آن استفاده کرد. در اینجا، بردارهای ورودی، بردارهای مسطح با مقادیر پیکسل به طول 28*28 هستند.

model = tf.keras.Sequential(
  [
    tf.keras.layers.Input(shape=(28*28,)),
    tf.keras.layers.Dense(10, activation='softmax')
  ])

model.compile(optimizer='sgd',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# print model layers
model.summary()

# utility callback that displays training curves
plot_training = PlotTraining(sample_rate=10, zoom=1)

پیکربندی مدل در Keras با استفاده از تابع model.compile انجام می شود. در اینجا ما از بهینه ساز اصلی 'sgd' (Stochastic Gradient Descent) استفاده می کنیم. یک مدل طبقه بندی به یک تابع از دست دادن آنتروپی متقابل نیاز دارد که در Keras 'categorical_crossentropy' نامیده می شود. در نهایت، از مدل می‌خواهیم متریک 'accuracy' را محاسبه کند، که درصدی از تصاویر طبقه‌بندی شده درست است.

Keras ابزار بسیار زیبا model.summary() را ارائه می دهد که جزئیات مدلی را که ایجاد کرده اید چاپ می کند. مربی مهربان شما ابزار PlotTraining را اضافه کرده است (تعریف شده در سلول "ابزارهای تجسمی") که منحنی های آموزشی مختلف را در طول آموزش نمایش می دهد.

سلول "آموزش و اعتبارسنجی مدل"

این جایی است که آموزش با فراخوانی model.fit و پاس کردن در هر دو مجموعه داده آموزشی و اعتبارسنجی اتفاق می‌افتد. به‌طور پیش‌فرض، Keras یک دور اعتبارسنجی را در پایان هر دوره اجرا می‌کند.

model.fit(training_dataset, steps_per_epoch=steps_per_epoch, epochs=EPOCHS,
          validation_data=validation_dataset, validation_steps=1,
          callbacks=[plot_training])

در Keras امکان افزودن رفتارهای سفارشی در حین آموزش با استفاده از callback وجود دارد. به این ترتیب طرح آموزشی به روز رسانی پویا برای این کارگاه اجرا شد.

سلول "تجسم پیش بینی ها"

هنگامی که مدل آموزش داده شد، می توانیم با فراخوانی model.predict() از آن پیش بینی دریافت کنیم:

probabilities = model.predict(font_digits, steps=1)
predicted_labels = np.argmax(probabilities, axis=1)

در اینجا مجموعه‌ای از ارقام چاپ شده از فونت‌های محلی را به‌عنوان آزمایشی آماده کرده‌ایم. به یاد داشته باشید که شبکه عصبی بردار 10 احتمال را از "softmax" نهایی خود برمی گرداند. برای دریافت برچسب، باید دریابیم که کدام احتمال بالاترین است. np.argmax از کتابخانه numpy این کار را انجام می دهد.

برای درک اینکه چرا پارامتر axis=1 مورد نیاز است، لطفاً به یاد داشته باشید که ما مجموعه ای از 128 تصویر را پردازش کرده ایم و بنابراین مدل 128 بردار احتمال را برمی گرداند. شکل تانسور خروجی [128، 10] است. ما argmax را در 10 احتمال بازگشتی برای هر تصویر محاسبه می کنیم، بنابراین axis=1 (محور اول 0 است).

این مدل ساده از قبل 90 درصد ارقام را تشخیص می دهد. بد نیست، اما شما اکنون این را به میزان قابل توجهی بهبود خواهید داد.

godeep.png

برای بهبود دقت تشخیص، لایه های بیشتری را به شبکه عصبی اضافه می کنیم.

اسکرین شات 27/07/2016 در 15.36.55.png

ما softmax را به عنوان تابع فعال‌سازی در آخرین لایه نگه می‌داریم زیرا این همان چیزی است که برای طبقه‌بندی بهتر عمل می‌کند. با این حال، در لایه‌های میانی از کلاسیک‌ترین تابع فعال‌سازی استفاده خواهیم کرد: سیگموئید:

برای مثال، مدل شما می‌تواند به این شکل باشد (کاما را فراموش نکنید، tf.keras.Sequential . Sequential یک لیست از لایه‌های جدا شده با کاما می‌گیرد):

model = tf.keras.Sequential(
  [
      tf.keras.layers.Input(shape=(28*28,)),
      tf.keras.layers.Dense(200, activation='sigmoid'),
      tf.keras.layers.Dense(60, activation='sigmoid'),
      tf.keras.layers.Dense(10, activation='softmax')
  ])

به "خلاصه" مدل خود نگاه کنید. اکنون حداقل 10 برابر پارامترهای بیشتری دارد. باید 10 برابر بهتر باشد! اما بنا به دلایلی اینطور نیست ...

به نظر می رسد باخت از پشت بام نیز عبور کرده است. چیزی کاملا درست نیست.

شما به تازگی شبکه های عصبی را تجربه کرده اید، همانطور که مردم در دهه های 80 و 90 آنها را طراحی می کردند. جای تعجب نیست که آنها از این ایده دست کشیدند و به اصطلاح "زمستان هوش مصنوعی" را آغاز کردند. در واقع، با اضافه کردن لایه‌ها، شبکه‌های عصبی مشکلات بیشتری برای همگرایی دارند.

به نظر می رسد که شبکه های عصبی عمیق با لایه های زیاد (20، 50، حتی 100 لایه امروزی) می توانند واقعاً خوب کار کنند، چند ترفند کثیف ریاضی برای همگرا کردن آنها. کشف این ترفندهای ساده یکی از دلایل تجدید حیات یادگیری عمیق در دهه 2010 است.

فعال سازی RELU

relu.png

تابع فعال سازی سیگموئید در واقع در شبکه های عمیق کاملاً مشکل ساز است. تمام مقادیر بین 0 و 1 را له می کند و وقتی این کار را به طور مکرر انجام دهید، خروجی های نورون و گرادیان آنها می توانند به طور کامل ناپدید شوند. به دلایل تاریخی ذکر شد، اما شبکه های مدرن از RELU (واحد خطی اصلاح شده) استفاده می کنند که به شکل زیر است:

از طرف دیگر relu مشتق 1 دارد، حداقل در سمت راست آن. با فعال‌سازی RELU، حتی اگر گرادیان‌هایی که از برخی نورون‌ها می‌آیند صفر باشد، همیشه دیگران شیب غیر صفر واضحی را ارائه می‌دهند و آموزش می‌تواند با سرعت خوبی ادامه یابد.

بهینه ساز بهتر

در فضاهای با ابعاد بسیار بالا مانند اینجا - ما در حدود 10K وزن و بایاس داریم - "نقاط زین" مکرر است. اینها نقاطی هستند که حداقل های محلی نیستند، اما با این حال شیب صفر است و بهینه ساز نزول گرادیان در آنجا گیر می کند. TensorFlow دارای مجموعه ای کامل از بهینه سازهای موجود است، از جمله برخی از آنها که با مقداری اینرسی کار می کنند و با خیال راحت از نقاط زین عبور می کنند.

مقداردهی اولیه تصادفی

هنر اولیه سازی سوگیری های وزنه ها قبل از تمرین به خودی خود یک حوزه تحقیقاتی است و مقالات متعددی در این زمینه منتشر شده است. در اینجا می توانید نگاهی به تمام اولیه سازهای موجود در Keras بیندازید. خوشبختانه Keras به طور پیش فرض کار درست را انجام می دهد و از مقداردهی اولیه 'glorot_uniform' استفاده می کند که تقریباً در همه موارد بهترین است.

هیچ کاری برای شما وجود ندارد، زیرا Keras قبلاً کار درست را انجام می دهد.

NaN ???

فرمول آنتروپی متقابل شامل یک لگاریتم است و log(0) عددی نیست (NaN، اگر ترجیح می دهید یک تصادف عددی است). آیا ورودی آنتروپی متقاطع می تواند 0 باشد؟ ورودی از softmax می آید که اساساً نمایی است و نمایی هرگز صفر نیست. پس ما در امان هستیم!

واقعا؟ در دنیای زیبای ریاضیات، ما ایمن خواهیم بود، اما در دنیای کامپیوتر، exp(-150)، که در قالب float32 نشان داده شده است، به اندازه صفر است و آنتروپی متقاطع خراب می شود.

خوشبختانه، در اینجا نیز کاری برای انجام دادن وجود ندارد، زیرا Keras از این کار مراقبت می کند و نرم افزار مکس و متقاطع آنتروپی را به روشی بسیار دقیق محاسبه می کند تا از ثبات عددی اطمینان حاصل کند و از NaN های مخوف جلوگیری کند.

موفقیت؟

اکنون باید به دقت 97 درصد رسیده باشید. هدف در این کارگاه این است که به طور قابل توجهی به بالای 99 درصد برسیم، پس بیایید ادامه دهیم.

اگر گیر کرده اید، در اینجا راه حل وجود دارد:

keras_02_mnist_dense.ipynb

شاید بتوانیم سعی کنیم سریعتر تمرین کنیم؟ نرخ یادگیری پیش فرض در بهینه ساز Adam 0.001 است. بیایید سعی کنیم آن را افزایش دهیم.

به نظر می رسد تندتر رفتن کمک زیادی نمی کند و این همه سروصدا چیست؟

منحنی های آموزشی واقعاً پر سر و صدا هستند و به هر دو منحنی اعتبار سنجی نگاه می کنند: آنها در حال بالا و پایین پریدن هستند. این به این معنی است که ما خیلی سریع پیش می رویم. ما می توانستیم به سرعت قبلی خود برگردیم، اما راه بهتری وجود دارد.

slow down.png

راه حل خوب این است که سریع شروع کنید و سرعت یادگیری را به صورت تصاعدی کاهش دهید. در Keras، می‌توانید این کار را با پاسخ تماس tf.keras.callbacks.LearningRateScheduler انجام دهید.

کد مفید برای کپی پیست:

# lr decay function
def lr_decay(epoch):
  return 0.01 * math.pow(0.6, epoch)

# lr schedule callback
lr_decay_callback = tf.keras.callbacks.LearningRateScheduler(lr_decay, verbose=True)

# important to see what you are doing
plot_learning_rate(lr_decay, EPOCHS)

فراموش نکنید که از lr_decay_callback که ایجاد کرده اید استفاده کنید. آن را به لیست تماس‌های برگشتی در model.fit :

model.fit(...,  callbacks=[plot_training, lr_decay_callback])

تاثیر این تغییر کوچک دیدنی است. می بینید که بیشتر نویز از بین رفته است و دقت تست اکنون بالای 98 درصد است.

اکنون به نظر می رسد که مدل به خوبی همگرا شده است. بیایید سعی کنیم حتی عمیق تر برویم.

کمک می کند؟

نه واقعاً، دقت هنوز در 98٪ گیر کرده است و به ضرر اعتبارسنجی نگاه کنید. داره بالا میره! الگوریتم یادگیری فقط بر روی داده های آموزشی کار می کند و بر این اساس از دست دادن آموزش را بهینه می کند. هیچ‌گاه داده‌های اعتبارسنجی را نمی‌بیند، بنابراین جای تعجب نیست که پس از مدتی کار آن دیگر تأثیری بر از دست دادن اعتبارسنجی ندارد که متوقف می‌شود و حتی گاهی اوقات به عقب بازمی‌گردد.

این فوراً بر قابلیت‌های تشخیص دنیای واقعی مدل شما تأثیر نمی‌گذارد، اما از اجرای بسیاری از تکرارها جلوگیری می‌کند و به طور کلی نشانه آن است که آموزش دیگر تأثیر مثبتی ندارد.

dropout.png

به این قطع ارتباط معمولاً «بیش از حد مناسب» می گویند و وقتی آن را می بینید، می توانید از یک تکنیک منظم سازی به نام «ترک کردن» استفاده کنید. تکنیک ترک تحصیل در هر تکرار آموزشی به نورون های تصادفی شلیک می کند.

Did it work?

Noise reappears (unsurprisingly given how dropout works). The validation loss does not seem to be creeping up anymore, but it is higher overall than without dropout. And the validation accuracy went down a bit. This is a fairly disappointing result.

It looks like dropout was not the correct solution, or maybe "overfitting" is a more complex concept and some of its causes are not amenable to a "dropout" fix?

What is "overfitting"? Overfitting happens when a neural network learns "badly", in a way that works for the training examples but not so well on real-world data. There are regularisation techniques like dropout that can force it to learn in a better way but overfitting also has deeper roots.

overfitting.png

Basic overfitting happens when a neural network has too many degrees of freedom for the problem at hand. Imagine we have so many neurons that the network can store all of our training images in them and then recognise them by pattern matching. It would fail on real-world data completely. A neural network must be somewhat constrained so that it is forced to generalise what it learns during training.

If you have very little training data, even a small network can learn it by heart and you will see "overfitting". Generally speaking, you always need lots of data to train neural networks.

Finally, if you have done everything by the book, experimented with different sizes of network to make sure its degrees of freedom are constrained, applied dropout, and trained on lots of data you might still be stuck at a performance level that nothing seems to be able to improve. This means that your neural network, in its present shape, is not capable of extracting more information from your data, as in our case here.

Remember how we are using our images, flattened into a single vector? That was a really bad idea. Handwritten digits are made of shapes and we discarded the shape information when we flattened the pixels. However, there is a type of neural network that can take advantage of shape information: convolutional networks. Let us try them.

If you are stuck, here is the solution at this point:

keras_03_mnist_dense_lrdecay_dropout.ipynb

In a nutshell

If all the terms in bold in the next paragraph are already known to you, you can move to the next exercise. If your are just starting out with convolutional neural networks, please read on.

convolutional.gif

Illustration: filtering an image with two successive filters made of 4x4x3=48 learnable weights each.

This is how a simple convolutional neural network looks in Keras:

model = tf.keras.Sequential([
    tf.keras.layers.Reshape(input_shape=(28*28,), target_shape=(28, 28, 1)),
    tf.keras.layers.Conv2D(kernel_size=3, filters=12, activation='relu'),
    tf.keras.layers.Conv2D(kernel_size=6, filters=24, strides=2, activation='relu'),
    tf.keras.layers.Conv2D(kernel_size=6, filters=32, strides=2, activation='relu'),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(10, activation='softmax')
])

In a layer of a convolutional network, one "neuron" does a weighted sum of the pixels just above it, across a small region of the image only. It adds a bias and feeds the sum through an activation function, just as a neuron in a regular dense layer would. This operation is then repeated across the entire image using the same weights. Remember that in dense layers, each neuron had its own weights. Here, a single "patch" of weights slides across the image in both directions (a "convolution"). The output has as many values as there are pixels in the image (some padding is necessary at the edges though). It is a filtering operation. In the illustration above, it uses a filter of 4x4x3=48 weights.

However, 48 weights will not be enough. To add more degrees of freedom, we repeat the same operation with a new set of weights. This produces a new set of filter outputs. Let's call it a "channel" of outputs by analogy with the R,G,B channels in the input image.

Screen Shot 2016-07-29 at 16.02.37.png

The two (or more) sets of weights can be summed up as one tensor by adding a new dimension. This gives us the generic shape of the weights tensor for a convolutional layer. Since the number of input and output channels are parameters, we can start stacking and chaining convolutional layers.

Illustration: a convolutional neural network transforms "cubes" of data into other "cubes" of data.

Strided convolutions, max pooling

By performing the convolutions with a stride of 2 or 3, we can also shrink the resulting data cube in its horizontal dimensions. There are two common ways of doing this:

  • Strided convolution: a sliding filter as above but with a stride >1
  • Max pooling: a sliding window applying the MAX operation (typically on 2x2 patches, repeated every 2 pixels)

Illustration: sliding the computing window by 3 pixels results in fewer output values. Strided convolutions or max pooling (max on a 2x2 window sliding by a stride of 2) are a way of shrinking the data cube in the horizontal dimensions.

The final layer

After the last convolutional layer, the data is in the form of a "cube". There are two ways of feeding it through the final dense layer.

The first one is to flatten the cube of data into a vector and then feed it to the softmax layer. Sometimes, you can even add a dense layer before the softmax layer. This tends to be expensive in terms of the number of weights. A dense layer at the end of a convolutional network can contain more than half the weights of the whole neural network.

Instead of using an expensive dense layer, we can also split the incoming data "cube" into as many parts as we have classes, average their values and feed these through a softmax activation function. This way of building the classification head costs 0 weights. In Keras, there is a layer for this: tf.keras.layers.GlobalAveragePooling2D() .

Jump to the next section to build a convolutional network for the problem at hand.

Let us build a convolutional network for handwritten digit recognition. We will use three convolutional layers at the top, our traditional softmax readout layer at the bottom and connect them with one fully-connected layer:

Notice that the second and third convolutional layers have a stride of two which explains why they bring the number of output values down from 28x28 to 14x14 and then 7x7.

Let's write the Keras code.

Special attention is needed before the first convolutional layer. Indeed, it expects a 3D 'cube' of data but our dataset has so far been set up for dense layers and all the pixels of the images are flattened into a vector. We need to reshape them back into 28x28x1 images (1 channel for grayscale images):

tf.keras.layers.Reshape(input_shape=(28*28,), target_shape=(28, 28, 1))

You can use this line instead of the tf.keras.layers.Input layer you had up to now.

In Keras, the syntax for a 'relu'-activated convolutional layer is:

tf.keras.layers.Conv2D(kernel_size=3, filters=12, padding='same', activation='relu')

For a strided convolution, you would write:

tf.keras.layers.Conv2D(kernel_size=6, filters=24, padding='same', activation='relu', strides=2)

To flatten a cube of data into a vector so that it can be consumed by a dense layer:

tf.keras.layers.Flatten()

And for dense layer, the syntax has not changed:

tf.keras.layers.Dense(200, activation='relu')

Did your model break the 99% accuracy barrier? Pretty close... but look at the validation loss curve. Does this ring a bell?

Also look at the predictions. For the first time, you should see that most of the 10,000 test digits are now correctly recognized. Only about 4½ rows of misdetections remain (about 110 digits out of 10,000)

If you are stuck, here is the solution at this point:

keras_04_mnist_convolutional.ipynb

The previous training exhibits clear signs of overfitting (and still falls short of 99% accuracy). Should we try dropout again?

How did it go this time?

It looks like dropout has worked this time. The validation loss is not creeping up anymore and the final accuracy should be way above 99%. تبریک می گویم!

The first time we tried to apply dropout, we thought we had an overfitting problem, when in fact the problem was in the architecture of the neural network. We could not go further without convolutional layers and there is nothing dropout could do about that.

This time, it does look like overfitting was the cause of the problem and dropout actually helped. Remember, there are many things that can cause a disconnect between the training and validation loss curves, with the validation loss creeping up. Overfitting (too many degrees of freedom, used badly by the network) is only one of them. If your dataset is too small or the architecture of your neural network is not adequate, you might see a similar behavior on the loss curves, but dropout will not help.

Finally, let's try to add batch normalization.

That's the theory, in practice, just remember a couple of rules:

Let's play by the book for now and add a batch norm layer on each neural network layer but the last. Do not add it to the last "softmax" layer. It would not be useful there.

# Modify each layer: remove the activation from the layer itself.
# Set use_bias=False since batch norm will play the role of biases.
tf.keras.layers.Conv2D(..., use_bias=False),
# Batch norm goes between the layer and its activation.
# The scale factor can be turned off for Relu activation.
tf.keras.layers.BatchNormalization(scale=False, center=True),
# Finish with the activation.
tf.keras.layers.Activation('relu'),

How is the accuracy now?

With a little bit of tweaking (BATCH_SIZE=64, learning rate decay parameter 0.666, dropout rate on dense layer 0.3) and a bit of luck, you can get to 99.5%. The learning rate and dropout adjustments were done following the "best practices" for using batch norm:

  • Batch norm helps neural networks converge and usually allows you to train faster.
  • Batch norm is a regularizer. You can usually decrease the amount of dropout you use, or even not use dropout at all.

The solution notebook has a 99.5% training run:

keras_05_mnist_batch_norm.ipynb

You will find a cloud-ready version of the code in the mlengine folder on GitHub , along with instructions for running it on Google Cloud AI Platform . Before you can run this part, you will have to create a Google Cloud account and enable billing. The resources necessary to complete the lab should be less than a couple of dollars (assuming 1h of training time on one GPU). To prepare your account:

  1. Create a Google Cloud Platform project ( http://cloud.google.com/console ).
  2. Enable billing.
  3. Install the GCP command line tools ( GCP SDK here ).
  4. Create a Google Cloud Storage bucket (put in the region us-central1 ). It will be used to stage the training code and store your trained model.
  5. Enable the necessary APIs and request the necessary quotas (run the training command once and you should get error messages telling you what to enable).

You have built your first neural network and trained it all the way to 99% accuracy. The techniques learned along the way are not specific to the MNIST dataset, actually they are widely used when working with neural networks. As a parting gift, here is the "cliff's notes" card for the lab, in cartoon version. You can use it to remember what you have learned:

cliffs notes tensorflow lab.png

Next steps

  • After fully-connected and convolutional networks, you should have a look at recurrent neural networks .
  • To run your training or inference in the cloud on a distributed infrastructure, Google Cloud provides AI Platform .
  • Finally, we love feedback. Please tell us if you see something amiss in this lab or if you think it should be improved. We handle feedback through GitHub issues [ feedback link ].

HR.png

Martin Görner ID small.jpg

The author: Martin Görner

Twitter: @martin_gorner

All cartoon images in this lab copyright: alexpokusay / 123RF stock photos