LCL Engineers' Blog

バス比較なび・格安移動・バスとりっぷを運営する LCLの開発者ブログ

教師データがない文章ラベリングへのアプローチ

この記事はLCL Advent Calendar 202018日目の記事です。

ブログではAndroidの話をしない、Androidアプリエンジニアの高橋です。

前回の自分の投稿で名寄せの機械学習について触れましたが、このケースでは自社内に名寄せ済みの停車地データが蓄積されていたので、それを教師データとして活用することができていました。

しかし、運用開始してから日の浅い新規事業では「教師データが全くない」状態のケースもあるかと思います。

例えば、弊社では主に旅行・交通機関のデータを取り扱っていますが、そもそもデータ自体が一般に公開されていないクローズドデータが比較的多いです。 教師あり学習をやる場合は当然教師データ(ラベル・アノテーション)も必要となるわけですが、それも自分たちでデータセットを作り上げる必要があるケースが大半です。

私が最近取り組んでいるタスクでも「教師データが全くない」ケースにぶち当たったので、それにどう立ち向かっているかを参考までにご紹介したいと思います。

機械学習の技術についての話はしてないので、そちら方面の技術的な話を聞きたいという方はそっ閉じ推奨です。

タスクの説明

自分が取り組んでいるタスクは、「文章に対して適切なラベルをつける」というものです。 1つの文章に対してラベルを複数可能ないわゆるマルチラベル分類です。 文章の数は5千~1万程度、ラベルの数は500~1000程度と仮置きしておきます。

ラベルって何?という定義はここでは曖昧にしておきますが、まぁSNSで見るハッシュタグ的なものと思ってください。

ラベルの数がかなり多く粒度がかなり細かいラベルになるので、実際のところは分類タスクというより固有名詞抽出タスクに近いかもしれません。

例えば、

「富士山散策と山麓周遊 世界文化遺産を巡るツアー 忍野八海や北口本宮冨士浅間神社もあわせて観光できます」

この文章に対しては、(自分の場合は)「富士山」「世界遺産」「冨士浅間神社」というラベルをつけます。

また、文章データは最初から自社内のデータベースに保持していますが、ラベルのマスタデータはこれから自分で用意しないといけない状態です。

自分なりのアプローチ

さて、教師データが全くないのでこのままでは機械学習できません。教師なし学習でのアプローチなど考えましたが、上手くいくかわからないので機械学習は早々と諦めました。(笑)

基本的な方針としては、「運用でカバー」です。ただし、その運用を楽にする仕組みを考えていきます。

5千~1万の文章を一つ一つ読んで、500~1000のラベルから複数の適切なラベルを選択するの手動でやるのはハッキリ言って無理だと思うので、まずは雑でいいので全ての文章に対して機械的にラベルを付与する仕組みを考えます。

そこでパッと思いつくのが、「特定のワードを含んでいたらそれを機械的にラベルづけする」ことです。

例えば、先ほどの例の文章に対して、「富士山」「世界文化遺産」「冨士浅間神社」というラベルをつけることは割と容易にできそうです。(文章にそのワードが含まれているので)

実際にはラベル付けの精度を高めるために、ただの部分一致ではなく形態素解析した上でそのワードが含まれているかをチェックします。

(余談ですが形態素解析に使う辞書はmecab-ipadic-neologdを使うと、新語や複合語にも対応していていい感じにトーカナイズしてくれます。メンテナの方々に感謝。)

ラベルの精度を高める仕組み

上記の文章にあるワードは「世界文化遺産」なので、「世界遺産」というラベルはこのままだと付与できません。

こう言った表記違いの問題に対しては、以下図のように「世界文化遺産」をラベル「世界遺産」の別名として登録します。

f:id:ktx_ku:20201215174142p:plain

このような構造のデータセットを持つことで、「世界文化遺産」、「世界遺産」どちらのワードを含んでいても「世界遺産」のラベルをつけることが可能になります。

しかし全体の文章にどういったワードが含まれていて、どのワード同士が意味的に近似しているのか把握してないと、上記図のようなデータ構造が作れません。

ここでWord2Vecの出番です。自然言語分野の機械学習の経験がある方々にはピンとくるかと思います。Wikipediaなどを事前学習済みのモデルを用いて、ワード同士の近似度を数値化しておけば上記図のようなことが実現できそうです。

注意点としては、Word2Vecの類似度はあくまで参考値なので、実際に別名としてもいいワードなのかは人間の目視で確認すべきです。

自分の場合は上記のようなデータセットを管理画面で登録するようにし、類似度は別名登録のサジェスト機能として活用しています。

f:id:ktx_ku:20201217123405p:plain
管理画面でサジェストしている様子

ラベルのマスタを作る仕組み

タスクの説明で述べたように、ラベル自体も自分たちで作らないといけません。

5千~1万の文章を全て読み通して「ラベルっぽい」ワードを1000個手動で拾い上げるのも中々骨の折れる作業です。

実際、全部の文章を形態素解析した結果から適当にいくつかワードをピックアップしてみると、、、

      word
----------------
 陣馬山
 景信山
 73m
 凍みこんにゃく
 コラーゲン
 宇都宮餃子
 おかき
 蔵出し
 腸
 仕込み
 6歳
 決行
 不具合
 大雨
 乗り降り
 10時間

ラベルとして使えそうなワード、ほとんどないですね。(腸って。。。 ^^;)

この課題に対してもWord2Vecを活用して、ある程度は補助することができます。

サービスのドメインが大雑把に「旅行」なので、それっぽいワードを全体の文章から拾い上げて見ます。 例えば、「絶景」「景色」に近い意味を持つワードを拾ってみると、

topics = ['絶景', '景色']
suggests = []
for word in words: # wordsは形態素解析で取得済み
    if word not in wv: continue
    scores = [(word, wv.similarity(word, topic)) for topic in topics]
    highest = max(scores, key=lambda x: x[1])
    if highest[1] >= 0.4:  # topicsのいずれかに0.4以上の類似度を持つワードを抽出
        suggests.append(highest)
print(suggests[:5])

出力:

[('川下り', 0.41952382), ('ジオラマ', 0.4060647), ('川湯温泉', 0.43732905), ('秘湯', 0.5778554), ('カルデラ湖', 0.4716155)]

さっきよりはラベルっぽい?ワードが出てきました。 実際にはこれを目視で精査して、ラベルのマスタデータとしてデータベースに登録します。

全体の流れ

これまでの話をまとめてフローとして表すと、こんな感じになります。

f:id:ktx_ku:20201217142852p:plain

「目視による作業」のところは弊社独自の管理画面で行っています。

今後の展望

今回は文章に特定のワードが含まれていればラベルを付けるという、半ば強引なやり方でラベル付けをやってみましたが、実際にはついて欲しくないラベルがついてしまったりして、精度が悪いのが欠点でした。 なので実運用ではついているラベルをマニュアル作業で修正するフローもあるのですが、ここでは割愛します。

今後の展望としては、現状のフローでしばらくラベル付け・修正を頑張った後、蓄積されたデータを教師データとして機械学習によるラベル付け自動化を進めたいと考えています。

また、今回は要件的に粒度の小さいラベルでしたが、粒度の大きいカテゴリー分類タスク(例. グルメ、ハイキング、果物狩りなど)にもラベルデータが活用できるのではないかと期待しています。

最後に

以上、自社内に教師データがない状態での文章ラベリングのアプローチをご紹介しました。

旅行・交通分野のデータはまだまだ一般公開されていないクローズドデータが多いので、今後オープンデータが加速していくといいなぁと今回のタスクを通じて思いました。

ご紹介したアプローチが全てのケースでフィットするわけではないですが、何か一つの気付きになれば幸いです。