رامین کمالی
رامین کمالی
خواندن ۱۰ دقیقه·۲ ماه پیش

خلاصه هفته دوم دوره Supervised Machine Learning: Regression and Classification از Andrew Ng: ورود به دنیای یادگیری ماشین

خلاصه هفته اول رو داخل پست قبلی نوشتمش، بدون مقدمه بریم سراغ هفته دوم!

فرض کن وقتی داری با داده‌ها کار می‌کنی، دو تا مفهوم خیلی مهم پیش میاد: یکی "فیچر" یا همون ویژگی‌هامون و یکی "وکتور" که همون برداره. حالا اگه بخوایم دقیق‌تر بگیم:

  • اگه کنار فیچر X یه عدد زیرنویس مثل 2 باشه، یعنی داریم به ستون دوم داده‌ها اشاره می‌کنیم (فیچر دوم).
  • ولی اگه اون عدد بالانویس باشه، منظورمون کل مقادیر سطر دومه که یه بردار تشکیل میده.

رگرسیون خطی چندگانه (Multiple Linear Regression) چیه؟


تو این روش، به جای اینکه بخوایم هر فیچر رو دستی ضرب کنیم تو ضریبش و بعد دونه دونه جمع بزنیم، یه ترفند باحال به اسم بردارسازی داریم. بردارسازی یعنی به جای اینکه دستی فیچرها رو ضرب و جمع کنی، از جمع داخلی دو تا بردار استفاده می‌کنی؛ یکی بردار فیچرها و یکی بردار ضرایب.

این روش یه فایده بزرگ داره: به جای اینکه برای هر فیچر حلقه for بزنی و کلی زمان بگذاری، می‌تونی از قدرت پردازشی کارت گرافیک (GPU) استفاده کنی که کار رو خیلی سریع‌تر پیش می‌بره. حالا اگه بخوای این کارو دستی انجام بدی، باید for بزنی که وقتی تعداد فیچرها زیاد بشه، زمان محاسبه خیلی طولانی میشه.

چرا بردارسازی خوبه؟

بردارسازی دو تا مزیت اصلی داره:

  1. کد رو کوچیک‌تر و تمیزتر می‌کنه: به جای اینکه کلی کد بنویسی، با چند خط جمع داخلی کار رو راه می‌ندازی.
  2. سرعت اجرا رو خیلی بیشتر می‌کنه: چون از سخت‌افزار موازی مثل GPU استفاده می‌کنه و دیگه نیازی نیست به روش قدیمی از حلقه for استفاده کنی.

این باعث میشه هم برنامه‌ت بهینه‌تر بشه و هم سریع‌تر اجرا بشه. خلاصه، بردارسازی یه جورایی جادوی سرعت تو محاسباته!

چرا بردارسازی سرعت رو بیشتر می‌کنه؟

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

برای همین تو پروژه‌های بزرگ که تعداد فیچرها بالاست، بردارسازی یه باید محسوب میشه، نه یه انتخاب. چون اختلاف زمانی بین این دو روش خیلی چشم‌گیره. مثلاً فرض کن تعداد فیچرها زیاده:

  • اگه از حلقه for استفاده کنی، ممکنه اجرای برنامه 943 ثانیه طول بکشه.
  • ولی اگه از Dot استفاده کنی، این زمان فقط 0.5 ثانیه میشه!

الگوریتم Normal Equation چیه؟

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

  1. فقط برای رگرسیون خطی کاربرد داره: یعنی برای انواع دیگه رگرسیون، مثل رگرسیون لجستیک، به کار نمیاد.
  2. نیازی به تکرار نداره: به جای اینکه مثل روش‌های دیگه هی تکرار کنه تا به جواب برسه، مستقیم میره سر اصل مطلب و مقادیر بهینه برای w و b رو پیدا می‌کنه.

اما یه نکته مهم اینه که اگه تعداد فیچرها زیاد بشه، مثلاً بالای 10 هزار تا، سرعت الگوریتم کم میشه. ولی نگران نباش، چون خودت مستقیم با این الگوریتم سر و کار نداری. اکثر کتابخونه‌هایی که برای رگرسیون خطی طراحی شدن، مثل Scikit-Learn، به صورت داخلی این الگوریتم رو دارن و خودشون w و b رو حساب می‌کنن. پس تو فقط از اون‌ها استفاده می‌کنی و لازم نیست نگران جزئیات الگوریتم باشی.

البته این الگوریتم به درد مصاحبه‌های شغلی می‌خوره، پس خوبه که بدونی چیه و چطوری کار می‌کنه.

تکنیک‌هایی برای سریع‌تر کردن گرادیان کاهشی !

حالا بریم سراغ اینکه چطور می‌تونیم گرادیان کاهشی (Gradient Descent) رو سریع‌تر کنیم. یکی از تکنیک‌های مهم برای این کار اسکیل کردن فیچرها است.

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

فرض کن دو تا فیچر داریم:

  • فیچر اول متراژ خونه است و مقادیرش بین 300 تا 2000 متغیره.
  • فیچر دوم تعداد اتاق‌هاست که بین 0 تا 5 متغیر میشه.

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

راه‌حل: اسکیل کردن فیچرها

برای اینکه این مشکل رو حل کنیم، باید داده‌های بزرگ رو اسکیل کنیم. وقتی فیچرها رنج‌های متفاوتی دارن، گرادیان کاهشی نمی‌تونه بهینه‌ترین جواب رو به راحتی پیدا کنه. حالا اگه بیایم هر دوتا فیچر رو به یه بازه مثل 0 تا 1 اسکیل کنیم، نمودار به یه دایره تبدیل میشه و کار گرادیان کاهشی برای رسیدن به مرکز (که همون نقطه بهینه است) خیلی آسون‌تر میشه.


چطور فیچرها رو اسکیل کنیم؟

برای اسکیل کردن فیچرها چند روش وجود داره:

1. اسکیل ساده

یه روش ساده اینه که هر عدد رو به بزرگ‌ترین عدد در اون فیچر تقسیم کنیم. اینطوری همه مقادیر فیچر توی یه بازه بین 0 تا 1 قرار می‌گیرن.

2. Mean Normalization (نرمال‌سازی با میانگین)

تو این روش، اول میانگین مقادیر هر فیچر رو از خودش کم می‌کنیم، بعد نتیجه رو به طول بازه (بزرگ‌ترین عدد منهای کوچک‌ترین عدد) تقسیم می‌کنیم. این روش باعث میشه داده‌ها توی یه بازه متعادل‌تری قرار بگیرن.

3. Z-score Normalization (نرمال‌سازی با Z-score)

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


هر کدوم از این روش‌ها بسته به نوع داده‌هات و مسئله‌ای که داری، می‌تونن مناسب باشن. اما در نهایت هدف همه‌شون اینه که فیچرها تو یه بازه‌ی معقول باشن تا گرادیان کاهشی سریع‌تر و دقیق‌تر عمل کنه.

اسکیل کردن دوباره و نرخ یادگیری در گرادیان کاهشی

Rescaling: اسکیل دوباره داده‌ها

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

چطور بفهمیم گرادیان کاهشی درست کار می‌کنه؟

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


انتخاب درست آلفا یا نرخ یادگیری

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

تست خودکار همگرایی (Automatic Convergence Test)

برای اینکه بفهمیم الگوریتم همگرا شده یا نه، از یه تست به نام تست همگرایی خودکار استفاده می‌کنیم. اگه تغییرات کاست فانکشن بعد از تعداد زیادی چرخش خیلی کم بود، مثلاً کمتر از یه مقدار کوچیک به نام اپسیلون (ε)، می‌تونیم بگیم که الگوریتم همگرا شده و مقادیر بهینه برای w و b پیدا شدن.


مشکلات رایج در همگرایی

حالا ممکنه یه وقتایی نمودار کاست فانکشن به درستی همگرا نشه. چند حالت اینجا پیش میاد:

  1. اگه نمودار به جای اینکه مرتب پایین بیاد، نوسان شدید کرد (یعنی یه کم نزولی شد، بعد دوباره صعودی شد و این چرخه ادامه داشت) یا حتی واگرا شد، دو دلیل ممکنه داشته باشه:نرخ یادگیری (آلفا) خیلی بزرگه: یعنی الگوریتم داره با گام‌های خیلی بزرگ حرکت می‌کنه و از جواب بهینه رد میشه.
  2. کد باگ داره!: ممکنه مشکل تو کدنویسی باشه و الگوریتم درست پیاده‌سازی نشده باشه. مخصوصا اگه با آلفا کوچیک باز هم نمودار واگرا شد یعنی کد ایراد اساسی داره!

انتخاب آلفا و مهندسی فیچرها

چطور مقدار مناسب آلفا رو پیدا کنیم؟

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

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

مهندسی فیچر (Feature Engineering)


مهندسی فیچر یکی از مهم‌ترین قسمت‌های یادگیری ماشینیه که می‌تونه تاثیر زیادی روی عملکرد مدل داشته باشه. مثلاً فرض کن دو تا فیچر داریم:

  1. طول زمین خونه
  2. عرض زمین خونه

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

به صورت کلی، مهندسی فیچر یعنی ما بیایم از داده‌های موجود، فیچرهای جدید و مفیدتری بسازیم تا مدل بهتر و دقیق‌تری داشته باشیم.

رگرسیون چندجمله‌ای (Polynomial Regression)

یکی دیگه از تکنیک‌های مهم تو مدلسازی، استفاده از رگرسیون چندجمله‌ای است. تو این روش، ما به جای اینکه فقط از توان یک فیچرها استفاده کنیم، توان‌های بالاتری مثل توان دوم، سوم و بیشتر رو هم به مدل اضافه می‌کنیم. اما باید مواظب باشیم! برای مثال:

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


اهمیت Feature Scaling در رگرسیون چندجمله‌ای

وقتی شروع به استفاده از توان‌های بالاتر می‌کنیم، مسئله اسکیل کردن فیچرها خیلی مهم میشه. چرا؟ چون مقادیر فیچرها ممکنه خیلی بزرگ بشن و اگه این فیچرها اسکیل نشن، الگوریتم گرادیان کاهشی نمی‌تونه درست کار کنه. بنابراین باید همه فیچرها رو به مقادیر کوچکتر و قابل قیاس اسکیل کنیم.

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

جمع‌بندی

حالا که تمام مباحث رو مرور کردیم، بذار یه جمع‌بندی داشته باشیم:

  1. بردارسازی باعث میشه محاسبات سریع‌تر انجام بشه، چون عملیات‌ها به صورت موازی روی GPU اجرا میشن و از حلقه for خلاص میشیم.
  2. استفاده از Normal Equation یه روش سریع برای محاسبه ضرایب رگرسیون خطی بدون نیاز به تکرارهای زیاد گرادیان کاهشیه، ولی توی مسائل بزرگ عملکردش کاهش پیدا می‌کنه.
  3. اسکیل کردن فیچرها یکی از مراحل ضروری برای اطمینان از عملکرد درست الگوریتم‌های یادگیری ماشینه، مخصوصاً وقتی که از گرادیان کاهشی استفاده می‌کنیم.
  4. انتخاب درست آلفا نقش کلیدی در گرادیان کاهشی داره. با تست مقادیر مختلف و پیدا کردن حداکثر آلفای ممکن می‌تونیم از نوسانات و واگرایی جلوگیری کنیم.
  5. مهندسی فیچر با ساخت فیچرهای جدید می‌تونه مدل رو بهینه‌تر کنه و پیش‌بینی‌های دقیق‌تری ارائه بده.
  6. رگرسیون چندجمله‌ای می‌تونه به ما کمک کنه تا روابط غیرخطی بین متغیرها رو مدل‌سازی کنیم، ولی باید حواسمون به اسکیل کردن فیچرها باشه تا مدل درست کار کنه.
گرادیان کاهشیماشین لرنینگیادگیری ماشین
برنامه نویس MQL و پایتون https://www.youtube.com/@mqlexpert/videos
شاید از این پست‌ها خوشتان بیاید