pikesaku’s blog

個人的な勉強メモです。記載内容について一切の責任は持ちません。

Jubaanomalyを使って不正ログイン検知

作ったプログラムは以下

①make_test_data.py

学習データにするログイン記録データを生成するツール。
1行あたりのフォーマットは以下。
日時, 接続元IPアドレス
オプションでログインソースの傾向を決められる。
本物のログイン記録になるっぽく少しいじってる。
 

②study.py

①で出力した学習データを学習するツール
 

③test.py(作成中)

②で作った学習モデルに対してテストをするツール
以下3つのデータをインプットさせ、外れ値を出力する。
1) 学習済みデータ
2) 未学習データだが不正ログインではないログインデータ
3) 不正ログインのログインデータ
 

④mylib.py

②、③のプログラムの共有ライブラリ
 

実行方法は以下(作成中)

 
 
 

①make_test_data.py

# -*- coding: utf-8 -*-

import argparse
import random
import ipaddress
import pandas as pd
import numpy as np
import datetime


all_prefixes = [
    # http: // bgp.he.net / AS4713
    # 上記URLより適当に選択
    '20.197.0.0/20',       # Computer Sciences Corporation US
    '27.114.0.0/17',       # NTT Communications Corporation JP
    '58.88.0.0/13',        # NTT Communications Corporation JP
    '60.32.0.0/12',        # NTT Communications Corporation JP
    '61.4.152.0/23',       # NDS Co.,Ltd JP
    '103.211.116.0/24',    # Internet Gateway Japan CH
    '166.119.72.0/21',     # Ministry of Agriculture, Forestry and Fisheries JP
    '169.145.224.0/20',    # SAP America Inc. US
    '170.252.144.0/23',    # Accenture LLP US
    '203.215.128.0/24'     # Servcorp SmartOffice1 GB
]


parser = argparse.ArgumentParser(description='making test data tool')
parser.add_argument('-n', '--num',      dest='num',      help='num',                     required=True, type=int)
parser.add_argument('-b', '--base',     dest='base',     help='num of base ips',         required=True, type=int)
parser.add_argument('-r', '--rate',     dest='rate',     help='rate of base ips(0-100)', required=True, type=int)
parser.add_argument('-p', '--prefixes', dest='prefixes', help='num of prefixes' + '(1-' + str(len(all_prefixes)) + ')', required=True, type=int)
args = parser.parse_args()


def get_target_ips(prefixes):
    target_ips = list()
    num_per_prefix = int(args.num / len(prefixes))
    for prefix in prefixes:
        # 大きなサブネットのIPで占有されないよう同じ数だけ選択
        if num_per_prefix > len(list(ipaddress.ip_network(prefix))):
            num_per_prefix = len(list(ipaddress.ip_network(prefix)))
        target_ips += random.sample(list(ipaddress.ip_network(prefix)),num_per_prefix)
    return target_ips


def get_base_ips(target_ips):
    # ターゲットIPからランダムにベースとなるIPを選択
    ips = random.sample(target_ips, args.base)
    num_of_base_ips = int(args.num / 100 * args.rate)

    # ベースとなるIPの出現回数に偏りをつける。
    # まずは平均をとる。
    num_per_ips = dict()
    for ip in ips:
        num_per_ips[ip] = int(num_of_base_ips / len(ips))
    # 次にランダムに2つチョイスし、出現回数の偏りをつける操作を行う。
    if len(ips) > 1:
        for _ in range(args.base):
            random_ips = random.sample(ips, 2)
            goukei = num_per_ips[random_ips[0]] + num_per_ips[random_ips[1]]
            num_per_ips[random_ips[0]] = int(goukei * 2 / 3)
            num_per_ips[random_ips[1]] = goukei - num_per_ips[random_ips[0]]

    base_ips = list()
    for ip,num in num_per_ips.items():
        base_ips += [ip for _ in range(num)]

    return base_ips


def get_random_date():
    we_th  = 4
    we  = 0
    non_regular_th = 3
    non_regular = 0
    while True:
        td = datetime.timedelta(days=365)
        end = datetime.datetime.now()
        start = end - td
        dts = (end - start).total_seconds()
        random_date = start + pd.Timedelta(np.random.uniform(0, dts), 's')
        if random_date.weekday() >= 5:
            we += 1
            if we >=  we_th:
                return random_date
            continue
        if random_date.hour >= 18 or 0 <= random_date.hour <= 8:
            non_regular += 1
            if non_regular >= non_regular_th:
                return random_date
            continue
        return random_date


def chk_args():
    if args.prefixes >= len(all_prefixes):
        print('Error: -p NUM must be smaller than ' + str(len(all_prefixes)))
        exit()


if __name__ == '__main__':
    chk_args()
    prefixes = random.sample(all_prefixes, args.prefixes)
    target_ips =  get_target_ips(prefixes)
    base_ips = get_base_ips(target_ips)
    if (args.num - len(base_ips)) > len(target_ips):
        other_ips = target_ips
        other_ips += random.sample(target_ips, args.num - len(base_ips) - len(target_ips))
    else:
        other_ips = random.sample(target_ips, args.num - len(base_ips))
    all_ips = base_ips + other_ips
    all_ips = [[ip, get_random_date()] for ip in all_ips]
    for ip in all_ips:
        print(str(ip[1].strftime("%Y/%m/%d %H:%M:%S")) + ',' + str(ip[0]))

 

②study.py

# -*- coding: utf-8 -*-

import signal
import argparse
from mylib import *

#parser = argparse.ArgumentParser(description='jubaanomaly study tool')
#parser.add_argument('name',                            help='data name')
#parser.add_mutually_exclusive_group('-f', '--file',    dest='file',    help='file', type=argparse.FileType('r'))
#parser.add_mutually_exclusive_group('-d', '--display', dest='display', help='display studied data', action="store_true", default=False)
#args = parser.parse_args()


parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('name', help='name')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-f', dest='file',    help='file',    type=argparse.FileType('r'))
group.add_argument('-d', dest='display', help='display', action='store_true')
args = parser.parse_args()


if __name__ == '__main__':
    signal.signal(signal.SIGINT, do_exit)
    if args.display:
        anom = connect_srv(args.name, 'display')
        display_studied_data(anom)
    else:
        anom = connect_srv(args.name, 'study')
        study_data = get_study_data(args.file)
        result = input_data(args.name, anom, study_data, 'study')

 

③test.py

 

④mylib.py

# -*- coding: utf-8 -*-

import signal
import datetime
import dateutil.parser

from cymruwhois import Client
from jubatus.anomaly import client
from jubatus.common import Datum


def err_fin(mes):
    print('Error: ' + mes)
    exit(1)


def warn_continue(mes):
    print('Warn: ' + mes)


def connect_srv(name, mode):
    try:
        anom = client.Anomaly("127.0.0.1", 9199, name)
        anom.clear()
    except:
        err_fin('failed to connect to server')

    if mode == 'test' or mode == 'display':
        try:
            anom.load(name)
        except:
            err_fin('failed to load data')
    return anom


def display_studied_data(anom):
    try:
        data = anom.get_all_rows()
    except:
        err_fin('failed to display data')
    if not data:
        warn_continue('no data')
    else:
        for ent in data:
            print(ent)


def do_exit(sig, stack):
    print('You pressed Ctrl+C.')
    print('Stop running the job.')
    exit(0)


def get_study_data(file):
    tmp_data1 = [line.strip().split(',') for line in file]
    tmp_data2 = list()
    for ent in tmp_data1:
        try:
            dt = dateutil.parser.parse(ent[0])
        except:
            err_fin('invalid date format: ' + ent[0])
        tmp_data2.append([dt.strftime('%Y%m%d-%H:%M:%S'), dt.weekday(), dt.strftime('%H'), ent[1]])

    src_ips = {ent[3] for ent in tmp_data2}
    src_ips_info = get_src_ips_info(src_ips)

    study_data = list()
    for ent in tmp_data2:
        src_ip = ent[3]
        src_ip_int = ip2int(src_ip)
        study_data.append(ent + [src_ip_int] + src_ips_info[src_ip])
    return study_data


def get_src_ips_info(src_ips):
    src_ips_info = dict()
    c = Client()
    for r in c.lookupmany(src_ips):
        src_ips_info[r.ip] = [r.asn, r.prefix, r.cc, r.owner]
    return src_ips_info


def ip2int(src_ip):
    o = [int(i) for i in src_ip.split('.')]
    src_ip_int = (16777216 * o[0]) + (65536 * o[1]) + (256 * o[2]) + o[3]
    return src_ip_int


def input_data(user, anom, data, mode):
    datum = Datum()
    result = list()
    for ent in data:
        #['20170101-00:00:00', 6, '00', '8.8.8.8', 134744072, '15169', '8.8.8.0/24', 'US', 'GOOGLE - Google Inc., US']
        date = ent[0]
        weekday_and_hour = int(str(ent[1]) +  str(ent[2]))
        ip = ent[3]
        ip_int = ent[4]
        asn = ent[5]
        cc = ent[6]
        owner = ent[7]

        string_data = [date, ip, asn, cc, owner]
        num_data = [weekday_and_hour]

        for ent in string_data:
            datum.add_string('string_data_' + str(string_data.index(ent)), ent)
#            print('string_data_' + str(string_data.index(ent)), ent)

        for ent in num_data:
            datum.add_number('num_data_' + str(num_data.index(ent)), ent)
#            print('num_data_' + str(num_data.index(ent)), ent)

        result.append(anom.add(datum))

    anom.save(user)
    return result

機械学習関係の勉強会メモ⑤

メモ

pythonのscikit-learnの勉強家
 
今のAIサービスは、ほとんど教師ありで実現されている。
http://postd.cc.practical-machibe-learning-problems

機械学習アルゴリズムとして以下があり。
・回帰
・決定木
SVM
 
回帰は、XからYを出力する規則をみつける手法
重回帰はXを構成する要素を複数にする。回帰の実践では、こちらの手法が採用されている。
 
機械学習ではデータ集め、前処理が時間かかる。
 
決定木は回帰より精度が高い傾向があり。
出力過程を可視化が可能
PDFで出力可能
 
SVMもより精度が高い傾向があり。
 

機械学習関係の勉強会メモ④

Tensor Flowユーザー会のプレゼン話のメモ

 
テンサーフロー
テンソルフロー

2つの呼び方があり。
ユーザー会では多数決でテンソルフローとしたが、一般的にはテンサーフロー
 
平日夜の月1〜2回程開催されるミートアップに150名定員で400名ほどの申し込みあり。
 
分科会や地方コミュニティもあり。
 
Deep LearningはPoCが大事。実現できることの把握が大事。
PoCでデータ特性から利用するアルゴリズムを見定める必要あり。
小さな問題からトライすべき
非機能要件の特定が困難(性能・容量)な為、PoCで予測する必要あり。
 
実際のビジネス活用では、完全AI任せでなく人間補助を前提としてケースが良い場合があり。
 
こんなサイクルがいいのでは?
f:id:pikesaku:20170723173333p:plain
 
以下が大事
課題とDeep Learningの紐つけ
PoCフェーズ
受注/発注者間の顕密な連携 
 
Tensor Flowは、Inception V3というGoogleの学習済みモデルを適用できる。
自前で学習させなくても画像認識が可能。
これらを活用しつつ、部分的に学習を追加する転移学習というテクニックもあり。実現するライブラリもある。
 
DeepLearning開発では、スクラッチでゼロから学習・実装するのではなく、既存の公開モデルを活用するのが良いのでは?

機械学習関係の勉強会メモ③

GoogleのAI取り組み話のメモ

 
DLは80層
 
Google Photos(スマホアプリ)
 写真を自動ラベル付け
 
Smart Reply(自動返信予測機能)
 Gmailで返信メッセージを予測し表示してくれる機能。
 利用者は、メッセージを選択すれば入力不要

DeepMind社のWaveNet
 音を生成するニューラルネットワーク
 例) クラッシック音楽を入力として、オリジナルのそれっぽい曲を生成できる。
 
DCの空調管理をAIにしてDC全体で15%のコスト削減
 
GoogleではAIは研究段階ではなく実用している★
 

GoogleのAIサービス

 

学習済みAPI

 
画像、動作、音声認識API等があり。
Google Cloud Platform(以降GCPと記載)で利用可能
cloud.google.com
 
 

①CLOUD NATURAL LANGUAGE API

cloud.google.com
 
強力なテキスト分析が可能
f:id:pikesaku:20170723164209p:plain
 
入力した文章を語句解析し、ラベル付け(Event、Other、Person等)やWikiのリンクも自動探索する。
また文章全体で重要なキーワードの特定も可能。(Salienceが重要度を意味する)
 

②CLOUD SPEECH API

cloud.google.com
 
音声を認識してテキスト化する。
f:id:pikesaku:20170723165329p:plain
 

③CLOUD TRANSLATION API

 
Google翻訳API
 

④CLOUD VISION API

cloud.google.com
 
画像認識機能を提供
f:id:pikesaku:20170723165914p:plain
 
Safe Searchタブには、アダルト度や暴力表現度のスコア情報も出力される。
 

⑤CLOUD VIDEO INTELLIGENCE

cloud.google.com
 
動画認識機能を提供
f:id:pikesaku:20170723170942p:plain
 
動画で表示された場面に写っているものを認識しラベル付けしてくれる。
 

カスタマイズ用

 

Tensor Flow

 
完全オープンソース化。自由に使ってよし。
Deep Learningフレームワークデファクトスタンダード
HWベンダーも含めたエコシステムが構成されてる。
数学知らなくても利用可能
CPUでも動作可能だが、活用するにはGPUが必要。
 

②CLOUD MACHINE LEARNING ENGINE

 
Tensor Flow実行環境をクラウドで提供。
GPU環境を1時間100円程度で利用可能。
 

学習モデルの活用

 
モデルは数百MB程度のサイズ
モバイルなどに搭載可能。オフラインでも利用可能。

機械学習関係の勉強会メモ②

Deep Learning(以降DLと記載)の代表的な使われ方

 
①画像認識
自然言語処理
音声認識
 
全ての用途においてDLがベストではない。
 

Microsoft Cognitive Services

 
画像分析、音声認識、レコメンデーション等のAPIツールを提供
デモとしてCaptionBotがあり。
 
CaptionBot - For pictures worth the thousand words
 
画像をアップロードすると、それが何であるか認識した結果を教えてくれる。
 
例)はりねずみの写真をアップロードした場合
f:id:pikesaku:20170723125023p:plain
 

Tensor Flow

 
Tensor Flowを理解するには、playgroundが有用
playground.tensorflow.org
 
ニューラルネットワークの仕組みを理解するための教育コンテンツ
 

簡単なデータセットの分類動作

f:id:pikesaku:20170723134213p:plain
左上のデータセット
 
デフォルトの設定のままでも、実行すれば精度の高い学習モデルはすぐできる。
 
実行前
f:id:pikesaku:20170723134025p:plain
 
実行後
f:id:pikesaku:20170723134043p:plain
 

複雑なデータセットの分類動作

右下のデータセット
f:id:pikesaku:20170723134954p:plain
  
デフォルトの設定では、時間がたっても
入力・ニューロン・レイヤを追加する必要あり。
 
実行前
f:id:pikesaku:20170723134705p:plain
 
実行後
f:id:pikesaku:20170723135201p:plain
 
Activationでは活性関数を指定
レイヤやニューロンは多ければ良いわけではない。
できるだけシンプルな構成で精度を上げるのが良し。
 

おすすめのDLフレームワークは?


使いやすいのが一番では?
 

DLの処理について

多層パーセプトロン 入力・出力サイズが固定
RNN 入力が可変のもを扱う。音声・時系列データ等
CNN 画像処理で使われる

 

関連キーワード

 
言語処理=>encodecモデル→RNN→Seq2Seq
Sequence-to-Sequence(Seq2Seq)学習は、任意長の入力列から任意長の出力列を出力するような学習のこと
 
chainernlpman.hatenablog.com
 

Deep Learningの実装はコーディングするべきか?

 
そうでもない。GithubにDL実装プログラムがあり。
言語処理の場合、OpenNMTがオススメ。コードを書かずモデル作成が可能。
 
DL実装ではバグ取りが大変。そもそも結果が何故そうなるかわからないのに、バグかどうかの判定も困難。
Githubソースを使う時の注意点はバグが有る点。信頼性の高いものを使うべき。
最近Tensor FlowがOpenNMTみたいなサービスを公開したらしい。。。
 

OpenNMTについて

 
必要なものはデータだけ。
質問&回答を記述した2つのファイルが必要。(2ファイル間で質問と回答が1行毎に対応している必要あり。)
質問・回答セットは万単位は必要
 

Deep Learning取り組みの現状

 
経験則が大きい。何故それが正しいかは、説明できないケースが多い。
ビジネス活用する時は、簡易PoCをして結果が出たものを使うべき。結果が出ない場合は、ニューラルネットをいくら調整しても無理な場合が多い。 
汎用人工知能→専門家の間では使われない言葉。SF映画レベルの色々できる人工知能はまだまだ実現できないとの感想。現在の人工知能はタスク特化。それらを組み合わせた結果、汎用的に見えるものもあるが、あくまで複合技術であり、カバーできる範囲は少ない。
 

Deep Learningに取り組む時の数学知識の取得について

 
Playgroundが理解できる程度でOK。(これだけでも難しいそうなのだが。。。。。) 
統計学の習得は不要。
数学知識の習得は必要に応じてやるのが効率良し。
 

OpenNMT検証メモ

  
OpenNMT - Open-Source Neural Machine Translation
 

環境

Ubuntu 16.04.2 LTS
最小インストー
言語・ロケールは日本語選択
 

環境構築手順

 
Installation - OpenNMT
 
上記のDocker利用方式でやってみる。
 
Ubuntuにopenssh-serverインストール&起動(Ubuntuは普段使わない。最小だとインストールされなかった)
$ sudo atp-get install openssh-server
$ systemctl start sshd
 
$ wget -P /tmp https://github.com/NVIDIA/nvidia-docker/releases/download/v1.0.1/nvidia-docker_1.0.1-1_amd64.deb
$ sudo dpkg -i /tmp/nvidia-docker*.deb && rm /tmp/nvidia-docker*.deb

$ sudo apt-add-repository 'deb https://apt.dockerproject.org/repo ubuntu-xenial main'
$ sudo apt-get update
$ vim /etc/apt/sources.list
以下行を最後に追加
deb http://kr.archive.ubuntu.com/ubuntu trusty main
$ sudo apt-get install libltdl7 docker-engine
$ sudo apt-get install nvidia-modprobe
$ sudo dpkg -i /tmp/nvidia-docker*.deb
 
※URLの手順だとlibltdl7が依存関係でインストールされない問題があった為、上記手順で実施。nvidia-dcockerも最新がリリースされていた為、wgetのパス変更。
 
$ sudo nvidia-docker run -it harvardnlp/opennmt:8.0



 

機械学習関係の勉強会メモ①

人工知能キーワード

 
f:id:pikesaku:20170723123119p:plain
 
機械学習=Deep Learningではない。
人工知能の明確な定義はなし。言ったもん勝ち
 

機械学習イメージ

f:id:pikesaku:20170723112242p:plain
 

機械学習で必要な数学を学ぶためのオススメ参考書

 
http://www.mathema.jp/books/zemi_math.html
微分積分キャンパス・ゼミ
線形代数キャンパス・ゼミ
統計学キャンパス・ゼミ
 

機械学習で利用される数学アルゴリズムには2つの流派があり

 

流派 難易度 抽象度
線形代数ベース 比較的具体的 SVM, DeepLearning, 重回帰分析
確率統計ベース MCMC, HMM, ベイズ統計(超難)

 
どちらでも、基本的に同じことが実現できる。
違いは確率統計ベースの方が、不確実性まで予測できる点
例) 家賃予測の場合
線形代数ベース→〜万
線形代数ベース→〜%の確率で〜万
 
機械学習は学習で挫折しない為に、まず線形代数ベースから取り組むべき。
機械学習で必要な数学知識を身につけるには、以下セミナーが有用
www.kikagaku.co.jp
 

機械学習における予測の考え方

 
f:id:pikesaku:20170723114643p:plain
 
上記のデータが学習済みの場合、、、
3が入力された場合の予測を内挿
5が入力された場合の予測を外挿
 
と言う。機械学習の予測は、基本的に内挿で行うべき。
外挿は予測結果が精度は低くなる。
 
基本的には与えたデータの範囲で予測すべき
 

学習データに異常データがあった場合の対処

除外する
他データの平均値で補完
 

機械学習の現状

 
経験則&勘で行われている。
何故、この方法がいいかは?は解明されないケースが多い。
実際にデータを学習し結果を見る必要があり。
 

Deep Learning(以降DLと記載)と、それ以外の機械学習の違い

 
f:id:pikesaku:20170723120313p:plain
 
DLは特徴量も自動抽出するのが特徴。
必ずしも、DLを採用すべきではない。扱うデータにより採用すべきアルゴリズムは変わる。
 
例)画像認識の場合
従来は、グレースケール処理(モノクロ変換)等、特徴量抽出の為の操作が必要であったが、DLの場合は、これらが不要
 

機械学習カテゴリ

 

教師あり 入力と答えのセットで学習
教師なし クラスタリング(顧客データの属性分け)、次元削減(大量データから密度の高い部分のみ抽出・主成分分析とも言う)
強化学習 思考錯誤を通じ、「価値を最大化するような行動」を学習。例)ルンバ

 
強化学習=DLではない。
強化学習については以下書籍に良い解説あり。
https://www.amazon.co.jp/実装-ディープラーニング-藤田一弥/dp/4274219992
 

データサイエンスとデータエンジニアリングの違い

 
データサイエンス→研究・数学
データエンジニアリング→プログラミング 
 

今オススメのDeep Learningフレームワークは?

  
Chainner or Tensor Flow
 
Chainnerは少し難しいことやろうとすると数学知識や深い知識が必要になる&技術情報は英語を読む必要あり。
Tensor Flowはネットに技術情報が多くある。今最も主流。
 
Tensor Flowをやるのが無難そう。
Chainnerを理解してれば、Tensor Flowへの転向は容易。