単語の音声的類似性。Pythonによるベクトル化アプローチ

以前の記事で、音声アルゴリズムについて紹介し、その多様性を示しました。より詳細には、編集距離(Levenshtein Distanceとも呼ばれる)を見てきました。このアルゴリズムは、ある単語から次の単語への文字置換数を計算するために開発されたものです。

前回の記事ですでにお気づきかもしれませんが、Soundex、Metaphone、Match Rating codexのように、単語の音を計算するさまざまな方法が存在します。その中には、より一般的なものもあります。例えば、Soundexの実装は、あらゆるプログラミング言語や、Oracle、MySQL、PostgreSQLなどのデータベース管理システム(DBMS)に含まれている。一方、MetaphoneやMatch Rating codexはほとんど使われておらず、ほとんどの場合、システムに追加のソフトウェアライブラリをインストールする必要があります。

この記事では、異なる音声認識アルゴリズムをベクトル化し、それぞれの特徴を生かすことで、それぞれのアルゴリズムを個別に使用するよりも優れた比較結果を得られる方法を提案します。これを実現するために、SourceForgeにあるAdvaS Advanced SearchというPythonベースのライブラリが活躍する。AdvaSには、一つの単語に対して複数の音声符号を一度に計算するためのメソッドがすでに含まれている。

音声アルゴリズムの説明

より正確には、各アルゴリズムは1つの単語に対して特定の音声表現を作成します。通常は、文字のみ、あるいは文字と数字の組み合わせからなる固定長、あるいは可変長の文字列で表現します。表現の詳細な構造は、アルゴリズムに依存する。実は、同じアルゴリズムで計算された2つの表現が似ていれば、元の2つの単語はどのように書かれていても同じように発音されるのである。このため、実際にはスペルが異なっていても、意図的であろうと偶然であろうと、似たような発音をする単語を検出することができる。

これらのアルゴリズムは、それぞれ特定の言語や目的を念頭に置いて設計されており、他の言語と全く同じように適合するわけではありません。表現は常に最適とは限らないが、できるだけ近い形で適合するように意図されていることを心に留めておいてほしい。例えば、オリジナルのSoundexアルゴリズムは英語に特化していますが、Kölner Phonetikはドイツ語に特化しており、ウムラウトや「ß」のような特殊文字が含まれます。

次に、音声認識アルゴリズムの一部を簡単に紹介します。より詳細な説明は、以下のリンクを参照してください。各アルゴリズムのドキュメントのレベルは、非常に詳細なものから非常に疎なものまで、かなり異なっていることに注意してください。

サウンデックス

Soundexアルゴリズムから得られる表現は、4文字の単語である。これは、1文字に続く3桁の数字に基づいている。たとえば、「Knuth」のSoundex値は「K530」で、「Kant」と似ている。このように単純化することで、誤解を招く表現が少なくない。しかし、一般的には非常に良い結果が得られている。Soundexは元々アメリカ英語のために設計されたが、今日ではフランス語、ドイツ語、ヘブライ語など、さまざまな言語固有のバージョンがある。

20世紀初頭にRobert C. RussellとMargaret King Odellによって開発されたSoundexは、英語を念頭に置いて設計されたものである。1930年代のアメリカの国勢調査で、似たような名前の姓を検出するために広く利用された。

メタフォン

1990年にLawrence Phillipsによって開発されたMetaphoneも、英語を意識して設計された。彼は、Soundexの仕組みを改良するために、英語のスペルや発音のバリエーションや矛盾の情報を使って、より正確な符号化を行おうとした。その結果、音声表現は16個の子音「0BFHJKLMNPRSTWXY」をベースにした可変長の単語となった。5つの母音 “AEIOU “も許容されますが、表現の先頭にのみ置かれます。

メタフォンのアルゴリズムの最初の記述はかなり不正確であったため、ダブルメタフォンとメタフォン3の両方が開発されることになりました。後者は、最初の2つのバージョンで生じた何千ものミスコーディ ングを修正することができます。Metaphone 3は、ドイツ語とスペイン語の両方の発音をサポートする商用ソフトウェアとして販売されています。

図1は、オランダの家系図作成サイトからのスクリーンショットで、「Knuth」という名前のSoundex、Metaphone、Double Metaphoneのそれぞれの表記を示したものです。また、図には、同じように表現され、同じ音声コード(”Gleiche Kodierung wie”)を持つ単語を選んで表示しています。アルゴリズムが特徴的であればあるほど、同じ音声コードを持つ単語の数は少なくなるのがベストである。

図1

Metaphoneアルゴリズムは、PHPなど一部のプログラミング言語にのみ標準搭載されている。Pythonでは、MetaphoneとDouble Metaphoneの両方がPhoneticsパッケージの一部になっています。C++、C#、Java、Python、Rubyのプログラミング言語向けに、商用実装が提供されている。

キャバーフォン

Caverphoneアルゴリズムは、2002年にDavid Hoodによって作成されました。2004年に改訂版がリリースされた。プロジェクト環境は、ニュージーランドのオタゴ大学にあるCaverhamプロジェクトである。このアルゴリズムの背景には、19世紀後半から20世紀初頭にかけての選挙人名簿データの照合を支援するため、「一般的に認識可能な形式」の名前だけが必要でした。このアルゴリズムは、大学のある自治体にちなんで名づけられ、名前の研究が行われた言語特有の文字の組み合わせに最適化されています。

デフォルトでは、キャバーフォン表現は6文字と数字で構成されています。実装によっては、10文字と数字まで拡張することができます。例として、”Thompson “は “TMPSN1 “というコードに変換されます。現在、このアルゴリズムは、C#、Python(改訂版)、Java(オリジナル版と改訂版の両方)、Rで利用可能である。

ニューヨーク州識別情報システム(New York State Identification and Intelligence System)

1970年代にNYSIIS(New York State Identification and Intelligence System)の一部として開発されたアルゴリズムです。現在も使用されており、その品質はSoundexアルゴリズムに近いと言われています。

アメリカ人の名前に特化してマッチングするように最適化された設計になっている。そのため、「Webberley」と「Wibberley」という2つの名前は、「WABARLY」という音声コードで表される。

ケルナー・フォンエティック

Soundex アルゴリズムに基づき、1969 年に Hans Joachim Postel が Kölner Phonetik を開発しました。これはドイツ語を対象としており、後にSAPシステムの一部となった。音声表現は単なる数字の可変長文字列である。

現在、Perl、PHP、JavaScriptでの実装が知られている。

マッチ・レイティング・アプローチ

マッチ・レイティング・アプローチ(MRA)コーデックスは、1977年にウエスタン航空が開発したものです。これは乗客名簿から同音異義語の名前を検出するもので、特に英語表記に重点を置いている。例として、「Smith」の表現は「SMTH」であるのに対し、「Smyth」は「SMYTH」で符号化される。

現在、MRAはC#の実装としてアーカイブされたウェブサイトから、またPythonのメソッドとしてJellyfishモジュールから利用可能です。

実装

類似度の計算は、以下のソースコードのリストで codeList1, codeList2, weight という3つのベクトルに基づいて行われる。Pythonでは、ベクトルはNumPyパッケージなどを用いて配列として実装することができる。ベクトル1番と2番は、2つの異なる単語の音声コードを表します。ベクトル番号3は、特定のアルゴリズムの重みを表し、その重みを表すために0から1の間の分数値を含んでいます。ベクトル3の単値の合計は正確には1の値であり、それ以下でもそれ以上でもないはずである。そのため、ベクトル3の単値はあらかじめ正規化されている必要がある。

図2に3つのベクトルを示す。

図2 データを保持するための3つのベクトル

算出された2つの単語の類似度は、音声アルゴリズムごとの計算による10進数値である(小計)。各小計は、codeList1codeList2の特定の音素表現間のレーベンシュタイン距離と、特定の音素アルゴリズムに応じた重みの積である。NYSIISの場合、以下のように計算される。

nysiis = Levenshtein(codeList1["nysiis"], codeList2["nysiis"]) * weight["nysiis"]
       = Levenshtein("Knatt", "Kand") * 0.1
       = 3 * 0.1
       = 0.3


前回説明したように、レーベンシュタイン距離とは、ある単語から次の単語に移るまでに必要な編集回数を返すものである。この場合、2つの単語はアルゴリズムごとに計算される音声コードである。コード間の変更(edit)数が少ないほど、アルゴリズムから見た元の単語の音素類似度が高いということになります。

以下のPythonコードは、AdvaSモジュールのPhoneticsクラスと、NumPyモジュールを使用しています。レーベンシュタイン関数の定義は、以前のレーベンシュタイン距離の記事と同様であり、念のため記載しておきます。次に、3つのベクトルを図2のように初期化し、ループで小計を計算し、その合計を標準出力に出力します。

# -*- coding: utf-8 -*-


from phonetics import Phonetics
import numpy as np


def levenshtein(seq1, seq2):  
    size_x = len(seq1) + 1
    size_y = len(seq2) + 1
    matrix = np.zeros ((size_x, size_y))
    for x in xrange(size_x):
        matrix [x, 0] = x
    for y in xrange(size_y):
        matrix [0, y] = y


for x in xrange(1, size_x):
        for y in xrange(1, size_y):
            if seq1[x-1] == seq2[y-1]:
                matrix [x,y] = min(
                    matrix[x-1, y] + 1,
                    matrix[x-1, y-1],
                    matrix[x, y-1] + 1
                )
            else:
                matrix [x,y] = min(
                    matrix[x-1,y] + 1,
                    matrix[x-1,y-1] + 1,
                    matrix[x,y-1] + 1
                )
    return (matrix[size_x - 1, size_y - 1])


# -- initialize phonetics object


word1 = Phonetics("Knuth")
word2 = Phonetics("Kant")
print ("Comparing %s with %s" % (word1.getText(), word2.getText()))


# -- phonetic code
codeList1 = word1.phoneticCode()
codeList2 = word2.phoneticCode()


# -- weight
weight = {
    "soundex": 0.2,
    "caverphone": 0.2,
    "metaphone": 0.5,
    "nysiis": 0.1
}


# -- algorithms
algorithms = ["soundex", "caverphone", "metaphone", "nysiis"]


# -- total
total = 0.0
for entry in algorithms:
    code1 = codeList1[entry]
    code2 = codeList2[entry]
    lev = levenshtein (code1, code2)
    currentWeight = weight[entry]
    print ("comparing %s with %s for %s (%0.2f: weight %0.2f)" % (code1, code2, entry, lev, currentWeight))
    subtotal = lev * currentWeight
    total += subtotal


print ("total: %0.2f" % total)


ソースコードがphonetics-vector.pyというファイルに格納されていると仮定すると、出力は以下のようになります。

$ python phonetics-vector.py
Comparing Knuth with Kant
comparing K530 with K530 for soundex (0.00: weight 0.20)
comparing KNT1111111 with KNT1111111 for caverphone (0.00: weight 0.20)
comparing n0h with nt for metaphone (2.00: weight 0.50)
comparing Knatt with Kand for nysiis (3.00: weight 0.20)
total: 1.60
$


類似度が小さいほど、2つの単語の発音が同じであることを示しています。上の例で示したように、「Knuth」と「Kant」の計算値は1.6であり、非常に低い値です。

結論

ここで説明したアプローチは、異なる音素解析手法の特殊性をバランスさせる解決策を見出すのに役立った。今のところ、最初の結果は有望であるが、まだ最適とは言えないかもしれない。重みベクトルは、各音声処理アルゴリズムの影響を調整するために使用される。言語ごとの適切なウェイト値分布を見つけるには、さらなる研究が必要である。また、考慮するアルゴリズムのリストは簡単に拡張することができる。

謝辞

Gerold RupprechtとZoleka Hatitongweの協力に感謝する。

タイトルとURLをコピーしました