Screaming Loud

日々是精進

パーセプトロンを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のコード書いてみた - きちめも

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