2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-12-12

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

##作ってみる

\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では行列の積は

import numpy as np

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

C = np.dot(A,B)
print(C)

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

\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]}\\
AB=C\hspace{30px}\normalsize c_{ik}={\large\displaystyle \sum_{\tiny j}}a_{ij}b_{jk}\\

forループを使って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
先頭のブロックから説明します
スクリーンショット (64).png
まず最初に行列計算ライブラリ戻り値を初期化します

0.png

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

0.3,0.4|0.5,0.6

となっているところを
スクリーンショット (63).png
変数を行列リスト状態に分解します。
行列展開1は行列計算ライブラリ一時的置き場1に保存します。
5.png
次のブロックは行列の積を計算するときに何回縦に計算をするのかを確認するために行の長さを計っています
8.png
次のブロックは大体同じことします違うところは行列展開1には行列計算ライブラリ引数1行列リストに保存されているため競合しないように保存先が行列展開2になっているところです。
888.png
次のブロックは行列計算ライブラリ一時的置き場2をリストの1番目をにベクトル展開関数の引数にしています
ベクトル展開は行列展開と同じように
特定の行列リストから取り出したベクトル展開関数に引数にします。
今回れどこの行列リストでもいいので確実にある1番指定しています
ベクトル展開は

0.3,0.4

ベクトルを
スクリーンショット (67).png
のようにベクトルをスカラー状態に分解します。
行列展開1はベクトル展開戻り値リストに保存します。
s.png
行の長さを保存していた理由と同じで次のブロックは行列の積を計算するときに何回横にに計算をするのかを確認するためにベクトルの長さを計っています
scratchにはカウンタ機能がないためカウンタ変数を初期化しています
これで前準備はすべて終わりました
行列の積の公式が
スクリーンショット (68).png

\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の長さが変われば1つ目の行列の積lrのCの列の長さも変わるた行列Aの行の長さ回繰り返すことにしています。
その次のブロックに
最初はカウンタを一増やして
二つめのカウンタを初期化しています
66.png
このベクトル展開入力値1用関数はベクトル展開1と同じ挙動をしますが保存先のリストが行列の積行列ライブラリ引数入力値1一時的置き場に変わります。理由はリストが競合しないようにです。
計算式を見るとaの行列取り出しににiのカウンタが使われてるため今のうちに展開しています
そして次に行列の積の結果を一時的に保存する変数を初期化しています
-.png
さっきと原理は同じで公式を見ると結果の長さはCの右下を見るとCikなっているため行列Bの列の長さであるkの長さが変われば1つ目の行列の積のCの列の長さも変わるた行列Bの列の長さ回繰り返すことにしています。
3つ目のカウンタを初期化して
次のループでΣが出てくるため一時的な置き場必要なので行列の積一時的を初期化します
06.png
まず最初はj(行列計算ライブラリカウンタ3)を一増やします

\hspace{30px}\normalsize c_{ik}={\large\displaystyle \sum_{\tiny j}}a_{ij}b_{jk}\\

さっきと原理は同じで上の計算式を見るとシグマになっていてj回繰り返すことになっているので行列Aの列の長さ回繰り返して足していきます。
行列と行列の一時的置き場のベクトルのj(行列計算ライブラリカウンタ3)番目を入れています
bの方はj(行列計算ライブラリカウンタ3)が先に指定されているためベクトル展開を変数を行列リストに展開したj(行列計算ライブラリカウンタ3)番目を展開することになります。
その次に指定されているのはk(行列計算ライブラリカウンタ2)なので行列の積計算用一時的2にはベクトル展開戻り値のk(行列計算ライブラリカウンタ2)番目を入れます
そして次は行列の積一時的に行列の積一時的+行列の積計算用一時的1*行列の積計算用一時的2にします。
スクリーンショット (72).png

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

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?