有限状態トランスデューサによる日本人名の構造化

scouty の高濱です。本記事では、日本人の名前の構造化のために、自然言語処理における古典的な手法である有限状態トランスデューサ (Finite-State Transducer; FST) を用いた例を紹介します。本記事で紹介するライブラリの実装や本記事のドラフトは、scouty インターンの Derick によって作成されました。 Derick による本記事の英語版も同時に公開されていますので、そちらもご参照ください。

はじめに

背景

scouty は「世の中のミスマッチをなくす」というミッションのもと、インターネット上のオープンデータを解析して人に関するデータベースを構築し、エンジニアと企業の最適なマッチングを提案するサービスを提供しています。

scouty では、scouty サービスを利用する企業が候補者に対してコンタクトをとる方法としてスカウトメールの送信機能を用意しています。scouty では候補者名を匿名化して企業に提示するため、企業側は候補者の氏名を見ることができません1。しかし、スカウトメールを送るときにはフルネームだけではなく「 名字 様」といった表現を使いたい場合もありますので、データベース内で候補者の姓と名を正しく構造化できていることが望ましいです。

これを踏まえて、構造化されていない日本人の名前の文字列を「 」からなる構造化表現に変換するプロジェクトに取り組みました2

日本人の名前の表記揺れ

さて、構造化されていない日本人の名前に関しては、大まかに以下のような表記揺れが存在します:

スペースを含む スペースを含まない
漢字 山田 太郎 山田太郎
ひらがな やまだ たろう やまだたろう
カタカナ ヤマダ タロウ ヤマダタロウ
ローマ字 Yamada Taro
Taro Yamada
YamadaTaro
TaroYamada

まず、名前にスペースが含まれている場合について考えます。漢字・ひらがな・カタカナで表記されているときは、ほとんどすべての場合で日本語の習慣に従って 姓 名 という順番で名前が書かれるため、推定の必要がありません。一方、ローマ字表記されているときは、日本語の習慣に従って 姓 名 の順になっていることもあれば、西洋の習慣に従って 名 姓 の順になっている場合もあります。そのため、ローマ字表記された日本人の名前に対して適切な姓名の構造を与えるためには、どちらが姓でどちらが名であるかを正しく判定する必要があります。本記事では、このスペースを含み、ローマ字表記された日本人の名前の構造推定問題を解決するための取り組みを詳細にご紹介します。

なお、名前にスペースが含まれていない場合は、姓らしき部分と名らしき部分を分割してスペースありで表記されているように扱うか、あるいは分割せずに姓名を推定する必要がありますが、本記事ではこのデータ形式を扱わず、今後の課題とします。

名字推定問題

名字由来net によれば、日本人の名字は10万種以上ありますが3、そのうち2万種の名字が日本の人口の99%以上をカバーしています4。一部の珍しい名字を除き、一般的な名字には頻度や読み方などの情報が十分にあります。一方、名字に比べ、下の名前に関する情報は相対的に不足しています。

以上を踏まえ、日本人の名前の構造化という目的を実現するため、姓と名の分類する問題ではなく名字を検知する問題に取り組むこととします。名字推定問題としての定式化によって、人口の数%には対応できなくともシステムの改善に寄与するのに十分な精度のアルゴリズムを構築することができると考えられます。

ローマ字逆変換

上述の名字推定問題は、具体的には、姓と名のいずれかに該当する2つの文字列が与えられたとき、どちらの方が名字としてより尤もらしいかを判断する問題として解釈できます。名字のデータベースには例えばこちらのようなものがありますが、こうした名字のデータベースは基本的に漢字とフリガナ(あるいはふりがな)に関するデータを格納しています。本プロジェクトでは特にローマ字表記された名前と入力文字列として扱いますから、データベースに照らし合わせるためにローマ字からカタカナへの字訳を行う必要があります。

ここで字訳 (transliteration) あるいは翻字とは、「特定の言語で記した文字表記を別の文字による表記に移すこと」と定義されます。ローマ字表記された日本人の名前をカタカナ表記に変えることは「ローマ字からカタカナへの字訳」と表現することができます。

ローマ字とカタカナの字訳は一見難しくありませんが、変換式が複数にある、マクロンを省略する、といったような、不規則な書き方が多いという問題を考慮する必要があります。その複雑な変換に対応できる既存のライブラリを見つけられなかったため、本プロジェクトではローマ字からカタカナへの字訳を行うライブラリを開発しました。

有限状態トランスデューサ (FST)

有限状態トランスデューサ (Finite-State Transducer; FST) は自然言語処理におけるテキストの正規化や品詞分類など、様々なタスクで活躍した手法です。有限状態トランスデューサは有限オートマトンの一種ですが、遷移の際に入力を考慮するだけではなく出力を行うことができるという特徴があります。

本記事の以下の部分では、有限オートマトンについての知識を前提とします。有限オートマトンについての基礎知識を確認したい方は 有限オートマトン - Wikipedia をご参照ください。

FSTにより、何らかの記号を用いて表記されたある言語(入力記号列)を他の言語(出力記号列)へ翻訳するために用いることができます。ある入力記号列に対する出力記号列は次のいずれかになります:

  • 出力なし:入力記号列が受容されなかった場合
  • 1つの出力:入力記号列に対して決定的である場合
  • 2つ以上の出力:入力記号列に対して非決定的である場合

本プロジェクトで扱うローマ字からカタカナへの字訳を行う際には曖昧さが存在します。そのため、入力記号列に対して非決定的に複数の出力記号列を扱えるというFSTの特徴は字訳に適しています。

基本的なFST

まず、FSTの最も簡単な例を示します:

上記のFSTは catdog という変換を行います。一般的な有限オートマトンが入力と状態を考慮して遷移を行うのに対し、FSTは遷移の際に何らかの出力を行うことができます。入力と出力は、遷移を表現する矢印に添えられて 入力:出力 と表記されます。初期状態から受理状態に遷移するまでに出力された記号の列が変換の結果としてFSTから出力されます。

ε は特殊な記号で、入力として指定されれば「何も読まない」ことを、出力として指定されれば「何も出力しない」ことを表現します。入力と出力にともに ε を指定することで catdog に加えて catsdogs の変換も行う以下のようなFSTが定義できます:

さらに、入力あるいは出力の一方に ε を用いることによって、入力記号列と出力記号列の長さが異なる場合に対応することもできます。また、入力記号列に対して非決定的である場合、1つの入力記号列が2つ以上の出力記号列に変換されることもあります。例えば、以下のFSTは dog → {wolf, wolves} という変換を行うものです:

FSTの合成

FSTの便利な性質として合成が可能であることが挙げられます。1つ目の例の前者の変換を行うFSTと、2つ目の例のFSTを合成することで、以下のFSTが得られます:

このFSTが行う変換は cat → {wolf, wolves} です。

重み付き有限状態トランスデューサ (WFST)

FSTの遷移に重みを持たせることで、重み付き有限状態トランスデューサ (Weighted Finite-State Transducer; WFST) を定義することができます。入力と出力と重みは、遷移を表現する矢印に添えられて 入力:出力/重み と表記されます。以下に例を示します:

このWFSTは入力記号列 meow を次の出力記号列に変換します:

  • woof (重み: 1/2)
  • woof woof (重み: 1/4)
  • woof woof woof (重み: 1/8)
    など

手法

本プロジェクトでは、FSTといくつかのヒューリスティックを組み合わせることで、ローマ字表記された名前の構造化を行います。

FSTによる名字推定

本プロジェクトで用いるFSTは、字訳器語彙知識をもつアクセプタを合成することによって作成します。また、実装にあたっては、FSTのライブラリである OpenFst を利用しました。

字訳器

字訳器 (transliterator) は、(語彙知識を無視して)ローマ字列からありえる全てのカタカナ文字列を生成します。

例として、 シンイチ のカタカナをヘボン式でローマ字化する場合、理想的には Shin'ichi になりますが、実際には Shinichi と書かれる場合も多いです。これによって、 シニチ とかぶって曖昧なケースが発生してしまいます。以下のトランスデューサは、上記の曖昧さを考慮した上で、次のような変換を行います:

  • Shinichi → {シンイチ, シニチ}
  • Shin'ichiシンイチ

このように、あらゆるローマ字列に対して考えうるすべてのカタカナ文字列を生成するようなFSTを構築し、字訳器とします。

語彙知識をもつアクセプタ

アクセプタ (acceptor) は、入力文字列を受理すれば入力文字列と同じ出力文字列を生成するようなトランスデューサです。ここで、「語彙知識」とは、「データベースにどのような名字があるのか」という情報と、「各名字の頻度」を指します。

ローマ字列を字訳器に入力することで、その文字列が日本語に見えるかどうかの判断はできますが、それが名字らしいかどうかを判断するには不十分です。そこで、語彙知識を使って字訳器の出力を絞って重みを付けます。一般的な約一万件の名字には頻度データがあります。またその約一万件に含まれない名字も考慮すべく、「少なくとも一人は存在する」数万件の名字の集合を作ります。

以下のアクセプタは、名字のデータベースに {サトウ, サトミ, サトハラ} が存在して、その頻度がそれぞれ 1000, 10, 1 であるというデータを反映して作成されたものです:

このように、データベースに含まれるすべての名字のデータを用いてアクセプタを構築します。

字訳器とアクセプタの合成

上述した字訳器とアクセプタを合成することで、以下のようなFSTが得られます:

このFSTを用いると、ローマ字を入力文字列とし、カタカナと重みの組の集合を得ることができます。

ヒューリスティックによる名字推定

日本人が名前をローマ字で書くとき、姓と名が曖昧にならないように工夫をすることがあります。これを踏まえて、以下のような名字推定の判断基準を設けることができます:

  • 一方だけがすべて大文字で書かれている場合、その部分は名字である可能性が高い
  • 一方だけがイニシャルで書かれている場合、その部分は名字ではない可能性が高い

本プロジェクトでは上記の2つのヒューリスティックを推定のシステムに組み込みます。

オープンソースパッケージ

上記の手法をオープンソースライブラリ myouji-kenchi として PyPI に投稿しました。

実験

GitHub の Usage にある通りですが、以下のように利用することができます。
myouji_kenchi.order_names によって、 [名, 姓] という形で推定結果を得ることができます:

>>> import myouji_kenchi
>>> myouji_kenchi.order_names(['Yamada', 'Satoshi'])
['Satoshi', 'Yamada']

myouji_kenchi.get_score_as_myouji によって、「各文字列がどの程度名字らしいか」のスコアを確認できます:

>>> myouji_kenchi.get_score_as_myouji('Yamada')
201046.0
>>> myouji_kenchi.get_score_as_myouji('Satoshi')
329.0

また、以下のようにして、字訳器の出力を確認できます。字訳器は、あり得る出力カタカナ列とその重みを出力します:

>>> transliterator = myouji_kenchi.MyoujiBackTransliteration()
>>> transliterator.back_transliterate('Yamada')
[('ヤマダ', 201046.0)]

おわりに

本記事では、名前の文字列に正しい姓名の構造を与えるためのプロジェクトのためにFSTを用いた例を紹介しました。

以下に今後の課題を述べます。

漢字を考慮したFST

名字によっては、名字をローマ字表記する際にフリガナだけではなく漢字も考慮しなくてはならない場合があります。

例として「石井」という名字と「井椎」という名前を考えます。これらはともに「イシイ」というフリガナを持ちますが、訓令式では「石井」は「Ishii」に、「井椎」は「Ishî」に、それぞれ変換されます。
ヘボン式の可能性を踏まえると、「Ishii」はどれにも一致する一方、「Ishî」は「石井」に一致しません。このように、漢字を考慮しないと正しく変換できないような場合に対しては、本記事の手法では対応することができません5

これを解決するためには、フリガナと漢字のアラインメント、つまり、どの漢字がどのカタカナに対応するかに関する情報を用意する必要があります。先にこうしたデータを用意した上で、それを利用することによりアルゴリズムをさらに改善できると考えられます。

重みを考慮した字訳器

本プロジェクトで実装した字訳器では、遷移に重みを付けませんでした。この字訳の際、経験的なデータの頻度を遷移の重みとしてFSTに組み込むことで、より高いパフォーマンスを得られることが期待できます。例として Shinichi, Shin'ichi を用いると、 ni を 75% の確率で に変換するべきとわかったら次のような字訳を行うFSTを書くことができます:

  • Shinichi → {(シンイチ, 0.25), (シニチ, 0.75)}
  • Shin'ichi → (シンイチ, 1)

ヒューリスティックの強化

本プロジェクトでは姓名の片方がすべて大文字で書かれている、イニシャルで書かれている場合を推定に利用しましたが、そのほかにも利用可能なヒューリスティックが存在する可能性があります。より広いデータの解析によって、さらに優れたヒューリスティックを開発できる可能性があります。

脚注

  1. 2018年7月23日以降は、オプトアウト形式に基づき氏名を公開します。
  2. 全世界のあらゆる人の名前を単一の構造化表現に収めることは難しいかもしれませんが、日本人の名前に限れば「 」による構造化を行うことができると考えられます。
  3. 漢字かフリガナのいずれかが異なれば、異なる姓としてカウントします。
  4. 比較として、2010年のアメリカの国勢調査では、200万種類の姓をもってしても人口の99%をカバーできませんでした。
  5. とはいえ、こうしたケースはあまり多くありません。