[MetaMind] RubyでMetaMindへアクセスしてみよう [Ruby]

今年はどんだけAC書けばいいんだろうかって感じですが、[AI Advent Calendar 2016](http://qiita.com/advent-calendar/2016/ai) 24日目、Xmasイブです! 個人的には、これが最後のAC投稿になります。予定通りできてよかった...。

なお、我が家は昨日イベントをすませてしまったので、今日も明日も何もありません。

# MetaMind ってなんだ

[Salesforce MetaMind](http://MetaMind.io/)はその名の通り、[Salesforce社](https://www.salesforce.com/jp/?ir=1)の持つ AI機能です。今年あたりに買収した会社ですね。参考 => [セールスフォース、人工知能関連の新興企業MetaMindを買収](http://japan.zdnet.com/article/35080670/)

Salesforce では、MetaMindにかぎらず、様々なAI機能を取り込んでいって、SaaSの機能として利用しやすくするだけにとどまらず、PaaSとしても利用可能なように提供していくようです。ところで、AIというと、色々かんちがいの起こるキーワードになるので、MetaMindの提供するサービスを正確に記述すると「Deep Learning(深層学習)」になります。現時点では、画像の深層学習を行うサービスをパイロットとして公開・提供しています。

画像の深層学習として、具体的に提供しているサービスは、大きく次の 2つです。

1. タグ付けられた画像の学習
2. ある画像が、どのタグづけされた画像に近いかを分析

ある特定の分野の画像ごとに、タグをつけながら学習をしていって、指定された画像がどのタグに分類されるかをニヤニヤすることがメインです。どのように応用できるかというと、例えば、ことみのインテリアの画像を提供すると、そのインテリアに近い不動産物件を紹介してくれるようなサービスなど、これまで言葉だけでは分析・検索の難しかった機能を、イメージを介することによって、より正当に近いものを紐付けていくようなことが実現できるようになります。

最近、そんなデモも作ってましたが(・´з`・)

# MetaMind を使ってみよう!

[MetaMind Documentation](http://docs.MetaMind.io/v1/docs) に書いてあるから、この通りやってみれば、良いのよと突っぱねることで、私のXmasイブに使える時間を増やすことができたのですが、利用するにあたって、色々と厄介で大変だったので、個人的なものも含めて書き記しておきます。

まず、MetaMind への Signup が必要になりますが、その前にSSL自己証明書が必要です。それも、Salesforce.com 組織のものが必要のため、一般的には、お持ちの Salesforceをご利用いただくか、Developerや Trial環境を準備していただく必要があります。

なお、Trial環境は取得後30日間無料でお使いいただけますし、ご契約いただけると、そのまま本番環境として利用できます。Developer環境は半永久的に無償のまま利用できますが、ライセンスの制約が厳しく、開発者だけの利用が基本です。ただ、パッケージを開発して提供することであれば、問題はほぼありません。素敵。いずれかの環境を準備していただくことが、容易です。

ヒント:組織が Salesforce.com であれば良い [jksつき自己証明書の作り方](https://blogs.oracle.com/blogbypuneeth/entry/steps_to_create_a_self1)

## signup しよう

次のような signup 画面になりますので、連絡のつくEmailアドレスと、作成した証明書ファイルを指定して、`Signup` します。

![screencapture-api-metamind-io-signup-1482560221959.png](https://qiita-image-store.s3.amazonaws.com/0/37263/7c0e0fb7-37f0-9691-56aa-4f075c7b0ea5.png "screencapture-api-metamind-io-signup-1482560221959.png")

登録したメールアドレス宛に、確認通知が来ますので、該当のURLをクリックすると、その後すぐに利用できるようになります。

## パスワードなし Private Key を作成しよう

Salesforce で .jksファイルを作成している場合、Passwordを登録していますので、パスワード無しの秘密鍵を作成する必要があります。一番短絡的に楽なのは、[MetaMind/api-utils](https://github.com/MetaMind/api-utils)から、`jwt.sh` を拾ってきて、秘密鍵を準備することが望ましいです。楽です。使い方は

`$ ./jwt.sh <*.jksファイル> <メールアドレス> <Expireまでの時間> https://api.metamind.io/`

4つめの引数は、MetaMind の APIサーバのURLになりますが、パイロットでなくなったあと、変わる可能性がありますので、チェックくださいませ。メールアドレスは、Signupした際に作成したメールアドレスです。

うまくいくと、これだけで MetaMindに正常に接続され、Access Tokenが返ってきます。うまくいかないときは、やばいときです。

結果、実行したディレクトリに `private_key` という秘密鍵ができていますので、大切に保管してください。

## Ruby で使ってみよう

Private Keyが出来上がれば後はかんたんなもので、ログイン時のお作法と、REST が使えれば、それだけで十分です。

今回は Rubyで認証→データセットの表示をやってみました。git にも公開しましたので、そちらからファイル取得されても結構です。また、二つばかり環境変数の設定が必要です。

```bash:env.sh
export CERT=<private_keyのPATH>
export SUBJECT=<Signup時のメールアドレス>
```

環境変数が設定できたら、次の通りじゅんぐりに実行すれば、結果が得られるでしょう。

`$ git clone https://github.com/sho7650/metamind.git`
`$ bundle install --path=vendor/bundle --binstubs=vendor/bin`
`$ bundle exec ruby ./metamind.rb`

プログラムは次のとおりです。流れとしては

1. 秘密鍵を読み込んd，PKey オブジェクトを作る
2. claim定義と秘密鍵を組み合わせて JWT署名を作成する (Json Web Sign = JWS)
3. JWS を使ってMetaMindから認証トークンを得る
4. 認証トークンを使って、MetaMind 機能を利用する


```ruby:metamind.rb
#!/usr/bin/env ruby
# coding: utf-8
require 'json/jwt'
require 'rest-client'

Audience = 'https://api.metamind.io/v1/oauth2/token'

# 秘密鍵の読み込み: 事前に "CERT" 環境変数へファイル名を指定すること
private_file = File.open(ENV['CERT']).read
private_key  = OpenSSL::PKey::RSA.new(private_file)

# metamind を利用するための定義とtokenの有効期限
# "SUBJECT" 環境変数には、sign in した時のメールアドレスを指定すること
claim = {
  iss: 'developer.force.com',
  sub: ENV['SUBJECT'],
  aud: Audience,
  exp: 1.week.from_now,
  nbf: Time.now
}

# 秘密鍵から Json Web Sign を作成する
jws = JSON::JWT.new(claim).sign(private_key, :RS256).to_s

# metamind へログイン
begin
  res = RestClient.post(Audience, {grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', assertion: jws})
rescue RestClient::ExceptionWithResponse => err
  err.response
end

# token 取得: token_type と access_token を組み合わせて認証済みTokenを構成
token         = JSON.parse(res.body)
expires_in    = token['expires_in']
authorization = token['token_type'] + " " + token['access_token']

# すべてのデータセットを取得
begin
  res = RestClient.get("https://api.metamind.io/v1/vision/datasets", {Authorization: authorization})
rescue RestClient::ExceptionWithResponse => err
  err.response
end

puts res
```

最後に JSONのままの結果を表示して終わっています。

ほかも同じように RESTコールすれば、情報の取得のみならず、学習や解析の結果を得ることもできます。そのへんはおいおい。

- [Predictive Vision Service API](http://docs.metamind.io/docs/predictive-vision-service-api)

ちょいちょい、不親切にも注釈が書いてあるのと、冗長的に書いたつもりなので、コードの説明はあまり必要ないとは思いますが、注意点を。

## 注意事項

RESTコールするときの注意点というか、次のように利用する形式となっています。

- header に認証トークンを設定
- 機能のオプションは payload に記述

ここで、payload へ、複数のオプションを指定する場合に、Multi-part を利用する場合があります。Ruby の `RestClient` では問題ないのですが、`node.js` などでよく使われている Rest clientでは、header と multi-part を同時に利用できないものがあったり、動作が不思議なものがあったりしたので、ご注意ください。私が探し回った中では、`unirest` くらいでしか実現できませんでした(´・ω・`)

## 今回利用したライブラリとか情報源

- [json web tokens](https://jwt.io/introduction/)
- [json-jwt(ruby)](https://github.com/nov/json-jwt)
- [rest-client(ruby)](https://github.com/rest-client/rest-client)
- [ruby で rsa暗号化した文字列を jsで復号する](https://doruby.jp/users/pe/entries/ruby-%E3%81%A7-rsa%E6%9A%97%E5%8F%B7%E5%8C%96%E3%81%97%E3%81%9F%E6%96%87%E5%AD%97%E5%88%97%E3%82%92-js%E3%81%A7%E5%BE%A9%E5%8F%B7%E3%81%99%E3%82%8B)


使ってませんが、次のようなライブラリなども参考にしました

- [ruby-jwt](https://github.com/jwt/ruby-jwt)

# 終わりに

ちょろっと書いてさらっと終わらせようって毎回思うのに、ネタ作って書いてると、結局4時間くらい平気で経過してるの、なんとかしたい