تنسورفلو ۸ : دسته بندی خطی

دسته بندی خطی چیست؟

در یادگیری تحت نظارت رگرسیون خطی و دسته بندی خطی رایج ترین الگوریتم ها هستند. رگرسیون خطی یک مقدار رو تخمین می زنه و دسته بندی خطی یک دسته رو.

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

اگه دو برچسب داشته باشیم دسته بندی دوگانه هست و در غیر این صورت چندگانه

مثال دسته بندی دوگانه این هست که آیا یک مشتری برای بار دوم خرید می کند یا نمی کند. و تشخیص نوع حیوان موجود در تصویر چند گانه هست.



دسته بندی دوگانه چگونه کار می کند؟

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

در یک دسته بندی دوگانه برچسب دو عدد صحیح مختلف می تونه باشه. مثلا ۰ یا ۱. که مثلا صفر ینی خرید مجدد انجام نمیده و یک ینی خرید مجدد انجام میده.

احتمال موفقیت به وسیله <<رگرسیون منطقی>> تشخیص داده میشه

تابع به دو قسمت تقسیم میشه

  • مدل خطی
  • تابع منطقی



مدل خطی

شما از قبل با اینکه چجوری وزن ها محاسبه میشه آشنا هستید وزن ها نمایانگر نحوه ارتباط متغیر های x و y هست.

مدل خطی در پایان یک عدد حقیقی بر میگردونه که به وسیله تابع منطقی به صفر یا یک تبدیل میشه.

تابع منطقی

تابع منطقی یا تابع سیگموئید شکلی شبیه به S داره که خروجی تابع همیشه بین صفر و یک هست و اگه عدد خروجی بین ۰ تا ۰.۴۹ بود مثلا میگیم در دسته ۰ و اگه بین ۰.۵ تا ۱ بود در دسته ۱ قرار می گیره.


چگونه کارایی یک مدل دسته بندی خطی را بسنجیم؟

دقت مدل

کارایی کلی یک دسته بند به وسیله میزان دقت اندازه گرفته می شه. برای محاسبه دقت تعداد پاسخ های درست رو بر تعداد کل مشاهدات تقسیم می کنیم. مثلا دقت ۸۰٪ ینی مدل برای ۸۰ درصد داده ها به درستی تخمین زده.

البته یک نکته ای که باید دقت کنیم تعادل داشتن پایگاه دانش هست. مثلا اگه ما یه دیتاستی داشته باشیم درباره تعداد مرگ و میر و ۹۵ ٪ داده ها عضو دسته مرگ باشند و بقیه عضو دسته کسایی که فوت نکردن. اگه مدل ما همیشه فوت کردن رو پیش بینی کنه می تونه دقت ۹۵ ٪ داشته باشه!!!!

ماتریس درهم‌ریختگی

ماتریس درهم‌ریختگی دقت یک دسته بند رو بر اساس مقایسه مقدار واقعی و مقدار تخمین زده شده محاسبه می کنه و دارای اجزا زیر هست:

  • مثبت درست: TP : مقدار تخمین زده شده به درستی به عنوان درست تخمین زده شده است
  • مثبت نادرست: FP : مقدار تخمین زده شده به اشتباه به عنوان درست تخمین زده شده است
  • منفی نادرست : FN: مقدار تخمین زده شده به اشتباه به عنوان نادرست تخمین زده شده است
  • منفی درست: TN : مقدار تخمین زده شده به درستی به عنوان نادرست تخمین زده شده است

دقت و حساسیت

به وسیله ماتریس درهم‌ریختگی میشه به طور دقیق تر کارایی رو محاسبه کرد.

صحت (Precision)

معیار صحت نشان می دهد پیش بینی کلاس های مثبت چه تعداد درست است.


وقتی که مدل به درستی تمام مقادیر مثبت را پیش بینی کرده مقدار یک هست.این معیار به تنهایی کافی نیست زیرا مقادیر منفی را در نظر نمی گیرد عموما با معیار حساسیت باهم بررسی می شود.

حساسیت(Recall)

حساسیت نسبت مقادیر مثبت درست تخمین شده رو نشون میده. این معیار نشان می دهد که چه مقدار یک مدل در تشخیص اعضا کلاس مثبت خوب عمل می کند.

دسته بندی خطی با TensorFlow

برای این آموزش از داده های سرشماری استفاده می کنیم. هدف این است که با استفاده از مقادیر دیتاست سرشماری سطح درآمد فرد رو تخمین بزنیم که خروجی دوگانه هست. برای مقادیر بیشتر از 50k یک و در غیر این صورت صفر بر می گرداند.

دیتاست شامل مقادیر زیر می شود:

  • محل کار
  • تحصیلات
  • وضعیت ازدواج
  • اشتغال
  • رابطه
  • نژاد
  • جنسیت
  • کشور اصلی

و مقادیر پیوسته:

  • age
  • fnlwgt
  • education_num
  • capital_gain
  • capital_loss
  • hours_week

با این آموزش قراره با دسته بندی توسط تنسورفلو آشنا بشیم و اینکه چجوری دقت مدل رو افزایش بدیم.

مرحله ۱) ورود داده ها

ابتدا لایبرری ها رو وارد می کنیم

import tensorflow as tf
import pandas as pd

سپس دیتا ها رو وارد می کنیم.

## Define path data
COLUMNS = ['age','workclass', 'fnlwgt', 'education', 'education_num', 'marital',
'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss',
'hours_week', 'native_country', 'label']
PATH = "https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data"
PATH_test = "https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.test"
df_train = pd.read_csv(PATH, skipinitialspace=True, names = COLUMNS, index_col=False)
df_test = pd.read_csv(PATH_test,skiprows = 1, skipinitialspace=True, names = COLUMNS, index_col=False)
print(df_train.shape, df_test.shape)
print(df_train.dtypes)

تنسورفلو برای تعلیم مدل نیاز به یک مقدار Boolean داره برای کلاس بندی داده ها. و باید string ها رو به int تبدیل کنیم

label = {'<=50K': 0,'>50K': 1}
df_train.label = [label[item] for item in df_train.label]
label_t = {'<=50K.': 0,'>50K.': 1}
df_test.label = [label_t[item] for item in df_test.label]
print(df_train["label"].value_counts())
### The model will be correct in atleast 70% of the case
print(df_test["label"].value_counts())
## Unbalanced label
print(df_train.dtypes)

مرحله ۲) تبدیل داده ها

باید داده ها رو برای ورود به مدل آماده کنیم. تخمین گر نیاز به لیست ویژگی ها برای تعلیم مدل داره و از این رو باید داده ستون ها به tensor تبدیل شود.

یک کار خوب تعریف دو لیست از مقادیر بر حسب نوع آن هاست (پیوسته و گسسته)

و س

ویژگی های این دیتاست از دو نوع هستند.

  • Integer
  • Object

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

## Add features to the bucket: 
### Define continuous list
CONTI_FEATURES = ['age', 'fnlwgt','capital_gain', 'education_num', 'capital_loss', 'hours_week']
### Define the categorical list
CATE_FEATURES = ['workclass', 'education', 'marital', 'occupation', 'relationship', 'race', 'sex', 'native_country']

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

def print_transformation(feature = "age", continuous = True, size = 2): 
    #X = fc.numeric_column(feature)
    ## Create feature name
    feature_names = [
    feature]

    ## Create dict with the data
    d = dict(zip(feature_names, [df_train[feature]]))

    ## Convert age
    if continuous == True:
        c = tf.feature_column.numeric_column(feature)
        feature_columns = [c]
    else: 
        c = tf.feature_column.categorical_column_with_hash_bucket(feature, hash_bucket_size=size) 
        c_indicator = tf.feature_column.indicator_column(c)
        feature_columns = [c_indicator]
    
## Use input_layer to print the value
    input_layer = tf.feature_column.input_layer(
        features=d,
        feature_columns=feature_columns
        )
    ## Create lookup table
    zero = tf.constant(0, dtype=tf.float32)
    where = tf.not_equal(input_layer, zero)
    ## Return lookup tble
    indices = tf.where(where)
    values = tf.gather_nd(input_layer, indices)
    ## Initiate graph
    sess = tf.Session()
    ## Print value
    print(sess.run(input_layer))
print_transformation(feature = "age", continuous = True) 
continuous_features = [tf.feature_column.numeric_column(k) for k in CONTI_FEATURES]

برای تبدیل داده های گسسته راه های مختلفی وجود داره یکی اینکه مثلا اگه داده ها محدود هستن تبدیل به id شود مثلا همسر، شوهر،مجرد بشه ۱ ۲ ۳

یک راه دیگه اینه که هر متغییری یک ستون جدا بشه و مقدار صفر یا یک داشته باشه

مثلا ستون مرد و ستون زن بجای تک ستون جنسیت

print_transformation(feature = "sex", continuous = False, size = 2)
relationship = tf.feature_column.categorical_column_with_vocabulary_list(
'relationship', [
'Husband', 'Not-in-family', 'Wife', 'Own-child', 'Unmarried',
'Other-relative'])

خود تنسورفلو یک متد خوب برای تبدیل متغیر های گسته داره که در زیر می بینید. تعداد باکت هم ینی این که تنسورفلو در کل چه تعداد گروه می تونه بسازه:

categorical_features = [tf.feature_column.categorical_column_with_hash_bucket(k, hash_bucket_size=1000) for k in CATE_FEATURES]

مرحله ۳) یادگیری مدل

برای تعریف مدل از کد زیر استفاده می کنیم که ورودی ها به ترتیب تعداد کلاس های طبقه بندی، دایرکتوری مدل و ستون های مدل هست

model = tf.estimator.LinearClassifier(
n_classes = 2,
model_dir="ongoing/train", 
feature_columns=categorical_features+ continuous_features)

و در کد های زیر تابع ورودی داده ها رو می سازیم

FEATURES = ['age','workclass', 'fnlwgt', 'education', 'education_num', 'marital', 'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss', 'hours_week', 'native_country']
LABEL= 'label'
def get_input_fn(data_set, num_epochs=None, n_batch = 128, shuffle=True):
    return tf.estimator.inputs.pandas_input_fn(
       x=pd.DataFrame({k: data_set[k].values for k in FEATURES}),
       y = pd.Series(data_set[LABEL].values),
       batch_size=n_batch,   
       num_epochs=num_epochs,
       shuffle=shuffle)

و با کد های زیر شروع می کنیم مدل رو تعلیم بدیم

model.train(input_fn=get_input_fn(df_train, 
                                      num_epochs=None,
                                      n_batch = 128,
                                      shuffle=False),
                                      steps=1000)

برای تخمین میزان کارایی مدل از کد زیر استفاده می کنیم

model.evaluate(input_fn=get_input_fn(df_test, 
                                      num_epochs=1,
                                      n_batch = 128,
                                      shuffle=False),
                                      steps=1000)

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

مرحله ۴) بهبود مدل

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

  • افزودن متغیر چند جمله ای
  • دسته بندی مقادیر پیوسته در بسته های گسسته

متغیر چند جمله ای

گاهی برای مدل کردن و تخمین برخی از داده ها مدل به صورت خطی با نتیجه مرتبط نیست مثلا در تصویر زیر بجای یک خط صاف یک خط خمیده بهتر بر روی داده ها قرار می گیره برای همین نیاز هست از متغیر های چند جمله ای مثل X^3 و.... استفاده کنیم

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

یک تابع می سازیم که دیتاست آموزش و آزمون و نام متغیر رو بگیره و توان دو رو به دیتاست ها اضافه کنه:

def square_var(df_t, df_te, var_name = 'age'):
    df_t['new'] = df_t[var_name].pow(2) 
    df_te['new'] = df_te[var_name].pow(2) 
    return df_t, df_te

سپس تابع رو صدا می زنیم.

df_train_new, df_test_new = square_var(df_train, df_test, var_name = 'age')			
print(df_train_new.shape, df_test_new.shape)	

و به لیست متغیر های پیوسته اضافه می کنیم

CONTI_FEATURES_NEW  = ['age', 'fnlwgt','capital_gain', 'education_num', 'capital_loss', 'hours_week', 'new']
continuous_features_new = [tf.feature_column.numeric_column(k) for k in CONTI_FEATURES_NEW]

و بعد مدل جدید رو می سازیم البته model_dir رو عوض باید کنیم

model_1 = tf.estimator.LinearClassifier(
    model_dir="ongoing/train1", 
    feature_columns=categorical_features+ continuous_features_new)

سپس آموزش میدیم

model_1.train(input_fn=get_input_fn(df_train, 
                                      num_epochs=None,
                                      n_batch = 128,
                                      shuffle=False),
                                      steps=1000)

و سپس ارزیابی

model_1.evaluate(input_fn=get_input_fn(df_test_new, 
                                      num_epochs=1,
                                      n_batch = 128,
                                      shuffle=False),
                                      steps=1000)

دقت از ۷۶ شد ۷۹ :دی

با دسته بندی مقادیر پیوسته و استفاده از اثر متقابل میشه بهترش کرد

فشرده سازی و اثر متقابل

خب تا اینجا مدل نتونست بازم رابطه سن و درآمد رو درست تشخیص بده یک قدم دیگه اینه که سن ها رو به یک سری مرز سنی تبدیل کنیم برای این کار از کد زیر استفاده می کنیم

age = tf.feature_column.numeric_column('age')
age_buckets = tf.feature_column.bucketized_column(
    age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])

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

education_x_occupation = [tf.feature_column.crossed_column(
    ['education', 'occupation'], hash_bucket_size=1000)]
age_buckets_x_education_x_occupation = [tf.feature_column.crossed_column(
    [age_buckets, 'education', 'occupation'], hash_bucket_size=1000)]

ورودی hash_bucket_size تعداد کلاس های مختلف ایجاد شده هست

base_columns = [
    age_buckets,
]

model_imp = tf.estimator.LinearClassifier(
    model_dir="ongoing/train3", 
    feature_columns=categorical_features+base_columns+education_x_occupation+age_buckets_x_education_x_occupation)
FEATURES_imp = ['age','workclass', 'education', 'education_num', 'marital',
                'occupation', 'relationship', 'race', 'sex', 'native_country', 'new']

def get_input_fn(data_set, num_epochs=None, n_batch = 128, shuffle=True):
    return tf.estimator.inputs.pandas_input_fn(
       x=pd.DataFrame({k: data_set[k].values for k in FEATURES_imp}),
       y = pd.Series(data_set[LABEL].values),
       batch_size=n_batch,   
       num_epochs=num_epochs,
       shuffle=shuffle)

آموزش و ارزیابی مدل جدید

model_imp.train(input_fn=get_input_fn(df_train_new, 
                                      num_epochs=None,
                                      n_batch = 128,
                                      shuffle=False),
                                      steps=1000)
model_imp.evaluate(input_fn=get_input_fn(df_test_new, 
                                      num_epochs=1,
                                      n_batch = 128,
                                      shuffle=False),
                                      steps=1000)


خب الان دقت شد ۸۳.۵۸

در پایان با جلو گیری از over fit شدن مدل رو بهبود میدیم.

مرحله ۵) بهبود پارامتر ها: Lasso & Ridge

یک مدل با دو مشکل می تونه روبرو بشه overfitting یا underfitting.

  • اولی Overfitting: مدل قابلیت تعمیم به داده های جدید برای تخمین زدن رو نداره
  • دومی Underfitting: وقتی که مدل قابلیت پیدا کردن الگو داده ها رو نداره مثلا استفاده از رگرسیون خطی برای داده های غیر خطی

برای جلوگیری از overfitting و عمومی تر کردن مدل تکنیک هایی وجود داره مثل:

  • L1: Lasso
  • L2: Ridge

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

البته خودتون با تغییر این دو پارامتر سعی کنید مدل رو بهبود بدید ببینید چقدر میشه بهتر کرد

model_regu = tf.estimator.LinearClassifier(
    model_dir="ongoing/train4", feature_columns=categorical_features+base_columns+education_x_occupation+age_buckets_x_education_x_occupation,
    optimizer=tf.train.FtrlOptimizer(
        learning_rate=0.1,
        l1_regularization_strength=0.9,
        l2_regularization_strength=5))

model_regu.train(input_fn=get_input_fn(df_train_new, 
                                      num_epochs=None,
                                      n_batch = 128,
                                      shuffle=False),
                                      steps=1000)
model_regu.evaluate(input_fn=get_input_fn(df_test_new, 
                                      num_epochs=1,
                                      n_batch = 128,
                                      shuffle=False),
                                      steps=1000)

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

خلاصه

برای آموزش یک مدل باید:

  • تعریف ویژگی ها: متغیر های مستقل : X
  • تعریف برچسب: Dependent variable: y
  • ساخت مجموعه های آموزش و آزمون
  • تعریف وزن های ابتدایی
  • تعریف تابع کاهش: MSE
  • بهبود مدل: Gradient descent
  • تعریف:
    • نرخ یادگیری
    • تعداد ایپاک
    • اندازه دسته ها
    • تعداد کلاس ها

برا استفاده از تنسورفلو باید مقادیر زیر رو تعریف کنید:

  1. ستون های ویژگی ها. tf.feature_column.numeric_column
  2. برآوردگر tf.estimator.LinearClassifier(feature_columns, model_dir, n_classes = 2)
  3. یک تابع برای ورود داده ها اندازه دسته ها و ایپاک ها: input_fn()

سپس آموزش و ارزیابی مدل

برای بهبود مدل:

  • استفاده از متغیر های چند جمله ای
  • اثر متقابل: tf.feature_column.crossed_column
  • اضافه کردن پارامتر تنظیم