- 原文地址:Time Series of Price Anomaly Detection
- 原文做者:Susan Li
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:kasheemlew
- 校對者:xionglong58,portandbridge
異常檢測是指檢測數據集中不遵循其餘數據的統計規律的數據點html
異常檢測,也叫離羣點檢測,是數據挖掘中肯定異常類型和異常出現的相關細節的過程。現在,自動化異常檢測相當重要,由於如今的數據量太龐大了,人工標記已經不可能實現了。自動異常檢測有着普遍的應用,例如反欺詐、系統監控、錯誤檢測、傳感器網絡中的事件檢測等等。前端
但我將對酒店房費進行異常檢測,緣由提及來有點自私。python
不知道你是否有過這樣的經歷,好比,你按期到某地出差,每次下榻同一個酒店。一般狀況下,房費的波動都不大。可是有些時候,即使仍是同一個酒店的同一個房型都貴得嚇人。因爲出差補貼的限制,這時你就只能選擇換一家酒店了。被坑了好幾回以後,我開始考慮建立一個模型來自動檢測這種價格異常。android
固然,有些反常狀況你一生只會遇到一次,咱們能夠提早知道,後面幾年應該不會在同一時間再次碰上。好比 2019 年 2 月 2 日至 2 月 4 日亞特蘭大驚人的房費。ios
在這篇文章中,我會嘗試不一樣的異常檢測技術,使用無監督學習對時間序列的酒店房費進行異常檢測。下面咱們開始吧!git
獲取數據的過程很艱難,我只拿到了一些不夠完美的數據。github
咱們要使用的數據是 Expedia 個性化酒店搜索數據的子集,點這裏獲取數據集。算法
咱們將從 training.csv 中分割出一個子集:後端
property_id = 104517
。price_usd
列。 這樣作是由於各個國家在顯示稅費和房費上有不一樣的習慣,這個房費多是每晚的費用也多是總計的費用,但咱們知道美國的酒店顯示的就是每晚不含稅費的價格。search_room_count = 1
.date_time
、price_usd
、srch_booking_window
和 srch_saturday_night_bool
。expedia = pd.read_csv('expedia_train.csv')
df = expedia.loc[expedia['prop_id'] == 104517]
df = df.loc[df['srch_room_count'] == 1]
df = df.loc[df['visitor_location_country_id'] == 219]
df = df[['date_time', 'price_usd', 'srch_booking_window', 'srch_saturday_night_bool']]
複製代碼
完成分割以後就能獲得咱們要使用的數據了:網絡
df.info()
複製代碼
df['price_usd'].describe()
複製代碼
如今咱們發現了一個嚴重的異常,price_usd 的最大值居然是 5584。
若是一個單獨的數據項與其餘數據相比有些反常的話,咱們就稱它爲單點異常(例如鉅額交易)。咱們能夠檢查日誌,看看究竟是怎麼回事。通過一番調查,我以爲多是數據錯誤,或者是某個用戶無心間搜了一下總統套房,可是並無預約或者瀏覽。爲了發現更多比較輕微的異常,我決定刪掉這條數據。
expedia.loc[(expedia['price_usd'] == 5584) & (expedia['visitor_location_country_id'] == 219)]
複製代碼
df = df.loc[df['price_usd'] < 5584]
複製代碼
看到這裏,你必定已經發現咱們漏掉了些條件,咱們不知道用戶搜索的房型,標準間的價格但是和海景大牀房的價格截然不同的。爲了證實,請記住這一點。好了,該繼續了。
df.plot(x='date_time', y='price_usd', figsize=(12,6))
plt.xlabel('Date time')
plt.ylabel('Price in USD')
plt.title('Time Series of room price by date time of search');
複製代碼
a = df.loc[df['srch_saturday_night_bool'] == 0, 'price_usd']
b = df.loc[df['srch_saturday_night_bool'] == 1, 'price_usd']
plt.figure(figsize=(10, 6))
plt.hist(a, bins = 50, alpha=0.5, label='Search Non-Sat Night')
plt.hist(b, bins = 50, alpha=0.5, label='Search Sat Night')
plt.legend(loc='upper right')
plt.xlabel('Price')
plt.ylabel('Count')
plt.show();
複製代碼
總的來講,搜索非週六的晚上獲得的價格更加穩定和低廉,搜索週六晚上獲得的價格明顯上升。看來這家酒店週末的時候比較受歡迎。
k-平均是一個應用普遍的聚類算法。它建立 ‘k’ 個類似數據點簇。在這些聚類以外的數據項可能被標記爲異常。在咱們開始用 k-平均聚類以前,咱們使用肘部法則來肯定最優簇數。
從上圖的肘部曲線來看,咱們發現圖像在 10 個簇以後逐漸水平,也就是說增長更多的簇並不能解釋相關變量更多的方差;這個例子中的相關變量是 price_usd
。
咱們設置 n_clusters=10
,使用 k-平均輸出的數據繪製 3D 的簇。
如今咱們得搞清楚要保留幾個成分(特徵)。
咱們能夠看到,第一個成分解釋瞭解釋了幾乎 50% 的方差,第二個成分解釋了超過 30% 的方差。然而,咱們應該注意,沒有哪個成分是能夠忽略不計的。前兩個成分包含了超過 80% 的信息,因此咱們設置 n_components=2
。
基於聚類的異常檢測中強調的假設是咱們對數據聚類,正常的數據歸屬於簇,而異常不屬於任何簇或者屬於很小的簇。下面咱們找出異常並進行可視化。
outliers_fraction
給算法提供數據集中離羣點比例的信息。不一樣的數據集狀況可能不一樣,可是做爲一個起點,我估計 outliers_fraction=0.01
,這正是標準正態分佈中,偏離均值的距離以 Z 分數的絕對值計超過 3 的觀測值所佔比例。outliers_fraction
計算 number_of_outliers
。threshold
設置爲離羣點間的最短距離。anomaly1
的異常結果包括上述方法的簇(0:正常,1:異常)。結果代表,k-平均聚類檢測到的異常房費要麼很是高,要麼很是低。
孤立森林純粹基於異常值的數量少且取值有異這一狀況來進行檢測。異常隔離不用度量任何距離或者密度就能夠實現。這與基於聚類或者基於距離的算法徹底不一樣。
fit
和 predict(data)
在數據集上執行異常檢測,對於正常值返回 1,對於異常值返回 -1。SVM 和監督學習緊密相連,可是 OneClassSVM 能夠將異常檢測看成一個無監督的問題,學得一個決策函數:將新數據歸類爲與訓練數據集類似或者與訓練數據集不一樣。
根據這篇論文: Support Vector Method for Novelty Detection。SVM 是基於間隔最大的方法,也就是不對一種機率分佈建模。基於 SVM 的異常檢測的核心就是找到一個函數,這個函數對於點密度高的區域輸出正值,對於點密度低的區域返回負值。
nu=outliers_fraction
,這是訓練偏差的上界和支持向量的下界,這個值必須在 0 到 1 之間。這基本上是咱們預計數據裏面的離羣值佔比多少。rbf
。此時 SVM 使用非線性函數將超空間映射到更高維度中。gamma
是 RBF 內核類型的一個參數,控制着單個訓練樣本的影響 — 它影響着模型的"平滑度"。通過試驗,我沒發現什麼重要的差異。predict(data)
執行數據分類。由於咱們的模型是一個單類模型,因此只會返回 +1 或者 -1,-1 表明異常,1 表明正常。高斯分佈又稱爲正態分佈。咱們將使用高斯分佈開發一個異常檢測算法,換言之,咱們假設數據服從正態分佈。這個假設並不適用於全部數據集,一旦成立,就能高效地肯定離羣點。
Scikit-Learn 的 [**covariance.EllipticEnvelope**](https://scikit-learn.org/stable/modules/generated/sklearn.covariance.EllipticEnvelope.html)
函數假設咱們的全體數據是一律率分佈的外在表現形式,其背後服從一項多變量高斯分佈,以此嘗試計算數據數據整體分佈的關鍵參數。過程相似這樣:
EllipticEnvelope
(高斯分佈)。contamination
參數,它是數據集中出現的離羣點的比例。decision_function
來計算給定觀測值的決策函數,它和平移馬氏距離等價。爲了確保和其餘離羣點檢測算法的兼容性,成爲離羣點的閾值被設置爲 0。predict(X_train)
使用擬合好的模型預測 X_train 的標籤(1 表示正常,-1 表示異常)。有趣的是,這種方式檢測只檢測到了異常高的價格,卻沒有檢測到異常低的價格。
目前爲止,咱們已經用四種方法完成了價格異常檢測。由於咱們是用無監督學習進行異常檢測的,建好模型以後,咱們沒什麼能夠用來對比測試,也就沒法知道它的表現究竟如何。所以,在用這些方法處理關鍵問題以前必須對它們的結果進行測試。
Jupyter notebook 已經上傳至 Github。 好好享受這一週吧!
參考文獻:
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。