Help us understand the problem. What is going on with this article?

[WIP]Rustで「ゼロからつくるDeep Learning」

More than 1 year has passed since last update.

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);
}

https://github.com/eiel/reading-deeplearning-for-rust/blob/4a9f0ffe5174265ade457a5c9f540553fd08d42d/src/main.rs#L8-L31

$ 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

https://github.com/eiel/reading-deeplearning-for-rust/blob/67a99aabf3e9b1f2db4240e1cd3f19627809a826/src/main.rs#L26-L35

別の形状の行列を確認します。

#[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

https://github.com/eiel/reading-deeplearning-for-rust/blob/67a99aabf3e9b1f2db4240e1cd3f19627809a826/src/main.rs#L37-L46

参考文献

http://nalgebra.org/rustdoc/nalgebra/core/struct.Matrix.html

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

リポジトリ

参考文献

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away