تشخیص ناهنجاری بهشیوه غیرنظارتشده برای دادههای تکمتغیری و چندمتغیری
تشخیص ناهنجاری Anomaly Detection به فرایند تشخیص آیتمها یا رویدادهای غیرمنتظرهای اشاره دارد که با نُرم موجود در دیتاستها تفاوت دارند. تشخیص ناهنجاری اغلب روی دادههای بدون برچسب اجرا میشود و به همین دلیل با نام تشخیص ناهنجاری غیرنظارتشده شناخته میشود. تشخیص ناهنجاری دو پیشفرض دارد:
- دادهها بهندرت ناهنجاری دارند.
- ویژگی ناهنجاری با ویژگی نمونههای نرمال تفاوتی معنادار و چشمگیر دارد.
تشخیص ناهنجاری تکمتغیری
قبل از اینکه وارد مبحث تشخیص ناهنجاری چندمتغیری Multivariate anomaly detection شویم، بهتر است مثالی از روش تشخیص ناهنجاری تکمتغیری را با هم مرور کنیم. این روش فرایندی است که طی آن دادههای پرت را از داخل توزیعی که در یک فضای ویژگی واحد قرار دارد، تشخیص میدهیم.
در این مثال از دیتاست Super Store Sales استفاده میکنیم (برای دانلود این دیتاست به این لینک مراجعه کنید). هدف این است الگوهایی را در این دیتاست پیدا کنیم که از رفتار موردانتظار پیروی نمیکنند. بهعبارت دیگر، قصد داریم دادههای پرت هر کدام از متغیرها را بهصورت یکبهیک شناسایی کنیم.
import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns import matplotlib from sklearn.ensemble import IsolationForest
df = pd.read_excel("Superstore.xls") df['Sales'].describe()
توزیع متغیر «فروش»
plt.scatter(range(df.shape[0]), np.sort(df['Sales'].values)) plt.xlabel('index') plt.ylabel('Sales') plt.title("Sales distribution") sns.despine()
sns.distplot(df['Sales']) plt.title("Distribution of Sales") sns.despine()
print("Skewness: %f" % df['Sales'].skew()) print("Kurtosis: %f" % df['Sales'].kurt())
توزیع میزان فروش با توزیع نرمال تفاوت زیادی دارد؛ دنباله مثبت این توزیع، باریک و بلند است، عمده مقادیر در سمت چپ توزیع قرار گرفتهاند و طول دنباله آن در مقایسه با دنبالههای توزیع نرمال خیلی بیشتر است.
در ناحیه سمت راست این توزیع، مقدار احتمال دادهها کم است.
توزیع متغیر «سود»
df['Profit'].describe()
plt.scatter(range(df.shape[0]), np.sort(df['Profit'].values)) plt.xlabel('index') plt.ylabel('Profit') plt.title("Profit distribution") sns.despine()
sns.distplot(df['Profit']) plt.title("Distribution of Profit") sns.despine()
print("Skewness: %f" % df['Profit'].skew()) print("Kurtosis: %f" % df['Profit'].kurt())
توزیع متغیر سود، هم دنباله منفی دارد و هم دنباله مثبت. با این حال، دنباله مثبت از دنباله منفی طولانیتر است. توزیع به سمت مثبت کجی دارد؛ بنابراین گفته میشود توزیع دمبلند Heavily tailed است یا تعداد زیادی داده پرت دارد.
دو ناحیه در این توزیع وجود دارند که احتمال حضور دادهها در آنجا کم است: سمت راست توزیع و دیگری سمت چپ آن.
تشخیص ناهنجاری تکمتغیری در متغیر «فروش»
جنگل ایزوله الگوریتمی است که برای تشخیص مقادیر پرت به کار میرود؛ خروجی این الگوریتم نمره ناهنجاری هر کدام از نمونههاست. الگوریتم جنگل ایزوله مبتنی بر این واقعیت است که ناهنجاریها، نمونههایی هستند که از نُرم متفاوت بوده و تعداد کمی دارند. جنگل ایزوله مدلی مبتنی بر درخت تصمیم است. در مدلهای مبتنی بر درخت، برای قسمتبندی نمونهها، ابتدا یک ویژگی تصادفی انتخاب شده و سپس از بین مقدار بیشینه و کمینه این ویژگی، یک مقدار Split تصادفی انتخاب میشود.
در این قسمت، رفتار جنگل ایزوله را در مثال دیتاست Superstore مرور میکنیم. این الگوریتم در کتابخانه sklearn پیادهسازی شده است (قسمت عمده کد از این دوره آموزشی گرفته شده است):
- آموزش الگوریتم جنگل ایزوله با استفاده از دادههای فروش
- ذخیره دادههای فروش در آرایه NumPy برای استفاده در گامهای بعدی
- محاسبه نمره ناهنجاری برای هر کدام از مشاهدات (نمونهها)؛ برای محاسبه مقدار ناهنجاری نمونههای ورودی، میانگین نمره ناهنجاری درختهای موجود در جنگل گرفته میشود.
- ردهبندی مشاهدات در دو کلاس پرت یا غیرپرت
- به تصویر کشیدن نواحی مربوط به دادههای پرت
بر اساس یافتهها و تصویر بالا، به نظر میرسد در متغیر فروش، مقداری که بیشتر از 1000 باشد را میتوان داده پرت در نظر گرفت.
مطالعه بصری یک نمونه ناهنجاری
df.iloc[10]
این نمونه خرید غیرعادی به نظر نمیرسد؛ اما در مقایسه با سایر سفارشات موجود در دیتاست، مقدار بیشتری دارد.
تشخیص ناهنجاری تکمتغیری در متغیر «سود»
- آموزش الگوریتم جنگل ایزوله روی دادههای متغیر سود
- ذخیره متغیر سود در آرایه NumPy برای استفاده در گامهای بعدی
- محاسبه نمره ناهنجاری برای مشاهدات؛ مقدار ناهنجاری هر نمونه ورودی از طریق محاسبه میانگین نمرات ناهنجاری درختهای حاضر در جنگل به دست میآید.
- ردهبندی مشاهدات در دو کلاس پرت یا غیرپرت
- به تصویر کشیدن نواحی مربوط به دادههای پرت
مطالعه بصری برخی از ناهنجاریها
با توجه به نتایج و تصویر بالا، به نظر میرسد مقدار سود زیر 100- یا بالاتر از 100 را میتوان داده پرت در نظر گرفت. حال میخواهیم با استفاده از چند مثال، ببینیم تشخیص مدل معنادار بوده است یا خیر:
df.iloc[3]
بدیهی است هر داده منفی برای متغیر سود، ناهنجاری به شمار میرود و باید بیشتر مورد مطالعه و بررسی قرار گیرد.
df.iloc[1]
طبق تشخیص مدل، این سفارش (نمونه) که سود زیادی داشته، یک ناهنجاری است. با این حال، شاید با بررسی بیشتر بفهمیم این سفارش مربوط به محصولی بوده که حاشیه فروش بیشتری داشته است.
دو تصویر بالا نمرات ناهنجاری و نواحی مربوط به دادههای پرت را نشان دادند. همانطور که پیشتر توضیح داده شد، نمرات ناهنجاری، شکل زیربنایی توزیع را تعیین میکنند و نواحی مربوط به مقادیر پرت، مناطقی از توزیع هستند که احتمال وجود نمونه در آنها پایین است.
با این حال، تجزیه و تحلیل تکمتغیری تنها همین میزان اطلاعات در اختیار ما قرار میدهد. حتی با بررسی بیشتر ممکن است بفهمیم برخی از ناهنجاریهایی که مدل تشخیص داده است، صحیح نیستند. اگر با دادههای چندبُعدی (و نه تکمتغیری) سروکار داشته باشیم، رویکردی که برای تشخیص ناهنجاری در پیش میگیریم از نظر محاسباتی سنگینتر و از لحاظ ریاضیاتی پیچیدهتر میشود.
تشخیص ناهنجاری چندمتغیری
با توجه به پیچیدگی دنیای واقعی، بیشتر تحلیلهایی که باید انجام دهیم از نوع چندمتغیری هستند. در تشخیص ناهنجاری چندمتغیری، مقادیری پرت تشخیص داده میشوند که حداقل در دو متغیر، مقداری غیرطبیعی داشته باشند.
اینجا قصد داریم با استفاده از متغیرهای فروش و سود، روش تشخیص ناهنجاری چندمتغیری غیرنظارتشده را روی چند مدل مختلف امتحان کنیم.
بدین منظور از کتابخانه PyOD استفاده میکنیم؛ یکی از کتابخانه پایتون که برای تشخیص ناهنجاری در دادههای چندمتغیری کاربرد دارد. این کتابخانه را یو ژاو ساخته است.
فروش و سود
در دنیای کسبوکار، انتظار میرود میزان فروش و سود با یکدیگر همبستگی مثبت داشته باشند. بر همین اساس، اگر برخی از نمونههای فروش و نمونههای سود همبستگی مثبت نداشته باشند، بهعنوان داده پرت در نظر گرفته میشوند و مورد بررسی بیشتر قرار میگیرند.
sns.regplot(x="Sales", y="Profit", data=df) sns.despine();
از نمودار همبستگی بالا میتوان دریافت که برخی از نمونهها به وضوح پرت هستند و بسیار بالاتر یا پایینتر از مقادیر نرمال قرار دارند.
عامل نقاط پرت محلی بر مبنای خوشهبندی (CBLOF)
در این روش، نمره پرت بودن بر اساس عامل CBLOF محاسبه میشود. برای محاسبه نمره ناهنجاری، فاصله هر نمونه از مرکز خوشهاش بر تعداد نمونههای موجود در آن خوشه تقسیم میشود. پیادهسازی CBLOF در کتابخانه PyOD موجود است.
قسمتی از کد پایین از دوره آموزشی PyOD گرفته شده و برای درک بهتر مخاطبین با مطالب این مقاله ترکیب شده است. گامهای فرایند را میتوان بدین ترتیب برشمرد:
- مقیاسبندی مقادیر فروش و سود بین صفر تا یک
- تنظیم دلخواه نسبت پرت بودن به عنوان 1 درصد (بر اساس حدس و آزمایش)
- برازش دادهها روی مدل CBLOF و پیشبینی نتایج
- استفاده از مقدار آستانه برای تشخیص پرت یا غیرپرت بودن یک نمونه
- استفاده از تابع تصمیم برای محاسبه نمره ناهنجاری برای هر نمونه
شناسایی نقاط پرت بر مبنای هیستوگرام (HBOS)
تکنیک HBOS، با فرض استقلال ویژگیها، میزان ناهنجاری را از طریق ساخت هیستوگرام محاسبه میکند. برای تشخیص ناهنجاری چندمتغیری میتوان هیستوگرام مربوط به هر ویژگی را جداگانه محاسبه کرد، به صورت جداگانه امتیاز داد و در پایان با هم ترکیب کرد. . اگر از کتابخانه PyOD استفاده میکنید، کد این مدل شباهت زیادی به کد CBLOF خواهد داشت.
جنگل ایزوله
جنگل ایزوله در اصل شبیه به جنگل تصادفی است و بر مبنای درختهای تصمیم ساخته میشود. در واقع جنگل ایزوله، با انتخاب یک ویژگی تصادفی، مشاهدات را ایزوله کرده و سپس بهصورت تصادفی یک مقدار بین کمینه و بیشینه آن ویژگی انتخاب میکند تا دادگان موجود در آن ویژگی به دو قسمت تقسیم شود. ماژول جنگل ایزوله PyOD یک تابع wrapper برای جنگل ایزوله sickit-learn است که توابع بیشتری را قبول میکند.
همسایه نزدیک (KNN)
KNN یکی از سادهترین روشهای تشخیص ناهنجاری است. فاصله هر نمونه تا kمین همسایه نزدیکش را میتوان بهعنوان نمره پرت بودن آن نمونه در نظر گرفت.
همانطور که مشاهده کردید، ناهنجاریهایی که از طریق چهار الگوریتم بالا پیشبینی شدهاند، تفاوت چندانی با هم ندارند.
مطالعه بصری چند نمونه از ناهنجاریها
در این قسمت میخواهیم چند نمونه از مقادیر پرتی را که از طریق مدل تعیین شدهاند، با جزئیات بیشتر بررسی کنیم. بهعنوان مثال، بر خروجیهای الگوریتم KNN تمرکز میکنیم، تا بفهمیم چرا بهعنوان ناهنجاری شناسایی شدهاند.
df.iloc[1995]
برای این سفارش، یک مشتری 5 محصول را با جمع قیمت 294.62 دلار و سود کمتر از 766- (یعنی با 80 درصد تخفیف) خریداری کرده است. باید زیان هر کدام از محصولات فروختهشده را محاسبه کنیم.
df.iloc[9649]
به نظر میرسد سود 4.7 درصدی این خرید خیلی کوچک است و به همین دلیل مدل این سفارش را بهعنوان ناهنجاری در نظر گرفته است.
df.iloc[9270]
برای سفارش بالا، مشتری 6 محصول با قیمت کل 4305 دلار خریداری کرده و 20 درصد تخفیف دریافت کرده است؛ با این حال، سود بهدستآمده همچنان 33 درصد است. بنابراین مدل این نمونه را هم بهعنوان ناهنجاری تشخیص داده است.
نوتبوک Jupyter برای تحلیل بالا را میتوانید روی این لینک Github مشاهده کنید.