パーセプトロン学習

<?php

/***********************************/
/* パーセプトロン学習              */
/* (Pocket Algorith with Ratcet) */
/*      coded by Y.Suganuma        */
/***********************************/

/**************************/
/* Perceptronクラスの定義 */
/**************************/
class Perceptron {
	private $max;   // 最大学習回数
	private $n;   // 訓練例の数
	private $p;   // 入力セルの数
	private $W_p;   // 重み(ポケット)
	private $W;   // 重み
	private $E;   // 訓練例
	private $C;   // 各訓練例に対する正しい出力
	
	/******************/
	/* コンストラクタ */
	/******************/
	function Perceptron($max_i, $n_i, $p_i)
	{
	/*
	     設定
	*/
		$this->max = $max_i;
		$this->n   = $n_i;
		$this->p   = $p_i;
	
		mt_srand();
	/*
	     領域の確保
	*/
		$this->E = array($this->n);
		for ($i1 = 0; $i1 < $this->n; $i1++)
			$this->E[$i1] = array($this->p+1);
	
		$this->W_p = array($this->p+1);
		$this->W   = array($this->p+1);
		$this->C   = array($this->n);
	}
	
	/******************************************/
	/* 訓練例の分類                           */
	/*      return : 正しく分類した訓練例の数 */
	/******************************************/
	function Bunrui()
	{
		$num = 0;
	
		for ($i1 = 0; $i1 < $this->n; $i1++) {
			$s = 0;
			for ($i2 = 0; $i2 <= $this->p; $i2++)
				$s += $this->W[$i2] * $this->E[$i1][$i2];
			if (($s > 0 && $this->C[$i1] > 0) || ($s < 0 && $this->C[$i1] < 0))
				$num++;
		}
	
		return $num;
	}
	
	/**************************/
	/* 学習データの読み込み   */
	/*      name : ファイル名 */
	/**************************/
	function Input($name)
	{
		$st = fopen($name, "r");
	
		fgets($st);
	
		for ($i1 = 0; $i1 < $this->n; $i1++) {
			$this->E[$i1][0] = 1;
			$str             = trim(fgets($st));
			$this->E[$i1][1] = intval(strtok($str, " "));
			for ($i2 = 2; $i2 <= $this->p; $i2++)
				$this->E[$i1][$i2] = intval(strtok(" "));
			$this->C[$i1] = intval(strtok(" "));
		}
	
		fclose($st);
	}
	
	/*********************************/
	/* 学習と結果の出力              */
	/*      pr : =0 : 画面に出力     */
	/*           =1 : ファイルに出力 */
	/*      name : 出力ファイル名    */
	/*********************************/
	function Learn($pr, $name = "STDOUT")
	{
		$n_tri = $this->Pocket($num);
	
		if ($pr == 0)
			$out = STDOUT;
		else
			$out = fopen($name, "wb");
	
		fwrite($out, "重み\n");
		$str = "";
		for ($i1 = 0; $i1 <= $this->p; $i1++)
			$str = $str.$this->W_p[$i1]." ";
		fwrite($out, $str."\n");
	
		fwrite($out,"分類結果\n");
		for ($i1 = 0; $i1 < $this->n; $i1++) {
			$s  = 0;
			for ($i2 = 0; $i2 <= $this->p; $i2++)
				$s += $this->E[$i1][$i2] * $this->W_p[$i2];
			if ($s > 0)
				$s = 1;
			else
				$s = ($s < 0) ? -1 : 0;
			$str = "";
			for ($i2 = 1; $i2 <= $this->p; $i2++)
				$str = $str.$this->E[$i1][$i2]." ";
			$str = $str."Cor ".$this->C[$i1]." Res ".$s;
			fwrite($out, $str."\n");
		}
	
		if ($this->n == $num)
			printf("  !!すべてを分類(試行回数:%ld)\n", $n_tri);
		else
			printf("  !!%ld 個を分類\n", $num);
	}
	
	/********************************************/
	/* Pocket Algorith with Ratcet              */
	/*      num_p : 正しく分類した訓練例の数    */
	/*      return : =0 : 最大学習回数          */
	/*               >0  : すべてを分類(回数) */
	/********************************************/
	function Pocket(&$num_p)
	{
	/*
	     初期設定
	*/
		$count = 0;
		$run   = 0;
		$run_p = 0;
		$sw    = -1;
		$num_p = 0;
	
		for ($i1 = 0; $i1 <= $this->p; $i1++)
			$this->W[$i1] = 0;
	/*
	     実行
	*/
		while ($sw < 0) {
	
			$count++;
			if ($count > $this->max)
				$sw = 0;
	
			else {
						// 訓練例の選択
				$k = intval(mt_rand() / mt_getrandmax() * $this->n);
				if ($k >= $this->n)
					$k = $this->n - 1;
						// 出力の計算
				$s = 0;
				for ($i1 = 0; $i1 <= $this->p; $i1++)
					$s += $this->W[$i1] * $this->E[$k][$i1];
						// 正しい分類
				if (($s > 0 && $this->C[$k] > 0) || ($s < 0 && $this->C[$k] < 0)) {
					$run++;
					if ($run > $run_p) {
						$num = $this->Bunrui();
						if ($num > $num_p) {
							$num_p = $num;
							$run_p = $run;
							for ($i1 = 0; $i1 <= $this->p; $i1++)
								$this->W_p[$i1] = $this->W[$i1];
							if ($num == $this->n)
								$sw = $count;
						}
					}
				}
						// 誤った分類
				else {
					$run = 0;
					for ($i1 = 0; $i1 <= $this->p; $i1++)
						$this->W[$i1] += $this->C[$k] * $this->E[$k][$i1];
				}
			}
		}
	
		return $sw;
	}
}

/****************/
/* main program */
/****************/

	if (count($argv) > 1) {
					// 基本データの入力
		$st = fopen($argv[1], "rb");
		fscanf($st, "%*s %ld %*s %ld %*s %ld", $max, $p, $n);
		fscanf($st, "%*s %s", $name);
		fclose($st);
					// ネットワークの定義
		$net = new Perceptron($max, $n, $p);
		$net->Input($name);
					// 学習と結果の出力
		if (count($argv) == 2)
			$net->Learn(0);
		else
			$net->Learn(1, $argv[2]);
	}

	else
		exit("***error   入力データファイル名を指定して下さい\n");

/*
---------------------入力ファイル------------
最大試行回数 100 入力セルの数 2 訓練例の数 4
入力データファイル or.dat

---------------------or.dat--------------
OR演算の訓練例.最後のデータが目標出力値
-1 -1 -1
-1  1  1
 1 -1  1
 1  1  1
*/

?>