JavaからIBM BlueMixのPersonality Insightsを呼び出して日本語テキストから性格分析を行ってみる

「福岡市防災減災アプリコンテスト by Mashup Awards 2016」に参加したので、ネタとして投稿してみる。
イベントのまとめはコチラ。

- [togetter: 福岡市防災減災アプリコンテスト by Mashup Awards 2016](http://togetter.com/li/1040042)。

イベントでは[Yahoo myThings](https://mythings-developers.yahoo.co.jp/)でTwitterの特定キーワード/ハッシュタグを監視し、拾ったツイートをAWS/EC2(CentOS7)上に構築したREST API(SpringBoot)に送り、PostgreSQL(残念ながらMongoDBじゃねーです||-_-)に格納。
バックグラウンドタスクでPostgreSQL上に蓄積されたツイートを定期的に取得し、[IBM BlueMix](https://console.ng.bluemix.net/)のPersonality Insightsで分析可能な単語数が含まれているであろう文字数？に達したら、性格分析APIに送りつけてその時の性格分析結果を取得、蓄積する。といった使い方をしました。
文字や画像から性格や感情を分析できるなんて！！！すごい時代になったものです。

ここから投稿の本編です。
※続編は[コチラ](http://qiita.com/r-hirakawa/items/1a541a43423d87038be9)

# JavaからIBM BlueMixのPersonality Insightsを呼び出して日本語テキストから性格分析を行ってみる
- サンプルコードは[GitHub](https://github.com/r-hirakawa/bluemix-examples/tree/master/personality-insights)に投稿しています。

## [事前準備] 無料トライアルの登録

[コチラ](https://www.ibm.com/cloud-computing/jp/ja/bluemix/)から無料トライアルのアカウント登録を行い、Personality Insightsを使えるようにしておく。

[MEMO]

- 無料トライアルの登録はできたのですが、なぜかハッカソン中にPersonality Insightsのサービス作成ができなかった。APIサポートの方のアカウントで作成してもらったPersonality Insightsを間借りさせて頂きました。m(_ _)m ありがとうございました。
- BlueMixのダッシュボードでカタログを表示した時、Firefox/IEだと何も表示されなかったが、Chromeだと一覧がちゃんと表示された。

## 1. pom.xmlにIBMの提供するSDKを定義

~~~
<dependency>
  <groupId>com.ibm.watson.developer_cloud</groupId>
  <artifactId>personality-insights</artifactId>
  <version>3.5.0</version>
</dependency>
~~~

- artifactIdに「java-sdk」を指定すると、他のAPI向けのSDKも含めて一気に取得できるみたいです。

## 2. PersonalityInsightsのライブラリ初期化

~~~
private static final String API_VERSION = "2016-10-19";
private static final String USERNAME = "dummy"; // FIXME: correct username
private static final String PASSWORD = "dummy"; // FIXME: correct password

…中略…

PersonalityInsights client = new PersonalityInsights(API_VERSION);
client.setUsernameAndPassword(USERNAME, PASSWORD);
~~~

- Personality Insightsのサービスにアクセスするためのユーザ名、パスワードとAPIバージョンの指定が必要です。

(11/04追加)

プロキシ環境で通信できなかったのだが、プロキシ設定を行うインタフェースが無いため、Reflectionで無理やり設定した。
GitHub等にソースが公開されているならissueを発行したいが、このライブラリについては、どのように修正依頼を行ったらいいのか。。。

~~~
// JDK標準のHTTPプロトコルハンドラがサポートするシステムプロパティからプロキシ設定を取得
String proxyHost = System.getProperty("http.proxyHost", null);
String proxyPort = System.getProperty("http.proxyPort", null);

// WatsonServiceクラスのclientフィールド(OkHttpClient)を取得
Class<?> clazz = client.getClass().getSuperclass();
// get OkHttpClient instance
Field field = clazz.getDeclaredField("client");
field.setAccessible(true);
OkHttpClient client = (OkHttpClient) field.get(client);
// OkHttpClientクラスのproxyフィールドを上書き
field = client.getClass().getDeclaredField("proxy");
field.setAccessible(true);
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, Integer.parseInt(proxyPort)));
field.set(client, proxy);
~~~

## 3. APIのオプション設定

~~~
ProfileOptions options = new ProfileOptions.Builder()
    // 解析対象の言語(日本語)
    .language(Language.JAPANESE)
    // 解析結果の言語(日本語)
    .acceptLanguage(AcceptLanguage.JAPANESE)
    // 解析対象のテキスト
    .text(getText()).build();
~~~

- 今回は日本語テキストを解析するので、languageオプションを指定
- APIからの応答で日本語を使ってほしいのでacceptLanguageオプションを指定
- オプションの中で解析対象のテキストも指定するみたいです

## 4. APIの呼び出し / 例外処理

~~~
Profile result = null;
try {
    // APIを実行し解析結果を受け取る
    ServiceCall<Profile> service = client.getProfile(options);
    result = service.execute();
} catch (BadRequestException e) {
    // APIのエラーメッセージ
    System.err.println(e.getMessage());
}
~~~

- 解析結果はJSONからバインディングされたオブジェクトのインスタンスとして返されます。APIが返すJSON文字列は「[src/main/resources/response.txt](https://github.com/r-hirakawa/bluemix-examples/blob/master/personality-insights/src/main/resources/response.txt)」に保存しています。

- 解析対象の日本語テキストに含まれる単語数が少なすぎると「BadRequestException」が返されます。その時のエラーメッセージは「[src/main/resources/error.txt](https://github.com/r-hirakawa/bluemix-examples/blob/master/personality-insights/src/main/resources/error.txt)」に保存しています。

~~~
重大: POST https://gateway.watsonplatform.net/personality-insights/api/v3/profile?version=2016-10-19, status: 400, error: 単語数 47 が、分析に必要な最小単語数よりも少なくなっています: 100
単語数 47 が、分析に必要な最小単語数よりも少なくなっています: 100
~~~

- 上記で指摘される100単語は、あくまでも最低限の指標であるため、高い精度の分析を行うには、もっと大量の単語数が必要みたいです。

## 5. 応答データオブジェクト

~~~
// APIの応答データ全体
System.out.println(result);

// 個性(personality)
List<Trait> personality = result.getPersonality();
System.out.println(personality);

// 欲求(needs)
List<Trait> needs = result.getNeeds();
System.out.println(needs);

// 価値(values)
List<Trait> values = result.getValues();
System.out.println(values);
~~~

- 応答データオブジェクトはtoString()するとAPIから返却されたJSON文字列に戻るようです。
- List\<Trait\>という型でJSONの配下要素を取り出すことができる。

## 補足

* テストで分析にかける長い日本語テキストどうしようか？と思って調べたら「[すぐに使えるダミーテキスト](http://lipsum.sugutsukaeru.jp/)」というのがあった。