الگوریتم ژنتیک
هنگامی که لغت تنازع بقا به کار میرود اغلب بار ارزشی منفی آن به ذهن میآید. شاید همزمان قانون جنگل به ذهن برسد و حکم بقای قویتر!
البته برای آنکه خیالتان راحت شود میتوانید فکر کنید که همیشه هم قویترینها برنده نبودهاند. مثلا دایناسورها با وجود جثه عظیم و قویتر بودن در طی روندی کاملا طبیعی بازی بقا و ادامه نسل را واگذار کردند در حالی که موجوداتی بسیار ضعیفتر از آنها حیات خویش را ادامه دادند. ظاهرا طبیعت بهترینها را تنها بر اساس هیکل انتخاب نمیکند! در واقع درستتر آنست که بگوییم طبیعت مناسب ترینها (Fittest) را انتخاب میکند نه بهترینها.
قانون انتخاب طبیعی بدین صورت است که تنها گونههایی از یک جمعیت ادامه نسل میدهند که بهترین خصوصیات را داشته باشند و آنهایی که این خصوصیات را نداشته باشند به تدریج و در طی زمان از بین میروند.
مثلا فرض کنید گونه خاصی از افراد، هوش بسیار بیشتری از بقیه افراد یک جامعه یا کولونی دارند. در شرایط کاملا طبیعی این افراد پیشرفت بهتری خواهند کرد و رفاه نسبتا بالاتری خواهند داشت و این رفاه خود باعث طول عمر بیشتر و باروری بهتر خواهد بود(توجه کنید شرایط طبیعیست نه در یک جامعه سطح بالا با ملاحظات امروزی یعنی طول عمر بیشتر در این جامعه نمونه با زاد و ولد بیشتر همراه است). حال اگر این خصوصیت(هوش)ارثی باشد به طبع در نسل بعدی همان جامعه تعداد افراد باهوش به دلیل زاد و ولد بیشتر اینگونه افراد بیشتر خواهد بود. اگر همین روند را ادامه دهید خواهید دید که در طی نسلهای متوالی دائما جامعه نمونه ما باهوش و باهوشتر میشود. بدین ترتیب یک مکانیزم ساده طبیعی توانسته است در طی چند نسل عملا افراد کم هوش را از جامعه حذف کند علاوه بر اینکه میزان هوش متوسط جامعه نیز دائما در حال افزایش است(البته امکان داشت اگر داروین بیعرضگی افراد باهوش امروزی را میدید کمی در تئوری خود تجدید نظر میکرد اما این مسئله دیگریست!).
بدین ترتیب میتوان دید که طبیعت با بهرهگیری از یک روش بسیار ساده(حذف تدریجی گونههای نامناسب و در عین حال تکثیر بالاتر گونههای بهینه) توانسته است دائما هر نسل را از لحاظ خصوصیات مختلف ارتقا بخشد.
البته آنچه در بالا ذکر شد به تنهایی توصیف کننده آنچه واقعا در قالب تکامل در طبیعت اتفاق میافتد نیست. بهینهسازی و تکامل تدریجی به خودی خود نمیتواند طبیعت را در دسترسی به بهترین نمونهها یاری دهد. اجازه دهید تا این مساله را با یک مثال شرح دهیم.
پس از اختراع اتومبیل به تدریج و در طی سالها اتومبیلهای بهتری با سرعتهای بالاتر و قابلیتهای بیشتر نسبت به نمونههای اولیه تولید شدند. طبیعیست که این نمونههای متاخر حاصل تلاش مهندسان طراح جهت بهینهسازی طراحیهای قبلی بوده اند. اما دقت کنید که بهینهسازی یک اتومبیل تنها یک "اتومبیل بهتر" را نتیجه میدهد.
اما آیا میتوان گفت اختراع هواپیما نتیجه همین تلاش بوده است؟ یا فرضا میتوان گفت فضا پیماها حاصل بهینهسازی طرح اولیه هواپیماها بودهاند؟
پاسخ اینست که گرچه اختراع هواپیما قطعا تحت تاثیر دستاورهای صنعت اتومبیل بوده است اما بههیچ وجه نمیتوان گفت که هواپیما صرفا حاصل بهینهسازی اتومبیل و یا فضا پیما حاصل بهینهسازی هواپیماست. در طبیعت هم عینا همین روند حکمفرماست. گونههای متکاملتری وجود دارند که نمیتوان گفت صرفا حاصل تکامل تدریجی گونه قبلی هستند.
در این میان آنچه شاید بتواند تا حدودی ما را در فهم این مساله یاری کند مفهومیست به نام : تصادف یا جهش.
به عبارتی طرح هواپیما نسبت به طرح اتومبیل یک جهش بود و نه یک حرکت تدریجی. در طبیعت نیز به همین گونهاست. در هر نسل جدید بعضی از خصوصیات به صورتی کاملا تصادفی تغییر مییابند سپس بر اثر تکامل تدریجی که پیشتر توضیح دادیم در صورتی که این خصوصیت تصادفی شرایط طبیعت را ارضا کند حفظ میشود در غیر اینصورت به شکل اتوماتیک از چرخه طبیعت حذف میگردد.
در واقع میتوان تکامل طبیعی را به اینصورت خلاصه کرد: جستوجوی کورکورانه(تصادف یا Blind Search)+ بقای قویتر.
حال ببینیم که رابطه تکامل طبیعی با روشهای هوش مصنوعی چیست. همانطور که در شماره قبل و در هنگام بحث راجع به کولونی مورچهها گفتیم هدف اصلی روشهای هوشمند به کار گرفته شده در هوش مصنوعی یافتن پاسخ بهینه مسائل مهندسی ست. بعنوان مثال اینکه چگونه یک موتور را طراحی کنیم تا بهترین بازدهی را داشته باشد یا چگونه بازوهای یک ربات را محرک کنیم تا کوتاهترین مسیر را تا مقصد طی کند(دقت کنید که در صورت وجود مانع یافتن کوتاهترین مسیر دیگر به سادگی کشیدن یک خط راست بین مبدا و مقصد نیست) همگی مسائل بهینهسازی هستند.
روشهای کلاسیک ریاضیات دارای دو اشکال اساسی هستند. اغلب این روشها نقطه بهینه محلی(Local Optima) را بعنوان نقطه بهینه کلی در نظر میگیرند و نیز هر یک از این روشها تنها برای مساله خاصی کاربرد دارند. این دو نکته را با مثالهای سادهای روشن میکنیم.
به شکل زیر توجه کنید. این منحنی دارای دو نقطه ماکزیمم میباشد. که یکی از آنها تنها ماکزیمم محلی ست. حال اگر از روشهای بهینهسازی ریاضی استفاده کنیم مجبوریم تا در یک بازه بسیار کوچک مقدار ماکزیمم تابع را بیابیم. مثلا از نقطه 1 شروع کنیم و تابع را ماکزیمم کنیم. بدیهیست اگر از نقطه 1 شروع کنیم تنها به مقدار ماکزیمم محلی دست خواهیم یافت و الگوریتم ما پس از آن متوقف خواهد شد. اما در روشهای هوشمند خاصه الگوریتم ژنتیک بدلیل خصلت تصادفی آنها حتی اگر هم از نقطه 1 شروع کنیم باز ممکن است در میان راه نقطه A به صورت تصادفی انتخاب شود که در این صورت ما شانس دستیابی به نقطه بهینه کلی (Global Optima) را خواهیم داشت.
چارچوب کلی الگوریتم ژنتیک
در دهه هفتاد میلادی دانشمندی از دانشگاه میشیگان به نام جان هلند ایده استفاده از الگوریتم ژنتیک را در بهینهسازیهای مهندسی مطرح کرد. ایده اساسی این الگوریتم انتقال خصوصیات موروثی توسط ژنهاست. فرض کنید مجموعه خصوصیات انسان توسط کروموزومهای او به نسل بعدی منتقل میشوند. هر ژن در این کروموزومها نماینده یک خصوصیت است. بعنوان مثال ژن 1 میتواند رنگ چشم باشد ، ژن 2 طول قد، ژن 3 رنگ مو و الی آخر. حال اگر این کروموزوم به تمامی، به نسل بعد انتقال یابد، تمامی خصوصیات نسل بعدی شبیه به خصوصیات نسل قبل خواهد بود. بدیهیست که در عمل چنین اتفاقی رخ نمیدهد. در واقع بصورت همزمان دو اتفاق برای کروموزومها میافتد. اتفاق اول موتاسیون (Mutation) است. موتاسیون به این صورت است که بعضی ژنها بصورت کاملا تصادفی تغییر میکنند. البته تعداد این گونه ژنها بسیار کم میباشد اما در هر حال این تغییر تصادفی همانگونه که پیشتر دیدیم بسیار مهم است. مثلا ژن رنگ چشم میتواند بصورت تصادفی باعث شود تا در نسل بعدی یک نفر دارای چشمان سبز باشد. در حالی که تمامی نسل قبل دارای چشم قهوهای بودهاند. علاوه بر موتاسیون اتفاق دیگری که میافتد و البته این اتفاق به تعداد بسیار بیشتری نسبت به موتاسیون رخ میدهد چسبیدن ابتدای یک کروموزوم به انتهای یک کروموزوم دیگر است. این مساله با نام CrossOver شناخته میشود. این همان چیزیست که مثلا باعث میشود تا فرزند تعدادی از خصوصیات پدر و تعدادی از خصوصیات مادر را با هم به ارث ببرد و از شبیه شدن تام فرزند به تنها یکی از والدین جلوگیری میکند.
شکل 2: عملگرهای ژنتیک (Genetic Operators)
بر اساس آنچه ذکر شد میتوانیم مراحل الگوریتم ژنتیک را به صورت زیر توضیح دهیم. ابتدا باید راه حلهای مختلف مساله را به صورت جمعیتی از کروموزومها نشان دهیم. هر کروموزوم نشان دهنده یک جواب مساله است. در واقع هدف الگوریتم دستیابی به کروموزمهای بهینه (جوابها) از طریق تولید مثل بهترین کروموزمها در هر نسل است. کروموزومها اغلب به صورت یک رشته از صفر و یک ها(مقادیر باینری) نمایش مییابند که هر بیت از این رشته میتواند نمایشگر یک ژن باشد. البته نمایشهای دیگری از کروموزوم به شکل درخت (Tree) یا لیست وجود دارد که فعلا به آنها نمیپردازیم.
پس از این کار باید معیاری برای تشخیص جوابهای بهینه یا ارزیابی بهترین کروموزمها داشته باشیم . این معیار اغلب به صورت یک تابع ریاضی که بر کروموزومها اعمال میشود نشان داده میشود که به نام تابع هدف یا تابع بهینگی نامیده میشود.
این دو کار در واقع مراحل آماده سازی جهت اجرای الگوریتم ژنتیک است. پس از انجام این دو کار یک جمعیت ابتدایی (نسل اولیه) از راه حلهای ممکن داریم. برای شروع الگوریتم این نسل اولیه باید تولید مثل کند. تولید مثل در سه مرحله انجام میشود. مرحله اول انتخاب یا Selection است. در این مرحله بهترین کروموزومها (بر اساس تابع بهینگی) برای تولید مثل انتخاب میشوند. ذکر این نکته ضروریست که حتی در اینجا هم اندکی تصادف را باید دخیل کنیم. یعنی مثلا بهترین کروموزمها را به احتمال 90% انتخاب کنیم. این احتمالاتی انتخاب کردن بهترینها اندکی شانس بقا به کروموزمهای ضعیفتر میدهد. اما شاید بپرسید چرا نباید با اطمینان بهترینها را انتخاب کرد و همه ضعیفترها را در هر نسل از بین برد؟
پاسخ به سئوال فوق تا حدی دلایل ریاضی دارد که توضیح آن در این مجال کمی مشکل است. اما میتوان به این توضیح بسنده کرد که لزوما تمامی ژنهای یک کروموزوم ضعیف ناقل خصوصیات منفی نیستند. چه بسا بعضی خصوصیات بسیار مثبت در یک کروموزوم ضعیف وجود داشته باشد. با دادن اندکی شانس بقا و تولید مثل به کروموزمهای ضعیف در واقع احتمال انتقال این خصوصیات فرضی را افزایش دادهایم.
پس از مرحله انتخاب نوبت به تولید مثل کروموزمهای انتخاب شده میرسد. اینکار همانگونه که پیشتر گفتیم توسط دو عملگر CrossOver و موتاسیون انجام میپذیرد. در مرحله CrossOver کروموزمهای انتخاب شده به تصادف با یکدیگر ترکیب میشوند تا کروموزوم جدیدی را برای نسل بعدی تولید کنند. این ترکیب تاکنون به شیوههای مختلفی پیادهسازی شده است. سادهترین شکل آن اینست که ابتدای یک کروموزم(یا همان رشته باینری) به انتهای کروموزوم دیگری بچسبد. به این ترتیب کروموزوم حاصل بخشی از خصوصیات هر دو را به ارث میبرد.
در مرحله موتاسیون مقدار بعضی ژنها به تصادف عوض میشود. یعنی مثلا اگر کروموزومهای ما چنانکه گفتیم بوسیله رشتههای صفر و یک نمایش داده شوند در هر کروموزوم تعداد صفر یا بیشتر ژن(بیت) انتخاب شده و مقدار آنها از صفر به یک یا برعکس تغییر مییابد. برای موتاسیون نیز روشهای دیگری وجود دارد که روش گفته شده سادهترین و در عین حال معمولترین آنهاست. آنچه باید بدان دقت شود اینست که درصد موتاسیون معمولا بسیار کم در نظر گرفته میشود. مثلا چیزی کمتر از پنج درصد کروموزومها تحت تاثیر موتاسیون قرار میگیرند.
پس ازین کار در واقع نسل جدید کروموزمها ایجاد شده است که بر طبق آنچه تا کنون گفتیم در مجموع بهینهتر از نسل قبلیست. تنها نکته باقی مانده اینست که نسل قبلی چه میشود. آیا پس از ایجاد نسل جدید کروموزمها تمامی نسل قبلی از بین میرود؟ یا اینکه هر کرموزوم پس از طول عمر مشخصی میمیرد؟ یا فقط تعدادی از بهترین کروموزومهای نسل قبلی در نسل جدید باقی میمانند و ....
در حالت کلی از هریک از روشهای فوق میتوان درباره نسل پیشین کروموزمها بهره گرفت، گرچه به نظر میرسد طول عمر داشتن کروموزمها از لحاظ طبیعی قابلیت تعمق بیشتری دارد.
تا کنون به کلیت و نیز چگونگی پیاده سازی الگوریتم ژنتیک اشاره کردیم. اینک میخواهیم یک جمع بندی از مزایا و معایب این الگوریتم داشته باشیم. اولین خصوصیت مثبت این الگوریتم دستیابی به نقطه بهینه کلی (Global Optima) به جای نقطه بهینه محلی ست. یعنی همیشه در حد بسیار مطلوبی میتوان به پاسخ این الگوریتم اعتماد کرد و اینکه پاسخی که مییابد به احتمال زیاد بهترین پاسخ ممکن است.
علاوه بر این این الگوریتم به همین شکل موجود در حل انواع مسائل میتواند به کار رود و نیازی به تغییر آن نیست. در واقع تنها کاری که در مورد هر مساله باید انجام دهیم اینست که جوابهای مختلف را به شکل کروموزومها بازنمایی کنیم.
هر چند خود الگوریتم ژنتیک برای حل مسائل بهینهسازی گسسته به کار میرود اما روشهای مشابهی همچون استراتژی تکاملی و یا الگوریتم آب دادن فولاد(ُSimulated Annealing) وجود دارند که عینا در مورد مسائل پیوسته میتوانند به کار روند.
نحوه تعریف و پیادهسازی این الگوریتم نیز به گونهایست که آن را بسادگی جهت اجرا بصورت موازی یا بر روی Multiprocessor ها مناسب میسازد.
اما مشکل اصلی الگوریتم ژنتیک علیرغم سادگی پیادهسازی، هزینه اجرای آنست.اغلب حل یک مسئله نیازمند تولید چندین هزار نسل از کروموزمهاست و این مسئله نیاز به زمان زیادی دارد(خصوصا اگر تعداد جمعیت اولیه زیاد باشد و نیز تابع هدف تابع پیچیدهای باشد). گاه پیش میآید که برای حل یک مسئله بعنوان مثال یک پردازنده پنتیوم باید بیش از یک هفته برنامه را اجرا کند. طبیعیست که این زمان زیادیست برای حل یک مساله و همین امر گاهی استفاده از الگوریتم را با مشکل مواجه میکند.
لیست کل یادداشت های این وبلاگ