端の知識の備忘録

技術メモになりきれない、なにものか達の供養先

句読点のない日本語の文章に句読点を挿入するBERTモデルを作る

概要

Whisperなどで日本語の文字起こしをした後の文章にほとんど句読点が入っておらず、そのまま文字起こし文章を結合すると利用しにくいことがありました。

そこで何らかの手法で句読点を挿入したいと思ったのですが意外と手軽な古典的な手法が見つかりませんでした。このようなタスクはpunctuation restorationと呼ばれるタスクのようです。

ちょっと調べて見つかったのは次のような日本語BERTモデルのMask Fillを逐語的に適応していく方法でした。

qiita.com

この手法でもできなくはないのですが思ったほど精度が良くないこと、さらに処理時間が文章の長さに比例してだんだん無視できないレベルになるので、もう少し効率的な方法がないかと考えました。

実のところChatGPTに適当に「次の句読点の入っていない文章に適当に句読点を挿入してください」という指示を出せばいい感じに句読点を入れてくれるのですが、わざわざこれだけのためにChatGPTを使うのもなぁと思い、BERTモデルを自分で作ってみることにしたというお話。

作成したコード、Weightは下記リポジトリにあります。

huggingface.co

ちょっとした解説

学習データとして次のKaggle データセットを利用しました。夏目漱石の「明暗」の読み上げを行った音声データと文章データが含まれています。このうち文章データのみを利用してBERTモデルを学習しました。

https://www.kaggle.com/datasets/bryanpark/japanese-single-speaker-speech-dataset/data?select=transcript.txt

基本的なコンセプトとしては、句読点がちゃんと入っている文章において、句読点の前の文字であるか否かをラベルとして付与します。

例えば、「今日は、いい天気。」という文章に対して、

[[0,0,1,0,0,0,0],[0,0,0,0,0,0,1]]

のようなラベルを付与し、BERTに対しては「今日はいい天気」という文章を入力として与え、句or読点の前の文字であるか否かを予測させるという流れです。実際にはTokenizerによりスペシャトークンが付与されるので位置がずれますが、基本的な考え方はこのような感じです。

ベースのモデルとしてはtohoku-nlp/bert-base-japanese-char-v3を利用し、出力のhidden layerを取り出して、その後に全結合層を追加したモデルを作ります。

huggingface.co

それぞれのトークンのhidden stateに対し共通のlinear layerを適応し、「、」の前であるか「。」の前であるかを意味する2次元の出力を得る形となり、最終的な出力形状は(num_tokens, 2)となります。

class punctuation_predictor(torch.nn.Module):
    def __init__(self, base_model):
        super().__init__()
        self.base_model = base_model
        self.linear = torch.nn.Linear(768, 2)

    def forward(self, input_ids, attention_mask):
        last_hidden_state = self.base_model(
            input_ids=input_ids, attention_mask=attention_mask
        ).last_hidden_state
        # get last hidden state token by token and apply linear layer
        return self.linear(last_hidden_state)

で、出来上がったものとしては、次のようにprocess_long_textという関数に生で句読点の入っていない日本語を入力すると、適当に句読点を入れてくれるもの。

process_long_text(
        "句読点ありバージョンを書きました句読点があることで僕は逆に読みづらく感じるので句読点無しで書きたいと思います"
    )

先ほどのQiitaの記事で紹介されていた文章で比較してみます。

本実装

句読点ありバージョンを書きました。句読点があることで僕は逆に読みづらく感じるので、句読点無しで書きたいと思います。

学習データは地の文にあまり点が入っていないうえ近代文学なこともあり、結構かけ離れた文章のはずですがそこそこ上手く句読点を入れてくれているように見えます。

自分で使う分には対象の文字起こし文章10000文字くらいをChatGPTに投げて教師データを作成し、専用モデルを学習させることで割と実用的な精度が出ることを確認しています。

今回の文章の量(2000センテンス)であればRTX4090 1枚でだいたい1epoch 45秒, 10epochで10分くらいで学習が終わるので、そこまで時間がかからないのも良いところです。

GPTを使うには文が多すぎる場合などに活用できるモデルというイメージです。

ChatGPT4

句読点ありバージョンを書きました。句読点があることで、僕は逆に読みづらく感じるので、句読点無しで書きたいと思います。

Qiitaで紹介されていた"cl-tohoku/bert-base-japanese-char"のMask Fillを利用する方法

句読点ありバージョンを書きました句読点があることで僕は逆に、読みづらく感じるので、句読点無しで書きたいと思います。