alikarimi120
alikarimi120
خواندن ۵ دقیقه·۳ سال پیش

پیاده سازی اولین شبکه عصبی برای طبقه بندی تصاویر با استفاده از پایتورچ (قسمت دوم)

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

کتابخانه PyTorch
کتابخانه PyTorch


چطور شبکه های عصبی رو آموزش بدیم؟

میدونیم شبکه های عصبی عمدتا با الگوریتم backpropagation انجام میشه. آموزش شبکه عصبی دو مرحله داره : 1- Forward propagation: ورودی اعمال میشه و خروجی بدست میاد که در مرحله بعد استفاده میشه 2-backward propagation: خطای نتایج با نتایج واقعی بدست میاد و این خطا رو در شبکه اعمال می کنیم تا گرادیان ها رو بدست بیاریم. حالا توی پایتورچ ابزاری که اجازه میده که این کار رو انجام بدیم . ابزار AutoGrad هست.

در واقع در پایتورچ به این صورت عمل می کنیم که هر تابعی که داخل این کتابخانه پیاده سازی شده و هر operator ای که می تونیم استفاده کنیم gradient اش تعریف شده است، و AutoGrad میاد این گرادیان ها رو حساب میکنه و ذخیره میکنه.

بذارید یک شبکه رو مثال بزنید، فرض کنید از کتابخانه Torchvision مدل از پیش آموزش داده شده resnet18 رو استفاده می کنیم. ورودی رندوم رو میسازم . مدل رو میسازیم.

https://gist.github.com/alikarimi120/56e6d0065574a1e408222f6b673c2a69

ورودی رو به شبکه میدیم و مجموع خطا رو در تنسوری به اسم loss ذخیره می کنیم و بعد در فاز backward با استفاده از تابع backward تمام گرادیان ها به صورت backward بدست میاد.

https://gist.github.com/alikarimi120/3f7ba69b51a7f902f92a04cd1ebe4de4
https://gist.github.com/alikarimi120/d16f2f12dbf1b0be66a7f44e3c13da97

روش های مختلفی برای به روزرسانی وزن های شبکه وجود داره، معروف ترین آنها Stochastic Gradient Descent هست، حالا تابع optim که فراخوانی میشه وزن های شبکه یک مرحله به روزرسانی میشه.

https://gist.github.com/alikarimi120/dfcf2c1a9f869eb01593c14557e780d9

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

https://gist.github.com/alikarimi120/1731ab6ff3f8eeb5be2aba63daca3382

در این مرحله یک گام از الگوریتم نزول گرادیان اجرا شد!

برای درک بهتر AutoGrad یک مثال میزنم.

ما دو تنسور A و B داریم که آنها را تعریف کردیم. نکته ای که وجود داره این هست که از کجا متوجه میشه گرادیان رو برای پارامترهای شبکه حساب میکنه. وقتی شبکه رو تعریف می کنید مجموعه ای از وزن ها هست که هر وزن هم در یک تنسور ذخیره میشه

https://gist.github.com/alikarimi120/ec6abfe087ce24deceb9e8a67c759e52

در مجموع برای تنسورهای AutoGrad گرادیان رو حساب میکنه که مقدار requires_grad برابر True باشد شبیه کاری که برای تنسور A و B کردیم.

یک تابع درجه 3 به اسم Q تعریف می کنیم ، حاالا می خوایم گرادیان رو به روش تحلیلی و با استفاده از AutoGrad استفاده کنیم که متوجه بشیم آیا AutoGrad درست کار میکنه یا نه.

https://gist.github.com/alikarimi120/c67cfa98ab9ae4d9977e3fce087f88f4

همانطور که می دونید گرادیان Q نسبت به a میشه 9 ضرب در a به توان 2 و گرادیان Q نسبت به b میشه 2b .

بردار ورودی رو میدیم و مقایسه می کنیم آیا این دو تا هم با هم مساوی هستن یا نه! و خوب نتیجه True هست.

https://gist.github.com/alikarimi120/2a3de4f3efa17551d06c46104ff34d0d

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

یادتون باشه در پایتورچ هر operation ای که استفاده می کنید گرادیانش تعریف شدس. پس در نظر داشته باشید اگه بخواهید operation جدید تعریف کنید باید تابع گرادیانش هم پیاده سازی کنید تا AutoGrad از اون تابع گرادیان استفاده کنه.

وقتی در گام فوروارد قرار دارید کاری که AutoGrad انجام میده دو بخش همزمان هست : 1 - در واقع operation هایی که شما تعریف کردید رو محاسبه میکنه و تنسورهای خروجی رو میسازه 2- به صورت همزمان تابع گرادیان هر operation رو هم ذخیره میکنه تا بتونه در گام backward از اونها استفاده کنه.

همانطور که گفته شد برای اینکه بتونید گرادیان خروجی رو نسبت به پارامترهای شبکه استفاده کنید باید از تابع backward استفاده کنید ، کافیه تابع backward روی ریشه گراف محاسباتی پیاده سازی کنید.

تابع backward که به ازای هر operation استفاده میشه تابع grad_fn هست ، هر operation به ازاش یک تابع grad_fn وجود داره. وقتی grad_fn رو محاسبه می کنید با استفاده از قاعده زنجیره ای گرادیان ها به دست میاد و از بالا به پایین propagate میشه. نکته ای که باید درنظر داشته باشد وقتی backward رو فراخوانی می کنید این مقادیر گرادیان ها در نودها انباشته میشه وقتی دو بار فراخوانی می کنید گرادیان ها که در دو مرحله به دست اومدند با هم جمع میشن. پس اگه می خواید تاریخچه گرادیان ها جمع نشه باید مقادیر گرادیان ها رو قبل از تابع backward صفر کنید..

به عنوان مثال یه گراف محاسباتی رو مشاهده کنید:

گراف محاسباتی
گراف محاسباتی

نکته ای که در مورد پایتورچ وجود داره، پایتورچ از نظر ساخت گراف محاسباتی یک کتابخانه داینامیک هست، گراف محاسباتی زمانی ساخته میشه که تابع backward رو فراخوانی کنید. بسته به شرایط اجرا میتونه گراف متفاوتی ساخته بشه و به همین دلیل گراف داینامیک یا همان پویا هست.

همانطور که گفته شد AutoGrad با backward رو فراخوانی می کنیم برای پارامترهایی گرادیان رو محاسبه میکنه که requires_grad برابر True باشه، اگه نمی خواهید حساب بشه باید False کنید. حالا چرا باید لازم بشه؟

در دو حالت می تونیم بگیم امکان حساب نکردن گرادیان یا خارج کردن تنسور از گراف محاسباتی به ما کمک میکنه:

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

2- وقتی شما از شبکه های pretrain استفاده می کنید که اینها رو می تونیم به دو بخش feature extraction و Classification تقسیم می کنیم، گاهی مدل قبلا رو مجموعه داده بزرگی آ»وزش دیده و ما در اینجا feature extraction رو مجددا آموزش نمی دیم و فقط لایه classification رو مجدد آموزش میدیم.

مثلا اینجا ما RESnet18 رو استفاده می کنیم، میایم پارامترها رو freeze می کنیم، پارامترهای شبکه با استفاده از تابع model.parameters بدست میاد.

https://gist.github.com/alikarimi120/91f054e46af584cf709cac340e6bd017

یک لایه fully connected هم در ادامه اضافه می کنیم

https://gist.github.com/alikarimi120/e54bf2452a448fe888c86afd006da998

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


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

شاید از این پست‌ها خوشتان بیاید