کهنه شدن مدل (رانش داده)
جواب کوتاه
مدل یادگیری ماشین از دادهی گذشته یاد میگیره؛ یعنی یه جور عکس از دنیای روز آموزشه. ولی دنیا عوض میشه و عکس نه. وقتی دادهی تازه دیگه شبیه دادهی آموزش نیست، پیشبینیهای مدل کمکم بدتر میشن؛ به این میگن رانش. گاهی خود ورودیها عوض میشن، مثلا مشتریها جوونتر میشن؛ گاهی رابطهی ورودی و جواب عوض میشه، مثلا همون مشتریها دیگه همونقدر نمیخرن. برای کارهایی که از رفتار آدمها میان، رانش تقریبا حتمیه. راهش اینه که خطای مدل رو مدام زیر نظر داشته باشید، برای وقتی بالا رفت مرز بذارید، و مدل رو با دادهی تازه دوباره آموزش بدید.
عکس یه روز
مدل نونوایی درسهای قبل رو یادتونه؟ ماهها خوب کار کرد. یه روز یه نونوایی تازه سر کوچه باز میشه. مدل از این خبر نداره.
مدل از دادهی گذشته یاد گرفته؛ یعنی یه جور عکس از دنیای روز آموزشه. روز آموزش، نقطهها دور خط بودن و مدل خوب حدس میزد. حالا نصف مشتریها میرن نونوایی تازه. مدل همون خط رو داره، ولی فروش واقعی پایینتر اومده. هر شب میگه «زیاد بپز»، و هر شب نون دور ریخته میشه.
به این اتفاق میگن رانش: داده طوری عوض میشه که مدل دیگه درست نیست. هیچ چیز توی مدل خراب نشده؛ کد همونه، عددها همونن. فقط دنیایی که مدل ازش عکس گرفته بود، دیگه همون دنیا نیست. نتیجهش اینه که پیشبینیها با گذشت زمان کمکم بدتر میشن.
توی درس مراحل یک پروژهی واقعی گفتیم کار با تحویل مدل تموم نمیشه و باید پایشش کرد. این درس میگه دقیقا چرا، و چی رو باید پایید.
دو جور
رانش دو تا شکل اصلی داره که خوبه از هم جداشون کنید، چون یکیش رو راحتتر میشه دید:
توی جور اول، خود ورودیها جابهجا میشن. فرض کنید یه فروشگاه، مدلی داره که حدس میزنه هر مشتری چی میخره، و با مشتریهای بزرگسال آموزش دیده. حالا یه تبلیغ تازه کلی مشتری جوون آورده. مدل هیچوقت اینهمه مشتری جوون ندیده بود، پس دربارهشون مطمئن نیست. قشنگیش اینه که این رو میشه زود دید؛ کافیه ورودیهای امروز رو با ورودیهای آموزش مقایسه کنید، حتی قبل از اینکه جواب درست معلوم بشه.
توی جور دوم، رابطهی ورودی و جواب عوض میشه. ورودیها همون شکل قبلیان، ولی جواب درست فرق کرده. همون نونوایی: روزهای هفته همونن، هوا همونه، ولی چون رقیب اومده، فروش هر روز کمتره. خط قبلی دیگه رابطهی واقعی نیست. این یکی رو فقط وقتی میفهمید که جوابهای واقعی برسن و ببینید مدل اشتباه کرده.
اسم دقیق این دومی رانش مفهوم هست، چون خود «مفهومی» که مدل یاد گرفته، یعنی رابطهی ورودی و جواب، عوض شده. خیلی وقتها هر دو با هم اتفاق میافتن.
چه شکلی میاد
رانش همیشه یه شکل نمیاد. سه جورش رو زیاد میبینید:
ناگهانی: یه روز همهچیز عوض میشه. نونوایی تازه باز میشه، یه قانون تازه میاد، یا یه برنامهی دیگه که داده رو میفرسته، عوض میشه. خوبیش اینه که معمولا زود معلومه.
آرومآروم: عادتهای مردم کمکم عوض میشن؛ هر ماه یه کم. این خطرناکترین نوعه، چون هر روز با دیروز تقریبا فرقی نداره و هیچ زنگی به صدا درنمیاد، ولی بعد از یه سال مدل خیلی دور شده.
فصلی: رفتار مردم با فصل عوض میشه، مثلا خرید آخر سال با تابستون فرق داره. خوبیش اینه که هر سال تکرار میشه و میشه پیشبینیش کرد. راهش اینه که فصل رو بهعنوان یه ویژگی به مدل بدید. این کار افت مدل رو کمتر میکنه، ولی از بین نمیبره؛ چون همیشه یه عامل تازه پیدا میشه که هیچ ستونی براش نداشتید.
چطوری بفهمیم
سادهترین راه اینه که خطای مدل رو هر روز یا هر هفته حساب کنید و براش دو تا مرز بذارید:
وقتی خطا از مرز اول رد شد، یعنی «حواست باشه، یه چیزی داره عوض میشه». وقتی از مرز دوم رد شد، یعنی «رانش قطعیه؛ وقت آموزش دوبارهست». دو تا مرز برای اینه که با هر بالا و پایین کوچیک و شانسی، زنگ نزنه، ولی وقتی واقعا کار خراب شد، دیر هم نشه.
ولی یه مشکل هست: برای حساب کردن خطا، جواب واقعی لازمه، و جواب واقعی همیشه زود نمیرسه. فروش نون فردا معلوم میشه؛ ولی اگه مدلتون حدس بزنه یه نفر وامش رو پس میده یا نه، جواب واقعی شاید سالها بعد برسه. تا اون موقع خیلی دیره.
برای همین یه راه دیگه هم هست که به جواب واقعی احتیاج نداره: دادهی تازه رو با دادهی قدیمیتر مقایسه کنید. یه پنجره از تازهترین دادهها و یه پنجره از دادههای قبلتر برمیدارید و میپرسید: این دو تا شبیه همن؟ مثلا میانگین و پراکندگی هر ستون. اگه یهو فرق زیادی دارن، یه چیزی توی دنیا عوض شده؛ حتی قبل از اینکه خطا بالا بره. این همون جور اول رانشه که گفتیم زودتر دیده میشه.
دو تا پنجره رو چطوری مقایسه کنیم؟
گفتیم بدون جواب واقعی هم میشه رانش ورودی رو دید: پنجرهی تازه رو با قبلی مقایسه کنید. ولی «مقایسه» دقیقا یعنی چی؟ میانگین گرفتن کافی نیست، چون دو تا گروه میتونن میانگین نزدیک و شکل خیلی متفاوت داشته باشن. یه راه قدیمی و ساده هست: برای هر پنجره یه منحنی بکشید که برای هر سن میگه «چند درصد مشتریها جوونتر از اینان». بعد بزرگترین فاصلهی دو منحنی رو پیدا کنید:
import random
random.seed(3)
def ks(a, b): # largest gap between the two "share below x" curves
a, b = sorted(a), sorted(b)
gap = i = j = 0
while i < len(a) and j < len(b):
if a[i] <= b[j]: i += 1
else: j += 1
gap = max(gap, abs(i / len(a) - j / len(b)))
return gap
old = [random.gauss(40, 10) for _ in range(1000)] # ages at training time
same = [random.gauss(40, 10) for _ in range(1000)] # a new week, nothing changed
young = [random.gauss(22, 3) if random.random() < 0.3 else random.gauss(40, 10)
for _ in range(1000)] # after an ad that brought young buyers
print("nothing changed:", round(ks(old, same), 3))
print("after the ad: ", round(ks(old, young), 3))
print("alarm above: ", round(1.36 * (2 / 1000) ** 0.5, 3))nothing changed: 0.043 after the ad: 0.277 alarm above: 0.061
سن مشتریها ساختگیه: هزار نفر روز آموزش، هزار نفر یه هفتهی عادی بعد، و هزار نفر بعد از تبلیغی که ۳۰ درصد مشتری جوون آورده. فاصلهی هفتهی عادی با روز آموزش ۰٫۰۴۳ بود؛ فقط بالا و پایین شانسی، چون هیچچیز عوض نشده بود. بعد از تبلیغ، ۰٫۲۷۷. اون عدد ۰٫۰۶۱ مرزیه که آمار برای دو تا پنجرهی هزارتایی میده: اگه هیچچیز عوض نشده باشه، فقط توی ۵ درصد از دفعهها فاصله شانسی از این بیشتر میشه. پس ۰٫۰۴۳ زیر مرزه و زنگ نمیخوره، و ۰٫۲۷۷ خیلی بالاتره و میخوره.
به این روش میگن آزمون کولموگوروف اسمیرنوف. خوبیش اینه که به شکل داده کاری نداره و هر تغییری رو، چه توی میانگین چه توی پخش بودن، میبینه. فقط حواستون باشه با پنجرههای خیلی بزرگ، مثلا میلیونها ردیف، حتی تغییرهای خیلی کوچیک و بیاهمیت هم از مرز رد میشن؛ اونجا بهجای «از مرز رد شد؟» بهتره خود اندازهی فاصله رو نگاه کنید و براش یه مرز معقول بذارید.
یه دام دیگه هم هست. معمولا فقط یه ستون نیست؛ شاید ۵۰ تا ستون داشته باشید و برای هر کدوم جدا این آزمون رو بزنید. اگه هر آزمون ۵ درصد احتمال زنگ الکی داشته باشه، با ۵۰ تا ستون، احتمال اینکه دستکم یکیشون بیدلیل زنگ بزنه، حدود ۹۲ درصده؛ تقریبا هر هفته. برای همین یا مرز رو سختتر میکنن، یا فقط وقتی چند ستون با هم زنگ زدن، یا یه ستون چند هفته پشت سر هم، جدی میگیرن. و مهمتر از همه، ستونهایی که مدل بیشتر بهشون تکیه داره رو با دقت بیشتری میپان.
چی کار کنیم
وقتی رانش پیدا شد، جوابش تقریبا همیشه یکیه: مدل رو با دادهی تازه دوباره آموزش بدید. سوال اینه که کی:
توی راه واکنشی، صبر میکنید تا زنگ بخوره؛ یعنی یکی از اون مرزها رد بشه. بعد مدل رو دوباره آموزش میدید و نسخهی تازه رو جای قبلی میذارید. سادهست، ولی یه ایراد داره: تا وقتی رانش فهمیده بشه، مدل بد کار کرده. اون دندونههای بلند توی نمودار، همون روزهاییه که مدل کهنه بوده و هنوز کسی نفهمیده بوده.
توی راه پیوسته، منتظر زنگ نمیمونید؛ مدل رو مدام و با تازهترین دادهها بهروز میکنید. مثلا هر هفته با دادهی چند ماه آخر دوباره آموزشش میدید. یا چند تا مدل کنار هم دارید، هر بار یه مدل تازه با آخرین دادهها میسازید و جای قدیمیترین رو بهش میدید. اینجوری مدل هیچوقت خیلی کهنه نمیشه، ولی کار بیشتری میبره.
هر کدوم رو انتخاب کنید، اون دو تا عادت درس به کار گذاشتن مدل اینجا به درد میخوره: هر نسخه رو نگه دارید، و قبل از اینکه نسخهی تازه رو بذارید، روی آخرین دادهها با نسخهی قبلی مقایسهش کنید. مدل تازه همیشه بهتر نیست.
رقیب سر کوچه، با عدد
همون نونوایی ساختگی درسهای قبل رو دوباره ساختیم، این بار با یه رقیب: از هفتهی ۶۰، یه نونوایی تازه باز میشه و فروش ۳۰ درصد کم میشه. مدل ما میانگین هر روز هفتهست که با سال اول آموزش دیده. سه جور رفتار رو امتحان کردیم: هیچوقت آموزش دوباره نده؛ با زنگ آموزش بده، یعنی هر وقت اشتباه یه هفته از ۹ نون در روز بیشتر شد، با ۴ هفتهی آخر؛ و هر هفته، با ۸ هفتهی آخر:
تا هفتهی ۵۹ هر سه حدود ۵ تا ۷ نون در روز اشتباه داشتن. هفتهی ۶۰ هر سه پریدن بالای ۲۰؛ هیچکدوم از رقیب خبر نداشتن. ولی بعدش راهشون جدا شد. مدلی که هیچوقت آموزش ندید، تا آخر روزی ۲۰ تا ۲۵ نون اشتباه کرد؛ میانگین هفتههای ۷۱ تا ۱۰۴، ۲۰٫۹. مدل با زنگ، از هفتهی ۶۱ هر هفته که زنگ خورد دوباره آموزش دید، و از هفتهی ۶۴ به حدود ۶ رسید.
جالب اینه که مدل «هر هفته» اینجا کندتر برگشت؛ هفتهی ۶۵ هنوز ۱۳٫۷. چرا؟ چون با ۸ هفتهی آخر آموزش میدید، و تا چند هفته بعد از اومدن رقیب، بیشتر اون ۸ هفته مال قبل از رقیب بود. یعنی اندازهی پنجرهی آموزش هم یه تصمیمه: پنجرهی بزرگ آرومتره و شانس رو بهتر صاف میکنه، ولی به تغییر یهویی دیرتر میرسه. هفتههای ۷۱ به بعد که دیگه همهی پنجره بعد از رقیب بود، هر دو به حدود ۴٫۷ و ۴٫۸ رسیدن.
زنگ، با کد
کل این آزمایش، اون قسمتی که تصمیم میگیره کی آموزش دوباره بده، همین چند خطه:
def run(policy):
model = weekday_means(0, 7 * 52) # trained on the first year
weekly = []
for wk in range(52, 104):
days = range(7 * wk, 7 * wk + 7)
err = st.mean(abs(model[d % 7] - sales[d]) for d in days)
weekly.append(err)
if policy == "every week":
model = weekday_means(7 * (wk - 7), 7 * (wk + 1)) # last 8 weeks
if policy == "on alarm" and err > 9:
model = weekday_means(7 * (wk - 3), 7 * (wk + 1)) # last 4 weeks
return weeklyهر هفته، اول اشتباه مدل فعلی روی هفت روز اون هفته حساب میشه و یادداشت میشه؛ این همون پاییدنه. بعد، بسته به رفتار، یا هیچ کاری نمیکنه، یا هر هفته مدل رو با ۸ هفتهی آخر از نو میسازه، یا فقط اگه اشتباه از ۹ بیشتر شد، با ۴ هفتهی آخر. دقت کنید مدل فقط بعد از اینکه اشتباه یه هفته رو دید عوض میشه، نه قبلش؛ برای همین هفتهی ۶۰، که رقیب اومد، هیچکدوم نتونستن جلوی اشتباه رو بگیرن. هیچ زنگی آینده رو نمیبینه؛ فقط کمک میکنه زودتر بفهمید.
افت آروم، با عدد
حالا یه دنیای دیگه: هیچ رقیبی یهویی نمیاد، ولی از هفتهی ۵۲ فروش هر هفته ۰٫۶ درصد کم میشه؛ اونقدر کم که هیچ هفتهای با قبلی فرق چندانی نداره. همون سه رفتار، این بار با پنجرهی ۴ هفتهای برای هر دو:
مدلی که هیچوقت آموزش ندید، ۱۰ هفتهی آخر روزی ۱۹٫۷ نون اشتباه داشت. مدل با زنگ، فقط ۳ بار در کل سال زنگ خورد و ۶٫۳ اشتباه داشت؛ چون هر بار باید صبر میکرد تا خطا به ۹ برسه، و بعد از هر آموزش دوباره، افت آروم از نو شروع میشد. مدل «هر هفته» با ۵٫۱ برنده شد؛ هیچوقت فرصت نکرد کهنه بشه.
پس جواب «با زنگ یا هر هفته؟» به شکل رانش بستگی داره. برای تغییر یهویی، زنگ با یه پنجرهی کوتاه زود جواب میده. برای افت آروم، بهروز کردن مرتب بهتره، چون زنگ دیر میخوره. و چون معمولا نمیدونید فردا کدومش پیش میاد، خیلیها هر دو رو با هم دارن: بهروز کردن مرتب، بهعلاوهی یه زنگ برای وقتی که یه چیزی یهو خراب شد.
فصل رو بهش بگید
گفتیم بخشی از تغییرها، مثل فصل، هر سال تکرار میشن و میشه از قبل به مدل گفتشون. این رو هم امتحان کردیم. یه نونوایی ساختگی دیگه ساختیم که فروشش توی سال، آروم بالا و پایین میره؛ یه وقتهایی ۱۵ درصد بیشتر، یه وقتهایی ۱۵ درصد کمتر. دو تا مدل رو با سال اول آموزش دادیم و با سال دوم سنجیدیم. اولی فقط روز هفته رو میدونست، و روزی ۷٫۸ نون اشتباه کرد. دومی روز هفته و ماه رو با هم میدونست، و ۵٫۳. یعنی حدود یکسوم اشتباه کمتر، فقط با اضافه کردن یه ستون، بی هیچ آموزش دوبارهای وسط سال.
یعنی چیزی که از بیرون شبیه رانش بود، فروش تابستون با زمستون فرق داره، اصلا رانش نبود؛ فقط ستونی بود که مدل نداشت. پس قبل از اینکه یه تغییر رو رانش حساب کنید و مدل رو مدام از نو آموزش بدید، بپرسید: این تغییر تکرار میشه؟ اگه آره، یه ستون برای دلیلش بسازید. ولی همونطور که گفتیم، این کار رانش رو کم میکنه، از بین نمیبره؛ رقیب سر کوچه توی هیچ ستونی نبود.
وقتی معنی عوض میشه
یه جور دیگهی رانش هم هست که اصلا ربطی به دنیای بیرون نداره، و از همه یواشکیتره:
فرض کنید یه ستون «قیمت» دارید که همیشه به تومن بوده. یه روز، یه برنامهنویس توی یه بخش دیگهی سیستم، تصمیم میگیره از این به بعد قیمت رو به ریال ذخیره کنه. ستون همون ستونه، همون اسم، همون شکل؛ فقط هر عدد ده برابر شده. هیچ برنامهای خطا نمیده. ولی مدل فکر میکنه همهچیز یهو ده برابر گرون شده.
به این میگن رانش معنایی: معنی داده عوض میشه، ولی شکلش نه. یه برادر هم داره به اسم رانش ساختاری، که شکل خود جدول عوض میشه؛ مثلا یه ستون حذف یا اسمش عوض میشه. اون یکی معمولا زود خطا میده و پیدا میشه؛ این یکی نه. اینها بیشتر توی سیستمهای بزرگ پیش میاد که آدمهای زیادی بدون خبر از اثر کارشون روی بقیه، چیزی رو عوض میکنن.
همون پنجرهها اینجا هم نجاتتون میدن: اگه میانگین ستون قیمت یه شبه ده برابر شد، احتمالا دنیا عوض نشده؛ معنی عوض شده. اینجور وقتها آموزش دوباره راهش نیست؛ راهش اینه که آمادهسازی داده رو درست کنید، همون حرف درس قبل.
وقتی جواب دیر میرسه
اگه جواب واقعی ماهها بعد برسه، بهجز ورودیها، یه چیز دیگه هم هست که میشه همین امروز پایید: خود حدسهای مدل. اگه مدل وام همیشه حدود ۱۰ درصد درخواستها رو پرخطر میدونست، و این هفته یهو ۳۰ درصد رو پرخطر میدونه، یه چیزی عوض شده؛ یا آدمهایی که درخواست میدن، یا دادهای که به مدل میرسه. این حدسها هر لحظه در دسترسن و هیچ صبری لازم ندارن. همون آزمون دو پنجره رو میشه روی حدسها هم زد.
مدلی که دادهی خودش رو عوض میکنه
یه جور خاص و خطرناک هم هست که بهش حلقهی بازخورد میگن. مدل نونوایی میگه «فردا ۶۰ تا بپز». نونوا ۶۰ تا میپزه و ساعت ۱۰ صبح تموم میشه. توی داده، فروش اون روز ۶۰ ثبت میشه؛ ولی شاید اگه ۸۰ تا داشت، ۸۰ تا میفروخت. حالا مدل با همین داده دوباره آموزش میبینه و فکر میکنه فروش ۶۰ بوده، پس دفعهی بعد شاید ۵۸ بگه. هر دور، مدل کمی کمتر میگه، و داده هم کمی کمتر نشون میده.
اینجا هیچ چیزی توی دنیا عوض نشده؛ خود مدل داده رو عوض کرده. راهش اینه که بدونید دادهتون کجا «سقف خورده»، مثلا روزهایی که نون تموم شده رو علامت بزنید، و با اون روزها جور دیگهای رفتار کنید؛ و هر از گاهی، عمدا کمی بیشتر از حدس مدل امتحان کنید، تا ببینید واقعا فروش بیشتری هست یا نه. این همون کاوشیه که توی یادگیری تقویتی دیدیم.
چند نکته
رانش حتمیه؛ هر کاری که از رفتار آدمها میاد، مثل خرید و فروش، با زمان عوض میشه، پس آموزش دوباره دیر یا زود لازمه. هم خطا رو بپایید هم ورودیها رو؛ خطا دقیقتره ولی دیر میرسه، و ورودیها زودتر خبر میدن. دو تا مرز بذارید، یکی برای هشدار و یکی برای اقدام. فصل و زمان رو به مدل بدید، چون بخشی از تغییرها قابل پیشبینیان. قبل از آموزش دوباره بپرسید چرا؛ اگه معنی داده عوض شده، آموزش دوباره فقط اشتباه رو یاد میگیره. و نسخهی تازه رو با قبلی مقایسه کنید، چون تازه همیشه بهتر نیست.
اشتباههای رایج
رایجترینش اینه که فکر کنید مدلی که روز تحویل خوب بود، همیشه خوب میمونه. بعدی فقط پاییدن خطا، که برای کارهایی که جواب واقعیشون دیر میرسه، خیلی دیره. بعدی یه مرز تنها که یا با هر بالا پایین شانسی زنگ میزنه یا خیلی دیر. بعدی پنجرهی آموزش نامناسب؛ دیدیم پنجرهی بزرگ بعد از یه تغییر یهویی چند هفته عقب میمونه. بعدی آموزش دوباره وقتی مشکل معنای دادهست، مثل تومن و ریال. و آخری، گذاشتن نسخهی تازه بی مقایسه با قبلی.
از کجا اومد
ابزارهای شناختن تغییر، خیلی قدیمیتر از یادگیری ماشینان:
آندری کولموگوروف سال ۱۹۳۳ و نیکلای اسمیرنوف سال ۱۹۴۸ همون آزمونی رو ساختن که بالاتر دیدیم. گرهارد ویدمر و میروسلاو کوبات سال ۱۹۹۶ یه مقالهی معروف دربارهی یادگیری وقتی مفهوم عوض میشه نوشتن، و اسم رانش مفهوم از همون دوره جا افتاد. سال ۲۰۱۴ ژوائو گاما و همکارهاش یه مرور کامل از راههای کنار اومدن با رانش منتشر کردن.
درس بعد دربارهی یه خطر دیگهست، که حتی بدون رانش هم پیش میاد: مدلی که با یه گروه از آدمها بدتر از بقیه رفتار میکنه:سوگیری و انصاف.
جمعبندی. آنچه از این درس با خودتان میبرید.
- مدل عکس دنیای روز آموزشه؛ دنیا عوض میشه.
- گاهی ورودیها جابهجا میشن، گاهی رابطه عوض میشه.
- ناگهانی، آرومآروم، یا فصلی.
- خطا رو با مرز هشدار و مرز رانش بپایید.
- آموزش دوباره برای کار آدمها همیشه لازمه.
خودتون رو بسنجید
۱۵ پرسش، هر بار تازه از میان ۳۰ پرسش این درس. آخرش فقط کارنامه رو میبینید: چند تا درست، چند تا نادرست.