شنائی با الگوریتمهای ژنتیک
همانطور که گفتیم یکی از شاخههای پردازش تکاملی، الگوریتمهای ژنتیک میباشد. این الگوریتمها با الهام از روند تکاملی طبیعت، مسائل را حل میکنند. به این معنی که مانند طبیعت یک جمعیت از موجودات تشکیل میدهند و درون این موجودات اقدام به انجام اعمالی چون انتخاب والدین، تولید مثل، جهش و ... میکنند و این اعمال را آنقدر تکرار میکنند تا به مجموعه بهینه و یا موجود بهینه برسند.
این الگوریتمها با توجه به خصوصیات خاصی که دارند، به خوبی از عهده حل مسائلی که نیاز به بهینهسازی دارند و یا پارامترهای زیادی در آنها دخیل است، برمیآیند. در این قسمت به معرفی این الگوریتمها میپردازیم.
تشریح ساختار الگوریتمهای ژنتیک
روند اجرای الگوریتمهای ژنتیک به صورت زیر است:
انتخاب والدین
باز ترکیبی
جهش
انتخاب فرزندان
تست شرط خاتمه
ارزیابی جمعیت
مساله
بازنمائی
جستجوی ژنتیکی
جواب
تشکیل جمعیت اولیه
همانطور که میبینید، برای حل یک مساله با استفاده از الگوریتمهای ژنتیک بایستی مراحل زیر را طی کنیم:
1. مدلسازی مساله یا بازنمائی
2. تشکیل جمعیت اولیه
3. ارزیابی جمعیت
4. انتخاب والدین
5. بازترکیبی
6. جهش
7. انتخاب فرزندان
8. شرط خاتمه الگوریتم
در ادامه به تشریح هر یک از قسمتهای این الگوریتمها میپردازیم.
مدلسازی مساله یا بازنمائی
بر خلاف بسیاری از روشهای حل مساله که از همان فرم کلی مساله برای حل مساله استفاده میکنند، برای اینکه بتوانیم یک مساله را بوسیله الگوریتمهای ژنتیک حل کنیم، بایستی آنرا به فرم مخصوص مورد نیاز این الگوریتمها تبدیل کنیم.
در این روند ما بایستی راه حل مورد نیاز مساله را به گونهای تعریف کنیم که قابل نمایش بوسیله یک کروموزوم باشد. این کروموزوم میتواند یک آرایه از اعداد، رشتهها و یا بیتها باشد، یا اینکه یک عدد طبیعی، یا حقیقی و ... باشد. اما به طور کلی بایستی به گونهای تعریف شود که بتوانیم عملگرهای خاص الگوریتمهای ژنتیک که بازترکیبی، جهش و ارزیابی هستند را برروی کروموزومها تعریف و اعمال کنیم.
به عنوان مثال در یک مساله مرتب سازی، کروموزوم را میتوانیم به این شکل تعریف کنیم که بعنوان مثال از چپ به راست، اندیس عناصر از کوچک به بزرگ را نگهداری کند. که در این حالت سمت چپترین عنصر، اندیس کوچکترین و سمت راستترین عنصر، اندیس بزرگترین عنصر آرایه باشد.
اینکه مساله خود را چگونه بازنمائی کنیم بسیار مهم است، چرا که یک بازنمائی خوب میتواند سرعت پیدا شدن جواب را افزایش دهد. علاوه بر اینکه در میزان مصرف حافظه و سرعت اعمال عملگرهای ژنتیک تاثیر فراوانی دارد. علت این امر نیز این است که هر یک از عملگرهای ژنتیک بایستی هزاران بار، در طول اجرای الگوریتم بر روی کروموزومهای مختلف اعمال شوند.
اینکه چه نوع بازنمائی را برای مساله استفاده شود، به شخص طراح و فرم مساله بستگی دارد. در زیر چند نمونه از بازنمائیهایی را که معمولاً استفاده میشوند را تعریف میکنیم:
< >اعداد صحیحرشتههای بیتی اعداد حقیقی در فرم نقطه شناوراعداد حقیقی به فرم رشته های بیتییک مجموعه از اعداد حقیقی یا صحیحماشینهای حالت محدودهر فرم دیگری که بتوانیم عملگرهای ژنتیک را بر روی آنها تعریف کنیم
تشکیل جمعیت اولیه
بعد از اینکه شکل کروموزومها را تعریف نمودیم، بایستی جمعیتی را تشکیل دهیم، که میخواهیم عناصر آنرا تکامل دهیم. تعداد عناصر موجود در این جمعیت معمولاً ثابت است. به این معنی که هنگامی که تعدادی عنصر در جریان تولید مثل به این جمعیت اضافه میکنیم، بایستی به همین تعداد عنصر نیز از جمعیت قبلی حذف کنیم.
قبل از اینکه الگوریتم بتواند آغاز به کار کند، بایستی یک جمعیت اولیه از کروموزومها تشکیل بدهیم. در اکثر الگوریتمها این جمعیت اولیه به صورت تصادفی تشکیل میشود. به این معنی که به اندازه طول جمعیت، کروموزوم تصادفی ایجاد میکنیم و آنها را به جمعیت اولیه اضافه میکنیم.
البته برای اینکه الگوریتم سریعتر به جواب برسد، میتوانیم بوسیله یکی از الگوریتمهای کم هزینه تعدادی از جوابهای تقریباً بهینه را محاسبه کرده و از آنها بعنوان جمعیت اولیه استفاده میکنیم. دیده شده است که در بعضی مسائل انجام این عمل تاثیر بسزائی در سرعت همگرائی الگوریتم دارد. البته نباید فراموش کنیم که انجام این عمل در مواردی باعث می شود، الگوریتم در مینیممهای محلی ناشی از جمعیت آغازی گیر افتاده و نتواند جوابهای بهتری را پیدا کند. برای جلوگیری از این مشکل علاوه بر جوابهای بهینه پیدا شده، تعدادی عنصر نیز به صورت تصادفی به جمعیت اضافه میکنیم.
ارزیابی جمعیت
برای اینکه بتوانیم موجودات بهتر را درون جمعیت تشخیص بدهیم بایستی معیاری را تعریف کنیم که بر اساس آن موجودات بهتر را تشخیص دهیم.
به این کار، یعنی تعیین میزان خوبی یک موجود، ارزیابی آن موجود میگویند. ارزیابی، اینگونه است که بر حسب اینکه موجود چقدر خوب است یک عدد به آن نسبت میدهیم، این عدد که برای موجودات بهتر بزرگتر (یا کوچکتر) است را شایستگی آن موجود مینامیم.
به عنوان مثال در صورتی که به دنبال مینیمم یک تابع هستیم، مقدار شایستگی را میتوانیم مقدار خروجی تابع به ازای ورودی مشخص باشد. همانطور که میبینید، هدف ما پیدا کردن مینیمم تابع است، پس ورودیهایی که مقادیر تابع برای آنها کمتر است، ورودیهای بهتری هستند.
بسته به نوع مساله ما میخواهیم شایستگی را بیشینه و یا کمینه کنیم. البته معمولاً در مطالعات تئوری فرض بر این گذاشته میشود که میخواهیم مقدار شاسیتگی را کمینه کنیم.
در مطالب این فصل تعاریف به گونهای ارائه شدهاند که در آنها سعی در مینیمم کردن یک تابع داریم، اما شایستگی عناصر به گونهای تعریف شده است که برای عناصر کوچکتر، مقادیر بزرگتری ارائه میدهد.
انتخاب والدین
در هر نسل تعدادی از عناصر جمعیت این فرصت را پیدا میکنند که تولید مثل کنند. به این عناصر که از میان جمعیت انتخاب میشوند، والدین میگویند. روشهای مختلفی برای انتخاب والدین وجود دارند. در زیر به چند مورد از این روشها اشاره میکنیم:
< >انتخاب تمام جمعیت بعنوان والدین: در واقع هیچگونه انتخابی انجام نمیدهیم.انتخاب تصادفی: بصورت تصادفی تعدادی از موجودات جمعیت را بعنوان والدین انتخاب میکنیم، این انتخاب میتواند با جایگذاری یا بدون جایگذاری باشد.روشهای مبتنی بر شایستگی: در این روشها عناصر با شایستگی بیشتر شانس بیشتری برای انتخاب شدن بعنوان والدین را دارند. سایر روشها: این روشها با استفاده از تکنیکهایی سعی میکنند که انتخابهایی را ارائه دهند، که هم رسیدن به جواب نهایی را تسریع کنند و هم اینکه کمک میکنند که جواب بهینهتری پیدا شود.باز ترکیبی (Recombination)
همانطور که میدانیم یک کروموزوم در طبیعت از تعداد زیادی ژن تشکیل شده است، که یک ژن یا ترکیبی از این ژنها یکی از خصوصیات موجود را معرفی مینمایند. بعنوان مثال یک ژن رنگ چشم، ژن دیگر شکل چشم و ... را نشان میدهند.
در حین عملیات تشکیل سلول تخم، کروموزومهای والدین با یکدیگر ترکیب میشوند و کروموزومهای جدیدی را بوجود میآورند، در جریان این کار به صورت اتفاقی بخشهایی از کروموزومها با یکدیگر عوض میشوند. این موضوع باعث میشود که فرزندان ترکیبی از خصوصیات والدین خود را به همراه داشته باشند و دقیقاً مشابه یکی از والدین نباشند.
ما نیز این موضوع را در الگوریتم ژنتیک خود شبیه سازی کنیم، به این امید که خصوصیات خوب دو موجود در فرزندشان جمع شده و یک موجود بهتری را تولید کند. روش کار در شکل زیر نشان داده شده است:
نحوه انجام عملیات بازترکیبی
روش کار به صورت زیر است:
< >بصورت تصادفی یک نقطه از کروموزوم را انتخاب میکنیمژنهای مابعد آن نقطه از کروموزومها را جابجا میکنیمشایان ذکر است که عمل بازترکیبی را میتوان هم از نقاط آغازین ژنها انجام داد و هم اینکه میتوان بدون توجه به محل شروع ژن، عمل بازترکیبی را انجام داد.
همچنین اگر مانند مثال فوق عملیات بازترکیبی را در یک نقطه انجام دهیم به آن بازترکیبی تک نقطهای (Single Point Crossover) میگویند. اما میتوانیم این عملیات را در چند نقطه انجام دهیم، که به آن بازترکیبی چند نقطهای (Multipoint Crossover) میگویند، و در پایان اگر تمام نقاط کروموزوم را بعنوان نقاط بازترکیبی انتخاب کنیم به آن بازترکیبی جامع (Uniform Crossover) میگوئیم. در این دو مورد اخیر، روند کار به این صورت است:
با احتمال ثابتی مثل Pc عمل بازترکیبی را انجام میدهیم
روش کار به صورت زیر است:
< >به ازای هر یک از قسمتهای کروموزوم:یک عدد تصادفی بین صفر و یک تولید میکنیماگر این عدد از مقدار ثابتی مثل Pc کوچکتر باشد، ژنهای مابعد آن نقطه از کروموزومها را جابجا میکنیملازم به ذکر است که عملیات بازترکیبی موجودات جدیدی تولید نمیکند و تنها باعث میشود که موجودات موجود بهتر شوند.
در صورتی که برای بازنمائی کروموزومها از روشهایی غیر از اعداد صحیح و یا رشتههای عددی استفاده کرده باشیم، عملیات بازترکیبی را به روشهای دیگری پیاده سازی میکنیم. بعنوان مثال اگر از اعداد حقیقی برای ارائه استفاده کرده باشیم، یک روش اینست که قسمت حقیقی و مانتیس دو عدد را جابجا کنیم. برای سایر بازنمائیها نیز روشهای مختلفی برای بازترکیبی ارائه شده است که از حوصله این بحث خارج است.
جهش
همانطوری که گفتیم عملیات بازترکیبی موجود جدیدی را بوجود نمیآورد و تنها به بهینه سازی و تغییرات کوچک در موجودات میپردازد. بعنوان مثال در مینیمم سازی یک تابع، عملیات بازترکیبی ما را به مینیممهای محلی که عناصر اولیه در نزدیکی آنها قرار داشتهاند هدایت میکند، و نمیتواند ما را به مینیممهای کلی و یا حتی مینیممهای محلی دیگر هدایت کند.
برای حل کردن این مشکل به این صورت عمل میکنیم که با تغییرات تصادفی در ژنها، کروموزومها را به نقاط ناشناخته پرتاب میکنیم، به این امید که احتمالاً این نقطه جدید ما را به مینیمم کلی هدایت کند.
برای انجام جهش به این صورت عمل میکنیم:
< >بصورت تصادفی تعدادی از کروموزومهای فرزند را انتخاب میکنیم به صورت تصادفی مقادیر یک یا چند ژن وی را تغییر میدهیم. همچنین در هنگام پیاده سازی به صورت زیر عمل میکنیم:< >به ازای هر کروموزوم اعمال زیر را انجام میدهیم:یک عدد تصادفی بین صفر و یک تولید میکنیماگر عدد تولید شده کوچکتر از Pm بود، به ازای هر ژن اعمال زیر را انجام میدهیم، در غیر اینصورت از جهش دادن کروموزوم صرفنظر می کنیم یک عدد تصادفی بین صفر و یک تولید میکنیماگر عدد تولید شده کوچکتر از Pg بود، ژن مربوطه را جهش می دهیم بعنوان مثال جهش برای کروموزومهای به فرم باینری به صورت زیر میباشد:
نحوه انجام عملیات جهش
جهش، برای بازنمائیهایی که از مقادیر حقیقی استفاده کردهاند، به این صورت پیاده سازی میشود که یک عدد حقیقی بصورت تصادفی در یک محدوده خاص تعیین و جایگزین عدد قبلی گردد و یا اینکه عدد اصلی با یک مقدار خاص جمع گردد و ....
برای سایر ارائهها نیز انواع خاصی از جهش پیشنهاد شده است.
با توجه به اینکه هدف از جهش بهتر شدن کروموزومها و یا اینکه پیدا شدن یک راه حل جدید است، میتوانیم به جای تغییر تصادفی کروموزومها، تغییرات کروموزومها را هدفمند کنیم. برای اینکار، بسته به نوع مساله، بر روی کروموزوم انتخاب شده یکی از روشهای کلاسیک حل مساله را اعمال کرده، و جواب حاصل را بعنوان کروموزوم جدید جایگزین میکنند. استفاده از این روش که از آن با عنوان جهش ابتکاری (Heuristic) یاد میشود، بسته به نوع مساله ممکن است دستیابی به راه حل نهایی را سریعتر کنند. البته در این موارد بایستی از روشهای ابتکاری سریع استفاده کنیم و یا اینکه با الگوریتمهای کم هزینه تنها به بهتر کردن کروموزوم بپردازیم.
نکته آخری که به آن اشاره کنیم این است که جهش باعث ایجاد تغییرات ناخواسته در جمعیت شده، و باعث بوجود آمدن موجودات جدید میشود. در واقع برتری جهش نسبت به بازترکیبی نیز همین مطلب میباشد. نتیجهای که از این مطلب بدست میآید اینست که در صورتی که فقط از جهش استفاده کنیم، ممکن است که بتوانیم جواب بهینه را پیدا کنیم، اما استفاده از بازترکیبی به تنهایی پیدا شدن جواب بهینه تضمین نمیشود.
انتخاب بازماندگان
بعد از اینکه تعداد مناسبی فرزند تولید کردیم، لازم است که جمعیت نسل بعد را آماده کنیم. همانطور که قبلاً ذکر شده است، طول جمعیت بایستی ثابت باشد. به همین دلیل لازم است که قبل از شروع دور جدید، جمعیتی با طول مشخص شده، تشکیل بدهیم.
برای تولید نسل بعدی دو استراتژی مختلف وجود دارد، استراتژی اول به این شکل عمل میکند که جمعیت نسل بعد فقط از میان فرزندان تولید شده تشکیل میشود. واضح است که در این حالت بایستی تعداد فرزندان تولید شده، بیشتر از تعداد عناصر لازم برای تشکیل جمعیت نسل بعد باشد. مزیت این استراتژی این است که به راحتی میتواند، مینیممهای محلی ناشی از عناصر بهینه نسل قبل را فراموش کرده و سعی کند که مینیمم کلی را پیدا کند.
در استراتژی دیگری که برای تشکیل جمعیت نسل بعد وجود دارد، به این شکل عمل میشود که جمعیت نسل بعد از میان والدین و فرزندانی که از آنها بوجود آمده است انتخاب میشوند. مزیت این استراتژی این است که عناصر بهینه نسل قبل حفظ میشوند، اما مشکلی که وجود دارد این است که ممکن است، الگوریتم در مینیممهای محلی ناشی از عناصر بهینه نسل قبل به دام بیفتد.
بدون توجه به اینکه از کدامیک از استراتژیهای فوق استفاده کنیم، بایستی با استفاده از یکی از روشهای انتخابی که در فصل بعد در مورد آنها توضیح میدهیم جمعیت نسل بعد را تشکیل دهیم.