本文主要介紹 ICSE 2021 年論文《Robust and Transferable Anomaly Detection in Log Data using Pre-Trained Language Models》 [1] 。相較傳統的日志異常檢測方法,論文雖然沿用相同的檢測架構,但是結合了預訓練語言模型,實作日志異常檢測模型在不同系統之間的可遷移性。論文對于如何使用 NLP 技術提升系統的 AIOps 能力提供了自己的實踐經驗,相信該論文可以對相關從業人員提供一定的幫助。
研究背景
近些年來,軟體架構發生了重大的變化,逐漸由單體架構演變為微服務架構。一個軟體系統可能由幾個,甚至幾十幾百個微服務構成,每一個微服務都可以獨立開發和更新,這使得軟體的開發更加靈活高效。另一方面系統的整體架構逐漸複雜,而且由于微服務技術異構、更新頻繁等特點,給維護系統的穩定性帶來了不小的挑戰;運維人員需要及時檢測、修複系統中的異常,以避免異常在整個系統中擴散導緻系統奔潰。
日志作為描述系統運作的一手資料,被廣泛的應用于軟體開發工作中。日志資料對于系統狀态的記錄被用于檢測、定位系統中異常。在早期以單體架構為主的系統中,日志資料散落在少量幾個檔案目錄中,通過 grep、awk 等指令就可以定位大多數日志資料中的異常。而在微服務架構迅速發展的今天,大量日志檔案散落在各處的,每天的日志量可以到達 TB 甚至 PB 級别,而且現今的軟體系統往往依賴大量的第三方服務,而運維人員對于這些服務的運作時日志不是很了解,這使得傳統的依賴 grep、awk 進行日志異常檢測的方法在微服務系統中變得低效。針對這個問題,催生出一系列以自動化方式進行日志異常檢測的方法、架構。這些方法一般包含一下幾個步驟:
- 挖掘一段時間内的日志資料的句法模闆,日志模闆對應程式中的日志輸出代碼,比如 print("server failed to bind port %d"),日志模闆是 "server failed to bind *",注意模闆中用通配符替換了日志中的變量(用 * 替換 %d);這樣,一個日志模闆對應多條日志資料,在日志模闆次元進行異常檢測可以有效的解決大量日志資料造成的異常檢測低效的問題。
- 基于挖掘出的日志模闆,統計每一個模闆對應的日志流量,比如系統心跳日志每1分鐘出現一次、系統 GC 日志每5分鐘出現一次等等。檢測每個日志模闆的流量是否發生突變,這種突變就可能是系統異常的前兆或者結果。如果出現了某些日志無法比對任何日志模闆,這也提示系統可能發生了某些異常。此外,還可以通過分析日志模闆中的變量是否異常判斷系統是否有異常。
這些方法雖然有效的解決了現今微服務架構下日志數量大、種類多導緻的人工檢測效率低的問題,但是無法有效處理日志資料結構變化導緻的異常檢測性能下降的問題。這是因為,日志模闆假設日志模闆是穩定不變的,使用句法模闆覆寫已經見到過的日志類型,但是當系統更新部署時,往往會出現新的句法結構的日志,已有的日志模闆無法覆寫新的日志,需要重新進行日志模闆挖掘;這使得日志異常檢測在系統正常變更時暫時失效,不能有效檢測系統變更後、模闆更新前的日志資料,而在微服務架構下系統的更新更新頻率高,這更突出了一般日志異常檢測架構的短闆。針對這個問題,今天要介紹的論文提出了提升以下兩種場景中日志異常檢測性能的方法:
- 已有系統持續更新疊代
- 新系統子產品部署
論文中方法主要通過預訓練語言模型挖掘日志資料的語義特征而不單單是句法特征,語義特征往往在系統變更前後仍然是相對穩定的,使得日志異常檢測是系統變更後仍然可以正常運作。不同系統的日志的句法結構往往不相同,但是它們的語義資訊往往有大量重合,是以對于一個系統的日志異常檢測能力也可以遷移到類外一個系統上。
原了解析
論文中日志異常檢測包括訓練和預測兩個階段。在訓練階段,挖掘日志資料的句法模闆,再使用預訓練語言模型将日志模闆向量化,然後使用深度模型編碼一段時間視窗内的日志模闆,根據編碼結果預測下一條日志的日志模闆;計算預測的日志模闆和實際的日志模闆的差異,根據差異大小調整模型參數。與一般方法不同,該方法通過預訓練語言模型提取日志的語義特征,利用語義特征的穩定性,使得高效地檢測系統變更後的日志異常成為可能。在預測階段,比較實際的日志模闆和根據曆史資訊預測的日志模闆,如果偏差較大,那麼實際的日志模闆可能出現異常。下面我們将詳細的介紹該方法的各個步驟。
日志預處理和模闆挖掘
日志資料中往往包含如 IP, 數字,時間等等通用資訊,根據業務場景,配置正規表達式替換掉日志資料中的通用資訊,可以有效的提高挖掘的日志模闆的準确性。論文使用 Drain
[2]對日志資料進行預處理,并挖掘日志模闆。
日志模闆向量化
日志模闆向量化是把日志模闆轉化成一個向量,即 log embedding,使得這個向量盡可能的包含日志模闆的特征,是以正常模闆和異常模闆可以通過計算它們對應向量之間的距離進行區分。一般可以使用詞袋模型對日志模闆進行向量,這種編碼方式更容易捕獲模闆的句法特征,但是較難展現模闆的語義特征;是以,論文使用預訓練語言模型對日志模闆進行編碼,使得向量更容易捕獲日志的語義特征。兩種編碼方式的差別可以通過下圖展示出來
對于 VM Create fininshed,VM Created completed 和 VM Fatal error 這三條日志,在句法結構上較為相似,但是在語義級别,VM Create finished 和 VM Create completed 都表示 VM 建立正常,在語義上更接近,VM Fatal error 語義上與另外兩條日志完全不同。上圖中,使用标準的 one-hot 編碼(左圖)無法展現這種語義級别的差別,而使用語言模型得到的日志編碼(右圖)卻可以展現這種卻别。對于預訓練語言模型感興趣的同學可以查閱預訓練語言模型的相關資料。論文使用了 GPT-2
[3],XL-Transformers
[4]和 BERT
[5]進行了測試。
日志上下文編碼
日志的輸出往往是上下文相關的,對下一條日志的預測需要考慮之前出現的日志。論文中選擇待預測日志之前的若幹日志的日志模闆作為上下文,在上一步中向量化得到的上下文中每個日志模闆的 embedding,通過 Bi-LSTM
[6]編碼得到對于待預測日志的上下文 embedding,如下圖所示
日志異常檢測
得到待預測日志的上下文編碼後,論文中設計了兩個任務進行異常檢測,分别是 Multi-Class Classification 任務和 Regression 任務。
Anomaly Detection using Multi-Class Classification
Multi-Class Classification 任務将預測下一條日志的模闆看做是一個分類問題,将日志上下文編碼映射到已知的日志模闆上。在訓練階段,所有訓練資料都是可見的,目标模闆必定出現在挖掘到的日志模闆中,是以可以直接使用 cross-entropy loss 損失函數對 Bi-LSTM 進行訓練。但是在預測階段,由于實際運作時的日志可能沒有出現在訓練資料中,對應日志模闆可能不在挖掘到的已知的日志模闆中,是以需要其他方法确定新的日志模闆與已知日志模闆的映射關系。論文中使用預訓練語言模型得到待預測日志的日志模闆的向量化表示 log embedding,然後分别計算這個 embedding 與已知模闆的 embedding 之間的距離,距離輸入的日志模闆距離最近的已知模闆看做是模闆的真實類别。然後使用 Bi-LSTM 預測輸入的日志模闆的分類,獲得可能性最高的 top-k 個日志模闆,如果輸入的日志模闆的真實類别在這 top-k 個日志模闆中,就認為輸入的日志模闆是正常的,否則認為其是異常日志。
Anomaly Detection using Log Vector Regression
Log Vector Regression 任務将預測下一條日志的日志模闆看做是一個資料回歸問題,将日志上下文編碼映射到與已知日志模闆的 Log embedding 相同的向量空間中,優化映射後的向量與其真實模闆的 embedding 之間的距離。在訓練階段,直接使用 mean squared error 作為損失函數優化 Bi-LSTM,使 Bi-LSTM 映射後的日志向量與其真實模闆的 embedding 之間的距離盡可能小;訓練階段需要統計最小距離的分布情況,擷取最小距離分布的 q 百分位數對應的數值作為門檻值。在預測階段,統計 Bi-LSTM 處理後輸入日志的模闆向量與各個已知模闆 embedding 之間的最小距離,如果最小距離小于 q 百分位數對應的門檻值,那麼認為目前輸入的日志是正常的,否則認為其是異常日志。
論文并沒有使用融合這兩個任務,而是對于使用以上兩個任務進行異常檢測分别進行了測試。
模型遷移
由于使用預訓練語言模型和 Bi-LSTM 模型擷取日志的語義特征,使得日志異常檢測能力可以遷移到沒有見到過的日志資料上。假設異常檢測模型在資料集 A 上進行訓練,資料集 B 是系統變更後的日志,與資料集 A 中日志結構不同。在模型遷移時,首先将資料集 B 中的日志映射到資料集 A 中的日志模闆上(通過 log embedding 之間的最小距離擷取映射),然後從資料集 B 中擷取少量資料對異常檢測模型進行小樣本訓練(few-shot training
[7]),微調後的模型就可以對資料集 B 中的資料進行檢測,完成了模型的遷移。
實驗評估
為了驗證日志異常檢測模型的有效性和可遷移能力,論文中設計了兩個實驗分别驗證模型的有效性和可遷移性。使用的驗證資料是 CloudLab OpenStack 日志資料集
[8],該資料集使用 Openstack 正常運作時的日志作為訓練資料集,之後随機注入異常,将異常注入後的日志作為測試資料集。為了對日志異常檢測模型進行驗證,論文中對原始資料集進行了拓展,資料集拓展方式如下:
- 為了驗證使用語義特征進行異常檢測的有效性,論文對原始測試資料集中日志的句法結構和序列結構進行了修改
-
- 修改日志句法結構:随機抽取部分測試日志,對測試日志中的少量 token 進行删除(deletion),替換(swap)和随機插入(imputation)等操作;修改的 token 占比越高,對于日志結構變更越大,越可能是異常日志,構造的資料集記為 semantic test dataset。
- 修改日志序列結構:随機選取一段日志序列,對其中的部分日志進行删除(deletion),替換(swap)和重複(imputation)操作;修改日志的占比越高,對于日志的序列結構變更越大,越有可能是異常日志,構造的資料集記為 sequential test dataset。
- 為了驗證使用語義特征進行異常檢測的可遷移性,論文對測試資料集進行了擴充。将原始資料集記為資料集 A,從中随機抽取 q% 的日志,對抽取的日志進行删除、替換和插入等操作,此外增加額外的 token 到日志中;日志随機抽取的比例 q 可以表示擴充後資料集 B 與資料集 A 的差異程度,實驗分别構造了差異度 20% 和 80% 的資料集,使用 B-similar 和 B-different 表示。
實驗一:分别使用 GPT-2, XL-Transformers 和 BERT 作為預訓練語言模型測試使用語義特征進行日志異常檢測的有效性,實驗結果如下:
從實驗結果中可以看到,對于回歸任務(regression),使用 GPT-2 的效果較好,而對于分類任務(classification)BERT 的效果較好。
實驗二:測試使用語義特征進行日志異常檢測的可遷移性,實驗結果如下:
從實驗結果中可以看到,BERT 在回歸任務和分類任務上的表現都較好。
從上述的實驗中可以看到對于不同的預訓練模型在不同的測試任務和測試資料上,對實驗結果有不同的影響;使用更高性能的預訓練語言模型可以進一步提升日志異常檢測的效果。