生き抜くぜ21世紀

統計?機械学習?っぽいことを書く

姓名分割プログラムをつくる-NameDivider利用マニュアル-

NameDIviderの利用マニュアルです。

DivideのことをDevideだと信じ込んでいたのでリポジトリ名から変更するという門出を迎えたわけですが、気を取り直していきましょう。

理論とかは訂正編手法編をご覧ください。

 

※ 追記

2020.11にPythonパッケージを作りました。

https://github.com/rskmoi/namedivider-python

記事にもして頂きました。

https://internet.watch.impress.co.jp/docs/yajiuma/1289735.html

エンジニアの方にはこちらをおすすめしています。合わせてぜひ。

 

どんなことができるの?

ざっくりいうと、苗字と名前の間にスペースが入っていない名前を苗字と名前に分けることができます。

篠田麻里子」を入力すると、「篠田 麻里子」が出力されるイメージです。

もちろん全部分かれ目を当てることはできませんが、簡単な統計を使って(意地でも人工知能とはいわないぞ)99%程度の精度で分割することができます。

詳しくできることを説明すると、

  • お試しで姓名分割を体感
  • テキストファイルに入っている名前データを読み込み、苗字と名前に分けてtxtファイルに出力
  • ↑を行った後、苗字の分かれ目である確信度が低い順にソートを行う(これがあると、目視で確認するとき楽かなーと思っています)
  • その他開発者用ツール

が入っています。このページの下のほうに詳しい使い方を書いてあります。

 

どんな時に使えるの?

  • 苗字と名前の間にスペースが入っていないデータを苗字と名前に分ける作業を大量に行わなければならないとき
  • ひまつぶし

ダウンロード方法は?

githubで公開しています。

GitHub - rskmoi/NameDivider

f:id:rskmoi:20170320210308p:plain

赤く囲ってあるところをクリック→Download ZIPでダウンロード完了。

適当に解凍してください。

起動方法

[NameDivider]→[confirmpg]→[bin]→[Release]の順に開き、最後にconfirmpg.exeをダブルクリック

f:id:rskmoi:20170320225519p:plain

こんな画面が出てきたらOKです。

今気づいたんですけど姓名分離くんってなんなんですかね。名前をはるか昔につけていたようですがNameDividerでいきます。

コマンド

test

コマンド操作になれていない人は申し訳ありませんが本アプリはコマンド操作をしないと動いてくれません。そんなに難しい操作はないのでちょっと頑張ってみてください。

testと打つとお試しができます。暇つぶしに遊んでみてください。

f:id:rskmoi:20170320230126p:plain

このプログラム、ののたは間違えます 

やめたくなったら

quit

と打ってください。

divide

メイン機能です。

まずは、以下のように名前データを揃えてみてください。

で準備するとうまくいきます。

f:id:rskmoi:20170320230417p:plain

これ以降の作業で注意なのですが、絶対に使用するファイルはバックアップを取っておいてください。何かあっても私は責任を負えませんので。。

さて、次にdivideとコマンドを打ってみてください。

ファイルのパス名を入力してくださいと出てくるはずです。

パス名というのはわからない方にはとても難しいですよね。。

何も考えずにファイルをドラッグアンドドロップするとパス名が入力されます。

すみませんこの辺がわからない人はググってみてください。

f:id:rskmoi:20170320231144p:plain

そしてEnterを押します

f:id:rskmoi:20170320231319p:plain

正常に終了しましたってどういうことだよ!!と言いたくなるかもしれません。すみません。

[NameDivider]→[confirmpg]→[biin]→[Release]の中に[NameDivideResult~]みたいなファイルができているはずなのでそれをダブルクリックすると、、、

f:id:rskmoi:20170320231526p:plain

苗字と名前が分かれてるやつが誕生!

あとはこれを煮るなり焼くなりしてください。

divide -c

-cってなんだよって思った人はまあオプションみたいなものと思っておいてください。

これを使うと、このプログラムが「ここで区切るのか自信ねえなあ。。。」って言ってる名前が上のほうに来ます。

すると、上のほうを重点的に人の目でチェックすればよいので集中力を保ったまま目視で検査ができる!というねらいです。

基本的にやることはdivideと変わりません。

f:id:rskmoi:20170320232213p:plain

難しそうな名前が上に来ているのがお分かりいただけますでしょうか?

ちなみに左の数字は元の並び順の番号なので、ソートを戻すときなどにお使いください。

開発者用コマンドはまた次の機会にしましょう。

みなさんガンガン使っていただければと思います。

 

最後に

以上となります。

なにかありましたらコメント欄など何かしらの形でお知らせください。

クソコードの自覚はあるのでちょいちょいアップデートはしていくつもりです。

姓名分割プログラムをつくる-復活編-

ついつい労働していたら2か月ほどたっててやばいやばいと思っていたらなぜかめっちゃアクセスが増えたと思ったらなんか掲示板?で記事を紹介していただけていたみたいでした。

 

やはり姓名分割の需要は無限に存在しているのですね

というわけでコード公開です

とても汚いのですが3連休でまあまあにしてみせる

 

GitHub - rskmoi/NameDevider

姓名分割プログラムをつくる-アイマスで試してみた編-

前回前々回と姓名分割プログラムのアルゴリズムを紹介してきました。

ここでいう姓名分割とは、苗字と名前の間にスペースが入っていないフルネーム(例:篠田麻里子
にスペースを適切な位置に挿入することをいいます。(例:篠田 麻里子)

今回は具体的にどんな感じで姓名を分けることができているのかについての記事です。
いわゆるテストデータを使って精度を検証してみよう!というやつです。
大体思いつく人名は全部学習に使用してしまったので二次元の人名で確かめてみようとおもいます。
最初に断っておくのですが、私はアイマスについて全然詳しくないので知らず知らずのうちになんか地雷を踏んでいたらすみません。。

使用データ

下記サイトより取得したアイマスのアイドル名174人分
(※全てカタカナで構成された名前は除いてある)
アイドル一覧 -アイマス デレステ攻略まとめwiki【アイドルマスター シンデレラガールズ スターライトステージ】 - Gamerch

検証方法

自作コンソールアプリを用いて検証をする

1)アプリを立ち上げる
f:id:rskmoi:20170129155210p:plain

2)accuracyコマンドを打って、ファイルパスを入力する
f:id:rskmoi:20170129155225p:plain

3)終わり
f:id:rskmoi:20170129155245p:plain

っというわけでこんなにも簡単に0.989の正解率で姓名を分けることができました。
それでは中身を見ていきます。

検証結果

間違えちゃったやつ

下記の二名を間違えています。

false,伊集 院惠
false,鷹富 士茄子
伊集院惠さん

「集」と「院」の漢字データが存在していませんでした。
(約35000人の人名データの中で9回以下の出現だったということ)
まあ4文字の名前の真ん中2つがNoDataだったらこうなる、ということでもっと頑張ってデータ増やさなきゃいけないですね。

鷹富士茄子さん

これは非常にむずかしいですね。
データにない漢字は「茄」だけだったのですが、

  • 「富」が苗字の最後に来る確率
  • 「士」が名前の最初に来る確率

の2つがかなり高く間違えてしまいました。
このアルゴリズムではどんなにデータを増やしても間違えてしまいます。
漢字同士の相性の良さをスコアに入れればいけるかもしれませんが(「富」と「士」は同じ側に分けられることが多い、「士」と「子」は反対側に分けられることが多いなど)、
現状1次元とか2次元で評価してるのでもうそういう情報を入れちゃうと別のシステムという感じになってしまいます。。

というわけで、この2人のファンの皆さま、大変申し訳ございませんでした。

上手くいったやつ

172人分を載せるのもあれなのでアピールしたいものを8人分載せますね

true,木村 夏樹
true,喜多 日菜子
true,喜多見 柚
true,南条 光
true,柳 清良
true,三村 かな子
true,一ノ瀬 志希
true,楊 菲菲
木村夏樹さん

一番ありふれた2文字&2文字の名前です。

喜多日菜子さん、喜多見柚さん

喜多までは同一ですが、そのあとの漢字によりうまく分割できています。
特に喜多見柚さんは3文字&1文字という難易度の高い分割方法ですがうまく分かれてくれました。

南条光さん、柳清良さん

パッと見では人間でも判断がつきづらい3文字フルネームも、うまく分けてくれています。

三村かな子さん、一ノ瀬志希さん

じつはカナ(かな)混じりの名前はちょっと処理が難しくて、漢字→カナ(かな)に変わった場所が姓名の区切れ目でいいじゃないかとおもったら一ノ瀬さんの「ノ」はカタカナなんですね。
ということでそういうところをうまく避けつつカナ(かな)混じりでもうまく分割できましたというアピールです。

楊菲菲さん

これは懺悔で紹介します。じつは「楊」も「菲」も漢字データには存在しておらず、このプログラムでは回答が出せません。
回答が出せなくなったら1文字目で区切ってとりあえず出力するという仕様がたまたまハマっただけなので、この結果は機械学習でも統計でもなんでもないことをあらかじめお詫び申し上げます。

まとめ

とまあこんな感じで遊べるプログラムができたわけです。
精度もまあまあいいし使いたくなってきた人が百万人くらいいることでしょう。このブログの総アクセス数は今現在100行ってないですが。
来週をめどに完成させて公開したいと思っています。
それでは。

姓名分割プログラムをつくる-訂正編-

前回の記事に間違いが見つかったのでそんな感じの記事です。

間違いを直す過程でちょっと手法を見直したりしました。

前回までのおさらい

  • 姓名分割プログラムを作ったよ
  • 姓名分割とは、姓名の間にスペースが入っていない人名にスペースを入れることだよ(篠田麻里子→篠田 麻里子)
  • 特徴量はOrderPointとLengthPointだよ
  • LengthPointの計算がなんかバグってるっぽいよ
  • OrderPointとLengthPointの2特徴量でロジスティック回帰をするよ
  • .990の制度が出たよ
  • 詳しくは前回の記事を読んでほしいよ

間違えてたところ

LengthPointの計算がなんかバグってるっぽいよ のところでした

 

やっぱりかよ

ただ単に計算部分のプログラムミスだったんで修正方法は割愛

間違いを直したら

まちがいを直した後にロジスティック回帰をしたところ、どちゃくそ精度が悪くなってしまいました。

回帰の方法については前回に詳しいです。

参考:前回の引用

篠田麻里子を学習用にデータ処理するとこうなります。

Name,OrderPoint,LengthPoint,Answer

篠 田麻里子,0.121212121212121,0.205,0
篠田 麻里子,0.777253191702888,0.616753480620525,1
篠田麻 里子,0.126787212337516,0.246093120543235,0
篠田麻里 子,0.10023598578629,0,0

Answerは正しく区切れているかのbool

 

なんでだろうとおもいながらグラフを書いてみました。

横軸にOrderPoint,縦軸にLengthPoint,赤が正解の区切り位置、青が間違えた区切り位置です。

f:id:rskmoi:20170122220635p:plain

ペロ、、これは多重共線性

多重共線性とは、特徴量(説明変数)同士に相関があるときに回帰がうまくいかなくなってしまう現象です。

OrderPointの係数が5前後なのに対し、LengthPointの係数が10前後になってしまいました。

> plot(OrderPoint,LengthPoint,col = ifelse(Answer == 1, "red", "blue"))
> res = glm(Answer ~ OrderPoint + LengthPoint,data,family = binomial)
> summary(res)
Call:
glm(formula = Answer ~ OrderPoint + LengthPoint, family = binomial,
data = data)
Deviance Residuals:
Min 1Q Median 3Q Max
-3.6175 -0.0433 -0.0301 0.0153 3.9811
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -7.9242 0.1952 -40.60 <2e-16 ***
OrderPoint 5.3504 0.2949 18.14 <2e-16 ***
LengthPoint 12.6333 0.4529 27.89 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 28915.4 on 22582 degrees of freedom
Residual deviance: 1747.2 on 22580 degrees of freedom
AIC: 1753.2
Number of Fisher Scoring iterations: 9


これじゃあいけませんので何かしましょう。
こういう時は主成分分析がいいという話になっているので主成分分析をしてみました。

2次元の主成分分析は感覚的にとても理解しやすいのでありがたいですね。

主成分分析(2次元)とは、分散が最大となる軸の回転方法を探す操作のことです。

この姓名分離でいうと、「右にあるのが赤で左にあるのが青!」と一番自信をもって言い切れる回転方法を探す操作のことです。

2次元で評価していたものを1次元で評価することにより、多重共線性により回帰ができなくなる問題を回避するわけです。

f:id:rskmoi:20170123214528p:plain

 こんな感じ

 prcompを使って主成分分析してみると...

> sdata <- cbind(OrderPoint,LengthPoint);
> result <- prcomp(sdata,scale =TRUE)
> summary(result)
Importance of components:
PC1 PC2
Standard deviation 1.392 0.24906
Proportion of Variance 0.969 0.03102
Cumulative Proportion 0.969 1.00000
> print(result)
Standard deviations:
[1] 1.3921087 0.2490648

Rotation:
PC1 PC2
OrderPoint 0.7071068 0.7071068
LengthPoint 0.7071068 -0.7071068

 

PC1で全体の96.9%が説明できて、それは

OrderPoint*0.7071068 + LengthPoint  *0.7071068(ぴったり45°回転!??!?)と言っています。

つまり、OrderPoint+LengthPointが一番大きいやつが正しい姓名分割ができてるやつだ、という計算をすればよいことになります。

なんか本当に合っているのか疑ってしまうくらいぴったりな結果が出てしまいました。

しかしながらこうすることによって式の正しさを保ちながら姓名分離することができるようになりましたとさ。

改めて制度検証

例のサッカー選手データ

前回 4人不正解/394人

今回 5人不正解/394人

 

今度解説するアイマスデータ

前回 3人不正解/174人

今回 4人不正解/174人

 

精度落ちとるやないか!

ということであわてて学習用人名を17000くらい追加したらサッカーデータ5人不正解、アイマスデータが2人不正解まで精度が上がりました。

まとめ

  • 多重共線性っぽかったら、主成分分析をしましょう
  • 計算バグがあって精度が高いよりバグがなくて精度が低いほうが気持ちいい

 

姓名分割プログラムをつくる-手法編-

(201701/23追記)

この記事にミスが見つかりましたので是非とも訂正編もあわせてご覧になってください。すみません。

 

人間だれでも一度は姓名分割を自動化したくなったことがありますよね?

しかしながらネットにはほんの少ししか姓名分割にチャレンジしている人がいないのが現状です。

精度もあまり高くありません。

ということで自分で姓名分割プログラムを作ってみました。

まあまあうまくいったのでブログにする次第です。

試行錯誤はけっこうしたのですが、内容がぶれるのを恐れてあくまで手法の解説記事とします。

 

要件

入力:篠田麻里子 出力:篠田 麻里子
のようなプログラムを作る

ざっくりした方針

さて、まず絶対にやりたくなかったのが苗字・名前データベースの作成です。理由は以下です。
・どれだけ苗字を用意すればいいのかわからない
・森、森久、森久保など苗字がどこまでなのかの判定が難しそうだったから(データ数的に)

というわけで漢字一文字ベースで判定をする方針にしてみました。

名前を区切れる場所すべてで区切っていき、
この漢字(一文字)は名前にあることが多いのか苗字にあることが多いのか、、といった確率を最大化する姓名分割方法を探す、というのが方針です。

  1. 大量の名前データ(以下、漢字学習データ)を準備する
  2. 漢字学習データから漢字とその出現データをまとめたリストをつくる
  3. 作ったリストをもちいて名前に対し特徴量を作成する
  4. 特徴量を用いて正しい場所で区切れているかそうでないかロジスティック回帰を行う
  5. テストデータで評価

というステップでモデルの作成をしました。

手法

漢字学習データを準備する

無作為に取ってきた人名34642人分を使いました。
ここでいう無作為とはこの苗字が欲しい!という取り方ではなく、ある業界(たとえば「バレーボール選手」など)から取得できるだけの人名を取得するということです。
wikiからなるべく男女比が1:1になるように様々な業界の名前を取ってきたりしました。
けっこう姓名分離されているケースが少なかったので頑張って自分で姓名を分離しました。辛かった。
プロゴルファーと日本証券アナリスト協会の名簿がネットに転がっていたのが本当に助かりました。

 

漢字学習データから漢字とその出現データをまとめたリストをつくる

漢字一文字を以下のようなデータ構造で保管します。(例は「田」)

田,690,57,4386,0,2,0,2,4911,220,0,0,0,1,1

意味は以下のようになります。

[漢字]

  文字:田

  [出てくる場所についての情報]
  苗字一文字目の観測数:690

  苗字その他文字目の観測数:57

  苗字最後文字目の観測数:4386

  名前一文字目の観測数:0

  名前その他文字目の観測数:2

  名前最後文字目の観測数:0

  [長さについての情報]

  この漢字が含まれる苗字の長さが1の観測数:2

  この漢字が含まれる苗字の長さが2の観測数:4911

  この漢字が含まれる苗字の長さが3の観測数:220

  この漢字が含まれる苗字の長さが4以上の観測数:0

  この漢字が含まれる名前の長さが1の観測数:0

  この漢字が含まれる名前の長さが2の観測数:0

  この漢字が含まれる名前の長さが3の観測数:1

  この漢字が含まれる名前の長さが4以上の観測数:1

 

観測数が10回以上のものを漢字リストに入れる条件としたところ、744種類の漢字データが集まりました。

なんでこの構造にしたかは次の項で。

 

作ったリストをもちいて名前に対し特徴量を作成する

特徴量は2つ、OrderPointとLengthPointです。

 

定義

OrderPoint

n文字からなる名前があったとします(n > 1)

[出てくる場所についての情報]データをもとに、i番目の漢字に対して

 \displaystyle Oi = \frac{実際に自分が存在する場所の観測数}{自分が存在しうる全ての場所の観測数の総和}

を求めます。

ただし、i ≠1,nです(1文字目が苗字の一文字目なのもn文字目が名前の最後文字目なのも確定だから)

 

篠田 麻里子の「里」で例示すると、

里,12,1,38,46,48,78,2,40,9,0,0,95,77,0

とあるので

  \displaystyle O4 = \frac{(名前のその他文字目:48)}{(苗字の最後の文字目:38)+(名前の1文字目:46)+(名前のその他文字目:48)}

  \displaystyle  = 0.3636

といったところです。

 

最後に

 \displaystyle OrderPoint: O = \frac{1}{n-2}\sum_{i=2}^{i-1} Oi

としてOrderpointを求めます。

 

LengthPoint

n文字からなる名前があったとします(n > 1)

[長さについての情報]データをもとに、i番目の漢字に対して

 \displaystyle Li = \frac{実際に自分が存在する苗字or名前の長さの観測数}{自分が存在しうる全ての長さの観測数の総和}

を求めます。

最後に

 \displaystyle LengthPoint: L= \frac{1}{n-1}\sum_{i=1}^{i} Li

としてOrderpointを求めます。

なんでnではなくn-1で割っているかみなさんはわかりますか?

なんと私もわかりません。nで割るべきでしょう(ブログを書いていて気付いた)

(2017/01/23追記)

やっぱりただのバグでした。以下の情報は参考程度に読んでください。。

しかしnで割ったほうが精度が出ません。つらい。

nが小さいほどLengthPointの重要性が高まることをうまく表現しているととらえてお茶を濁しましょうかね。つらい。

 

 説明

これは我々人間が苗字と名前の分割をこの2つでやっているのではないかという仮説の元作成した特徴量です。

・OrderPointについて

篠田麻里子が篠田 麻里子とわかるのは、

「麻」が名前の1文字目にある可能性が苗字の最後にある可能性より高いからそう判断しているのではないでしょうか?

篠田川里子さんだったら篠田川 里子と分割する人が多いように思います。

という考えをモデル化したものです。

・LengthPointについて

松高子さんは松 高子

松高萌さんだったら松高 萌

と人間は分割するでしょう。これは「子」1文字の名前は存在しづらく、「萌」1文字の名前が存在しやすいことに起因しているのではないでしょうか。

という考えをモデル化したものです。

 

特徴量を用いて正しい場所で区切れているかそうでないかロジスティック回帰を行う

篠田麻里子を学習用にデータ処理するとこうなります。

Name,OrderPoint,LengthPoint,Answer

篠 田麻里子,0.121212121212121,0.205,0
篠田 麻里子,0.777253191702888,0.616753480620525,1
篠田麻 里子,0.126787212337516,0.246093120543235,0
篠田麻里 子,0.10023598578629,0,0

Answerは正しく区切れているかのbool

 

で、この処理を漢字学習データの一部(8000人弱)(以下回帰学習データ)に対して行い、rでプロットしたものが以下です。

> plot(OrderPoint,LengthPoint,col = ifelse(Answer == 1, "red", "blue"))

f:id:rskmoi:20170115174455p:plain

赤が正解、青が間違いです。まあまあうまくいっていると思いますので以下のようにロジスティック回帰をして

> res = glm(Answer ~ OrderPoint + LengthPoint,data,family = binomial)
> summary(res)

Call:
glm(formula = Answer ~ OrderPoint + LengthPoint, family = binomial,
data = data)

Deviance Residuals:
Min 1Q Median 3Q Max
-4.1218 -0.0637 -0.0373 0.0152 3.8672

Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -7.4769 0.1628 -45.94 <2e-16 ***
OrderPoint 10.2511 0.2691 38.09 <2e-16 ***
LengthPoint 5.3108 0.1892 28.06 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

Null deviance: 28915.4 on 22582 degrees of freedom
Residual deviance: 2247.6 on 22580 degrees of freedom
AIC: 2253.6

Number of Fisher Scoring iterations: 8

みたいな結果を得ます。

ここまでの説明でお分かりの方も多いと思いますが私はあんまり統計のことを理解できていないのでこの辺の解釈は今月中にします。

 

テストデータで評価

テストデータ

なでしこリーグに所属する選手200人弱+Jリーガー200人弱 = 394人分の人名

です。

漢字かな(カナ)交じりの名前は入れていますが、かな(カナ)のみの名前は入れていません。

評価手法

区切れるすべての個所で区切り、

作ったモデルにおいて値が最大になるものを真の名前と判定します。

ただし、漢字かな(カナ)交じりの名前は漢字とかなの区切れ目が姓名の区切れ目と判定します。

結果

正答:390

誤答:4

正答率:0.990

 

ワーイワーイ

ただし2文字目で切るだけでも正答率90出るのであんまり手放しには喜べませんが。。

結果の考察

なんかつらつら書いてたんですが実在する選手の名前についてあーだこーだいうのも悪趣味かなかなと思いはじめてきたんで後日アイマスのアイドルたちを姓名分割した結果をブログに書きます。

結果だけ言うと誤答2人/全体174人の正答率0.989でした。

 

まとめ

  • 正答率0.990を誇る姓名分割プログラムが爆誕した
  • ただ、ブログを書いている途中に使っている式に全く論理性がないことに気づき(LengthPointのとこ)凹む
  • 実在の人名についてネット上であれこれ言うのはどうかと思い、結果の考察が次回に持ち越し

世界中誰でも姓名分離ができる夢のようなプログラムが使えるようにコンソールアプリを作っている(95%できている)ので、それも使い方記事と一緒に今度アップします。それでは。