User Localの氏名自動識別APIを、デレマスのアイドル190人の名前で精度検証した(+ LINEボット)


先日、User Localが[人工知能ボットAPI](http://ai.userlocal.jp/)を公開しました。ボット開発に必要な自然言語処理ライブラリを集約したもので、現時点で以下のような機能が提供されています。

- 全自動会話API
- 氏名自動識別API
- キャラクター会話API
- 形態素解析API

[前回](http://qiita.com/EastResident/items/0abe9e35b3811c6cbb3a)は全自動会話APIを試したので、今回は氏名自動識別APIを使ってみます。

さらにベンチマークとして、[アイドルマスターシンデレラガールズ](http://imas-cinderella.com/)に登場するアイドル 196人の名前を識別させ、精度を検証してみます。ちなみに、アイドルの名前は[Pixiv百科事典](http://dic.pixiv.net/)からスクレイピングしてきました。

加えて、このAPIを自社サービスに組み込むことを想定し、フルネームを入力したら 性、 名、 性別を返すLINEボットを実装します。

# 機能確認

## 準備

利用するためには[ここ](http://ai.userlocal.jp/)から会員登録が必要で、現在、第2期募集を行っています。自分は第一期に申し込んで、6月15日にAPIキーが届きました。今から申し込んだ場合は、利用できるまでに若干時間がかかるかもしれません。ただし、会員登録しなくても試用する方法は用意されています。


また、今回はBot実装にあたって、Ruby、sinatra、heroku、LINE BOT API等を利用しています。
[参考1](http://www.sakana.tech/entry/2016/04/12/112700) [参考2](http://qiita.com/EastResident/items/d1b572f00c29f907b743)

## 動作確認

ベンチマークを採る前にとりあえず通信してみます。氏名自動識別APIを利用するには、パラメータを付与して<https://chatbot-api.userlocal.jp/api/name>に向けてGETすればいいようです(POSTもできます)。

```
https://chatbot-api.userlocal.jp/api/name?key=your_api_key&name=田中太郎
```

"key"はUserLocalから提供されるAPIキーです。"name"に識別させたい名前を入力します。ちなみに、keyに "sample"と入れると、未登録でも利用できます(10回/1hまで)。

それでは試しに通信して、結果を受け取ってみましょう。 "天海春香" を識別させてみました。最初はPOSTで通信しようと思ったのですが、RubyのRestClientではなぜかjsonが渡らなかったので、getでリクエストを送っています。

```rb
require 'json'
require 'rest-client'
require "cgi"

request_content = {'key' => 'your_api_key', 'name' => CGI.escape('天海春香')}
request_params = request_content.reduce([]) do |params, (key, value)|
  params << "#{key}=#{value}"
end
rest = RestClient.get('https://chatbot-api.userlocal.jp/api/name?' + request_params.join('&').to_s)
result = JSON.parse(rest)
p result
```

上記のコードを実行すると、以下のような結果が出力されました(実際はインデントされていません)。

```json
{
  "status"=>"success",
  "result"=>{
    "last_name"=>"天海", "last_name_yomi"=>nil,
    "first_name"=>"春香", "first_name_yomi"=>"はるか",
    "gender"=>2, "gender_accuracy"=>5,
    "nickname"=>["はるぴー", "はるたん", "はるちゃん", "はるぽん", "きち", "はるたろー", "はるのすけ", "はるべえ", "はるっち", "春太郎", "はる"]
  }
}
```

結果のjsonを見ていきます。"last_name"と"first_name"で性と名がそれぞれ識別されます。さらに、ひらがなでの読みも予測してくれます。今回の例では性と名の分割は成功していますが、性の読み仮名がnilとなっており、うまく識別できていないようです。

次に "gender" と "gender_accuracy" です。"gender" は "1" が男性で "2" が女性を表しています。"gender_accuracy" は男女識別の精度を表します(0-5の範囲)。

"nickname" は見ての通り、ニックネームの候補をいくつか提示してくれます。何通りか試して見ましたが、単純なパターンしか用意されていない印象です。

# デレマスキャラによる検証

それでは、実際にデレマスキャラの名前を使って検証していきます。今回は、

- first_nameとlast_nameの識別が正しいか
- 性別の識別が正しいか

の二点を評価します。
## 入力値

デレマスに登場するアイドル196人の名前を列挙したCSVファイルを用意します。自分は、Pixiv大百科からスクレイピングしてきました。

```csv:idol_name_list_full.csv
相原雪乃,赤西瑛梨華,浅野風香,安部菜々,天海春香,有浦柑奈,安斎都,五十嵐響子,池袋晶葉,一ノ瀬志希,今井加奈,井村雪菜,江上椿,太田優,大西由里子,大沼くるみ,大原みちる,緒方智絵里,奥山沙織,乙倉悠貴,我那覇響,菊地真,日下部若葉,工藤忍,クラリス,栗原ネネ,古賀小春,輿水幸子,小早川紗枝,小日向美穂,西園寺琴歌,榊原里美,佐久間まゆ,櫻井桃華,椎名法子,島村卯月,白菊ほたる,涼宮星花,関裕美,高槻やよい,月宮雅,道明寺歌鈴,長富蓮実,中野有香,丹羽仁美,早坂美玲,原田美世,兵藤レナ,福山舞,藤本里奈,双葉杏,前川みく,松原早耶,間中美里,水本ゆかり,三村かな子,宮本フレデリカ,棟方愛海,村松さくら,持田亜里沙,桃井あずき,楊菲菲,柳清良,柳瀬美由紀,遊佐こずえ,横山千佳,相川千夏,秋月律子,浅利七海,アナスタシア,綾瀬穂乃香,荒木比奈,伊集院惠,氏家むつみ,梅木音葉,大石泉,岡崎泰葉,上条春菜,神谷奈緒,川島瑞樹,神崎蘭子,如月千早,岸部彩華,木場真奈美,桐野アヤ,桐生つかさ,黒川千秋,ケイト,小室千奈美,鷺沢文香,佐々木千枝,佐城雪美,塩見周子,四条貴音,篠原礼,渋谷凛,白坂小梅,瀬名詩織,高垣楓,高橋礼子,鷹富士茄子,高峯のあ,多田李衣菜,橘ありす,東郷あい,成宮由愛,西川保奈美,新田美波,二宮飛鳥,服部瞳子,速水奏,柊志乃,藤居朋,藤原肇,古澤頼子,ヘレン,北条加蓮,松尾千鶴,松永涼,松本沙理奈,三浦あずさ,水木聖來,水野翠,三船美優,望月聖,森久保乃々,八神マキノ,大和亜季,結城晴,吉岡沙紀,ライラ,脇山珠美,和久井留美,愛野渚,相葉夕美,赤城みりあ,イヴ・サンタクロース,市原仁奈,上田鈴帆,衛藤美紗希,海老原菜帆,及川雫,大槻唯,片桐早苗,北川真尋,喜多日菜子,喜多見柚,木村夏樹,キャシー・グラハム,小関麗奈,小松伊吹,財前時子,斉藤洋子,冴島清美,佐藤心,沢田麻理菜,首藤葵,城ヶ崎美嘉,城ヶ崎莉嘉,杉坂海,仙崎恵磨,相馬夏美,高森藍子,土屋亜子,十時愛梨,ナターリア,並木芽衣子,南条光,難波笑美,西島櫂,野々村そら,萩原雪歩,浜川愛結奈,浜口あやめ,日野茜,姫川友紀,双海亜美,双海真美,星井美希,星輝子,堀裕子,本田未央,槙原志保,松山久美子,的場梨沙,真鍋いつき,三好紗南,水瀬伊織,向井拓海,村上巴,メアリー・コクラン,諸星きらり,矢口美羽,依田芳乃,龍崎薫,若林智香
```

## 出力値

"性 名 性別ID" の形式でCSVを出力させます。上記動作確認の例だと、

```csv
"天海 春香 2",
```

となります。

## 実装

以下のファイルを作成し、実行しました。

```rb:name_parser.rb
require 'json'
require 'rest-client'
require "cgi"
require 'csv'

name_list = []

filename = "idol_name_list_full.csv"

CSV.foreach(filename) do |line|
  name_list.concat line
end

name_list_detail = name_list.map do |name|
  request_content = {'key' => 'your_api_key', 'name' => CGI.escape(name)}
  request_params = request_content.reduce([]) do |params, (key, value)|
    params << "#{key}=#{value}"
  end
  rest = RestClient.get('https://chatbot-api.userlocal.jp/api/name?' + request_params.join('&').to_s)
  result = JSON.parse(rest)['result']
  "#{result['last_name']} #{result['first_name']} #{result['gender']}"
end

open("idol_name_list_parsed.csv", "w"){|f| f.write name_list_detail.join(',')}
```

特に難しいことはしていないので、コードの説明は割愛します。

## 結果

以下が出力されたCSVです。

```csv:idol_name_list_parsed
相原 雪乃 0,赤西瑛梨 華 2,浅野 風香 0,安部 菜々 2,天海 春香 2,有浦柑 奈 0,安斎 都 2,五十嵐 響子 2,池 袋晶葉 0,一ノ瀬 志希 0,今井 加奈 2,井村 雪菜 0,江上 椿 0,太田 優 0,大西 由里子 2,大沼 くるみ 0,大原 みちる 2,緒方智 絵里 2,奥山 沙織 2,乙倉悠 貴 1,我那覇 響 0,菊地 真 1,日下部 若葉 0,工藤 忍 0,クラ リス 0,栗原 ネネ 0,古賀 小春 0,輿水 幸子 2,小早川 紗枝 0,小日向 美穂 2,西 園寺琴歌 0,榊原 里美 2,佐久間 まゆ 2,櫻井 桃華 0,椎名 法子 2,島村 卯月 0,白 菊ほたる 0,涼宮星 花 2,関 裕美 2,高槻 やよい 2,月宮 雅 0,道明寺歌 鈴 0,長富蓮 実 1,中野 有香 2,丹羽 仁美 2,早坂 美玲 0,原田 美世 0,兵藤 レナ 0,福山 舞 2,藤本 里奈 2,双葉 杏 2,前川 みく 0,松原 早耶 0,間中 美里 2,水本 ゆかり 2,三村 かな子 0,宮本 フレデリカ 0,棟方 愛海 0,村松 さくら 2,持田 亜里沙 2,桃井 あずき 0,楊 菲菲 0,柳清 良 1,柳瀬 美由紀 2,遊佐 こずえ 2,横山 千佳 2,相川 千夏 2,秋月 律子 2,浅利 七海 0,アナス タシア 0,綾瀬穂乃 香 2,荒木 比奈 0,伊集院 惠 2,氏家 むつみ 2,梅木 音葉 0,大石 泉 0,岡崎 泰葉 0,上条 春菜 2,神谷 奈緒 2,川島 瑞樹 1,神崎 蘭子 0,如月 千早 0,岸部 彩華 0,木場 真奈美 2,桐野 アヤ 0,桐生 つかさ 2,黒川 千秋 2,ケイ ト 0,小室 千奈美 0,鷺沢文 香 2,佐々木 千枝 2,佐城 雪美 0,塩見 周子 2,四条貴 音 0,篠原 礼 0,渋谷 凛 0,白坂 小梅 0,瀬名 詩織 2,高垣 楓 2,高橋 礼子 2,鷹 富士茄子 0,高 峯のあ 0,多田 李衣菜 0,橘 ありす 0,東郷 あい 2,成宮由 愛 2,西川 保奈美 0,新田 美波 2,二宮 飛鳥 0,服部 瞳子 0,速水 奏 0,柊 志乃 2,藤居 朋 0,藤原 肇 1,古澤 頼子 2,ヘレ ン 0,北 条加蓮 0,松尾 千鶴 2,松永 涼 1,松本 沙理奈 0,三浦 あずさ 2,水木 聖來 0,水野 翠 2,三船美 優 0,望月 聖 1,森 久保乃々 0,八神 マキノ 0,大和 亜季 2,結城 晴 0,吉岡 沙紀 2,ラ イラ 0,脇山 珠美 2,和久井 留美 2,愛野 渚 2,相葉 夕美 0,赤城 みりあ 0,イヴ サンタクロース 0,市原 仁奈 0,上田 鈴帆 0,衛藤 美紗希 0,海老原 菜帆 0,及川 雫 0,大槻 唯 2,片桐 早苗 2,北川 真尋 0,喜多 日菜子 0,喜多 見柚 0,木村 夏樹 0,キャシー グラハム 0,小関 麗奈 0,小松 伊吹 0,財前 時子 0,斉藤 洋子 2,冴島 清美 2,佐藤 心 0,沢田 麻理菜 0,首藤 葵 2,城ヶ崎 美嘉 0,城 ヶ崎莉嘉 0,杉坂 海 1,仙崎恵 磨 0,相馬 夏美 2,高森 藍子 2,土屋 亜子 2,十時 愛梨 2,ナター リア 0,並木 芽衣子 0,南条 光 1,難波 笑美 0,西 島櫂 0,野々村 そら 0,萩原雪 歩 0,浜 川愛結奈 0,浜口 あやめ 0,日野 茜 2,姫川 友紀 2,双海 亜美 2,双海 真美 2,星井 美希 2,星 輝子 0,堀 裕子 2,本田 未央 2,槙原 志保 2,松山 久美子 2,的場 梨沙 2,真鍋 いつき 0,三好 紗南 0,水瀬 伊織 0,向井 拓海 1,村上 巴 0,メアリー コクラン 0,諸星 きらり 0,矢口 美羽 0,依田 芳乃 0,龍崎 薫 0,若林 智香 2
```


結果を詳しく見ていきます。まず、アイドル196人中、"アナスタシア"　、 "ライラ" などlast_name、 first_nameの概念がないものを取り除きます。その上で、

### last_name、 first_name識別精度 = 88%
190人中、識別の間違いが23人ありました。"赤西瑛梨 華" など、5文字以上で識別間違いが多い印象です。ちなみに、"キャシー グラハム" や "イヴ サンタクロース"など、日本名ではないものも識別できていました。

### 性別識別精度 = 42% (87%)

190人中、女性と判断されたものが79、 男性が11、 識別不能が100でした。男性と識別されたものに関しては、"川島 瑞樹" や "向井 拓海"のように男女どちらでもおかしくない名前が多かったので、仕方がないかなーという感じです。しかし、半分以上が識別不能判定となったのは残念な結果です。識別不能を除いた場合、精度は87%となりました。

# LINEボットの実装

最後に、フルネームを入力したら 性、 名、 性別を返すLINEボットを実装します。今回は、Ruby、Sinatra、heroku、LINE BOT APIを使用しています。このあたりの詳しい使用方法に関しては先人の方々がまとめてくださっているので、詳しくは割愛します。

[参考1:LINE botでオウム返しをブラウザ上で作る全手順](http://www.sakana.tech/entry/2016/04/12/112700)
[参考2:LINE BOT APIでgoogle検索ボットを作った](http://qiita.com/EastResident/items/d1b572f00c29f907b743)

## ファイル構成
ファイル構成は以下のような感じです。

- working_folder
  - Gemfile
  - config.ru
  - main.rb

## Gemfileの編集

Gemfileを以下のように編集し、'bundle install'を実行します。

```
source "https://rubygems.org"

gem 'sinatra'
gem 'json'
gem 'rest-client'
gem 'sinatra-contrib'
```

## config.ruの編集

config.ruは、以下のように編集します。こちらに処理を書いてもいいのですが、今回はmain.rbのほうに処理を書いていきます。

```rb
require './main.rb'

run Main
```

## 環境変数に追加
API KEYやID等の情報をherokuの環境変数に追加します。
LINE developersで作成したアカウントのBasic informationから、Channel ID、 Channel Secret、 MIDの3つ、herokuのFixieアドオンのproxy_URL、そして人工知能ボットAPIのAPI キーを登録します。

```
heroku config:add LINE_CHANNEL_ID="your_channel_id"
heroku config:add LINE_CHANNEL_SECRET="your_channel_secret"
heroku config:add LINE_CHANNEL_MID="your_channel_mid"
heroku config:add USR_LOCAL_API_KEY="your_api_key"
heroku config:add FIXIE_URL="your_proxy_url"
```

## main.rbの編集

main.rbを以下のように編集します。

```rb
require 'bundler/setup'
require 'sinatra/base'
require 'json'
require 'rest-client'
require "cgi"

class Main < Sinatra::Base

  post '/linebot/callback' do
    @params = JSON.parse(request.body.read)['result'].first

    post_text(get_name_parse_api)
  end

  private

  def post_text(post_messages)
    line_pipe = {
      'Content-Type': 'application/json; charset=UTF-8',
      'X-Line-ChannelID': ENV['LINE_CHANNEL_ID'],
      'X-Line-ChannelSecret': ENV['LINE_CHANNEL_SECRET'],
      'X-Line-Trusted-User-With-ACL': ENV['LINE_CHANNEL_MID']
    }

    RestClient.proxy = ENV['FIXIE_URL']
    endpoint_uri = 'https://trialbot-api.line.me/v1/events'

    post_message = post_messages.join
    request_content = {
      to: [@params['content']['from']],
      toChannel: 1383378250,
      eventType: '138311608800106203',
      content: {contentType:1, toType:1, text: post_message }
    }
    content_json = request_content.to_json

    RestClient.post(endpoint_uri, content_json, line_pipe)
  end

  def get_name_parse_api
    word = @params['content']['text']

    request_content = {'key' => ENV['USR_LOCAL_API_KEY'], 'name' => CGI.escape(word)}

    request_params = request_content.reduce([]) do |params, (key, value)|
      params << "#{key}=#{value}"
    end
    rest = RestClient.get('https://chatbot-api.userlocal.jp/api/name?' + request_params.join('&').to_s)

    result = JSON.parse(rest)['result']
    messages = [
      "あなたは\n",
      "性: #{result['last_name']}\n",
      "名: #{result['first_name']}\n",
      "性別: #{%w(不明 男性 女性)[result['gender'].to_i]}\n",
      "ですね？"
    ]
  end
end
```

## 動作確認

LINE上で人物名を入力してみます。

![スクリーンショット 2016-06-19 00.05.52.png](https://qiita-image-store.s3.amazonaws.com/0/79302/439beece-51b9-fcee-1af9-f748bdc2c93b.png)

# 感想

今回デレマスキャラの名前を使って氏名自動識別APIを精度を検証してみましたが、性・名の識別に関しては88%とまずまずの結果が出ました。アニメキャラでこの精度なので、現実の人名ならさらに高い精度が出ると予測されます。もう少し学習が進めば、十分実用レベルになるのではないでしょうか。

性別判定に関しては難しいですね。そもそも、日本人の名前は男女どちらでも使えるものも多いですし、近年は名前自体が多様化してますからね(キラキラネームとか・・・)