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

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

2018年05月

IBM Cloud から提供されているコグニティブエンジン IBM Watson を使って、
 1. MNIST の手書き数字サンプルデータを学習させて、
 2. 実際に手書き数字データを送信して、認識させる
という、「学習」と「問い合わせ」のコグニティブエンジン一連の作業を再現させてみます(した)。


今回紹介する一連の作業では、IBM Cloud の以下のサービスを連動させて使います:
 ・IBM Watson Studio
 ・IBM Machine Learning
 ・IBM Cloud Storage
 ・SDK for Node.js ランタイム(上記2のサンプルをクラウド上で稼働させる場合)

以下で紹介する手順は IBM Cloud の無料版であるライトアカウントを使っても同様に動かすことができるようにしているので、興味ある方は是非挑戦してみてください。


1. MNIST の手書き数字サンプルデータを学習させる

人工知能とか機械学習とかを勉強していると、そのチュートリアルとして "MNIST" (Modified National Institute of Standards and Technology)を目にする機会があると思っています。機械学習のサンプルとして手書きで描かれた数字の画像データと、そのラベル(何の数字を描いた画像なのか、の答)が大量にサンプルデータとして公開されており、機械学習を説明する際の様々な場面で使われています:
2018050800


今回、この MNIST データを IBM Watson StudioIBM Watson Machine Learning を使って学習させ、かつ問い合わせ用の REST API を用意します。

・・・と、偉そうに書いていますが、この部分の手順については私の尊敬する大先輩・石田剛さんが Qiita 上でわかりやすく紹介していただいています。今回の学習部分についてはこの内容をそっくりそのまま使わせていただくことにします(石田さん、了承ありがとうございます):
Watson Studioのディープラーニング機能(DLaaS)を使ってみた 

2018050801

↑この作業で MNIST の手書き画像を IBM Watson Machine Learning を使って学習させ、その問い合わせ API を REST API で作成する、という所までが完了します。


2. 手書き数字データを送信して、認識させる

マウスやタッチ操作で画面に手書き数字を描き、その内容を 1. の作業で用意した REST API にポストして何の数字と認識するか、を確認できるようなアプリケーションを作成します。

・・・というか、しました(笑):
2018050804


PC またはスマホでこちらのサイトにアクセスすると体験できるようにしています:
https://dotnsf-fingerwrite-mnist.us-east.mybluemix.net/


フロントエンドはもともと以前に「イラツイ」という手描きイラスト付きツイートサービスを作った際のものを丸パク応用し、問い合わせ API を呼び出すバックエンド部分はデプロイしたモデルの Implementation タブ内にある JavaScript の Code Snippets を参考に作りました。この Code Snippents は各種言語のサンプル(アクセストークンを取得してエンドポイントにリクエストするサンプル)が用意されていて、とても便利です:
2018050809


アプリケーションの使い方はマウスまたは指でキャンパス部分に数字を描いて、"fingerwrite" ボタンを押すと、その描いた数字データを上記 1. で作成した REST API を使って識別し、最も可能性が高い、と判断された数値とその確率が表示される、というものです:
2018050805


PC 画面の場合に限りますが、デバッグコンソールを表示した状態で上記を実行すると、可能性が最も高いと思われた結果だけでなく、全ての数値ごとの確率を確認することもできます:
2018050806

↑常に「2」の確率が高くなってる気がする。。原因は学習の調整不足だろうか??それともデータを渡すフロントエンド側??(2018/May/09 ピクセル毎のデータを取り出すロジックに不具合があったので、修正しました)


なお、この 2. のサンプルアプリは Node.js のソースコードを公開しているので、興味ある方は自分でも同様のサイトを作成してみてください:
https://github.com/dotnsf/fingerwrite-mnist

2018050807


このソースコードから動かす場合、事前に settings.js ファイルを編集しておく必要があります:
2018050808


まず上の3つ、 exports.wml_url, exports.wml_username, exports.wml_password の3つの変数の値は 1. で MNIST データを学習した際に使った IBM Watson Machine Learning サービスのサービス資格情報を確認して、その中の url, username, password の値をそれぞれコピー&ペーストしてください(最初の exports.wml_url だけはおそらくデフォルトで url の値になっていると思います。異なっていた場合のみ編集してください):
2018050803


また一番下の exports.ws_endpoint の値は同様に 1. で使った IBM Watson Studio の Web サービスのエンドポイント(学習モデルをデプロイした時に作成した Web サービス画面の Implementation タブから確認できる Scoring End-point の値)をそのまま指定します:
2018050802


ここまでの準備ができた上でアプリケーションを実行します。ローカル環境で動かす場合は普通に npm install して node app で起動します:
$ npm install
$ node app

IBM Cloud (の SDK for Node.js)を使って動かす場合は、cf ツールbx ツールを使って、そのまま cf push で公開されます:
$ cf push (appname)


今回紹介した方法では IBM Watson Studio と IBM Watson Machine Learning を使って画像データを学習させ、その学習結果に対して REST API で問い合わせをする、という機械学習の一連の流れを体験できます。また学習データ(とモデリング)を変更することで、異なる内容の学習をさせる応用もできますし、学習した内容に問い合わせを行う API も自動生成されるので、フロントエンドの開発も非常に楽でした。
 

たまに子供や初心者を相手にプログラミングを教えてみせたりする際に Scratch を使っています:
Screenshot from 2018-05-04 15-28-23


MIT メディアラボが開発した GUI によるプログラミング開発言語および実行環境です。プログラムをエディタでガリガリと記述するような、いわゆる「コーディング」に相当する作業があまり必要なく、GUI を中心とした見た目に対する処理や変更をイベント単位に足していきながら少しずつ動くものを作っていくものです。そのためユーザーが完成形をイメージしながら作っていくことができ、コーディングそのものに不慣れな人がプログラミングを理解するのに向いていると思っています。

この Scratch は良くも悪くも GUI 中心の作業になるため、他の環境との連携が難しい(というか、そもそもどういう外部インターフェースが用意されているのかわかっていなかった)という印象を持っていました。Scratch の閉じた世界の中では初心者にも簡単でわかりやすい、という面があるのと同時に、Scratch 以外の環境とは繋がりにくいと思っていました。

で、なんかインターフェースが公開されてないかなあ・・・ と思って探していたら、この Scratch の国内第一人者でもある石原さんが書かれた一連のブログを見つけました:
Scratch(スクラッチ)を外部のプログラムなどとつなぐ「遠隔センサー接続」を解説する(その1)


↑このブログで書かれている内容は Scratch 1.4 の「遠隔センサーを有効に設定」して 42001 番ポートを開放した上で、TCP/IP のメッセージングによる連携を Ruby で行う、というものです。基本的にはほぼ同じ内容を Node.js で実装してみようと思い、調べた結果をまとめました。


なお、石原さんは「まちくえすと」の開発などで活躍されていてご存知の方もいらっしゃると思いますが、拙作マンホールマップiOS アプリ版の開発に尽力いただいたご縁もあり、そのお付き合いの中で Scratch のことも教えていただきました。僕にとっては「Scratch の先生」でもあります。石原さん、このブログとても助かりました。ありがとうございます!



まず、以下の作業を行う前提として、Scratch はローカルインストール可能なバージョン 1.4 を使う必要があります。公式サイトからオンライン版であるバージョン 2.0 以降を利用することも可能ですが、以下で紹介する Node.js 連携は Scratch 1.4 をローカルインストールして使う想定で記載しています。各種 OS 向けの Scratch 1.4 はこちらからダウンロードして導入してください(以下、Ubuntu 16.04 環境に Scratch 1.4 を導入した前提で記述します):
https://scratch.mit.edu/scratch_1.4/



まず最初に、Scrach を外部連携するためのポート開放を行います。Scratch 1.4 を起動し、「調べる」カテゴリ内「●●センサーの値」と書かれたブロックを右クリックして、「遠隔センサー接続を有効にする」を選択します:
Screenshot from 2018-05-04 15-29-09


すると「遠隔センサー接続が有効になりました」という確認ダイアログが表示されます:
Screenshot from 2018-05-04 15-29-39



この作業後、Scratch は 42001 番ポートで接続できるようになっています。つまり僕の以前のブログで紹介したような TCP/IP プログラミングによって Scratch との TCP/IP 接続が可能になっているのでした。試しに以下のような Node.js プログラム(scratch01.js)を書いて実行してみます(以前のブログで書いたものよりも更に簡略化し、最小限必要な内容だけにしています):
var net = require( 'net' );

//. 接続先 IP アドレスとポート番号
var host = '127.0.0.1';
var port = 42001;

//. 接続
var client = new net.Socket();
client.connect( port, host, function(){
  console.log( '接続: ' + host + ':' + port );
});

//. サーバーからメッセージを受信したら、その内容を表示する
client.on( 'data', function( data ){
  console.log( '' + data );
});

↑このプログラムを実行すると、127.0.0.1(自分自身)の 42001 番ポートに TCP/IP 接続し、接続先から何かメッセージが送られてくるとそのメッセージ内容を文字列化してそのまま表示する、というものです。これをまず node コマンドを使って実行しておきます:
$ node scratch01.js

これで 42001 番ポートに接続し、メッセージの受信待ち状態になりました。


そして Scratch 側からはこの(42001 番ポートの)接続先に対してメッセージを送信してみます。この処理を行うには「制御」カテゴリ内「●●を送る」ブロックを使います:
Screenshot from 2018-05-04 15-40-55


同ブロックを右クリックして「新規」を選択し、メッセージの名前を任意に指定します。以下の例ではメッセージの名前に Hello と指定してます:
Screenshot from 2018-05-04 15-42-14


するとブロックの表示内容が「Hello を送る」と切り替わります。この状態で同ブロックを何度かマウスでクリックします(クリックするたびにこのブロックが処理を実行します):
Screenshot from 2018-05-04 15-42-41


すると先に実行していた Node.js プログラムのコンソール画面に(最初の制御文字に続いて) 
broadcast "Hello"
という文字行がクリックするたびに表示されます:
Screenshot from 2018-05-04 15-46-59


この結果から Scratch の「●●を送る」ブロックを1回実行すると
broadcast "(指定したメッセージ)"

という文字列が送信される、ということが分かりました。これで Scratch から Node.js のプログラムにメッセージを送信し、Node.js プログラム側でその内容を受信することができました。


この応用で逆に Node.js から Scratch にメッセージを送ることもできます。例えば scratch02.js を以下の内容で作成します(接続後に 'broadcast "a"' というメッセージを Scratch に送信する、という内容です):
//. scratch02.js
var net = require( 'net' );
var host = '127.0.0.1';
var port = 42001;

var client = new net.Socket();
client.connect( port, host, function(){
  console.log( '接続: ' + host + ':' + port );

  var msg = 'broadcast "a"';
  var len = msg.length;
  var buf = pack( len );
  client.write( buf + msg );  //. msg の内容を Scratch へ送信
});

//. 4バイトのバイト列(Buffer)に変換
function pack( n ){
  var m1 = n % 256;
  n = Math.floor( n / 256 );
  var m2 = n % 256;
  n = Math.floor( n / 256 );
  var m3 = n % 256;
  var m4 = Math.floor( n / 256 );

  return new Buffer( [ m4, m3, m2, m1 ] );
}



そして Scratch 側では 'broadcast "a"' というメッセージを受け取ったら何らかの処理がスタートする、というプログラムを書いておきます(下の例ではスプライト1の猫のキャラクターが画面内を歩き始める処理がスタートする、という内容にしています):
Screenshot from 2018-05-05 15-54-06


この状態で scratch02.js を実行すると、TCP/IP 通信によって Scratch に接続し、broadcast "a" というメッセージが送られ、Scratch のスプライト1のキャラクターが壁に跳ね返りながら動き回る処理がスタートします(特に止める方法を用意していないので、止めるには画面右上の赤丸部分をクリックしてください):
Screenshot from 2018-05-05 16-02-12


これで Scratch から Nodejs にメッセージを送ったり、逆に Node.js から Scratch にメッセージを送る、ということが実現できました。

 

Node.js はサーバーサイドで動く JavaScript ということもあって、Express などを使ってサーバーサイドのアプリケーションランタイムとして利用することが多いと思っています。

が、必ずしもサーバーサイド開発言語にのみに使えるということはなく、クライアント側のアプリケーションを記述・実行することも可能です。そんな一例として(HTTP よりも汎用的な)TCP/IP を使ったクライアントアプリケーションを作るサンプルを紹介します。

以下は 127.0.0.1 の 42001 番ポートで待ち受けるサーバーに対して、クライアントとして接続したり、メッセージを送信したり、受信したり、切断したときの挙動を実装しています:

var net = require( 'net' );

//. 接続先 IP アドレスとポート番号
var host = '127.0.0.1';
var port = 42001;

//. 接続 var client = new net.Socket(); client.connect( port, host, function(){ console.log( '接続: ' + host + ':' + port ); //. サーバーへメッセージを送信 client.write( 'ハロー' ); }); //. サーバーからメッセージを受信したら、その内容を表示する client.on( 'data', function( data ){ console.log( data ); }); //. 接続が切断されたら、その旨をメッセージで表示する client.on( 'close', function(){ console.log( '切断' ); })

実際の挙動としてはサーバーへメッセージを送る必要があるのかないのか、サーバーからメッセージを受信することがあるのかないのか、・・・といったサーバー側の挙動も考慮してクライアント側を作ることになりますが、特定のポートに対する TCP/IP 接続はこんな感じで実現できるようです。

このページのトップヘ