喫茶XRAYトップページ

[スレッド全体
]

[12677] 単純パーセプトロンを作ってみました。返信 削除
2019/4/3 (水) 20:48:06 taki
Delphiで単純パーセプトロンを作ってみました。
入力はx1,x2でANDゲートの動きをします。

参考にしたサイト
http://hokuts.com/2015/11/24/ml1_func/
https://qiita.com/yudsuzuk/items/a8e1eee92403f0921d92
http://enakai00.hatenablog.com/entry/20150112/1421036041

実行結果の見方は
0,0  -0.63… 負なので0
0,1  -0.15… 負なので0
1,0  -0.02… 負なので0
1,1  0.469… 正なので1
ボタンを押す度に違う値になりますが、負,負,負,正は同じです。

教師データをNANDやORに書き換えるとそのように学習します。

重みを更新の部分のWeightVector[]はforループを使用するべきでしょう。


unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.StdCtrls, System.Types,
  Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    Label11: TLabel;
    Label12: TLabel;
    Label13: TLabel;
    Label14: TLabel;
    Label15: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

  InputNodeCount:integer;        // 入力ノード数
  InputVector:TDoubleDynArray;   // 入力ベクトル サイズはInputNodeCount + 1
  WeightVector:TDoubleDynArray;  // 重みベクトル サイズはInputNodeCount + 1
  TeachrData:TDoubleDynArray;    // 教師データ   サイズはInputNodeCount + 2

  aa,cc:Double;

implementation

{$R *.dfm}


procedure SetRandomWeightVector(var AWeightVector:TDoubleDynArray);
var
  i,lng:integer;
begin
  lng := Length(AWeightVector);
  Randomize;
  for i := 0 to lng - 1 do begin
    AWeightVector[i] := Random; // 0〜1
  end;
end;

// 識別関数
function DiscriminantFunction(var AInputVector:TDoubleDynArray;
                              var AWeightVector:TDoubleDynArray):Double;
var
  i,j,lng:integer;
  val:Double;
begin
  lng := Length(AInputVector);
  val := 0.0;
  AInputVector[0] := 1.0;  // バイアスなので強制的に1.0へ
  for i := 0 to lng - 1 do begin
    val := val + AWeightVector[i] * AInputVector[i];
  end;
  result := val;
end;

// ANDの場合の教師データ 4種類のデータをセット
procedure SetTeachrData(var TData:TDoubleDynArray; DataNo:integer);
begin
  if DataNo = 0 then begin
    TData[0] := 1;    // バイアス1
    TData[1] := 0;    // 入力1
    TData[2] := 0;    // 入力2
    TData[3] := 0;    // 答え
  end;
  if DataNo = 1 then begin
    TData[0] := 1;
    TData[1] := 0;
    TData[2] := 1;
    TData[3] := 0;
  end;
  if DataNo = 2 then begin
    TData[0] := 1;
    TData[1] := 1;
    TData[2] := 0;
    TData[3] := 0;
  end;
  if DataNo = 3 then begin
    TData[0] := 1;
    TData[1] := 1;
    TData[2] := 1;
    TData[3] := 1;
  end;
end;

procedure StudyPerceptron;
var
  i,j,k,maxloop:integer;
  rho,wx:Double;
begin
  rho := 0.1;   // 更新の刻み値
  maxloop := 0;
  // 重みをランダムで設定
  SetRandomWeightVector(WeightVector);

  repeat
    k := 0;  // 更新のカウンタ
  // 全教師データで処理
    for i := 0 to 3 do begin
    // i= 0     1      2      3
    // x1,x2  x1,x2  x1,x2  x1,x2
    //  0,0    0,1    1,0    1,1
      SetTeachrData(TeachrData,i);
    // 教師データのXを入力し、
      for j := 0 to 2 do begin
        InputVector[j] := TeachrData[j];  // 1  x1 x2
      end;
    // 出力が正解と合っているかチェック
      wx := DiscriminantFunction(InputVector,WeightVector);
      if (wx >= 0) then begin  // プラス領域=1
        if (TeachrData[3] = 0) then begin  // しかし答えは0で、間違いなので重み更新
          inc(k);     // 更新の回数を記憶
          WeightVector[0] := WeightVector[0] - rho;                 // 重みを更新
          WeightVector[1] := WeightVector[1] - rho*InputVector[1];  // 重みを更新
          WeightVector[2] := WeightVector[2] - rho*InputVector[2];  // 重みを更新
        end else begin  // 答えも1なので
        // 何もしない
        end;
      end else begin  // マイナス領域 =0
        if (TeachrData[3] = 1) then begin  // しかし答えは1で、間違いなので重み更新
          inc(k);     // 更新の回数を記憶
          WeightVector[0] := WeightVector[0] + rho;                 // 重みを更新
          WeightVector[1] := WeightVector[1] + rho*InputVector[1];  // 重みを更新
          WeightVector[2] := WeightVector[2] + rho*InputVector[2];  // 重みを更新
        end else begin  // 答えも0なので
        // 何もしない
        end;
      end;
    end;

    inc(maxloop);   // 無限ループ脱出用
    if maxloop > 10000 then break;
  until k = 0;   // 重みの更新がなくなるまで繰り返す
  // 無い場合、終了
  aa := - WeightVector[1] / WeightVector[2];     // グラフの傾き
  cc := - WeightVector[0] / WeightVector[2];     // グラフの切片

  Form1.Label1.Caption := floattostr(aa);
  Form1.Label2.Caption := floattostr(cc);
  Form1.Label3.Caption := inttostr(k);
  Form1.Label4.Caption := inttostr(maxloop);

  Form1.Label5.Caption := floattostr(WeightVector[0]);
  Form1.Label6.Caption := floattostr(WeightVector[1]);
  Form1.Label7.Caption := floattostr(WeightVector[2]);

  Form1.Label12.Caption := floattostr(WeightVector[0]);
  Form1.Label13.Caption := floattostr(WeightVector[0] + WeightVector[2]);
  Form1.Label14.Caption := floattostr(WeightVector[0] + WeightVector[1]);
  Form1.Label15.Caption := floattostr(WeightVector[0] + WeightVector[1] + WeightVector[2]);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  InputNodeCount := 2;    // 入力2
  SetLength(InputVector,InputNodeCount + 1);
  SetLength(WeightVector,InputNodeCount + 1);
  SetLength(TeachrData,InputNodeCount + 2);
  StudyPerceptron;
end;

end.


[12678] Re:単純パーセプトロンを作ってみました。返信 削除
2019/4/4 (木) 06:32:22 Mr.XRAY
▼ takiさん
> Delphiで単純パーセプトロンを作ってみました。
> 入力はx1,x2でANDゲートの動きをします。


パーセプトロンなんて用語,知りませんでした.
リンクの記事を読んでみました.へぇ〜.
面白いですね.一昔前の私だったらはまりそうです (^^;


[▼次のスレッド]
INCM/CMT
Cyclamen v3.82