pikesaku’s blog

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

Jubatusチュートリアルメモ

Classifierチュートリアル

jubaclassifierを使って入力データの分類を行う

サンプルプログラムの内容

昔の将軍の名前を入力にして名字をあてる。
例)
入力) 家康→ 出力) 徳川
入力) 尊氏→ 出力) 足利  

Recommender チュートリアル

jubarecommenderを使って類似するデータを推薦する
※jubarecommenderはECサイト商品お勧めなどに利用することができる

サンプルプログラムの内容

プロ野球選手の野手成績を学習し似たタイプ(成績)を推薦する
例)
入力) 中田翔→ 出力) 井口資仁新井貴浩 中村紀洋

Anomaly チュートリアル

jubaanomalyを使って外れ値検知をする
LOFを使う
LOFはN次元空間で、近くにある点がどの程度あるか調べて、外れ値を検知
不正検知、障害検知に利用可能

サンプルプログラムの内容

Regression チュートリアル

jubaregressionを使って線形回帰機能(Regression)を提供する
線形回帰機能は、入力データから出力データを推定する機能
株価予測や消費電力に利用できる

サンプルプログラムの内容

賃貸物件の情報(駅からの距離、面積、年数等)から家賃を推測する

Graph チュートリアル

jubagraphを使ってグラフマイニング機能(Graph)を提供する
グラフマイニング機能は与えられたグラフ構造から中心点や最短距離を抽出する機能
ソーシャルコミュニティ分析やネットワーク構造分析に利用可能

サンプルプログラムの内容

鉄道路線の最短経路を推定するプログラム
例)
入力) 品川・お茶ノ水 出力) 品川→東京→お茶ノ水(最短距離経路)

※山手線と中央線の接続を示すグラフを作成して、上記を実現

Stat チュートリアル

jubastatを使って統計分析機能(Stat)を提供
統計分析機能は時系列データのウィンドウ設定つき分析機能であり、センサー監視や異常データ検知などに利用可能

サンプルプログラムの内容

オレンジ・りんご・メロンの直径・重さ・価格を学習し、フルーツ毎にパラメータの合計値や標準偏差など統計分析をする

Jubatusチュートリアルをやる!

* 参考URL

チュートリアル — Jubatus

チュートリアルプログラムの概要

自然言語の分類をする
・評価用データとしてNews 20を使う
 Home Page for 20 Newsgroups Data Set
・News 20は自然言語分類の評価用データ
 80%が学習用データ
 20%がテスト用データ
・News 20はニュースグループ。20個のグループがあり、色々な人がメッセージを投稿している。
チュートリアルプログラムは、学習用データを読み込んで、テスト用データとしてメッセージを読み込み、どのグループの投稿データかを推測する。
・自分の環境では、正答率は1回目71%、2回目74%、3回目75%、4回目75%、

メモ

・分類機能があるjubaclassifierを利用
・設定ファイルを引数にしてjubaclassifierをjubatusサーバで実行する
・設定ファイルの主な指定項目は以下。
 method アルゴリズムを指定。パーセプトロン
 converter 入力データの特徴ベクトルへの変換方法を指定
 parameter 今回は指定なし
・クライアントが学習用データとテスト用データを投げ込んで、テスト結果を得る
・trainメソッドで学習
・classifyメソッドでテスト

PostfixのSMTP認証に認証ユーザー単位の接続元NW制限を追加する方法

PostfixのPolicyサービスを使う

Polixyサービスとは?

独自プログラムを呼び出して細かいアクセス制御を実現する機能
独自プログラムは、Postfixが呼び出し時に提供する属性情報(SASL認証ユーザー名等)を利用してリレー判定結果を返すだけ
→入力データはPostfix経由時に精査されて、プログラムに渡される為、簡単&安心に使える感じ!
Postfixパッケージにサンプルプログラムがあり。
メールデータをいじらない為、メールデータロストのリスクは少ない(?)

今回実装したPolicyサービスの動き

①クライアントがRCPTコマンドを実行

Postfixが標準入力で以下の情報をプログラムに渡す
例)

request=smtpd_access_policy
protocol_state=RCPT
protocol_name=SMTP
helo_name=some.domain.tld
queue_id=8045F2AB23
sender=foo@bar.tld
recipient=bar@foo.tld
client_address=1.2.3.4
client_name=another.domain.tld
instance=123.456.7
sasl_method=plain
sasl_username=you
sasl_sender=
ccert_subject=solaris9.porcupine.org
ccert_issuer=Wietse Venema
ccert_fingerprint=C2:9D:F4:87:71:73:73:D9:18:E7:C2:F3:C1:DA:6E:04
size=12345
[empty line]

③プログラムはPostfixから渡される以下情報を利用してリレー判定をする。

接続元IP(client_address)
SMTP認証ユーザー情報(sasl_username)

接続元IPの情報を、DBに問い合わせて接続を許可するNWか判定する。

④プログラムは判定結果を標準出力に以下のデータを返す。

リレー拒否する場合

action=reject ERROR MESSAGE
[empty line]

リレー拒否しない場合

actions=dunno
[empty line]

actions=には、Postfixaccessテーブルで利用可能なアクションが設定できる。
Postfix manual - access(5)

dunnoは、テーブルにマッチするものがない場合と同じ動き。
こうすれば、smtpd_recipient_restrictionsで定義した以降のフィルタも評価される。

前提

ユーザー毎に接続を許可する接続元NWをDBに入れておく。

テスト環境構築

main.cf

smtpd_recipient_restrictions = check_policy_service unix:private/policy, permit_sasl_authenticated, reject_unauth_destination
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes
smtpd_sasl_local_domain = $myhostname
smtp_sasl_path = smtpd

master.cf

policy unix - n n - - spawn user=nobody argv=/bin/python /usr/libexec/postfix/smtpd-policy-chk.py

/usr/libexec/postfix/smtpd-policy-chk.py

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import MySQLdb
import ipaddress
import syslog
import time


def output_log(mes):
    syslog.openlog()
    syslog.syslog("DEBUG: " + mes)
    syslog.closelog()


def get_attr():
    attr = dict()
    while True:
        ent = raw_input()
        if ent:
            if "=" in ent:
                k = ent.split("=")[0]
                v = ent.split("=")[1]
                attr[k] = v
                output_log("Input: " + ent)
            else:
                output_log("Invalid entry: " + ent)
        else:
            output_log("Fin input" + ent)
            break
    return attr


def chk_relay(client_address, sasl_username):
    connector = MySQLdb.connect(host="localhost", db="testdb", user="testuser", passwd="password", charset="utf8")
    cursor = connector.cursor()
    sql = "select network from access where user = '" + sasl_username + "'"
    cursor.execute(sql)
    result = cursor.fetchall()
    cursor.close()
    connector.close()

    if result:
        network = result[0][0]
        nw = ipaddress.ip_network(network)
        ip = ipaddress.ip_address(unicode(client_address, "utf-8"))
        if ip in nw:
            return "relay"
        else:
            return "no_relay"
    else:
        return "no_relay"


def main():
    attr = get_attr()
    if attr.has_key("client_address") and attr.has_key("sasl_username"):
        client_address = attr["client_address"]
        sasl_username = attr["sasl_username"].split("@")[0]
        if client_address and sasl_username:
            ret = chk_relay(client_address, sasl_username)
            if ret == "relay":
                output_log("Result 1")
                sys.stdout.write("action=dunno\n\n")
            else:
                output_log("Result 2")
                sys.stdout.write("action=reject Dameyo\n\n")
        else:
            output_log("Result 3")
            sys.stdout.write("action=dunno\n\n")
    else:
        output_log("Result 4")
        sys.stdout.write("action=dunno\n\n")
    exit()

if __name__ == '__main__':
    main()

テストデータ

mysql
> create database testdb;
> grant all on testdb.* to testuser@localhost;
> flush privileges;
> set password for testuser@localhost=password('password');
> use testdb
> create table access (user VARCHAR(32), network VARCHAR(64));
> insert into access (user, network) values("test1", "127.0.0.1/32");
> insert into access (user, network) values("test2", "172.31.27.95/32");
> create table user (user VARCHAR(32), password VARCHAR(64), maildir VARCHAR(64));
> insert into user (user, password) values("test1", "password");
> insert into user (user, password) values("test2", "password");
> select * from user;
+-------+----------+---------+
| user  | password | maildir |
+-------+----------+---------+
| test1 | password | NULL    |
| test2 | password | NULL    |
+-------+----------+---------+
2 rows in set (0.00 sec)

> select * from access;                                                                        
+-------+-----------------+
| user  | network         |
+-------+-----------------+
| test1 | 127.0.0.1/32    |
| test2 | 172.31.27.95/32 |
+-------+-----------------+
2 rows in set (0.00 sec)

動作結果

SMTP認証に成功しただけでは、リレー許可されない。
DBに接続元NWが登録されている必要がある。

最後に

上記のプログラムは簡単に動作確認するだけのもので、細かい点はあまり考慮してません。

シグモイド関数の微分を理解する

ここにたどり着くまでに、微分対数を勉強する必要があった。。。とっても疲れた。

シグモイド関数とは?

ニューラルネットワーク

或る細胞の内部状態を出力値に変換する関数として、シグモイド関数がよく使われる。入力値の絶対値がいくら大きくても出力値は0~1の範囲に収まり、細胞に近い反応をする関数と言える。入力値が負ならば0.5以下、正ならば0.5以上の出力値となる。

http://www.sist.ac.jp/~kanakubo/research/neuro/neuro2.png

シグモイド関数微分が、ニューラルネットワークで利用される。

シグモイド関数の式

広義

           1
f(x) = -----------
       1 + e^(-ax)

aをゲインと呼ぶ。ゲインによる傾きに変換を与える事が可能


狭義

           1
f(x) = -----------
       1 + e^(-x)

ゲインは1。

シグモイド関数微分

微分の中に関数自体が含まれるのが特徴

微分の公式

f'(x) = (1 -f(x))f(x)

確かに微分の中に、関数自体( f(x) )が含まれている。

微分の公式の証明

手作業で微分してみる。

①スタート

           1
f(x) = -----------
       1 + e^(-x)


②指数法則を使い展開

f(x) = (1 + e^(-x))^-1

微分しやすくするため、関数を合成関数の形態に展開。

1) 以下の定義をする。
u = 1 + e^(-x)

2) uを使って、元の式を表記。合成関数の表記になる
f(u) = u^(-1)

合成関数の微分は、2つの関数の微分を掛け合わせたものになる。

④関数uを微分する。

公式: (x^n)' = nx^(n-1)

f(u) = u^(-1)
→右辺を公式に従い展開すると微分になる

f'(u) = -1 * u^(-1-1) = -u^(-2)

⑤新たに定義した関数uの微分をする。

以下3つの公式を使う
a) (a^x)' = a^x * loge(a)
b) (e^x)' = e^x
c) (ax)' = a

u = 1 + e^(-x)

・1は定数のため微分するとゼロになる
・eはネイピア数

ここでも合成関数の微分を使う
-xを関数vとして以下の合成関数にする。

v = -x
u = e^v

関数vは、cの公式で微分して以下
(v)' = -1

関数e^vは、bの公式で微分して以下
(e^v)' = e^v

よって合成関数の微分は、以下
(u)' = -1 * e^v
(u)' = -1 * e^(-x)
(u)' = -e^(-x)

⑥これで最初の以下の合成関数の微分をする

f'(x) = -u^(-2) * -e^(-x)
f'(x) = u^(-2) * e^(-x)

uを展開

f'(x) = (1 + e^(-x))^(-2) * e^(-x)

↓

             e^(-x)
f'(x) = ---------------
        (1 + e^(-x))^2

↓

          e^(-x)        1
f'(x) = ---------- * ----------
        1 + e^(-x)   1 + e^(-x)


ここで左側部分を以下に変形する。
  e^(-x)     1 + e^(-x)       1
---------- = ---------- - ----------
1 + e^(-x)   1 + e^(-x)   1 + e^(-x)


↓

         1 + e^(-x)       1              1
f'(x) =( ---------- - ---------- ) * ----------
         1 + e^(-x)   1 + e^(-x)     1 + e^(-x)

ここで、最初の関数表記を代入すると
           1
f(x) = -----------
       1 + e^(-x)

↓

やっとこさ、以下の形になる!

f'(x) =(1 - f(x)) * f(x)     

分かったこと!

自分の頭では数学の理解はムリ!大人しく公式を覚えて活用できればいい!
シグモイド関数は、微分に元の関数が含まれる!
数式を色々展開すると、色々な姿、関係性が見えてくる。これが数学の楽しさ?

自然対数とは?

ネイピア数eとセットで用いられる対数

そもそもネイピア数とは?

・eと表記される。

・円周率や黄金比と同じく無理数

以下式で表わされる

           1
lim  (1 + ---)^n
n→∞        n

上記式の結果は、上限があり収束する。上限は、2.7182818......。これがネイピア数

・使い道が分かりづらいが数学乗では重要な定数らしい。一定範囲の中で無限に数扱う事ができる & 対数計算で利用ができることから、無限に細かい計算ができるのがメリット。

改めて、自然対数とは?

底がネイピア数である対数

→上記よりネイピア数は自然対数の底ともいう。

自然対数の式は以下の通り。

まずネイピア数は以下式で表わされる、、、

           1
lim  (1 + ---)^n = e
n→∞        n

この式の1/nの1を任意の数rにした場合、以下の式が成り立つ

           r
lim  (1 + ---)^n = e^r
n→∞        n

この証明は参考URLを参照

上記式を対数表記にすると、、、、

左辺をxにする。

x = e^r
r = loge(x)

ネイピア数(無理数)を底とした対数
→これを自然対数と呼ぶ

相乗平均と相加平均の違い

参考

【対数】インデックス | 大人が学び直す数学

相加平均とは?

和の平均。算術平均と呼ばれる。

例) 試験の平均点等

相乗平均とは?

積の平均。幾何平均とも言われる。

積の平均。対象データを乗じた値の累乗根が相乗平均になる

例) 複利計算

http://www.globis.jp/1515

n√x1 * x2 * xn 

※上記のx1~2は数列

ポイント

必ず以下になる
算術平均 > 相乗平均

指数関数と対数関数について

ポイント

対数関数の式は以下の通り

y = log2(x)

グラフの特徴(グラフの形は参考URLを参照)
・xは0より大きな値になる。
 →2乗のため真数はマイナスにならない。
 →log2(0)は成立しない。
・右肩上がり

指数関数の式は以下の通り

y = 2^x

グラフの特徴(グラフの形は参考URLを参照)
・yは0より大きな値になる。
 →x乗のためyはマイナスにならない。
・右肩上がり

対数関数と指数関数は「逆関数

以下は同じことを意味する。

y = log2(x)
x = 2^y

逆関数のグラフは、y=xのグラフを基準とすると対象的な値を取る。

詳細は参考URLを見ること。

逆関数の表記

y = f(x)の場合

y = f(^-1)(x)

※-1は指数ではない

指数関数の微分の公式

証明方法は参考URL参照

(a^x)' = a^x * loge(a)

対数関数の微分の公式

証明方法は参考URL参照

                  1
(loga(x))' = -----------
             x * loge(a)

感想

指数関数の微分は不思議だらけ。
ただ証明できる事から、そういう性質を持っていると考えよう。
以下を覚えておく。
・指数や対数関数の微分に処理の過程にeが潜んでいる
・定数aがeであった場合に、微分結果がシンプルに変化する点
・公式があるってこと