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

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

タグ:watson

以前に紹介した SaaS 型のデータ解析サービス IBM Watson Analytics は強力な解析エンジンが安価で使えて、面白いサービスだと思っています:
2017040501



ただちょっと不便に感じたのは、データのインポート方法でした。手元に CSV などのファイルがあれば Watson Analytics 側に用意された機能でインポートできます。またデータが Box や Twitter などのクラウドに公開されたものであれば、やはり Watson Analytics に用意された機能で取り込むことができました:
2017040502


しかし現実的にはデータベースサーバーから直接取り込むことができると便利です。更に言うと、インターネットに公開されたデータベースサーバーではなく、プライベートネットワーク内にあるデータベースのデータを簡単に取り込むことができると、プライベートデータを(いちいち CSV にしてから手動インポートする必要がなくなって)便利なのですが、現在 Watson Analytics 単体にそこまでの機能は提供されていません。


しかし、単体では提供されていなくても、IBM Bluemix から提供されるいくつかの機能を組み合わせることで上記の要件は可能になります。具体的には Secure Gateway サービスと Data Connect サービスを使います。Secure Gateway でプライベートネットワークのデータベースに対してセキュアなトンネリングを貼った上で Data Connect を使い、そのプライベートデータベースと Watson Analytics との間での自動データマイグレーションが実現できます(そのマイグレーションを定期的に実行するようなスケジューリングまで含めて可能になります)。






以下に実際の手順を紹介しますが、実際にこの手順を行うにはプライベートネットワーク内のデータベースに加えて、IBM Bluemix および IBM Watson Analytics 両方のアカウントが必要です。そして IBM Bluemix では Secure Gateway と Data Connect の2つのサービスインスタンスが有効になっている必要があります。無料版にサインアップするなどして、あらかじめご用意ください。


まずプライベートネットワーク内のデータを確認しておきます。今回対象とするのはプライベートネットワーク内のデータベースサーバーで管理されている(仮想の)人事情報とします。名前(name)や年齢(age)、収入レベル(income)、容姿(looking)、血液型(blood_type)、出身地(prefecture)といった情報が格納されているものです(注 乱数で生成した偽データです)。これは認証は当然ですが、現時点ではプライベートネットワーク内からのアクセスしか許可していないものです。目的はこれらのデータを(一度 CSV ファイルにして取り出して手動アップロードしたりするのではなく)Watson Analytics で解析可能なデータとしてシステマチックにインポートすることとします:
2017040513


では実際の手順を紹介します。まずは Secure Gateway でこのプライベートネットワーク内のデータベースを IBM Bluemix 環境からアクセスできるようにします。その手順はこちらを参照してください。
SecureGateway で Bluemix とプライベートネットワークをセキュアに接続する


次に Secure Gateway で参照できるようになったプライベートデータを Data Connect のデータセットとして定義します。この手順についてはこちらを参照してください。
Data Connect サービスでデータベースをマイグレーションする


ここまで完了したら、次は Data Connect の接続先として利用できるよう、Watson Analytics を追加します。Data Connect サービスのダッシュボード画面左メニューから "Connections" を選択し、画面右上の "Create New" をクリックします:
2017040501


接続先のデータベースタイプの選択画面では "IBM Watson Analytics" を選択します:
2017040502


詳細設定画面に移動しますが、ここで設定する項目は接続名称を入力するだけです。最後に画面右上の "Create Connection" をクリックします:
2017040503


この接続情報が保存される際に IBM Watson Analytics への認証が行われます。IBM Watson Analytics を利用している IBM ID とパスワードを指定してログインしてください:
2017040504


正しく有効な ID が確認されると、Data Connect の接続情報として IBM Watson Analytics が追加されます:
2017040505


では作成した Watson Analytics にプライベートデータベースからデータをマイグレーションします。引き続き Data Connect ダッシュボードの画面左メニューから "Activities" を選び、右上の "Create New" をクリックします:
2017040506


まずマイグレーション元を指定します。あらかじめ作成してある Secure Gateway 経由の(プライベートデータベースの)データセットを選択します。選択したら画面右上の "Copy to Target" をクリックします:
2017040507


次にマイグレーション先を指定します。今回のマイグレーション先は IBM Watson Analytics なので、Connections から上記で定義した IBM Watson Analytics の接続名を選択します。このままマイグレーション実行スケジュールの指定("Schedule Activity")も可能ですが、まず1回手動で実行してみることにします。画面右上の並んだボタンから "Run" をクリックします:
2017040508


指定したとおりの(プライベートデータベースから Watson Analytics への)マイグレーション処理が実行され、アクティビティとして記録されます。なお、このアイコンから実行スケジュール(定期実行など)を編集することも可能です:
2017040509


実際にマイグレーションが行われたかどうかを確認してみます。改めて IBM Watson Analytics にログインすると、元々は存在していなかったデータセットが作られているはずです。これをクリックして、実際に解析してみます:
2017040510


解析テンプレートが表示されます。いくつかありますが、"What is the trend of looking over age by blood_type?"(血液型別に年齢と容姿にどういったトレンドがあるか?)が面白そう(笑)なので選択してみます:
2017040511


この解析用の画面に移動します。画面左側に年齢ごとの容姿の推移が血液型ごとの折れ線になって表示されます。また画面右にはこれらの分析からの発見として、「43歳の収入が一番高い」とか「年収をトータルすると AB 型が一番低い」といったインサイトも併せて表示されています(※繰り返しますが、乱数を使って生成した偽データです):
2017040503


特別何もしなくてもデータを与えるだけでここまで分析してくれる Watson Analytics もすごいのですが、その Watson Analytics に直接プライベートデータや社内データをセキュアにマイグレートしてくれる Secure Gateway や Data Connect と併せて利用することで、より便利に大事な情報を簡単に解析できるようになると感じます。


MQTT は IoT の仕組みの中で使われることの多いプロトコルですが、以前からそれだけに使うのは勿体ないなあと思っていました。MQTT のリアルタイム性はチャットなどのメッセージングアプリケーションにも向いていると思っており、実際に Facebook Messenger の仕組みとしても使われているとの情報もあります。というわけで、MQTT を使ったチャットアプリの開発に挑戦してみました。

加えて、IoT といえば IBM Bluemix からも提供されている Node-RED が有名です。今回は IBM Bluemix 環境上の Node-REDIBM IoT Platform サービスの quickstart エディションを使ってチャットを作ってみることに挑戦しました。

何はともあれ、まずは Node-RED 環境を用意します。IBM Bluemix にログインし、ボイラープレートから Node-RED Starter を選択して、Node-RED が使えるアプリケーションサーバーインスタンスを用意します:
2017040301


IBM Bluemix の Node-RED を使わずに、自前等で Node-RED 環境を用意する場合は npm などで node-red-contrib-scx-ibmiotapp ノードをインストールして有効にしておく必要があります:
2017040302

(↑この ibmiot ノードが使える状態にしてください)


では Node-RED でチャットアプリを作ります、といっても実はかなりシンプルです。1つ1つノードを配置してもいいのですが、まずはインポートして中身を確認し、必要に応じて説明を加えることにします。画面右上のハンバーガーメニューを開き、 読み込み→クリックボード を選択します:
2017040303

 
「フローの読み込み」画面になったら、ここの内容をそっくりそのままコピー&ペーストして「読み込み」ボタンをクリックし、フロー定義を作成します:
2017040304


正しく読み込みが完了すると以下のような3本のデータフローが再現されるはずです。上から1つ目は GET /chat 実行時のチャット画面(HTML)の定義、2つ目はチャットメッセージを POST(POST /post) した時の処理、そして3つ目は IBM IoT サービスを使って MQTT 経由でチャット参加メンバーのメッセージを取り出す処理を定義しています。いずれもシンプルな処理で実現できていることが確認できます:
2017040305


画面内に2つの IBM IoT ノードが含まれています(青いノード、INPUT/OUTPUT が1つずつ)。それぞれダブルクリックすると、どちらにも Device Id を入力する項目があり、いずれも初期状態では空になっているはずです。この Device Id にはユニークな値を指定する必要があります。以下の例では "dotnsf.mqtt.chat.001" という値を設定していますが、ここには自分の名前や日付を含めるなどして、誰とも被らないユニークな値を設定します(2つのノード両方の Device Id に同じ値を指定します)。指定後「完了」ボタンをクリックします:
2017040306


また、2つ目のフローの中にある function ノードの中身を確認します。ここはチャット参加者が自分のメッセージを投稿した時に実行されるフローで、HTTP リクエストの本文(msg.req.body)の値を取り出して、その値を IoT の Payload に変換している部分です。これも非常にシンプルな処理内容が記述されていることが確認できます:
2017040307


改めて3つそれぞれのフローの中でどのような処理が定義されているのかを確認してみましょう。1つ目のフローはウェブブラウザから(サーバー名)/chat という URL に(GET リクエストで)アクセスした時に返される HTML の定義です。実際の HTML や CSS/JavaScript 定義そのものは「チャット画面」というテンプレートノードの中で定義されています(後述します):
2017040301


2つ目のフローはチャット画面の中で利用者が自分のメッセージをチャットに流した時に実行される処理です。チャットにメッセージを流すと(サーバー名)/post という URL に名前やメッセージ内容が HTTP POST され、その内容を(上記のように)取り出して MQTT の Payload に変換し、IBM IoT に転送(MQTT の処理でいうと「パブリッシュ」)しています。転送時にユニークな Device Id を指定していることで、同じテンプレートを使っても異なるアプリケーションであるとみなし、他の人が作ったチャットと混線しないようにしています。なお、緑色のノードはデバッグノードで、POST されたメッセージの内容をこの画面内からも確認できるようにしているだけで、実際の処理には無関係です(無くても動きます):
2017040302


そして3つ目のフローは上記2つ目のフローで処理されたメッセージを取り出すフローになります。自分だけでなく、同じチャット画面を見ている他のユーザーがメッセージを流した場合もこの処理が実行され、IBM IoT 経由で送られたメッセージが(サーバー名)/ws/chat という URL の WebSocket に送信されるよう記述されています。実際には1つ目の HTML の中で /ws/chat を監視しており、ここにメッセージが送られてきた場合の処理が記述されています:
2017040303


この状態でデプロイ(画面右上のボタン)をクリックすることで実際にチャットアプリケーションを使うことができるようになります。デプロイ後、PCやスマホのウェブブラウザで https://(Node-RED の動いているホスト名)/chat にアクセスしてみてください。Node-RED の一番上のフローが呼ばれ、テンプレートノードの中で定義された内容の HTML が表示されます。初期状態では↓のように名前の入力を求められます:
2017040301


適当な名前を入力して「入室」ボタンをクリックします(入室のタイミングで IoT と接続します):
2017040302


入室すると画面が切り替わり、自分の名前とメッセージ入力フィールドが画面下に表示されます。画面の大半はチャット履歴が表示される画面ですが、まだ何もメッセージがないので何も表示されていません:
2017040303


では試しに何かメッセージを入力してみます。入力を確定するには PC からであれば Enter キーを、スマホであれば「開く」などでメッセージを確定させてください:
2017040304


入力したメッセージがチャット履歴に表示されます。これは自分のメッセージなので右側に吹き出しがついて、緑色で表示されるようにしています:
2017040305


もう1つメッセージを送ると、メッセージが下に追加されます:
2017040306


試しに別のブラウザや別のスマホなどから同じ URL にアクセスして、別の名前で入室してメッセージを送信してみます。このユーザーから見ると入室前のメッセージは見れないので、自分のメッセージが一番最初に表示されます:
2017040307


が、元のユーザーからは別のユーザーが入室してきてメッセージを送信したことになります。その場合は白背景で、左側に吹き出しがある状態でチャット履歴に記録されます(この UI 見たことありますよね。意識して CSS を作ってます(笑)):
2017040308


同様にして別のユーザーが入室してくると、そのユーザーのメッセージも白背景で左に吹き出しが付く形で表示されていく、というものです。最低限のグループチャットの機能は実現できていると思ってます:
2017040309


さて、ではこのチャット画面の HTML はどうなっているのかを説明します。具体的な内容は PC ブラウザからアクセスして HTML ソース(と埋め込まれた CSS など)を直接参照していただきたいのですが、肝心な部分の JavaScript はこのようになっています(赤字はコメント):
  :

var socket; var wsUrl = 'wss://' + location.hostname + '/ws/chat'; //. WebSocket監視先URL function connect(){ //. 「入室」時に呼ばれる処理 console.log( "connect()" ); socket = new WebSocket(wsUrl); //. WebSocket 接続 socket.onmessage = function(e) { //. WebSocket にメッセージが来たら、以下を実行 var msg = JSON.parse(e.data); //. 送信データ(POST されたデータ)を JSON で取り出し //console.log( msg ); if( msg.id != deviceid ){ //. 自分のメッセージなのか、他人のメッセージなのかを判別 //. 自分以外の発言 var box = "<div class='question_box'><p class='notmymessage'>" + msg.name + "</p><div id='arrow_question'>" + msg.message + "</div></div>"; $('#contents').append( box ); }else{ //. 自分の発言 var box = "<div class='answer_box'><p class='mymessage'>" + msg.name + "</p><div id='arrow_answer'>" + msg.message + "</div></div>" $('#contents').append( box ); } } }

:

2つ目のフローで投稿したメッセージの内容が IBM IoT ノードに(MQTT で)送られていました。自分だけでなく同じチャットルームに入室している全ての人のメッセージがこのように MQTT データとして送信されます。 そしてその内容を3つ目のフローで取得し、/ws/chat というパスに WebSocket データとして送信していました。つまりチャットで誰かがメッセージを送ると、/ws/chat に WebSocket でデータが送られるということになります。そのデータを監視して、自分のメッセージか他人のメッセージかを判別して Dynamic HTML でチャット履歴に追加する、という部分の処理が上記になります。


そしてこれだけでチャットが実現できているということは、(気付いている人もいるかもしれませんが)少なくともここまでの処理に関してはデータベースを一切使わずに実現できていることを意味しています。確かにリアルタイムデータ処理なのでデータを保存する必要はないのですが、実際に保存せずに実現できるというのはなかなか興味深いのではないかと思っています。


IBM ワトソンなどの各種コグニティブエンジンを使う際に、重要なのは学習データだと思っています。

要はコグニティブエンジンを使って問い合わせを行うわけですが、その問い合わせする前に何らかの学習をする必要があり、そこで何をどれだけ学習させたかによって問い合わせの精度が変わってくるからです。

で、先日こんなツイートをしました:
2017022601

https://twitter.com/dotnsf/status/834959690803007488


↑は、その学習データを何らかの方法で集める際に、(非同期処理で集めるのではなく)マルチスレッド処理がいいのではないか、と思ってつぶやいたのでした。このことをもう少し詳しく紹介しようと思います。なお、あくまで特定条件下での個人見解なので他の人の意見も聞いてみたいと思ってます。

やろうと思っているのは、大まかにはこんな内容です:
  1. あらかじめ用意したインターネット上の URL のリストから、その HTML を取得して学習データにしたい
  2. この「リスト」が大量にあるケースを想定する
  3. なお、リストの中には実在しない URL が存在している可能性がある(つまりリストそのものが間違っている可能性がある)
  4. また URL は実在しているのだが、ネットワーク障害や DNS の設定ミスなど何らかの原因でアクセスする際に非常に長い時間がかかる(長い時間がかかった結果、タイムアウトになったり成功したりする)場合もある

1と2はごく普通の条件ですよね。ここではインターネット上の URL のリストは配列変数で用意されているものとしましょう。配列から1つずつ URL を取り出して、その URL の HTML を取得する、という処理を施すことになります。

問題は3と4の条件です(でも現実的に想定すべき条件だと思ってます)。3は用意されたリストそのものが間違っているというケース、つまり与えられた URL は "Unknown Host" になったり、404 などのエラーが返ってくることを想定しないといけない、ということです。

また4は更にややこしい条件です。成功するかしないかではなく、こちらからは手が出せない箇所のなんらかの障害によって目的の URL へのアクセスに非常に時間がかかってしまうケースです。時間がかかった結果、目的の HTML が取得できればいいのですが、最終的にタイムアウトエラーが発生することもあり得る、というケースを想定する必要があります。


要するにこれらを想定したエラー対策が必要になるのですが、まずは3と4を無視して(エラーが発生しない前提で)普通にアルゴリズムを考えるとこんな感じになるでしょうか:
//. URL の配列
urls = [
  "http://xxx.com/a1.html",
  "https://xxx.com/a2.html",
  "https://yyy.net/",
  "http://abc.com/xyz.html",
     :
     :
];

//. URL を1つずつ取り出して HTML を取り出す
forall( url in urls ){
  html = getHTML( url );
      :
      :
  (取り出した HTML を使って学習処理を行う)
      :
      :
}


↑の例ですと、getHTML 関数の中で実際に指定した URL にアクセスして HTML を取り出す、という処理をするものとします。そしてこの関数の中で3や4を原因とするエラーや時間がかかるといった現象が発生することを想定してみます。

3のケースは実は単純で、実在しない URL が指定された場合はこの関数の返り値を null などにして、null でなかった場合のみ処理を続ける、という判断を加えることで解決します:
//. URL の配列
urls = [
  "http://xxx.com/a1.html",
  "https://xxx.com/a2.html",
  "https://yyy.net/",
  "http://abc.com/xyz.html",
     :
     :
];

//. URL を1つずつ取り出して HTML を取り出す
forall( url in urls ){
  html = getHTML( url );
  if( html != null ){ //. 3の対策
      :
      :
  (取り出した HTML を使って学習処理を行う)
      :
      :
  }
}


さて問題は 4 のケースです。3 のアルゴリズムを単純に(シーケンシャルに)実行した場合、特定の URL から HTML を取り出す処理に時間がかかってしまうような事態が発生すると、リストの途中で処理が先に進まなくなってしまう、ということになります。このようなケースを想定した上で効率よく処理を実行するにはどう改良すべきでしょうか?

1つの方法として考えたのが非同期処理です。上記のループ部分を非同期に処理して、リスト内の各 URL へのアクセスを非同期に行う、という方法です。アルゴリズムにするとこのような感じになるでしょうか:
//. URL の配列
urls = [
  "http://xxx.com/a1.html",
  "https://xxx.com/a2.html",
  "https://yyy.net/",
  "http://abc.com/xyz.html",
     :
     :
];

//. URL を1つずつ取り出して HTML を取り出す
forall( url in urls ){
  html = getHTML( url, function( err, html ){  //. getHTML を非同期に実行する
    if( err ){
      //. 3. のエラー処理
    }else{
//. 成功した場合
: : (取り出した HTML を使って学習処理を行う) : : } }); }
↑非同期ということで JavaScript っぽくしてみました

このように非同期に処理を行うことで、「各 URL の HTML を取得する」という命令を全て先に実行しておき、(成功にせよエラーにせよ)取得の処理が終わったらそれぞれの続きを行う、というロジックです。こうすることで 4 のような事態が発生しても、正常な URL に対する処理は邪魔されることなく先に終了し、時間のかかる処理だけが後から実行される、という一見きれいな形になります。

しかし、この方法にも問題点がありました。それは URL のリストが膨大だった場合です。上記のコードが非同期に実行されるとまず全て URL に対する HTML 取得のリクエストが発行されます。そしてその処理はシステムのメモリ量や TCP ソケット数上限を超えて実行されてしまう可能性があります。この部分はコーディングというよりもシステムのメモリ管理やソケット数管理などの厄介そうな処理を行う必要がでてきてしまいます。

で、冒頭のマルチスレッドです。上記の非同期で行っていた部分をマルチスレッドに書き換えることで、(スレッド生成間のスリープ時間を調整するなどの)ある程度のスケジュール調整をした上で同時に HTML を取得する処理を行うことができるようになります。例えばこんな感じです(ここは言語依存がありそうなので、Java 丸出しで記述しています):
//. URL の配列
urls = [
  "http://xxx.com/a1.html",
  "https://xxx.com/a2.html",
  "https://yyy.net/",
  "http://abc.com/xyz.html",
     :
     :
];

//. URL を1つずつ取り出して HTML を取り出す
forall( url in urls ){
  //. 子スレッドを生成して、スレッドの中で取得処理を実行
  Download dl = new Download( url );
  Thread t = new Thread( t );
  t.start();

  try{
    Thread.sleep( 1000 ); //. 1秒待ってから次の URL を処理
  }catch( Exception e ){
  }
}


//. マルチスレッドで動くインスタンス
public class Download implements Runnable{
  private String url = "";
  public Download( String url ){
    this.url = url;
  }

  public void run(){
    html = getHTML( url );
    if( html != null ){
      :
      :
  (取り出した HTML を使って学習処理を行う)
      :
      :
    }
  }
}

↑マルチスレッドなので Java 全開の記述法で

この方法であれば、各 URL に対する HTML の取得は別々のスレッドで行われるため、特定の URL で 4 のような遅延現象が発生しても、他の URL に対する処理がその遅延に巻き込まれることはありません。また、この例では1秒(1000ミリ秒)おきにスレッドを生成するようにしています。例えば1スレッドの処理が2秒程度かかるようなケースであっても、3つ目のスレッドが生成されたタイミングで最初のスレッドの処理は終了している可能性が高くなり、同時に実行されるスレッドの数がさほどは増えないようなアルゴリズムになっています。仮に 4 のようなケースが発生したとしても、そのスレッドだけはしばらくの間生き続けることになりますが、他のスレッドは生成しては処理されて消えてゆくという流れになるので、やはりシステムの限界を意識するような処理にはなりにくいアルゴリズムになっているのでした。


という所まで作っての「マルチスレッド処理が効率よい」という冒頭の結論に至ったわけでした。要するにこれなら深く考えずに作って動かしてもややこしい対応が必要になる心配が少ないかな、と。またマルチスレッド処理となると Java が得意とする処理ロジックであり、オッサン脳の出番が増えるかもしれないなあ、と感じたのでした。

普段は IBM Bluemix の関連で IBM Watson の API を紹介する機会があります(このブログでも何度か扱っています)。これらの多くは Watson Cognitive と呼ばれる言語や画像などを対象にした認識/分類を行うタイプのエンジンです。

一方で、Cognitive 以外の Watson もあります。その1つが Watson Analytics と呼ばれるもので、これは「データ分析」を目的としたエンジンです。現在では API というよりも、クラウド上のツールとしてその機能が提供されています:

http://www.ibm.com/software/jp/cmp/watsonanalytics/
2017020100


この Watson Analytics ツールは、誰でも申し込みから 30 日間は無料で使うことができます。というわけで自分も申し込んで、どんなものなのかを試しに使ってみました。

まず上記ページの「無料トライアルを試す」から申し込みを行います。しばらくすると以下のようなメールが送られてくるので、「ログインする」をクリックして、申込み時に指定したメールアドレスとパスワードで Watson Analytics サービスにログインしてください:
2017020201


Watson Analytics サービスにログインした直後はこのような画面になります:
2017020201



さて、今回このサービスを試用するにあたって、どんなデータで何を試すかを考えました。そこまでアナリティクスに詳しいわけでもなく、そこまで詳しい人を対象に紹介するつもりもないので、自分の守備範囲の中で試すことにします。 というわけで、以前にこのブログで紹介したことと同様のことを Watson Analytics サービスで挑戦してみることにします:

dashDB の R Studio を使ってデータの相関関係を調べる
http://dotnsf.blog.jp/archives/1047597688.html


ここで紹介しているのはセンサーとみなしたラズベリーパイから3種類(CPU 温度、CPU 負荷率、サインカーブを描く値)のデータを1秒おきに取り出したものです(取得済み)。そしてこの3つのデータの相関関係有無を調べてみることにします。同ブログエントリ内でも紹介されていますが、取得済みのデータは CSV で公開しているので、同じものをそのまま使うことにします。ここからダウンロードしておいてください:
https://raw.githubusercontent.com/dotnsf/RPDATA/master/RPDATA.csv


なお、この CSV ファイルをそのままエクセルで開くと、最初の方はこんな感じになります。1行目はヘッダでデータは2行目以降。B列が CPUTEMP(CPU温度)、C列がCPULOAD(CPU負荷率)、そしてD列がSINE(サインカーブを描く値)になっています。A列のIDはユニーク文字列ですが、今回は使いません:

2017020211


ちなみに CPUTEMP と CPULOAD を折れ線グラフにするとこんな感じ。途中でわざと負荷をかけたので、山が1つ出来ていることが見てとれます:

2017020212


一方、同じ期間中の CPULOAD と SINE とを比較したのがこちら。SINE は CPU の負荷とは無関係に、一定のリズムでサインカーブを描いていることがわかると思います:
2017020213


ここまで見た上で意識していただきたいのは以下のことです:
・ CPUTEMP が上がると CPULOAD も上がり、CPUTEMP が下がると CPULOAD も下がる
・ SINE にはそういった相関関係が CPUTEMP にも CPULOAD にもない
・ いまグラフの形を見たからそういうことに気付くことができた
・ また CPUTEMP は CPU 温度で CPULOAD は CPU 負荷率だった。つまりどちらも CPU に関連するセンサー値だった。そこまで理解した上でこの数値やグラフを見ているから、これらには関係があるように推測できた


つまり、人間の目でグラフの形を見比べた上で、なんとなく「形が似ている」と気づけたわけです。また「同じ CPU に関する値だったと分かっていたから、それらには関係があるだろう、と推測できた、ということです。

では同じことを目で判断せずに、加えて事前に同じ CPU の情報であることを知らない Watson が、数値データの羅列を与えただけで気付くことができるでしょうか? ということを今回の調査テーマとしてみることにします。


改めて、この CSV ファイルを Watson Analytics にロードします。新規データとして追加するので "+ New data" と書かれた箇所をクリックします:
2017020201


Watson Analytics には標準でサンプルデータが用意されているのでそれを使うことも出来る上、Box や OneDrive、Twitter などのソーシャルサービスから条件を指定してデータを取り出すことも可能になっています。今回は手元にある CSV ファイルを元にデータを生成したいので、"Local file" タブを選択します:
2017020202


このような画面になったら「Browse」をクリックして、先程ダウンロードした RPDATA.csv ファイルを指定します:
2017020203


こんな感じの画面になって RPDATA.csv ファイルがアップロード候補に加えられている状態になりました。このまま「Import」をクリックしてアップロードします:
2017020204


元の画面に戻りますが、いまアップロードした RPDATA.csv が分析データとして追加されていることが分かります。この画面では "Processing..." と表示されています。これはアップロードは完了しているのですが、分析するための処理を行っている途中であることを意味しています。このままもうしばらく待ちます:
2017020205


下図のような状態になると分析処理も完了したことになります。このアイコンをクリックして、実際の分析結果を確認してみます:
2017020206


(RPDATA に対して)どんな分析をするのか、という選択画面が表示されます。上述したように、今回は値の相関関係を調べたいので、"What drives SINE?"(何が SINE に影響しているのか?)を選びます。SINE との影響だけを調べたいわけではないのですが、この "SINE" の部分を切り替えて調べることができるので、今回はこの観点で各種データの影響要素を調べることにします:
2017020207


"What drives SINE?" をクリックすると、しばらく画面構成の処理で時間がかかるのですが、ロードが完了するとこのような画面になるはずです。結論として、"SINE" に関しては "No key drivers were found."(特に影響要素は存在しない)と表示されています。まあ答をわかっている立場からすると当然の結果なのですが、解析結果としても同じ結論になっていることが分かります:
2017020208


もともとの目的にもよるのですが、例えばこのセンサーモジュールの故障やエラー、利用不可になる予防保全をしたい場合などは CPU 利用率があまり高くない状態をキープしていてほしいものです。センサーが PC などと直結していれば、その PC からセンサーの CPU 状態を把握できることもありますが、そうでないケースも珍しくありません。そうなると「センサーの CPU 状態を直接モニタリングする」ことは簡単ではないことになります。これをどうにかできないでしょうか?

では今度はこの "SINE" を "CPULOAD(CPU 負荷率)" に変更して、CPULOAD に影響を与えている要素があるかどうかを調べてみましょう。画面左下の "SINE" と書かれた箇所をクリックし、"CPULOAD" を選択します:
2017020209


同様にして少し処理時間で待つことになりますが、画面のロードが完了するとこんな結果になりました。"CPULOAD" は "CPUTEMP" の影響を受けている、という解析結果が表示されています!
2017020210


もともとは CPU 負荷率の状態をなんとか調べることができないか、という課題があったわけですが、今回の結果によって、このセンサーモジュールでは CPU 温度と CPU 負荷率との間に相関関係があることが統計的にもわかったことになります。そして CPU の温度は直接外部から測定することもできるものなので、CPU の温度を測定していれば、CPU の負荷が高くなっているのかどうかを判断することができる、という可能性がでてきたことになるわけです!


くどいようですが、我々のように事前にグラフの形を見比べたわけではなく、またこれらがどちらも CPU に関する値である、という情報が事前に与えられたわけでもない中での自動解析結果であることを考慮すると、「ここまで簡単に分かっちゃうのか・・」という印象でした。

今回のデータは ID を除けば3種類しかなく、人間の目や脳でもわかりやすいものでした。ただこれが数10種類ものデータを含むもので、その中から存在するかどうかもわからない相関関係を見つけ出すというのはなかなか大変なんだろうなあ・・・と思うのですが、このツールはその作業負担を大幅に軽減できるものだと思います。


というわけで、このような IBM Watson Analytics サービスが無料で試せるというのはなかなかすごいのではないかと思っています。皆様も興味があれば是非試していただいて、手元にある数値データを使って簡単に解析できるという体験をしていただきたいです。



IBM Watson の画像認識 API である Visual Recognition を使った類似画像検索サービスを作り、そのソースコードを公開しました:
https://github.com/dotnsf/imageSearchDemo


コードは Node.js で作りました。プロジェクト自体に(著作権フリーな)サンプル画像もいくつか含まれていますが、サンプル画像を置き換えて使うことでご自身が所有している画像を使った類似画像検索サービスにすることも可能です。

また基本的に Watson API は使いやすいものばかりだと思ってますが、このサンプルアプリもその特徴を最大限に活かして、単純に「学習させたい画像を用意すれば動く」ようにしました。細かな実装内容はソースコードを参照ください。


上記 github 上のリポジトリの README.md の中に使い方も(英語で)記載していますが、このブログでは日本語での簡単な使い方とカスタマイズについて紹介します。前提としてログインできる CentOS/RHEL のインスタンスに git と Node.js がインストールされている環境をご用意ください。また最終的なウェブアプリケーションは IBM Bluemix 上で動かすことにします(この場合は cf コマンドもインストールしておいてください)。異なるプラットフォームでも動くと思いますが、適宜読み替えてください。


準備

何はともあれ、IBM Watson の API を利用するためには IBM Bluemix のアカウントが必要です。まだアカウントをお持ちでない場合はトップページの「フリーアカウントを作成」ボタンから 30 日間無料で使えるトライアルアカウントを作成できます:
2017012601


Bluemix のアカウントでログインし、カタログ内 "Watson" カテゴリの "Visual Recognition" を選択して、この API を追加してください:
2017012602


なお作成時に "Free" プランを選択すると1日に 250 API call まで無料で利用できるプランになります。本格的に利用する場合はその下の "Standard" プランを検討ください:
2017012603


Visual Recognition API を作成後に、ダッシュボードから作成した同サービスを選択して、サービス資格情報から資格情報を確認します(なければ追加します)。そして "api_key" の値(下図ではモザイクにしています)がこの後必要になるのでメモしておきます:
2017012604


改めて github からソースコードを用意します。いくつかの方法がありますが、git が使える場合は git clone してください(または zip をダウンロードして展開してください):
$ git clone https://github.com/dotnsf/imageSearchDemo
$ cd imageSearchDemo

ソースコードを展開したディレクトリの直下に settings.js というファイルがあります。この中の exports.vr_apikey の値を先程メモした Visual Recognition API の API Key の値に書き換えて保存してください:
2017012605


他はそのままでも動きます。なお、exports.limit の値(デフォルトだと 5)はウェブアプリケーションで類似画像を検索した結果として、上位いくつまでの結果を表示対象とするかの数値です。学習させる画像の数などにもよりますが、必要に応じて書き換えて使ってください。

最後に、この後の学習時に必要な Node.js のミドルウェア: watson-developer-cloud を npm でインストールしておきます:
$ npm install watson-developer-cloud

この結果、プロジェクトのホームディレクトリ(imageSearchDemo)に node_modules というフォルダが作られていれば watson-developer-cloud の導入に成功したことになり、学習処理が行えるようになります。


画像の学習

最終的には類似画像を検索する仕組みを作りますが、そのためにはあらかじめ検索結果となる画像を学習させておく必要があります(学習させた画像の中から類似画像を探します)。そして学習のためにはある程度の枚数の画像が必要です。

上記ソースコードの public/images/ の中にはサンプルとして著作権フリーな画像が含まれています。これらをそのまま使って学習させることもできますし、手元にある画像で類似画像検索システムを作りたい場合はそれらを使うこともできます(その場合は public/images/ 以下のサンプル画像を全て削除した上で、ご自身の画像をこのフォルダ内に格納してください):
2017012606


そして、以下のコマンドを実行すると public/images/ フォルダ以下にある画像を Watson に学習させます(上記の settings.js ファイルの編集を忘れずに行っておいてください):
$ node learnImages.js
imagelearn_xxxxxx

学習が正しく行われた結果、画面には imagelearn_xxxxxx という文字列が表示されます。これが collection_id と呼ばれるもので、後述の Watson API Explorer などでこの API を実行する際には必要になります。またこのコマンドの実行後に setting.js の最終行に以下のような1行が追加されているはずです:
exports.vr_collection_id = 'imagelearn_xxxxxx';


ウェブアプリケーションとして利用

では学習した内容を使った類似画像検索ウェブアプリケーションを作成します。今回は IBM Bluemix 上のランタイムとして作成するので、SDK for Node.js ランタイムを1インスタンス作成します:
2017012607


アプリケーション名は適当にユニークなものを指定します。この例では dotnsf-imagesearch という名前のランタイムを作っています:
2017012608



合わせてソースコードの manifest.yml ファイルを更新します。具体的には name と host 両方の値を、実際に作成するアプリケーション名と同じものにします:
2017012609


ここまで準備できたらアプリケーションをデプロイします。cf コマンドを使ってログインし、プッシュします:
$ cf login -a http://api.ng.bluemix.net/
   :
   :

$ cf push

しばらくするとアプリケーションの転送とステージングが完了して、ランタイムが起動した旨のメッセージが表示されます:
2017012601


この段階でアプリケーションにアクセス可能です。PCかスマホのブラウザでアプリケーションの URL (上記の例だと http://dotnsf-imagesearch.mybluemix.net/)を指定して開くと、このような画面が表示されます:
2017012601


「参照」ボタンをクリックして、類似画像の対象となる画像を選択します。今回は学習データの中に野球ボールがあったので、それが検索できるかどうかを調べる目的で、学習データとは異なる野球ボール画像を指定してみることにします:
野球ボール


画像を指定すると画面が暗転して、類似画像検索が行われます:
2017012602


しばらくすると結果が得られて画面の暗転が戻ります。画面を下にスクロールすると学習データの中の類似画像候補が指定数(デフォルトでは5)表示されます。一番左に野球ボールが検索できているのがわかります。まあこんな感じのウェブアプリケーションサンプルです:
2017012603


画像を学習する部分は learnImages.js で実装しています。学習時に与えるメタデータ(画像検索の結果と一緒に取得できるテキスト情報)の内容を変更する場合はこのファイルをカスタマイズしてください。またウェブアプリケーション部分は app.js で、そしてウェブアプリケーションの見栄え部分は public/ フォルダ内の index.html に依存しています。見栄えを含めたウェブアプリケーションの挙動の変更はこれらのファイルを自由にカスタマイズしてお使いください(ソースコードは MIT ライセンスで配布しています)。



なお、API Key や collection_id を利用して実際に Visual Recognition API を実行する場合は、こちらの Watson API Explorer をお使いいただくのが便利です。仮に作成した collection_id を一度削除するような場合はこの画面から DELETE を実行いただくことができます:
https://watson-api-explorer.mybluemix.net/apis/visual-recognition-v3

また、Watson Visual Recognition API の関数リファレンスはこちらを参照ください:
https://www.ibm.com/watson/developercloud/visual-recognition/api/v3/



このページのトップヘ