selecao3のブログ

技術系の備忘録

DeepLearningモデルメモ

何これ

自作DeepLearningエンジンを作成しているわけだが、ちょっとわけわかんなくなったのでメモ

モデルの各概要

skip-gram

ターゲットとなる一つの単語から両隣の単語をそれぞれ推測する。
候補の単語が比較的多いので難しい(訓練次第では性能のいいものが作れる)

Word2Vec

両隣の単語からターゲットとなる一つの単語をそれぞれ推測する。
候補の単語が比較的少ないので簡単(しかし、そんなに性能はよくない)

上記2つは単語の並びを含めて推論することができないという問題点を抱えている。
具体的に言えば、(you,say)と(say,you)は同じものと捉える。
この問題点を解決するにはモデルに単語の並びを記憶させることが必要。
その問題点を解決するのが以下のモデル

RNN

ざっくり言うと、何度もループさせることで記憶を保持するモデル。
しかし、大規模なデータを保持することができない。構造上、逆伝搬の際に勾配が消失するか爆発し、記憶が保持できないからである。

LSTM

RNNの強化版。RNNゲートを追加したことで大規模なデータを記憶することができるようになったモデル。

その他メモ

MatMulレイヤ

小規模のデータならMatMulレイヤ(積レイヤ)を使えばいいが大規模のデータになると現実的ではなくなる(極端な話100万の行列×100万の行列をするから)
その代わりにEmbeddingレイヤなどを使って効率よく演算することになる

基本情報技術者試験

基本情報技術者試験受けてきた

結論から言うとおそらく落ちた。来季受ける自分に向けての記事。

試験勉強

一週間前の土日に参考書一気読み。
テストの前日に過去問(平成30年度の春季と秋季)の午前の部を3回、午後の部は春だけ1回。

午前の部

自己採点では7割あったので午前の部に関しては受かってるかも

  • 過去問を連続で3回やったのが良かった。
  • 前日に一気にやったのも良かった(アホみたいにしんどかったけど)

午後の部

アルゴリズムとプログラミングで滅茶苦茶にされた。おそらく午後の部は5割ぐらいしか取れていない。

  • アルゴリズムとプログラミングがヤバいことは分かっていたが、午前の部の勉強だけでもしんどすぎて過去問を1回しかやったことがダメだった

総合

  • アルゴリズムをいくつか暗記した方が良い(やりやすい)と思った。
  • 「午前の部受かってないと話にならんやろw」と思って午前の部だけきっちりやったが本当の敵は午後の部だった。来季は午後の部についてはきっちりやっておきたい。

Rust備忘録〜Trait(トレイト境界), impl〜

Rust備忘録〜Trait(トレイト境界), impl〜

環境

サンプルコード(

https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/traits.html

trait HasArea {
    fn area(&self) -> f64;
}

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl HasArea for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

struct Square {
    x: f64,
    y: f64,
    side: f64,
}

impl HasArea for Square {
    fn area(&self) -> f64 {
        self.side * self.side
    }
}

fn print_area<T: HasArea>(shape: T) {
    println!("This shape has an area of {}", shape.area());
}

fn main() {
    let c = Circle {
        x: 0.0f64,
        y: 0.0f64,
        radius: 1.0f64,
    };

    let s = Square {
        x: 0.0f64,
        y: 0.0f64,
        side: 1.0f64,
    };

    print_area(c);
    print_area(s);
}

Trait

もしHasArea Traitがなければ"T型"にareaメソッドなんてもんねーぞ的なエラーが出る。なぜならT型はどのような型にもなれるため、受け取った引数がどのような型にもなれるT型としか分からないコンパイラコンパイルすることができないからである。
ここでtrait機能を使う。使うとコンパイラT型の引数を取ったメソッドを使ったらこのtraitの中にあるメソッドだけ使ってくれやと伝えることができる。
つまり、TraitというのはT型を制限するシステム(トレイト境界)である。

impl

型にメソッドを実装する。
例えば、Square型とCircle型で同名だけど内容は違うメソッド(areaメソッド)を使いたい場合、implがなければif文で分岐する感じになるが、implを使えば引数がCircle型ならimpl Circleのareaメソッドが、引数がSquare型ならimpl Squareのareaメソッドが使える。

impl Trait for stract

型にそのTraitのメソッドを実装させることができる。
例えば

#[derive(Clone)]
pub struct MatrixOne<T> {
    vec: Vec<T>,
    dim: usize,
}
//配列へアクセスするためにIndexトレイトをMatrixOneに実装
impl<T, I: SliceIndex<[T]>> Index<I> for MatrixOne<T> {
    type Output = I::Output;

    #[inline]
    fn index(&self, index: I) -> &Self::Output {
        Index::index(&self.vec, index)
    }
}

MatrixOne内のVec型のメンバ変数に[x]でアクセスしたい際はIndexトレイトをMatrixOneに実装する。
型にメソッドを実装したい時はimpl Trait for structとするだけで良いのである。強い(あまり理解していない。)
ちなみに

#[derive(Clone)]
pub struct MatrixOne<T> {
    vec: Vec<T>,
    dim: usize,
}

#[derive(Clone)]というのはアトリビュートと呼ばれるもので、こいつもまたコンパイラに機能を伝えてくれるやつである。
#[derive(Trait名)]は、型の上に追記させることでその型にそのTraitのメソッドを実装してくれる。impl何とかかんとかといちいち書かなくて良いのである。
アトリビュート一覧は下のURL
https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/reference.html#attributes

補足

"型にメソッドを実装する"

型にメソッドを実装する=その型だけでしか使えないメソッドを作成する
という意味である。
メリットとしては

  • バグが少なくなる(型でメソッドの挙動を制限できるから)
  • implで述べた通り、型で条件分岐ができるようになる

macでリモート実行やってみた

macでリモート実行やってみた

環境

  • macOS
  • Windows10 1809
  • Git
  • Rust 1.33.0

概要

開発マシン(macOS)でRustのソースコードをクロスコンパイルし、クロスコンパイルで生成したexeファイルをサーバー(Windows10)上でリモート実行する。

手順

1.macOSでRustをクロスコンパイルする
 前回の記事の通り
macOSでWindowsターゲットでクロスコンパイルする - selecao3のブログ

2. macOSにgit をインストール

brew install git

でgit をインストール。
3. サーバーにgit をインストール
今回、chocolateyというWindows用のパッケージマネージャーを使ってgitをインストールした。
https://chocolatey.org/install#installing-chocolatey
上記のURLに従い、chocolateyをインストール

choco install git

※ 念の為、ここでsshdのパスを確認する。

which sshd

/usr/bin と表示されればOK。それ以外の場合は
C:\Program Files\Git\usr\bin
環境変数Pathに追加すること。自分はここで3日間足踏みすることになった(後述)
4. サーバーにリモートレポジトリを作成
今回はD:¥develop¥rust にリモートレポジトリを作成する。

cd D:¥develop¥rust
mkdir sample.git
git init --bare --shared
git config --get-all core.bare # 共有レポジトリになっているかの確認。falseなら次のコマンドを入力
git config --bool core.bare true

5. サーバーでsshdコマンドを実行
6. 開発マシン上でgit clone

git clone {サーバーのユーザー名}@{サーバーのIPアドレス}/d/develop/rust/sample.git

次に、.git/hooks内のファイルを編集する。
〜開発マシン側〜

cd .git/hooks
touch pre-commit
vim pre-commit

# 以下のように編集
#!/bin/sh

scp {開発マシンのローカルレポジトリのルート}/target/x86_64-pc-windows-gnu/debug/hoge.exe {サーバーのユーザー名}@{サーバーのIPアドレス}/d/
# ここまで
chmod a+x pre-receive

※アップロード先のサーバー上のディレクトリは任意の場所でおk

〜サーバー側〜(以下はGit Bushで行うこと)

cd {サーバーのリモートレポジトリのルート}/hooks
touch pre-receive
vim pre-receive

# 以下のように編集
#!/bin/sh

cd /d/
./hoge.exe
# ここまで
chmod a+x pre-receive

問題が無ければ開発マシン上で
git add .
git commit -m "remote test"
git push
とすれば下の画像のような感じになる。はず。
f:id:selecao3:20190406170131p:plain
remote: リモート実行テスト
とあるが、サーバー上で標準出力された文字列はこのように表示される。

つまづいたこと

はじめ、サーバー上ではmicrosoft公式(?)のWindowsのOpenSSH serverをインストールして使っていた。
しかし、そのOpenSSHのsshdを実行するとgit push, git clone が出来なかった
結果として、Gitの/usr/bin配下のsshd.exeを使ったところ無事にgitコマンドが使えるようになった。(解決に3日かかったf**k)

補足

今回は.git/hooks内のスクリプトを利用してリモート実行を行った。
.git/hooksとは、gitのコマンドに応じてスクリプトを自動実行してくれるヤベー奴である。
.git/hooksについて詳しい説明は以下のURL参照
Git - Git フック

最後に

なんでこんなことしたん?

以下の経緯でこんなことをした。

  • Rustで深層学習してみたいな
  • WindowsだとコーディングしにくいからmacOSで開発したいな
  • でも学習(実行)させる時はGPUが搭載されてるWindows側で実行したいな
  • 開発機でクロスコンパイルしたものをサーバーでリモート実行できたらええな
  • リモート実行するかな

っていう感じ

macOSでWindowsターゲットでクロスコンパイルする

macOSWindowsターゲットでクロスコンパイルする

 環境

macOSでRustのコードをWindowsターゲットでクロスコンパイルするのに少々手こずったのでメモ

 

 手順

  1. mingw-w64のインストール
    必要なライブラリをbrew からインストール
    # brew install mingw-w64
  2. ~/.cargo/configに以下の記述を追加
    [target.x86_64-pc-windows-gnu]
    linker = "x86_64-w64-mingw32-gcc"
    
    [target.i686-pc-windows-gnu]
    linker = "i686-w64-mingw32-gcc"
    configファイルがない場合は作成する。
  3. macOSWindowsターゲットでクロスコンパイル
    以下のコマンドを実行
    # rustup target add x86_64-pc-windows-gnu
    # cargo build --target x86_64-pc-windows-gnu

ここまですればイケるらしいが自分は"undefined reference to `__onexitbegin"というようなエラーが出た。

以下は、手順3. まで行ってもうまくクロスコンパイルが出なかった場合 or エラー文の中に"crt2.o"というオブジェクトファイルがある方は以下のコマンドを実行してみるといいかもしれない。

 # cd $HOME

# cd .rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/x86_64-pc-windows-gnu/lib

# mv crt2.o crt2.o.bak 

# cp /usr/local/Cellar/mingw-w64/6.0.0/toolchain-x86_64/x86_64-w64-mingw32/lib/crt2.o ./

参考URL

https://github.com/rust-lang/rust/issues/48272#issuecomment-429596397

crt2.oというオブジェクトファイルがうまく機能していないのは分かったがよく分かんないっす....

ちなみに

Ubuntu 18.04でも同様の症状が出た。

/usr/x86_64-w64-mingw32/lib/crt2.oを.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/x86_64-pc-windows-gnu/libへコピーすれば解決するはず。多分

 

 

spawn rustup ENOENTというエラー

spawn rustup ENOENTというエラー

 環境

問題

VScodeでRustの補完プラグイン"Rust(rls)"を導入したところ、表題のエラーが表示された。

理由

 (おそらく)下記のようなインストール方法をしてたせいでおかしくなった

  • VScode: Windows10由来
  • rustup: WSL由来

解決策

 Windows10でrustupをインストールしたところ解決した。

 

hotplugイベントが発火しない

hotplugイベントが発火しない

 環境

 

問題

 /hogeというマウントポイントに対し、

# mv /etc/network/ /hoge/etc/

# ln -s /hoge/etc/network /etc/network

というコマンドを実行し、再起動させると、起動時に自動でインターネットに繋がらなくなった。

理由

 /hogeがマウントされる前に、カーネルethernetの接続を検知するから。

つまり、/etc/networkが存在していない時点でhotplugイベントが発火したから。

解決策

 /etc/udev/rules.d/60-enp0s3.rules を作成し、下記の一文を書く。

KERNEL=="enp0s3", RUN="/bin/mount /hoge"

経緯

 バイト先で、以下の依頼があった。

 /hogeというマウントポイントがあるのだが、そこに/etc/networkを移動させ、シンボリックリンクで元の/etc/networkに貼ると、起動時にネットに繋がらなくなる。

 原因と対策を考えてほしい。

  そんなこんなで調査が始まった。

 Linuxは遊び程度でしか触ったことがなかったのでそこまで深くは分からなかったが、「ショートカット(シンボリックリンク)貼ったのにネットが自動起動しなくなる」という点がまず不思議だった。

 考えていくうちに「/hogeがマウントされていない、つまり/etc/network/*が存在していないときにethernetが接続されたせいでhotplugイベントが起きなかった」と考えるのが自然だと辿り着いた。

 まず、対策として思いついたのが以下の3つ

  • ルートディレクトリがマウントされる前に/hogeをマウントさせる
  • (Eathernetが繋がったうえで)hotplugイベントのタイミングを弄る
  • systemd.deviceの起動を遅延させる。

1番目:

「ルートディレクトリの前に/hogeがマウントされへんとシンボリックリンクのファイルが認識されへんやん!」ということで思いついた。

fstabのルートディレクトリの項目のoption部分を"noauto"に書き換えることで自動マウントを禁止し、/etc/init.d/rc.localを作成し、その中に"mount /"と書いた。

fstabによってルートディレクトリ以外をマウントさせ、最後にルートディレクトリをマウントしたらいいなぁと思ってやってみた。

結果は失敗で、理由は「Debianを起動させた時点でルートディレクトリ(というよりもブートローダーがインストールされているディレクトリ)はマウントされている」から。あと、「"mount"という/bin/mountコマンドが使えている=ルートディレクトリが既にマウントされている」である。

 2番目:

「hotplugイベントを任意のタイミングに変えれば繋がるんちゃうか!?」っていう感じ。

結果は失敗。というより、遅延の方法が思いつかなかったし、上記に書いた解決方法の方がスマートのように感じた。

3番目:

「deviceの検知を遅延させたろ!」的な発想である。

結果は失敗というか、自分自身systemdについてよく理解していなかったので出来なかった。

ちなみになぜsystemd.deviceの起動遅延に辿り着いたかというと、/var/log/daemon.confにsystem[1] fount device ethernet...みたいなことが書いてあったから。

 

で、調べている内にudevというものに出会った。

udevとは、例えば起動時に外部端末が接続されると、カーネルはそれを検知しudevくんに伝える。udevくんはrulesファイルに従ってアクションを起こす。

ざっくりいうとUSBやらLANケーブルを接続or切断した際に動的に行動してくれるスゴイやつである。

で、あとは解決方法に書かれている通りである。