توسعهدهنده نرمافزار و SRE در سحابپرداز
یک روش عملیاتی در دادهکاوی، برای پیشبینی انتخابات
مقدمه
یک روش برای پیشبینی انتخابات، تحلیل احساسات افراد حین ارسال توییتهایی دربارهی کاندیداها است. برای مثال اگر افراد زیادی از ترامپ راضی باشند، توییتهایی با مضامین آمیخته با خوشحالی و احساس رضایت دربارهی ترامپ، بیشتر به چشم خواهد خورد.
ما دو مجموعهداده داریم. در مجموعهدادهی اول، هر توییت به احساس فرد نویسنده حین ارسال، نگاشت شده است. برای مثال متن توییت و احساس خوشحالی. مجموعهدادهی دوم، هرتوییت به کاندیدایی که آن توییت دربارهی اوست، نگاشت شده است. برای مثال متن توییت و دونالد ترامپ.
روش اجرا
ما با اجرای الگوریتمهای پردازش متن، بر روی مجموعهدادهی اول، مدلی بهدست میآوریم که میتواند احساسات توییتهای دیگر را پیشبینی کند. با ارسال توییتهای مجموعهدادهی دوم به این مدل، احساسات افراد حین ارسال توییت دربارهی کاندیداها پیشبینی میشود. حال با این روش، این دو مجموعهداده ادغام شده و هر دادهی ما بهصورت کاندیدا و احساس خواهد بود. کاندیدایی که احساسات مثبت بیشتری نسبت به آن وجود دارد، احتمال برنده شدن بیشتری دارد.
اولین ایده برای پیادهسازی این روش، پیادهسازی کدی به زبان پایتون است. ابتدا مجموعهدادهی اول را از فایل خوانده، سپس بر روی آن الگوریتم پردازش متن، اجرا میشود. بعد، مجموعهدادهی دوم را از فایل خوانده، و هر رکورد از آن، به مدل خروجی قسمت اول داده شده و احساس را پیشبینی میکند. سپس با توجه به هر احساس، به هر کاندیدا نمرهای داده شده و کاندیدایی که نمره بیشتری دارد، انتخاب میشود.
مهمترین مزیت این روش، سادگی آن است؛ اما مقیاسپذیر نیست و با افزایش تعداد توییتهای مجموعهی دوم، زمان بیشتری صرف خواهد شد.
افزودن مقیاسپذیری
برای افزودن قابلیت مقیاسپذیری، پروژه به سه ماژول تقسیم میشود. ماژول اول که سازندهی جریان داده نام دارد، مجموعهدادهی دوم را از فایل خوانده و داخل یک صف توزیعشده، ذخیره میکند. همانطور که مشخص است، همچنان این ماژول را نمیتوان روی چند سرور مختلف اجرا کرد؛ اما زمان اجرای آن در حد یک تا دو دقیقه است و چون عملیات آن سریع انجام میشود، نیازی به افزودن قابلیت مقیاسپذیری نیست.
ماژول دوم که پردازشگر جریان داده نام دارد، رکوردهای مجموعهدادهی دوم را از صف توزیعشده گرفته، آنرا به مدل خود داده و احساس توییت را پیشبینی میکند. سپس نام کاندیدا، احساس توییت، تعداد لایک و تعداد ریتوییت آن را در پایگاهدادهی اصلی ذخیره میکند. زمان اجرای این ماژول بر روی یک ماشین، برابر یک ساعت بوده و با اجرای آن بر روی دو ماشین، این عدد به سی دقیقه کاهش مییابد.
ماژول سوم که پیشبینیگر نام دارد، بر روی پایگاهداده پردازش انجام داده و برندهی انتخابات را تعیین میکند. زمان اجرای آن برای مجموعهدادهی خروجی ماژول پردازشگر جریان که برابر یک میلیون داده است، برابر یک دقیقه بوده که عدد خوبی به حساب میآید. با همین محاسبات، اگر تعداد دادهها برابر یک میلیارد باشد، زمان اجرای آن برابر شانزده ساعت خواهد شد. پس بهتر است این عملیات نیز بر روی چند ماشین اجرا شود.
انتخاب ابزارها
همانطورکه گفته شد، ما به دو ابزار نیاز داریم. یکی از این ابزارها، صف توزیعشده است. ابزارهای زیادی وجود دارند، که میتوانند نیاز ما را برآورده کنند. حسب تجربه، ابزار Apache Kafka انتخاب میشود.
ابزار دیگری که به آن نیاز داریم، یک پایگاهداده یا حتی یک فایلسیستم است که بتواند دادهها را بهصورت توزیعشده، روی چند سرور نگهداری کند. باید بتوان روی آن، الگوریتمهای کلانداده را اجرا کرد. با توجه به نیاز بیانشده، Apache Hadoop انتخاب ما است؛ اما یک مشکل اساسی وجود دارد و آن اینکه نمیتوان از چند نخ مجزا استفاده و در یک فایل داده ذخیره کرد. برای رفع این مشکل، پردازشگر جریان به دو قسمت تقسیم میشود، یکی از آنها داده را پردازش کرده و در Apache Kafka ذخیره میکند و ماژول دیگر که ذخیرهساز نام دارد، داده را از Apache Kafka گرفته و در Apache Hadoop ذخیره میکند. حال ماژول پردازشگر جریان، کاملا میتواند به صورت توزیعشده، روی چند سرور اجرا شود؛ اما ماژول ذخیرهساز حتما باید دارای یک نخ باشد، و روی یک سرور اجرا شود. البته با توجه به اینکه پردازشی روی داده در ماژول ذخیرهساز وجود ندارد، کار این ماژول در کمتر از ۱ دقیقه تمام میشود، پس نیازی به اجرای آن روی چند سرور نیست.
ابزارهای گفتهشده، برروی JVM اجرا میشوند و کلاینتهای آنها در زبان جاوا پختگی کامل دارند؛ اما الگوریتمهای پردازش متن در زبان پایتون فراوانترند. بنابراین ماژول پردازشگر جریان، دوباره به ۲ ماژول تقسیم میشود. یک ماژول به نام تشخیصدهندهی احساسات افزوده شده و ارتباط ماژول پردازشگر جریان با آن، بهصورت صدازدن API خواهد بود. بدین صورت که پردازشگر جریان، توییت را در قالب درخواست HTTP از نوع POST به تشخیصدهندهی جریان داده و تشخیصدهندهی جریان در پاسخ، احساس را میفرستد.
برای اجرای تشخیصدهندهی احساسات، ابتدا باید دستههای ممکن مشخص، سپس ویژگیهای هر داده انتخاب و در مرحلهی آخر باید الگوریتم انتخاب شود. ابتدا با توجه به مجموعهدادهی موجود، دستههای موجود در این مجموعهداده تعیین میشود. در پردازش زبان طبیعی، تمام کلمات موجود در زبان، بهعنوان ویژگی در نظر گرفته میشوند. با این کار، الگوریتم به کلمات حساس خواهد شد. برای مثال اگر در توییتی کلمهی happy وجود داشته باشد، این توییت احتمالا توییتی با احساس خوشحال است. برای مشخص کردن مقدار ویژگی، میتوان از روشی به نام TF-IDF استفاده کرد که در آن، مقدار هر ویژگی به تعداد تکرار هر کلمه در هر توییت و معکوس تعداد تکرار این کلمه در تمام توییتها وابسته است؛ اما به دلیل سادهسازی، ما ارتباط معکوس با تعداد تکرار کلمه در تمام توییتها را حذف کردیم و تنها وابستگی، وابستگی مقدار به تعداد تکرار کلمه در آن توییت است. در مرحلهی آخر، باید الگوریتم انتخاب شود. ما الگوریتمهای MultiNomialNB و ComplementNB را روی مجموعهداده اجرا کردیم و با توجه به دقت 79.88 درصدی ComplementNB در مقابل دقت 61.33 درصدی MultiNomialNB، الگوریتم ComplementNB انتخاب شد.
اولین اجرای کامل برنامه
پس از تقسیم برنامه به چند ماژول و سپس توسعهی تمام ماژولها، برنامه بهصورت کامل اجرا شد؛ اما با اجرای برنامه متوجه چند مشکل شدیم. اولین مشکل این بود که نمیخواستیم سیستم خود را آلوده کنیم. بنابراین تصمیم گرفتیم به جای نصب ابزارها و برنامه به صورت مستقیم روی سیستم، چند ماشین مجازی روی سیستم نصب و پس از نصب ابزارها، برنامه اجرا شود. در آخر نیز پس از اتمام اجرای برنامه، این ماشینهای مجازی حذف شوند. البته نصب ماشین مجازی، نصب ابزارها، نصب برنامه و حذف ماشینهای مجازی به صورت دستی، امری زمانبر و تکراری بود. ما با توسعهی ماژولی به نام provisioning و با استفاده از ابزاری به نام انسیبل، توانستیم عملیات اجرای برنامه را کاملا خودکارسازی کنیم. پس از این امر، عملیات نصب که حدود ۱ ساعت زمان مصرف میکرد، به ۱۰ دقیقه کاهش پیدا کرد.
مشکل دیگری که با آن برخورد کردیم، مشخص کردن نمره به ازای هر احساس بود. برای مثال اگر ترامپ و احساس خوشحالی با هم باشند، باید به ترامپ چه نمرهای اختصاص داد. به همین دلیل، ما ماژول دیگری را به نام گزارشگر توسعه دادیم که تعداد توییتهای مربوط به هر کاندیدا و توییتهای مربوط به هر احساس را خروجی داده با استفاده از آن میتوان به ضرایب خوبی رسید.
آخرین مشکل در این اجرا، خروجی غیرقابل انتظار بود. ما پس از بررسی مجموعهداده دوم، به این نتیجه رسیدیم، که این مجموعهداده، توییتهای هرکاندیدا بوده و انتظار ما این بود که این توییتها، توییتهایی باشند که دربارهی هر کاندیدا ارسال شده است. بنابراین باید از یک مجموعهدادهی دیگر استفاده کنیم که چون مجموعهدادهی مناسبی که جواب نیاز ما را بدهد یافت نشد، ما تصمیم بر توسعهی یک خزشگر توییتر گرفتیم.
خزش در توییتر
حال که تصمیم به خزش در توییتر گرفتیم، باید استراتژی خزش را مشخص کنیم. برای این کار، ما از API جستجویی که خود توییتر فراهم میکند، استفاده و همزمان، زبان توییتهای حاصل را فیلتر میکنیم. برای اینکه یک توییت را چند مرتبه بررسی و ذخیره نکنیم، از ابزاری به نام ردیس استفاده کردیم. با توجه به محدودیتهای توییتر، ما هر ۵ ثانیه میتوانیم به توییتر درخواست جستجو بدهیم و توییتر در هر درخواست ما، ۱۵ توییت بازمیگرداند. از بین این ۱۵ توییت، به صورت میانگین، ۵ توییت را قبلا بررسی کردهایم. تمام این ۱۵ توییت به زبان انگلیسی هستند. پس ما در هر ۵ ثانیه، به صورت میانگین ۱۰ توییت ذخیره میکنیم.
پایش برنامه
هر برنامه، باید پایش شود تا از درحالکاربودن و عملکرد آن، اطمینان حاصل شده و در صورت وجود نقطات بحرانی، آنها شناسایی شده و بهبود یابند. لذا ما نیز برنامهی خود را پایش میکنیم. هر ماژول برنامه، تعداد توییتهایی را که از آن عبور کرده، ذخیره و با هر درخواست کاربر، این تعداد را اعلام میکند. حال ابزاری به نام پرومتئوس نصب شده است، که هر ۱۵ ثانیه یکبار، درخواست را به برنامه ارسال و نتیجه را ذخیره میکند. همچنین ابزاری به نام گرافانا نصب شده است که هر ۱۵ ثانیه یکبار به پرومتئوس درخواست ارسال کرده و خروجی را به صورت گراف نشان میدهد.
با استفاده از این روش، ما برنامه را پایش میکنیم. تمام اطلاعات داده شده در این گزارش بر مبنای این پایش نرمافزار است.
عکس بالا، مربوط به ساعت 20:17 روز 26 تیر 1399 است، که تا آن لحظه تعداد 327562 توییت پردازش شده بودند.
امن کردن سرورها
همانطور که دیدیم، از ابزارهای مختلفی در این برنامه استفاده شده است و نمیتوان از امن بودن تمام مولفههای آن اطمینان حاصل نمود. بهترین روش برای امن کردن سرورهایی که درچنین وضعی قراردارند، نصب یک فایروال برروی سرور نصب است. این فایروال اجازه دسترسی به سرور را تنها برای یک لیست خاص از آیپیها و پورتها صادر میکند.
ما نیز در این پروژه، برای امن کردن سرورهایمان از ابزاری به نام iptables استفاده کردیم. با این ابزار اجازهی دسترسی به سرورها، فقط از خود سرورها صادر شده است. تنها پورتهایی که برای عموم باز است، پورت 22 بوده که برای ssh است و امنیت آن تضمین شده و نیز پورت 3000 که برای گرافانا بوده و صرفا ابزاری برای نمایش است. بنابراین، هیچکس نمیتواند توییتها و دیگر اطلاعات موجود در سرورها را بخواند.
تعیین برندهی انتخابات 2020
ما ماژولهای گزارشگر و پیشبینیگر را روی 1115000 توییت اجرا کردیم. از بین این تعداد، 283849 توییت با احساس عصبانی، 532306 عدد با احساس ترسیده، 89312 عدد با احساس خوشحال، و 209533 عدد با احساس ناراحت بودند. همانطور که مشخص است، به دلیل موضوع سیاسی و همچنین ماهیت توییتر، تعداد توییتها با بار منفی، بسیار بیشتر از توییتها با بار مثبت است.
همچنین از بین این تعداد توییت، 555164 عدد دربارهی دونالد ترامپ و 559836 عدد دربارهی جو بایدن بود.
با توجه به تعداد بسیار زیاد توییتهای با بار منفی، پس باید ضریب اختصاص دادهشده برای احساس خوشحالی که تنها احساس با بار مثبت است، بسیار بیشتر از ضرایب دیگر احساسات باشد. ما دوبار ماژول پیشبینیگر را با دو گروه ضرایب مختلف اجرا کردیم.
در مرتبهی اول، برای خوشحال 10، و برای عصبانی -4 را اختصاص دادیم. در این حالت دونالد ترامپ با نمره 5058292 نسبت به نمره 3197202 جو بایدن، برنده شد.
در مرتبهی دوم، علاوه بر ضرایب مرتبهی اول، برای ناراحت -2 و برای ترسیده -1 را نیز اختصاص دادیم. در این حالت جو بایدن با نمره -7789532 نسبت به نمره -13117919 دونالد ترامپ، برنده شد.
حال باید با توجه به این دو اجرا با خروجی کاملا متفاوت، دربارهی برندهی انتخابات اظهارنظر کرد. نظر ما برنده شدن دونالد ترامپ است، چون رای، بیشتر بر مبنای احساس مثبت است. نظرشما چیست؟
کدهای پروژه
تمامی کدهای پروژه، در این لینک، موجود است. پذیرای Pull Requestهای شما هستیم. با تشکر از حسن توجه شما!
مطلبی دیگر از این انتشارات
Git workflow | Centralized workflow
مطلبی دیگر از این انتشارات
همه چیز درباره جاوا ( Java ):
مطلبی دیگر از این انتشارات
قسمت دوم Java Zone (ماشین مجازی جاوا)