読者です 読者をやめる 読者になる 読者になる

Screaming Loud

研究・プログラミングなど気づいたことをメモをしています

パーセプトロンをPythonでかいてみた.

python 機械学習 プログラミング

機械学習の初歩ということでパーセプトロンを実装してみました.
パーセプトロンとは2値識別器です.

クラスは1イテレーションを表しています.
2次元以上でも計算できるようにしました.

import sys
from optparse import OptionParser
import matplotlib as mpl
mpl.use('PDF')
import matplotlib.pyplot as plt
import numpy
import random

def get_train_data(filename):
    '''make dictionary from training data'''
    train = []
    with open(filename) as f:
	for line in f:
	    line = line.split()
	    train.append((line))
    return train

def plot_point(train_data):
    '''plot points with color'''
    for coord in train_data:
	if int(coord[-1])==1:
	    c = 'b'
	else:
	    c = 'r'
	plt.scatter(float(coord[0]),float(coord[1]),color=c)

def plot_line(weight,bias):
    '''plot result line'''
    x = [-1.0,1.0] # from -1.0 to 1.0
    y = [-weight[0]*xi/weight[1]+bias/weight[1] for xi in x]
    plt.plot(x,y)

def make_train_data(N):
    '''make random training data'''
    w0 = numpy.array([-0.2, 0.5, -1])
    x1, x2 = -1 + numpy.random.rand(N)*2, -1 + numpy.random.rand(N)*2
    t = (w0[0]+x1*w0[1]+x2*w0[2]>=0) * 2 - 1
    return x1,x2,t

class Perceptron(object):
    '''Perceptron class learns one iteration.
    when you call this function learning(), it complete one iteration
    for training dataset.'''
    def __init__(self,train_data,eta,bias):
	'''train_data is x which is coords'''
	dimention = len(train_data[0])-1
	self.weight = [0.0 for i in range(dimention)]
	self.bias = bias
	self.eta = eta

    def activation(self,coord):
	'''this is an activation function.
	so it returns positive or negative'''
	summation = sum(map((lambda x,y: float(x)*y),coord[:-1],self.weight)) - self.bias
	if summation>=0:
	    self.output = 1
	else:
	    self.output = -1

    def calc_value(self,coord):
	self.value = float(coord[-1])-self.output

    def update_weights(self,coord):
	'''update weight parameters '''
	new_weight = []
	for i,w in enumerate(self.weight):
	    va = w+self.eta*(self.value)*float(coord[i])
	    new_weight.append(va)
	self.weight = new_weight

    def update_bias(self):
	'''update bias parameter'''
	self.bias -= self.eta*(self.value)

    def learning(self):
	'''learning parameters'''
	for coord in train_data:
	    self.activation(coord)
	    self.calc_value(coord)
	    self.update_weights(coord)
	    self.update_bias()

if __name__ == '__main__':
    usage = 'usage: %prog FILE [options]'
    parser = OptionParser(usage)
    parser.add_option('--iter',dest='iter',type='int',default = 16)
    parser.add_option('-b',dest='bias',type='float',default = 0.0)
    parser.add_option('-e',dest='eta',type='float',default = 1.0)
    parser.add_option('-f','--file',dest='filename',default = '')
    (options,args) = parser.parse_args()

    if options.filename=="":
	m = make_train_data(50)
	train_data = zip(m[0],m[1],m[2])
    else:
	train_data = get_train_data(options.filename)
    PR = Perceptron(train_data,options.eta,options.bias)
    for i in range(options.iter*len(train_data)):
	PR.learning()
    print PR.weight,PR.bias
    plot_point(train_data)
    plot_line(PR.weight,PR.bias)
    plt.savefig('output')

ランダムサンプルの出力結果はこんな感じ
f:id:yuutookun:20120314005624p:image

eta:学習率
bias:PDFで言うθ
weight:これはそのまま重み

  • グラフによる直感的な意味
    • weightは原点を中心とした向きを表している.従って,グラフでは重みを変えると回転するイメージ
    • biasは原点からの距離を表している.従ってグラフではbiasを変えると平行移動する.
    • 学習率は1回の学習でどれくらいパラメータを変えるかの度合い.小さければパラメータの更新も少ない.

またORの入力だったら

0 0 -1
0 1 1
1 0 1
1 1 1

こういうテキストファイルを読み込ませても出力出来る。
結果は以下
f:id:yuutookun:20120314120505p:image

参考文献
パーセプトロンのPDF
パーセプトロンとNNのコード書いてみた - きちめも

他にもテキストマイニングのための機械学習超入門 二夜目 パーセプトロン - あんちべ!などで詳しく解説されてます.