NTTドコモR&Dの技術ブログです。

基地局の「いつもと違う」をAutoEncoderで見つけてみた

こんにちは。ドコモの鈴木です。
本記事は、ドコモアドベントカレンダー23日目の記事になります。
本記事では、機械学習の一手法であるAutoEncoderを用いて基地局の「いつもと違う」状態を見つける仕組みについて記載いたします。

モバイルネットワークの品質維持業務

「24時間365日」、快適なモバイル通信サービスを提供することを目的としたネットワーク品質を維持するための業務です。
主に①監視、②分析、③措置の3つのフェーズで構成されており、主に故障した装置から上げられるアラーム情報を基として体系化された手順が確立されています。

しかし、全ての異常がアラームとして定義できているわけではなく、アラームとして定義されていない異常(以下、サイレント異常)も存在しており、その発見および対応はネットワーク保守者の重要な業務の1つに含まれます。

基地局のサイレント異常

サイレント異常は様々ですが、例えば人がイベントなどにより一時的に密集したりすると通信が一時的に混雑して速度が落ちるような事象は装置故障ではないため、アラームが上がらず、機械的に検知することが困難です。

ネットワーク保守者はこれらの異常について、人手でトラフィックデータの値を分析し、怪しい基地局の目星をつけることにより対応を図っています。

しかしドコモが保有する基地局数は膨大であり、とても時間のかかる大変な業務となっています・・・

そこで、ドコモに蓄積された大量のトラフィックデータAutoEncoderを掛け合わせることで解決策の検討を実施しました!

アプローチ

本記事にて検討を実施したサイレント異常検知は、以下の3ステップで構成されます。

(1) 入力データの事前定義
(2) 学習フェーズ
(3) 分析フェーズ

蓄積されたトラフィックデータは、異常時に対して正常時の傾向を占めるデータが圧倒的に多いことを踏まえ、正常時データのみで学習できるAutoEncoderを採用しました。
今回は各トラフィックの正常時の値を学習し、分析時に異常な値を示したトラフィック項目が存在する場合に異常度(=トラフィック値の正常時との差分)が大きくなるようにモデル構築を実施しています。

(1)入力データの事前定義

基地局の通信の状態を表すトラフィックデータの中から、ネットワーク保守者が普段から見ている項目を参考に、10種類の異なる周期性を持つデータを検証用に生成しました。
下の図がその一例です。
今回分析対象とした基地局の異常発生期間周辺のみを切り取っているためわかりにくいですが、グラフの波形が大きくなっている部分は普段と著しく異なっている状態を示しており、このようなトラフィックの変動をAIモデルで検知しています。

トラフィックデータはユーザの通信行動の結果であるため、時間によってデータの傾向が異なる可能性があると考え、今回の検討ではサイレント異常検知の検知精度に対して時間情報が与える影響についても調査します。
以下の3パターンでAIモデルを構築し、検討を実施しました。

①トラフィック情報のみ
②トラフィック情報+休祝日情報
③トラフィック情報+休祝日情報+時刻情報
※以降のアプローチ(2)(3)では、①~③それぞれのパターンに対して処理を実行します。

(2)学習フェーズ

サイレント異常検知用のAutoEncoderモデルを構築するため、(1)で記載した入力データを用いて学習を実施します。

AutoEncoderモデルの概要イメージを以下に示します。
学習データを一度中間層で次元削減し、それを最も損失なく再現できるパラメータを探索していきます。
これにより、学習データ(=正常時のトラフィックデータ)のみを復元できるAutoEncoderモデルが出来上がります。


以下に示すコード文は、サンプルとしてパターン3の「トラフィック情報+休祝日情報+時刻情報」のデータを用いた場合のものを記載しています。

import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
import joblib
import datetime as dt
import keras
from keras.layers import Input, Dense
from keras.models import Model
from keras.callbacks import EarlyStopping
from keras.models import load_model

# カラム定義
data_col = [
"accessdate", # レコードの日時(yyyymmddhhmmss)
"equipment_id", # 基地局ID情報
"holiday", # 休祝日か平日かの判定(休祝日=1, それ以外=0)
"ampm", # 午前か午後かの判定(午前=0, 午後=1)
"time1", "time2", "time3", "time4", # 0~11時を4bitで表記(10時=1010)
"tr01","tr02","tr03","tr04","tr05","tr06","tr07","tr08","tr09","tr10", # トラフィック項目10種
]

df = pd.read_csv('train/inputdata_001.dat', header=None, encoding = "shift-jis", names = data_col, dtype = float)

df = df.astype({"accessdate":int, "equipment_id":int})
df = df.astype({"accessdate":str, "equipment_id":str})

# 変数管理(AutoEncoderのハイパーパラメータ)
EPOCHS = 30000
BATCH_SIZE = 32
PATIENCE = 5
DIM = 8

# モデル作成処理
df["date"] = pd.to_datetime(df['accessdate'])
df_t = df[df.date >= dt.datetime(20XX,XX,XX)] # 日付情報を用いて学習期間の指定
equipment_id_list = df_t.equipment_id.unique()
for equipment_id in equipment_id_list:
df_data = df_t[df_t.equipment_id == equipment_id].drop(["date"],axis=1).copy()
inputScaler = df_data.values
inputScaler = inputScaler[:, 4:]

sc = StandardScaler()
sc.fit_obj = sc.fit(inputScaler)
data = sc.fit_obj.transform(inputScaler)

joblib.dump(sc.fit_obj,"deviation_full_"+equipment_id+".pkl")

input = data
dataShape = input.shape[1]
encoding_dim = DIM

# 入力と出力の次元数 (入力データの数)
input_img = Input(shape=(dataShape,))
encoded = Dense(encoding_dim, activation='relu')(input_img)
decoded = Dense(dataShape, activation='linear')(encoded)
autoencoder = Model(input_img, decoded)

autoencoder.compile(optimizer='adadelta', loss='mse', metrics=['accuracy'])

es = EarlyStopping(monitor='loss', patience=PATIENCE, verbose=0)
hist = autoencoder.fit(input, input, epochs=EPOCHS, batch_size=BATCH_SIZE ,callbacks=[es])
autoencoder.save('autoencoder_full_'+equipment_id+'.h5')

(3)分析フェーズ

学習フェーズで生成したAutoEncoderモデルと、異常判定用に新規に取得したトラフィックデータを用いて分析を実施していきます。
異常発生時は正常時とは異なったトラフィック値を示すので、正常時データを再現するように学習したAutoEncoderでは再現することができなくなり、入出力の値に大きな差が生じます。
この時の差分(異常度)を基に、異常検知を行います。

df["date"] = pd.to_datetime(df['accessdate'])
df_t = df[df.date >= dt.datetime(20XX,XX,XX)] # 日付情報を用いて分析期間の指定
equipment_id_list = df_t.equipment_id.unique()

for equipment_id in equipment_id_list:
df_data = df_t[df_t.equipment_id == equipment_id].drop(["date"],axis=1).copy()
inputScaler = df_data.values
inputScaler = inputScaler[:, 4:]

sc = joblib.load("deviation_full_"+equipment_id+".pkl")
data = sc.transform(inputScaler)
input = data
model = load_model('autoencoder_full_'+equipment_id+'.h5')
predict = model.predict(input)
square = np.power((predict - input),2)
square_t0 = np.sum(square, axis=1)

実行結果

異常検知で気にするポイントとして、検知が出来ているかと正常な期間にも関わらずAIが異常と判断してしまう誤検知をしていないかの2点があげられます。

検知が出来ているか

3パターンすべてにおいてサイレント異常が起きていた期間にAIモデルが反応していることが確認できました。

※凡例
 ①トラフィック情報のみ:"1. no_info"
 ②トラフィック情報+休祝日情報:"2. add_holiday"
 ③トラフィック情報+休祝日情報+時刻情報:"3. add_full"
 背景が灰色の期間:ドコモNW保守者が、サイレント異常が発生していたと判断した期間
 縦軸:異常度RSS(以下に計算式を記載)

\displaystyle{RSS(X,Y)=\sum_{i=1}^N {(x_i-y_i)}^2}
\displaystyle{X=\{x_1 ... x_N\}・・・入力}
\displaystyle{Y=\{y_1 ... y_N\}・・・出力}

誤検知をしていないか

AutoEncoderは学習で使った正常なトラフィックデータのパターンしか判断できません。
誤検知は、学習したパターン以外を異常と判断してしまうことで発生します。
今回対象とした基地局では誤検知と言えるほどの異常度の急増は発生していませんが、以下のグラフを見ると"1. no_info"と"2. add_holiday"はところどころで異常度が立っている箇所があるのに対し、"3. add_full"は安定した値を示していることが確認できます。
これは細かい時間情報を追加したことで時間ごとのトラフィックデータの特徴を学習することができたためと考えられます。

さいごに

本記事では、機械学習の一手法であるAutoEncoderを用いて基地局の「いつもと違う」状態を検知する仕組みについて記載しました。
今回の例では1基地局に絞って学習/分析を行った結果をお見せしましたが、実際にはもっと大規模な基地局を分析する必要があります。
実用に向けて色々と検討を行っている段階ですが、本技術にてドコモユーザーの皆様に快適な通信を提供するために頑張っていきます!