ترافیک شهری در اسنپ - بخش یک - چالش‌های پایپ‌لاین محصول مبتنی بر یادگیری ماشین

Photo by Payton Mcdonald on Unsplash
Photo by Payton Mcdonald on Unsplash


چکیده

یادگیری‌ماشین یکی از موضوعات داغ این روزهای فضای صنعت در جهان است و شرکت‌های مختلف برای حل بسیاری از مسائل خود از آن استفاده می‌کنند. در کشور ما، مسائل مرتبط با فضای یادگیری‌ماشین بیشتر در فضای آکادمیک دنبال می‌شد. اما این روزها با توجه به توسعه و رشد شرکت‌ها، حجم داده‌هایی که تولید و نگه‌داری می‌شوند نیز افزایش پیداکرده‌ است. در نتیجه این فرصت برای ما نیز فراهم‌ شده‌ است که با استفاده از یادگیری‌ماشین به برخی از مسائل پاسخ بدهیم.

پس از آن که شرکت‌ها برای حل مسائل خود به روش‌های یادگیری‌ماشین روی‌ آوردند، با چالش‌هایی مواجه شدند که جنس آن‌ها با چالش‌ها در فضای آکادمیک متفاوت بود. از فرایند یادگیری‌ماشین به عنوان فرایندی که منجر به تولید یک محصول می‌شود می‌توان به عنوان مهمترین چالش افراد فعال در این حوزه اشاره‌کرد. به همین دلیل موضوعات و مفاهیمی مانند MLOps و DataOps ظهور پیداکرده‌ اند و به دنبال این مفاهیم ابزارهایی برای تحقق بخشیدن به آن‌ها ایجاد‌ شده‌ است.

ما قصد داریم در قالب یک سری مقالات بیان کنیم که با چه مشکلاتی در تیم ترافیک نقشه اسنپ روبرو هستیم و چه راه‌حل‌هایی برای آن‌ها انتخاب کرده ایم.

در مقاله زیر نیز تصویر کلی از معماری محاسبه ترافیک در تیم ترافیک نقشه اسنپ معرفی شده است که می‌توانید به آن مراجعه کنید.

https://engineering.snapp.ir/%D8%AA%D8%B1%D8%A7%D9%81%DB%8C%DA%A9-%D8%B4%D9%87%D8%B1%DB%8C-%D8%AF%D8%B1-%D8%A7%D8%B3%D9%86%D9%BE-%D8%A8%D8%AE%D8%B4-%D8%B5%D9%81%D8%B1-x6u0qxmf2qec

مقدمه

یکی از وظایف تیم ترافیک محاسبه تخمین مدت زمان سفر (ETA)، برای راننده‌ها پیش از هر سفر است. به همین دلیل نیاز داریم که در ابتدا تخمینی از وضعیت ترافیکی حال حاضر شهرها را داشته‌ باشیم و در ادامه با استفاده از آن، پیش‌بینی کنیم که در آینده وضعیت ترافیکی به چه شکلی خواهد بود. سپس با استفاده از اطلاعات و مشخصات مبدا و مقصد سفر، تخمینی از مدت زمان سفر را محاسبه‌ کنیم. در نتیجه مسئله تخمین مدت زمان سفر به شکل زیر تقسیم خواهد شد:

  • محاسبه‌ی وضعیت فعلی ترافیک
  • پیش‌بینی وضعیت ترافیک در آینده

برای حل کردن این دو مسئله، که خود نیز به مسائل دیگری شکسته می‌شوند، از روش‌های یادگیری‌ماشین استفاده‌ می‌کنیم. در این مقاله از مسئله ETA به عنوان یک Case-Study استفاده‌ می‌کنیم و با نگاهی Blackbox به یک مدل یادگیری‌ماشین، بیان می‌کنیم چه چالش‌هایی بر سر راه تولید این نوع محصول وجود خواهد داشت و راه‌حل‌ها و ابزارهای خود را تشریح خواهیم کرد.

چالش‌های تعریف مساله

فرض کنید می‌خواهیم سیستمی طراحی‌کنیم تا با کمک آن بتوانیم به مساله پیش‌بینی وضعیت ترافیک در ساعت‌های آینده پاسخ دهیم. در همین ابتدا با پرسش‌های زیر روبرو خواهیم بود:

  • چگونه این مساله را با استفاده از روش‌های یادگیری‌ماشین می‌توانیم حل کنیم؟
  • برای ایجاد مدل از چه داده‌ها و با چه ویژگی‌هایی استفاده کنیم؟
  • آیا داده‌های ما از کیفیت مناسب برخوردار هستند؟
  • چگونه می‌توانیم کیفیت عملکرد سیستم خود را با استفاده از مدل ساخته شده ارزیابی‌کنیم؟
  • چگونه می‌توانیم مشکلات سیستم را متوجه شویم و خطاهای آن را استخراج‌کنیم؟
  • کارهایی را که از سوالات بالا استخراج می‌شود چگونه اولویت‌بندی کنیم؟

این پرسش‌ها بخشی از سوالاتی است که در فرایند تولید یک محصول مبتنی بر یادگیری‌ماشین در ذهن ایجاد می‌شود. در ادامه ما با استفاده از چارچوب محاسبه ترافیک مرتبط با ETA به این سوالات پاسخ خواهیم داد.

در مساله پیش‌بینی ترافیک، برای اینکه بتوانیم از یک روش یادگیری‌ماشین استفاده کنیم، نیازمند به یک پایپ‌لاین محاسباتی هستیم. وظیفه‌ی این پایپ‌لاین این است که داده، یا به عبارتی Feature-Vector مورد نیاز مدل مارا ساخته و به عنوان ورودی به مدل ما بدهد. شکل زیر تصویری ساده‌سازی شده از معماری این سیستم است.

تصویر ۱: معماری اولیه محاسبه ترافیک
تصویر ۱: معماری اولیه محاسبه ترافیک

تصویر بالا نشان می‌دهد که GPSها از طریق کلاینت (اپلیکیشن راننده) به سمت سرور فرستاده می‌شوند و داخل یک صف قرار می‌گیرند. در ادامه سرویس Traffic-Estimator داده‌ی GPS‌ها را Consume کرده و ترافیک فعلی را محاسبه می‌کند. در گام بعدی سرویس ‌Traffic-Predictor با استفاده از داده‌های ترافیک حال و زمان گذشته (Feature-Vector) به عنوان ورودی، ترافیک آینده را پیش‌بینی خواهد کرد.

در بخش زیر چالش‌هایی را که در ابتدای راه برای راه‌اندازی این سیستم با آن روبرو بودیم، تشریح خواهیم کرد.

چالش‌های پایپ‌لاین

نگهداری داده‌های حجیم: یکی از چالش‌هایی که در ابتدای امر با آن روبرو هستیم پردازش کردن حجم زیادی از داده‌های GPS راننده‌ها است. به عنوان مثال اگر تصور کنیم ۱میلیون راننده در حال سفر داشته باشیم و سیستم ما به گونه‌ای باشد که هر ۱۰ ثانیه، اپلیکیشن موقعیت راننده را به سمت سرور ارسال کند، باید ۶ میلیون درخواست در دقیقه را بتوانیم پردازش کنیم. به عبارت دیگر ۱۰۰k rps، که عدد بسیار بزرگی خواهد بود. به همین دلیل نیازمند ابزارهای BigData هستیم. از این رو، در قدم اول تصمیم گرفتیم GPS‌ها را در Apache Kafka نگه‌داری کنیم تا سرویس‌های مختلفی که نیازمند این داده هستند نیز بتوانند از آن‌ها استفاده کنند. همچنین به دلیل نرخ بالای ایجاد داده GPS، عملکرد سرویس‌ها دچار مشکل نشود.

تصویر ۲: آپاچی کافکا، پلتفرم توزیع شده‌ در پردازش استریم
تصویر ۲: آپاچی کافکا، پلتفرم توزیع شده‌ در پردازش استریم

کیفیت داده: یکی دیگر از چالش‌هایی که با آن روبرو هستیم کیفیت داده است. از آن جا که دستگاه GPS گوشی‌های هوشمند دارای خطا هستند و همچنین در برخی از نقاط شهر مثل مناطقی که دارای ساختمان‌های بلند و یا مناطقی که دارای ملاحظات امنیتی هستند، عملکرد GPS گوشی ها با اختلالاتی مواجه می‌شود. این اختلالات باعث ایجاد نویز در گزارش موقعیت راننده‌ها می‌شود. این اختلالات باعث ایجاد خطا ( ۱ تا بیش از ۱۰ها متر ) و یا مخدوش شدن در گزارش موقعیت راننده به سمت سرور می‌شود. برای حل این مشکل از الگوریتم‌های Hidden Markov استفاده می‌کنیم تا بتوانیم خطاهای ناشی از GPSهای گزارش شده را تا حدی کاهش دهیم

از طرف دیگر مناطقی از شهر وجود دارد که میزان کافی از GPSها در برخی ساعت‌های شبانه روز گزارش نمی‌شود به همین دلیل در این نقاط ما دچار مشکل Missing در داده خواهیم بود. برای حل این مشکل نیز از روش‌های Imputation استفاده می‌کنیم که پرداختن به جزئیات این موضوعات خارج از چارچوب این مقاله خواهد بود. برای آشنایی با بخشی از این روش‌ می‌توانید به مقاله زیر مراجعه کنید:

https://engineering.snapp.ir/usage-of-matrix-factorization-to-predict-offline-speeds-at-snapp-aqszq458uajh
تصویر۳:  کاوریج ترافیک محاسبه شده در بخشی از شهر تهران، با استفاده از سیستم محاسبه ترافیک تیم نقشه اسنپ، برای مدت زمان ۱۵ دقیقه، با استفاده از GPSهای ارسال شده‌ی رانندگان در حال سفر.
تصویر۳: کاوریج ترافیک محاسبه شده در بخشی از شهر تهران، با استفاده از سیستم محاسبه ترافیک تیم نقشه اسنپ، برای مدت زمان ۱۵ دقیقه، با استفاده از GPSهای ارسال شده‌ی رانندگان در حال سفر.


پردازش داده‌های حجیم: برای محاسبه ترافیک نیازمندیم که بتوانیم سرعت ماشین‌هایی که از خیابان‌ها عبور می‌کنند محاسبه کنیم و به هر خیابان سرعتی بر اساس سرعت راننده‌های در حال عبور از آن را نسبت دهیم. به عبارت دیگر این سرعت بیانگر این خواهد بود که به طور متوسط یک راننده با چه سرعتی، در زمان مشخص می‌تواند از خیابان مورد نظر عبور کند. به عنوان مثال در اتوبان همت یک راننده در ساعت ۲۱ شب با سرعت متوسط ۷۰ کیلومتر بر ساعت می‌تواند عبور کند. با فرض اینکه از فرمول زیر برای محاسبه سرعت بخواهیم استفاده کنیم،

Median-Speed = Median(Distance(GPS2, GPS1) / (T2-T1))

باید بتوانیم این پردازش را در زمانی مشخص انجام دهیم. یعنی اگر ما بخواهیم سرعت متوسط در بازه زمانی ۵دقیقه اخیر را به عنوان سرعت ترافیک فعلی حساب کنیم زمان محاسبه‌ی این پردازش نباید بیش از ۵دقیقه طول بکشد. زیرا این کار هر ۵دقیقه باید به شکل متناوب انجام شود و اگر زمان محاسبه یکی از این batch jobها بیشتر از این مدت زمان طول بکشد داده ارزش زمانی خود را از دست داده و همچنین با batch job زمان بعدی دچار تداخل خواهد شد. در نتیجه نیازمند پلتفرمی هستیم که بتواند این قدرت پردازشی را به ما بدهد و همچنین scalable باشد. برای دستیابی به این امر، به سراغ روش‌های پردازش توزیع شده رفته ایم. یکی از شناخته‌ شده‌ ترین تکنولوژی‌ها در این حوزه Apache Spark است که قابلیت پردازش داده به شکل Stream و ‌Batch را به ما می‌دهد.

تصویر۴: با اتصال اسپارک به سورس‌های مختلف از آن به عنوان پلتفرم پردازش داده‌های بچ و استریم استفاده می‌کنیم.
تصویر۴: با اتصال اسپارک به سورس‌های مختلف از آن به عنوان پلتفرم پردازش داده‌های بچ و استریم استفاده می‌کنیم.


نرخ بالا در نوشتن داده‌های پردازش شده: بعد از محاسبه‌ی داده‌ی ترافیکی و پیش‌بینی ترافیک برای آینده، نیاز داریم که این داده را به منظور اهدافی مانند مانیتورینگ، تحلیل داده و آموزش مدل یادگیری ذخیره سازی کنیم. به همین دلیل نیازمند به دیتابیسی هستیم که سرعت نوشتن روی آن بالا بوده تا در برابر نرخ بالای نوشتن تحمل پذیری بالایی داشته باشد. به همین خاطر باید دیتابیس خود را به شکل کلاستر نگه‌داری کنیم و همچنین داده‌ها را به شکل رپلیکیت شده روی آن ذخیره کنیم تا در صورت خرابی بتوان همچنان به سیستم اتکا کرد و اگر یکی از نودهای دیتابیس دچار خرابی شد فرایند محاسبه ترافیک دچار مشکل نشود و همچنین بعد از برطرف شدن مشکل سیستم خودش بتواند این داده را روی نود فیکس شده دوباره لود کند. اصطلاحا دیتابیسی با قابلیت Relaiblity، Fault tolerancy و Self-Healing مناسب نیاز داشتیم. برای دستیابی به این دیتابیس سراغ Apache‌ Cassandra رفتیم که علی رغم پیچیدگی‌هایی که از لحاظ نگهداری و طراحی مناسب به سیستم اضافه می‌کرد، کمک شایانی برای پشت سر گذاشتن این چالش کرد.

تصویر ۵: کلاستر آپاچی کاساندرا که به عنوان دیتابیس نگه‌دارنده داده‌های ترافیکی استفاده می‌شود
تصویر ۵: کلاستر آپاچی کاساندرا که به عنوان دیتابیس نگه‌دارنده داده‌های ترافیکی استفاده می‌شود

دیتابیس Apache Cassandra یکی از دیتابیس‌های NoSQL از خانواده‌ی دیتابیس‌های Column-Family است که قابلیت استقرار Distributed را داراست و این اطمینان را به شما به عنوان یک معمار نرم افزار می‌دهد که در صورت بالا رفتن نرخ داده، می‌توان دیتابیس را به راحتی با اضافه کردن نود، Scale out کرد. از نکاتی که در طراحی داده مدل این دیتابیس باید به آن توجه داشت این است که جدول‌ها در این دیتابیس بر اساس Query‌هایی که نیاز داریم طراحی می‌شود. به بیان دیگر در این دیتابیس ما نتایج کوئری‌هایی که نیاز داریم در قالب دیتا در جدول‌ها ذخیره می‌کنیم و با مدل Entity-Relationship که در دیتابیس‌های Relational با آن آشنا هستیم قدری متفاوت است. از این رو، طراحی جدول‌ها در این دیتابیس Query-Based است در حالیکه در دیتابیس‌های Relational طراحی جدول‌ها Entity-Based است. همچنین نکته‌ی دیگری که باید در هنگام طراحی این دیتابیس توجه داشته باشیم این است که طوری تنظیمات مربوط به partitioning داده را انجام دهیم که داده‌‌ با Partition-Size‌های همسان در نودها توزیع شود. این موضوع باعث می‌شود که عملیات روی دیتابیس با سرعت بهتری انجام شود. طراحی بهینه‌ی ساختار داده‌ای یکی دیگر از چالش‌های ما در طراحی این سیستم بود. زیرا ما نیاز داشتیم که داده را با سرعت بالایی روی دیتابیس بنویسیم و هم به منظور استفاده در پیش‌بینی ترافیک که به داده‌ی گذشته ترافیکی نیاز داشت، با سرعت بالایی بخوانیم تا زمان اجرای پایپ‌لاین بیش از حد مجاز طول نکشد.

مدل ‌پیش‌بینی: به عنوان اولین مدل، به دنبال روشی بودیم که به عنوان Baseline کار خود قرار دهیم و بتوانیم روش‌های پیچیده‌تر را به کمک آن مقایسه و ارزیابی کنیم. به همین دلیل مدل AutoRegression را به عنوان اولین مدل پیش‌بینی خود انتخاب کردیم و با استفاده از آن و Feature-Vectorی که درایه‌های آن ترافیک فعلی و ترافیک در یک ساعت گذشته‌ هر خیابان است، برای یک ساعت آینده هر خیابان، ترافیک را پیش‌بینی کردیم. به بیان دیگر برای پیش‌بینی ترافیک آینده، از میانگین وزن‌دار ترافیک حال و گذشته‌ی هر خیابان بهره بردیم.

تا این‌جا بیان کردیم که برای راه‌اندازی یک سیستم محاسبه و پیش‌بینی ترافیک با یک مدل و فرمول اصطلاحا Baseline با چه چالش‌هایی روبرو بودیم. اما این پایان کار برای سیستم ما نبود و بعد از لانچ این سیستم ما با چالش‌هایی روبرو شدیم که در ادامه آن‌ها را برای شما بازگو خواهیم کرد.

چالش‌های سیستم Baseline

وقتی یک سیستم Baseline را لانچ می‌کنیم با چالش‌های جدیدی روبرو می‌شویم. فرض کنید مدل یادگیری‌ماشینی train کرده ایم و در آزمایشات به دقت خوبی رسیده ایم و آن را دیپلوی کرده ایم.در ادامه چالش‌هایی که ما با آن روبرو شدیم را ذکر خواهیم کرد:

  • چگونه باید متوجه بشویم که آیا عملکرد سیستم خوب است یا خیر. آیا سرعت‌های محاسبه شده برای خیابان‌ها، که نشانگر میزان ترافیک آن خیابان‌ها است، به اندازه کافی دقت دارد؟ در اولین قدم دریافتیم که دچار مشکل عدم وجود یک Ground-Truth برای مقایسه عملکرد خود با آن هستیم.
  • چه تعریفی برای عملکرد خوب داشته باشیم. اگر به لحاظ متریک‌های یادگیری‌ماشین مدل دقت خوبی داشته باشد، آیا کافی است؟ چگونه تاثیر کار خود را بر روی متریک‌های بیزینسی اندازه‌گیری کنیم؟
  • اگر به طریقی متوجه شدیم که عملکرد ما نیاز به بهبود دارد، حال کدام بخش از این پایپ‌لاین را باید اصلاح کنیم؟ بخشی که خطاهای GPS‌ها را برطرف می‌کند؟ بخشی که سرعت ماشین‌ها را محاسبه می‌کند و یا بخشی که پیش‌بینی ترافیک را دارد؟ به بیان دیگر Troubleshooting یکی دیگر از چالش‌های موجود در این سیستم و سیستم‌های مبتنی بر یادگیری‌ماشین است.
  • اگر دریافتیم که مدل پیش‌بینی ترافیک ما باعث پایین آمدن دقت سیستم شده و می‌خواهیم مدل ساده پیش‌بینی ترافیک خود را بهبود دهیم و از مدل بهتری استفاده کنیم، مدل جدید با توجه به ساختار فعلی ما با چه مشکلات و محدودیت‌هایی روبرو خواهد بود؟ آیا با توجه به نیازها مجبور می‌شویم تا بخش یا کل پایپ‌لاین خود را تغییر دهیم؟ این تغییرات چه میزان هزینه برای ما خواهد داشت؟
  • چگونه از آسیب دوباره‌کاری در مرحله تحلیل داده‌ها و انجام آزمایشات برای پیدا کردن مدل بهینه در امان بمانیم؟ با توجه به این موضوع که تعداد آزمایش‌ها به خودی خود بسیار زیاد است چگونه نتایج آزمایش‌ها و مدل‌های ساخته شده‌ی قبلی را مدیریت کنیم تا بتوانیم به آن‌ها رجوع کنیم و یا با رفتن اعضای تیم و آمدن اعضای جدید دچار چالش کمتری شویم.
  • داده‌ی مورد نیاز برای آموزش مدل‌ها را چگونه جمع‌آوری کنیم که به شکل دستی نباشد و این داده‌ها را در مرحله‌ی آنلاین چگونه فراهم کنیم تا مدل بتواند عمل پیش‌بینی را انجام دهد و زمان اجرا نیز از حد مجاز بیشتر نشود.
  • چگونه مدل Candidate را قبل از دیپلوی روی محیط پروداکشن بتوانیم تست کنیم که آیا به اندازه کافی عملکرد خوبی دارد یا خیر؟

جمع‌بندی

بعد از مطالعات متعدد دریافتیم که برای روبرو شدن با چالش‌های فوق که برخی از آن‌ها راه حل قطعی نیز ندارند نیازمند این هستیم که باید نگاه enterprise به ساختار و فرایندهای تولید محصول مبتنی بر یادگیری‌ماشین داشته باشیم و با به‌کارگیری Best-Practice‌هایی مانند MLOPs و DataOps و استفاده از ابزارهایی که باعث می‌شود بتوانیم این practice‌ها را به کار ببریم، بتوانیم انعطاف‌پذیری قابل قبولی در برابر چالش‌های مطرح شده داشته باشیم. این موضوع باعث می‌شود بتوانیم ۰ تا ۱۰۰ فرایند تولید یک مدل پیش‌بینی را به شکل یک Workflow با انعطاف پذیری قابل قبول داشته باشیم.

در مقالات آینده تلاش می‌کنیم نقش تکنولوژی‌هایی را که در این مسیر به ما کمک کرده‌‌اند برای شما بیان کنیم.

در انتها، اگر احساس می‌کنید به پروژه‌هایی شبیه به پروژه‌ی بالا علاقه‌مندید و تمایل دارید به تیم ما ملحق شوید، خوشحال می‌شویم که رزومه‌های خود را از طریق آدرس engineering@snapp.cab برای ما ارسال کنید.