Raspberry Pi で TensorFlow する Docker 環境構築

# ラズパイだからいいや
と思って痛い目を見たわけなんです、はい。

ラズパイは趣味で触っている程度なので、パッケージやライブラリなんかは何も考えずに入れまくっていました。
しかしある日、プリインストールされていた Python2 を誤って削除したとき、それは起きます。

```bash
$ sudo apt-get upgrade
```

もはや息をするように叩いて彼が常に Python2 の依存関係エラーで異常終了するようになってしまいました。
当然 Python に依存しているパッケージ群はインストールすることができません。

下記の実行や手動インストールもやってみましたが、すべて思うようにはなりませんでした。

```bash
$ sudo dpkg -P --force-remove-reinstreq パッケージ名
$ sudo apt-get -f install
```

「ラズパイだからいいや」なんて思っていたはずなのに、自分が作ったものが動かない環境になってしまったやり場のない悲しみ。
そして最終的には、自分の力が及ばない悔しさを噛み締めながら、OS の再インストールから始める羽目になったのでした。

# 反省を踏まえて

1. ラズパイは PC。開発するなら "ローカルはキレイな状態" を保ちたい。
2. ローカル PC と同じように Docker を導入したい。
3. ついでにラズパイっぽい環境（TensorFlowの動作環境）を構築したい。
4. 誰かが自分のような失敗をしないよう役に立ちたい。

という気持ちをもって本稿を書きます。
(ssh や location などの初期設定は割愛します)

# RaspberryPi に Raspbian をえいや

ラズパイには豊富な OS イメージが準備されていますが、今回は最もポピュラーな Raspbian を導入します。
https://www.raspberrypi.org/downloads/

ラズパイといえば、"ストレージは SD カード" ですね。
ラズパイの公式ドキュメントは非常に分かりやすく、 SD カードの推奨サイズなども記載されています。
https://www.raspberrypi.org/documentation/installation/sd-cards.md
さらにインストール手順には、作業者の OS ごとに推奨される SD フォーマット、解凍ツールまであるので、一度読んでおくと良いかと思います。

▼公式より一部抜粋
![SD_cards_-_Raspberry_Pi_Documentation.png](https://qiita-image-store.s3.amazonaws.com/0/123634/52e164cc-f696-9f09-41e7-a1fb48117f8e.png)

さて実際の作業に入っていきますが、Raspbian を導入する方法は大きく分けて2通りあります。

1. NOOBS を使用して、ラズパイ起動時に OS を選択してインストール。
2. SDカードに直接 Raspbian の OS イメージを書き込み。

## 1. NOOBSを使用してインストール

> 公式ドキュメント
> https://www.raspberrypi.org/documentation/installation/noobs.md
> 
> New Out Of Box Software (NOOBS) is an easy operating system installation manager for the Raspberry Pi.

NOOBS を使えば、簡単にラズパイに OS インストールできまっせ。とのことです。
実際に、ドキュメント通りに進めるだけで非常に簡単です。

1. https://www.raspberrypi.org/downloads/noobs/ から NOOBS をダウンロード。
2. SD カードをフォーマット。
3. 1. で落としたファイルを、SD カードに解凍。
4. ラズパイに SD カードを挿入し、起動。
5. NOOBS の OS選択画面にて "Raspbian" を選択　してインストール。

NOOBS には2種類ありますが、オンライン環境があるならどちらでもOKです。
Raspbian と LibreELEC 以外をインストールしたい場合は LITE 版がよいかと思います。

![Download_NOOBS_for_Raspberry_Pi.png](https://qiita-image-store.s3.amazonaws.com/0/123634/23906b3a-8d7c-2721-afc7-672ef540d4e2.png)

| NOOBS                           |NOOBS LITE       |
|:--------------------------------|:----------------|
|インストーラーとOS（Raspbian、LibreELEC）|インストーラーのみ    |
|上記のOSならオフラインでインストール可    |オンラインでインストール|

## 2. 直接 OS イメージを書き込み

NOOBS を使わずに直接 OS イメージを SD カードに書き込みます。

> 公式ドキュメント
> https://www.raspberrypi.org/documentation/installation/installing-images/README.md
>
> We recommend most users download NOOBS, which is designed to be very easy to use. However, more advanced users looking to install a particular image should use this guide.

簡単な NOOBS がオススメではあるそうです。

1. https://www.raspberrypi.org/downloads/ から OS イメージをダウンロード（今回は Raspbian)。
2. SD カードをフォーマット。
3. 1. で落としたファイルを、SD カードに書き込み。
4. ラズパイに SD カードを挿入し、起動。

OS イメージを直接書き込んでいるので、ラズパイを起動すればすぐに使用することができます。
ちなみにドキュメント記載の通り、イメージ書き込みには [Etcher](https://www.balena.io/etcher/) が便利です。

![Download_Raspbian_for_Raspberry_Pi.png](https://qiita-image-store.s3.amazonaws.com/0/123634/551f5aca-9fdc-c812-76b9-e88eee9edf3e.png)

# RaspberryPi に Docker をえいや

それでは実際に Docker をインストールします. 

```bash
# インストール.
$ curl -sSL https://get.docker.com/ | sh

# バージョン確認
$ sudo docker -v

# root ユーザ以外で docker を使用できるように
# docker グループに pi ユーザを追加. 
$ sudo usermod -aG docker pi

# 自動起動設定.
$ sudo systemctl enable docker
```

ここで一度、再起動しましょう。

```bash
$ sudo reboot
```

再起動が完了したら、 docker プロセスを立ち上げます。
前述の設定で `sudo` なしで `docker` コマンドを使用できるようになっているはずです。

```bash
# 今回は stretch(Raspbian9).
# --privileged オプションで ラズパイ(Raspbian)上のデバイスに接続可能.
# --name オプションは好みで.
$ docker run --name rpi-raspbian-stretch -ti --privileged resin/rpi-raspbian:stretch /bin/bash
```
動作確認は [こちら](https://qiita.com/ykshr/items/c78eb72e3ee75664a5fe#docker%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%8B%E3%82%89l%E3%83%81%E3%82%AB) の方が分かりやすく記載してくださっています。

# Docker 環境に TensorFlow をえいや

さて、ここからは TensorFlow 環境を構築していきます。
前述で動作確認した環境に地道に構築していってもよいですが、せっかくなので Dockerfile を使用して Docker Image をビルドします。

## Dockerfile でえいや

```dockerfile
FROM resin/rpi-raspbian:stretch

ENV LANG C.UTF-8

ENV DEBIAN_FRONTEND noninteractive

ENV APP_ROOT /usr/src
WORKDIR $APP_ROOT
COPY src $APP_ROOT

RUN echo "deb http://mirrordirector.raspbian.org/raspbian/ stretch main contrib non-free rpi firmware" > /etc/apt/sources.list

RUN apt-get update -y
RUN apt-get install -y --no-install-recommends \ 
        vim git less wget \
        build-essential \
        libatlas-base-dev \
        python3-pip python3-dev python3-setuptools\
        python3-scipy python3-h5py \
        libraspberrypi-bin \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

RUN pip3 install --upgrade pip setuptools
RUN pip3 install rpi.gpio
RUN pip3 install numpy opencv-python
RUN pip3 install tensorflow
RUN pip3 install keras
RUN pip3 install jupyter

# Jupyter のデフォルトポート
EXPOSE 8888
```
参考
[DEBIAN_FRONTEND=noninteractive ってなんだ
](https://qiita.com/udzura/items/576c2c782adb241070bc)

TensorFlow や Jupyter については、すでに公式や多くの方から Image が公開されていますが、今回は勉強をかねて Raspbian に地道にインストールしています。 
(Dockerは独学なので、ご指摘等あればコメントください)

```bash
# build
$ docker build -t rpi-raspbian-stretch . 
```
ビルドが完了したら、container　を作成、起動します。
[Jupyter](http://jupyter.org) を使用するのでデフォルトポート `8888` をバインドしますが、他のポートを使用する際は適宜読み替えてください。

```bash
$ docker run --name rpi-raspbian-stretch -ti -p 8888:8888 --privileged -d kohbis/rpi-raspbian-stretch
```

無事に起動していることが確認できたら、 container に接続して作業していきます。

```bash
$ docker exec -ti rpi-raspbian-stretch /bin/bash
```

まずは Jupyter を起動します。
ここまでで、個別のユーザ設定をしていない場合は、 root ユーザで起動することになります。
root ユーザでの起動は非推奨であるため `Running as root is not recommended. Use --allow-root to bypass.` というメッセージが表示されますが、今回はメッセージにある `--allow-root` オプションを指定して進めます。

```bash
jupyter notebook --allow-root --ip 0.0.0.0 --port 8888
```

Jupyter にブラウザからアクセスします。
初回のみ起動時に表示されている token 付き URL にアクセスする必要があります。

```
    Copy/paste this URL into your browser when you connect for the first time,
    to login with a token:
        http://(hostname or 127.0.0.1):8888/?token=********************
```

* ラズパイ上で作業している場合は `127.0.0.1:8888`
* ラズパイに ssh して作業している場合は `ラズパイのIPアドレス:8888`

![jupyter.png](https://qiita-image-store.s3.amazonaws.com/0/123634/3304b179-7189-1b24-30bf-9f8785849986.png)

Jupyter の画面が表示されたら
最後に、 `Hello World`　しましょう。

右上の `new` -> `Python3` から Notebook を作成して、プログラムを実行します。

```py
import tensorflow as tf

hello = tf.constant('Hello, TensorFlow !!')
sess = tf.Session()

print(sess.run(hello))
```

![helloworld.png](https://qiita-image-store.s3.amazonaws.com/0/123634/4dcec682-186d-af7c-1289-e754929e41a7.png)

画像のようになれば成功です。

今回は環境構築なので一旦ここまですが、次回以降は実際にカメラモジュールを使用して画像解析をやりたいです。

# 所感

Docker を導入したことで、ラズパイのローカルをキレイな状態に保つことに成功しました。
作業している最中には考えていませんでしたが、 Docker リポジトリにプッシュしてラズパイがないときでも開発を進めたり、といったこともできます。
個人的には Docker Image をビルドするので、複数の Image をラズパイのローカルに持ちたい場合は SD 容量は 8GB だと心もとないかな？ という感覚です。

今後は 機械学習そのものに手を出すことが初めてなので、勉強と手探りで進めていこうと思います。
AI × IoT 頑張ります！