はじめに
私はK-POPが好きでよく聴くのですが、やはり日本語や英語の曲と比べると歌詞の意味がよく分からなくて十分に楽しめないという課題を抱えていました。
一から学習書を用いて勉強するのが王道かと思いますが、通常の学習書に載っている会話や単語を見てもあまりスッと入ってこなくて学習のモチベーションを保つのに苦労しました。
そこで、K-POPの歌詞から単語を抽出して覚えるために韓国語の形態素解析と単語別の翻訳をしてみようと思いました。
韓国語学習者の方にとって以外は少々読みづらい内容となっているかもしれませんがご容赦ください。
利用技術
- プログラミング言語: Python
- 形態素解析: mecab-ko, open-korean-text
- Pythonライブラリ: KoNLPy, python-mecab-ko
- 翻訳: GASのLanguageApp
KoNLPyとは
https://konlpy.org/en/latest/
韓国語の自然言語処理を行うためのPythonのパッケージです。名称はKorean Natural Language Processing in Python
の略語となっています。
GitHubのスター数も1.2kとなっておりPythonで韓国語の形態素解析をお手軽に行うライブラリとしては現在最もポピュラーな手段かと思います。
taggerの選定
POS tagging (Part-of-Speech Tagging/品詞タグ付け)、つまり文章に品詞を振るために数種類の選択肢からtaggerを選ぶ必要があります。
今回はKoNLPy公式にもあるように最も実行時間の短い日本語の形態素解析でもおなじみのMeCabをカスタマイズしたmecab-ko(以下、MeCabと記載)と、次いで実行時間が短く、辞書の中身も違うと思われるTwitter社製のopen-korean-text(以下、Oktと記載)を利用します。
公式に掲載されている手順に従い環境構築を行ったあと下記のように実行してみると、文章が分かち書き(トークナイズ)されて、MAG
やVV
といった単語が割当されているのがわかるかと思います。こちらのMAG
やVV
は品詞(副詞と動詞)を表しています。これをタギングと呼びます。
$ python
>>> from konlpy.tag import Mecab
>>> from konlpy.utils import pprint
>>> mecab = Mecab()
>>> pprint(mecab.pos("빨리 가자."))
[('빨리', 'MAG'), ('가', 'VV'), ('자', 'EF'), ('.', 'SF')]
デモ
ここではaespaのBlack Mambaという曲を題材に試してみようと思います。
曲中にBlack Mamba(ヘビ)についての歌詞が出てくるので、こちらをサンプルに使用します。
널 유혹해 삼킨 건 Black Mamba
君を誘惑して飲み込んだのは Black Mamba
という意味です。
KoNLPy - MeCabの場合
mecab_poslist = Mecab().pos("널 유혹해 삼킨 건 Black Mamba")
print(mecab_poslist)
# 結果
[('널', 'NP+JKO'), ('유혹', 'NNG'), ('해', 'XSV+EC'), ('삼킨', 'VV+ETM'), ('건', 'NNB+JX'), ('Black', 'SL'), ('Mamba', 'SL')]
結構細かめに、一つのトークンをNP+JKO
(代名詞+目的格助詞)のように語幹と活用語尾や合成された助詞の品詞を個別にタギングしてくれます。
KONLPy - Oktの場合
okt_poslist = Okt().pos("널 유혹해 삼킨 건 Black Mamba", norm=False, stem=True)
print(okt_poslist)
# 結果
[('널', 'Noun'), ('유혹', 'Noun'), ('하다', 'Verb'), ('삼키다', 'Verb'), ('건', 'Noun'), ('Black', 'Alpha'), ('Mamba', 'Alpha')]
stem
オプションを指定することで語幹+다で活用されている動詞や形容詞の原形を作ってくれるところが高機能でいいですね。
ただ、代名詞がNoun(名詞)のように大きな括りに入ってしまったり、活用語尾について分類されていないところはMeCabに軍配が上がるかと思います。
"널(neol/君を)" を "너(neo/君)"+ "을(eul/〜を)" に分解したい
韓国語には二つの品詞が合わさって一つの文字になるリエゾンの文字バージョンのような特徴があり、これも慣れている方は分かりますが初学者にとっては合成された文字であること分かりづらいです。
通常はトークナイズした結果に対して品詞のタギングを行いますが、合成された1トークンをさらに分解することができないか調べてみました。
MeCabの仕様を調べる中で、mocab-ko-dic辞書の構成(各項目のことを"素性"と呼びます)が下記のようになっており生のMeCabを使用すれば複数の品詞が合成されたトークンを分解した内容も取得できることが分かりました。(CSVの一番右です)
$ mecab -d /usr/local/lib/mecab/dic/mecab-ko-dic
mecab-ko-dic은 MeCab을 사용하여, 한국어 형태소 분석을 하기 위한 프로젝트입니다.
mecab SL,*,*,*,*,*,*,*
- SY,*,*,*,*,*,*,*
ko SL,*,*,*,*,*,*,*
- SY,*,*,*,*,*,*,*
dic SL,*,*,*,*,*,*,*
은 JX,*,T,은,*,*,*,*
MeCab SL,*,*,*,*,*,*,*
을 JKO,*,T,을,*,*,*,*
사용 NNG,행위,T,사용,*,*,*,*
하 XSV,*,F,하,*,*,*,*
여 EC,*,F,여,*,*,*,*
, SC,*,*,*,*,*,*,*
한국어 NNG,*,F,한국어,Compound,*,*,한국/NNG/*+어/NNG/*
형태소 NNG,*,F,형태소,Compound,*,*,형태/NNG/*+소/NNG/*
분석 NNG,행위,T,분석,*,*,*,*
을 JKO,*,T,을,*,*,*,*
하 VV,*,F,하,*,*,*,*
기 ETN,*,F,기,*,*,*,*
위한 VV+ETM,*,T,위한,Inflect,VV,ETM,위하/VV/*+ᆫ/ETM/*
프로젝트 NNG,*,F,프로젝트,*,*,*,*
입니다 VCP+EF,*,F,입니다,Inflect,VCP,EF,이/VCP/*+ᄇ니다/EF/*
. SF,*,*,*,*,*,*,*
EOS
Pythonのライブラリにjonghwanhyeon/python-mecab-koというものがあり、これらの結果を返すparse
APIがあるようなのでそちらを使用してみることにしました。
python-mecab-ko - MeCabの場合
import mecab
tokens = mecab.MeCab().parse("널 유혹해 삼킨 건 Black Mamba")
print(tokens)
# 結果(+改行)
[('널', Feature(pos='NP+JKO', semantic=None, has_jongseong=True, reading='널', type='Inflect', start_pos='NP', end_pos='JKO',
expression='너/NP/*+ᆯ/JKO/*')),
('유혹', Feature(pos='NNG', semantic='행위', has_jongseong=True, reading='유혹', type=None, start_pos=None, end_pos=None,
expression=None)),
('해', Feature(pos='XSV+EC', semantic=None, has_jongseong=False, reading='해', type='Inflect', start_pos='XSV', end_pos='EC',
expression='하/XSV/*+아/EC/*')),
('삼킨', Feature(pos='VV+ETM', semantic=None, has_jongseong=True, reading='삼킨', type='Inflect', start_pos='VV', end_pos='ETM',
expression='삼키/VV/*+ᆫ/ETM/*')),
('건', Feature(pos='NNB+JX', semantic=None, has_jongseong=True, reading='건', type='Inflect', start_pos='NNB', end_pos='JX',
expression='것/NNB/*+ᆫ/JX/*')),
('Black', Feature(pos='SL', semantic=None, has_jongseong=None, reading=None, type=None, start_pos=None, end_pos=None, expression=None)),
('Mamba', Feature(pos='SL', semantic=None, has_jongseong=None, reading=None, type=None, start_pos=None, end_pos=None,
expression=None))]
当初の目論み通り、널
が'너/NP/*+ᆯ/JKO/*'
のように二つの品詞が合成されたトークンであるという情報を取得できました。
トークンごとに妥当な日本語訳を付けたい
翻訳にはGASのLanguageApp
が無料で利用できたためそちらを使用しました。
単純にトークンごとに翻訳をしてみるとやはり文脈がないため意味不明な結果が返ってきます。
널・・・ヌル
유혹・・・誘惑
해・・・太陽
삼킨・・・飲み込む
건・・・銃
Black・・・ブラック
Mamba・・・マンバ
そこで、トークン単体で意味が取得しやすい品詞(名詞類、動詞、形容詞、副詞など)について翻訳を行い、助詞が合成されている名詞類(タグがNから始まるもの)は原形で翻訳してみます。
널・・・あなた
유혹・・・誘惑
해・・・(対象外)
삼킨・・・飲み込む
건・・・もの
Black・・・(対象外)
Mamba・・・(対象外)
今度はだいぶ意味が通るようになったのではないでしょうか?
ここで널
が「あなたを」と翻訳されるようになったらだいたい実現したいことが表現できるのであと一歩です。
最後にこれまで試した内容をまとめて見やすくした結果をコーディングし出力しました。
"tokens": [
{
"token": "널",
"stem": "너",
"suffix": "*+ᆯ",
"translation": "あなた",
"word_class": "代名詞+目的格助詞"
},
{
"token": "유혹",
"stem": null,
"suffix": null,
"translation": "誘惑",
"word_class": "一般名詞"
},
{
"token": "해",
"stem": "하다",
"suffix": "*+아",
"translation": null,
"word_class": "動詞の接尾辞+接続語尾"
},
{
"token": "삼킨",
"stem": "삼키다",
"suffix": "*+ᆫ",
"translation": "飲み込む",
"word_class": "動詞+連体形語尾"
},
{
"token": "건",
"stem": "것",
"suffix": "*+ᆫ",
"translation": "もの",
"word_class": "依存名詞+補助詞"
},
{
"token": "Black",
"stem": null,
"suffix": null,
"translation": null,
"word_class": "外国語"
},
{
"token": "Mamba",
"stem": null,
"suffix": null,
"translation": null,
"word_class": "外国語"
}
]
さいごに
今回は形態素解析+品詞タグづけをいろいろな方法で試してみました。
やってみて分かったのは、韓国語学習において文を形態素の単位で分解すると、トークンや文字をさらに分解したいパターンとちょっと分解の単位が細かすぎて翻訳に困るパターンの両方が発生することです。
構文解析など他にもいろいろなことをやってみたいと思うので実現できればまた記事にしていこうと思います。