اگه تا اینجای کار اومدین معلومه که حتما این آموزشها براتون خوب بوده و یه سطحی از دانش شبکه های عمیق رو دارین و خوندن بخش های قبلی این آموزش ازتون وقتی نمیگیره. پس برین قسمت اول و قسمت دومش رو هم بخونین و سریع برگردین.
توی قسمت قبل تا اینجا اومدیم که یه سری پیش پردازش اولیه دیتا رو انجام دادیم و از دیتاهای ورودی و لیبلها امبدینگ ساختیم. اگه قسمت قبل رو دیده باشین دو سری امبدینگ از ورودیها ساختیم. دیتاهای categorical جدا و دیتاهای عددی هم جدا.
شما باید با این مفهوم امبدینگ (Embedding) آشنایی داشته باشین که میتونین از اینجا و اینجا با مفهوم اصلی آشنا بشین و از اینجا هم لایه امبدینگ پایتورچ رو توضیح داده. این هم خوبه.
ببینین امبدینگ ساختن از دیتای ورودی شبکه یه مرحله خیلی مهم از ساخت مدلهای دیپ لرنینگ هست. مخصوصا اگه دیتامون متنی باشه. ولی اینجا که ما از دیتای عددی داریم استفاده میکنیم یه کم قضیه فرق میکنه. وقتی میخوان ساخت امبدینگ از دیتای متنی رو توضیح بدن، اولین و بدیهی ترین روش onehot هست که معمولا زود از روش رد میشن چون خیلی بیخود و بیمزه است. ولی اینجا ما ازش استفاده میکنیم. اول اینکه میخوایم ساده باشه و دوم اینکه ساختارش برامون مهم نیست. و از همون لایه امبدینگ پایتورچ استفاده میکنیم. ایده کلی وان هات اینه که یه جدولی میسازه (یا مثلا برای دیتای متنی شما یه چیزی شبیه دیکشنری فرض کن) بعد به مقادیرش یه ایندکسی میده و اون ایندکس ها رو تبدیل به بردار میکنه. ( اگه نفهمیدین ببخشین. خیلی ساده و مسخره اس. خودتون برید بخونین. منم بعدا یه پست راجع به لایه embedding در پایتورچ مینویسم)
برای شروع کار میایم برای بردارهای امبدینگمون سایز در نظر میگیریم.
cat_szs = [len(df[col].cat.categories) for col in cat_cols] emb_szs = [(size, min(50, (size+1)//2)) for size in cat_szs] emb_szs
الان اینجا اومدیم سایز بردار امبدینگ رو نصف تعداد ورودی های منحصر به فرد به ازای هر فیچر در نظر میگیرن. الان emb_szs سه جفت عدد هست. به ازای سه تا فیچر categorical مون. و اندازه امبدینگهای مربوط به هر کدوم هم نصف مقادیرش در نظر گرفتیم. 24 تا برای hour . برای AMorPM هم 2 تا . برای Weekday هم 7 تا . اگه بیشتر از 50 بود هم که ولش کن و عدد 50 رو ست کن. اون // هم برای اینه که نتیجه تقسیم بر 2 رو اعشاری نکنه و جزء صحیحش رو بهمون بده.
دیتاستها از فیچرهای مختلفی تشکیل شدن. انواع داده ها در اون ها موجوده. داده های عددی و categorical و غیره. به این مدل دیتاستها معمولا میگن داده های جدولی یا tabular . برای کار با این دیتاستها میان مدلهای tabular میسازن. ساخت مدلهای tabular یه روال مشخصی داره که اینجا باهاش آشنا میشیم
این مدلمون هست که مرحله به مرحله اش رو براتون توضیح میدم:
class TabularModel(nn.Module): def __init__(self, emb_szs, n_cont, out_sz, layers, p=0.5): super().__init__() self.embeds = nn.ModuleList([nn.Embedding(ni, nf) for ni,nf in emb_szs]) self.emb_drop = nn.Dropout(p) self.bn_cont = nn.BatchNorm1d(n_cont) layerlist = [] n_emb = sum((nf for ni,nf in emb_szs)) n_in = n_emb + n_cont for i in layers: layerlist.append(nn.Linear(n_in,i)) layerlist.append(nn.ReLU(inplace=True)) layerlist.append(nn.BatchNorm1d(i)) layerlist.append(nn.Dropout(p)) n_in = i layerlist.append(nn.Linear(layers[-1],out_sz)) self.layers = nn.Sequential(*layerlist) def forward(self, x_cat, x_cont): embeddings = [] for i,e in enumerate(self.embeds): embeddings.append(e(x_cat[:,i])) x = torch.cat(embeddings, 1) x = self.emb_drop(x) x_cont = self.bn_cont(x_cont) x = torch.cat([x, x_cont], 1) x = self.layers(x) return x
برای ساخت مدلهای شبکه عصبی با استفاده از پایتورچ از کلاس nn.module ارث بری میکنیم. این کلاس دو تا متد مهم داره. متد init یا سازنده و متد forward .
کار مهمی که توی متد سازنده انجام شده یکی محاسبه سایز ورودی شبکه اس که مجموع سایز امبدینگ های فیچرهای categorical هست به علاوه فیچرهای عددی :
n_emb = sum((nf for ni,nf in emb_szs)) n_in = n_emb + n_cont
و دومی تعریف اجزای لایه های شبکه است. تعداد لایه ها رو هم از ورودی و هنگام تعریف آبجکت مشخص میکنیم:
layerlist = [] for i in layers: layerlist.append(nn.Linear(n_in,i)) layerlist.append(nn.ReLU(inplace=True)) layerlist.append(nn.BatchNorm1d(i)) layerlist.append(nn.Dropout(p)) n_in = i
layerlist.append(nn.Linear(layers[-1],out_sz)) self.layers = nn.Sequential(*layerlist)
همونطور که میبینین هر لایه تشکیل شده از یک لایه linrear به اندازه ای که بالا محاسبه کردیم. ینی 12+1+4+6 که این 6 تعداد فیچرهای عددی مون بود. بعد از لایه پرسپترون تابع فعالسازی میذاریم. بعد یه لایه نرمالیزاسیون و بعد هم دراپ اوت.
آخر از همه هم یه لایه linear دوباره اضافه میکنیم به عنوان لایه خروجی که اندازه اش رو از ورودی میگیریم. حالا چون مسئله امون رگرسیون هست و میخوایم یه عدد کرایه رو پیش بینی کنیم out_sz رو 1 میذاریم.
بعد هم لیستی که ساختیم رو به nn.Sequential میدیم و self.layers میشه ساختار شبکه ما.
یه layers هم ورودی سازنده بود که تعداد لایه ها بود و این با اون فرق داره.
سه تا متغیر دیگه هم داریم : self.embeds ، self.emb_drop و self.bn_cont . که اینا جزو ساختار شبکه ما نیستن و بعدا در تابع forward ازشون استفاده میکنیم.
داخل متد forward هم میایم دیتا رو اول میدیم به لایه امبدینگ که ساختیم. بعد میدیم به dropout . بعد از نرمالیزاسیون رد میکنیم. بعد همه رو ادغام میکنیم با هم. و آخرش میدیم به شبکه مون و خروجی اش رو میگیریم.
def forward(self, x_cat, x_cont): embeddings = [] for i,e in enumerate(self.embeds): embeddings.append(e(x_cat[:,i])) x = torch.cat(embeddings, 1) x = self.emb_drop(x) x_cont = self.bn_cont(x_cont) x = torch.cat([x, x_cont], 1) x = self.layers(x) return x
الان این شد tabular model ما . که اگه دفه اولتون هست یه کم گیج میشین ولی به قول جادی خوبه که گیج بشین. ینی دارین چیز میزای جدید یاد میگیرین. پس یکی دو بار دیگه بخونین اوکی میشه همه چی.
این مدلی که نوشتیم برای کار با هر نوع دیتاست جدولی دیگه ای هم اوکیه و میتونین با دیتاست های دیگه هم تست کنین. حالا بیاید بریم سراغ train مدل :)