はじめに
こんにちは。この記事では、pythonで日本語を読み方に沿ったローマ字に変換する方法を説明します。
さて、何でそんなことすることになったかと言いますと、先日OSCに参加し、AIを使ったロボットのデモに関連しています。AIを使ったロボットについてはこちらの記事に詳しく書いてあります。
このロボット(Qumcum)には喋る機能があり、AIによって喋る内容を決めています。Qumcumはローマ字しか受け付けない仕様となっていますが、AIは喋りたい文章を日本語(漢字かな交じり)で返します。しかも、喋るということは、文字ではなく音ですので読み方に準拠したローマ字に変換する必要があります。つまり、「日本語は難しい」は「nihongowamuzukasi-」の様に「は」は「wa」に、「しい」は「si-」にする必要があります。(半角ハイフンは長音符です。)
ということで、Pythonのライブラリを使って漢字かな交じりの日本語を読み方のローマ字表記に変換します。
つまりこの記事は、AIのデモのAIじゃない部分の解説ってことです。(笑)
実装
漢字かな交じり⇒カタカナ(読み)
漢字かな交じりの日本語の文章から読みの音に変換するにはfugashiというライブラリを使用します。fugashiは形態素解析ツールであるMeCabのラッパーで、辞書としてUniDicが推奨されています。
pip install 'fugashi[unidic-lite]'
fugashi.Tagger
で日本語を形態素解析したfugashi.fugashi.UnidicNode
型のリストに出来るので、各要素で読み方(w.feature.pron
)を取得して結合します。ちなみにそのままの文字列はw.surface
で、読み方ではなく文字としてのカタカナはw.feature.kana
で取得できます。
import fugashi
fugger = fugashi.Tagger()
text = "日本語は難しい"
# fugashiで日本語の部分を読み(カタカナ)に変換
words = fugger(text)
result = []
for w in words:
result.append(w.feature.pron)
kana = "".join(result)
print(kana)
実際に使う時には、途中に読まない文字(句読点や?など)があると消えてしまうので、re.sub()
関数を使って、正規表現で日本語の部分だけを抜き出して変換します。この関数は、re.sub(正規表現, 関数, 入力文字列)の形で使うと、入力文字列の内、正規表現に対応する部分全てに対して、関数が実行されその返り値に置換されます。
また、辞書にない単語(固有名詞など)では、w.feature.pron
がNone
になり、join
出来ないので、読みではなくそのままの文字を返します。
import fugashi
import re
fugger = fugashi.Tagger()
text = "日本語は難しい"
# fugashiで日本語の部分を読み(カタカナ)に変換
def convert_to_yomi(match):
# 辞書に読みが無い場合(固有名詞など)はそのままの文字を返す
return "".join([w.feature.pron if w.feature.pron else w.surface for w in fugger(match.group())])
# 正規表現でテキスト中の日本語を見つけるて読みに変換する
kana = re.sub(r'[ぁ-んァ-ン一-龥々〆ヵヶー]+', convert_to_yomi, text)
print(kana)
ニッポンゴワムズカシー
同形異音語は処理できないので、「ニホンゴ」ではなく「ニッポンゴ」になっていますが、それ以外は正しく読み方に変換できました。
カタカナ⇒ローマ字
カタカナをローマ字に変換するには、pykakasiというライブラリを使用しました。pykakasiは、漢字・ひらがな・カタカナの日本語テキストをローマ字に変換するライブラリで、c言語のkakasiというライブラリのpythonバージョンです。
今回は、読みの音のローマ字にしたかったので、前段でfugashiを入れましたが、ただローマ字にしたい場合は、pykakasiだけで十分だと思います。
pip install pykakasi
kks.convert(text)
で文節ごとに分割してローマ字の辞書のリストが出来るので、その中から、hepburn
(ヘボン式ローマ字)を取得して結合しています。他にも、orig
(入力テキスト),hira
(ひらがな),kana
(カタカナ),kunrei
(訓令式ローマ字),passport
(パスポート仕様のローマ字)が取得できます。
import pykakasi
kks = pykakasi.kakasi()
kana = "ニッポンゴワムズカシー"
words = kks.convert(kana)
print("words",words, type(words))
result = []
for w in words:
result.append(w['hepburn'])
roman = "".join(result)
print(roman)
このライブラリだと長音府が母音に変換されてしまう(「シー」⇒「shii」など)のですが、そのままにしてほしかったので、前段と同じく、re.sub関数を使って、カタカナの部分だけ、pykakasiを適応しました。
import pykakasi
import re
kks = pykakasi.kakasi()
text = "ニッポンゴワムズカシー"
def convert_to_roman(match):
result = kks.convert(match.group())
return "".join([r['hepburn'] for r in result])
# 正規表現でカタカナを見つけてローマ字に変換
roman = re.sub(r'[ァ-ン]+', convert_to_roman, text)
print(roman)
nippongowamuzukashiー
ローマ字に変換できました
全体
qumcumに喋らせるにあたって、qumcumの仕様として他に調整が必要な個所がいくつか有ったので、それらを追加すると全体のコードは以下の様になります。AIがどんなテキストを送ってくるか予想が付かないので、いろんなパターンを考慮していたら長くなってしまいました。(まだ、漢字の固有名詞など対応で来てないですが諦めました)
import fugashi
import re
import pykakasi
import unicodedata
fugger = fugashi.Tagger()
kks = pykakasi.kakasi()
# 日本語をローマ字に変換する関数
def japanese_to_roman(text):
# 半角・全角を正規化
text = unicodedata.normalize('NFKC', text)
# fugashiで日本語の部分を読み(カタカナ)に変換
def convert_to_yomi(match):
# 辞書に読みが無い場合(固有名詞など)はそのままの文字を返す
return "".join([w.feature.pron if w.feature.pron else w.surface for w in fugger(match.group())])
# 正規表現でテキスト中の日本語を見つけるて読みに変換する
yomi = re.sub(r'[ぁ-んァ-ン一-龥々〆ヵヶー]+', convert_to_yomi, text)
# アルファベットはアルファベットの読みに変換
alphabet = str.maketrans({
'A': 'エー', 'B': 'ビー', 'C': 'シー', 'D': 'ディー', 'E': 'イー',
'F': 'エフ', 'G': 'ジー', 'H': 'エイチ', 'I': 'アイ', 'J': 'ジェイ',
'K': 'ケイ', 'L': 'エル', 'M': 'エム', 'N': 'エヌ', 'O': 'オー',
'P': 'ピー', 'Q': 'キュー', 'R': 'アール', 'S': 'エス', 'T': 'ティー',
'U': 'ユー', 'V': 'ヴィー', 'W': 'ダブリュー', 'X': 'エックス', 'Y': 'ワイ', 'Z': 'ゼット',
})
yomi = yomi.upper().translate(alphabet)
# pykakasiでカタカナをローマ字に変換
def convert_to_roman(match):
result = kks.convert(match.group())
return "".join([r['hepburn'] for r in result])
# 正規表現でカタカナを見つけてローマ字に変換
roman = re.sub(r'[ァ-ン]+', convert_to_roman, yomi)
# 不要な記号を削除
roman = re.sub(r'[^a-zA-Z0-9ー、。?]', '', roman)
# 数字を <NUMK VAL=(数値)> の形式に変換
def replace_match(match):
return f"<NUMK VAL={match.group()}>"
# 正規表現で数字部分を見つけて変換
num = re.sub(r'\d+', replace_match, roman)
# その他の文字を変換
translation_table = str.maketrans({
'、': ',',
'。': '.',
'ー': '-',
'?': '?',
})
return num.translate(translation_table)
補足
こんなに長々と変換処理を書くなら、AIでやれば良いんじゃない?って思うかもしれません。
試してみました。コードは省略しますが、以下の画像の通り、間違えることが多いですし、毎回回答が変わって不安定ですし、実行速度も遅いです。(ライブラリを使った方法は、0.002秒ぐらいです。)
プロンプトなどを改善すれば、実行速度以外は良くなるかもしれませんが、私にはできませんでした。複雑で厳格なルールに則った変換はAIはまだまだ苦手そうです。
まとめ
今回は、pythonで日本語のテキストを読み方に沿ったローマ字に変換する方法について書きました。割と今回使ったロボットの仕様のせいで複雑になってるコードが多くなってしまいました。同じロボットを使ってるって方にはそのまま参考になるかもしれませんが、それ以外の方は適宜必要な個所だけ参考にしていただければと思います。
関連記事
このコードを使ったデモについて(つまり、AIのデモのAIの部分の解説)は、以下にリンク張るので、良ければ読んでいってください。
「Qumcum×生成AI」ロボットで試すAIエージェント:Python