MATHGRAM

主に数学とプログラミング、時々趣味について。

python: オンライン学習-Passive Aggressive-を実装してみた

こんにちは、また久しぶりの投稿になってしまいました。

久しぶりということでtipsとかではなく、ちゃんとした記事書きます。

初めて、理論の本から実装したので、まだまだ甘い部分が目立ちますが許してください。

※3/25 追記

ket-30.hatenablog.com
こっちに今回のコードを改善しつつCWも実装しています。
こっちの記事は結構良くない部分が目立つので、ぜひ参考にしてくださるのであれば、こちらを見てみてください。(むしろご指摘待ってます。)

オンライン学習

まずオンライン学習について、軽く。
まあ僕なんかの説明より本読んだ方が早いっすけど。笑

オンライン学習とは、機械学習の手法の1つです。

通常の機械学習では、データセットを全て舐めて重みのパラメータ調整を行なっていきます。

それに比べてオンライン学習は、1つのデータだけで学習行うという手法です。

1つのデータを読み込み学習する。これを繰り返して重みを調整していくということですね。

メリット

  1. 1つずつデータを読み込むので、膨大なデータセットに適している。
  2. 将来的にもデータが手に入ることがわかっているならば、成長する判別器的なものを作れる。
  3. アルゴリズムが引くほど簡単

デメリット

  1. 後半に同じラベルのデータを繰り返し読み込んでしまうと、そのラベルに学習が偏ってしまう。

個人的にメリットの方が大きいと思います。
実際には、twitterやyahooなど、毎日大量のデータが手に入る会社が使っているようです。

Passive Aggresssive

Passive Aggressive(以下PA) は、オンライン学習のアルゴリズムの一つで基本となっているもの?らしいです。
まあ古いアルゴリズムなんですかね。

詳しい理論は他サイトや本で確認してください。
理論書いていくのホントめんどい笑
すみません。

実装

pythonコードはこちら。

#coding:utf-8

import numpy as np

class PassiveAggressive:
    def __init__(self, feat_dem=None):#feat_dem="特徴量の次元数"
        self.t = 0
        if feat_dem is not None:
            self.w = np.ones(feat_dem+1)
        else: self.w = None

        self.feat_dem = feat_dem
    #--------クラス内で使うメソッド-----------

    def L_hinge(self,feats, y):
        loss = max([0, 1-y*self.w.dot(feats)])
        return loss

    def update(self, feats, y):
        loss = self.L_hinge(feats,y)
        norm = feats.dot(feats)
        eta = loss/norm
        #重み更新
        #loss=0だったらeta=0
        self.w += eta*y*feats
        self.t += 1#更新回数


    #---------呼び出すメソッド-------------

    def fit(self, feats_vec, y_vec):
        if self.w is None:
            weight_dem = len(feats_vc)
            #全ての要素(引数が次元)が1になるベクトルを作成する
            self.w = np.ones(weight_em)
        self.update(feats_vec, y_vec)


    def predict(self, feats):
         pred = self.w.dot(feats)
         return 1 if pred > 0 else -1

このモジュールを使って、次を実行させるとオンライン学習ちっくなことができます。

#coding:utf-8

import numpy as np
import pandas as pd
import matplotlib.pyplot  as plt
from passive_aggressive import PassiveAggressive

plt.style.use('ggplot')

#create datas
def make_data(N, draw_plot=True, is_confused=False, confuse_bin=50):
    #N個のデータセットを作成する
    #データにノイズをかけるためis_confusedを実装する

    np.random.seed(1)#シードを固定すると乱数が毎回同じ出力になる

    feature = np.random.randn(N, 2)#N*2の行列を作成
    df = pd.DataFrame(feature, columns=["x","y"])

    #二値分類の付与: 人為的な分離戦の上下何方にいるかで機械的に判定
    #ここの値がラベルになる
    df["c"] = df.apply(lambda row : 1 if (5*row.x + 3*row.y -1)>0 else -1, axis=1)

    #データの攪乱
    if is_confused:
        def get_model_confused(data):
            c = 1 if (data.name % confuse_bin) == 0 else data.c
            return c

            df["c"] = apply(get_model_confused, axis=1)

    #データの可視化: どんな感じのデータになったかをグラフ化する
    if draw_plot:
        plt.scatter(x=df.x, y=df.y, c=df.c, alpha=0.6)#cはカラーにあたる。
        plt.xlim([df.x.min() - 0.1, df.x.max() + 0.1])
        plt.ylim([df.y.min() - 0.1, df.y.max() + 0.1])

    return df

def draw_split_line(weight_vector):

    a,b,c = weight_vector
    x = np.array(range(-10,10,1))
    y = (a * x + c)/-b
    plt.plot(x,y, alpha=0.3)

#データセットの作成
dataset = make_data(1000)
print(dataset.head(5))
feats_vec = dataset.ix[:,"x":"y"]
feats_vec["b"] = np.ones(dataset.shape[0])
feats_vec = feats_vec.as_matrix()
print(feats_vec)
y_vec = dataset.ix[:,2]
y_vec = y_vec.as_matrix()

model = PassiveAggressive()
#引数は特徴量の次元数
#引数がない場合は勝手に次元数読み込みます。

#オンライン学習

for i in range(len(y_vec)):
    model.fit(feats_vec[i,],y_vec[i])

draw_split_line(model.w)
print(model.w)

plt.show()

ちなみに

from passive_aggressive import PassiveAggressive

で先ほどのモジュールを読み込んでいます。

学習結果

実行結果はこんな感じ。

f:id:ket-30:20160311122039p:plain:w500:h400

若干ずれていますが、学習できていますね。
初めてにしては上出来...でしょ!!(自分に甘くしていくスタイル)

p.s. いやいやちゃんと理論かけよ自分...


参考サイト様
実装して理解するオンライン学習器(1) - PassiveAggresive - About connecting the dots.
統計の素人だけどPythonで機会学習モデルを実装したい、そんな人のための第一歩 - Qiita