یادگیری Q (Q-learning)
جواب کوتاه
یادگیری Q یه روش یادگیری تقویتیه که یه جدول میسازه. هر ردیف جدول یه حالته، مثلا یه خونهی هزارتو، و هر ستونش یه کار، مثلا چپ یا راست. توی هر خونهی جدول یه عدد هست که میگه «اگه اینجا این کار رو بکنم، چقدر پاداش در انتظارمه»؛ به این عدد میگن Q، یعنی کیفیت. اولش همهی عددها صفره. هر بار که عامل یه کار میکنه و یه پاداش میگیره، فقط همون یه خونهی جدول رو یه کم به سمت «پاداش همین قدم + ۰٫۹ × بهترین عدد حالت بعد» میبره. کمکم ارزش از ته راه به اول پخش میشه، و آخرش جدول نشون میده از هر جا کدوم کار بهتره. لازم هم نیست کسی نقشهی محیط رو بده.
جدول Q
توی درس سیاست و ارزش دیدیم اگه ارزش هر کار توی هر حالت رو بدونیم، بهترین رفتار خودش معلومه. یادگیری Q دقیقا همین عددها رو یاد میگیره، توی یه جدول:
هر ردیف یه حالته؛ اینجا یه خونهی هزارتو. هر ستون یه کاره؛ چپ، بالا، راست، پایین. توی هر خونهی جدول یه عدد مینویسیم که میگه اگه توی این حالت این کار رو بکنم و بعدش خوب بازی کنم، چقدر پاداش در انتظارمه. به این عدد میگن Q. حرف Q از کلمهی انگلیسی Quality میاد، یعنی کیفیت؛ کیفیت یه کار توی یه حالت.
اولش هیچی نمیدونیم، پس همهی عددها رو یه چیز ثابت میذاریم، معمولا صفر. بعد عامل شروع میکنه به بازی کردن و جدول کمکم پر میشه.
یه چیز خیلی خوب دربارهی یادگیری Q اینه که هیچ مدلی از محیط لازم نداره. لازم نیست کسی بهش بگه هر کار به کجا میبره یا قاعدهی محیط چیه؛ فقط بازی میکنه و از نتیجه یاد میگیره. حتی اگه محیط یه کم شانسی باشه، مثلا گاهی موش لیز بخوره و جای دیگهای بره، یادگیری Q بدون هیچ تغییری کار میکنه.
قطار
قبل از قاعده، یه مثال از زندگی که نشون میده چرا باید به کل راه فکر کرد، نه فقط به قدم اول. فرض کنید هر روز سوار یه قطار شلوغ میشید و میخواید کل زمان سوار شدن کم باشه:
روز اول، همین که در باز میشه هجوم میبرید. صبر نمیکنید، ولی چون آدمها دارن پیاده میشن، ۱۵ ثانیه کشمکش دارید. روز دوم، شانسی تصمیم میگیرید اول صبر کنید تا بقیه پیاده بشن. ۵ ثانیه صبر میکنید، ولی بعدش هیچ کشمکشی نیست. قدم اولش بدتر بود، ولی کلش خیلی بهتر شد.
دو تا چیز از این مثال یاد میگیریم. اول اینکه ارزش یه کار رو باید با هر چی بعدش میاد سنجید. دوم اینکه اگه روز دوم چیز تازهای امتحان نمیکردید، هیچوقت راه بهتر رو پیدا نمیکردید؛ همون کاوش.
قاعده
حالا دل ماجرا. عامل توی یه حالته، یه کار میکنه، یه پاداش میگیره و به یه حالت تازه میرسه. فقط همون یه خونهی جدول، یعنی «اون حالت و اون کار»، اینجوری درست میشه:
- عدد قدیمی رو از جدول برمیداریم. فرض کنید هنوز صفره.
- یه عدد تازه میسازیم: پاداش همین قدم، بهعلاوهی ۰٫۹ ضرب در بهترین عدد ردیف حالت بعد. این همون ایدهی بلمن هست. فرض کنید این قدم پاداشی نداشت، ولی توی حالت بعد بهترین کار ۱۰ میارزه. پس عدد تازه میشه ۰ بهعلاوهی ۰٫۹ ضرب در ۱۰، یعنی ۹.
- این دو تا رو با هم قاطی میکنیم. چقدر از کدوم؟ اینو نرخ یادگیری میگه. اگه نصف باشه، نصف عدد قدیمی رو نگه میداریم و نصف عدد تازه رو.
- نتیجه رو جای عدد قدیمی مینویسیم. اینجا میشه نصف صفر بهعلاوهی نصف ۹، یعنی ۴٫۵.
چرا یهدفعه ۹ رو جاش نمینویسیم؟ چون یه تجربه ممکنه شانسی باشه. اگه محیط یه کم شانسی باشه، با قاطی کردن، عدد جدول آرومآروم به میانگین همهی تجربهها نزدیک میشه، نه اینکه با هر تجربه بالا پایین بپره.
کل الگوریتم همینه: یه کار انتخاب کن، انجامش بده، پاداش و حالت تازه رو ببین، همون یه خونهی جدول رو با این قاعده درست کن، و دوباره. کار رو هم معمولا با همون تاس انتخاب میکنن؛ بیشتر وقتها بهترین عدد ردیف، گاهی یه کار شانسی.
پخش ارزش
یه سوال: اگه پاداش فقط کنار پنیره، خونههای دور از کجا میفهمن خوبن؟ یه راهروی ساده رو ببینیم، با پنیر ته سمت چپ. هر دور، موش از سمت راست میاد و میره سمت پنیر:
دور اول، فقط خونهی کنار پنیر چیزی یاد میگیره؛ چون فقط اونجا پاداش دیده، ۱۰. خونههای دیگه هنوز صفرن، چون بهترین عدد حالت بعدشون هنوز صفره.
دور دوم، خونهی دوم وقتی قاعده رو اجرا میکنه، میبینه بهترین عدد خونهی بعدش حالا ۱۰ هست؛ پس عددش میشه ۰٫۹ ضرب در ۱۰، یعنی ۹. دور سوم خونهی سوم ۸٫۱ میگیره و دور چهارم خونهی چهارم حدود ۷٫۳. یعنی ارزش، دور به دور، از ته راه به اول راه پخش میشه؛ مثل خبری که دهن به دهن میچرخه.
اینجا نرخ یادگیری رو یک گرفتیم که عددها یهدفعه بشینن. توی محیطی که هیچ چیز شانسی نداره، نرخ یک بهترین انتخابه؛ ولی توی محیط شانسی معمولا نرخ کمتری میذارن و عددها آرومتر به جای درستشون میرسن.
اگه عامل به اندازهی کافی کاوش کنه و همهی خونهها و کارها رو بارها امتحان کنه، یادگیری Q آخرش بهترین سیاست رو برای هر محیط محدودی پیدا میکنه. البته «به اندازهی کافی» توی تئوری یعنی بینهایت؛ توی عمل یعنی خیلی زیاد.
با پایتون
کل یادگیری Q، با پایتون خالی و بی هیچ کتابخونهای. یه راهروی ۶ خونهای که پنیر تهشه؛ موش دو کار داره، چپ یا راست. نرخ یادگیری ۰٫۵، ضریب تخفیف ۰٫۹، و یه تاس که ۱۰ درصد وقتها کار شانسی میکنه. خودمون اجراش کردیم:
import random
random.seed(1)
N = 6 # خونهی ۰ تا ۵؛ پنیر توی خونهی ۵
Q = [[0.0, 0.0] for _ in range(N)] # برای هر خونه: [چپ، راست]
alpha, gamma, eps = 0.5, 0.9, 0.1
def step(s, a):
s2 = max(0, s - 1) if a == 0 else s + 1
return s2, (10 if s2 == N - 1 else 0)
for episode in range(1, 51):
s, steps = 0, 0
while s != N - 1:
if random.random() < eps or Q[s][0] == Q[s][1]:
a = random.randint(0, 1)
else:
a = 0 if Q[s][0] > Q[s][1] else 1
s2, r = step(s, a)
best_next = 0 if s2 == N - 1 else max(Q[s2])
Q[s][a] += alpha * (r + gamma * best_next - Q[s][a])
s, steps = s2, steps + 1
if episode in (1, 2, 5, 10, 50):
print("episode", episode, "steps", steps)
for s in range(N - 1):
print(s, "left", round(Q[s][0], 2), "right", round(Q[s][1], 2))episode 1 steps 23 episode 2 steps 60 episode 5 steps 5 episode 10 steps 5 episode 50 steps 5 0 left 4.99 right 6.56 1 left 2.95 right 7.29 2 left 5.96 right 8.1 3 left 5.73 right 9.0 4 left 7.09 right 10.0
دور اول، موش ۲۳ قدم سرگردون بود تا تصادفی به پنیر برسه؛ دور دوم حتی بدتر، ۶۰ قدم. چرا بدتر؟ چون هنوز فقط خونهی کنار پنیر یه عدد گرفته و بقیهی جدول صفره؛ موش توی بیشتر خونهها هنوز داره شیر یا خط میندازه. ولی از دور پنجم، هر دور فقط ۵ قدم، یعنی کوتاهترین راه ممکن.
حالا به جدول آخر نگاه کنید. عدد «راست» از ته به اول ۱۰، ۹، ۸٫۱، ۷٫۲۹، ۶٫۵۶ شده؛ دقیقا همون عددهایی که توی درسسیاست و ارزش با معادلهی بلمن و نقشهی کامل راهرو حساب کردیم. فرقش اینه که اینجا موش هیچ نقشهای نداشت؛ فقط راه رفت، پاداش گرفت، و جدولش رو قدم به قدم درست کرد. کل یادگیری هم توی همون یه خط Q[s][a] += alpha * (...) اتفاق میافته؛ همون قاعدهای که بالاتر تیکهتیکه دیدیم.
ولی عددهای «چپ» یه جوریان: ۴٫۹۹، ۲٫۹۵، ۵٫۹۶. درستش برای خونهی ۱ باید ۵٫۹ باشه، نه ۲٫۹۵. دلیلش اینه که موش وقتی فهمید راست بهتره، دیگه کم سراغ چپ رفت؛ فقط اون ۱۰ درصد تاس. پس عدد چپ کم بهروز شده و دقیق نیست. و این اصلا مشکلی نیست؛ برای انتخاب کار، فقط لازمه بدونیم کدوم بزرگتره، نه اینکه عدد کار بد دقیقا چنده.
دو پیچ
یادگیری Q دو تا عدد داره که خودمون باید انتخاب کنیم؛ دو تا ابرپارامتر:
نرخ یادگیری میگه هر تجربهی تازه چقدر جای قبلی رو بگیره. اگه صفر باشه، عامل هیچی یاد نمیگیره و همیشه همون عدد قدیمی رو نگه میداره. اگه یک باشه، فقط آخرین تجربه رو حساب میکنه و هر چی قبلا یاد گرفته رو کنار میذاره. توی عمل معمولا یه عدد کوچیک و ثابت، مثلا ۰٫۱، میذارن.
ضریب تخفیف همونیه که قبلا دیدیم. اگه صفر باشه، عامل کوتهبین میشه و فقط پاداش همین قدم رو میبینه. هر چی به یک نزدیکتر، دنبال پاداش بلندمدتتر. ولی نباید به یک برسه یا ازش رد بشه، چون اونوقت عددهای جدول ممکنه همینجوری بزرگ و بزرگتر بشن و هیچوقت نشینن.
کی مطمئنیم درست درمیاد؟
کریستوفر واتکینز و پیتر دایان سال ۱۹۹۲ ثابت کردن که یادگیری Q، با جدول، آخرش به عددهای درست و سیاست بهینه میرسه؛ ولی با دو تا شرط. اول اینکه هر جفت «حالت و کار» بارها و بارها امتحان بشه، نه یه بار؛ برای همین کاوش لازمه. دوم اینکه نرخ یادگیری کمکم کوچیک بشه، ولی نه خیلی تند؛ تا آخرش عددها بالا پایین نپرن و سر جاشون بشینن.
توی محیطی که همهچی قطعیه، مثل راهروی ما که «راست» همیشه یعنی خونهی بعد، حتی نرخ یادگیری یک هم خوبه، چون یه بار دیدن کافیه. ولی اگه محیط شانسی باشه، مثلا پاداش یه کار گاهی ۱۰ باشه گاهی صفر، با نرخ یک، عدد جدول همیشه برابر آخرین شانس میشه و هیچوقت میانگین درست رو یاد نمیگیره. توی عمل، خیلیها نرخ رو ثابت و کوچیک میذارن، مثلا ۰٫۱، که بهاندازهی کافی خوب کار میکنه.
شروع خوشبین
گفتیم اول کار جدول رو معمولا صفر میذارن. ولی یه ترفند جالب هست: جدول رو از اول با عددهای بالا پر کنیم، انگار عامل خیلی خوشبینه:
حالا هر کاری که عامل امتحان کنه، عددش از اون ۱۰ خوشبینانه پایینتر میاد، چون واقعیت معمولا اونقدر خوب نیست. پس دفعهی بعد، کارهایی که هنوز امتحان نشدن و عددشون هنوز ۱۰ هست، بهتر به نظر میان و انتخاب میشن. یعنی عامل خودبهخود همهی کارها رو حداقل یه بار امتحان میکنه. به این میگن شروع خوشبین، و یه راه ساده برای تشویق به کاوشه.
سارسا، پسرعموی محتاط
یادگیری Q یه پسرعمو داره به اسم سارسا. فرقشون فقط توی یه تیکه از قاعدهست. یادگیری Q میگه «پاداش + ۰٫۹ ضرب در بهترین عدد خونهی بعد»، حتی اگه موش قدم بعد، به خاطر تاس، اون بهترین کار رو نکنه. سارسا میگه «پاداش + ۰٫۹ ضرب در عدد همون کاری که واقعا قدم بعد انجام میدم». اسمش هم از همین میاد: حالت، کار، پاداش، حالت بعد، کار بعد؛ به انگلیسی State، Action، Reward، State، Action، که سرهم میشه SARSA.
این فرق کوچیک، رفتار رو خیلی عوض میکنه. یه مثال معروف از کتاب ساتن و بارتو رو خودمون اجرا کردیم: یه زمین ۴ در ۱۲، شروع و پنیر دو سر ردیف پایین، و بینشون پرتگاه. هر قدم منفی یک، و افتادن توی پرتگاه منفی ۱۰۰ و برگشت به شروع. هر دو روش، با تاس ۱۰ درصد، ۵۰۰ دور یاد گرفتن، و این رو ۲۰ بار تکرار کردیم.
یادگیری Q کوتاهترین راه رو پیدا کرد: ۱۳ قدم، درست لب پرتگاه. سارسا راه بالا رو انتخاب کرد: ۱۷ قدم، دور از لبه. کدوم بهتره؟ بستگی داره. اگه موش از این به بعد بی تاس راه بره، راه یادگیری Q بهتره. ولی تا وقتی تاس هست، هر از گاهی یه قدم شانسی لب پرتگاه یعنی سقوط؛ برای همین میانگین پاداش ۱۰۰ دور آخر، با همون تاس، برای یادگیری Q منفی ۵۱٫۵ بود و برای سارسا منفی ۲۷٫۷. سارسا چون حساب قدمهای شانسی خودش رو هم میکنه، یاد میگیره از جای خطرناک دوری کنه.
به این فرق میگن روی سیاست و بیرون سیاست. سارسا ارزش همون سیاستی رو یاد میگیره که داره باهاش راه میره، تاسش هم جزوشه. یادگیری Q ارزش بهترین سیاست رو یاد میگیره، هر سیاستی که باهاش راه بره. همین ویژگی دوم، یادگیری Q رو قوی کرده؛ چون میتونه از تجربههایی هم یاد بگیره که با رفتار دیگهای جمع شدن، مثلا تجربههای قدیمی خودش یا تجربهی یه آدم. برای یه ربات واقعی که افتادنش گرونه، ولی، احتیاط سارسا میتونه بهتر باشه.
یادگیری Q دوتایی
یادگیری Q یه عیب پنهون داره: زیادی خوشبینه. چون همیشه «بزرگترین عدد خونهی بعد» رو برمیداره، و وقتی عددها نویز دارن، بزرگترینشون معمولا اونیه که شانسی بالا اومده. مثلا فرض کنید ده تا سکه دارید که هر کدوم میانگینش کمی منفیه؛ اگه هر کدوم رو چند بار بندازید، احتمالا یکیش شانسی مثبت درمیاد، و اگه «بهترین» رو بردارید، فکر میکنید یه سکهی برنده دارید.
این رو هم با یه آزمایش از همون کتاب دیدیم. موش اول کار دو راه داره. راست، بازی تموم، پاداش صفر. چپ، میره یه خونه که ده تا کار داره، و هر کدوم پاداش شانسی با میانگین منفی ۰٫۱ میده. پس چپ کمی بده و راست بهتره. ولی یادگیری Q تا دور ۲۵، ۹۳ درصد وقتها چپ رفت، چون بزرگترین عدد اون ده تا کار، شانسی مثبت دیده میشد. تا دور ۱۰۰ هنوز ۴۸ درصد، و دور ۳۰۰ رسید به ۱۳ درصد. هر کدوم رو هزار بار تکرار کردیم.
هادو ون هاسلت سال ۲۰۱۰ یه راه ساده داد: دو تا جدول جدا. هر قدم یکی از دو تا، شیر یا خط، بهروز میشه؛ اون جدول انتخاب میکنه کدوم کار بهترینه، ولی عددش رو از جدول دیگه میگیره. چون نویز دو تا جدول به هم ربطی نداره، کاری که توی یکی شانسی بالا اومده، توی اون یکی معمولا بالا نیست، و این خوشبینی بیخود از بین میره. توی همون آزمایش، یادگیری Q دوتایی دور ۲۵ فقط ۲۸ درصد و دور ۱۰۰، ۹ درصد چپ رفت. کمتر از ۵ درصد هم ممکن نبود، چون تاس ۱۰ درصد وقتها شانسی کار میکنه و نصف اونها چپه.
جدول جا نمیشه
جدول برای هزارتوی کوچیک عالیه. ولی هر چی حالتها و کارها بیشتر بشن، احتمال اینکه عامل به هر خونهی جدول سر بزنه کمتر میشه و جدول دیگه خوب پر نمیشه. مثلا فرض کنید میخوایم یه چوب رو روی انگشت نگه داریم:
برای اینکه بدونیم الان اوضاع چطوره، چهار تا عدد لازمه: جای انگشت، اینکه انگشت چقدر تند حرکت میکنه، زاویهی چوب، و اینکه چوب چقدر تند میچرخه. هر کدوم از اینها میتونه بینهایت مقدار مختلف داشته باشه، پس حالتها بینهایتن و هیچ جدولی جاشون نمیشه.
یه راه ساده اینه که هر عدد رو بریزیم توی چند تا دسته. مثلا لازم نیست دقیقا بدونیم انگشت چند سانت جابهجا شده؛ کافیه بدونیم «نزدیکه» یا «دوره». اینجوری تعداد حالتها محدود میشه و دوباره جدول جواب میده.
یه راه قویتر اینه که اصلا جدول نذاریم و به جاش یه شبکهی عصبی بذاریم که حالت رو بگیره و عدد Q هر کار رو حدس بزنه. اینجوری حتی حالتهای پیوسته هم ممکن میشن، و شبکه میتونه از تجربههای قبلی، برای حالتهایی که هیچوقت ندیده هم حدس خوبی بزنه.
یادگیری Q عمیق
سال ۲۰۱۴ یه سیستم، یادگیری Q رو با یه شبکهی عمیق کانولوشنی ترکیب کرد که تصویر خود صفحهی بازی رو میدید، و بازیهای ویدیویی قدیمی رو در حد یه بازیکن خبره بازی کرد. به این میگن یادگیری Q عمیق. ولی یه مشکل داشت که باید حلش میکردن:
وقتی جای جدول یه شبکهی عصبی میذاریم، یادگیری ناپایدار میشه. یکی از دلیلهاش اینه که تجربههایی که پشت سر هم میان، خیلی شبیه همن؛ مثلا ده تا قدم پشت سر هم توی یه گوشهی بازی. اگه شبکه فقط از قدم آخر یاد بگیره، مدام بین این گوشه و اون گوشه بالا پایین میپره.
راه حل اول اسمش بازپخش تجربه هست. هر تجربه رو توی یه جعبه نگه میدارن، و موقع یاد گرفتن، به جای قدم آخر، چند تا تجربهی شانسی از کل جعبه برمیدارن. اینجوری تجربههای پشت سر هم قاطی میشن و یادگیری آرومتر میشه. راه حل دوم اینه که عددهای هدف، یعنی همون «پاداش + ۰٫۹ × بهترین بعدی»، هر قدم عوض نشن و فقط گاهبهگاه بهروز بشن، تا شبکه دنبال یه هدف متحرک ندوه.
Q عمیق از نزدیک
ولودیمیر منیه و همکارهاش سال ۲۰۱۵ نتیجهی کامل این کار رو منتشر کردن. ورودی شبکه فقط پیکسلهای صفحهی بازی و امتیاز بود؛ هیچکس بهش نگفته بود توپ چیه یا دشمن کجاست. و همون یه شبکه، با همون تنظیمها، روی ۴۹ بازی مختلف امتحان شد و توی بیشترشون در حد یه آزمایشگر حرفهای بازی کرد. خروجی شبکه هم ساده بود: برای هر دکمهی دستهی بازی، یه عدد Q؛ و بازی دکمهای رو میزد که عددش بیشتره.
همون خوشبینی بیخودی که بالاتر دیدیم، توی شبکه هم بود. سال ۲۰۱۶ ون هاسلت و همکارهاش همون ایدهی دوتایی رو به Q عمیق آوردن: شبکهی اصلی انتخاب میکنه کدوم کار بهتره، و شبکهی هدف، همون که گاهبهگاه بهروز میشه، عددش رو میده. لازم هم نشد شبکهی تازهای بسازن، چون از قبل دو تا شبکه داشتن. به این میگن Q عمیق دوتایی، و با همین تغییر کوچیک، تخمینها واقعبینانهتر و نتیجه توی خیلی از بازیها بهتر شد.
چند نکته
دو تا نکتهی آخر. بدون کاوش هیچی: اگه عامل همیشه بهترین عدد فعلی رو برداره، ممکنه هیچوقت راه بهتر رو پیدا نکنه؛ تاس یا شروع خوشبین لازمه. و حالت پایانی: خونهی آخر بازی، مثل پنیر، دیگه قدم بعدی نداره؛ عدد اونجا همون پاداش خودشه و معمولا صفر حساب میشه، تا ارزش از همونجا به عقب پخش بشه. توی کد بالا، همین کار رو خط best_next = 0میکرد.
اشتباههای رایج
رایجترینش کاوش نکردنه؛ موشی که از روز اول همیشه بهترین عدد فعلی رو برمیداره، ممکنه با اولین راهی که پیدا کرد بمونه. بعدی فراموش کردن حالت پایانیه؛ اگه برای خونهی پنیر هم «بهترین عدد بعدی» حساب بشه، عددهای الکی وارد جدول میشن. بعدی ضریب تخفیف یک یا بیشتر توی بازیهایی که تموم نمیشن، که عددها رو تا بینهایت بالا میبره. بعدی قضاوت از روی عدد کارهای بد؛ دیدیم که اون عددها کم بهروز میشن و دقیق نیستن. و آخری، وقتی جای جدول شبکه میذاریم، یاد گرفتن فقط از قدم آخر و با هدف متحرک، که دقیقا دلیل ساخته شدن بازپخش تجربه و شبکهی هدف بود.
از کجا اومد
یادگیری Q از یه رسالهی دکتری شروع شد:
کریستوفر واتکینز سال ۱۹۸۹ توی رسالهی دکتریش توی دانشگاه کمبریج یادگیری Q رو معرفی کرد، و سال ۱۹۹۲ همراه پیتر دایان ثابت کرد که به جواب درست میرسه. سارسا سال ۱۹۹۴ از گاوین رامری و ماهسان نیرانجان اومد؛ اسم سارسا رو بعدا ریچارد ساتن روش گذاشت. ون هاسلت سال ۲۰۱۰ یادگیری Q دوتایی رو داد، منیه و همکارهاش سال ۲۰۱۵ Q عمیق رو، و سال ۲۰۱۶ Q عمیق دوتایی اومد.
درس بعد مستقیم سراغ همون تاس میریم، یعنی تعادل بین امتحان کردن راه تازه و استفاده از راه آشنا:کاوش یا بهرهبرداری.
جمعبندی. آنچه از این درس با خودتان میبرید.
- جدول: هر حالت در هر کار، یه عدد Q.
- Q تازه = قاطی قدیمی و «پاداش + ۰٫۹ × بهترین بعدی».
- ارزش از ته راه، دور به دور به اول میرسه.
- نرخ یادگیری و ضریب تخفیف، دو پیچ اصلی.
- جدول بزرگ نمیشه؟ شبکهی عصبی جاش.
خودتون رو بسنجید
۱۵ پرسش، هر بار تازه از میان ۳۰ پرسش این درس. آخرش فقط کارنامه رو میبینید: چند تا درست، چند تا نادرست.