طبقهبندی کننده بیز ساده (Naive Bayes) طبقهبندی کننده ساده و شناخته شدهای است که در مواقعی که تعداد مشاهدات کمی در دسترس باشد نیز عملکرد خوبی دارد. در این آموزش یک طبقهبندی کننده بیز ساده گاوسی (Gaussian Naive Bayes) را از پایه ایجاد خواهیم کرد و با استفاده از آن، کلاس (طبقهبندی یا برچسب) نقاط داده که از قبل دیده نشدهاند را پیشبینی میکنیم.
این آموزش بر اساس مثالی است که در صفحه ویکیپدیا طبقهبندی کننده بیز ساده وجود دارد. من این مثال را به کمک زبان برنامهنویسی پایتون پیادهسازی و نکتههایی را برای فهم بهتر و سادهتر توضیحها به اون اضافه کردم.
از اونجایی که ترجمه خیلی از اصطلاحها و کلمهها جالب و قشنگ در نمیان و باعث میشن ما خوشحال نباشیم، من اصطلاحاتی که توی متن به کار میبرم رو سعی میکنم واژه اصلیش رو هم کمارش بیارم.
امکان جا نشدن بعضی از فرمولها یا رابطههای ریاضی در صفحه به دلیل این که طولانی هستن وجود داره و برای مشاهده کامل باید روی سیستم خودتون ذخیرشون کنید.
درمورد مفهومی به نام درستی(Likelihood) که در این پست بهش اشاره شده میتونید پست قبلی (مفهوم Likelihood در یادگیری ماشین) رو مطالعه کنید.
کتابخانههای (پکیجهای) مورد نیاز
import pandas as pd import numpy as np
ایجاد داده
مجموعهداده(dataset) ما، داده مربوط به هشت نفر را در بر میگیرد. من از این مجموعه استفاده میکنم تا طبقهبندی کننده(Classifier)ای بسازم که اطلاعات قد، وزن و سایز پای یک فرد را به عنوان ورودی دریافت میکند و به عنوان خروجی، جنسیت او را پیشبینی میکند.
# Create an empty dataframe data = pd.DataFrame() # Create our target variable data['Gender'] = ['male','male','male','male','female','female','female','female'] # Create our feature variables data['Height'] = [6,5.92,5.58,5.92,5,5.5,5.42,5.75] data['Weight'] = [180,190,170,165,100,150,130,150] data['Foot_Size'] = [12,11,12,10,6,8,7,9] # View the data data
جنسیت | قد | وزن | سایز پا | |
---|---|---|---|---|
۰ | مرد | ۶.۰۰ | ۱۸۰ | ۱۲ |
۱ | مرد | ۵.۹۲ | ۱۹۰ | ۱۱ |
۲ | مرد | ۵.۵۸ | ۱۷۰ | ۱۲ |
۳ | مرد | ۵.۹۲ | ۱۶۵ | ۱۰ |
۴ | زن | ۵.۰۰ | ۱۰۰ | ۶ |
۵ | زن | ۵.۵۰ | ۱۵۰ | ۸ |
۶ | زن | ۵.۴۲ | ۱۳۰ | ۷ |
۷ | زن | ۵.۷۵ | ۱۵۰ | ۹ |
مجموعهداده بالا برای ساختن طبقهبندیکننده ما استفاده شده است. در زیر من یک شخص جدید را ایجاد خواهم کرد که از مقدار ویژگی(feature)های اون از قبل اطلاع دارم ولی از جنسیت اون اطلاعی ندارم. در اینجا هدف، پیشبینی جنسیت این شخص هست.
# Create an empty dataframe person = pd.DataFrame() # Create some feature values for this single row person['Height'] = [6] person['Weight'] = [130] person['Foot_Size'] = [8] # View the data person
قد | وزن | سایز پا | |
---|---|---|---|
۰ | ۶ | ۱۳۰ | ۸ |
قضیه بیز (Bayes Theorem)
قضیه بیز یک معادله معروف است که به ما اجازه میدهد بر اساس داده، عمل پیشبینی را انجام دهیم. در زیر نسخه اولیه قضیه بیز نشان داده شده است.
که در آن:
- class یک طبقهبندی یا برچسب خاص است.
- data یک داده مشاهدهشده است.
- posterior (پسین) نامیده میشود.
- likelihood (درستی) نامیده میشود.
- prior (پیشین) نامیده میشود.
- marginal probability (احتمال حاشیهای) نامیده میشود.
در طبقهبندیکننده بیز، ما پسین (posterior) را محاسبه میکنیم (به طور فنی ما فقط صورت کسر پسین را محاسبه میکنیم). سپس مشاهدات را بر اساس کلاسی با بزرگترین مقدار پسین، طبقهبندی میکنیم. در مثال ما دو کلاس(همان جنسیتهای زن و مرد) و یک مشاهده برای پیشبینی داریم. در نتیجه دو پسین محاسبه خواهیم کرد: یکی برای کلاس مرد و یکی برای کلاس زن.
طبقهبندی کننده بیز ساده گاوسی (Gaussian Naive Bayes Classifier)
بیز ساده گاوسی یکی از مشهورترین نوع طبقهبندی کنندههای بیز است. برای توضیح این که این اسم به چه معنی است، اجازه دهید که ببینیم معادله بیز زمانی که دو کلاس (زن و مرد) و سه ویژگی (قد، وزن و سایز پا) را اعمال میکنیم، به چه شکلی در میآید.
حال اجازه دهید که معادله بالا را کمی باز کنیم:
- احتمالهای پیشین(prior) است. این مانند وقتی است که شما به سادگی احتمال مرد بودن یک مشاهده را محاسبه میکنید که در واقع این همان تقسیم تعداد مردهای مجموعهداده به تعداد کل افراد مجموعهداده است.
- درستی(Likelihood) است. توجه کنید که ما داده مربوط به شخص را باز کردیم، پس در آن همه ویژگیهای موجود در مجموعه داده را میبینیم. کلمههای «گاوسی (Gaussian)» و «ساده (Naive)» از این دو فرضی که در این درستی(Likelihood) است گرفتهشدهاند:
- اگر شما به هر یک از عبارتهای likelihood نگاه کنید، متوجه خواهید شد که هر ویژگی با ویژگیهای دیگر، نامرتبط است. به عنوان مثال: مقدار قد با وزن و سایز پا هیچ رابطهای ندارد. واضح است که این فرض صحیحی نیست و همانطور که از اسم «بیز ساده» داریم، یک فرض ساده است.
- ما فرض میکنیم که مقدار ویژگی(feature)ها (مثل: قد یک زن و وزن یک زن) به طور نرمال(گاوسی) توزیع شدهاند. به این معنی که با قرار دادن پارامترهای مورد نیاز در تابع چگالی احتمال توزیع نرمال، محاسبه شدهاست.
- احتمال حاشیهای(marginal probability) به احتمال زیاد یکی از گیجکنندهترین قسمتهای رویکردهای بیزی است. در مثالهای ساده (مانند مثال ما)، محاسبه احتمال حاشیهای کاملا ممکن است. با این وجود در برخی از موارد دنیای واقعی، پیدا کردن مقدار احتمال حاشیهای سخت و یا حتی غیرممکن است (توضیح دلیل این موضوع خارج از محدوده این آموزش است). این موضوع برای طبقهبندیکننده ما، مشکل خاصی ایجاد نمیکند (اگر در حال فکر کردن به آن هستید!). و دلیلش هم این است که برای ما مقدار واقعی پسین(posterior) اهمیت ندارد. برای ما فقط پسینی با بیشترین مقدار اهمیت دارد. و به این دلیل که احتمال حاشیهای برای همه اعضای کلاس برابر است:
- میتوانیم از مخرج کسر صرفنظر کنیم.
- برای هر کلاس، فقط صورت کسر پسین(posterior) را محاسبه کنیم.
- صورت کسری که بیشتر است را انتخاب کنیم. و به همین علت ما میتوانیم از مخرج کسر پسین صرفنظر کنیم و پیشبینی(طبقهبندی) را فقط بر اساس مقدارهای نسبی صورت کسر posterior انجام دهیم.
خب! مطلبهای تئوری تمام شدن. حالا اجازه بدین که محاسبه همه قسمتهای مختلف معادلههای بیز را شروع کنیم.
محاسبه پیشینها (Priors)
پیشینها هم میتوانند مقدارهای ثابت و هم توزیعهای احتمال باشند. در مثال ما، این به سادگی محاسبه احتمال جنسیت افراد است. محاسبه این مقدار ساده است:
# Number of males n_male = data['Gender'][data['Gender'] == 'male'].count() # Number of males n_female = data['Gender'][data['Gender'] == 'female'].count() # Total rows total_ppl = data['Gender'].count()
# Number of males divided by the total rows P_male = n_male/total_ppl # Number of females divided by the total rows P_female = n_female/total_ppl
محاسبه درستی (Likelihood)
به خاطر داشته باشید، فرض شده که هر عبارت (مانند: ) در درستی ما، یک تابع چگالی آمار نرمال است. برای مثال:
این یعنی برای هر ترکیب کلاس (مانند: زن) و ویژگی (مانند: قد)، ما باید مقدار واریانس و میانگین داده را محاسبه کنیم. کتابخانه (پکیج) پاندا (Pandas) این کار را برای ما آسان کردهاست:
# Group the data by gender and calculate the means of each feature data_means = data.groupby('Gender').mean() # View the values data_means
قد | وزن | سایز پا | |
---|---|---|---|
جنسیت | |||
زن | ۵.۴۱۷۵ | ۱۳۲.۵۰ | ۷.۵۰ |
مرد | ۵.۸۵۵۰ | ۱۷۶.۲۵ | ۱۱.۲۵ |
# Group the data by gender and calculate the variance of each feature data_variance = data.groupby('Gender').var() # View the values data_variance
قد | وزن | سایز پا | |
---|---|---|---|
زن | ۰.۰۹۷۲۲۵ | ۵۵۸.۳۳۳۳۳۳ | ۱.۶۶۶۶۶۷ |
مرد | ۰.۰۳۵۰۳۳ | ۱۲۲.۹۱۶۶۶۷ | ۰.۹۱۶۶۶۷ |
حال میتوانیم همه متغیرهایی که لازم داریم را ایجاد کنیم. کد زیر ممکن است که در نگاه اول پیچیده به نظر برسد ولی در واقع همه کاری که ما انجام میدهیم، ایجاد متغیرهای هر سلول از دو جدول بالا است.
# Means for male male_height_mean = data_means['Height'][data_variance.index == 'male'].values[0] male_weight_mean = data_means['Weight'][data_variance.index == 'male'].values[0] male_footsize_mean = data_means['Foot_Size'][data_variance.index == 'male'].values[0] # Variance for male male_height_variance = data_variance['Height'][data_variance.index == 'male'].values[0] male_weight_variance = data_variance['Weight'][data_variance.index == 'male'].values[0] male_footsize_variance = data_variance['Foot_Size'][data_variance.index == 'male'].values[0] # Means for female female_height_mean = data_means['Height'][data_variance.index == 'female'].values[0] female_weight_mean = data_means['Weight'][data_variance.index == 'female'].values[0] female_footsize_mean = data_means['Foot_Size'][data_variance.index == 'female'].values[0] # Variance for female female_height_variance = data_variance['Height'][data_variance.index == 'female'].values[0] female_weight_variance = data_variance['Weight'][data_variance.index == 'female'].values[0] female_footsize_variance = data_variance['Foot_Size'][data_variance.index == 'female'].values[0]
در نهایت، ما باید برای محاسبه چگالی احتمال هر عبارت از درستی(likelihood) (مانند: ) یک تابع(متد) ایجاد کنیم.
# Create a function that calculates p(x | y): def p_x_given_y(x, mean_y, variance_y): # Input the arguments into a probability density function p = 1/(np.sqrt(2*np.pi*variance_y)) * np.exp((-(x-mean_y)**2)/(2*variance_y)) # return p return p
اعمال طبقهبندی کننده بیز به داده جدید
خیلی خب! طبقهبندی کننده بیز ما آماده است. به خاطر داشته باشید که از آنجایی که ما میتوانیم از احتمال حاشیهای(مخرج کسر) چشمپوشی کنیم، چیزی که در واقع ما محاسبه مبکنیم عبارت زیر است:
برای انجام این کار، ما فقط از مقدارهای ویژگیهای مربوط به شخص طبقهبندینشده، متغیرهای مجموعهداده(مانند: میانگین و واریانس قد زنها) و تابع(متد) (p_x_given_y) که در بالا آن را ایجاد کردیم، استفاده کنیم:
# Numerator of the posterior if the unclassified observation is a male P_male * \ p_x_given_y(person['Height'][0], male_height_mean, male_height_variance) * \ p_x_given_y(person['Weight'][0], male_weight_mean, male_weight_variance) * \ p_x_given_y(person['Foot_Size'][0], male_footsize_mean, male_footsize_variance)
6.1970718438780782e-09
# Numerator of the posterior if the unclassified observation is a female P_female * \ p_x_given_y(person['Height'][0], female_height_mean, female_height_variance) * \ p_x_given_y(person['Weight'][0], female_weight_mean, female_weight_variance) * \ p_x_given_y(person['Foot_Size'][0], female_footsize_mean, female_footsize_variance)
0.00053779091836300176
به دلیل این که صورت کسر پسین(posterior) برای زنها بیشتر از مردها است، پیشبینی میکنیم که آن شخص عضو کلاس(طبقهبندی) زن است.
منبع این پست، صفحه زیر نوشته آقای Chris Albon هست و من در واقع در این پست فقط اون رو به فارسی ترجمه کردم. همینجا از نویسنده اصلی بابت نوشتن این مطلب مفید تشکر میکنم.
(.Thank you dear Chris for this useful article)
شاد باشید!