۱. مرور کلی
در این آزمایش، شما با معماری مدرن کانولوشن آشنا خواهید شد و از دانش خود برای پیادهسازی یک کانولوشن ساده اما مؤثر به نام "squeezenet" استفاده خواهید کرد.
این آزمایشگاه شامل توضیحات نظری لازم در مورد شبکههای عصبی کانولوشن است و نقطه شروع خوبی برای توسعهدهندگانی است که در مورد یادگیری عمیق یاد میگیرند.
این آزمایشگاه بخش چهارم از مجموعه «کرس روی TPU» است. میتوانید آنها را به ترتیب زیر یا به صورت مستقل انجام دهید.
- خطوط لوله داده با سرعت TPU: tf.data.Dataset و TFRecords
- اولین مدل Keras شما، با یادگیری انتقالی
- شبکههای عصبی کانولوشن، با Keras و TPUها
- [این آزمایشگاه] شبکههای عصبی مصنوعی مدرن، squeezenet، Xception، با Keras و TPUها

آنچه یاد خواهید گرفت
- برای تسلط بر سبک تابعی Keras
- برای ساخت یک مدل با استفاده از معماری squeezenet
- استفاده از TPUها برای آموزش سریع و تکرار روی معماری شما
- برای پیادهسازی افزایش داده با tf.data.dataset
- برای تنظیم دقیق یک مدل بزرگ از پیش آموزش دیده (Xception) روی TPU
بازخورد
اگر در این آزمایشگاه کد، نکتهی نادرستی میبینید، لطفاً به ما بگویید. میتوانید از طریق GitHub issues [ لینک بازخورد ] بازخورد خود را ارائه دهید.
۲. شروع سریع Google Colaboratory
این آزمایشگاه از Google Collaboratory استفاده میکند و نیازی به راهاندازی از طرف شما ندارد. Colaboratory یک پلتفرم دفترچه یادداشت آنلاین برای اهداف آموزشی است. این پلتفرم آموزش رایگان CPU، GPU و TPU را ارائه میدهد.

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

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

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

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

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

دسترسی به مخازن ذخیرهسازی ابری گوگل خصوصی شما برای Colab امکانپذیر است، مشروط بر اینکه با یک حساب کاربری مجاز احراز هویت شوید. قطعه کد بالا فرآیند احراز هویت را آغاز میکند.
۳. [اطلاعات] واحدهای پردازش تنسور (TPU) چیستند؟
به طور خلاصه

کد آموزش یک مدل روی TPU در Keras (و در صورت عدم دسترسی به TPU، استفاده از GPU یا CPU):
try: # detect TPUs
tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()
strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
strategy = tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines
# use TPUStrategy scope to define model
with strategy.scope():
model = tf.keras.Sequential( ... )
model.compile( ... )
# train model normally on a tf.data.Dataset
model.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)
ما امروز از TPUها برای ساخت و بهینهسازی یک طبقهبندیکننده گل با سرعتهای تعاملی (دقیقه در هر اجرای آموزشی) استفاده خواهیم کرد.

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

تصویر: یک لایه شبکه عصبی متراکم به عنوان یک ضرب ماتریسی، با دستهای از هشت تصویر که به طور همزمان از طریق شبکه عصبی پردازش میشوند. لطفاً ضرب یک خط در ستون را اجرا کنید تا تأیید کنید که واقعاً مجموع وزنی تمام مقادیر پیکسلهای یک تصویر را انجام میدهد. لایههای کانولوشن را میتوان به عنوان ضرب ماتریسی نیز نمایش داد، اگرچه کمی پیچیدهتر است ( توضیح در اینجا، در بخش 1 ).
سختافزار
MXU و VPU
یک هسته TPU نسخه ۲ از یک واحد ضرب ماتریس (MXU) که ضربهای ماتریسی را اجرا میکند و یک واحد پردازش برداری (VPU) برای سایر وظایف مانند فعالسازیها، softmax و غیره ساخته شده است. VPU محاسبات float32 و int32 را مدیریت میکند. از سوی دیگر، MXU با فرمت ممیز شناور با دقت ترکیبی ۱۶ تا ۳۲ بیتی عمل میکند.

ممیز شناور با دقت ترکیبی و bfloat16
MXU ضرب ماتریسها را با استفاده از ورودیهای bfloat16 و خروجیهای float32 محاسبه میکند. جمعهای میانی با دقت float32 انجام میشوند.

آموزش شبکه عصبی معمولاً در برابر نویز ناشی از کاهش دقت ممیز شناور مقاوم است. مواردی وجود دارد که نویز حتی به همگرایی بهینهساز کمک میکند. دقت ممیز شناور ۱۶ بیتی به طور سنتی برای تسریع محاسبات استفاده شده است، اما فرمتهای float16 و float32 محدودههای بسیار متفاوتی دارند. کاهش دقت از float32 به float16 معمولاً منجر به سرریز و سرریز میشود. راهحلهایی وجود دارد، اما معمولاً برای کار کردن float16 به کار اضافی نیاز است.
به همین دلیل است که گوگل فرمت bfloat16 را در TPUها معرفی کرد. bfloat16 یک float32 کوتاه شده با دقیقاً همان بیتهای توان و محدوده float32 است. این، علاوه بر این واقعیت که TPUها ضرب ماتریسها را با دقت مختلط با ورودیهای bfloat16 اما خروجیهای float32 محاسبه میکنند، به این معنی است که معمولاً هیچ تغییر کدی برای بهرهمندی از افزایش عملکرد دقت کاهش یافته لازم نیست.
آرایه سیستولیک
MXU ضرب ماتریسها را در سختافزار با استفاده از معماری به اصطلاح «آرایه سیستولیک» پیادهسازی میکند که در آن عناصر داده از طریق آرایهای از واحدهای محاسباتی سختافزاری جریان مییابند. (در پزشکی، «سیستولیک» به انقباضات قلب و جریان خون اشاره دارد، در اینجا به جریان دادهها اشاره دارد.)
عنصر اساسی ضرب ماتریسی، حاصلضرب نقطهای بین یک خط از یک ماتریس و یک ستون از ماتریس دیگر است (به تصویر بالای این بخش مراجعه کنید). برای ضرب ماتریسی Y=X*W، یکی از عناصر حاصل به صورت زیر خواهد بود:
Y[2,0] = X[2,0]*W[0,0] + X[2,1]*W[1,0] + X[2,2]*W[2,0] + ... + X[2,n]*W[n,0]
در یک پردازنده گرافیکی (GPU)، میتوان این ضرب نقطهای را در یک "هسته" پردازنده گرافیکی (GPU) برنامهریزی کرد و سپس آن را روی هر تعداد "هسته" که به صورت موازی در دسترس هستند اجرا کرد تا سعی شود هر مقدار ماتریس حاصل را به طور همزمان محاسبه کرد. اگر ماتریس حاصل 128x128 بزرگ باشد، برای محاسبه همزمان هر مقدار ماتریس حاصل، به 128x128 = 16K "هسته" نیاز است که معمولاً امکانپذیر نیست. بزرگترین پردازندههای گرافیکی (GPU) حدود 4000 هسته دارند. از سوی دیگر، یک TPU از حداقل سختافزار برای واحدهای محاسباتی در MXU استفاده میکند: فقط bfloat16 x bfloat16 => float32 multibulators، نه چیز دیگری. اینها آنقدر کوچک هستند که یک TPU میتواند 16K از آنها را در یک MXU 128x128 پیادهسازی کند و این ضرب ماتریس را به صورت یکجا پردازش کند.

تصویر: آرایه سیستولیک MXU. عناصر محاسباتی، ضرب-انباشتگر هستند. مقادیر یک ماتریس در آرایه بارگذاری میشوند (نقاط قرمز). مقادیر ماتریس دیگر در آرایه جریان مییابند (نقاط خاکستری). خطوط عمودی مقادیر را به سمت بالا منتشر میکنند. خطوط افقی جمعهای جزئی را منتشر میکنند. به عنوان یک تمرین، به کاربر واگذار میشود تا تأیید کند که با جریان دادهها در آرایه، نتیجه ضرب ماتریس از سمت راست خارج میشود.
علاوه بر این، در حالی که ضربهای نقطهای در یک MXU محاسبه میشوند، جمعهای میانی به سادگی بین واحدهای محاسباتی مجاور جریان مییابند. آنها نیازی به ذخیره و بازیابی به/از حافظه یا حتی یک فایل رجیستر ندارند. نتیجه نهایی این است که معماری آرایه سیستولیک TPU هنگام محاسبه ضربهای ماتریسی، از نظر چگالی و قدرت و همچنین از نظر سرعت نسبت به GPU برتری قابل توجهی دارد که قابل چشمپوشی نیست.
TPU ابری
وقتی شما یک " Cloud TPU نسخه ۲" را در پلتفرم ابری گوگل درخواست میکنید، یک ماشین مجازی (VM) دریافت میکنید که دارای یک برد TPU متصل به PCI است. برد TPU دارای چهار تراشه TPU دو هستهای است. هر هسته TPU دارای یک VPU (واحد پردازش برداری) و یک MXU (واحد ضرب MatriX) با ابعاد ۱۲۸x۱۲۸ است. این "Cloud TPU" معمولاً از طریق شبکه به ماشین مجازی که آن را درخواست کرده است متصل میشود. بنابراین تصویر کامل به این شکل است:

تصویر: ماشین مجازی شما با یک شتابدهنده "Cloud TPU" متصل به شبکه. خود "Cloud TPU" از یک ماشین مجازی با یک برد TPU متصل به PCI با چهار تراشه TPU دو هستهای روی آن ساخته شده است.
غلافهای TPU
در مراکز داده گوگل، TPU ها به یک اتصال داخلی محاسبات با کارایی بالا (HPC) متصل هستند که میتواند آنها را به عنوان یک شتابدهنده بسیار بزرگ نشان دهد. گوگل آنها را پاد مینامد و میتوانند تا ۵۱۲ هسته TPU v2 یا ۲۰۴۸ هسته TPU v3 را در خود جای دهند.

تصویر: یک غلاف TPU نسخه ۳. بردها و رکهای TPU از طریق اتصال HPC به هم متصل شدهاند.
در طول آموزش، گرادیانها با استفاده از الگوریتم all-reduce بین هستههای TPU رد و بدل میشوند ( توضیح خوبی در مورد all-reduce در اینجا آمده است ). مدلی که آموزش داده میشود میتواند با آموزش در اندازههای دستهای بزرگ، از سختافزار بهره ببرد.

تصویر: همگامسازی گرادیانها در طول آموزش با استفاده از الگوریتم all-reduce در شبکه HPC مش چنبره دوبعدی Google TPU.
نرمافزار
آموزش در اندازه دستههای بزرگ
اندازه ایدهآل دسته برای TPUها، ۱۲۸ آیتم داده در هر هسته TPU است، اما سختافزار میتواند از ۸ آیتم داده در هر هسته TPU به خوبی استفاده کند. به یاد داشته باشید که یک Cloud TPU دارای ۸ هسته است.
در این آزمایشگاه کد، ما از API کرس استفاده خواهیم کرد. در کرس، دستهای که شما مشخص میکنید، اندازه دسته سراسری برای کل TPU است. دستههای شما به طور خودکار به ۸ قسمت تقسیم شده و روی ۸ هسته TPU اجرا میشوند.

برای نکات بیشتر در مورد عملکرد، به راهنمای عملکرد TPU مراجعه کنید. برای اندازههای دستهای بسیار بزرگ، ممکن است در برخی مدلها مراقبتهای ویژهای لازم باشد، برای جزئیات بیشتر به LARSOptimizer مراجعه کنید.
زیر کاپوت: XLA
برنامههای Tensorflow نمودارهای محاسباتی را تعریف میکنند. TPU مستقیماً کد پایتون را اجرا نمیکند، بلکه نمودار محاسباتی تعریف شده توسط برنامه Tensorflow شما را اجرا میکند. در پشت صحنه، کامپایلری به نام XLA (کامپایلر جبر خطی شتابیافته) نمودار گرههای محاسباتی Tensorflow را به کد ماشین TPU تبدیل میکند. این کامپایلر همچنین بهینهسازیهای پیشرفته زیادی را روی کد و طرحبندی حافظه شما انجام میدهد. کامپایل به طور خودکار با ارسال کار به TPU انجام میشود. لازم نیست XLA را به طور صریح در زنجیره ساخت خود بگنجانید.

تصویر: برای اجرا روی TPU، نمودار محاسباتی تعریف شده توسط برنامه Tensorflow شما ابتدا به یک نمایش XLA (کامپایلر جبر خطی شتاب یافته) ترجمه میشود، سپس توسط XLA به کد ماشین TPU کامپایل میشود.
استفاده از TPU در Keras
TPU ها از طریق رابط برنامهنویسی کاربردی Keras از Tensorflow 2.1 پشتیبانی میشوند. پشتیبانی Keras روی TPU ها و TPU pod ها کار میکند. در اینجا مثالی آورده شده است که روی TPU، GPU(ها) و CPU کار میکند:
try: # detect TPUs
tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()
strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
strategy = tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines
# use TPUStrategy scope to define model
with strategy.scope():
model = tf.keras.Sequential( ... )
model.compile( ... )
# train model normally on a tf.data.Dataset
model.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)
در این قطعه کد:
-
TPUClusterResolver().connect()واحد پردازش مرکزی (TPU) را در شبکه پیدا میکند. این تابع در اکثر سیستمهای ابری گوگل (مشاغل پلتفرم هوش مصنوعی، Colaboratory، Kubeflow، ماشینهای مجازی یادگیری عمیق که از طریق ابزار 'ctpu up' ایجاد شدهاند) بدون پارامتر کار میکند. این سیستمها به لطف متغیر محیطی TPU_NAME میدانند که TPU آنها کجاست. اگر یک TPU را به صورت دستی ایجاد میکنید، یا متغیر محیطی TPU_NAME را روی ماشین مجازی که از آن استفاده میکنید تنظیم کنید، یاTPUClusterResolverرا با پارامترهای صریح فراخوانی کنید:TPUClusterResolver(tp_uname, zone, project) -
TPUStrategyبخشی است که توزیع و الگوریتم همگامسازی گرادیان «تماماً کاهشی» را پیادهسازی میکند. - استراتژی از طریق یک محدوده اعمال میشود. مدل باید در محدوده ()strike تعریف شود.
- تابع
tpu_model.fitبرای آموزش TPU، یک شیء tf.data.Dataset را به عنوان ورودی دریافت میکند.
وظایف رایج پورت کردن TPU
- در حالی که روشهای زیادی برای بارگذاری دادهها در یک مدل Tensorflow وجود دارد، برای TPUها، استفاده از API
tf.data.Datasetالزامی است. - TPUها بسیار سریع هستند و دریافت دادهها اغلب هنگام اجرا روی آنها به یک گلوگاه تبدیل میشود. ابزارهایی وجود دارد که میتوانید برای تشخیص گلوگاههای داده و سایر نکات مربوط به عملکرد در راهنمای عملکرد TPU از آنها استفاده کنید.
- اعداد int8 یا int16 به عنوان int32 در نظر گرفته میشوند. TPU سختافزار عدد صحیحی که روی کمتر از ۳۲ بیت کار کند، ندارد.
- برخی از عملیات Tensorflow پشتیبانی نمیشوند. لیست آنها اینجاست . خبر خوب این است که این محدودیت فقط برای کد آموزشی، یعنی عبور رو به جلو و عقب از مدل شما اعمال میشود. شما همچنان میتوانید از تمام عملیات Tensorflow در خط لوله ورودی داده خود استفاده کنید زیرا روی CPU اجرا خواهد شد.
-
tf.py_funcدر TPU پشتیبانی نمیشود.
۴. [اطلاعات] طبقهبندیکننده شبکه عصبی ۱۰۱
به طور خلاصه
اگر تمام اصطلاحات پررنگشده در پاراگراف بعدی را از قبل میدانید، میتوانید به تمرین بعدی بروید. اگر تازه یادگیری عمیق را شروع کردهاید، خوش آمدید و لطفاً ادامه مطلب را بخوانید.
برای مدلهایی که به صورت دنبالهای از لایهها ساخته شدهاند، Keras رابط برنامهنویسی کاربردی Sequential را ارائه میدهد. برای مثال، یک طبقهبندیکننده تصویر با استفاده از سه لایه متراکم را میتوان در Keras به صورت زیر نوشت:
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=[192, 192, 3]),
tf.keras.layers.Dense(500, activation="relu"),
tf.keras.layers.Dense(50, activation="relu"),
tf.keras.layers.Dense(5, activation='softmax') # classifying into 5 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, ... )

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

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

محبوبترین تابع فعالسازی، RELU (Rectified Linear Unit) نام دارد. همانطور که در نمودار بالا میبینید، این تابع بسیار ساده است.
فعالسازی سافتمکس
شبکه فوق با یک لایه ۵ نورونی به پایان میرسد زیرا ما گلها را به ۵ دسته (رز، لاله، قاصدک، مینا، آفتابگردان) طبقهبندی میکنیم. نورونهای لایههای میانی با استفاده از تابع فعالسازی کلاسیک RELU فعال میشوند. با این حال، در لایه آخر، میخواهیم اعداد بین ۰ و ۱ را محاسبه کنیم که نشاندهنده احتمال این است که این گل رز، لاله و غیره باشد. برای این کار، از یک تابع فعالسازی به نام "softmax" استفاده خواهیم کرد.
اعمال softmax روی یک بردار با در نظر گرفتن تابع نمایی هر عنصر و سپس نرمالسازی بردار، معمولاً با استفاده از نرم L1 (مجموع قدرمطلقها) انجام میشود، به طوری که مجموع مقادیر برابر با ۱ شود و بتوان آنها را به عنوان احتمال تفسیر کرد.


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

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

مینی بچینگ و مومنتوم
شما میتوانید گرادیان خود را فقط روی یک تصویر نمونه محاسبه کنید و وزنها و بایاسها را فوراً بهروزرسانی کنید، اما انجام این کار روی یک دسته، مثلاً ۱۲۸ تصویر، گرادیانی را ارائه میدهد که محدودیتهای اعمال شده توسط تصاویر نمونه مختلف را بهتر نشان میدهد و بنابراین احتمالاً سریعتر به سمت راهحل همگرا میشود. اندازه مینی-دسته یک پارامتر قابل تنظیم است.
این تکنیک که گاهی اوقات «کاهش گرادیان تصادفی» نامیده میشود، یک مزیت عملیتر دیگر نیز دارد: کار با دستهها به معنای کار با ماتریسهای بزرگتر نیز هست و بهینهسازی این ماتریسها روی GPUها و TPUها معمولاً آسانتر است.
با این حال، همگرایی هنوز میتواند کمی آشوبناک باشد و حتی اگر بردار گرادیان همه صفر باشد، میتواند متوقف شود. آیا این بدان معناست که ما یک مینیمم پیدا کردهایم؟ نه همیشه. یک مؤلفه گرادیان میتواند روی یک مینیمم یا یک ماکزیمم صفر باشد. با یک بردار گرادیان با میلیونها عنصر، اگر همه آنها صفر باشند، احتمال اینکه هر صفر مربوط به یک مینیمم باشد و هیچ یک از آنها به یک نقطه ماکزیمم نرسد، بسیار کم است. در فضایی با ابعاد زیاد، نقاط زینی بسیار رایج هستند و ما نمیخواهیم در آنها متوقف شویم.

تصویر: یک نقطه زینی. گرادیان صفر است اما در همه جهات حداقل نیست. (منبع تصویر: ویکیمدیا: نوشته نیکوگوارو - اثر شخصی، CC BY 3.0 )
راه حل این است که مقداری مومنتوم به الگوریتم بهینهسازی اضافه کنیم تا بتواند بدون توقف از نقاط زینی عبور کند.
واژهنامه
دستهای یا مینی-دستهای : آموزش همیشه روی دستههایی از دادههای آموزشی و برچسبها انجام میشود. انجام این کار به همگرایی الگوریتم کمک میکند. بُعد «دستهای» معمولاً اولین بُعد از تانسورهای داده است. به عنوان مثال، یک تانسور شکل [100، 192، 192، 3] شامل 100 تصویر با ابعاد 192x192 پیکسل با سه مقدار در هر پیکسل (RGB) است.
تابع زیان آنتروپی متقاطع : یک تابع زیان ویژه که اغلب در طبقهبندیکنندهها استفاده میشود.
لایه متراکم : لایهای از نورونها که در آن هر نورون به تمام نورونهای لایه قبلی متصل است.
ویژگیها : ورودیهای یک شبکه عصبی گاهی اوقات «ویژگیها» نامیده میشوند. هنر تشخیص اینکه کدام بخشهای یک مجموعه داده (یا ترکیبی از بخشها) باید به یک شبکه عصبی داده شوند تا پیشبینیهای خوبی حاصل شود، «مهندسی ویژگی» نامیده میشود.
برچسبها : نام دیگری برای «کلاسها» یا پاسخهای صحیح در یک مسئله طبقهبندی نظارتشده
نرخ یادگیری : کسری از گرادیان که وزنها و بایاسها در هر تکرار حلقه آموزش بهروزرسانی میشوند.
لوجیتها : خروجیهای یک لایه از نورونها قبل از اعمال تابع فعالسازی، «لوجیت» نامیده میشوند. این اصطلاح از «تابع لجستیک» یا «تابع سیگموئید» گرفته شده است که قبلاً محبوبترین تابع فعالسازی بود. «خروجیهای نورون قبل از تابع لجستیک» به «لوجیتها» خلاصه شد.
تابع خطا (loss) : تابع خطایی که خروجیهای شبکه عصبی را با پاسخهای صحیح مقایسه میکند.
نورون : مجموع وزنی ورودیهایش را محاسبه میکند، یک بایاس اضافه میکند و نتیجه را از طریق یک تابع فعالسازی ارسال میکند.
کدگذاری وان-هات : کلاس ۳ از ۵ به صورت برداری با ۵ عنصر کدگذاری میشود که همه عناصر آن صفر هستند به جز عنصر سوم که ۱ است.
relu : واحد خطی یکسو شده. یک تابع فعالسازی محبوب برای نورونها.
سیگموئید : تابع فعالسازی دیگری که قبلاً محبوب بود و هنوز هم در موارد خاص مفید است.
softmax : یک تابع فعالسازی ویژه که روی یک بردار عمل میکند، تفاوت بین بزرگترین مؤلفه و سایر مؤلفهها را افزایش میدهد، و همچنین بردار را طوری نرمالسازی میکند که مجموع آن ۱ باشد تا بتوان آن را به عنوان برداری از احتمالات تفسیر کرد. به عنوان آخرین مرحله در طبقهبندیکنندهها استفاده میشود.
تانسور : یک "تانسور" مانند یک ماتریس است اما با تعداد دلخواهی از ابعاد. یک تانسور یک بعدی یک بردار است. یک تانسور دو بعدی یک ماتریس است. و سپس میتوانید تانسورهایی با ۳، ۴، ۵ یا بیشتر بعد داشته باشید.
۵. [اطلاعات] شبکههای عصبی کانولوشن
به طور خلاصه
اگر تمام اصطلاحات پررنگشده در پاراگراف بعدی را از قبل میدانید، میتوانید به تمرین بعدی بروید. اگر تازه با شبکههای عصبی کانولوشن شروع کردهاید، لطفاً ادامه مطلب را بخوانید.

تصویر: فیلتر کردن یک تصویر با دو فیلتر متوالی که هر کدام از ۴x۴x۳=۴۸ وزن قابل یادگیری ساخته شدهاند.
یک شبکه عصبی کانولوشن ساده در Keras به این شکل است:
model = tf.keras.Sequential([
# input: images of size 192x192x3 pixels (the three stands for RGB channels)
tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu', input_shape=[192, 192, 3]),
tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=12, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=6, padding='same', activation='relu'),
tf.keras.layers.Flatten(),
# classifying into 5 categories
tf.keras.layers.Dense(5, activation='softmax')
])
model.compile(
optimizer='adam',
loss= 'categorical_crossentropy',
metrics=['accuracy'])

شبکههای عصبی کانولوشنی ۱۰۱
در یک لایه از یک شبکه کانولوشن، یک "نورون" مجموع وزنی پیکسلهای درست بالای خود را، فقط در یک ناحیه کوچک از تصویر، انجام میدهد. این یک بایاس اضافه میکند و مجموع را از طریق یک تابع فعالسازی تغذیه میکند، درست همانطور که یک نورون در یک لایه متراکم معمولی انجام میدهد. سپس این عملیات در کل تصویر با استفاده از همان وزنها تکرار میشود. به یاد داشته باشید که در لایههای متراکم، هر نورون وزنهای خود را داشت. در اینجا، یک "وصله" از وزنها در هر دو جهت در تصویر میلغزد (یک "کانولوشن"). خروجی به تعداد پیکسلهای موجود در تصویر، مقادیر دارد (البته مقداری لایهگذاری در لبهها ضروری است). این یک عملیات فیلترینگ است که از یک فیلتر با وزنهای 4x4x3=48 استفاده میکند.
با این حال، ۴۸ وزن کافی نخواهد بود. برای اضافه کردن درجات آزادی بیشتر، همین عملیات را با مجموعهای جدید از وزنها تکرار میکنیم. این کار مجموعهای جدید از خروجیهای فیلتر را تولید میکند. بیایید آن را «کانال» خروجیها بنامیم، مشابه کانالهای R، G و B در تصویر ورودی.

دو (یا چند) مجموعه از وزنها را میتوان با اضافه کردن یک بُعد جدید به عنوان یک تانسور خلاصه کرد. این به ما شکل کلی تانسور وزنها را برای یک لایه کانولوشن میدهد. از آنجایی که تعداد کانالهای ورودی و خروجی پارامترها هستند، میتوانیم شروع به انباشت و زنجیرهسازی لایههای کانولوشن کنیم.

تصویر: یک شبکه عصبی کانولوشنی، «مکعبهای» داده را به «مکعبهای» داده دیگر تبدیل میکند.
کانولوشنهای گامبهگام، حداکثر تجمع
با انجام کانولوشنها با گام ۲ یا ۳، میتوانیم مکعب داده حاصل را در ابعاد افقی نیز کوچک کنیم. دو روش رایج برای انجام این کار وجود دارد:
- کانولوشن گامدار: یک فیلتر لغزشی مانند بالا اما با گام بزرگتر از ۱
- حداکثر ادغام: یک پنجره کشویی که عملیات MAX را اعمال میکند (معمولاً روی تکههای ۲x۲، که هر ۲ پیکسل تکرار میشود)

تصویر: جابجایی پنجره محاسبات به اندازه ۳ پیکسل منجر به مقادیر خروجی کمتری میشود. کانولوشنهای گامدار یا حداکثر تجمع (حداکثر در یک پنجره ۲x۲ که با گام ۲ جابجا میشود) راهی برای کوچک کردن مکعب داده در ابعاد افقی هستند.
طبقهبندیکنندهی تکاملی C
در نهایت، با مسطح کردن آخرین مکعب داده و عبور آن از یک لایه متراکم و فعالشده با softmax، یک هد طبقهبندی متصل میکنیم. یک طبقهبندیکننده کانولوشنی معمولی میتواند به شکل زیر باشد:

تصویر: یک طبقهبندیکننده تصویر با استفاده از لایههای کانولوشن و softmax. این طبقهبندیکننده از فیلترهای ۳x۳ و ۱x۱ استفاده میکند. لایههای maxpool حداکثر گروههای نقاط داده ۲x۲ را میگیرند. سر طبقهبندیکننده با یک لایه متراکم با فعالسازی softmax پیادهسازی شده است.
در کراس
پشته کانولوشنی نشان داده شده در بالا را میتوان در Keras به صورت زیر نوشت:
model = tf.keras.Sequential([
# input: images of size 192x192x3 pixels (the three stands for RGB channels)
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu', input_shape=[192, 192, 3]),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=16, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=8, padding='same', activation='relu'),
tf.keras.layers.Flatten(),
# classifying into 5 categories
tf.keras.layers.Dense(5, activation='softmax')
])
model.compile(
optimizer='adam',
loss= 'categorical_crossentropy',
metrics=['accuracy'])
۶. [اطلاعات جدید] معماریهای کانولوشن مدرن
به طور خلاصه

تصویر: یک "ماژول" کانولوشن. در حال حاضر بهترین گزینه چیست؟ یک لایه max-pool و به دنبال آن یک لایه کانولوشن 1x1 یا ترکیبی متفاوت از لایهها؟ همه آنها را امتحان کنید، نتایج را به هم پیوند دهید و بگذارید شبکه تصمیم بگیرد. در سمت راست: معماری کانولوشن " inception " با استفاده از چنین ماژولهایی.
در Keras، برای ایجاد مدلهایی که جریان دادهها میتواند به شاخههای مختلف تقسیم شود، باید از سبک مدل «تابعی» استفاده کنید. در اینجا مثالی آورده شده است:
l = tf.keras.layers # syntax shortcut
y = l.Conv2D(filters=32, kernel_size=3, padding='same',
activation='relu', input_shape=[192, 192, 3])(x) # x=input image
# module start: branch out
y1 = l.Conv2D(filters=32, kernel_size=1, padding='same', activation='relu')(y)
y3 = l.Conv2D(filters=32, kernel_size=3, padding='same', activation='relu')(y)
y = l.concatenate([y1, y3]) # output now has 64 channels
# module end: concatenation
# many more layers ...
# Create the model by specifying the input and output tensors.
# Keras layers track their connections automatically so that's all that's needed.
z = l.Dense(5, activation='softmax')(y)
model = tf.keras.Model(x, z)

ترفندهای ارزان دیگر
فیلترهای کوچک ۳x۳

در این تصویر، نتیجه دو فیلتر متوالی ۳x۳ را مشاهده میکنید. سعی کنید نقاط دادهای که در نتیجه نقش داشتهاند را ردیابی کنید: این دو فیلتر متوالی ۳x۳ ترکیبی از یک ناحیه ۵x۵ را محاسبه میکنند. این دقیقاً همان ترکیبی نیست که یک فیلتر ۵x۵ محاسبه میکند، اما ارزش امتحان کردن را دارد زیرا دو فیلتر متوالی ۳x۳ ارزانتر از یک فیلتر ۵x۵ هستند.
کانولوشنهای ۱x۱؟

از نظر ریاضی، کانولوشن "1x1" ضرب در یک ثابت است، که مفهوم خیلی مفیدی نیست. با این حال، در شبکههای عصبی کانولوشن، به یاد داشته باشید که فیلتر روی یک مکعب داده اعمال میشود، نه فقط یک تصویر دوبعدی. بنابراین، یک فیلتر "1x1" مجموع وزنی یک ستون 1x1 از دادهها را محاسبه میکند (به تصویر مراجعه کنید) و همانطور که آن را روی دادهها میلغزانید، یک ترکیب خطی از کانالهای ورودی به دست خواهید آورد. این در واقع مفید است. اگر کانالها را به عنوان نتایج عملیات فیلترینگ جداگانه در نظر بگیرید، به عنوان مثال یک فیلتر برای "گوشهای نوکتیز"، یکی دیگر برای "سبیلها" و سومی برای "چشمهای شکافدار"، یک لایه کانولوشن "1x1" چندین ترکیب خطی ممکن از این ویژگیها را محاسبه میکند که ممکن است هنگام جستجوی "گربه" مفید باشد. علاوه بر این، لایههای 1x1 از وزنهای کمتری استفاده میکنند.
7. اسکوئیزنت
یک روش ساده برای کنار هم قرار دادن این ایدهها در مقاله "Squeezenet" ارائه شده است. نویسندگان، یک طراحی ماژول کانولوشنی بسیار ساده را پیشنهاد میکنند که تنها از لایههای کانولوشنی 1x1 و 3x3 استفاده میکند.

تصویر: معماری squeezenet مبتنی بر "ماژولهای آتش". آنها یک لایه ۱x۱ را که دادههای ورودی را در بعد عمودی "فشرده" میکند، به طور متناوب قرار میدهند و به دنبال آن دو لایه کانولوشنی موازی ۱x۱ و ۳x۳ قرار دارند که عمق دادهها را دوباره "گسترش" میدهند.
عملی
در ادامهی دفترچه یادداشت قبلی خود، یک شبکه عصبی کانولوشنی با الهام از squeezenet بسازید. شما باید کد مدل را به «سبک تابعی» Keras تغییر دهید.
Keras_Flowers_TPU (playground).ipynb
اطلاعات تکمیلی
برای این تمرین، تعریف یک تابع کمکی برای ماژول squeezenet مفید خواهد بود:
def fire(x, squeeze, expand):
y = l.Conv2D(filters=squeeze, kernel_size=1, padding='same', activation='relu')(x)
y1 = l.Conv2D(filters=expand//2, kernel_size=1, padding='same', activation='relu')(y)
y3 = l.Conv2D(filters=expand//2, kernel_size=3, padding='same', activation='relu')(y)
return tf.keras.layers.concatenate([y1, y3])
# this is to make it behave similarly to other Keras layers
def fire_module(squeeze, expand):
return lambda x: fire(x, squeeze, expand)
# usage:
x = l.Input(shape=[192, 192, 3])
y = fire_module(squeeze=24, expand=48)(x) # typically, squeeze is less than expand
y = fire_module(squeeze=32, expand=64)(y)
...
model = tf.keras.Model(x, y)
هدف این بار رسیدن به دقت ۸۰ درصدی است.
چیزهایی که باید امتحان کنید
با یک لایه کانولوشنی واحد شروع کنید، سپس با " fire_modules " ادامه دهید و به طور متناوب از لایههای MaxPooling2D(pool_size=2) استفاده کنید. میتوانید با ۲ تا ۴ لایه max pooling در شبکه و همچنین با ۱، ۲ یا ۳ ماژول fire متوالی بین لایههای max pooling آزمایش کنید.
در ماژولهای آتش، پارامتر "فشردهسازی" معمولاً باید کوچکتر از پارامتر "گسترش" باشد. این پارامترها در واقع تعداد فیلترها هستند. معمولاً میتوانند از ۸ تا ۱۹۶ متغیر باشند. میتوانید معماریهایی را آزمایش کنید که در آنها تعداد فیلترها به تدریج در شبکه افزایش مییابد، یا معماریهای سادهای که در آنها همه ماژولهای آتش تعداد فیلتر یکسانی دارند.
در اینجا یک مثال آورده شده است:
x = tf.keras.layers.Input(shape=[*IMAGE_SIZE, 3]) # input is 192x192 pixels RGB
y = tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu')(x)
y = fire_module(24, 48)(y)
y = tf.keras.layers.MaxPooling2D(pool_size=2)(y)
y = fire_module(24, 48)(y)
y = tf.keras.layers.MaxPooling2D(pool_size=2)(y)
y = fire_module(24, 48)(y)
y = tf.keras.layers.GlobalAveragePooling2D()(y)
y = tf.keras.layers.Dense(5, activation='softmax')(y)
model = tf.keras.Model(x, y)
در این مرحله، ممکن است متوجه شوید که آزمایشهای شما چندان خوب پیش نمیروند و هدف دقت ۸۰٪ دور از دسترس به نظر میرسد. وقت آن است که چند ترفند ارزان دیگر را امتحان کنید.
نرمالسازی دستهای
Batch norm will help with the convergence problems you are experiencing. There will be detailed explanations about this technique in the next workshop, for now, please use it as a black box "magic" helper by adding this line after every convolutional layer in your network, including the layers inside of your fire_module function:
y = tf.keras.layers.BatchNormalization(momentum=0.9)(y)
# please adapt the input and output "y"s to whatever is appropriate in your context
The momentum parameter has to be decreased from its default value of 0.99 to 0.9 because our dataset is small. Never mind this detail for now.
Data augmentation
You will get a couple more percentage points by augmenting the data with easy transformations like left-right flips of saturation changes:


This is very easy to do in Tensorflow with the tf.data.Dataset API. Define a new transformation function for your data:
def data_augment(image, label):
image = tf.image.random_flip_left_right(image)
image = tf.image.random_saturation(image, lower=0, upper=2)
return image, label
Then use it in you final data transformation (cell "training and validation datasets", function "get_batched_dataset"):
dataset = dataset.repeat() # existing line
# insert this
if augment_data:
dataset = dataset.map(data_augment, num_parallel_calls=AUTO)
dataset = dataset.shuffle(2048) # existing line
Do not forget to make the data augmentation optional and to add the necessary code to make sure only the training dataset is augmented . It makes no sense to augment the validation dataset.
80% accuracy in 35 epochs should now be within reach.
راه حل
Here is the solution notebook. You can use it if you are stuck.
Keras_Flowers_TPU_squeezenet.ipynb
What we've covered
- 🤔 Keras "functional style" models
- 🤓 Squeezenet architecture
- 🤓 Data augmentation with tf.data.datset
Please take a moment to go through this checklist in your head.
8. Xception fine-tuned
Separable convolutions
A different way of implementing convolutional layers had been gaining popularity recently: depth-separable convolutions. I know, it's a mouthful, but the concept is quite simple. They are implemented in Tensorflow and Keras as tf.keras.layers.SeparableConv2D .
A separable convolution also runs a filter on the image but it uses a distinct set of weights for each channel of the input image. It follows with a "1x1 convolution", a series of dot products resulting in a weighted sum of the filtered channels. With new weights each time, as many weighted recombinations of the channels are computed as necessary.

Illustration: separable convolutions. Phase 1: convolutions with a separate filter for each channel. Phase 2: linear recombinations of channels. Repeated with a new set of weights until the desired number of output channels is reached. Phase 1 can be repeated too, with new weights each time but in practice it is rarely so.
Separable convolutions are used in most recent convolutional networks architectures: MobileNetV2, Xception, EfficientNet. By the way, MobileNetV2 is what you used for transfer learning previously.
They are cheaper than regular convolutions and have been found to be just as effective in practice. Here is the weight count for the example illustrated above:
Convolutional layer: 4 x 4 x 3 x 5 = 240
Separable convolutional layer: 4 x 4 x 3 + 3 x 5 = 48 + 15 = 63
It is left as an exercise for the reader to compute than number of multiplications required to apply each style of convolutional layer scales in a similar way. Separable convolutions are smaller and much more computationally effective.
Hands-on
Restart from the "transfer learning" playground notebook but this time select Xception as the pre-trained model. Xception uses separable convolutions only. Leave all weights trainable. We will be fine-tuning the pre-trained weights on our data instead of using the pre-trained layers as such.
Keras Flowers transfer learning (playground).ipynb
Goal: accuracy > 95% (No, seriously, it is possible!)
This being the final exercise, it requires a bit more code and data science work.
Additional info on fine-tuning
Xception is available in the standard pre-trained models in tf.keras.application.* Do not forget to leave all weights trainable this time.
pretrained_model = tf.keras.applications.Xception(input_shape=[*IMAGE_SIZE, 3],
include_top=False)
pretrained_model.trainable = True
To get good results when fine-tuning a model, you will need to pay attention to the learning rate and use a learning rate schedule with a ramp-up period. Like this:

Starting with a standard learning rate would disrupt the pre-trained weights of the model. Starting progressively preserves them until the model has latched on your data is able to modify them in a sensible way. After the ramp, you can continue with a constant or an exponentially decaying learning rate.
In Keras, the learning rate is specified through a callback in which you can compute the appropriate learning rate for each epoch. Keras will pass the correct learning rate to the optimizer for each epoch.
def lr_fn(epoch):
lr = ...
return lr
lr_callback = tf.keras.callbacks.LearningRateScheduler(lr_fn, verbose=True)
model.fit(..., callbacks=[lr_callback])
راه حل
Here is the solution notebook. You can use it if you are stuck.
07_Keras_Flowers_TPU_xception_fine_tuned_best.ipynb
What we've covered
- 🤔 Depth-separable convolution
- 🤓 Learning rate schedules
- 😈 Fine-tuning a pre-trained model.
Please take a moment to go through this checklist in your head.
۹. تبریک میگویم!
You have built your first modern convolutional neural network and trained it to 90% + accuracy, iterating on successive training in only minutes thanks to TPUs. This concludes the 4 "Keras on TPU codelabs":
- TPU-speed data pipelines: tf.data.Dataset and TFRecords
- Your first Keras model, with transfer learning
- Convolutional neural networks, with Keras and TPUs
- [THIS LAB] Modern convnets, squeezenet, Xception, with Keras and TPUs
TPUs in practice
TPUs and GPUs are available on Cloud AI Platform :
- On Deep Learning VMs
- In AI Platform Notebooks
- In AI Platform Training jobs
Finally, we love feedback. Please tell us if you see something amiss in this lab or if you think it should be improved. Feedback can be provided through GitHub issues [ feedback link ].

|

