Deep Learningを学ぶべくゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装を読んだ。理解を深めるのとRustの学習を兼ねて、本書に書かれた内容をRustに書いてみる。なお筆者はDeep LearningにもRustにも、行列にも精通していない。
p26 2.3.2 重みとバイアスの導入
パーセプトロンのANDゲートを実装するのに、行列の計算に置き換える箇所。
行列の内積後、バイアスを加える処理をするところ。
行列の計算にはnagebraを使うことにした。
#[test]
fn page_26() {
let x = Matrix1x2::new(0.0, 1.0);
println!("{}", x);
let w = Vector2::new(0.5, 0.5);
println!("{}", w);
let b = Vector1::new(-0.7); // x * w の結果はVector1になる。プリミティブ型を直接演算はできないので、Vector1に持ち上げておく。
println!("{}", b);
let sum = x * w + b;
println!("{}", sum);
let expect = Vector1::new(-0.2);
let range = Vector1::new(0.0001);
assert!(sum < expect + range);
assert!(sum > expect - range);
}
$ cargo test page_26 -- --nocapture
running 1 test
┌ ┐
│ 0.000 1.000 │
└ ┘
┌ ┐
│ 0.500 │
│ 0.500 │
└ ┘
┌ ┐
│ -0.700 │
└ ┘
┌ ┐
│ -0.200 │
└ ┘
test page_26 ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out
Vector2は 2x1の行列のエイリアスになっているので、Vector2同士の内積は計算できない。
Matrix1x2をつかっていますが、Vector2をtransposeしてもOKです。
参考文献
p48 シグモイド関数の実装
ベクターにシグモイド関数を適用できればよさそうなので、スカラー値用のシグモイド関数を用意してmapメソッドがつかうとよさそう。
こんな感じで使えるようにする。
Vector3::new(-1.0, 1.0, 2.0).map(sigmoid)
シグモイド関数の実装はこんな感じ。expの計算はf64しかなさそうなので、f64のみの実装にする。
fn sigmoid(x: f64) -> f64 {
1.0 / (1.0 + f64::exp(-x))
}
つかってみる。
#[test]
fn page_49() {
let x = Vector3::new(-1.0, 1.0, 2.0).map(sigmoid);
// let x = Vector3::new(-1.0, 1.0, 2.0).sigmoid();
println!("{}", x);
let range = 0.00000001;
let expect_x1 = 0.26894142;
assert!(x[0] < expect_x1 + range);
assert!(x[0] > expect_x1 - range);
let expect_x2 = 0.73105858;
assert!(x[1] < expect_x2 + range);
assert!(x[1] > expect_x2 - range);
let expect_x3 = 0.88079708;
assert!(x[2] < expect_x3 + range);
assert!(x[2] > expect_x3 - range);
}
$ cargo test page_49 -- --nocapture
running 1 test
┌ ┐
│ 0.2689414213699951 │
│ 0.7310585786300049 │
│ 0.8807970779778823 │
└ ┘
test page_49 ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 3 filtered out
参考書の結果と一致する。
参考文献
p53 多次元配列
NumPyをつかった多次元配列の扱いを学ぶ部分です。
#[test]
fn page_53_1() {
let a = Vector4::new(1.0,2.0,3.0,4.0);
println!("{}", a);
// 次元数の取得
println!("{}", a.nrows()); // => 4
// 行列の形状
let (rows, columns) = a.shape();
println!("{}, {}", rows, columns); // => 4, 1
}
$ cargo test page_53_1 -- --nocapture
running 1 test
┌ ┐
│ 1.000 │
│ 2.000 │
│ 3.000 │
│ 4.000 │
└ ┘
4
4, 1
test page_53_1 ... ok
別の形状の行列を確認します。
#[test]
fn page_53_2() {
let b = Matrix3x2::new(
1,2,
3,4,
5,6);
println!("{}", b.nrows()); // => 3
let (rows, columns) = b.shape();
println!("{}, {}", rows, columns); // =>s 3, 2
}
$ cargo test page_53_2 -- --nocapture
running 1 test
3
3, 2
test page_53_2 ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out
参考文献
p64 3.4.3 実装のまとめ
3層ニューラルネットワークを実装しようという部分。
ネットワークの構築関数であるforward配下のように実装。
fn forward(
w1: Matrix2x3<f64>,
w2: Matrix3x2<f64>,
w3: Matrix2<f64>,
b1: Matrix1x3<f64>,
b2: Matrix1x2<f64>,
b3: Matrix1x2<f64>,
x: Matrix1x2<f64>,
) -> Matrix1x2<f64> {
let a1 = x * w1 + b1;
let z1 = a1.map(sigmoid);
let a2 = z1 * w2 + b2;
let z2 = a2.map(sigmoid);
let a3 = z2 * w3 + b3;
identity_function(a3)
}
fn identity_function<T>(x: T) -> T {
x
}
関数合成で実装してみたくなるところだけど、先に勧めたいので割愛。
書籍内では2引数であるが、第1引数は展開した状態にした。1引数を受け取るクロージャを返す用にすると意図が合うようになりそうだけど、割愛。
実行してみます。
#[test]
fn page_64() {
let w1 = Matrix2x3::from_rows(
&[
RowVector3::new(0.1, 0.3, 0.5),
RowVector3::new(0.2, 0.4, 0.6),
],
);
let b1 = Matrix1x3::new(0.1, 0.2, 0.3);
let w2 = Matrix3x2::from_rows(
&[
RowVector2::new(0.1, 0.4),
RowVector2::new(0.2, 0.5),
RowVector2::new(0.3, 0.6),
],
);
let b2 = Matrix1x2::new(0.1, 0.2);
let w3 = Matrix2::from_rows(&[RowVector2::new(0.1, 0.3), RowVector2::new(0.2, 0.4)]);
let b3 = Matrix1x2::new(0.1, 0.2);
let x = Matrix1x2::new(1.0, 0.5);
let y = forward(w1, w2, w3, b1, b2, b3, x);
println!(" = {}", y);
let expect_y1 = 0.31682708;
let expect_y2 = 0.69627908;
let range = 0.00000001;
assert!(y[0] < expect_y1 + range);
assert!(y[0] > expect_y1 - range);
assert!(y[1] < expect_y2 + range);
assert!(y[1] > expect_y2 - range);
}
$ cargo test page_64 -- --nocapture
running 1 test
=
┌ ┐
│ 0.3168270764110298 0.6962790898619668 │
└ ┘
test page_64 ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 4 filtered out
読みやすくなるようにfrom_rowsをつかった。
https://github.com/eiel/reading-deeplearning-for-rust/blob/798efb46038a45333191510ddcd2a40aab2aca50/src/main.rs#L12-L31
https://github.com/eiel/reading-deeplearning-for-rust/blob/798efb46038a45333191510ddcd2a40aab2aca50/src/main.rs#L96-L127