いざ！PostgreSQLで機械学習ひせむ！―単純パーセプトロン編

SQLで教師データ(レコードでもテーブルでも)を`INSERT`をするだけで賢くなる人工知能があれば、だれでも使える人工知能になるよねと思ったので書いてみました。

# 1. ニューラルネットワークはPDCAを自分で回してくれるプログラムのことだ
ニューラルネットワークを極端に単純化していうと、「入力ベクトル×行列＝出力べクトル」の計算をして、入力から出力を得ることです。
ニューラルネットワークの「すごさ」を一言で説明すると、PDCAの内の「DCA」を自動で実行してどんどん高みを目指していってくれることだと思います。

P・・・PLAN、計画。これは人間がやります。
D・・・DO、実行。とりあえずやってみる。最初は、成分がすべて乱数という「超適当な行列」を使います。
C・・・CHECK、確認。出力が「正解」か確認します。まあ最初は「超適当な行列」を使ったのだから、「不正解」の可能性が高いです。
**A・・・ACTION、行動**。こいつがすごい。『**「自分が出力した不正解」と「正解」の違い**』を数字で表し、この数字**をもとに、行列を書き換えます**。「ニューラルネットワークが反省する」といってもよいでしょう。
小学校の運動会でおなじみの「玉入れ」をやるなら、最初は超適当に投げるんです。適当に投げるので、玉はゴールより5メートルも東に落下したりします。すると次回からは「もっと西に投げよう」と反省します。今度はゴールより3メートル西に落下しました。今度は「さっきより気持ち東に投げよう」と反省し、・・・いつかはゴールに玉を入れることができるわけです。

## 1-1. ニューラルネットワークの劣化版「パーセプトロン」
ニューラルネットワークの劣化版に「パーセプトロン」というものがあります。
(本当はこの言い方は適切ではありません。正確には、パーセプトロンを拡張したのがニューラルネットワークなわけです。)
ニューラルネットワークが「入力ベクトル×行列＝出力べクトル」を計算するのに対し、
パーセプトロンは「入力ベクトル×ベクトル＝出力スカラ」を計算します。
ニューラルネットワークのやっている(P)DCAを理解するにはもってこいなので、説明したいと思います。

### 1-1-1. パーセプトロンのPDCAの「D」―内積と活性化関数

例えば身長$x$と睡眠時間$y$から、大人(-1)か子供(1)かを答えてくれるパーセプトロンを作るとしましょう。
まず、直線の式$y=px+q$の式を考えます。これが「大人と子供の境界線」となります。この線より下側は大人、上側は子供です。
ここに、$x$, $y$, (大人か子供かの答え)の3要素のペアを何組も何組も渡します。
例えば「身長$x=1.30$, 睡眠時間$y=0.8$は子供」とか、「$x=1.70, y=0.55$, 大人」とかです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319477/9578a753-cae0-bdeb-ca0d-1e357dfd66b0.png)


こうした情報を大量に集めてきたら、座標平面上に表した「大人」と「子供」の点たちの、「境界線」がなんとなく見えてきそうですね。その式を、$p$や$q$を「調節」しながら求めていくわけです。
実際には直線の式は、次のように変形し、$a$, $b$, $c$を調節していきます。

```math
y=px+q \\
-px+y-q=0 \\
-pbx+by-qb=0 \\
ax+by+c=0 \\


 \left(
    \begin{array}{c}
      a \\
      b \\
      c
    \end{array}
  \right)
\bullet
 \left(
    \begin{array}{c}
      x \\
      y \\
      1
    \end{array}
  \right)=0



```
この式より上だと子供(1)で、下だと大人(-1)なわけです。
すなわち

```math:
 \left(
    \begin{array}{c}
      a \\
      b \\
      c
    \end{array}
  \right)
\bullet
 \left(
    \begin{array}{c}
      x \\
      y \\
      1
    \end{array}
  \right)
```
が正なら1、負なら-1を返す関数(ヘビサイド関数といいます)が欲しいのです。

```math
\Phi
\left(
 \left(
    \begin{array}{c}
      a \\
      b \\
      c
    \end{array}
  \right)
\bullet
 \left(
    \begin{array}{c}
      x \\
      y \\
      1
    \end{array}
  \right)
\right)=
  \begin{cases}
    　1 \left( \left(
    \begin{array}{c}
      a \\
      b \\
      c
    \end{array}
  \right)
\bullet
 \left(
    \begin{array}{c}
      x \\
      y \\
      1
    \end{array}
  \right)\geq0\right) \\
    -1 \left( \left(
    \begin{array}{c}
      a \\
      b \\
      c
    \end{array}
  \right)
\bullet
 \left(
    \begin{array}{c}
      x \\
      y \\
      1
    \end{array}
  \right)\lt0\right)
  \end{cases}
```

このように内積を「正なら〇、負なら□」のように変換する関数を活性化関数といいます。

<sub><sub>活性化関数には微分可能な数学関数を使う場合もあるのですが(シグモイド関数など)、単純パーセプトロンの場合はヘビサイド関数のほうが良いことがわかっています。学習(PDCAのCA)時に便利な関数を選ぶ必要があるため、「相性」があるのです。</sub></sub>

### 1-1-2. パーセプトロンPDCAの「C」―損失関数

出力と「正解」の、方向や大きさを数量的に評価するのが「損失関数」です。「誤差」と考えてもよいでしょう
今回は単に「$正解-出力$」でよいでしょう。

- 出力が正解に一致した場合、損失関数は0となります。
- 子供(1)が正解のときに出力が大人(-1)となってしまった場合、損失関数は1-(-1)=2となります。
- 大人(-1)が正解のときに出力が子供(1)となってしまった場合、損失関数は-1-1=-2となります。

符号の有無により、子供→大人なのか、大人→子供なのか、修正の方向が示され、大きさにより、修正の必要性の有無が示されます。

### 1-1-3. パーセプトロンPDCAの「A」

損失関数をもとに、パラメータ$a,b,c$を更新します

```math
新しいa = 古いa + \eta(正解-出力)x \\
新しいb = 古いb + \eta(正解-出力)y \\
新しいc = 古いc + \eta(正解-出力)  \\
ただし\etaは学習率で、0超1以下です。

```

なんでこんな式でパラメータを更新できるのかは、「※」で説明しています。


実例でみていきましょう。$\eta=0.5とし、$訓練データを次の1～3とします。
訓練データ1「$x=1.30, y=0.8, 子供(1)$」
訓練データ2「$x=1.70, y=0.55, 大人(-1)$」
訓練データ3「$x=1.65, y=0.6, 大人(-1)$」

まず、超適当に$a=1,b=2,c=3$とした場合、次のグラフのようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319477/491183e5-a0f6-0ec2-ddee-cc96b191a97b.png)

訓練データ1を学習させましょう。

```math
 \left(
    \begin{array}{c}
      a \\
      b \\
      c
    \end{array}
  \right)
\bullet
 \left(
    \begin{array}{c}
      x \\
      y \\
      1
    \end{array}
  \right)
=
 \left(
    \begin{array}{c}
      1 \\
      2 \\
      3
    \end{array}
  \right)
\bullet
 \left(
    \begin{array}{c}
      1.30 \\
      0.8 \\
      1
    \end{array}
  \right)
=5.9
```
これは正なので、活性化関数(ヘビサイド関数)は1となります。
正解は子供(1)なので、損失関数は1-1=0となります。
したがって、パラメータは更新されません。

続いて訓練データ2を学習させましょう。

```math
 \left(
    \begin{array}{c}
      1 \\
      2 \\
      3
    \end{array}
  \right)
\bullet
 \left(
    \begin{array}{c}
      1.70 \\
      0.55 \\
      1
    \end{array}
  \right)
=5.8
```
これは正なので、活性化関数は1となります。
正解は大人(-1)なので、損失関数は-1-1=-2となります。

```math
新しいa = 1 - 0.5\times2\times1.70 =-0.7\\
新しいb = 2 - 0.5\times2\times0.55 =1.45 \\
新しいc = 3 - 0.5\times2 = 2  \\
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319477/cc6ee954-5ca5-cb1a-2499-8e9a9920d18e.png)

訓練データ3も同様に学習させてみましょう。

```math
 \left(
    \begin{array}{c}
      -0.7 \\
      1.45 \\
      2
    \end{array}
  \right)
\bullet
 \left(
    \begin{array}{c}
      1.65 \\
      0.6 \\
      1
    \end{array}
  \right)
=1.715
```
これは正なので、活性化関数は1となります。
正解は大人(-1)なので、損失関数は-1-1=-2となります。

```math
新しいa = -0.7 - 0.5\times2\times1.65 =-2.35\\
新しいb = 1.45 - 0.5\times2\times0.6 =0.85 \\
新しいc = 2 - 0.5\times2 = 1  \\
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319477/c2fc9c2a-4ad7-9e93-520f-b0188b441679.png)

以上3図を見比べると、直線のグラフが、子供と大人の境界へ近づいていく様子がわかるかと思います。

----

※・・・パラメータの更新式は、ちゃんとかくと次の通りです。

```math
新しいa = 古いa - \eta\frac{\partial}{\partial a}(損失関数) \\
新しいb = 古いb - \eta\frac{\partial}{\partial b}(損失関数) \\
新しいc = 古いc - \eta\frac{\partial}{\partial c}(損失関数) \\
```

損失関数は、「誤差の大きさ」みたいなものです。
したがって、例えば$\frac{\partial}{\partial a}(損失関数)$は、$a$を増やしたとき誤差がどれだけ大きくなるかを表しています。
(微分だから当たり前ですね。難しく考えないでください)
$a- \eta\frac{\partial}{\partial a}(損失関数)$は、$\frac{\partial}{\partial a}(損失関数)$が大きければ大きいほど、小さくなります。「$a$を増やすと誤差が大きい」なら、「$a$を減らせば誤差が小さくなる」だろうという魂胆です。
さて、損失関数は今回$正解-出力$でした。
$正解$は定数ですから微分すると0になります。無視しましょう。$-出力$と同じことです。しかも出力は内積$ax+by+c\dot{}1$と同じ符号を持つので、これを$a$で微分すると、$x$となりますね。したがって、$\frac{\partial}{\partial a}(損失関数)$は$(損失関数)\dot{}x$に負の比例定数で比例するはずなんです。その比例定数は$\eta$で誤魔化してしまえば、
$新しいa = 古いa - \eta\frac{\partial}{\partial a}(損失関数)$は
$新しいa = 古いa + \eta(損失関数)\dot{}x$と同じであるとみなせるわけです。






## 1-2. 機械学習するパーセプトロンをPostgreSQLで実装する

1-1節では、パーセプトロンのパラメータは$a,b,c$の3種類、入力は身長$x$と睡眠時間$y$の2種類としていました。しかし実際にはパーセプトロンはもっと多くの入力を受け付けることができます。
入力を$x_1～x_n$の$n$種類、パラメータを$w_0～w_n$の$n+1$種類とするとき、このパーセプトロンは$n$次元空間上で説明することができます。2次元の$xy$平面上で説明したかったために、1-1節では入力を2種類にしたわけです。

さてここで、n次元のパーセプトロンをPostgreSQLで作っていきましょう。
まず、パーセプトロンは行うべき操作が多く存在します。またその過程で、パーセプトロンの内部状態(内部情報)も変化します。
そのため、パーセプトロンの「記憶領域」と「操作」をスキーマでまとめて管理できると便利です。
オブジェクト指向に慣れている方向けに説明するなら、「クラス」に当たるものをスキーマで表現するのです。

```sql:Perceptronスキーマ(クラス)の作成
CREATE SCHEMA Perceptron;
```
(スキーマの代わりにデータベースを使うという案もあるかもしれませんが、こちらは外部からのアクセスが難しいため不便です。)

続いて複合型を作り、記憶領域(フィールド)を定義していきます。
オブジェクト指向でいうなら、これはプロパティをまとめたものといえるし、同時にインスタンスであるということもできるでしょう。

```sql:パーセプトロンのフィールドの定義
CREATE TYPE Myrange AS (min numeric, max numeric);]
CREATE TYPE Perceptron.fields AS (dimension int, weights numeric[], ranges Myrange[], learning_rate numeric);
```
`dimension`は次元数で、`weights`はパラメータを順に並べた配列です。
各パラメータの範囲を`ranges`で管理しています。

<!--
そして`is_learning`はfalseとすることで学習を停止させることができます。
-->

`learning_rate`は学習率です。

また、関数(メソッドやコンストラクタ)を多数定義します。

### 1-2-1. PDCAの「P」―コンストラクタ

オブジェクト指向というプログラミング手法では、小さなプログラム(クラス)Aを元に仮想コンピュータ(インスタンス)を作り、その仮想コンピュータを別のプログラム(クラス)Bで操作します。<sub><sub>これはもはやたとえ話ではなく、論理的に正しい主張です。コンピュータのモデルであるチューリングマシンを、「クラス」にて設計できます。したがってクラスによる設計を実体化したインスタンスはチューリング完全ですので、コンピュータです。そしてコンピュータを設計したクラスは「OS」ですから、確かに「プログラム」です。</sub></sub>これは一見複雑に見えますが、細かい具体的な処理を仮想コンピュータに押し付けることで、これを利用するプログラムは、「おおざっぱなコード」が書けるようになり、見た目がシンプルになってすっきりするというメリットがあります。

プログラムから仮想コンピュータを作る際、初期設定をする必要があります。記憶領域の初期値を設定することなどです。
それを行うのがコンストラクタです。

```sql:パーセプトロンのコンストラクタ

CREATE OR REPLACE FUNCTION Perceptron.construct(dimension int, ranges Myrange[], learning_rate numeric) RETURNS Perceptron.fields AS
$cf$
    DECLARE
        this Perceptron.fields := null;
        min numeric; max numeric;   
    BEGIN
        this.dimension := dimension;
        ranges := ('[0:'||array_length(ranges,1)-1||']='||(ranges::text))::Myrange[];
        FOR i IN 0 .. dimension LOOP
            IF ranges[i].min is not null AND ranges[i].max is not null THEN
                min := ranges[i].min;
                max := ranges[i].max;
                this.weights[i] := (random() * (max - min)) + min;
            ELSIF ranges[i].min is null AND ranges[i].max is not null THEN
                this.weights[i] := ranges[i].max;
            ELSIF ranges[i].min is not null AND ranges[i].max is null THEN
                this.weights[i] := ranges[i].min;
            ELSE
                this.weights[i] := 0;
            END IF;
        END LOOP;
        this.ranges := ranges;
        this.learning_rate := learning_rate; 
        return this;
    END
$cf$ LANGUAGE plpgsql;
```
ここで
`SELECT Perceptron.construct(3, '{"(,10)","(-10,)","(-10,10)","(,)"}',0.5);`
を実行すると、パーセプトロンの仮想コンピュータが作られ、その記憶領域を見ることができます。
例えば次のような結果がコンソールに表示されるでしょう。

```
(3,"[0:3]={10,-10,0.763660138472915,0}","[0:3]={""(,10)"",""(-10,)"",""(-10,10)"",""(,)""}",0.5)
```
左から順に、
「次元数」、「パラメータ(定義域内でランダムに決めました)」、「パラメータの定義域」、「学習率」
となっています。
パラメータの定義域については、`Myrange`型で実現しています。
下限を`null`とすると負の無限大、上限を`null`とすると正の無限大を表します。すなわち`'(,)'::Myrange`は理論上実数全体を表します。
ただし初期化時(`Perceptron.construct`関数)については、上下限のいずれか一方が`null`の場合はもう片方の下上限の値で初期化されます。上下限の両方が`null`の場合、0で初期化されます。
またPostgreSQLでは配列はデフォルトが1オリジンですが、パラメータのインデックスは0オリジンとしています。
特別に定義した「0番目」はバイアスというものです。

#### バイアスとは
直線の方程式$ax+by+c=0$を思い出してください。左辺は内積

```math
\left(
\begin{array}{}
a \\
b \\
c \\
\end{array}
\right) \dot{}
\left(
\begin{array}{a}
x \\
y \\
1 \\
\end{array}
\right)
```
とみることもできるのでした。

ベクトルの成分の順番を、両方同じように入れ替えても内積は変わりませんから、

```math
\left(
\begin{array}{}
c \\
a \\
b \\
\end{array}
\right) \dot{}
\left(
\begin{array}{a}
1 \\
x \\
y \\
\end{array}
\right)
\begin{array}{a}
←0番目 \\
←1番目 \\
←2番目 \\
\end{array}
```
とみることもできます。
すると、入力ベクトルの1番目以降は変数であるのにもかかわらず、**0番目だけは定数1**となっています。
定数1の「相手」となるパラメータ$c$を、「バイアス」というのです。

### 1-2-2. 入出力関数

入出力関数calculate/learnは、1-2-3節の「D」を呼び出します。
`learn`は1-2-4節の「C」や1-2-5節の「A」も呼び出し、パーセプトロンの更新済みフィールド(`Perceptron.fields`)を返却します。
`calculate`は「D」の結果を返却します。

```sql:学習(learn)
CREATE OR REPLACE FUNCTION learn(this Perceptron.fields, inputs numeric[], correct_answer int) RETURNS Perceptron.fields AS
$cf$
    DECLARE answer int; loss int; tmptxt text;
    BEGIN
        SELECT D(this, inputs) INTO answer;
        SELECT C(this, answer, correct_answer) INTO loss;
        SELECT A(this, loss, inputs)::text INTO tmptxt; this := tmptxt::Perceptron.fields; --バグ回避
        return this;
    END;        
$cf$ LANGUAGE plpgsql;
```

```sql:答えの出力(calculate)
CREATE OR REPLACE FUNCTION calculate(this Perceptron.fields, inputs numeric[]) RETURNS int AS
$cf$
    DECLARE answer int;
    BEGIN
        SELECT D(this, inputs) INTO answer;
        return answer;
    END;
$cf$ LANGUAGE plpgsql;
```


### 1-2-3. PDCAの「D」

出力(入力ベクトルとパラメータの内積を引数とする活性化関数(ヘビサイド関数))を計算します。

```sql:活性化関数の計算
CREATE OR REPLACE FUNCTION dot_(vector1 numeric[], vector2 numeric[]) RETURNS numeric AS
$cf$
    DECLARE ans numeric := 0;
    BEGIN
        IF vector1::text ~ '^\[.*' THEN
            vector1 := substring(vector1::text from '^\[[^]]*]=(.*)$')::numeric[];
        END IF;
        IF vector2::text ~ '^\[.*' THEN
            vector2 := substring(vector2::text from '^\[[^]]*]=(.*)$')::numeric[];
        END IF;
        FOR idx IN 1..(array_length(vector1, 1)) LOOP
            ans := ans + vector1[idx]*vector2[idx];
        END LOOP;
        return ans;
    END;
$cf$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION D(this Perceptron.fields, inputs numeric[]) RETURNS int AS
$cf$
    DECLARE weights numeric[]; dot numeric;
    BEGIN 
        inputs:= 1.0 || inputs;
        weights := substring((this.weights)::text from '=(.*)$')::numeric[]; 
        -- 1オリジン化
        dot := dot_(inputs, weights); --内積
        IF dot > 0 THEN return 1;
        ELSIF dot < 0 THEN return -1;
        ELSE return 0; END IF;
    END; 
$cf$ LANGUAGE plpgsql;
```

動作を確認してみましょう。

```
postgres=# SELECT D(
postgres(# '(2,"[0:2]={3,1,2}","[0:2]={""(,)"",""(,)"",""(,)""}",0.5)'::Perceptron.fields, -- パーセプトロン
postgres(# '{-1,-2}'                                                           --座標
postgres(# ) AS "P(-1,-2)";
 P(-1,-2)
----------
       -1
(1 行)


postgres=# SELECT D(
postgres(# '(2,"[0:2]={3,1,2}","[0:2]={""(,)"",""(,)"",""(,)""}",0.5)'::Perceptron.fields, -- パーセプトロン
postgres(# '{0,-1}'                                                            --座標
postgres(# ) AS "Q(0,-1)";
 Q(0,-1)
---------
       1
(1 行)


postgres=# SELECT D(
postgres(# '(2,"[0:2]={3,1,2}","[0:2]={""(,)"",""(,)"",""(,)""}",0.5)'::Perceptron.fields, -- パーセプトロン
postgres(# '{1,-3}'                                                            --座標
postgres(# ) AS "R(1,-3)";
 R(1,-3)
---------
      -1
(1 行)
```
これを、グラフで見てみると、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319477/19455a45-005d-9c71-e8da-322f02a57fc2.png)
となっていて、パラメータの作る式を境に-1と1を適切に出力していることがわかります。
直線の式はパラメータ`(c,a,b)=(3,1,2)`より、$x+2y+3=0$です。

### 1-2-4. PDCAの「C」

損失関数を計算します。

```sql:損失関数
CREATE OR REPLACE FUNCTION C(this Perceptron.fields, answer int, correct_answer int) RETURNS int AS
'SELECT correct_answer-answer;' LANGUAGE SQL;
```


### 1-2-5. PDCAの「A」

重みを更新します。

```sql:重みの更新
CREATE OR REPLACE FUNCTION A(this Perceptron.fields, loss int, inputs numeric[]) RETURNS Perceptron.fields AS
$cf$
--本当は、Myrangeで指定した範囲内しかパラメータを変動できなくしたいのだが、
--それはまた今度やろうと思う。
    DECLARE 
        eta numeric := this.learning_rate;
        weights numeric[] := this.weights;
    BEGIN
        inputs := 1.0 || inputs;
        inputs := ('[0:'||array_length(inputs,1)-1||']='||(inputs::text))::numeric[];
        -- 0オリジン化

        FOR i in 0..(array_length(weights, 1)-1) LOOP
            weights[i] := weights[i] + eta*loss*inputs[i];
        END LOOP;
        this.weights := weights;

        return this;
    END;
$cf$ LANGUAGE plpgsql;
```

1-1節と比較することで、動作を確かめてみましょう。

パラメータが$c=3, a=1, b=2$($c$はバイアスなので、先頭である0番目にもってきました)のとき、訓練データ2(1番目1.70, 2番目0.55)を学習率0.5、正解ラベル-1で学習させると、パラメータは$c=2, a=-0.7, b=1.45$に更新されるはずです。
これは次のように確認できます。

```
postgres=# SELECT learn
postgres-# (
postgres(#     '(2,"[0:2]={3,1,2}","[0:2]={""(,)"",""(,)"",""(,)""}",0.5)'::Perceptron.fields,
postgres(#                     -- パーセプトロン
postgres(#     '{1.70, 0.55}', -- 入力
postgres(#     -1              -- 正解ラベル
postgres(# );
                                 learn
-----------------------------------------------------------------------
 (2,"[0:2]={2.00,-0.700,1.450}","[0:2]={""(,)"",""(,)"",""(,)""}",0.5)
```

パラメータの配列が`[0:2]={2.00,-0.700,1.450}`となっていることから、正しく更新されたことがわかります。

## 1-3. パーセプトロンを学習させてみよう

単純パーセプトロンを実際に学習させてみましょう。

まず、学習させたい直線を決めます。
今回は適当に$x+2y+9=0$としましょう。すなわち、2次元のパーセプトロンに、パラメータ`[0:2]={9,1,2}`を学ばせたいわけです。
(実際には`[0:2]={18,2,4}`を学んだり、`[0:2]={-90,-10,-20}`を学んだりし得ます。あくまでもパラメータ同士の割合と思ってください。)

$x+2y+9=0$は変形すると$y=-0.5x-4.5$となります。
$x$と$y$をランダムに決め、$y$の値が$-0.5x-4.5$より上なら1、それ以外なら-1を正解ラベルとします。
例えば$x=2, y=3$なら、$-0.5\times2-4.5$よりも$y$が上なので、正解ラベルは1となります。
これにより訓練データ$x=2, y=3, 1$ができました。
この手順を繰り返せば繰り返すほど、訓練データを大量に生成できます。
訓練データを入れていくにつれ、パラメータが`[0:2]={9,1,2}`へ近づいていくのを見てみましょう。


```sql
DO
$do$
    DECLARE 
        p Perceptron.fields;
        m Myrange[];
        x numeric; y numeric; t int; weights numeric[]; tmp numeric;
    BEGIN 
        -- パーセプトロン作成。
        FOR i IN 1..3 LOOP
            m := m || '(,)'::Myrange;
        END LOOP;
        p :=  Perceptron.construct(2, m, 0.5);

        --学習を100回繰り返す。
        FOR i IN 1..10000 LOOP
            -- x, y, tを決め、訓練データを生成
            x := (random() * (5 - (-5))) + (-5);
            y := (random() * (2.275 - (-2.725))) + (-2.725);
            IF y>-0.5*x-4.5 THEN t := 1; ELSE t := -1; END IF;
           
/*          IF i % 100 = 0 THEN
                RAISE NOTICE '%回目の訓練データ:x=%,y=%,正解=%',i,x,y,t;
            END IF;*/

            -- 各回学習前のパラメータを表示。ただし1番目が1となるように約分のようなことをする
            weights := p.weights;
            IF weights[1] = 0 THEN
                IF weights[0] = 0 THEN
                    IF weights[2] != 0 THEN
                        weights[2] := 1;
                    END IF;
                ELSE
                    tmp := weights[0];
                    weights[0] := weights[0]/tmp;
                    weights[2] := weights[2]/tmp;
                END IF;
            ELSE
                tmp := weights[1];
                weights[0] := weights[0]/tmp;
                weights[1] := weights[1]/tmp;
                weights[2] := weights[2]/tmp;
            END IF;

            IF i % 100 = 0 THEN
                RAISE NOTICE '%回目のパラメータ:%',i, weights;
            END IF;
            -- 学習
            p := learn(p, array[x,y], t);
        END LOOP;
    END;
$do$;
            


```

実行させてみると、次の通りになりました。

```
NOTICE:  100回目のパラメータ:[0:2]={-7.25919492604946979,1.00000000000000000000,0.69840166961480351949}
NOTICE:  200回目のパラメータ:[0:2]={2.62708252772593513,1.00000000000000000000,0.43995372675223418387}
NOTICE:  300回目のパラメータ:[0:2]={7.16420661690930048,1.00000000000000000000,-0.04185565231155350207}
NOTICE:  400回目のパラメータ:[0:2]={-21.94314990197906776,1.00000000000000000000,-1.830380581950941656}
NOTICE:  500回目のパラメータ:[0:2]={-21.94314990197906776,1.00000000000000000000,-1.8303805819509416564}
NOTICE:  600回目のパラメータ:[0:2]={-21.94314990197906776,1.00000000000000000000,-1.8303805819509416564}
NOTICE:  700回目のパラメータ:[0:2]={-21.94314990197906776,1.00000000000000000000,-1.8303805819509416564}
NOTICE:  800回目のパラメータ:[0:2]={7.81732249274029410,1.00000000000000000000,1.44353111749843662596}
NOTICE:  900回目のパラメータ:[0:2]={7.81732249274029410,1.00000000000000000000,1.44353111749843662596}
NOTICE:  1000回目のパラメータ:[0:2]={7.81732249274029410,1.00000000000000000000,1.44353111749843662596}
NOTICE:  1100回目のパラメータ:[0:2]={7.81732249274029410,1.00000000000000000000,1.44353111749843662596}
NOTICE:  1200回目のパラメータ:[0:2]={7.81732249274029410,1.00000000000000000000,1.44353111749843662596}
NOTICE:  1300回目のパラメータ:[0:2]={25.54340600031499160,1.00000000000000000000,7.5095776951111895237}
NOTICE:  1400回目のパラメータ:[0:2]={-8.26689424090244204,1.00000000000000000000,-1.5785135133104178827}
NOTICE:  1500回目のパラメータ:[0:2]={9.118987396693210542,1.00000000000000000000,2.3882325642181354645}
NOTICE:  1600回目のパラメータ:[0:2]={47.616825343897669264,1.00000000000000000000,0.25832435507319780749}
NOTICE:  1700回目のパラメータ:[0:2]={101.3954766207931511976,1.00000000000000000000,30.0821242905959444873}
NOTICE:  1800回目のパラメータ:[0:2]={29.7140418581867220796,1.00000000000000000000,9.5604047073278331660}
NOTICE:  1900回目のパラメータ:[0:2]={-25.8355150355187045163,1.00000000000000000000,-5.3018902470570401510}
NOTICE:  2000回目のパラメータ:[0:2]={-25.8355150355187045163,1.00000000000000000000,-5.3018902470570401510}
NOTICE:  2100回目のパラメータ:[0:2]={-25.8355150355187045163,1.00000000000000000000,-5.3018902470570401510}
NOTICE:  2200回目のパラメータ:[0:2]={-25.8355150355187045163,1.00000000000000000000,-5.3018902470570401510}
NOTICE:  2300回目のパラメータ:[0:2]={10.0422930398549653071,1.00000000000000000000,2.5331353731397985325}
NOTICE:  2400回目のパラメータ:[0:2]={10.0422930398549653071,1.00000000000000000000,2.5331353731397985325}
NOTICE:  2500回目のパラメータ:[0:2]={7.4665955635061155941,1.00000000000000000000,1.9246108113535242947}
NOTICE:  2600回目のパラメータ:[0:2]={-17.1371939768181755413,1.00000000000000000000,-1.9229082773469877491}
NOTICE:  2700回目のパラメータ:[0:2]={-17.1371939768181755413,1.00000000000000000000,-1.9229082773469877491}
NOTICE:  2800回目のパラメータ:[0:2]={71.3885791743053178439,1.00000000000000000000,9.1727121006048692508}
NOTICE:  2900回目のパラメータ:[0:2]={71.3885791743053178439,1.00000000000000000000,9.1727121006048692508}
NOTICE:  3000回目のパラメータ:[0:2]={3.8324109951952109611,1.00000000000000000000,1.06902459036795582912}
NOTICE:  3100回目のパラメータ:[0:2]={17.8924384667590856249,1.00000000000000000000,4.2876008132052584196}
NOTICE:  3200回目のパラメータ:[0:2]={17.8924384667590856249,1.00000000000000000000,4.2876008132052584196}
NOTICE:  3300回目のパラメータ:[0:2]={26.6136390040957765644,1.00000000000000000000,6.9989908429163713952}
NOTICE:  3400回目のパラメータ:[0:2]={284.3364737866778406197,1.00000000000000000000,85.0184363245200410552}
NOTICE:  3500回目のパラメータ:[0:2]={173.3019933156188144935,1.00000000000000000000,39.8307694288574306376}
NOTICE:  3600回目のパラメータ:[0:2]={-368.7928815926095909975,1.00000000000000000000,-81.5897540043401428602}
NOTICE:  3700回目のパラメータ:[0:2]={-230.9133057788618609473,1.00000000000000000000,-45.5338449842941562805}
NOTICE:  3800回目のパラメータ:[0:2]={-230.9133057788618609473,1.00000000000000000000,-45.5338449842941562805}
NOTICE:  3900回目のパラメータ:[0:2]={9.0682394289946362283,1.00000000000000000000,2.0804885880578672459}
NOTICE:  4000回目のパラメータ:[0:2]={9.0682394289946362283,1.00000000000000000000,2.0804885880578672459}
NOTICE:  4100回目のパラメータ:[0:2]={9.0682394289946362283,1.00000000000000000000,2.0804885880578672459}
NOTICE:  4200回目のパラメータ:[0:2]={-20.5746648974464717555,1.00000000000000000000,-2.0399951183780109406}
NOTICE:  4300回目のパラメータ:[0:2]={5.5521470219366250405,1.00000000000000000000,1.1232606422438250295}
NOTICE:  4400回目のパラメータ:[0:2]={22.8522661672711540145,1.00000000000000000000,4.2328717568047673516}
NOTICE:  4500回目のパラメータ:[0:2]={22.8522661672711540145,1.00000000000000000000,4.2328717568047673516}
NOTICE:  4600回目のパラメータ:[0:2]={22.8522661672711540145,1.00000000000000000000,4.2328717568047673516}
NOTICE:  4700回目のパラメータ:[0:2]={22.8522661672711540145,1.00000000000000000000,4.2328717568047673516}
NOTICE:  4800回目のパラメータ:[0:2]={3.6614869618576794751,1.00000000000000000000,1.1304477868197766937}
NOTICE:  4900回目のパラメータ:[0:2]={8.1539840869167714095,1.00000000000000000000,1.7263681915714636763}
NOTICE:  5000回目のパラメータ:[0:2]={8.8501480289024563449,1.00000000000000000000,1.9792030019887255528}
NOTICE:  5100回目のパラメータ:[0:2]={8.8501480289024563449,1.00000000000000000000,1.9792030019887255528}
NOTICE:  5200回目のパラメータ:[0:2]={-23.9614557161019297551,1.00000000000000000000,-2.3839226485913495209}
NOTICE:  5300回目のパラメータ:[0:2]={-23.9614557161019297551,1.00000000000000000000,-2.3839226485913495209}
NOTICE:  5400回目のパラメータ:[0:2]={4.7874868721664658115,1.00000000000000000000,1.00592940363360262731}
NOTICE:  5500回目のパラメータ:[0:2]={63.5030382005121432293,1.00000000000000000000,7.6297141985739170612}
NOTICE:  5600回目のパラメータ:[0:2]={25.2387325496155702691,1.00000000000000000000,-0.16780582537144023091}
NOTICE:  5700回目のパラメータ:[0:2]={25.2387325496155702691,1.00000000000000000000,-0.16780582537144023091}
NOTICE:  5800回目のパラメータ:[0:2]={34.0008450982463852613,1.00000000000000000000,1.8974626097718613919}
NOTICE:  5900回目のパラメータ:[0:2]={4.4336894624112206605,1.00000000000000000000,1.4488523818766546510}
NOTICE:  6000回目のパラメータ:[0:2]={15.4722100713983544041,1.00000000000000000000,3.0149412075981528527}
NOTICE:  6100回目のパラメータ:[0:2]={8.9852631396835734806,1.00000000000000000000,1.7743578269904653441}
NOTICE:  6200回目のパラメータ:[0:2]={8.9852631396835734806,1.00000000000000000000,1.7743578269904653441}
NOTICE:  6300回目のパラメータ:[0:2]={8.9852631396835734806,1.00000000000000000000,1.7743578269904653441}
NOTICE:  6400回目のパラメータ:[0:2]={8.9852631396835734806,1.00000000000000000000,1.7743578269904653441}
NOTICE:  6500回目のパラメータ:[0:2]={8.9852631396835734806,1.00000000000000000000,1.7743578269904653441}
NOTICE:  6600回目のパラメータ:[0:2]={8.9852631396835734806,1.00000000000000000000,1.7743578269904653441}
NOTICE:  6700回目のパラメータ:[0:2]={8.9852631396835734806,1.00000000000000000000,1.7743578269904653441}
NOTICE:  6800回目のパラメータ:[0:2]={8.9852631396835734806,1.00000000000000000000,1.7743578269904653441}
NOTICE:  6900回目のパラメータ:[0:2]={-30.9937723941170115035,1.00000000000000000000,-6.5637347501400466067}
NOTICE:  7000回目のパラメータ:[0:2]={99.7938106507557349267,1.00000000000000000000,22.5607999874074746897}
NOTICE:  7100回目のパラメータ:[0:2]={-477.1745323298607046942,1.00000000000000000000,-116.3218913187523896540}
NOTICE:  7200回目のパラメータ:[0:2]={-477.1745323298607046942,1.00000000000000000000,-116.32189131875238965400}
NOTICE:  7300回目のパラメータ:[0:2]={13.6425302069385141460,1.00000000000000000000,4.31007279705010343480}
NOTICE:  7400回目のパラメータ:[0:2]={-11.7633987125026943795,1.00000000000000000000,-2.59427609998415861361}
NOTICE:  7500回目のパラメータ:[0:2]={12.4415842137738499914,1.00000000000000000000,3.94728233205215294274}
NOTICE:  7600回目のパラメータ:[0:2]={12.4415842137738499914,1.00000000000000000000,3.94728233205215294274}
NOTICE:  7700回目のパラメータ:[0:2]={-27.0525018293783846590,1.00000000000000000000,-5.79109866748989604570}
NOTICE:  7800回目のパラメータ:[0:2]={17.0109156807607718380,1.00000000000000000000,4.10772162714158497117}
NOTICE:  7900回目のパラメータ:[0:2]={18.1296513169014205477,1.00000000000000000000,6.05369234855178737463}
NOTICE:  8000回目のパラメータ:[0:2]={-10.8274907680158068601,1.00000000000000000000,-2.58420897951270727550}
NOTICE:  8100回目のパラメータ:[0:2]={-27.7716911079505726017,1.00000000000000000000,-6.72820799827842571865}
NOTICE:  8200回目のパラメータ:[0:2]={-15.1372657120610670732,1.00000000000000000000,-4.13470662110308738495}
NOTICE:  8300回目のパラメータ:[0:2]={13.4012097603906076565,1.00000000000000000000,3.95759601104542248332}
NOTICE:  8400回目のパラメータ:[0:2]={13.4012097603906076565,1.00000000000000000000,3.95759601104542248332}
NOTICE:  8500回目のパラメータ:[0:2]={13.4012097603906076565,1.00000000000000000000,3.95759601104542248332}
NOTICE:  8600回目のパラメータ:[0:2]={-44.2562661579647821310,1.00000000000000000000,-8.55838736437415037451}
NOTICE:  8700回目のパラメータ:[0:2]={-44.2562661579647821310,1.00000000000000000000,-8.55838736437415037451}
NOTICE:  8800回目のパラメータ:[0:2]={7.5487992235318723365,1.00000000000000000000,2.33687025004813529168}
NOTICE:  8900回目のパラメータ:[0:2]={9.2881496220641953451,1.00000000000000000000,2.32819864249105859562}
NOTICE:  9000回目のパラメータ:[0:2]={-85.2558004766092160923,1.00000000000000000000,-13.49506875171969602601}
NOTICE:  9100回目のパラメータ:[0:2]={-85.2558004766092160923,1.00000000000000000000,-13.49506875171969602601}
NOTICE:  9200回目のパラメータ:[0:2]={-181.9562964975891634289,1.00000000000000000000,-29.88687058482400448935}
NOTICE:  9300回目のパラメータ:[0:2]={29.2942502286689166773,1.00000000000000000000,5.80772061748999350421}
NOTICE:  9400回目のパラメータ:[0:2]={19.1532152278884971411,1.00000000000000000000,4.22851918790817538034}
NOTICE:  9500回目のパラメータ:[0:2]={19.1532152278884971411,1.00000000000000000000,4.22851918790817538034}
NOTICE:  9600回目のパラメータ:[0:2]={9.3615568315914919946,1.00000000000000000000,2.00695570960664009655}
NOTICE:  9700回目のパラメータ:[0:2]={9.3615568315914919946,1.00000000000000000000,2.00695570960664009655}
NOTICE:  9800回目のパラメータ:[0:2]={9.3615568315914919946,1.00000000000000000000,2.00695570960664009655}
NOTICE:  9900回目のパラメータ:[0:2]={9.3615568315914919946,1.00000000000000000000,2.00695570960664009655}
NOTICE:  10000回目のパラメータ:[0:2]={8.9578326006909129566,1.00000000000000000000,2.01179774230337629541}
```
6100回以降から、かなり`{9,1,2}`に近づいていますね。
また、9600～9900回の間は、パラメータが更新されなかったことも分かります。

# 最後に

一から実装すると、単純パーセプトロンの機械学習ですらこんなに疲れるとは思わなかったです。
次回は、(途中で挫折しなければ)ニューラルネットワークによる機械学習の記事を書こうと思います。


<!--
を`INSERT`をするだけで賢くなる人工知能があれば、だれでも使える人工知能になるよねと思ったので書いてみました。

\# 1. ニューラルネットワークはPDCAを自分で回してくれるプログラムのことだ
ニューラルネットワークを極端に単純化していうと、「入力ベクトル×行列＝出力べクトル」の計算をして、入力から出力を得ることです。
ニューラルネットワークの「すごさ」を一言で説明すると、PDCAの内の「DCA」を自動で実行してどんどん高みを目指していってくれることだと思います。

P・・・PLAN、計画。これは人間がやります。
D・・・DO、実行。とりあえずやってみる。最初は、成分がすべて乱数という「超適当な行列」を使います。
C・・・CHECK、確認。出力が「正解」か確認します。まあ最初は「超適当な行列」を使ったのだから、「不正解」の可能性が高いです。
**A・・・ACTION、行動**。こいつがすごい。『**「自分が出力した不正解」と「正解」の違い**』を数字で表し、この数字**をもとに、行列を書き換えます**。「ニューラルネットワークが反省する」といってもよいでしょう。
小学校の運動会でおなじみの「玉入れ」をやるなら、最初は超適当に投げるんです。適当に投げるので、玉はゴールより5メートルも東に落下したりします。すると次回からは「もっと西に投げよう」と反省します。今度はゴールより3メートル西に落下しました。今度は「さっきより気持ち東に投げよう」と反省し、・・・いつかはゴールに玉を入れることができるわけです。

\## 1-1. ニューラルネットワークの劣化版「パーセプトロン」
ニューラルネットワークの劣化版に「パーセプトロン」というものがあります。
(本当はこの言い方は適切ではありません。正確には、パーセプトロンを拡張したのがニューラルネットワークなわけです。)
ニューラルネットワークが「入力ベクトル×行列＝出力べクトル」を計算するのに対し、
パーセプトロンは「入力ベクトル×ベクトル＝出力スカラ」を計算します。
ニューラルネットワークのやっている(P)DCAを理解するにはもってこいなので、説明したいと思います。

\### 1-1-1. パーセプトロンのPDCAの「D」―内積と活性化関数

例えば身長$x$と睡眠時間$y$から、大人(-1)か子供(1)かを答えてくれるパーセプトロンを作るとしましょう。
まず、直線の式$y=px+q$の式を考えます。これが「大人と子供の境界線」となります。この線より下側は大人、上側は子供です。
ここに、$x$, $y$, (大人か子供かの答え)の3要素のペアを何組も何組も渡します。
例えば「身長$x=1.30$, 睡眠時間$y=0.8$は子供」とか、「$x=1.70, y=0.55$, 大人」とかです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319477/9578a753-cae0-bdeb-ca0d-1e357dfd66b0.png)


こうした情報を大量に集めてきたら、座標平面上に表した「大人」と「子供」の点たちの、「境界線」がなんとなく見えてきそうですね。その式を、$p$や$q$を「調節」しながら求めていくわけです。
実際には直線の式は、次のように変形し、$a$, $b$, $c$を調節していきます。

```math
y=px+q \\
-px+y-q=0 \\
-pbx+by-qb=0 \\
ax+by+c=0 \\


 \left(
    \begin{array}{c}
      a \\
      b \\
      c
    \end{array}
  \right)
\bullet
 \left(
    \begin{array}{c}
      x \\
      y \\
      1
    \end{array}
  \right)=0



```
この式より上だと子供(1)で、下だと大人(-1)なわけです。
すなわち

```math:
 \left(
    \begin{array}{c}
      a \\
      b \\
      c
    \end{array}
  \right)
\bullet
 \left(
    \begin{array}{c}
      x \\
      y \\
      1
    \end{array}
  \right)
```
が正なら1、負なら-1を返す関数(ヘビサイド関数といいます)が欲しいのです。

```math
\Phi
\left(
 \left(
    \begin{array}{c}
      a \\
      b \\
      c
    \end{array}
  \right)
\bullet
 \left(
    \begin{array}{c}
      x \\
      y \\
      1
    \end{array}
  \right)
\right)=
  \begin{cases}
    　1 \left( \left(
    \begin{array}{c}
      a \\
      b \\
      c
    \end{array}
  \right)
\bullet
 \left(
    \begin{array}{c}
      x \\
      y \\
      1
    \end{array}
  \right)\geq0\right) \\
    -1 \left( \left(
    \begin{array}{c}
      a \\
      b \\
      c
    \end{array}
  \right)
\bullet
 \left(
    \begin{array}{c}
      x \\
      y \\
      1
    \end{array}
  \right)\lt0\right)
  \end{cases}
```

このように内積を「正なら〇、負なら□」のように変換する関数を活性化関数といいます。

<sub><sub>活性化関数には微分可能な数学関数を使う場合もあるのですが(シグモイド関数など)、単純パーセプトロンの場合はヘビサイド関数のほうが良いことがわかっています。学習(PDCAのCA)時に便利な関数を選ぶ必要があるため、「相性」があるのです。</sub></sub>

\### 1-1-2. パーセプトロンPDCAの「C」―損失関数

出力と「正解」の、方向や大きさを数量的に評価するのが「損失関数」です。「誤差」と考えてもよいでしょう
今回は単に「$正解-出力$」でよいでしょう。

- 出力が正解に一致した場合、損失関数は0となります。
- 子供(1)が正解のときに出力が大人(-1)となってしまった場合、損失関数は1-(-1)=2となります。
- 大人(-1)が正解のときに出力が子供(1)となってしまった場合、損失関数は-1-1=-2となります。

符号の有無により、子供→大人なのか、大人→子供なのか、修正の方向が示され、大きさにより、修正の必要性の有無が示されます。

\### 1-1-3. パーセプトロンPDCAの「A」

損失関数をもとに、$a,b,c$を更新する








<!--
それに「近いこと」をしてくれる関数の一つに「シグモイド関数」というものがあります。

```math
出力=\frac{1}{1+\exp
\left(
- \left(
    \begin{array}{c}
      a \\
      b \\
      c
    \end{array}
  \right)
\bullet
 \left(
    \begin{array}{c}
      x \\
      y \\
      1
    \end{array}
  \right)
\right)}
```
とするのです。

\### 1-1-2. パーセプトロンのPDCAのC―二乗和で誤差を定量化

<!--<sub><sub>※本来、パーセプトロンによる機械学習でPDCAの「C」の過程は不要ですが、ニューラルネットワークと対応させて説明することで理解を容易にするものと判断し、本節を執筆しました。読者の皆様におかれましては、各自で判断のうえ、1-1-2節は飛ばして1-1-3節から読んでいただいてもかまいません。
(ニューラルネットワークでは誤差の「方向」と「相対的な大きさ」しか求められないのに対し、パーセプトロンでは誤差の絶対的な大きさ(理論的に最も正しいパラメータ)を直接求めることができます。その関係で、パーセプトロンでは実際にはPDCAのCの過程は不要で、Dの直後にAを実行することが可能です。)</sub></sub> 間違ってるかも - ->

1-1-1節で述べたとおりの出力が、正解にどれだけ近いのかを確認します。
シグモイド関数は引数が正であるとき1に、負であるときに0に「近い」数値を返却する「滑らかな関数」です。
滑らかなので、1と0の間が存在します。例えば引数が完全に0だったなら、シグモイド関数は0.5を返却します。
しかもシグモイド関数は完全な0や1を返却することはありません。0.9996とか0.0001とかを返却します。
この特徴のおかげで、「正解にどれだけ近いのか」、「誤差はどのくらいなのか」を計算することができるわけです。
誤差の大きさを数値で表現する技の一つに「二乗和」というのがあります。正解(完全な「0または1」)と出力結果(0超1未満)の
差を2乗し、その総和を求めるのです。

例えばパラメータを$a=1, b=2, c=3$としていたとしましょう。
パーセプトロンの出力は

```math
\frac{1}{1+\exp(-x-2y-3)}

```
という式で表されます。
「$x=1.30, y=0.8, 子供(1)$」
「$x=1.70, y=0.55, 大人(0)$」
「$x=1.65, y=0.6, 大人(0)$」
というデータを入力すると、
出力はそれぞれ

```math
\frac{1}{1+\exp(-1.30-1.6-3)} =0.99726804... \\
　\\
\frac{1}{1+\exp(-1.70-1.1-3)} =0.99698158... \\
　\\
\frac{1}{1+\exp(-1.65-1.2-3)} =0.99712837...

```

となります。
二乗和により誤差を求めると、

```math
(1-0.99726804)^2+(0-0.99698158)^2+(0-0.99712837)^2=1.98824472
```

となります。


\### 1-1-3. パーセプトロンのPDCAのA―最小二乗法

1-1-2節で求めた誤差を最小にするような$a,b,c$を求めてみましょう。
二乗和を、$a,b,c$の文字を使って表すと

```math
\left(1-\frac{1}{1+\exp(-1.30a-0.8b-c)}\right)^2
+\left(0-\frac{1}{1+\exp(-1.70a-0.55b-c)}\right)^2
+\left(0-\frac{1}{1+\exp(-1.65a-0.6b-c)}\right)^2
```
[拙ブログ:シグモイド関数が(0, 0.5)に対して点対称であることの証明](https://912.hateblo.jp/entry/2020/03/04/125622)
より、

```math
\left(1-\frac{1}{1+\exp(-Y)}\right)^2=\left(\frac{1}{1+\exp(Y)}\right)^2
```

が成り立つことを使うと、二乗和は

```math
\left(\frac{1}{1+\exp(1.30a+0.8b+c)}\right)^2
+\left(\frac{1}{1+\exp(-1.70a-0.55b-c)}\right)^2
+\left(\frac{1}{1+\exp(-1.65a-0.6b-c)}\right)^2
```

と整理することができます。

実数の範囲では、二乗和が原理的に取りうる最小値は0です。
したがって、誤差(二乗和)を最小にしたければ、0にすればよいわけです。
これを0にするためには、

```math
\frac{1}{1+\exp(1.30a+0.8b+c)}=\frac{1}{1+\exp(-1.70a-0.55b-c)}=\frac{1}{1+\exp(-1.65a-0.6b-c)}=0

```

にする他ありません。


ただし、これは実現不可能(シグモイド関数は0超1未満だからです)で、実際にできるのは「近づける」ということだけです。

```math
1.30a+0.8b+c→∞ \\
-1.70a-0.55b-c→∞ \\
-1.65a-0.6b-c→∞ \\
1.30a+0.8b+c = -1.70a-0.55b-c = -1.65a-0.6b-c = n \\
最後の式は条件ではないが、この定義が矛盾を引き起こすこともない
```

$n$は限りなく大きくしたいところですが、そうしてしまうと$a,b,c$のいずれかまたはすべてについて絶対値が発散してしまい、議論ができなくなってしまいます。そこで、$n$は正の十分大きな数という条件付きで「ハイパーパラメータ」ということにしてしまいましょう。

例えば$n=10000$としたとき、



http://hokuts.com/2015/11/25/ml2_perceptron/

-->