最小二乗法をビジュアル・プログラミング・エディタで書いてみる

# はじめに

[数理的な分野への適用を重視した](https://qiita.com/cocoakamen/items/4380d8151c7c44033907#%E6%95%B0%E7%90%86%E7%9A%84%E3%81%AA%E5%88%86%E9%87%8E%E3%81%AB%E7%84%A6%E7%82%B9%E3%82%92%E5%BD%93%E3%81%A6%E3%81%A6%E3%81%BF%E3%82%8B)ブロック型のビジュアル･プログラミング･エディタであるところの[K3Tunnel](https://k3tunnel.com)。

「数理的分野」のひとつの例として「本の厚さと重さ」の関係を表す直線の式を最小二乗法を使って求める例を紹介しています。初歩的な機械学習の例でもあり、やや骨太な内容になっています。正直言うと、この内容で最小二乗法で直線の式を決めるだけなら、Excelのグラフでちょろっとやればいいんじゃないの？という気もしますが、その裏で何をしているのか見えるようにしていきたいと思います。

なお、プログラムコードは、[Loquat](https://loquat.md)のエンジニアの方に協力していただいて作成しました。

# プログラム例

ものはコチラです。
https://k3tunnel.com/k3t/sampleWork.html?sampleId=A0027

# 使うデータ

本の厚さと重さの実データから、その関係を表す式を求めていきます。

|書籍名            |厚さ[cm]|重さ[g]| 
|:-----------------|-------:|------:|
|[謎の独立国家ソマリランド](http://www.webdoku.jp/column/takano/)|4.2|677|
|[天地明察](https://www.kadokawa.co.jp/product/200907000044/)|3.5|552|
|[FACTFULNESS](https://www.nikkeibp.co.jp/atclpubmkt/book/19/P89600/)|2.8|520|
|[フーガはユーガ](http://www.j-n.co.jp/fuga_yuga/)|2.6|404.5|
|[ライフロング・キンダーガーテン](https://shop.nikkeibp.co.jp/front/commodity/0000/P55550/)|2.5|412|
|[鳥類学者無謀にも恐竜を語る](https://books.google.co.jp/books?id=wYan8Q0sFsoC&pg=)|2|385|
|[ゾンビ対数学](https://gihyo.jp/book/2018/978-4-7741-9921-4)|1.7|329.5|
|[鉄と鉄鋼がわかる本](https://www.njg.co.jp/book/9784534038357/)|1.5|358.5|
|[ルビィの冒険](https://www.shoeisha.co.jp/book/rubynobouken/)|1.3|423|
|[へんしんトンネル](https://www.kinnohoshi.co.jp/search/info.php?isbn=9784323033495)|1|388|

上記データは、すべて実測値です。

重さをはかっているところ。
![10_book_weight.jpg](https://qiita-image-store.s3.amazonaws.com/0/308852/d39f5309-9ade-3670-aa01-6c7d511e34ab.jpeg)

厚さはかなりアバウトです。
![10_book_width.jpg](https://qiita-image-store.s3.amazonaws.com/0/308852/ebe823c8-8dd7-cbfe-2434-eb74e6355454.jpeg)

こんな感じで地道に10冊すべて測定しました。

# とりあえず実行してみる

何をともあれ、実行してみると、こうなります。

![19_result.jpg](https://qiita-image-store.s3.amazonaws.com/0/308852/d07642bb-4e79-a1b3-57d3-d72734907ffe.jpeg)


右のグラフは、ピンクの〇が実データ。青い線が、プログラムで求めた式の直線。
左の表は、まあ、いろいろです。

# コードを上から眺めていきます

## 前半は、実データを確認しているだけ

まずはこの部分。
![20_part1.jpg](https://qiita-image-store.s3.amazonaws.com/0/308852/99b9eb76-242a-ed25-efe3-170c114cdb02.jpeg)


みたまんまですが、ざっくりまとめるとこんな感じ。

1. 本の厚さと本の重さのリストを作って変数に入れる。
2. 横軸を本の厚さ、縦軸を本の重さにしたグラフにデータをプロットしていく。
3. リストの内容を表に出力。
4. 厚さと重さの平均を計算。（合計値÷データ数）
5. 計算した平均値を表に出力


ここまでで実行するとこう。
![21_part1_result.jpg](https://qiita-image-store.s3.amazonaws.com/0/308852/dd7d77d3-6b60-971f-ff9a-57b2c0b7ab56.jpeg)

ここまでは、K3Tunnelのクセはあるものの、特に難しいことはしていません。
このあと、シグマだの偏差だの出てくるので、いったんコードから離れます。

## このプログラムのゴール

### 直線を表す数式を求めたい

直線を表す式が、y = ax + bであることは、中学生のときに習いました。ここでは、yが本の重さ、xが本の厚さということになります。未知のパラメータa, bを決めてこの式を求める。というのが、このプログラムのゴールです。
 
![22_line.jpg](https://qiita-image-store.s3.amazonaws.com/0/308852/9dcaa909-49d0-7f46-f2f0-ac74833118bb.jpeg)

### a, bを求める数式

最小二乗法を使うと、a, bは、以下の式で表すことができます。
![23_model.jpg](https://qiita-image-store.s3.amazonaws.com/0/308852/cd1e9800-8095-9f82-b25b-f9de66c4db45.jpeg)


なぜこうなるかは、おいておいて、この式を使ってa, bを計算します。
Σは全部足す。文字の上に線があるやつは、平均値という意味です。

### aを求める数式を日本語にする

今回の問題設定にあわせて、aを求める数式を日本語にしてみます。

**まずは分子から**
![23_model1.jpg](https://qiita-image-store.s3.amazonaws.com/0/308852/5490e7e6-3f54-b3db-0d9f-fe50256485bf.jpeg)

1. すべての本について、`(本の厚さ - 厚さの平均）× (本の重さ - 重さの平均)`を計算する
2. 上で求めた数値を全部足す

プログラム例では、この値を**「偏差のシグマ」**と呼んでいます。

**次は分母**
![23_model2.jpg](https://qiita-image-store.s3.amazonaws.com/0/308852/7c10910a-0c3f-3370-d071-bea6db38d60f.jpeg)

1. すべての本について、`(本の厚さ - 厚さの平均）の二乗`　を計算する
2. 上で求めた数値を全部足す

プログラム例では、この値を**「偏差の二乗シグマ」**と呼んでいます。

最終的にaは以下で求められます。
![23_model3.jpg](https://qiita-image-store.s3.amazonaws.com/0/308852/48d79992-0bd9-e751-35fb-fc6ff806f339.jpeg)


### bを求める数式を日本語にする

bを求める数式も、一応、日本語にしておきます。

`b = (重さの平均）- a × (厚さの平均)`

## aの分母と分子をそれぞれ計算する

プログラムに戻ります。
リストに入れたデータをひとつずつ取り出してきて、「偏差の二乗シグマ」と「偏差のシグマ」を計算しています。

![22_part2.jpg](https://qiita-image-store.s3.amazonaws.com/0/308852/a4653c77-94d0-b36a-0e0d-e40fd8b485ff.jpeg)

見たまんまですが、日本語にするとこんな感じ。
1. 変数を初期化
2. 本のデータの1番目から最後まで③から⑦までの処理を繰り返す
3. `(本の厚さ - 厚さの平均）`を計算して「厚さの偏差」とする
4. `(本の重さ - 重さの平均）`を計算して「重さの偏差」とする
5. 「偏差の二乗シグマ」変数に`「厚さの偏差」の二乗`を足していく（繰り返しのたびに値が増える）
6. 「厚さの偏差」×　「重さの偏差」を計算する
7. 「偏差のシグマ」変数に`「厚さの偏差」×「重さの偏差」`を足していく（繰り返しのたびに値が増える）
8. 「偏差の二乗シグマ」と「偏差のシグマ」を表に出力する

ここまでで、a を求める用意ができました。

## a,b を計算する

その次は、計算式に代入した結果を、表出力しているだけです。
![24_part3.jpg](https://qiita-image-store.s3.amazonaws.com/0/308852/5021b64a-feda-54da-0cea-79b020a77546.jpeg)


## もとめた直線をかく

最後のパート。表示するためにごちゃごちゃやっています。

![26_part5.jpg](https://qiita-image-store.s3.amazonaws.com/0/308852/5da8680c-b0f7-4ff8-987d-49ae9e880347.jpeg)

1. y = ax + b　の形で表示するために文字列操作。
2. 式に値を代入して、グラフにプロットして線をつなぐ。

## 完成

![19_result.jpg](https://qiita-image-store.s3.amazonaws.com/0/308852/d07642bb-4e79-a1b3-57d3-d72734907ffe.jpeg)

この式を使えば、「厚さを測れば、重さを推定できるようになる。(似たような紙質であれば)」というわけです。紙質まで考慮しようとすると、どーんと難しくなりますが、それは、また別のお話。

## 自分でいろいろやってみるには

[プログラム例の画面](https://k3tunnel.com/k3t/sampleWork.html?sampleId=A0027)にて、「コピーしてプロジェクト作成」すると、編集できるようになります。プログラム例の画面ではブロックを動かすこともできません。。

![01_copyNew.jpg](https://qiita-image-store.s3.amazonaws.com/0/308852/097b37b9-7ca7-a2bb-348b-9c114c8cd8a4.jpeg)

これをクリックしたのち、確認画面で「新規作成」をクリックすると編集画面に移ります。




# おわりに

自分で最小二乗法を実装することは、もうないような気がします。