まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

Google が提供する機械学習ライブラリ TensorFlow公式にはラズベリーパイ用のモジュールは用意されていませんが、ラズベリーパイ向けのパッケージを作って公開されているものを見つけました:
https://github.com/samjabrahams/tensorflow-on-raspberry-pi


実際に導入してみた手順を紹介します。なお以下の情報は 2017/Jul/19 時点のものであり、実際に試した時の環境は以下のとおりです:
ハードウェア: Raspberry Pi 3 Type B
OS: Raspbian GNU/Linux 8.0(Jessie)
Linux カーネル: 4.9.28
TensorFlow: 1.1.0


【Pythonのインストール】
Python 2.7 または 3.3 以上が必要です。自分の環境では標準で 2.7 がインストールされていたので、これをそのまま使うことにしました。というわけで、以下の手順は Python 2.7 用のものです。


【pip 他のインストール】
TensorFlow 本体のインストール時に必要な pip や開発用依存モジュールをまとめてインストールします:
$ sudo apt-get update
$ sudo apt-get install python-pip python-dev

【TensorFlow のインストール】
ラズパイ用の TensorFlow 1.1.0 をダウンロードし、pip でインストールします:
$ wget https://github.com/samjabrahams/tensorflow-on-raspberry-pi/releases/download/v1.1.0/tensorflow-1.1.0-cp27-none-linux_armv7l.whl
$ sudo pip install tensorflow-1.1.0-cp27-none-linux_armv7l.whl

wheel パッケージが展開され、各種ライブラリ含めたビルドが行われます。実際には結構な時間がかかる作業です。


【mock ライブラリの再インストール】
最後に mock ライブラリの再インストール(一度削除して、もう一度インストール)が必要とのことで、その作業を行っておきます:
$ sudo pip uninstall mock
$ sudo pip install mock

【動作確認】
クラスメソッド様のサイトに「TensorFlow 版ハローワールド」的なサンプルプログラムがあったので、これをそのまま動かしてみます:
TensorFlowで Hello Worldを動かしてみた&その解説

テキストエディタで以下の内容を編集し、hello-tf.py という名前で保存します:
# hello-tf.py
import tensorflow as tf
import multiprocessing as mp
 
core_num = mp.cpu_count()
config = tf.ConfigProto(
    inter_op_parallelism_threads=core_num,
    intra_op_parallelism_threads=core_num )
sess = tf.Session(config=config)
 
hello = tf.constant('hello, tensorflow!')
print sess.run(hello)
 
a = tf.constant(10)
b = tf.constant(32)
print sess.run(a+b)

これを Python で実行します:
$ python hello-tf.py
hello, tensorflow!
42

青字のような結果が出力されれば、とりあえず動作していることが確認できました。


 

自分はテキストエディタにはこだわりがあります。

Vz エディタから始まり、vi(vim)、Emacs、・・と使ってきました。現在はこんな感じで使い分けています:
  • Java コードの記述には Eclipse
  • JavaScript の記述には ATOM
  • サーバーにターミナルログインして使う場合は vi(vim)
  • PC でマークダウンを記述する場合は Boostnote
  • それ以外はメモ帳かサクラエディタ

大きくは PC 環境なのか、サーバー環境なのかの違いです。基本的にサーバーにログインして使う場合は vi(vim) ばかり使ってます。一方 PC 環境の場合、Java だけは例外的に Eclipse でないと使いづらいのですが、それ以外はあまりこだわりはありません(最近、周囲の影響で ATOM を使い始めました)。実はメモ帳を使うことも結構多いのですが、議事録などはマークダウンで書くことが多く、その時は Boostnote を使ってます。


さて、クライアント環境では「好きなエディタをインストールして使う」ことも可能ですが、サーバー環境ではそうもいかないケースがあります。特に X Window システムが導入されていない CUI 環境の場合、そもそもテキストエディタの選択肢がほとんどなく、標準搭載されている vi を使うか、環境によってはたまに使うことの出来る Emacs を使うか、という形になることが多いと思ってます。ただいずれもキーバインドにクセがあり、サーバー環境初心者がメモ帳感覚でサーバー上のエディタを使うのが難しいという一面もあります。

そんな中、比較的普通(?)のキーバインド感覚で使えるのが "nano" エディタです。GNU プロジェクトの1つであり、GUI なしのターミナル環境で使える軽量エディタです。vi や Emacs だと「ファイルを保存」したり「エディタを終了」したりするにも専用のキーバインドを覚える必要があるので、初心者のとっかかりにはかなり高いハードルになってしまいますが、nano エディタは「常時表示されているメニューから選ぶだけ」なので、そのあたりのハードルは低めに設定されているといえます。 個人的にはあまり利用する機会のなかった nano エディタを調べてみました。


【インストール】
nano エディタは多くの環境で標準コマンドとして導入済みのことが多いと思いますが、導入されていない場合はインストールする必要があります。環境に合わせて、以下のいずれかのコマンドで導入してください:
(CentOS/RedHat 系の場合)
$ sudo yum install nano

(Ubuntu/Debian/Raspbian 系の場合)
$ sudo apt-get install nano


【起動】
コマンドラインからそのまま
$ nano

と入力することで nano エディタが新規ファイル作成モードで起動します。またはファイルの名前と一緒に
$ nano test01.txt

と入力すると、指定したファイルを編集するモードで起動します。


【画面】
実行中のターミナル内でフルスクリーンエディタとして起動します。画面下部にはメニューが常時表示されます。普通にカーソルキーで上下左右にカーソルを移動させることができ、キーを入力するとそのまま画面に表れます:
20170711


【メニュー】
以下、各メニューの項目を紹介します。メニュー項目を利用する場合はファンクションキーか Ctrl キーと表示されている文字を同時に押します( "^G" と表示されている場合は Ctrl + G です)。また以下で "M-*" という表記になっている場合は 「ESC キーを押してから * キー」という意味です:


^G (F1) : ヘルプ

以下のようなオンラインヘルプ画面を表示します。^Y / ^V で次/前ページへ移動、^P / ^N で次/前行で移動します( ^Y / ^V は編集画面でも同様に動きます)。^X でこのメニュー画面を終了します:
2017071102


^X (F2) : 終了

nano エディタを終了します。未保存の編集中のファイルがある場合は保存するかどうか確認した上で終了します(Y で保存、N で変更破棄、^C でキャンセル)。
2017071101


^O (F3) : ファイル書き出し

編集した内容をファイルに書き出します。ファイル名が指定されている場合はそのファイルに、ファイル名が指定されておらず、新規作成モードの場合はファイル名を指定して保存します:

2017071104

ESC キーとの併用でファイルフォーマットを指定したり、別のファイルの最後尾に追加する、という指定も可能です。


^J (F4) : テキスト整列

現在のコンソールサイズに合わせてテキストを整列し直します。



^R (F5) : ファイル読み込み

カーソル位置に別のファイルの内容を挿入します:

2017071103

^X で「コマンドの実行結果を挿入する」という指定もできるようです。


^W (F6) : 検索

指定したテキストを、現在のカーソル位置から後方に向かって検索し、最初に見つかった所へカーソルを移動します:

2017071105

ESC キーを組み合わせることで検索方向を前方に変更したり、正規表現指定が可能になったりします。


^Y (F7) : 前ページへ移動


^V (F8) : 次のページへ移動


^K (F9) : 1行カット

カーソルのある行をカットします。カーソル行は削除されますが、後述のペーストで元に戻せます。


^U (F10) : 1行ペースト

「カットのアンドゥ」で、^K でカットした行をペーストします。


^C (F11) : カーソル位置の確認


テキストが長くなって一画面で全てが表示しきれないような場合に、現在のカーソル位置が全体の何行目の、何文字目にあるのか、という情報を出力してくれます:

2017071106



^T (F12) : スペルチェック

内蔵されている spell を使ったスペルチェック機能らしいのですが、自分の環境ではうまく動きませんでした。。



以上、nano エディタの基本的な使い方について紹介しました。本格的なソースコード編集は手元の専用エディタを使うとして、サーバーにログインして作業が必要になった場合に vi や emacs が分からなくても、この nano エディタを使うことができれば、最小限の設定ファイル書き換えなどはできそうなので、これら2つのエディタに不慣れな人は重宝するかもしれません。



なお、nano エディタの解説はこちらの wiki にも詳しく紹介されていました。設定ファイルによってシンタックスハイライトなどもできそうです。こちらも参考にどうぞ:
https://wiki.archlinuxjp.org/index.php/Nano








 

このブログでも何度か Node.js のネタを扱ってますが、非同期処理に悩まされることが多いので、自分の理解の意味でもまとめておくことにしました。

まず理解の大前提として、Node.js はシングルスレッドで動作するため、いわゆる並列処理はできない仕様になっています。そして非同期に処理を実行することができます。これによって並列処理ができなくても、何かの時間のかかる処理があった場合にその終了を待たずに次の処理に進むことができることを意味しています。 ただ、この辺りがややこしく難解になっていることも事実です。

例えば REST API などの HTTP リクエストを行って、その結果が得られたら、その得られた結果の JSON オブジェクトの値を使って処理をする、などというよくあるケースでもこの問題に直面します。

まずは何が難解なのかを紹介します。REST API だとローカル環境で気軽に試せないので、わざと実行に時間のかかる関数を用意して説明します。

例えばこのような処理を考えてみます:
// test.js

// わざと1秒かけてから、パラメータの2倍の数値を出力する関数
function func1( x ){
  setTimeout( function(){
    console.log( 2 * x );
  }, 1000 );
}

// 初期値を設定して出力
var n0 = 10;
console.log( 'n0 = ' + n0 );

// 初期値を上記関数のパラメータに入れて実行
func1( n0 );

この中では func1 という関数を定義しています。setTimeout を使ってわざと1秒(1000ミリ秒)待ってから、パラメータの値の2倍を画面に出力する、という関数です。 この関数を n0 (=10 に設定)という変数をパラメータにして実行する、というものです。なので "20" という結果が出力されることを期待しています。

この内容を test.js というファイルに保存して、実行してみます(青字部分が出力結果):
$ node test
n0 = 10
20

期待通りに "20" と出力されました。とりあえずここまでは成功です。


さて問題はここからです。上記例では関数 func1 の中で console.log が実行されて出力までを行いました。これを func1 からは与えた数値を2倍した結果を受け取るようにして、func1 の外側で出力するように変更してみます。深く考えずにやるとこんな感じでしょか?
// test.js(注 正しく動きません)

// わざと1秒かけてから、パラメータの2倍の数値を出力する関数
function func1( x ){
  setTimeout( function(){
    console.log( 2 * x );
  }, 1000 );
}

// わざと1秒かけてから、パラメータの2倍の数値を戻す関数
function func2( x ){
  setTimeout( function(){
    return ( 2 * x );
  }, 1000 );
}

// 初期値を設定して出力
var n0 = 10;
console.log( 'n0 = ' + n0 );

// 初期値を上記関数のパラメータに入れて実行し、戻り値を出力
var n1 = func2( n0 );
console.log( 'n1 = ' + n1 );

func1 をほぼコピペして func2 という関数を作りました。console.log の代わりに return にして、値を返すようにしています。呼び出し元からはこの func2 を実行して、得られた結果を出力するようにしています。

これを先程と同様に実行するとこうなります:
$ node test
n0 = 10
n1 = undefined

n0(10)の値の2倍の "20" という結果を期待していたのですが、"undefined" と表示されてしまいます(正確に書くと、上記の2行はすぐに表示されますが、更に1秒くらい経過してから終了します)。この理由は最初に書いたように func2 は非同期に実行されているので、return の行が実行されるまでには1秒かかります。しかしその前に(return の行が実行される前に)関数そのものの処理は終了してしまいます。つまり値が戻る前に(戻っていない値を受け取ることになっている)n1 という変数を出力しているので "undefined" になっているのでした。

このように、期待通りに動かなかった理由は明白なのですが、ではどうすればこの関数が期待通りに動く(1秒後に与えられた結果を戻り値として戻し、受け取った側がその値を出力する)ようにできるでしょうか?これが今日紹介する大きなテーマです。


結論を先に紹介すると、ここで Promise オブジェクトを使って関数を修正し、受け取った側もその変更に合わせて一部書き直す必要があります。具体的には以下のように修正します:
// test.js

// わざと1秒かけてから、パラメータの2倍の数値を出力する関数
function func1( x ){
  setTimeout( function(){
    console.log( 2 * x );
  }, 1000 );
}

// わざと1秒かけてから、パラメータの2倍の数値を戻す関数
function func2( x ){
  return new Promise( function( resolve ){
    setTimeout( function(){
      resolve( 2 * x );
    }, 1000 );
  });
}

// 初期値を設定して出力
var n0 = 10;
console.log( 'n0 = ' + n0 );

// 初期値を上記関数のパラメータに入れて実行し、戻り値を出力
func2( n0 ).then( function( n1 ){
  console.log( 'n1 = ' + n1 );
});

まず関数 func2 側は、Promise オブジェクトを新規に作成します。Promise オブジェクトは処理が成功した場合の関数をパラメータに指定します。上図だと
function( resolve ){
  setTimeout( function(){
    resolve( 2 * x );
  }, 1000 );
}

という関数がパラメータに指定されているので、成功するとこの関数が実行されます(今回は使っていませんが、第二パラメータを指定した場合は失敗時に実行する処理を指定したことになります)。この処理の中で1秒待って、指定したパラメータを2倍して resolve とする、ということになります。

そしてこの関数 func2 を呼び出す側も少し変更が必要になります。 func2() 関数の実行結果をそのまま変数として受け取るのではなく、成功した場合(今回の例だと1秒待って2倍になった値が返された場合)の処理を .then() 内に渡して処理することになります。この then 内の処理で計算結果(resolve で処理された内容)を n1 という変数で受け取って console.log で表示する、という内容にしています。

こうして修正した test.js を実行すると、以下のような結果になります(実際には n0 = 10 がすぐに表示され、1秒くらい待ってから n1 = 20 の行が表示されて終了します):
$ node test
n0 = 10
n1 = 20

Node.js の関数内で非同期処理を実行して、その非同期処理の終了を待って値を受け取るような関数を作る場合は、Promise オブジェクトを使って上記のように記述します、という紹介でした。非同期実行に慣れていないと、この辺りで戸惑うことが多いと感じたので、まとめておきました。


このページのトップヘ