C++製のxtensorで多次元配列を扱ってみる

github.com

今回はこのxtensorというC++で多次元配列を扱うライブラリを使う。
このxtensorはnumpyにinspireされて作られているとのこと。
numpyに関してはPythonから使うのだがxtensorはC++で作られているとのことで、各言語でバインディングを用意してあげればその言語で多次元配列の演算とかが出来るようになる。
現状だとPythonとかRとかJuliaにはあるみたいです。

準備

まずはxtensor自体はC++で書かれているのでC++コンパイル出来る状態でないといけない。
C++14をサポートしているので、今回はGCC6.1からC++14が標準になったということで今が最新の7.1を使用することにした。

$ add-apt-repository ppa:ubuntu-toolchain-r/test
$ apt-get update
$ apt-get install g++-7

xtensor自体のインストールに関してはこちらのリポジトリのREADME.mdにもあるようにcondaがmakeでインストールをする。
今回はcondaを使用することにした。

$ wget https://repo.continuum.io/archive/Anaconda3-4.4.0-Linux-x86_64.sh
$ bash Anaconda3-4.4.0-Linux-x86_64.sh -b

これでcondaが入ったので conda install -c conda-forge xtensor でxtensorをインストールする。
今回はrootユーザーにてcondaをインストールしてディレクトリはデフォルトのホームディレクトリを指定したので、xtensorのインクルードファイルは/root/anaconda3/include/配下のxtensorに入っている。

最初から入っているg++を入れ直したりとかわりと面倒だったので今後のためにもxtensorが動かせる環境をdocker imageに作成をしておいた。

https://hub.docker.com/r/hatappi/xtensor/

この後の動かしてみたに関しては今回作成したdocker imageを元にして検証している。

いくつか動かしてみた

多次元配列同士の足し算

#include <iostream>
#include "xtensor/xarray.hpp"
#include "xtensor/xio.hpp"

int main() {
  xt::xarray<double> arr1
    {{1, 2, 3},
     {4, 5, 6},
     {7, 8, 9}};

  xt::xarray<double> arr2
    {{1, 2, 3},
     {4, 5, 6},
     {7, 8, 9}};;

  xt::xarray<double> res = arr1 + arr2;

  std::cout << res;
  std::cout << "\n";
  return 0;
}

実行する

# -I でxtensorが入っているインクルードディレクトリを指定する
# 指定しないとxtensorないよってコンパイル時にいわれる
$ g++-7 -I /root/anaconda3/include -o test test.cpp
$ ./test
{{  2.,   4.,   6.},
 {  8.,  10.,  12.},
 { 14.,  16.,  18.}}

おぉーちゃんと計算されてる

sumとかもある

#include <iostream>
#include "xtensor/xarray.hpp"
#include "xtensor/xio.hpp"

int main() {
  xt::xarray<double> arr
    {{1, 2, 3},
     {4, 5, 6},
     {7, 8, 9}};

  std::cout << "ALL SUM\n";
  xt::xarray<double> all_sum = xt::sum(arr);
  std::cout << all_sum;

  std::cout << "\nROW SUM\n";
  xt::xarray<double> row_sum = xt::sum(arr, {0});
  std::cout << row_sum;

  std::cout << "\nCOLUMN SUM\n";
  xt::xarray<double> column_sum = xt::sum(arr, {1});
  std::cout << column_sum;

  std::cout << "\n";
  return 0;
}

実行

$ g++-7 -I /root/anaconda3/include -o test test.cpp
$ ./test
ALL SUM
 45.
ROW SUM
{ 12.,  15.,  18.}
COLUMN SUM
{  6.,  15.,  24.}

この他にもnumpyでいう~~はあるがそれはthe-numpy-to-xtensor-cheat-sheetを見ると分かります。

最後に

今回はとりあえず動かしてみた

任意のユーザーのログインシェルを任意のシェルにコマンド一発で変更する

あまり使用することのないコマンドとかのhelpとかmanで見たりすると「あっこんなオプションあったんだ」って気づくことがある。
今回はログインシェルを変更する時に使用するchsh

例えばhatappiというユーザーのログインシェルをzshに変えるとする。 今までは

# hatappiユーザーにスイッチして
$ su hatappi
# 新しいシェルの入力を求められるので`/usr/local/bin/zsh`と入力する
$ chsh
root のシェルを変更します。
新しいシェル [/bin/bash]: /usr/local/bin/zsh

これで次からはzshで起動される
ただこれをMItamaeのようなプロビジョニングツールでやろうってなった時に入力求められる場所どうするんだろう??ってなった

解決方法は当然あるわけでhelpを見ると
※ Ubuntu16.04環境で確認しました

$ chsh -h
Usage: chsh [options] [LOGIN]

Options:
  -h, --help                    display this help message and exit
  -R, --root CHROOT_DIR         directory to chroot into
  -s, --shell SHELL   

-sコマンドライン上でログインシェルを指定できるのとオプションの後にユーザーを指定できると
つまり

$ chsh -s /usr/local/bin/zsh hatappi

になる。
簡単

生まれて初めてGPUにふれた

生まれてこの方GPUというものにふれてこず印象はなんか計算が早くなるんでしょみたいな解釈しかしてませんでした。

とはいえさわってみないと分からないので今回は実際にさわってみてその効果のほどを確認することにしました。
まずGPUインスタンスを搭載したマシンをどう調達するかについてですが、今年のAWSのEC2で5月に東京リージョンへGPUを搭載したP2インスタンスがきたのでそれを使用することにしました。
なんと最大でGPUが16搭載したインスタンスが用意できると言う….凄い世界だ…

Amazon EC2 P2 Instances are now available in Asia Pacific (Tokyo) and Asia Pacific (Sydney) Regions

今回やること

Chainerを使用してその中でサンプルとしてMulti-Layer Perceptron for MNIST Classificationを使ってGPUを使ってない場合と使った場合を比較する。

動作環境

  • インスタンスタイプ: p2.xlarge
  • AMI: NVIDIA CUDA Toolkit 7.5 on Amazon Linux
    • 今回はGPUを使用するためのドライバとかをいれる時間を省くためにそれらが入ったAMIを使用しました。

GPUの情報はnvidia-smiというコマンドで確認できます。

$ nvidia-smi
Sat Jul 29 02:13:54 2017
+------------------------------------------------------+
| NVIDIA-SMI 352.99     Driver Version: 352.99         |
|-------------------------------+----------------------+----------------------+
| 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           On   | 0000:00:1E.0     Off |                    0 |
| N/A   42C    P8    25W / 149W |     55MiB / 11519MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

検証準備

ドライバなどは入っているので、後はChainerを使用するのでPythonとChainerを入れるだけです。
(とその時は思ってた)

Pythonpyenvを使用していれます。

$ python --version
Python 3.6.2

ChainerのインストールとGPUを使用する時に必要なcupyもいれます。

# chainerは2.0.1, cupyは1.0.1が入りました
$ pip install chainer cupy

検証

まずはGPUを使用していないものから検証しました。

GPUを使用しない

$ python chainer-2.0.1/examples/mnist/train_mnist.py
GPU: -1
# unit: 1000
# Minibatch-size: 100
# epoch: 20

epoch       main/loss   validation/main/loss  main/accuracy  validation/main/accuracy  elapsed_time
1           0.192339    0.11301               0.941083       0.9648                    17.211
2           0.0733699   0.0710786             0.977117       0.9774                    36.1076
3           0.0475365   0.0789325             0.98475        0.9769                    55.7616
4           0.0348562   0.0745593             0.98885        0.9787                    75.8827
5           0.0277311   0.102257              0.9907         0.9724                    96.4579
6           0.0241048   0.0879807             0.992483       0.9779                    117.416
7           0.0219072   0.0710079             0.992833       0.9818                    138.455
8           0.017988    0.0681981             0.993783       0.982                     160.079
9           0.0172883   0.0842126             0.994467       0.9804                    181.897
10          0.0123837   0.0860014             0.99585        0.9809                    204.486
11          0.0167936   0.081263              0.9943         0.9822                    227.67
12          0.0123561   0.0797155             0.996233       0.9839                    251.241
13          0.0113728   0.100481              0.9964         0.9814                    275.906
14          0.0119349   0.120433              0.996317       0.9799                    301.254
15          0.00982063  0.13196               0.996917       0.9787                    327.206
16          0.0106473   0.119098              0.99675        0.9797                    353.597
17          0.0108263   0.103797              0.997133       0.9806                    380.39
18          0.00896206  0.0968817             0.99725        0.9837                    408.096
19          0.00930343  0.100058              0.9972         0.9808                    436.251
20          0.00915619  0.130336              0.997133       0.9789                    465.153

GPUを使用する

続いてがGPUを使用した時で--gpu [GPU ID]オプションを指定するだけです。
GPU IDは先程nvidia-smiコメンドで確認した際のGPUというカラムの値の数値を使用します。
今回はIDが0なので--gpu 0と指定して実行します。

ただこの時1つ問題がありました。

まずは No such file or directory: 'nvcc'と出てそもそも実行されない。
これは単純に/usr/local/cuda/bin/nvccにパスが通ってないだけなのでexport PAHT=$PATH:/usr/local/cuda/binとパスを通してあげたらいけました。
これで実行できるようになったのですが、次は cuDNN is not enabled.が出てWARNINGが出たのですが、この解決が少し厄介でした。

ソース自体はここから落としてくるのですが、まずユーザー登録をしないといけないです。
ユーザー登録をして先程のリンクを開くとcuDNNがバージョンとプラットフォームごとに提供されているので、今回はcuDNN v6.0 (April 27, 2017), for CUDA 7.5cuDNN v6.0 Library for Linuxを使用しました。
今回は一度ローカルにファイルを落としてscpでインスタンスにアップロードしました。
ここから少しはまったのですが、どうやらchainerとかcupyを再インストールする必要があり、再インストールした後に無事動きました。
動いた結果が下記になります。

$ python chainer-2.0.1/examples/mnist/train_mnist.py --gpu 0
GPU: 0
# unit: 1000
# Minibatch-size: 100
# epoch: 20

epoch       main/loss   validation/main/loss  main/accuracy  validation/main/accuracy  elapsed_time
1           0.191885    0.118277              0.941883       0.9621                    3.53774
2           0.0720202   0.081614              0.977799       0.9763                    6.58676
3           0.0503432   0.0710561             0.984349       0.9785                    9.64134
4           0.0356193   0.0623461             0.988699       0.9829                    12.7405
5           0.0277371   0.082205              0.990815       0.9782                    15.8074
6           0.0252137   0.0703964             0.992082       0.9842                    18.9066
7           0.018074    0.0871086             0.994048       0.9794                    21.9982
8           0.0193796   0.0948052             0.993699       0.979                     25.0666
9           0.0176497   0.0816459             0.994315       0.9799                    28.1616
10          0.0145537   0.105795              0.995649       0.975                     31.252
11          0.0136489   0.0991074             0.995982       0.9787                    34.3346
12          0.0139412   0.0984373             0.995632       0.9811                    37.4096
13          0.0116104   0.104966              0.996449       0.981                     40.4892
14          0.0111883   0.0863221             0.996499       0.9835                    43.5692
15          0.011418    0.0900974             0.996432       0.9827                    46.6455
16          0.00842275  0.112952              0.997332       0.9796                    49.7249
17          0.00932168  0.0908319             0.997299       0.9827                    52.8396
18          0.0130952   0.106359              0.996282       0.9814                    55.9584
19          0.00778945  0.120892              0.997666       0.9789                    59.0483
20          0.00633532  0.106582              0.998116       0.9836                    62.1564

結果

GPUなしの時が7分くらいでGPUありで1分くらい 早い!

最後に

今回はとりあえず動かしてみただけなのでソースとか動作的な動きまでは追ってないですが、
コードを見る感じだとわりと簡単にかけそう?な感じ。

p2インスタンスを使用する時はお金と時間のトレードオフになりますが、やっぱり早く終るのは嬉しいなぁー

Ubuntuでテレビをモニタとして使った時に起きることとその対策

自宅にUbuntuのDesktop環境を整えたのは良いが、モニタがなくて今はテレビを使っている。

ただテレビをモニタとして使うと困ることがある。

テレビを見ながら作業が出来ない

最近のテレビは画面分割が出来るけど、画面が小さくなる分作業がはかどらない。。。
そこでリモートデスクトップのようにMacから繋げるようにした

Ubuntu側の作業

元々desktop sharingが入っていたのでそれを使用する。 ターミナルからvino-preferencesと入力すると設定画面が開く。

今回は下記のように設定した。
家庭内のネットワークではあるけどパスワードは要求することにした。
後は毎回接続許可をUbuntu側でするのは面倒だったのでYou must confirm each access to this machineは外している。

f:id:hatappi1225:20170727214801p:plain

後はターミナルから下記のコマンドを入力する
Linux同士なら問題はないけどMacとかWindowsから繋ぐときはこれを入力しないとMacでの接続時に「互換性がない」と言われて入れない

$ gsettings set org.gnome.Vino require-encryption false

後はserverを起動する

$ /usr/lib/vino/vino-server &

これでUbuntuの設定は一通り終了

Mac側での作業

MacVNCクライアント何を使おうかなと思ったら標準で入ってた
(知らなかった。。。)

Finderを開いてcmd + kを押すと下記のような画面が開くので
vnc://[ubuntuのipアドレス]:[port]を入力して接続する

f:id:hatappi1225:20170727215723p:plain

これで無事接続が出来る

最後に

やっぱり直接さわるよりも遅いから辛い。。。