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

دلایل عدم تعادل داده‌ها

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

  1. ممکن است مدل تنها دسته‌هایی با تعداد داده‌های بیشتر را آموزش دیده باشد.
  2. ممکن است مدل برای تمامی دسته‌ها از دقت خوبی برخوردار باشد، ولی با گرفتن داده‌های جدید که در اقلیّت هستند، همان دقت را نداشته باشد.

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

برای مثال، فرض کنید یک مسئله طبقه‌بندی با 4 دسته وجود دارد که سهم هر دسته از کل مجموعه داده به شرح زیر است:

سهم از کل دسته
9% 1
12% 2
71% 3
8% 4

اگر یک مدل Dummy (مدلی که بدون توجه با داده ورودی، تنها Label مربوط به دسته غالب را در خروجی می‌دهد) روی این مجموعه داده آموزش دهیم، خیلی ساده به دقت %71 می‌رسیم که در ظاهر عدد بسیار مناسبی است. اما با بررسی Recall و F1 Score مربوط به سایر دسته‌ها، متوجه اشکال مدل می‌شویم.

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

  1. وزن‌دهی به هر دسته با توجه به تعداد داده‌های آن
  2. تغییر مجموعه داده:
    1. حذف بخشی از داده‌های دسته‌های موجود در اکثریت
    2. افزایش داده‌های دسته‌های موجود در اقلیت

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

  • برای مشاهده مجموعه آموزش‌های برنامه نویسی پایتون (Python) — مقدماتی تا پیشرفته + اینجا کلیک کنید.

متعادل کردن داده در پایتون: روش وزن دهی دسته ها

در این مطلب می‌خواهیم از روش اول استفاده کنیم.

به این منظور وارد محیط برنامه‌نویسی شده و کتابخانه‌های مورد نیاز را فراخوانی می‌کنیم:

import numpy as np
import sklearn.metrics as met
import sklearn.datasets as dt
import sklearn.linear_model as li
import sklearn.model_selection as ms

هر فراخوانی به ترتیب برای موارد زیر مورد استفاده قرار خواهد گرفت:

  1. کار و عملیات روی آرایه‌ها
  2. محاسبه معیارهای سنجش مدل‌ها
  3. فراخوانی داده
  4. استفاده از مدل‌های خطی
  5. تقسیم داده

حال Random State را تنظیم می‌کنیم:

np.random.seed(0)

برای شروع، ابتدا باید یک مجموعه داده انتخاب و آن را فراخوانی کنیم. به این منظور از مجموعه داده IRIS استفاده خواهیم کرد:

IRIS = dt.load_iris()
X = IRIS.data
Y = IRIS.target
TN = IRIS.target_names

به این ترتیب، ویژگی‌های ورودی، ویژگی‌های خروجی و اسم دسته‌ها استخراج می‌شود.

حال تعداد داده‌های هر کلاس را محاسبه می‌کنیم:

N = {tn: Y[Y == i].size for i, tn in enumerate(TN)}

به این ترتیب، تعداد داده‌های هر دسته با کلید مربوط به اسم مروبوطه در دیکشنری N ذخیره می‌شود. با خروجی گرفتن از N خواهیم داشت:

{'setosa': 50, 'versicolor': 50, 'virginica': 50}

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

حال مجموعه داده‌های آموزش و آزمایش را جدا می‌کنیم:

trX, teX, trY, teY = ms.train_test_split(X, Y,
                                         train_size=0.7,
                                         random_state=0)

یک مدل «رگرسیون لجستیک» (Logistic Regression) روی مجموعه داده آموزش می‌دهیم:

Model = li.LogisticRegression(random_state=0)
Model.fit(trX, trY)

دقت مدل را برای مجموعه داده آموزش و آزمایش محاسبه و نمایش می‌دهیم:

trAc = 100 * Model.score(trX, trY)
teAc = 100 * Model.score(teX, teY)

print(f'Train Accuracy: {round(trAc, 2)} %')
print(f'Test  Accuracy: {round(teAc, 2)} %')

که خواهیم داشت:

trAc = 98.09 %
teAc = 97.77 %

به این ترتیب مشاهده می‌کنیم که دقت بسیار مناسب است.

حال Classification Report را محاسبه و نشان می‌دهیم:

tePr = Model.predict(teX)

teCR = met.classification_report(teY, tePr, target_names=TN)

print(f'Test Classification Report:\n{teCR}')

که در این بخش نیز خروجی زیر حاصل می‌شود:

Test Classification Report:

               precision    recall  f1-score   support

      setosa       1.00      1.00      1.00        16
  versicolor       1.00      0.94      0.97        18
   virginica       0.92      1.00      0.96        11

    accuracy                           0.98        45
   macro avg       0.97      0.98      0.98        45
weighted avg       0.98      0.98      0.98        45

و به این ترتیب مشاهده می‌کنیم که مقدار Recall و F1 Score برای تمامی دسته‌ها مناسب است و بنابراین در این حالت مشکلی وجود ندارد.

حال مقداری عدم تعادل در اندازه دسته‌ها ایجاد می‌کنیم. برای این منظور، 50 داده از دسته اول، 10 داده از دسته دوم و 15 داده از دسته سوم انتخاب می‌کنیم.

حال شماره داده‌ها را به شکل زیر تعیین می‌کنیم:

ind = list(range(0, 50)) + list(range(50, 60)) + list(range(100, 115))

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

X = IRIS.data[ind]
Y = IRIS.target[ind]

حال اگر دیکشنری N را نمایش دهیم، به اعداد زیر می‌رسیم:

{'setosa': 50, 'versicolor': 10, 'virginica': 15}

به این ترتیب، دسته‌های نامتعادل ایجاد شدند.

حال اگر مدل را آموزش دهیم به دقت‌های زیر می‌رسیم:

Train Accuracy: 100.0 %
Test  Accuracy: 78.26 %

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

Test Classification Report:

               precision    recall  f1-score   support


      setosa       0.92      1.00      0.96        12
  versicolor       0.67      0.33      0.44         6
   virginica       0.57      0.80      0.67         5

    accuracy                           0.78        23
   macro avg       0.72      0.71      0.69        23
weighted avg       0.78      0.78      0.76        23

متوجه می‌شویم که Recall و F1 Score برای دسته‌های Versicolor و Virginica مناسب نیست.

بنابراین، اثر نامتعادل بودن دسته‌ها به این شکل مشاهده می‌شود.

رفع مشکل

حال می‌خواهیم با تعریف وزن برای هر دسته، تعادل را بین دسته‌ها برقرار کنیم. برای این کار ابتدا باید مقدار وزن مورد نیاز را شرایط مورد نیاز برای وزن‌ها را بررسی کنیم. اگر دو دسته با شماره‌های 1 و 2 داشته باشید و به ترتیب دارای $$n_1$$ و $$n_2$$ داده باشند، وزن‌های تعریف‌شده باید در روابط زیر صدق کنند:

$$ \large \begin {align}
w_1+w_2 & =1 \\
w_1 n_1 & = w_2 n_2
\end {align} $$

از حل این دستگاه دومعادله و دو مجهول به جواب‌های زیر می‌رسیم:

$$ \large \begin {align}
w_1 & = \frac {n_2}{n_1+n_2 }\\
w_2 & = \frac {n_1}{n_1+n_2 }
\end {align} $$

که اگر این روابط را تعمیم دهیم، خواهیم داشت:

$$ \large w_x=\frac {\sum n_i -n_x}{(C-1) \sum n _ i } $$

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

nTotal = Y.size

W = {i: (nTotal - N[TN[i]]) / (2 * nTotal) for i in range(3)}

به این ترتیب، دیکشنری وزن‌ها ایجاد می‌شود که مقادیر موجود در آن به‌صورت زیر است:

{0: 0.16, 1: 0.43, 2: 0.40}

حال مشاهده می‌کنیم که مجموع وزن‌ها 1 و مقدار وزن هر دسته متناسب با تعداد داده‌های آن داده است.

اکنون مدل ایجاد شده را تغییر داده و وزن دسته‌ها را به آن می‌دهیم:

Model = li.LogisticRegression(random_state=0, class_weight=W)

پس از آموزش مدل، دقت‌ها به مقادیر زیر می‌رسد:

Train Accuracy: 94.23 %
Test Accuracy: 78.26 %

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

همچنین Classification Report به شکل زیر درخواهد آمد:

Test Classification Report:

               precision    recall  f1-score   support

       setosa       0.92      1.00      0.96        12
  versicolor       1.00      0.17      0.29         6
   virginica       0.56      1.00      0.71         5

     accuracy                           0.78        23
   macro avg       0.83      0.72      0.65        23
weighted avg       0.86      0.78      0.73        23

که مشاهده می‌کنیم مقدار Recall و F1 Score برای دسته virginica بهبود یافته است.

توجه داشته باشید که کاهش معیار‌های گفته شده برای دسته Versicolor را نیز داریم، اما در کل avg برای معیار‌های مختلف افزایش یافته است که نشانه خوبی از اثر وزن‌دهی است.

به این ترتیب، توانستیم یک مدل رگرسیون لجستیک (Logistic Regression) را روی مجموعه داده‌ تغییر یافته IRIS آموزش دهیم و با وجود نامتعادل بودن دسته‌ها، نتایج مناسب‌تری بگیریم.

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

معرفی فیلم آموزش یادگیری ماشین Machine Learning با پایتون Python

آموزش یادگیری ماشین Machine Learning با پایتون Python

فیلم آموزش یادگیری ماشین Machine Learning با پایتون Python در ۱۰ ساعت و در قالب ۹ درس ارائه شده است. عناوین درس‌های این آموزش، عبارت‌اند از:‌ مقدمه‌ای در رابطه با یادگیری ماشین، آشنایی با بسته NumPy، آشنایی با بسته Pandas، ترسیم داده‌ها، آشنایی مقدماتی با مباحث آماری، پیش‌پردازش داده‌ها، یادگیری نظارت‌شده، یادگیری غیرنظارت‌شده و کاهش ابعاد.

جمع‌بندی

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

اگر این مطلب برای شما مفید بوده است، آموزش‌ها و مطالب زیر نیز به شما پیشنهاد می‌شوند:

بر اساس رای ۱۱ نفر
آیا این مطلب برای شما مفید بود؟
شما قبلا رای داده‌اید!
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.

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

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد.

مشاهده بیشتر