2017/03/22

TensorFlowでXLAを使えるようにする

このブログ記事は2017年3月22日に開催されたRecap of TensorFlow DEV SUMMIT 2017の私が担当したXLAに関する発表の補足資料です。
XLAの概要を知りたい方は発表資料を御覧ください。


TensorFlow1.0にXLAというコンパイルの仕組みが実験的に導入されましたので、それを試す方法を記します。
XLAの概要や狙いなどはGoogle Developers Japanの記事が参考になります。

XLAはJITとAOTの2つのコンパイラから使われます。
御存知の通り、JITはランタイム上で実行時にコンパイルされ、AOTはランタイムを含まない静的な実行バイナリ(Cライブラリ)を作るコンパイラです。

ここでは、JITとAOTを試しに使ってみる方法をご紹介します。
まずは「A: TensorFlowのビルド」を説明し、「B: JITコンパイルを試す」でJITコンパイルする方法について説明し、「C: tfcompileを試す」でAOTコンパイラのビルドと使い方について説明します。

A: TensorFlowのビルド

プロダクションではXLAがどうやらONになっていないので、自分でXLAがONのTensorFlowをビルドします。

(※基本的にオフィシャルドキュメントに従えばビルドできるはず…)

0. 環境

GCEのGPUインスタンス(Ubuntu16.10 + vCPU:4 + RAM:15GB + Tesla K80)

1. ビルドツール「bazel」のインストール

オフィシャルサイトの手順に従えば躓くこと無くインストールできます。
私はrecommendedになっている「Using Bazel custom APT repository 」にしたがってインストールしました。

2. TensorFlowの依存ライブラリをインストール

$ sudo apt-get install python-numpy python-dev python-pip python-wheel

3. NVIDIA系ライブラリのインストール

以下の3つをインストールします。インストール方法はコチラを参考にどうぞ。
CUDA Toolkitとそれに準拠するドライバのインストール
ディープラーニング用ライブラリcuDNNのインストール
libcupi-devのインストール

4. TFをビルド

まずソースコードをcloneしてきます。
$ git clone https://github.com/tensorflow/tensorflow

gccのバージョンを4にします。
以下のコマンドでgccのバージョンを確認します。
$ gcc -v
gcc version 6.2.0 20161005

nvcc(Nvidia CUDA Compiler)がgcc4までしか対応していないのでgcc-4.9をインストールします。
$ sudo add-apt-repository ppa:ubuntu-toolchain-r/test
$ sudo apt-get update
$ sudo apt-get install gcc-4.9

コンフィグを設定します。
$ ./configure
以下の設定項目だけ注意して設定して下さい。
・gccのパスは先にインストールしたgcc-4.9を指定
・CUDA supportをON
・XLA just-in-time compilerをON

コンフィグが整ったらビルドします。
$ bazel build --config=opt --config=cuda //tensorflow/tools/pip_package:build_pip_package

凄く時間がかかります。私の環境では2時間ぐらい掛かりました。
ちなみに、TF自体をCPU環境に特殊化し高速化したい場合、このStack Overflowのようにビルドコマンドにオプションを追加するとSSEやAVX、FMAが利用できます。
ビルドが完了したらwheelパッケージを作ります。

$ bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg

これで/tmp/tensorflow_pkg配下に「tensorflow-1.0.1-cp27-cp27mu-linux_x86_64.whl」というwheelファイルが作られます。

あとは、これをpip installするだけですが、実験要素を含んだ不安定なTensorFlowなので、virtualenvの中にインストールしておいたほうが良いと思います。

$ virtualenv --system-site-packages .venv
$ source .venv/bin/activate
$ pip install /tmp/tensorflow_pkg/tensorflow-1.0.1-cp27-cp27mu-linux_x86_64.whl

これでXLAが使えるTensorflowのビルドとインストールが完了しました。
では、実際にJITコンパイルしてみます。

B: JITコンパイルを試す

サンプルコードがあるので、そちらを使います。
JITの使用方法をオフィシャルサイトで確認しておくと、理解しやすいと思います。
$ cd tensorflow/tensorflow/examples/tutorials/mnist/
$ python mnist_softmax_xla.py --data_dir=./data

実行が完了するとtimeline.ctf.jsonというファイルが生成されますので、それをChrome Trace Event Profileで表示できます。
Chromeを開いてURLに「chrome://tracing」と入力するとファイルを読み込めるので、そこからtimeline.ctf.jsonを読み込みます。

残念ながら、現バージョン(1.0.1)のソースコードで私がビルドしたTensorFlowでは、XLAのJITコンパイルが動いていないようでした…(対象のソースコードを色々とイジって試してみたのですが、どれもJITコンパイルされませんでした)
うまくJITコンパイルされると、
これ↓が

こう↓なるみたいです。

_XlaLaunchというOperationがJITコンパイルされたOperationということらしいです。

※ちなみに、1.0.1で公開されているサンプルプログラムのmnist_softmax_xla.pyでは--xlaオプションが効かない(単純にこのプログラムのバグ)ため、XLAをON/OFFしたい場合は、ソースコードを修正する必要があります

B: tfcompileを試す

tfcompileはXLAの仕組みを使ってTensorFlowのコードを実行可能なライブラリにコンパイルしてくれるツールです。
何が凄いって、TensorFlowのランタイムを必要とせずTensorFlowのグラフを実行できるので、実行ファイルのサイズが小さくなったり、レイテンシを低下させたりと、推論フェーズ(実際にMachine Learningをプロダクションで利用するフェーズ)に対して強力なツールとなります。
推論エンジンとして最適化するとGPUがなくてもレイテンシを上げることができるので、サーバではCPUインスタンスで運用できたり(GPUインスタンス高いしドライバとかアップデートの問題とか運用に手間が必要…)、モバイルでの利用も見えていきます。
XLAの本命はこれだと個人的に考えています。

1. tfcompileをビルドする

TFをビルドするときと同じようにbazelを使います。
$ bazel build --config=opt --config=cuda //tensorflow/compiler/aot:tfcompile

これだけでtfcompileが完成します。
実際tfcompileはbazelのマクロとして利用します。

2. サンプルで動作を確認

動作概要をオフィシャルサイトで確認しておくと理解しやすいかと思います。
まずグラフデータが必要になりますので、グラフデータを作ります。
$ cd tensorflow/compiler/aot/tests
$ python make_test_graphs.py

これはいくつかのパターンのグラフデータを作成するプログラムです。
実行するとtest_graph_tfmatmul.pbというファイルが作成されます。
これがグラフデータです。
これにconfigファイルを足すと、実行可能なバイナリとヘッダーファイル(すなわちライブラリ)が生成されます。

以下のコマンドがそれを実行してくれます。
$ bazel build :test_graph_tfmatmul

対応するBUILDの設定はこのようになっています。
tf_library(
    name = "test_graph_tfmatmul",
    testonly = 1,
    config = "test_graph_tfmatmul.config.pbtxt",
    cpp_class = "foo::bar::MatMulComp",
    graph = "test_graph_tfmatmul.pb",
    tags = ["manual"],
)

configでConfigurationファイル”test_graph_tfmatmul.config.pbtxt”を指定しています。
cpp_classでC++のクラス名(名前空間付き)を指定します。
graphは先ほどのグラフファイルを指定します。
Configファイルの中身は以下のようになっています。
feed {
  id { node_name: "x_hold" }
  shape {
    dim { size: 2 }
    dim { size: 3 }
  }
}
feed {
  id { node_name: "y_hold" }
  shape {
    dim { size: 3 }
    dim { size: 2 }
  }
}
fetch {
  id { node_name: "x_y_prod" }
}

feedがグラフの入力ノードで受け取る多次元配列の形を規定します。ノードが複数あれば、その数分feedを規定します。
fetchは出力ノードです。こちらも同じく、出力ノードの数だけfetchを規定します。

これをビルドすると、ビルド結果が//bazel-genfiles/tensorflow/compiler/aot/testsの中にtest_graph_tfmatmu.{h|o}が生成されます。この場所はbaselのライブラリ/includeパスに含まれているので、生成されたファイルを移動させる必要はありません。

ライブラリが完成しました。
これを利用するプログラムを書きます。こちらもbazelのビルドツールを利用すると以下のようになり簡単です。

cc_binary(
    name = "my_binary",
    testonly = 1,
    srcs = [
        "my_code.cc",
    ],
    deps = [
        ":test_graph_tfmatmul",
        "//third_party/eigen3",
    ],
    linkopts = [
          "-lpthread",
    ]
)

nameは生成される実行ファイルの名前を指定します。
depsは依存するライブラリです。「:(コロン)」始まりはbazelのタスク名です。
srcsは実際のソースコードです。先ほど作成したヘッダーファイルをincludeして利用するプログラムです。

my_code.ccの中身は以下のとおりです。
#define EIGEN_USE_THREADS
#define EIGEN_USE_CUSTOM_THREAD_POOL

#include 
#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
#include "tensorflow/compiler/aot/tests/test_graph_tfmatmul.h" // generated

int main(int argc, char** argv) {
  Eigen::ThreadPool tp(2);  // Size the thread pool as appropriate.
  Eigen::ThreadPoolDevice device(&tp, tp.NumThreads());

  foo::bar::MatMulComp matmul;
  matmul.set_thread_pool(&device);

  // Set up args and run the computation.
  const float args[12] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
  std::copy(args + 0, args + 6, matmul.arg0_data());
  std::copy(args + 6, args + 12, matmul.arg1_data());
  matmul.Run();

  // Check result
  if (matmul.result0(0, 0) == 58) {
    std::cout << "Success" << std::endl;
  } else {
    std::cout << "Failed. Expected value 58 at 0,0. Got:"
              << matmul.result0(0, 0) << std::endl;
  }

  return 0;
}

ライブラリのヘッダーファイルをincludeして、そのクラス名のfoo::bar::MatMulCompを使ったプログラムです。
グラフの出力ノードの値が正しければ(58なら)、「Success」と標準出力するプログラムです。

これをビルドします。
$ bazel build :my_binary

ビルドされた実行ファイル(my_binary)は下記に配置されます。
//bazel-bin/tensorflow/compiler/aot/tests/my_binary

実行するとたしかに「Success」という文字を得ることが出来ます。

C: おわりに

TensorFlow1.0からexperimental機能として実装されたXLAについて、JIT/AOTの両方で使う方法と実際に使う手順を説明しました。
まだうまく動かなかったり、思ったような性能を得られなかったりするので、あくまで実験機能として使うと良いと思います。

JITは学習フェーズの"自動"高速化、AOTは推論フェーズの低レイテンシ・省メモリ化など、Machine Learningの開発から運用までがトータルにサポートされる期待の機能ですので、今後も動向をチェックしていきたいと思います。

2017/03/14

GCEのGPUインスタンスでTensorFlowを動かす方法

Google Compute Engine(GCE)にGPUインスタンス(インスタンスにGPUを追加できる)が利用できるようになったので、TensorFlow(TF)を動かした。 

1. GPUインスタンスを作る

オフィシャルドキュメントに沿って作るもよし、Cloud Consoleから作るもよし。

GPUが使えるゾーンには現時点(2017/03/14)では制限があるので気を付ける。
OSの選択は気を付ける。GPU(NVIDIAのCUDA)がサポートしているOSにする。
また、各OS毎にCUDAツールキットのインストール方法が異なるので、気を付ける。
私はUbuntu 16.10のOSを選択。

1.1 CUDA Toolkit 8.0をインストールする

インスタンスが生成されたらSSHとかでログインして下記のスクリプトを実行する。
これでCUDA Toolkit 8.0がインストールされる。
#!/bin/bash
echo "Checking for CUDA and installing."
# Check for CUDA and try to install.
if ! dpkg-query -W cuda; then
  # The 16.04 installer works with 16.10.
  curl -O http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/cuda-repo-ubuntu1604_8.0.61-1_amd64.deb
  dpkg -i ./cuda-repo-ubuntu1604_8.0.61-1_amd64.deb
  apt-get update
  apt-get install cuda -y
fi

他(バージョンも含めて)のOSの場合、インストールスクリプトが異なるので、オフィシャルドキュメントを参考にして下さい。

2. GPUの動作確認

nvidia-smiを使ってGPUが認識しているかを確認する。
$ nvidia-smi
Tue Mar 14 01:56:30 2017       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 375.26                 Driver Version: 375.26                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla K80           Off  | 0000:00:04.0     Off |                    0 |
| N/A   35C    P8    27W / 149W |     13MiB / 11439MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0      2145    G   /usr/lib/xorg/Xorg                              13MiB |
+-----------------------------------------------------------------------------+

コノような内容がコンソールに表示されたらGPU(0番デバイスにTesla K80)が認識されている。

3. cuDNNを追加

すでにドライバとCUDA Toolkitがインストールされているので次に、TensorFlowを動くようにする。
cuDNNはNVIDIAから取得できるが、アカウント登録が必要になるため、コチラから登録する。連絡がくるまで1日要することもあるので、お早めに。
アカウントがあれば先のURLのDownloadボタンから、必要なcudnnライブラリをダウンロードする。
今回の例だと、CUDA 8.0とLinuxの条件に当てはまる「cuDNN v5.1 Library for Linux」が適切。

.tgz形式のファイルがダウンロードできるので、それを展開して、適切な場所にコピーしてインストール完了。
$ tar zxvf cudnn-8.0-linux-x64-v5.1.tgz
$ sudo cp ./cuda/include/cudnn.h /usr/local/cuda/include
$ sudo cp ./cuda/lib64/libcudnn* /usr/local/cuda/lib64 
sudo chmod a+r /usr/local/cuda/lib64/libcudnn*

4. CUDA Compute Capabilityをインストール

$ sudo apt-get install libcupti-dev


5. TFをインストール

TFのインストールも簡単になったものです。下記コマンド1つでインストールできます。
Python2.7とPython3系ではインストール方法が異なるので気をつけて下さい。
TFはまだバージョン更新が激しいのでvirtualenvを作っておくと幸せになれると思います。
$ mkdir tf && cd tf
$ virtualenv --system-site-packages .venv
$ source .venv/bin/activate
(.venv)$ pip install --upgrade tensorflow-gpu

もし、CPUバージョンのTFをインストールしたいならtensorflow-gpuをtensorflowにして下さい。
Python3系を利用している場合はpipをpip3にして下さい。
これでインストールが完了しました。

5.1 動作確認

$ python
>>> import tensorflow as tf
>>> sess = tf.Session()

これでErrorが表示されなければ(Warningは表示されます)正常動作しています。

2017/02/08

GAE/pyのプロダクションのDatastoreをdev_appserverにコピーする方法

ある程度運用しているGAEのDatastoreをまんまローカルにコピーして、dev_appserverで動かす方法。

0. remote_apiの設定

何はともあれremote_apiを使えるようにする必要がある。
app.yamlに下記を追加
builtins:
- remote_api: on

とりあえずデプロイ。デプロイしたアプリのバージョンをdefaultにする必要はない。
ここではバージョンを10として説明します。

1. Datastoreのダウンロード

下記のコマンドでDatastoreのエンティティをダウンロードする

% appcfg.py download_data --application=YOUR_APP --url=http://10-dot-YOUR_APP.appspot.com/_ah/remote_api --filename=datastore.sqlite

YOUR_APPはGAEアプリの名前。
--urlで指定する時、デプロイしたバージョンのサーバ(remote_apiが使えるサーバ)にアクセスする。
必要なkindだけをダウンロードしたい場合は--kindなどで指定できる。
datastore.sqliteというファイル名でダウンロードされる。ファイル名は任意。

2. dev_appserverにアップロード

dev_appserverを起動する。
% dev_appserver.py app.yaml
下記コマンドでdev_appserverに先ほどダウンロードしたdatastoreのデータをアップロードする。
% appcfg.py upload_data --application=dev~YOUR_APP --url=http://localhost:8080/_ah/remote_api --filename=./datastore.sqlite
applicationで指定する名前に注意。
アプリ名の前に「dev~」が付きます。
urlではdev_appserverのURLを指定する。
実行すると、アップロードが開始され、完了するとサーバのDatastoreがローカルに構築されている。

3. 401エラー

dev_appserverにデータストアのデータをアップロードすると、
Refreshing due to a 401
というエラーが表示され、dev_appserver側のログでも
INFO     2017-02-08 04:49:05,871 module.py:787] default: "GET /_ah/remote_api?rtok=584784529929 HTTP/1.1" 401 57
という401エラーが出る場合がある。
remote_apiがadmin権限でしかアクセスできないが、appcfg.pyの認証系がうまく動いていない場合に発生するっぽい。
ちょっと強引な方法だが、remote_apiのadminチェックを切ってしまえばOK。
まず、下記ファイルを開く。
${GCLOUD_ROOT}/platform/google_appengine/google/appengine/ext/remote_api/handler.py
その中に、def CheckIsAdmin(self)メソッドがあるので、そのメソッドの返り値を無条件でreturn Trueとする。メソッドの一行目にreturn TrueとすればOK。
def CheckIsAdmin(self)
    return True
注意)アップロード処理が終わったら、この修正をもとに戻しておくことをおすすめします。

4. おまけ

dev_appserverのdatastoreの保存先ファイルをバックアップしておくことで、いつでもデータストアを復元できて便利です。
たとえば本エントリーでdatastoreをアップロードし終わったら、そのファイルを保存しておき、いつでもその状態でdev_appserverを起動できます。
まずアップロードする前に下記手順でdev_appserverを起動します。
% touch datastore.db
% dev_appserver.py --datastore_path=./datastore.db app.yaml
そして、appcfg.pyを使ってデータストアのデータをアップロードします。
アップロードが完了したらdev_appstoreを停止し、datastore.dbをバックアップします。
% mv datastore.db datastore.master.db
今後、dev_appserverを起動するときに、以下の手順で起動すると、かならず元のdatastoreが復元されるので色々便利だと思います。
% cp datastore.master.db datastore.db
% dev_appserver.py --datastore_path=./datastore.db app.yaml

2015/12/04

MacとRaspberry Pi2をTTLシリアル通信で繋いでお手軽開発環境

前の投稿と同じ類です。
ラズパイ開発にディスプレイとかUSBキーボードとか接続するのメンドウなのでシンプルにUSB挿すだけで接続できる環境を構築したい。

今回は前回よりもシンプル。
ケーブル(若干特殊なUSBケーブル)1本だけ。

USBシリアル接続すればいいよね。
ついでにソコから電源取れたら1本でラズパイとMacを繋げられるよねって話。
※これもググったらすぐ出てくる情報ですが自分用のメモとして。


準備するもの
Raspberry Pi2 Model B(たぶん普通のラズパイでもOK)
・micro SDカード(ラズパイのOS焼く用。私はコレを買った)
・Macbook(Mac OSXが動けば何でもOK)
・USB-COM変換ケーブル(ラズパイにシリアル接続するケーブル。私はコレを買った)


0. ラズパイOSをセットアップ(ラズパイ動かせてる人は不要)
前回の投稿と同じ


1. ドライバをインストール
ドライバをダウンロードする
ドライバをインストールする
Macを再起動する


2. ラズパイのGPIOにケーブルを接続
赤色の線を04番ピン(DC Power 5V)に挿す
黒色の線を06番ピン(Ground)に挿す
白色の線を08番ピン(GPIO14)に挿す
緑色の線を10番ピン(GPIO15)に挿す

ピン番号はググれば沢山出てきますが、このサイトとかが良いかも。
GPIOから電源を投入することによる危険性については色々と議論があるようなので、心配な場合は赤色の線を接続せず、既存のmicro-USBケーブルなどで電源を取るとよいと思います。線が2本になるのが残念だけど…


3. ラズパイにシリアルTTL接続
ターミナルを開いてコマンドで一発
    % screen /dev/tty.usbserial 115200

何も表示されないがキーボードの任意のキーを叩くとユーザ名とパスワードを聞かれるのでそれぞれ「pi」「raspberry」と入力
ログインできたはず

ログアウトは以下のコマンド
    % exit


ちなみに、minicomを使う場合は以下のコマンド
    % minicom -b 115200 -o -D /dev/tty.usbserial


できた


参考


2015/11/17

Mac上でディスプレイもUSBキーボードも使わないRaspberry Pi2開発環境を整える

ラズパイ使うのにHDMIでディスプレイ繋いだり、USBキーボード持って行ったりするのメンドウ。
MacからSSHとかで繋げたら楽でいいね。
一回もHDMIディスプレイ使わない方法でラズパイ2の環境構築をやってみた。
※そこかしこで書かれている内容なので今更感あるけど、自分用のメモとして。


準備するもの
Raspberry Pi2 Model B(たぶん普通のラズパイでもOK)
・micro SDカード(ラズパイのOS焼く用。私はコレを買った)
・Macbook(Mac OSXが動けば何でもOK)
Thunderboltイーサネットアダプタ(有線LANでラズパイとつなぐ)
・有線LANケーブル(Mac <-> (アダプタ) <-> (有線LAN) <-> ラズパイという形。巻取り式が便利)
・USBケーブル(ラズパイに電源を供給する。巻取り式がコンパクトで便利)


0. ラズパイOSをセットアップ(ラズパイ動かせてる人は不要)
・micro SDカードをMacbookにセット(SDカードアダプタなどで)
・コマンドラインでディスク名を調べる
    % diskutil list
    /dev/disk1とか/dev/disk2とかがそれ。/dev/disk2s1とかはパーティションなので違う。
・Macのディスクユーティリティを起動してマウントしているSDカードがFAT32フォーマットされているかを確認し、「マウント解除」。「取り出し」じゃないので注意
・ラズパイのOS「RASPBIAN」をダウンロードして展開して.imgファイルを取得。私はWHEEZYの方をダウンロード
・SDカードに以下のコマンドで焼き込む。私の環境で約330秒かかった
    % sudo dd bs=1m if=2015-05-05-raspbian-wheezy.img of=/dev/disk2
    2015-05-05-raspbian-wheezy.imgはダウンロードして展開したファイルのPATH。/dev/disk2はdiskutil listで調べたディスク名
・焼き終わったらディスクユーティリティでSDカードを「取り出し」


1. ネットワーク環境を整える
・Macbook ThunderboltイーサネットアダプタをMacbookに挿しLANケーブルを挿す
・システム環境設定の「共有」で「イーサネット共有」を開き(チェックボックスはOFFのまま)、「共有する接続経路」を「Wi-Fi」にする(Thunderbolt経由じゃなければ何でもOK)。相手のコンピュータが使用するポートを「Thunderbolt Ethernet」のチェックボックスをON。
・「イーサネット共有」のチェックボックスをON


2. ラズパイ起動
・RASPBIANを焼いたmicro SDカードをラズパイに挿す
・MacbookのThunderboltイーサネットアダプタを経由して有線LANをラズパイにつなぐ
・USBケーブルでラズパイに通電


3. ラズパイにSSH接続
・コマンドでThunderboltイーサネット共有のIP番号を調べる。
    % ifconfig
    bridge100という項目。192.168.2.1とか192.168.3.1とかになっているんじゃないかな?
・コマンドでラズパイに割り当てられているIPを調べる
    % arp -a
    「on bridge100」という項目で割り当てられているIPを調べる。
    たぶん、192.168.2.1が大元で192.168.2.2がラズパイ。
・SSHでアクセスする
    % ssh pi@192.168.2.2
    うまくアクセスできたらパスワードを聞かれるので「raspberry」
    sshできなかったらIPが間違っているので別のIPを試す


できた




参考

2015/09/02

Udacityを使ってAndroid勉強会を開催する方法


はじめに


GoogleがUdacity上でオフィシャルに提供している学習コンテンツを使って勉強会を開くStudy Jamsを活用し、GDG石巻でAndroid開発勉強会で講師をしてきました。


Udacityを利用する勉強会では今までの講師の役割が全く違ったものになりますので、私が体験した講師の存在意義についてをご紹介し、今後、同様の勉強会を開催する方の参考になればと思います。


Udacityとは?

簡単に説明するなら「オンライン授業」です。あらゆる授業が録画ビデオで受講できるようになっています。
Google関連技術についてはGoogleがオフィシャルで授業コンテンツを作成しているため、質の高いコンテンツが揃っています。
ただし、ほとんどが英語。YouTubeを活用しているので、字幕を付けることが出来る授業もあり、なかには日本語の字幕の付いている授業もります。
Google JapanのStudy Jamsがサポートしている授業だと比較的入りやすいと思います。私はこの中から「Developing Android Apps」を選び、勉強会を開催しました。


勉強会講師の役割

勉強会開催にあたって、下記の疑問が自然と生まれました。


・Udacityの動画内に講師(技術を教えてくれる人)がいるので、講師は不要では?
・個人で受講できるのに、勉強会を開催して集まる必要ある?


勉強会を開催する前に、自分でUdacityの授業を幾つかうけてみました。
ただ受けるのではなく、勉強会で講師をするつもりで受講し、上記疑問に対する答えを探しました。


答えは「両方とも意義はある」と思い、実際に開催し、その仮説の検証を行いました。


勉強会のスタイル

いろいろなスタイルが考えられると思いますが、以下のスタイルがやりやすいのではないかと思います。

スタイル1:個人のPCで各個人がUdacityの授業を受講する
スタイル2:授業を複数準備し、興味ある授業ごとにグループを作り、それぞれのグループであつまり各授業を受講する
スタイル3:プロジェクターなどで1つの画面にUdacityの授業を映しだし、全員で受講する
スタイル4:スタイル3に加えて、授業内で説明される内容を実際に手を動かし確認する


勉強会の規模、設備、サポートしてくれる人・人数によって実施スタイルが異なると思います。今回は規模が10人程度に対して、サポートしてくれるスタッフ(GDG石巻のマネージャなど)がそろっていたのでスタイル4で勉強会を実施し、スタイル1で継続して勉強を続けられるよう意識した内容にしました。


設備としてプロジェクターがあったので、Udacityの授業をプロジェクターで映しだし、全員で1つの画面に向かい受講しました。


勉強会講師の存在意義

勉強会講師(今回は私)は講師ではなくサポータという役割が適切じゃないかと思いました。
動画内の講師が説明する内容について

・受講者の顔を見ながら、理解度にあわせて分かりやすく解説し直す
・開発経験から得られた「ここ重要!」な内容を強調する
・ハンズオン形式の場合のファシリテーション
・実装確認(バグっていたら一緒に原因を特定する、特定してあげるのではなく、特定の方法を教える。プリントデバッグ、スタックトレースの読み方、デバッガの使い方など)
・質問がある場合、それに答える
・補足情報(経験した事例や、専門用語の意味など)を加える


すべて技術的な知識や開発経験があるからこその付加情報なので、勉強会講師の存在意義はある程度あるのではないかと思いました。


勉強会の開催意義

オンライン授業の大きな利点の一つ「いつでも、どこでも受講できる」をなくしてまで勉強会を開催する意義についてですが、

・他の人の意見を聞ける
・他の人に教えてもらったり、教えたりして理解を深めることができる
・他の人の疑問点やつまづくポイントを知ることができる(自分がその疑問に気付かなかっただけ。授業を作業化して自分で考える事をやめ、自分の理解に対して無自覚になることは多い。)
・モチベーションが続く(他の人が頑張っていると自分も!ってなる)
・時間のコントロールを勉強会主催者に委ねることで確実に勉強が進む(長時間勉強で疲れないように適宜休憩が入ったり、サボれない)


という理由により、意義はあると思いました。
特に他の受講者の存在により、自分の理解の深化が最大のメリットじゃないかと思いました。


勉強会講師の準備

普段の勉強会だと、資料作成が必要ですが、Udacityを利用するとそれが必要なくなります。その代わり、Udacityの授業をサポートする資料が必要になります。
私が準備した資料は3つです。


・自己紹介
・Udacityの使い方
・授業の進行表


自己紹介は言わずもがなですね。サポートしてくれる人が何者なのか分からないままだと安心して質問できません。


Udacityの使い方ですが、受講生の全員がUdacityを始めて使う状況だったので、どのように活用すると、今後の勉強に役立つかを知ってもらうために使い方の説明を加えました。勉強会で勉強したことを自宅で復習したり、継続して勉強するのにも役立ててもらう意図もありました。


最後の進行表ですが、これが一番大事だと思います。私は勉強会で使う授業を事前に2,3回受講し、

・どのように授業が進むのか
・どこが大事なのか
・どこが難しく、再生を止めて追加の解説を加えるのか


を事前にメモしておきました。
Udacityは1レッスンが複数のセクションに分かれており、セクションごとにタイトルが付いています。タイトル毎に上記をメモしておきました。これのおかげで、授業を邪魔しないようなサポートがある程度できたのではないかと思います。
※事前にリハーサルなどをしていくとよりクォリティが上がると思います


勉強会のすすめ方

今回の授業は所要時間が4〜5時間(実際受講するとそこまで長くない)と表記されていて長めなので、適宜休憩を入れたり、授業内のミニクイズをみんなで考えたり、補足情報を加えて飽きない(寝ない)ようにするなどの工夫をしました。

また、実際に実装を通してAndroidのアプリ開発の基礎と基本概念を理解する授業でしたので、受講者にAndroid Studioを開いてもらい、授業の進行と平行して実際にコードを書いてもらいました。当然、何らかのミスによりうまくいかないことも有るため、GDG石巻のメンバーや私がサポートし不具合を解消しながら進めました。
実際、個人で勉強を続けるとしても、手を動かす内容であれば手を動かしたほうが確実に身につくと思い、サポートコストが高くなりますし授業の進行としてはリズムが悪くなるのですが、このような方法をとりました。

もっと長い授業もあるので、勉強会の時間を鑑みて、飛ばすセクションを決めておく、2回に分割するなどの工夫も効果的かもしれません。


先の進行表に休憩のタイミングやミニクイズに充てる時間、また難しいクイズの場合はヒントなども記載しておきました。


まとめ

Udacityを使ったAndroid開発勉強会を開催する1手法について説明しました。
GDG石巻で実際に行った経験から、Udacityを使った勉強会の意義や講師としての役割などについての考察をまとめました。

Udacityは個人で受けることのできるオンライン授業ではありますが、勉強会講師などに質問したり、他者から刺激(付加情報)を得るなど、勉強会の価値はある程度あると思われます。

勉強会講師は一般的な勉強会とは異なり、事前に資料の準備は必要ありませんが、授業コンテンツの理解と、進行管理の事前準備を行い、受講者のサポートに回る役割が求められるのではないかと思いました。

最後に、
GDG石巻で使った資料を公開しておきます。