scratchでディープラーニングを作ってみた①行列の積

#scratchでディープラーニングを作ってみた
「ゼロから作るDeep Learning: Pythonで学ぶディープラーニングの理論と実装」をpythonで実装しているときに行列の積などは最適出来ないため計算にはとても時間がかかるけど、もしかしたらscratchの標準機能だけで機械学習を実装できるのではないかと思ったため「ゼロから作るDeep Learning: Pythonで学ぶディープラーニングの理論と実装」の本を参考にしてscratchで機械学習を作ってみました。
最初から詳しく解説してみようかと思います。

##作ってみる
```math
\begin{pmatrix}
0.3 & 0.4 \\
0.5& 0.6
\end{pmatrix}
```
scratchでは一次元配列しか使用できないため自分で二次元配列の作る必要があります
一次元配列にするため。

```
0.3,0.4
0.5,0.6
```
リストだと違うリストにデーターを移動するのが面倒なので
二次元配では下のようにあらわすところを

```
0.3,0.4|0.5,0.6
```
と行列を変換することにします。
###行列の積実装
numpyでは行列の積は

```python
import numpy as np

A = np.random.randn(2,4)
B = np.random.randn(2,4)

C = np.dot(A,B)
print(C)
```
と簡単に実装できます。
しかしscratchにはそういう行列などを扱う拡張機能がないため自分で作る必要があるため最初に行列の積の処理を実装することにします
行列の積の公式はこんな感じなので

```math
\hspace{30px}AB=C\\ \normalsize{\left[\begin{array}\\ a_{\small 11}& \cdots& a_{\small 1j}\\ a_{\small 21}& \cdots& a_{\small 2j}\\ \vdots& \ddots& \vdots\\a_{\small i1}& \cdots& a_{\small ij}\\\end{array}\right]} {\left[\begin{array}\\ b_{\small 11}& \cdots& b_{\small 1k}\\ b_{\small 21}& \cdots& b_{\small 2k}\\ \vdots& \ddots& \vdots\\b_{\small j1}& \cdots& b_{\small jk}\\\end{array}\right]}={\left[\begin{array}\\ c_{\small 11}& \cdots& c_{\small 1k}\\ c_{\small 21}& \cdots& c_{\small 2k}\\ \vdots& \ddots& \vdots\\c_{\small i1}& \cdots& c_{\small ik}\\\end{array}\right]}\\
```
```math
AB=C\hspace{30px}\normalsize c_{ik}={\large\displaystyle \sum_{\tiny j}}a_{ij}b_{jk}\\
```
forループを使ってpythonで実装するとこんな感じになります

```python
A = np.random.randn(2,4)
B = np.random.randn(2,4)

for i in range(A.shape[0]):
    for j in range(B.shape[1]):
        for k in range(B.shape[0]):
            C[i][j] = B[i][k] * B[k][j]
```
となるのでscratch作ると下の図のようになります![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651557/fa67b7f8-da43-a8c9-c009-c789324f57c5.png)
先頭のブロックから説明します
![スクリーンショット (64).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651557/00a40c3a-caf8-324c-b752-75326a945d71.png)
まず最初に行列計算ライブラリ戻り値を初期化します

![0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651557/9e313f48-f9d9-374c-8dc7-1dcaf01ffb4f.png)

次のブロックはscratchでいう関数で
行列ライブラリ引数行列1の引数の変数を関数の引数にしています
行列展開1は

```
0.3,0.4|0.5,0.6
```
となっているところを
![スクリーンショット (63).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651557/3adc1dab-4c55-0284-ebe4-0f27650b74d4.png)
変数を行列リスト状態に分解します。
行列展開1は行列計算ライブラリ一時的置き場１に保存します。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651557/e0a3fa7f-64d2-98c3-6b71-171b7dcd2528.png)
次のブロックは行列の積を計算するときに何回縦に計算をするのかを確認するために行の長さを計っています
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651557/338053b0-43e5-ab9f-a817-be32da99f1d3.png)
次のブロックは大体同じことします違うところは行列展開1には行列計算ライブラリ引数１行列リストに保存されているため競合しないように保存先が行列展開2になっているところです。
![888.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651557/b8bddd23-87eb-5f9a-8b93-c7aa1bff0418.png)
次のブロックは行列計算ライブラリ一時的置き場2をリストの１番目をにベクトル展開関数の引数にしています
ベクトル展開は行列展開と同じように
特定の行列リストから取り出したベクトル展開関数に引数にします。
今回れどこの行列リストでもいいので確実にある１番指定しています
ベクトル展開は

```
0.3,0.4
```
ベクトルを
![スクリーンショット (67).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651557/faa50c55-d099-3731-9f18-afccb31ba605.png)
のようにベクトルをスカラー状態に分解します。
行列展開1はベクトル展開戻り値リストに保存します。
![s.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651557/4e9b489b-663a-c11a-db6d-466627c8d772.png)
行の長さを保存していた理由と同じで次のブロックは行列の積を計算するときに何回横にに計算をするのかを確認するためにベクトルの長さを計っています
scratchにはカウンタ機能がないためカウンタ変数を初期化しています
これで前準備はすべて終わりました
行列の積の公式が
![スクリーンショット (68).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651557/b7de39b3-14cf-11e0-2e5a-77f3114c992d.png)

```math
\hspace{30px}AB=C\\ \normalsize{\left[\begin{array}\\ a_{\small 11}& \cdots& a_{\small 1j}\\ a_{\small 21}& \cdots& a_{\small 2j}\\ \vdots& \ddots& \vdots\\a_{\small i1}& \cdots& a_{\small ij}\\\end{array}\right]} {\left[\begin{array}\\ b_{\small 11}& \cdots& b_{\small 1k}\\ b_{\small 21}& \cdots& b_{\small 2k}\\ \vdots& \ddots& \vdots\\b_{\small j1}& \cdots& b_{\small jk}\\\end{array}\right]}={\left[\begin{array}\\ c_{\small 11}& \cdots& c_{\small 1k}\\ c_{\small 21}& \cdots& c_{\small 2k}\\ \vdots& \ddots& \vdots\\c_{\small i1}& \cdots& c_{\small ik}\\\end{array}\right]}\\
```
公式を見ると結果の長さはCの右下を見るとCikなっているため行列Aの行の長さであるkの長さが変われば１つ目の行列の積lrのCの列の長さも変わるた行列Aの行の長さ回繰り返すことにしています。
その次のブロックに
最初はカウンタを一増やして
二つめのカウンタを初期化しています
![66.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651557/5a10e606-3db6-a74c-3721-cbdd3a757ce8.png)
このベクトル展開入力値１用関数はベクトル展開1と同じ挙動をしますが保存先のリストが行列の積行列ライブラリ引数入力値１一時的置き場に変わります。理由はリストが競合しないようにです。
計算式を見るとaの行列取り出しににiのカウンタが使われてるため今のうちに展開しています
そして次に行列の積の結果を一時的に保存する変数を初期化しています
![-.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651557/5c22b6b6-c859-cb35-955f-056173626d6b.png)
さっきと原理は同じで公式を見ると結果の長さはCの右下を見るとCikなっているため行列Bの列の長さであるkの長さが変われば１つ目の行列の積のCの列の長さも変わるた行列Bの列の長さ回繰り返すことにしています。
３つ目のカウンタを初期化して
次のループでΣが出てくるため一時的な置き場必要なので行列の積一時的を初期化します
![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651557/aaa01ca1-e9b9-90e5-1e60-97ad7583b1b2.png)
まず最初はj（行列計算ライブラリカウンタ３）を一増やします

```math
\hspace{30px}\normalsize c_{ik}={\large\displaystyle \sum_{\tiny j}}a_{ij}b_{jk}\\
```
さっきと原理は同じで上の計算式を見るとシグマになっていてj回繰り返すことになっているので行列Aの列の長さ回繰り返して足していきます。
行列と行列の一時的置き場のベクトルのj（行列計算ライブラリカウンタ３）番目を入れています
bの方はj（行列計算ライブラリカウンタ３）が先に指定されているためベクトル展開を変数を行列リストに展開したj（行列計算ライブラリカウンタ３）番目を展開することになります。
その次に指定されているのはk（行列計算ライブラリカウンタ2）なので行列の積計算用一時的２にはベクトル展開戻り値のk（行列計算ライブラリカウンタ2）番目を入れます
そして次は行列の積一時的に行列の積一時的+行列の積計算用一時的１*行列の積計算用一時的２にします。
![スクリーンショット (72).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651557/2b61da63-968a-d9a2-8d8f-408c3db2ac75.png)

次は計算式から出た結果をもとに行列を作成します最後以外は,が必要なので最後なのかを判定して最後以外には,は付けません
次も大体同じで最後以外は|が必要なので最後なのかを判定して最後以外には|は付けません
これで行列の積の結果が分かります行列計算ライブラリ戻り値に入っています
##処理速度の問題
しかしscratchはvm環境で動いているJavaScriptとは処理速度に速度に差が出てしまいます、なのでforkphorusなどを使いjavascriptに変換して実行しましたがあまり早くならず理由としては２次元配列が使えないためいちいち展開作業やforループを行列を計算しているためだと思います使っているため処理速度遅くなっている要因だと思います、解決策としてはscratch拡張機能からWebGLを使って最適化された環境で行列の積を計算するなどがあります。
次回が活性化関数についてです