IBM が提唱する新しい人工知能である「コグニティブコンピューティング」、その柱となる Watson アナリティクスの API を使った PHP プログラムを作ってみます。API そのものは REST のウェブ API なので、PHP 以外の別言語であっても簡単に移植できると思います(PHP だと記述がシンプル&見やすい、という理由で選んでいます)。


まず最初にこのサービスの内容を簡単に紹介します。まずはこちらのデモサイトにアクセスしてみてください:
https://watson-pi-demo.mybluemix.net/

2015060801


画面左のテキストエリアに長文が入力されています。これはサンプルで、ある一人の人が誰かに宛てて書いたメールの本文だと思ってください。そして、この Personality Insights API はそのメール本文で使われている単語や文法を元に、書いた人の性格を分析して、その結果を返してくれる、というものです。実際に分析を実行するには "Analyze" ボタンをクリックします。

すると画面右に分析結果が一覧表示されます:
2015060802


分析結果はカテゴライズされていて、例えば "Openness(社交性)" が 94%、その Openness を更に細分化すると、Adventurousness(冒険性) が 72%、Artistic interests(芸術への興味) が16%、・・・ といった感じになっています。

また、これは人工知能とは異なりますが、オプションとしてこの結果を視覚化する機能も提供されています(deprecated)。画面を下にスクロールするとグラフ化された結果が確認できます:
2015060803


Watson Personal Insights API は、このように「入力したテキスト」を元に「性格分析結果の JSON」を返す API です。最新の API 詳細についてはリファレンスを参照ください:
http://www.ibm.com/smarterplanet/us/en/ibmwatson/developercloud/apis/#!/personality-insights

このリファレンスにも記述されていますが、性格分析を行う機能は /v2/profile という API で提供されています。このエンドポイントに対して、入力テキストをそのまま POST すればよい、という簡単な API のようです。


というわけで、実際にこのサービスを使って性格分析を行う、という内容のシンプルなプログラムを PHP で記述してみます。 

なお、この API は現在対応言語が英語のみとなっているため、ポストする入力テキストは英語で記述されている必要があり、かつある程度の精度を確保する目的で 100 単語以上をポストする必要があります。場合によっては事前に翻訳してから実行する、という処理が必要になることをご了承ください。


IBM Bluemix にログインし、(PHP の)ランタイムを1つ作成してください。ここまでの手順がよく分からない場合はこちらのエントリを参照してください:
http://dotnsf.blog.jp/archives/1000961115.html

この例では dotnsf-watsonpi という名前の PHP ランタイムを1つ作成しています。ここで「サービスまたは API の追加」をクリックします:
2015060901


サービスの一覧が表示されます。非常に多くのサービスが並んでいますが、Watson カテゴリ内の "Personality Insights" サービスをクリックして選びます:
2015060902


サービス内容を確認します。この Personality Insights API は有料サービスであり、API を月に100回実行するまでは無料です。それ以降は1回の実行につき63円かかります(Bluemix の30日間無料トライアル期間中であれば101回目以降も無料です)。この価格に関しては今後変更される可能性があるので、最新の情報についてはその都度確認してください。最後に先程作成したランタイムの名前(この例では dotnsf-pi)が選択されていることを確認して「作成」ボタンをクリックします。更に再ステージングについて問われたら「再ステージ」をクリック:
2015060903


これで PHP のランタイムに Watson Personality Insights サービスがバインドされました。この時点で API を利用するための資格情報を確認できます。資格情報を確認するには Personality Insights サービスアイコンの「資格情報の表示」と書かれた部分をクリックします:
2015060904


画面が縦に広がって、このような情報が出力されます:
2015060905


出力された中身はこのような内容の JSON テキストになっています。ここに API を実行するために不可欠な情報(特に赤字部分)が書かれています。なお username と password はこの API を実行する際に指定するユーザー名とパスワードで、実際にはランダムに生成された文字列になっています:
{
  "personality_insights": [
    {
      "name": "Personality Insights-74",
      "label": "personality_insights",
      "plan": "IBM Watson Personality Insights Monthly Plan",
      "credentials": {
        "url": "https://gateway.watsonplatform.net/personality-insights/api",
        "username": "(username)",
        "password": "(password)"
      }
    }
  ]
}

この url の情報と、上記の API リファレンスを組み合わせると API が実行できます。例えば上記デモサイトで行ったような、あるテキストメッセージを書いた人の性格を API で問い合わせる場合を調べてみます。

リファレンスによると "/v2/profile" という関数が目的の検索機能であり、ここに POST メソッドの本文としてメール本文の内容を付与して実行すればいい、ということが書かれています:
2015060906


実際に API を実行するプログラムを記述する際の API エンドポイントは上記 JSON に書かれていた url 値である "https://gateway.watsonplatform.net/personality-insights/api" に、リファレンスの内容を足した "https://gateway.watsonplatform.net/personality-insights/api/v2/profile" になります。この URL に JSON 内の username および password の情報を使って Basic アクセスをして、性格を解析したいテキスト本文を POST する、という処理内容を記述することになります。

では書かれている通りの内容で PHP プログラム(wpi.php とします)を作ってみると、こんな感じになりますかね。エラーハンドリングなどは省略して、なるべくシンプルにしています。TIPS として触れておくと HTTP リクエストヘッダに "Content-Type: text/plain" の指定が必須です:
<?php
// エンドポイント
$url = 'https://gateway.watsonplatform.net/personality-insights/api/v2/profile';

// 認証
$username = '(username)'; // JSON に書かれていた内容を指定
$password = '(password)'; // JSON に書かれていた内容を指定

// 本文(198単語)
$data = "Call me Ishmael. Some years ago-never mind how long precisely-having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world. It is a way I have of driving off the spleen and regulating the circulation. Whenever I find myself growing grim about the mouth; whenever it is a damp, drizzly November in my soul; whenever I find myself involuntarily pausing before coffin warehouses, and bringing up the rear of every funeral I meet; and especially whenever my hypos get such an upper hand of me, that it requires a strong moral principle to prevent me from deliberately stepping into the street, and methodically knocking people's hats off-then, I account it high time to get to sea as soon as I can. This is my substitute for pistol and ball. With a philosophical flourish Cato throws himself upon his sword; I quietly take to the ship. There is nothing surprising in this. If they but knew it, almost all men in their degree, some time or other, cherish very nearly the same feelings towards the ocean with me.";

// POST で API 実行
$headers = array(
    'Authorization: Basic '.base64_encode($username.':'.$password),
    'Content-Type: text/plain' // 必須指定
);
$options = array('http' => array(
    'method' => 'POST',
    'content' => $data,
    'header' => implode("\r\n", $headers)
));
$result = file_get_contents($url, false, stream_context_create($options));

// 結果を出力
echo $result;
?>

このプログラム(wpi.php)を実行すると、こんな感じの結果が出力されます(見やすいように改行と赤字コメントを加えています。ただ性格分析は僕の専門ではないので、おかしな翻訳やコメントになっていたらごめんなさい):
$php -f wpi.php

{
 "id":"*UNKNOWN*",
 "source":"*UNKNOWN*",
 "word_count":198, 単語数
 "word_count_message":"There were 198 significant words in the text, we recommend text with at least 100 (and preferably 2,000) words",
 "tree":{ 分析結果のツリーはここから
  "id":"r",
  "name":"root",
  "children":[
   {
    "id":"personality",
    "name":"Big 5 ", ビッグ5と呼ばれる性格分析結果
    "children":[
     {
      "id":"Neuroticism_parent",
      "name":"Emotional range", 感受性
      "category":"personality",
      "percentage":0.8918895452867617, 89%
      "children":[ 感受性の子要素
       {
        "id":"Openness",
        "name":"Openness", 社交性
        "category":"personality",
        "percentage":0.715912798590218, 72%
        "sampling_error":0.28262000000000004, そのサンプリングエラー率 28%
        "children":[ 社交性の子要素
         {
          "id":"Adventurousness",
          "name":"Adventurousness", 冒険心
          "category":"personality",
          "percentage":0.24200589364589645, 24%
          "sampling_error":0.2349836 そのサンプリングエラー率 23%
         },
         {
          "id":"Artistic interests",
          "name":"Artistic interests", 芸術への興味
          "category":"personality",
          "percentage":0.1779719351186918, 18%
          "sampling_error":0.47357392 そのサンプリングエラー率 47%
         },
         {
          "id":"Emotionality",
          "name":"Emotionality",
          "category":"personality","percentage":0.8348632312299376,
          "sampling_error":0.26339703999999997
         },
         {
          "id":"Imagination",
          "name":"Imagination",
          "category":"personality",
          "percentage":0.40246782944324716,
          "sampling_error":0.31812512000000004
         },
         {
          "id":"Intellect",
          "name":"Intellect",
          "category":"personality",
          "percentage":0.5337358423377544,
          "sampling_error":0.28229272
         },
         {
          "id":"Liberalism",
          "name":"Authority-challenging",
          "category":"personality",
          "percentage":0.9036798826551202,
          "sampling_error":0.36567567999999995
         }
        ]
       },       {
        "id":"Conscientiousness",
        "name":"Conscientiousness",
        "category":"personality",
        "percentage":0.5445535670078625,
        "sampling_error":0.35129408,
        "children":[
         {
          "id":"Achievement striving",
          "name":"Achievement striving",
          "category":"personality",
          "percentage":0.2700341317349067,
          "sampling_error":0.32485944
         },
         {
          "id":"Cautiousness",
          "name":"Cautiousness",
          "category":"personality",
          "percentage":0.742529850394309,
          "sampling_error":0.36339328000000004
         },
         {
          "id":"Dutifulness",
          "name":"Dutifulness",
          "category":"personality",
          "percentage":0.3009058388302808,
          "sampling_error":0.44760968
         },
         {
          "id":"Orderliness",
          "name":"Orderliness",
          "category":"personality",
          "percentage":0.9511955761089055,
          "sampling_error":0.294814
         },
         {
          "id":"Self-discipline",
          "name":"Self-discipline",
          "category":"personality",
          "percentage":0.2560312591429979,
          "sampling_error":0.38256207999999997
         },
         {
          "id":"Self-efficacy",
          "name":"Self-efficacy",
          "category":"personality",
          "percentage":0.6787093695767238,
          "sampling_error":0.41188784
         }
        ]
       },
       {
        "id":"Extraversion",
        "name":"Extraversion",
        "category":"personality",
        "percentage":0.14544123271874,
        "sampling_error":0.38825928,
        "children":[
         {
          "id":"Activity level",
          "name":"Activity level",
          "category":"personality",
          "percentage":0.032364142051857475,
          "sampling_error":0.48726208000000004
         },
         {
          "id":"Assertiveness",
          "name":"Assertiveness",
          "category":"personality",
          "percentage":0.1717311752830918,
          "sampling_error":0.4465296
         },
         {
          "id":"Cheerfulness",
          "name":"Cheerfulness",
          "category":"personality",
          "percentage":0.1353039029992872,
          "sampling_error":0.34023712
         },
         {
          "id":"Excitement-seeking",
          "name":"Excitement-seeking",
          "category":"personality",
          "percentage":0.031036118864266764,
          "sampling_error":0.36140536
         },
         {
          "id":"Friendliness",
          "name":"Outgoing",
          "category":"personality",
          "percentage":0.04360600032167501,
          "sampling_error":0.41489215999999995
         },
         {
          "id":"Gregariousness",
          "name":"Gregariousness",
          "category":"personality",
          "percentage":0.00993996956776937,
          "sampling_error":0.42565192
         }
        ]
       },
       {
        "id":"Agreeableness",
        "name":"Agreeableness",
        "category":"personality",
        "percentage":0.41731474192057555,
        "sampling_error":0.37315656,
        "children":[
         {
          "id":"Altruism",
          "name":"Altruism",
          "category":"personality",
          "percentage":0.1893141565019656,
          "sampling_error":0.4574676
         },
         {
          "id":"Cooperation",
          "name":"Cooperation",
          "category":"personality",
          "percentage":0.7569715715364396,
          "sampling_error":0.42429936
         },
         {
          "id":"Modesty",
          "name":"Modesty",
          "category":"personality",
          "percentage":0.05410449484777291,
          "sampling_error":0.43126808000000005
         },
         {
          "id":"Morality",
          "name":"Uncompromising",
          "category":"personality",
          "percentage":0.6031683897259557,
          "sampling_error":0.40826744000000004
         },
         {
          "id":"Sympathy",
          "name":"Sympathy",
          "category":"personality",
          "percentage":1.0,
          "sampling_error":0.47322736
         },
         {
          "id":"Trust",
          "name":"Trust",
          "category":"personality",
          "percentage":0.18556199834989792,
          "sampling_error":0.4375552
         }
        ]
       },
       {
        "id":"Neuroticism",
        "name":"Emotional range",
        "category":"personality",
        "percentage":0.8918895452867617,
        "sampling_error":0.28468672,
        "children":[
         {
          "id":"Anger",
          "name":"Fiery",
          "category":"personality",
          "percentage":0.8060263824156899,
          "sampling_error":0.25980440000000005
         },
         {
          "id":"Anxiety",
          "name":"Prone to worry",
          "category":"personality",
          "percentage":0.7723866874361663,
          "sampling_error":0.29556752
         },
         {
          "id":"Depression",
          "name":"Melancholy",
          "category":"personality",
          "percentage":0.5106478670677633,
          "sampling_error":0.3757664
         },
         {
          "id":"Immoderation",
          "name":"Immoderation",
          "category":"personality",
          "percentage":0.6744415372949466,
          "sampling_error":0.26551199999999997
         },
         {
          "id":"Self-consciousness",
          "name":"Self-consciousness",
          "category":"personality",
          "percentage":0.7924144626881476,
          "sampling_error":0.4019884
         },
         {
          "id":"Vulnerability",
          "name":"Susceptible to stress",
          "category":"personality",
          "percentage":0.9138950516717607,
          "sampling_error":0.29872272
         }
        ]
       }
      ]
     }
    ]
   },
   {
    "id":"needs",
    "name":"Needs",
    "children":[
     {
      "id":"Closeness_parent",
      "name":"Closeness",
      "category":"needs",
      "percentage":1.0,
      "children":[
       {
        "id":"Challenge",
        "name":"Challenge",
        "category":"needs",
        "percentage":0.9821912864702969,
        "sampling_error":0.81476168
       },
       {
        "id":"Closeness",
        "name":"Closeness",
        "category":"needs",
        "percentage":1.0,
        "sampling_error":0.8723808799999999
       },
       {
        "id":"Curiosity",
        "name":"Curiosity",
        "category":"needs",
        "percentage":1.0,
        "sampling_error":0.78142888
       },
       {
        "id":"Excitement",
        "name":"Excitement",
        "category":"needs",
        "percentage":1.0,
        "sampling_error":0.7837571999999999
       },
       {
        "id":"Harmony",
        "name":"Harmony",
        "category":"needs",
        "percentage":1.0,
        "sampling_error":0.82233096
       },
       {
        "id":"Ideal",
        "name":"Ideal",
        "category":"needs",
        "percentage":0.04112302144450648,
        "sampling_error":0.77215992
       },
       {
        "id":"Liberty",
        "name":"Liberty",
        "category":"needs",
        "percentage":0.0033214357670770067,
        "sampling_error":0.81046376
       },
       {
        "id":"Love",
        "name":"Love",
        "category":"needs",
        "percentage":1.0,
        "sampling_error":0.85773144
       },
       {
        "id":"Practicality",
        "name":"Practicality",
        "category":"needs",
        "percentage":1.0,
        "sampling_error":0.83880064
       },
       {
        "id":"Self-expression",
        "name":"Self-expression",
        "category":"needs",
        "percentage":0.0068972518833839295,
        "sampling_error":0.79480384
       },
       {
        "id":"Stability",
        "name":"Stability",
        "category":"needs",
        "percentage":0.07939535553110567,
        "sampling_error":0.80823464
       },
       {
        "id":"Structure",
        "name":"Structure",
        "category":"needs",
        "percentage":0.02549425311812723,
        "sampling_error":0.06421888
       }
      ]
     }
    ]
   },
   {
    "id":"values",
    "name":"Values",
    "children":[
     {
      "id":"Conservation_parent",
      "name":"Conservation",
      "category":"values",
      "percentage":0.00995039863428774,
      "children":[
       {
        "id":"Conservation",
        "name":"Conservation",
        "category":"values",
        "percentage":0.00995039863428774,
        "sampling_error":0.4966044
       },
       {
        "id":"Openness to change",
        "name":"Openness to change",
        "category":"values",
        "percentage":0.9711138591560894,
        "sampling_error":0.5254172
       },
       {
        "id":"Hedonism",
        "name":"Hedonism",
        "category":"values",
        "percentage":0.9245332640747028,
        "sampling_error":0.51724872
       },
       {
        "id":"Self-enhancement",
        "name":"Self-enhancement",
        "category":"values",
        "percentage":0.3464581176518462,
        "sampling_error":0.50278936
       },
       {
        "id":"Self-transcendence",
        "name":"Self-transcendence",
        "category":"values",
        "percentage":0.8998366368666929,
        "sampling_error":0.47211168
       }
      ]
     }
    ]
   }
  ]
 }
}

JSON が複雑な親子関係になってはいますが、基本的にはデモサイトの画面右側に出力されていたものと同じ構成の結果が返ってきている、ということが分かると思います。更によく見ると、各項目ごとの結果には "percentage" という数値結果に加えて、"sampling_error" という数字があることに気付きます。これはデモサイトの結果には表示されていない情報です。

この sampling_error(サンプリングエラー率)とは、いわゆる「誤差」のことです。つまり本来の値と、今回の入力に使ったテキストだけから得られた結果との間にどれだけの誤差が生じている可能性があるか、という数値です。要はこの数値が小さい項目ほど結果が正しい可能性が高くなる、という数値です。デモサイトだけを使っているとこのサンプリングエラーを意識することはありませんが、実はこういった誤差率までが取得できる API が用意されている、ということになります。


で、後は得られたこの結果をどうやって使うのか、そもそも入力テキストをどこからどうやって取得するのか、、、 それは開発者のアイデア次第です! ということで(笑)