コンサルでデータサイエンティスト

仕事でPythonを書いてます。機械学習、Webマーケティングに興味があります。趣味は旅です。

【3D物体検出論文】Orthographic Feature Transform for Monocular 3D Object Detection をまとめた

3D object detection (3D 物体検出) に関する2018-2019期の最新の論文『Orthographic Feature Transform for Monocular 3D Object Detection』*1について読んでまとめました。

3D Object detection とは、自動運転などにおいて 3次元空間における物体の位置情報を画像データから予測する物体検出タスクです。本記事では、3D object detection の概念について通常の物体検出と比較しながら紹介した後に、提案された最新の3D物体検出手法について解説していきます。

3d_object_detection
目次

3D Object detection とは

3D Object detection (3D 物体検出) とはどういったものなのでしょうか。実際の予測画像を紹介しながら、ほかの一般的な画像認識タスクと比較をしていきます。


画像認識タスクには一般的に以下の4種類があります。

  • Classification(分類)
  • Classification + Localization(分類 + 位置推定)
  • Object Detection(物体検出)
  • Semantic segmentation(領域分割)


image_recognition_task



3D Object detection はこれらのいずれにも含まれない新たな画像認識タスクとして、近年注目され始めています。

通常の物体検出 (2D Object detection) は様々な用途におけるニーズを伸ばしており、多くのアプリケーションで実用化されています。しかし、自動運転などにおいては通常の物体検出では不足している部分があります。例えば、下図の左画像のような2次元の物体検出では画像内に自動車が映っていることはわかりますが、画像に映っている自動車までの距離情報を認識できないという問題があります。


3d_object_detection_compare


そこで、物体の奥行き情報も考慮した3D Object detection(3次元物体検出) が登場しました。3D Object detection とは、物体の3次元空間における位置情報(3次元の直方体の位置、大きさ、角度)を予測するタスクです。

自動運転などでは、従来 LiDARと呼ばれる光を用いたリモートセンシング技術を用いて周辺の車両の位置推定を行っていました。


lidar_object_detection
画像: https://medium.com/@jhkoh/object-detection-with-lidar-point-cloud-algorithm-94a241fd3f49

しかし、LiDARが精密機械ゆえに自動車搭載に不向きであることや、安いものでも50万円程度と高価であることから、カメラ画像から 3D 空間の物体検出を行いたいというニーズが高まっています。

このような理由から、カメラ画像による 3D Object detection タスクが誕生しました。

論文の要旨

3D Object detection の概念をお伝えしたところで、ここから論文『Orthographic Feature Transform for Monocular 3D Object Detection』の解説に入っていきたいと思います。


本論文の特徴は以下の通りです。

  • 1台のカメラで撮影した画像から 3D Object detection を行う
  • 2次元画像空間における特徴を現実の 3D 空間にマッピングすることで3次元空間の特徴を捉える
  • 3D 空間における特徴マップに対して Orthographic space(正投影空間)に変換する機構を加えることで計算量削減


本論文は、2018年11月に arxiv に登場した時点では 1台のカメラ画像による 3D 物体検出手法で State-of-the-art を達成しています。つまり、共通のデータセットで評価を比較した際には最も良い精度が出ているということになります。

3D 物体検出までの流れ

本論文の提案手法について、ネットワークの学習および推論までのステップについて解説していきます。

提案手法では、複数のネットワークを組み合わせた大きなネットワーク構成となっています。共通の損失関数からすべてのネットワークの重みを学習することができることから、本ネットワークは End-to-end network であるといえます。

提案手法のネットワークアーキテクチャは下図の通りです。

3d_object_detection_architecture


この図に書かれている各ネットワークの詳細について、ひとつずつ解説していきます。


ResNet による特徴抽出

はじめに、画像が入力される最初のネットワークについて説明いたします。
ここでは、一般的な画像認識タスクでも多く使われている ResNet という Convolutional Neural Network (CNN) に画像を入力し、入力画像の特徴マップを抽出します。

このとき、入力画像に対してマルチスケールな特徴マップを抽出することで、画像の大まかな特徴から細かな特徴といった多様な特徴を捉えた特徴マップを得ることができます。


画像特徴マップを 3D 空間へマッピング

提案手法では、現実空間の特徴をより捉えるために画像空間ではなく 3D 空間で物体検出を行います。ここでは1つ目の CNN で抽出された特徴マップを 3D 空間にマッピングしていきます。

画像の最小の解像度はピクセルで表されるように、筆者らは現実の 3D 空間の最小の解像度をボクセルで表しています。


orthographic_feature_transform


1つのボクセルを1辺の長さが r の立方体だとすると、カメラで撮影した立方体のボクセルは画像空間内では下図のような六角形に映ります。


object_detection_cube


ボクセルの中心座標を (x, y, z)、カメラの位置を( (c_u, c_v))、カメラの焦点距離fとすると、画像空間内に映るボクセルのバウンディングボックス座標は下式で求めることができます。


bounding_box_equation


ボクセルが画像空間内でどのような映るかがわかったところで、画像空間における特徴マップを 3D 空間にマッピングしていきます。

中心座標が (x, y, z) のボクセルの特徴マップ g(x, y, z) は、上記で求めたバウンディングボックスに含まれる範囲の特徴マップに対して Average pooling(平均プーリング)をかけることで求めることができます。


mapping_equation


特徴マップのテンソルを繰り返し足し合わせる処理は計算量増大の原因になってしまいます。そこで、提案手法では Integral images(積分画像)による高速化を図っています。ピクセル (u, v) の Integral map を F(u, v) は、再帰関数の利用により下式のように表すことができます。


recursive_integral_image


求めた積分画像を用いると、ボクセルの特徴マップ g(x, y, z) は下式のように書き換えることができます。

speedy_feature_mapping


積分画像については、こちらの方の記事を読んでみるといいかもしれません。

画像処理 Integral Image(積分画像) - のんびりしているエンジニアの日記


Orthographic Feature Transform(正投影空間への変換)

3次元空間に特徴をマッピングすることで 3D 空間の情報を抽出することができました。しかし、このまま 3次元空間の特徴マップを用いて物体検出をしようとすると計算量が膨大になってしまうことは明らかです。
そこで、提案手法では 3次元の特徴マップを高さ方向で圧縮して 2次元にマッピングしています。これを Orthographic Feature Transform と呼んでいます。

実空間にマッピングした特徴マップ (Voxel features) のy軸方向の高さを Hとすると、正投影空間における特徴マップ h(x, z) は下式で求めることができます。
orthographic_feature_transform_h


求めた正投影空間における特徴マップ h(x, z) を再び CNN に入力し、出力に対して Confidence map、位置、大きさ、向きに関する 4つの損失関数を最小化するようにネットワーク全体を学習していきます。


topdown_network

画像空間ではなく実空間に対応する特徴マップを入力するため、カメラから遠い物体をカメラから近い物体と同様に扱うことができるというのが提案手法の大きな特徴です。


3D 物体の位置推定

物体の位置推定ステップでは一般的な背景・物体の分類問題を解く手法ではなく、物体の中心座標の存在確率を表す Confidence map を回帰する手法を利用します。

confidence_map


下記の中心座標を持つ N 個の既知の物体(学習データ)があるとすると


training_data_equation

正投影空間における点(x, z) の ground truth Confidence score は次のガウス関数で表現することができます。

confidence_map_equation

ここで定義した ground truth Confidence map と予測した Confidence map の差を l1 損失関数で表し、学習時にはこの損失関数を最小化するように学習していく。


さらに、位置・大きさ・向きについても損失関数を用意することで精度向上を図ります。

f:id:hktech:20181217225719p:plain



評価

KITTI の 3D Object detection データセットを用いて学習・テストを実施し、評価を行っています。
KITTI データセットの中身については、次の方がまとめてくださっています。

kitti関連の覚え書き - 八谷大岳の覚え書きブログ

評価実験は下記の諸元で実施しています。

*Features
Front-end network ResNet-18
Feature map scales 1/8, 1/16, 1/32
3次元空間のグリッドサイズ 80m × 4m × 80m
グリッド解像度 r 0.5 m
Topdown network 16-layer ResNet
Data Number (Training, Validation) 3712, 3769
Data augmentation Random cropping, scaling, flipping, camera parameters
Training epochs 600

物体検出結果を画像で確認していきます。ご覧のとおり、画像上に正投影空間に投影した Confidence map が描画されているのが確認できると思います。また、bird-eye's-view(鳥瞰図)でみると、画像内の車両位置と対応するように、Confidence mapで描かれた円がプロットされていることがわかります。

object_detection_result



定量的に結果を確認していきます。ここでは、カメラを2台使った従来手法である
3DOP*2 と、1台のカメラ画像を使った従来手法であるMono3D*3を比較します。


evaluation_result


KITTI ベンチマークでは正解と予測の重複が 0.7 以上のものを予測成功と定義されています。
Mono3D と比較すると精度が改善していることがわかります。また、2台のカメラを使用する 3DOP に近い精度を達成しています。特に、物体の重複がある場合や物体が遠方なときの Hard カテゴリでは精度が大きく改善していることがわかります。これは、画像空間ではなく実空間に対応する特徴マップを入力するため、カメラから遠い物体をカメラから近い物体と同様に扱うことが可能だからです。

まとめ

3D Object detection の最新論文についてまとめました。
対象論文『Orthographic Feature Transform for Monocular 3D Object Detection』では単一のカメラで撮影した画像を使って3次元空間における物体の位置情報を検出する 3D 物体検出手法が提案され、2018年末時点での State-of-the-art を達成しました。
まだまだ実用化に向けて精度を改善していかなければいけないフェーズですが、カメラ画像を使った 3D Object detection は今後も注目され続ける分野だと思います。今後も画像認識に関連した新しい手法について調べてまとめていきたいので、ご期待ください。

高い圧縮率を誇るbz2拡張子ファイルを圧縮・解凍する方法

サーバ上やローカルのLinuxで作業していると、直近で必要のないファイルが溜まって容量が不足してしまうことがありますよね。

そんな問題を解決してくれるのが圧縮・展開系のコマンドです。
zipやgzipなどさまざまなコマンドがありますが、それらと比較して高い圧縮率で圧縮できるのがbzip2コマンドです。
bzip2コマンドや、bz2拡張子を持つファイルに馴染みのない方も多いと思いますが、サーバの容量を圧迫したくないときなどにはおすすめなコマンドです。

今回はそんなbzip2による圧縮・解凍コマンドを備忘録としてまとめておきます。

 

目次

f:id:hktech:20181126222606j:plain
 

bz2ファイルへの圧縮方法

bzip2 コマンドによる圧縮方法は次の通りです。

$ bzip2 compression.log


圧縮後は以下のように拡張子.bz2を持つファイルに圧縮されます。

compression.log.bz2


圧縮処理自体に時間はかかりますが、ファイルによっては50倍以上の小ささに圧縮することができます。

また、ディレクトリ(フォルダ)ごと圧縮したい場合は以下のように圧縮することができます。

tar cvjf compression.tar.bz2 logs/

 

bz2ファイルの解凍方法

bz2拡張子を持つファイルの解凍方法は2通りあります。

bunzip2コマンドを使う場合は以下の通りです。bunzip2は解凍専用コマンドなので特にオプションは必要ありません。

$ bunzip2 compression.png.bz2

 

bzip2コマンドを使う場合は以下の通りです。d オプション (decompression) をつけることでファイルを解凍・展開することができます。

$ bzip2 -d compression.png.bz2

 

さきほどディレクトリごと圧縮する方法を紹介しましたが、このような場合の解凍方法は以下の通りです。

$ tar xvjf compression.tar.bz2

 

まとめ

bzip2 コマンドを使ったbz2拡張子ファイルの圧縮・解凍方法について紹介しました。さまざまな圧縮コマンドがありますが、Linux環境ではbzip2をおすすめします。実際の業務では、サーバの容量に制約条件があったりするので覚えておいて損はないと思います。容量があふれそうだけどファイルを消したくない、そんなときに試してみてはいかがでしょうか。

SSH先でJupyter notebook を使用する方法

Raspberry PiAWS EC2など、ローカルからリモートサーバに接続してPythonのプログラムを書く際にJupyter notebookを使いたくなることがあると思います。今回はSSH先でJupyter notebookを立ち上げて、ローカルで使用する方法についてご紹介します。

目次

SSH接続したリモート先の設定

リモートサーバ側の設定方法について確認していきます。
RaspberryPi でJupyter notebookを使用したい方は こちらの記事AWS EC2で使用したい方は こちらの記事を参考にしていただいてもよいかもしれません。


まずは外部接続環境用のJupyter notebook 設定ファイルを作成します。

mkdir ~/.jupyter


設定ファイル (config file) をviエディタで編集していきます。

vi ~/.jupyter/jupyter_notebook_config.py


ファイルが空の場合は下記の2行を書き込みましょう。

c.NotebookApp.ip = '0.0.0.0'
c.NotebookApp.port = 8888


ほとんどの場合、このファイルは空ではないので、該当行を探して編集しましょう。

vi ではスラッシュで文字列を検索できます。先頭のコメントアウト文字列#を消して、上の例のように2行分を編集しましょう。

/c.NotebookApp.ip


編集が完了したらファイルを保存してエディタを終了します。

:wq


最後に、Jupyter notebookを作成したいディレクトリに移動し下記のコマンドを実行します。

jupyter notebook


正常に起動できていれば、下記のようなメッセージが表示されます。

Copy/paste this URL into your browser when you connect for the first time,
to login with a token:
http://(raspberrypi or 127.0.0.1):8888/?token=abcabc123efg456xyz


リモート側の設定は以上となります。


ローカルの設定

ローカル側では特に設定は必要ありません。

適当なブラウザを開いて、URL欄に先ほどのURLを入力しましょう。

以下の画面が表示されれば無事にすべてのステップが完了です!

f:id:hktech:20181118231958p:plain

まとめ

SSH先でJupyter notebookを使用する方法についてご紹介しました。Python でプログラムを実装したり、データ解析などをしたい場合はJupyter notebookをリモートサーバだからJupyter notebookが使えないというのももったいないので、この記事を参考にしながら設定をしてみてはいかがでしょうか。


参考記事

RaspberryPi の場合

RaspberryPiでJupyter notebookを使う場合の設定方法はこちら
qiita.com

AWS EC2 の場合

AWS EC2でJupyter notebookを使う場合の設定方法はこちら
blog.roy29fuku.com

Pythonでk-NNをフルスクラッチで実装してみた

Scikit-learnを始めとしたパッケージが充実してきているおかげで、データ分析に関わる人もスクラッチから機械学習モデルを実装する機会が少なくなっています。

しかし、機械学習モデルを理解するためにはそのモデルを実装してみるのが一番早いと言われています。たとえパラメータをいじるだけの作業であったとしても、モデルの理解が深い人とそうでない人では、問題解決の質が変わってきます。

本記事では、最も基本的な分類モデルである k-NN (k-Nearest Neighbor) 法をフルスクラッチで実装したのでご紹介します。

目次

k-Nearest Neighbor法

k-Nearest Neighbor 法 (k-NN)k近傍法とも呼ばれ、分類モデルの中でももっともシンプルに実装できるものとして広く知られています。

k-NNとは、特徴量空間にデータをプロットしたときに、プロットと距離が近い学習データを近いものから順にk個選び出し、それらの学習データのクラスを多数決することでデータのクラスを決定する分類モデルです。

図のように、青四角クラス赤丸クラスの学習データが配置された特徴量空間を考えます。

k-NNのkは、テストデータ(星型)から何個の近傍点をクラス予測時に考慮するかということを決定するパラメータです。この例の場合、k=3のときは赤い学習データのほうが多いため赤クラスに分類されます。一方で、k=5のときは青のほうが多数となるため、テストデータは青クラスに分類されます。

f:id:hktech:20180926093820p:plain

k-NNをPythonで実装する

k-NNの概要については理解していただけたと思います。実にシンプルなモデルであるため、スクラッチから実装することはさほど難しくないです。それどころか、データサイエンティストであればこれくらいは実装できるのが最低要件だと思います。


シンプルである一方で、テストデータと複数の学習データとの距離をそれぞれ計算する必要があるため、計算量が多いという欠点があります。

こちらが実装したコードです。

import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score
import time


def load_data():
    iris = load_iris()
    return train_test_split(iris.data, iris.target, test_size=0.3)

def predict(k, test_plot, X_train,  y_train):
    dist_dict = {}

    for i, train_plot in enumerate(X_train):
        dist = np.linalg.norm(train_plot - test_plot)

        if len(dist_dict) <  k:
            dist_dict[dist] = y_train[i]    
        elif dist < np.max(list(dist_dict)) and len(dist_dict) == k:
            dist_dict.pop(np.max(list(dist_dict)))
            dist_dict[dist] = y_train[i]     

    count = np.bincount(list(dist_dict.values()))
    pred = np.argmax(count)
    return pred

def predict_test_data(k, X_train, X_test, y_train, y_test):
    pred_list = []
    
    for test_plot in X_test:
        pred = predict(k, test_plot, X_train, y_train)
        pred_list.append(pred)
    return f1_score(y_test, pred_list, average='macro')

if __name__ == '__main__':
    X_train, X_test, y_train, y_test = load_data()
    start_time = time.time()
    fscore = predict_test_data(3, X_train, X_test, y_train, y_test)
    process_time = time.time() - start_time
    print('F-score: {:.3f}'.format(fscore))
    print('Process time: {:.3f} sec'.format(process_time))

実装したモデルをirisデータセットを用いてF値で評価すると、次のような結果となりました。

F-score: 0.978
Process time: 0.118 sec

クラスの分類がうまくいっていることを確認することができました。

k-NNの特徴としてすべての点との距離を求める処理が必要となることから計算量が大きくなってしまうということがあります。そのため、プログラムの実行時間を計測できるような処理を実装しています。今回は何も考えずに最も単純な方法で実装したため計算量がかなり大きくなってしまっています。

Scikit-learnなどで実装されているk-NNは、この問題をできるだけ解決するように実装の工夫がなされています。実装を工夫した際のコードについてはまた別の機会にご紹介できればと思います。

まとめ

k-Nearest Neighbor法をフルスクラッチで実装してみました。まずは計算量などを考慮せずに最低限の精度が出るモデルの実装ができたので、次のステップとして高速化を考えていきたいです。k-NNは最も単純な機械学習モデルのひとつです。この記事を参考にしつつ、一から実装してみてはいかがでしょうか。


関連記事
決定木分類器をフルスクラッチで実装した際の記事です。
hktech.hatenablog.com

異常検知ビジネスで活用できる外れ値検知手法まとめ

機械学習の中でも教師なし学習に分類される分野として異常検知という技術があります。研究分野としては近年下火になりつつあるものの、人工知能やAIを使った異常検知技術はビジネス界隈では期待の大きい分野として有名です。

本記事では、異常検知分野のなかでも外れ値検知の一般的な手法についてまとめました。


目次

はじめに

外れ値検知はビジネスにおけるさまざまな場面で使用されています。

外れ値検知とは、正常時の状態から外れた点を見つけ出す異常検知の種類のひとつです。クレジットカードの利用状況の解析などでは、異常な行動を即時に検出することで被害の拡大を防ぐことができます。また、工場などのモニタリングでは、機械の異常状態をセンサなどで検知してアラートを出すなどのユースケースが考えられます。大量のデータをモニタリングする必要のあるセキュリティ分野や機械などを無人でモニタリングするスマートファクトリー分野のほか、高齢者の転倒検知や患者の体調急変といった予測に需要があるヘルスケア分野などでも実用化が始まっています。

それでは、最初におさえておきたい4つの基本的な外れ値検知手法について確認していきます。

ホテリング理論

最も基本的な手法として、ホテリング理論による外れ値検知について紹介します。

この手法は平均や分散といったデータの基本的な分布情報をもとに、観測値 x' より算出した異常度 a(x') を用いて外れ値検知を行うものです。まずは一般的な外れ値手法を理解するところから始めましょう。

はじめに、ほとんど外れ値が含まれていない1次元のデータセットを用意します。ホテリング理論ではこのデータセット正規分布に従うことを大前提としているため、ヒストグラムやQQ Plot などを使って正規分布であることを確認しておきましょう。もし正規分布でない場合は対数変換ボックスコックス変換などをかけて正規化する必要があります。

正規分布を仮定した場合、ホテリング理論をもとに算出した異常度はデータ数が十分に大きければ自由度1のカイ二乗分布に従うとされています。

chi_squared_distribution

異常発生の確率値 α を定義すれば、カイ二乗分布の表から異常度の閾値を求めてることができます。たとえば、発生確率が 1% 以下のものを異常値とするのであれば、α = 0.01 に対応する異常度を閾値に設定すればよいということになります。

異常度は次式で求めることができます。

 異常度  \,\,  a(x')= \Bigl(\frac{x'-\hat{\mu}}{\hat{\sigma}}\Bigr)


以上が1次元データにおけるホテリング理論による外れ値検知でした。



多次元データ(2次元以上)にこれを適用する場合は、マハラノビス距離という指標を異常度として採用します。マハラノビス距離とは、データの分布を考慮した距離の指標です。

1次元データでは平均と分散を求めましたが、多次元データでは標本平均と標本共分散行列を下記のように求めます。

 標本平均 \qquad \quad \, \, \hat{\mu} = \frac{1}{N} \sum_{n=1}^{N} x^{(n)}
 標本共分散行列 \quad \hat{\Sigma} \frac{1}{N}\sum_{n=1}^{N} (x^{(n)} - \hat{\mu})(x^{(n)} - \hat{\mu})^T


また、異常度はマハラノビス距離を用いて以下のように求めることができます。

 異常度  \,\,  a(x')= (x'-\hat{\mu})^T \hat{\Sigma}^{-1}(x'-\hat{\mu})


1次元でも多次元でも、異常度というスカラー値に落としこんでいるという点では共通していることがわかります。このように求めた異常度に閾値を設定することで、異常か正常かを識別することができます。
明らかに分布から外れている異常点を検出したいときには有効な手法です。

k-近傍法 (k-NN)

あまり有名ではないものの、シンプルに実装ができる古典的な手法として k-近傍法 (k-NN) による外れ値検知 があります。k-近傍法は k-nearest neighbor 法とも呼ばれていますが、異常検知におけるk-近傍法は機械学習の最も基本的な分類手法である k-NNとは少し異なることに注意してください。

想像ができている方もいらっしゃると思いますが、この手法ではk-NNと同様にある点から最も近い k 個の点を考慮して外れ値検知を行います。対象の点から近傍のk個の点を含むような円を描きます。

次の例では k=5 のときを考えます。

knn_anomaly

このとき、データ群から離れた異常点が描く円の半径 ε は正常データ群のものよりも大きくなっていることがわかります。
この性質に基づき、ε を異常度として使用して外れ値検知を行います。具体的には、εがある閾値を超えた場合は異常、そうでない場合は正常といったようにスカラー値に落として外れ値を検出することができます。
メリットとして、多次元データにもそのまま適用できるということがあります (ただし、次元数が10以下を目安と考えたほうがよいでしょう)。一方で、すべての点との距離を計算する必要があることから、計算量が大きくなってしまうというデメリットがあります。


Local Outlier Factor (LOF)

同じく距離をベースにした外れ値検知手法として、Local Outlier Factor (LOF) を紹介します。
この手法では 局所密度 (Local density) という指標に注目します。 局所密度は、周辺の点との密度を表します。点Aは近傍の点 B, C, D との距離が大きいため、局所密度が低いということになります。一方で、点B, C, D はそれぞれの近傍点との距離が近いため局所密度が高いといえます。

Local Outlier Factor (LOF)

つまり、近傍点の局所密度が類似している場合には近くにデータが集まっているということがわかります。点Aのようにデータ群と離れているような点の場合、自身の局所密度と近傍点の局所密度の差が大きいということになります。この差を利用して外れ値を検出することができるのが LOF です。

より詳しい内容については次の記事でまとめたので興味がある方はご確認ください。

hktech.hatenablog.com


One class SVM (Support Vector Machine)

分類アルゴリズムである Support Vector Machine (SVM) を応用した外れ値検知手法として、One class SVM を紹介します。

One class SVMとは、機械学習の分類アルゴリズムである Support Vector Machine (SVM) を教師なしの1クラスに適用した手法です。正常データとして1つのクラス分を学習させ、識別境界を決定することで、その境界を基準として外れ値を検出します。異常がほとんど発生せず、異常クラスのデータを集めにくいようなシステムで異常検知を実現したい場合には有効な外れ値検知手法です。

One class SVMではすべての学習データをクラスタ 1とし、原点のみをクラスタ -1に属するようにカーネルトリックと呼ばれる手法を用いて、高次元空間の特徴空間へデータを写像します。このとき、学習データは原点から遠くに配置されるように写像されるため、もとの学習データと類似していないデータは原点の近くに集まるようになります。

one_class_svm

One class SVM のメリットとしては、学習したデータをもとに複雑な境界線を引くことができるという点があります。一方で、パラメータをチューニングする必要があるというデメリットもあります。

One class SVM については次の記事でより詳細にまとめています。
hktech.hatenablog.com

まとめ

異常検知ビジネスで活用できる一般的な外れ値検知手法についてまとめました。外れ値検知をサービスや研究などで使おうと考えている方には参考になる内容になっているかと思います。Deep Learning などを使ってなにかを始めようとする前に、まずはこの記事で紹介されているような基本的なアルゴリズムで試してみてはいかがでしょうか。



異常検知と変化検知 (機械学習プロフェッショナルシリーズ)

One class SVM による外れ値検知についてまとめた

はじめに

異常検知技術が実用システムに導入される例が増えています。今回は外れ値検知手法として人気が高いアルゴリズムのひとつであるOne class SVMについてご紹介します。

One class SVMとは、機械学習の分類アルゴリズムである Support Vector Machine (SVM) を教師なしの1クラス分類に応用した手法です。正常データとして1つのクラス分を学習させ、識別境界を決定することで、その境界を基準に外れ値を検出します。異常がほとんど発生せず、異常クラスのデータが集まらないようなシステムで異常検知を実現したい場合には有効な外れ値検知手法です。

最後にPythonによる簡単な実装例についてご紹介します。

One Class SVMSVMの違い

One Class SVMSVM の違いについて理解するためには、まずSupport Vectorとは何かということを把握する必要があります。Support Vectorとは学習データの中で最も他クラスと近い位置に点を指します。Support Vector Machineでは、各クラスのSupport Vectorを基準として、それらのユークリッド距離が最大化するように識別境界を設定します。これをマージン最大化と呼びます。

f:id:hktech:20181011233116p:plain


また、識別境界が非線形の場合はカーネルを用いてデータを特徴空間に写像します。カーネルを適切に選択することで、複雑なデータ配置でも識別境界を引くことができます。

f:id:hktech:20181011233133p:plain


通常のSVMでは、学習データとして複数クラスのデータが用意されています。したがって、学習時に識別境界を決定すれば、あるデータがどのクラスに分類されるかを判別する分類器として使うことができます。

しかし、One Class SVMでは学習データとして正常データの1つのクラスしか用意されていません。One class SVMは識別境界を設定するという点では共通しているものの、分類手法としての役割を持つSVMと異なり、識別境界を境に正常データと異常データを識別する外れ値検知手法として使うことができます。

One class SVMによる外れ値検知

SVMでは複数のクラスのSupport Vectorを基準として識別境界を決定するということは理解できました。One class SVMでは1つのクラスのみでどのように識別境界を求めるのかということについて明らかにしていきます。

One class SVMではすべての学習データをクラスタ 1とし、原点のみをクラスタ -1に属するようにカーネルトリックと呼ばれる手法を用いて、高次元空間の特徴空間へデータを写像します。このとき、学習データは原点から遠くに配置されるように写像されるため、もとの学習データと類似していないデータは原点の近くに集まるようになります。この性質を用いて正常および異常データの区別をすることができます。

f:id:hktech:20181011233151p:plain

Python + Scikit-learn を使った実装

PythonでScikit-learn (sklearn) を使えば、One class SVMを数行で実装することができます。

from sklearn.svm import OneClassSVM
clf = OneClassSVM(nu=0.003, kernel='rbf', gamma='auto')
clf.fit(X_train)
pred = clf.predict(X_test)


SVMのパラメータとしてkernelとgamma、One class SVM特有のパラメータとして nu があります。
学習データはすべて綺麗なデータであるとは限らないため、識別境界の決定時に考慮しないデータの割合をnuで定義することができます。

predictの返り値としては 正常: 1、異常: -1のリストが返ってくるため、異常と判定されたサンプルのインデックス番号を確認するには次のコードを実行します。

import numpy as np
np.where(pred < 0)

このあたりはLOFの実装のときと同じですね。このように手法が変わっても返り値が同じ形式で得られるのもScikit-learnパッケージの強みです。

まとめ

異常検知技術のひとつであるOne class SVMについてご紹介しました。One class SVMSupport Vector Machineを1クラス分類に応用することで外れ値検知を実現する手法であるということがわかりました。また、Pythonを使って簡単に実装できることも確認することができました。SVMの中身は理解が難しいことで有名であるため、機会があればスクラッチから実装してみたいと思います。

関連記事
hktech.hatenablog.com



異常検知と変化検知 (機械学習プロフェッショナルシリーズ)

PythonでLocal Outlier Factor (LOF)を実装してみた

目次

はじめに

教師なし学習のひとつとして異常検知という分野があります。その中に含まれる手法として、正常時の状態から外れた点を見つけ出す外れ値検知手法があります。外れ値検知は実アプリケーションにも数多く導入されており、機械学習分野で注目を浴びている技術です。今回はその入門編として Local Outlier Factor (LOF) という手法をPythonで実装してみたいと思います。LOF の理論と仕組みについては次の記事でまとめたので確認してみてください。

hktech.hatenablog.com


Scikit-learnによるLOFの実装

機械学習パッケージであるScikit-learnを使って実装していきます。 今回は、Local Outlier Factor (LOF) のアルゴリズムに基づいてデータXから外れ値検知を行います。

パッケージが入っていなければ、インストールしておきましょう。

pip install scikit-learn
pip install matplotlib
pip install seaborn


早速、Scikit-learnを使って実装していきます。ここでは考慮する近傍点の数を n_neighbors=7 としています。

from sklearn.neighbors import LocalOutlierFactor
clf = LocalOutlierFactor(n_neighbors=7)
pred = clf.fit_predict(X)

Scikit-learnを使うとたった3行で外れ値を検出することができます。簡単ですね。


fit_predictの返り値としては 正常: 1、異常: -1のリストが返ってくるため、異常と判定されたサンプルのインデックス番号を確認するには次のコードを実行します。

import numpy as np
np.where(pred < 0)


試しに実際のデータを使って外れ値検知を行い、検出結果の可視化をしてみたいと思います。今回は、irisデータの一部変数を使い、2次元での可視化を行いました。

import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.neighbors import LocalOutlierFactor
from sklearn import datasets
%matplotlib inline
 
iris = datasets.load_iris()
X = iris.data
 
clf = LocalOutlierFactor(n_neighbors=7, contamination=0.005)
pred = clf.fit_predict(X[:, (1, 2)])
 
# 正常データのプロット
plt.scatter(X[:,1][np.where(pred > 0)], X[:,2][np.where(pred > 0)])
# 異常データのプロット
plt.scatter(X[:,1][np.where(pred < 0)], X[:,2][np.where(pred < 0)])

f:id:hktech:20181010230057p:plain


図をみてみると、左上と右下の2つクラスタのいずれにも含まれないオレンジのサンプルが外れ値として検出されていることがわかります。このように、LOFはデータが複数のクラスタに分かれているような場合でも、それらを考慮しつつ外れ値を検出できるモデルであるということが確認できました。

まとめ

Python で Local Outlier Factor (LOF) を実装する方法についてご紹介しました。今回はScikit-learnを使って実装を進めましたが、LOFのアルゴリズム自体はそんなに難しくないため、スクラッチから実装してみるのもいい練習になるかと思います。