ساخت مجموعهٔ آزمون | سوگیری وارسی داده و نمونه‌گیری طبقه‌بندی‌شده

ساخت مجموعهٔ آزمون و نمونه‌گیری طبقه‌بندی‌شده

توسط admin | گروه هوش مصنوعی | 1405/06/01

نظرات 0

ساخت مجموعهٔ آزمون و نمونه‌گیری طبقه‌بندی‌شده

عنوان اصلی
Create a Test Set; Data Snooping Bias; Stable Train/Test Split; Stratified Sampling; Explore and Visualize the Data to Gain Insights
عنوان ترجمه‌شده
ساخت مجموعهٔ آزمون و نمونه‌گیری طبقه‌بندی‌شده
اثر
Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow - ویرایش سوم
نویسنده
Aurélien Géron
سمت/سابقهٔ نویسنده
مشاور یادگیری ماشین؛ مدیر پیشین تیم طبقه‌بندی ویدئوی YouTube
زبان اصلی
انگلیسی
صفحات منبع
18-22 از PDF فعلی؛ صفحات چاپی کتاب 56-60
وضعیت حقوق
حق‌نشر اثر اصلی © 2023 Aurélien Géron؛ کاربر حق ترجمه و استفاده/بازنشر را برای این پردازش تأیید کرده است.
تاریخ ترجمه
1405/06/01 / 2026-08-23
اعتبار ترجمه
ترجمه با کمک هوش مصنوعی

ساخت مجموعهٔ آزمون

ممکن است در این مرحله کنارگذاشتن داوطلبانهٔ بخشی از داده عجیب به نظر برسد. تا اینجا فقط نگاهی سریع به داده انداخته‌اید و منطقی است فکر کنید پیش از انتخاب الگوریتم باید اطلاعات بسیار بیشتری دربارهٔ آن به دست آورید. این حرف درست است، اما مغز انسان سامانه‌ای شگفت‌انگیز برای کشف الگو است و درست به همین دلیل به بیش‌برازش نیز بسیار مستعد است. اگر مجموعهٔ آزمون را ببینید، ممکن است ناخواسته الگویی ظاهراً جالب در آن پیدا کنید و بر پایهٔ آن نوع خاصی از مدل یادگیری ماشین را انتخاب کنید. سپس وقتی خطای تعمیم را با همان مجموعهٔ آزمون اندازه می‌گیرید، برآوردتان بیش از حد خوش‌بینانه می‌شود و سامانه‌ای را راه‌اندازی می‌کنید که در عمل به خوبی انتظار شما کار نمی‌کند. این پدیده «سوگیری ناشی از وارسی داده» یا data snooping bias نام دارد.

در نظریه، ساخت مجموعهٔ آزمون ساده است: تعدادی نمونه را به‌صورت تصادفی، معمولاً حدود ۲۰ درصد کل مجموعه‌داده - یا کمتر اگر مجموعه‌داده بسیار بزرگ باشد - انتخاب و کنار بگذارید:

import numpy as np

def shuffle_and_split_data(data, test_ratio):
    shuffled_indices = np.random.permutation(len(data))
    test_set_size = int(len(data) * test_ratio)
    test_indices = shuffled_indices[:test_set_size]
    train_indices = shuffled_indices[test_set_size:]
    return data.iloc[train_indices], data.iloc[test_indices]

اکنون می‌توانید تابع را به این صورت استفاده کنید:

>>> train_set, test_set = shuffle_and_split_data(housing, 0.2)
>>> len(train_set)
16512
>>> len(test_set)
4128

این روش کار می‌کند، اما بی‌نقص نیست: اگر برنامه را دوباره اجرا کنید، مجموعهٔ آزمون دیگری ساخته می‌شود. با گذشت زمان، شما یا الگوریتم یادگیری ماشین عملاً همهٔ مجموعه‌داده را خواهید دید؛ دقیقاً چیزی که می‌خواستید از آن جلوگیری کنید.

یک راه این است که مجموعهٔ آزمون را در نخستین اجرا ذخیره کنید و در اجراهای بعد همان را بارگذاری کنید. راه دیگر این است که پیش از فراخوانی np.random.permutation()، Seed مولد اعداد تصادفی را مثلاً با np.random.seed(42) تنظیم کنید تا همیشه همان اندیس‌های برزده‌شده تولید شوند.۶

بااین‌حال هر دو راه وقتی نسخهٔ تازه‌ای از مجموعه‌داده دریافت کنید دچار مشکل می‌شوند. برای داشتن تقسیم آموزش/آزمون پایدار حتی پس از به‌روزرسانی داده، یک روش رایج این است که از شناسهٔ هر نمونه برای تصمیم‌گیری دربارهٔ قرارگرفتن آن در مجموعهٔ آزمون استفاده کنید؛ به شرط آنکه نمونه‌ها شناسه‌های یکتا و تغییرناپذیر داشته باشند. برای مثال می‌توانید Hash شناسهٔ هر نمونه را محاسبه کنید و اگر مقدار Hash کمتر یا مساوی ۲۰ درصد بیشینهٔ مقدار Hash بود، نمونه را در مجموعهٔ آزمون قرار دهید. این روش تضمین می‌کند مجموعهٔ آزمون در اجراهای مختلف، حتی پس از تازه‌سازی مجموعه‌داده، سازگار بماند. مجموعهٔ آزمون جدید شامل ۲۰ درصد نمونه‌های جدید خواهد بود، اما هیچ نمونه‌ای که قبلاً در مجموعهٔ آموزشی بوده به مجموعهٔ آزمون منتقل نمی‌شود.

یک پیاده‌سازی ممکن:

from zlib import crc32

def is_id_in_test_set(identifier, test_ratio):
    return crc32(np.int64(identifier)) < test_ratio * 2**32

def split_data_with_id_hash(data, test_ratio, id_column):
    ids = data[id_column]
    in_test_set = ids.apply(lambda id_: is_id_in_test_set(id_, test_ratio))
    return data.loc[~in_test_set], data.loc[in_test_set]

متأسفانه مجموعه‌دادهٔ مسکن ستون شناسه ندارد. ساده‌ترین راه این است که از اندیس سطر به‌عنوان شناسه استفاده کنید:

housing_with_id = housing.reset_index()  # adds an `index` column
train_set, test_set = split_data_with_id_hash(housing_with_id, 0.2, "index")

اگر اندیس سطر را شناسهٔ یکتا در نظر بگیرید، باید مطمئن باشید دادهٔ جدید همیشه به انتهای مجموعه‌داده افزوده می‌شود و هیچ سطری هرگز حذف نمی‌گردد. اگر چنین تضمینی ندارید، می‌توانید از پایدارترین ویژگی‌ها برای ساخت یک شناسهٔ یکتا استفاده کنید. برای مثال، عرض و طول جغرافیایی یک ناحیه برای چند میلیون سال پایدار خواهند ماند، بنابراین می‌توانید آن‌ها را به شکل زیر ترکیب کنید:۷

housing_with_id["id"] = housing["longitude"] * 1000 + housing["latitude"]
train_set, test_set = split_data_with_id_hash(housing_with_id, 0.2, "id")

Scikit-Learn چند تابع برای تقسیم مجموعه‌داده به زیرمجموعه‌های گوناگون ارائه می‌کند. ساده‌ترین آن‌ها train_test_split() است که تقریباً همان کار تابع shuffle_and_split_data() را انجام می‌دهد، اما چند قابلیت اضافی دارد. نخست، پارامتر random_state اجازه می‌دهد Seed مولد تصادفی را تعیین کنید. دوم، می‌توانید چند مجموعه‌داده با تعداد سطر یکسان به آن بدهید تا همه را روی اندیس‌های یکسان تقسیم کند؛ این قابلیت وقتی برچسب‌ها در DataFrame جداگانه‌ای قرار دارند بسیار مفید است:

from sklearn.model_selection import train_test_split
train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)

خطر سوگیری نمونه‌گیری

تا اینجا فقط روش‌های نمونه‌گیری کاملاً تصادفی را در نظر گرفتیم. اگر مجموعه‌داده به اندازهٔ کافی بزرگ باشد - به‌ویژه در مقایسه با تعداد ویژگی‌ها - این روش عموماً مناسب است؛ اما در مجموعه‌های کوچک‌تر خطر ایجاد سوگیری نمونه‌گیری جدی وجود دارد.

وقتی کارکنان یک شرکت نظرسنجی می‌خواهند برای پرسیدن چند سؤال با ۱۰۰۰ نفر تماس بگیرند، صرفاً ۱۰۰۰ نام تصادفی از دفترچه تلفن انتخاب نمی‌کنند. آن‌ها می‌کوشند این ۱۰۰۰ نفر، با توجه به پرسش‌هایی که مطرح می‌شود، نمایندهٔ کل جمعیت باشند. برای نمونه، جمعیت ایالات متحده ۵۱٫۱ درصد زن و ۴۸٫۹ درصد مرد است؛ بنابراین یک نظرسنجی درست تلاش می‌کند این نسبت را در نمونه حفظ کند: ۵۱۱ زن و ۴۸۹ مرد، دست‌کم اگر احتمال داده شود پاسخ‌ها با جنسیت تفاوت داشته باشند.

این روش «نمونه‌گیری طبقه‌بندی‌شده» نام دارد: جمعیت به زیرگروه‌های همگن، یا «طبقه‌ها»، تقسیم می‌شود و از هر طبقه تعداد مناسبی نمونه انتخاب می‌شود تا مجموعهٔ آزمون نمایندهٔ کل جمعیت باشد. اگر مسئولان نظرسنجی از نمونه‌گیری صرفاً تصادفی استفاده کنند، حدود ۱۰٫۷ درصد احتمال دارد نمونه‌ای نامتوازن با کمتر از ۴۸٫۵ درصد یا بیش از ۵۳٫۵ درصد شرکت‌کنندهٔ زن به دست آید؛ در هر دو حالت نتیجهٔ نظرسنجی احتمالاً سوگیری محسوسی خواهد داشت.

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

با نگاه دقیق‌تر به هیستوگرام درآمد میانه، بیشتر مقادیر در بازهٔ ۱٫۵ تا ۶ - یعنی تقریباً ۱۵ هزار تا ۶۰ هزار دلار - متمرکزند، اما بعضی درآمدها بسیار بالاتر از ۶ می‌روند. لازم است در هر طبقه تعداد کافی نمونه وجود داشته باشد؛ وگرنه برآورد اهمیت آن طبقه ممکن است سوگیری پیدا کند. بنابراین نباید تعداد طبقه‌ها بیش از حد زیاد باشد و هر طبقه باید به‌اندازهٔ کافی بزرگ بماند. کد زیر با pd.cut() ویژگی دستهٔ درآمد را با پنج دسته، برچسب‌خورده از ۱ تا ۵، می‌سازد. دستهٔ ۱ از صفر تا ۱٫۵ - کمتر از ۱۵ هزار دلار -، دستهٔ ۲ از ۱٫۵ تا ۳ و به همین ترتیب ادامه دارد:

housing["income_cat"] = pd.cut(housing["median_income"],
                               bins=[0., 1.5, 3.0, 4.5, 6., np.inf],
                               labels=[1, 2, 3, 4, 5])

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

housing["income_cat"].value_counts().sort_index().plot.bar(rot=0, grid=True)
plt.xlabel("Income category")
plt.ylabel("Number of districts")
plt.show()
هیستوگرام دسته‌های درآمدی برای نمونه‌گیری طبقه‌بندی‌شده
شکل ۲-۹. هیستوگرام دسته‌های درآمد

نمونه‌گیری طبقه‌بندی‌شده با Scikit-Learn

اکنون آماده‌اید نمونه‌گیری طبقه‌بندی‌شده را بر اساس دستهٔ درآمد انجام دهید. Scikit-Learn در بستهٔ sklearn.model_selection چند کلاس تقسیم‌کننده دارد که راهبردهای گوناگونی برای تقسیم مجموعه‌داده به مجموعهٔ آموزشی و آزمون پیاده‌سازی می‌کنند. هر تقسیم‌کننده متد split() دارد که یک Iterator از تقسیم‌های مختلف آموزش/آزمون روی همان داده بازمی‌گرداند.

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

from sklearn.model_selection import StratifiedShuffleSplit

splitter = StratifiedShuffleSplit(n_splits=10, test_size=0.2, random_state=42)
strat_splits = []
for train_index, test_index in splitter.split(housing, housing["income_cat"]):
    strat_train_set_n = housing.iloc[train_index]
    strat_test_set_n = housing.iloc[test_index]
    strat_splits.append([strat_train_set_n, strat_test_set_n])

فعلاً فقط از نخستین تقسیم استفاده کنید:

strat_train_set, strat_test_set = strat_splits[0]

و چون نمونه‌گیری طبقه‌بندی‌شده بسیار رایج است، برای گرفتن فقط یک تقسیم می‌توانید از روش کوتاه‌تر، یعنی train_test_split() همراه آرگومان stratify، استفاده کنید:

strat_train_set, strat_test_set = train_test_split(
    housing, test_size=0.2, stratify=housing["income_cat"], random_state=42)

برای بررسی نتیجه، ابتدا نسبت دسته‌های درآمد را در مجموعهٔ آزمون ببینید:

>>> strat_test_set["income_cat"].value_counts() / len(strat_test_set)
3    0.350533
2    0.318798
4    0.176357
5    0.114341
1    0.039971
Name: income_cat, dtype: float64

با کدی مشابه می‌توانید نسبت دسته‌های درآمد را در کل مجموعه‌داده نیز اندازه بگیرید. جدول شکل ۲-۱۰ نسبت دسته‌های درآمد را در کل داده، در مجموعهٔ آزمون ساخته‌شده با نمونه‌گیری طبقه‌بندی‌شده و در مجموعهٔ آزمون ساخته‌شده با نمونه‌گیری صرفاً تصادفی مقایسه می‌کند. همان‌طور که دیده می‌شود، نسبت‌ها در نمونهٔ طبقه‌بندی‌شده تقریباً با کل مجموعه‌داده یکسان‌اند، درحالی‌که نمونهٔ تصادفی انحراف دارد.

شکل ۲-۱۰. مقایسهٔ سوگیری نمونه‌گیری: طبقه‌بندی‌شده در برابر کاملاً تصادفی
دستهٔ درآمدکل داده، ٪طبقه‌بندی‌شده، ٪تصادفی، ٪خطای طبقه‌بندی‌شده، ٪خطای تصادفی، ٪
۱3.984.004.240.366.45
۲31.8831.8830.74-0.02-3.59
۳35.0635.0534.52-0.01-1.54
۴17.6317.6418.410.034.42
۵11.4411.4312.09-0.085.63

دیگر به ستون income_cat نیاز ندارید؛ بنابراین بهتر است آن را حذف کنید تا داده به حالت اصلی بازگردد:

for set_ in (strat_train_set, strat_test_set):
    set_.drop("income_cat", axis=1, inplace=True)

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

کاوش و مصورسازی داده برای کسب بینش

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

پاورقی‌ها

  1. زیاد می‌بینید که Seed تصادفی روی ۴۲ تنظیم می‌شود. این عدد هیچ ویژگی ریاضی خاصی ندارد؛ فقط در کتاب «راهنمای مسافران مجانی کهکشان» پاسخ «پرسش نهایی زندگی، جهان و همه‌چیز» است.
  2. اطلاعات مکانی در واقع نسبتاً کم‌دقت است؛ در نتیجه بسیاری از ناحیه‌ها دقیقاً شناسهٔ یکسان خواهند داشت و همگی در یک مجموعه، آزمون یا آموزش، قرار می‌گیرند. این امر مقداری سوگیری نمونه‌گیری ناخوشایند ایجاد می‌کند.

امتیاز کاربران به این مقاله

☆☆☆☆☆

0 نفر امتیاز داده اند. میانگین: 0.0 از 5

 

0 نظر

نظر محترم شما در مورد مقاله های وب سایت برنامه نویسی و پایگاه داده

نظرات محترم شما در خدمات رسانی بهتر ما را یاری می نمایند. لطفا اگر مایل بودید یک نظر ما را مهمان فرمائید. آدرس ایمیل و وب سایت شما نمایش داده نخواهد شد.

0 / 500

اطلاعات تماس

  • آدرس:اصفهان-خیابان ام کلثوم غربی - بعد خیابان تخم چی - بیست متر بعد از پیتزا ننه شب - کوچه تعمیر گاه سمار زغالی - پلاک 354 - درب مشکی - طبقه هفتم
  • آدرس ایمیل:najafzade@gmail.com
  • وب سایت:http://www.a00b.com/
  • تلفن ثابت:(+98)9131253620
  • تلفن همراه:09131253620