FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

「ゼロから作るディープラーニング②」を読む (7. RNNによる文章生成)

ゼロから作るDeep Learning ? ―自然言語処理編

ゼロから作るDeep Learning ? ―自然言語処理編

「ゼロから作るディープラーニング②」を購入した。

あまり内容を詳細に記録するつもりはないので、読んで纏めていったものをGistに張り付けていく。

第7章はRNNにより、文章を生成する。いろんなテクニックを使って、文章の精度を高めていく。

  • 第7章 RNNによる文章生成

Jupyter Notebook Viewer

第7章まとめ

  • RNNを用いた言語モデルで新しい文章を生成する
  • モデルの出力(確率分布)から、新しい文字をサンプリングという手順を繰り返す。
  • RNNを2つ組み合わせることで、時系列データを時系列データに変換する (seq2seq)
  • seq2seqにより、Encoderによる入力分のエンコード、それをDecoderがデコードする。
  • 入力分を反転させることで、seq2seqの精度を向上させることができる。
  • Peeky(エンコード情報を、Decoderの複数のレイヤに与えること)で、seq2seqの精度を向上させることができる。
f:id:msyksphinz:20180819001653p:plain
図. RNNの自動生成において、Reverse + Peekyをそれぞれ評価した結果。

「ゼロから作るディープラーニング②」を読む (6. ゲート付きRNN)

ゼロから作るDeep Learning ? ―自然言語処理編

ゼロから作るDeep Learning ? ―自然言語処理編

「ゼロから作るディープラーニング②」を購入した。

あまり内容を詳細に記録するつもりはないので、読んで纏めていったものをGistに張り付けていく。

第6章はゲート付きRNNだ。シンプルなRNNでは学習の効率が良くないところを、ゲート付きのRNN(いわゆるLSTMなど)を使って効率化していく。

  • 6章 ゲート付きRNN

Jupyter Notebook Viewer

f:id:msyksphinz:20180812022100p:plain

6.6 まとめ

  • 単純なRNNの勾配喪失・勾配爆発の問題を解決するために、以下のテクニックを導入する。
  • LSTMに使用されるゲート
    • inputゲート
    • forgetゲート
    • outputゲート
    • それぞれに重みがついている。sigmoid関数で0.0から1.0までの値が使われる。
  • 言語モデルの実装
    • LSTMレイヤの多層化
    • Dropout
    • 重み共有
  • RNNの正則化は重要なテーマであり、Dropoutベースの様々な手法が提案されている。
f:id:msyksphinz:20180812021948p:plain
図. ゲート付きRNNの実行結果。

「ゼロから作るディープラーニング②」を読む (5. リカレントニューラルネットワーク(RNN))

ゼロから作るDeep Learning ? ―自然言語処理編

ゼロから作るDeep Learning ? ―自然言語処理編

「ゼロから作るディープラーニング②」を購入した。

あまり内容を詳細に記録するつもりはないので、読んで纏めていったものをGistに張り付けていく。

次は第5章のリカレントニューラルネットワークだ。そのままの通り、時系列の情報を持ったニューラルネットワークとなる。

Jupyter Notebook Viewer

5.6 まとめ

  • RNNはループする経路があり、これにより記憶を保持することができる。
  • RNNのループを展開することで、一般的な誤差逆伝搬法を適用できる。
  • 適当な長さでデータのまとまりを作ることで、長い系列のデータを区切る。
    • この場合、逆伝搬のつながりを切断する。
    • 順方向は切断しない。
  • 言語モデルは、単語の羅列を確立として解釈する。
  • RNNレイヤを利用した条件付き言語モデルは、単語の情報を記憶できる。
f:id:msyksphinz:20180805221750p:plain
図. リカレントニューラルネットワーク(RNN)の実行結果。

RISC-Vの割り込みとタイマの仕組み

RISC-Vの割り込みについて。

RISC-Vのサポートしている割り込み・例外について

こちらについては、一覧表が作られているので参照されたい。

f:id:msyksphinz:20180808230058p:plain
図. RISC-Vのサポートしている割り込み・例外。(The RISC-V Instruction Set Manual Volume II: Privileged Architecture Privileged Architecture Version 1.10 より抜粋)

上記の値が要因の値として、例外・割り込み発生時には例外要因レジスタ(mcause, scausenなど)に格納される。

タイマ割り込みの挿入について

例えば、SiFiveのSoCプラットフォームであれば、タイマ割り込み向けのレジスタはCPUの外に定義されている。 CLINTレジスタ群がそれに当たる。0x0200_0000に定義されている(これはSiFiveのプラットフォームではこのように定義されている、という話であってすべてがそうではない)。

f:id:msyksphinz:20180808231519p:plain

RISC-Vの仕様としては、mtimeシステムレジスタを参照すればよいのだが、SiFiveではmtimeレジスタを持っていないので、システムレジスタにアクセスした際に例外が発生し、上記のCLINT Memory Mappedレジスタにアクセスするようになっている。

上記の資料では、mtimecmpレジスタが5つ定義されているが、U54-MCではCPUコアが5つ搭載されているのでそれぞれのコア向けに定義されている。このmtimecmpと比較して、一致すればそのコアに割り込みが発生するわけだ。

では、割り込みが発生したときに、CPUが割り込み処理にジャンプする条件はどのようになっているのだろうか?ここでは、一番簡単にCPUモデルの記述を見てチェックしていこう。 Spikeシミュレータの実装を見るのが簡単だ。

  • riscv-isa-sim/riscv/clint.cc
void clint_t::increment(reg_t inc)
{
  mtime += inc;
  for (size_t i = 0; i < procs.size(); i++) {
    procs[i]->state.mip &= ~MIP_MTIP;
    if (mtime >= mtimecmp[i])
      procs[i]->state.mip |= MIP_MTIP;
  }
}

明らかに実装を見るとわかる。割り込みを発生させるとCPU内のMIPレジスタに1が設定され、これが割り込み発生トリガとなる。では、これを受け付ける条件は何か。

結論から言うと、割り込み許可レジスタ(MIE)がAssertされているときである。

  • riscv-isa-sim/riscv/processor.h
  void take_pending_interrupt() { take_interrupt(state.mip & state.mie); }

割り込み受付処理

では、割り込み受付条件を見てみよう。midelegレジスタは割り込み受付条件を譲渡するためのシステムレジスタで、midelegの該当ビットが1になっていれば、その割り込み要因はそのままSupervisorモードに渡される。

  • riscv-isa-sim/riscv/processor.cc
void processor_t::take_interrupt(reg_t pending_interrupts)
{
  reg_t mie = get_field(state.mstatus, MSTATUS_MIE);
  reg_t m_enabled = state.prv < PRV_M || (state.prv == PRV_M && mie);
  reg_t enabled_interrupts = pending_interrupts & ~state.mideleg & -m_enabled;

  reg_t sie = get_field(state.mstatus, MSTATUS_SIE);
  reg_t s_enabled = state.prv < PRV_S || (state.prv == PRV_S && sie);
  // M-ints have highest priority; consider S-ints only if no M-ints pending
  if (enabled_interrupts == 0)
    enabled_interrupts = pending_interrupts & state.mideleg & -s_enabled;

...
}

割り込みの受付優先度

The RISC-V Instruction Set Manual Volume II: Privileged Architecture Privileged Architecture Version 1.10 によると、

The priority value 0 is reserved to mean \never interrupt", and interrupt priority increases with increasing integer values.

つまり、割り込み要因の数字が大きいほど割り込み優先度が高い。このルールに従って割り込みを受け付ける。

  • riscv-isa-sim/riscv/processor.cc
    // nonstandard interrupts have highest priority
    if (enabled_interrupts >> IRQ_M_EXT)
      enabled_interrupts = enabled_interrupts >> IRQ_M_EXT << IRQ_M_EXT;
    // external interrupts have next-highest priority
    else if (enabled_interrupts & (MIP_MEIP | MIP_SEIP))
      enabled_interrupts = enabled_interrupts & (MIP_MEIP | MIP_SEIP);
    // software interrupts have next-highest priority
    else if (enabled_interrupts & (MIP_MSIP | MIP_SSIP))
      enabled_interrupts = enabled_interrupts & (MIP_MSIP | MIP_SSIP);
    // timer interrupts have next-highest priority
    else if (enabled_interrupts & (MIP_MTIP | MIP_STIP))
      enabled_interrupts = enabled_interrupts & (MIP_MTIP | MIP_STIP);
    else
      abort();
  • riscv-isa-sim/riscv/sim.cc
// fetch/decode/execute loop
void processor_t::step(size_t n)
{
...
    try
    {
      take_pending_interrupt();

      if (unlikely(slow_path()))
      {
        while (instret < n)
        {
          if (unlikely(!state.serialized && state.single_step == state.STEP_STEPPED)) {
            state.single_step = state.STEP_NONE;

Freedom-U-SDKで生成したLinuxバイナリを自作RISC-Vシミュレータで実行

HiFive Unleashedは高くて買えないのだけれども、RISC-V SDKであるFreedom-U-SDKを使ってみることにした。

これはデフォルトでRISC-V上で動作するLinuxの環境が乗っているようで、SDカードなど用意してLinuxをビルドすると、ちゃんとHiFiveで動作するらしい。

こちらの記事など参考になる。

qiita.com

前回ビルドしたLinuxを指定して自作RISC-Vシミュレータにかけると、途中でクラッシュしてしまった。

Spikeと一致比較をしているのだが、どうもうまく一致しない。 まださらに検証する必要がありそうだ...というか、mstatusのDirtyビットとかをきちんと実装する必要がありそうだ。

f:id:msyksphinz:20180807022048p:plain

あとは、RISC-Vの仮想メモリアドレス変換についてもまた珍しい機能を見つけた。 これを実装しないと、Linuxはブートしないらしい。くそう。

The MPRV (Modify PRiVilege) bit modifies the privilege level at which loads and stores execute
in all privilege modes. When MPRV=0, translation and protection behave as normal. When
MPRV=1, load and store memory addresses are translated and protected as though the current
privilege mode were set to MPP. Instruction address-translation and protection are unaffected.
MPRV is hardwired to 0 if U-mode is not supported.

簡単に言うと、MPRVビットが1になっているときは、フェッチ以外のメモリアクセスはMPPビットの動作モードに基づいて実行がなされる。

HiFive Unleashed向けのオープンソースRISC-V SDK Freedom-U-SDKの環境構築

HiFive Unleashedは高くて買えないのだけれども、RISC-V SDKであるFreedom-U-SDKを使ってみることにした。

というのは、どうにかしてまともにシミュレータで動かせるLinuxを用意して、自作RISC-VシミュレータでLinuxを立ち上げたい。

せっかくvmlinuxの最初のブートができるようになったので、もう少し先に進めたいのだがこのためにはFreedom-U-SDKを使うしかないということが分かってきた。 (Fedora-25のQEMU環境がもう少し解析出来たらいいのだけれど...)

github.com

f:id:msyksphinz:20180806001637p:plain

ダウンロードだけで非常に時間がかかる。

$ clone https://github.com/sifive/freedom-u-sdk.git
$ cd freedom-u-sdk
$ git submodule update --init --recursive

の後で、いろいろと試行錯誤して気が付いたのだが、Ubuntuの環境では以下のパッチを充てることが推奨されている。

github.com

Unescaped left brace in regex is illegal here in regex; marked by <-- HERE in m/\${ <-- HERE ([^ \t=:+{}]+)}/ at /home/mclark/src/sifive/freedom-u-sdk/work/buildroot_initramfs/host/usr/bin/automake line 3936.
autoreconf: /home/mclark/src/sifive/freedom-u-sdk/work/buildroot_initramfs/host/usr/bin/automake failed with exit status: 255
package/pkg-generic.mk:185: recipe for target '/home/mclark/src/sifive/freedom-u-sdk/work/buildroot_initramfs/build/libtirpc-1.0.1/.stamp_configured' failed
make[1]: *** [/home/mclark/src/sifive/freedom-u-sdk/work/buildroot_initramfs/build/libtirpc-1.0.1/.stamp_configured] Error 1
make[1]: Leaving directory '/home/mclark/src/sifive/freedom-u-sdk/buildroot'
Makefile:90: recipe for target '/home/mclark/src/sifive/freedom-u-sdk/work/buildroot_initramfs/images/rootfs.tar' failed
make: *** [/home/mclark/src/sifive/freedom-u-sdk/work/buildroot_initramfs/images/rootfs.tar] Error 2

同じエラーにぶち当たった。

そこで、上記のGitHub Issuesで議論されているように、buildrootリポジトリのリビジョンを変更し、再度ビルドにトライした。

$ cd ./buildroot
$ git checkout sifive

再度makeを実行すると、とりあえず最後までビルドが完了したようだ。

$ make -j4
...
make[1]: Leaving directory '/home/msyksphinz/work/freedom-u-sdk/work/riscv-pk'
riscv64-unknown-linux-gnu-objcopy -S -O binary --change-addresses -0x80000000 /home/msyksphinz/work/freedom-u-sdk/work/riscv-pk/bbl /home/msyksphinz/work/freedom-u-sdk/work/bbl.bin
xxd -c1 -p /home/msyksphinz/work/freedom-u-sdk/work/bbl.bin > /home/msyksphinz/work/freedom-u-sdk/work/bbl.hex

This image has been generated for an ISA of rv64imafdc and an ABI of lp64d
Find the image in work/bbl.bin, which should be written to a boot partition

To completely erase, reformat, and program a disk sdX, run:
  sudo make DISK=/dev/sdX format-boot-loader
  ... you will need gdisk and e2fsprogs installed

SpikeでLinuxをブートするためには

以下のコミュニティのやり取りを参考にすると、conf/linux_defconfigに細工をしなければならない。

# CONFIG_HVC_RISCV_SBI is not set
CONFIG_HVC_RISCV_SBI=y

これで再度Makeを行った。

QEMUでのLinux起動

次に、QEMUでシミュレーションを行った。正常に起動したようだ。

$ make qemu
...
/home/msyksphinz/work/freedom-u-sdk/work/riscv-qemu/prefix/bin/qemu-system-riscv64 -nographic -machine virt -kernel /home/msyksphinz/work/freedom-u-sdk/work/riscv-pk/bbl \
        -drive file=/home/msyksphinz/work/freedom-u-sdk/work/rootfs.bin,format=raw,id=hd0 -device virtio-blk-device,drive=hd0 \
        -netdev user,id=net0 -device virtio-net-device,netdev=net0
bbl loader

                SIFIVE, INC.

         5555555555555555555555555
        5555                   5555
       5555                     5555
      5555                       5555
     5555       5555555555555555555555
    5555       555555555555555555555555
   5555                             5555
  5555                               5555
 5555                                 5555
5555555555555555555555555555          55555
 55555           555555555           55555
   55555           55555           55555
     55555           5           55555
       55555                   55555
         55555               55555
           55555           55555
             55555       55555
               55555   55555
                 555555555
                   55555
                     5

           SiFive RISC-V Coreplex
[    0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80200000
[    0.000000] Linux version 4.15.0-00044-g2b0aa1de45f6 (msyksphinz@msyksphinz-VirtualBox) (gcc version 7.2.0 (GCC)) #2 SMP Mon Aug 6 00:21:37 JST 2018
[    0.000000] bootconsole [early0] enabled
[    0.000000] Initial ramdisk at: 0x        (ptrval) (9457664 bytes)
[    0.000000] Zone ranges:
...
Sending select for 10.0.2.15...
Lease of 10.0.2.15 obtained, lease time 86400
deleting routers
adding dns 10.0.2.3
Starting dropbear sshd: OK

Welcome to Buildroot
buildroot login:

Spike でのLinux起動

次に、SpikeでLinuxの起動を行った。正常に起動したようだ。

$ make sim
/home/msyksphinz/work/freedom-u-sdk/work/riscv-isa-sim/prefix/bin/spike --isa=rv64imafdc -p4 /home/msyksphinz/work/freedom-u-sdk/work/riscv-pk/bbl
bbl loader

                SIFIVE, INC.

         5555555555555555555555555
        5555                   5555
       5555                     5555
      5555                       5555
     5555       5555555555555555555555
    5555       555555555555555555555555
...
Starting logging: OK
Starting mdev...
sort: /sys/devices/platform/Fixed: No such file or directory
modprobe: can't change directory to '/lib/modules': No such file or directory
Initializing random number generator... done.
Starting network...
Waiting for interface eth0 to appear............... timeout!
run-parts: /etc/network/if-pre-up.d/wait_iface: exit status 1
Starting dropbear sshd: OK

Welcome to Buildroot
buildroot login:

「ゼロから作るディープラーニング②」を読む (4. 第4章 word2vecの高速化)

ゼロから作るDeep Learning ? ―自然言語処理編

ゼロから作るDeep Learning ? ―自然言語処理編

「ゼロから作るディープラーニング②」を購入した。

あまり内容を詳細に記録するつもりはないので、読んで纏めていったものをGistに張り付けていく。

  • Jupyter Notebook Viewer: 4章 word2vecの高速化

Jupyter Notebook Viewer

word2vecは、語彙数が増えてくと計算量が非常に大きくなるという問題があり、これを解決するために様々なテクニックを導入する。

  • Embeddeingレイヤによる入力レイヤの削減
  • 多値問題から二値分類に変換することで、計算量を減らす。
  • 二値分類になったことで、計算量が削減された分、外れた値(負例)をサンプリングして別の解を探す(Negative Sampling)
f:id:msyksphinz:20180804145417p:plain
グラフ: 高速化したword2vecの学習経過。

まとめは以下。

  • word2vecを使うと計算量が増加するので、近似計算を行う。
  • Embeddingレイヤを使って、単語のIDを抽出する。計算量を減らす役割がある。
    • Negative Samplingを使って、負例をいくつかサンプリングする。
    • 二値分類を使用して、計算量を減らす。
  • 単語の分散表現は、単語の意味が含まれており、単語ベクトル空間上で似た単語は近い位置に表示されるようになる。
  • word2vecの単語の分散表現は、類推問題をベクトルの加算と減算によって解ける性質を持つ。
  • word2vecは転移学習の点で特に重要である。様々な自然言語処理に適用可能である。