ナード戦隊データマン

機械学習, 自然言語処理, データサイエンスについてのブログ

括弧の文法エラーをチェックする30行のコード

括弧は、始括弧と終括弧のペアによって機能する文法です。括弧の文法エラーは単純なルールベースで検出できると思ったため、コードを書きました。

カッコの文法規則

カッコには以下の文法規則があります。

  1. 始括弧が出現したら、対応する終括弧が出現する必要がある。
  2. 終括弧は対応する始括弧が出現するまで出現してはならない。
  3. 出現順とは逆順に閉じる。

このルールを30行程度のコードで記述します。

コード

class JQuoteChecker:
    def __init__(self):
        self._sgroups = [
            '「', '『', '[', '【', '〖', '〘', '〚', '《', '〈', '[', '〔', '(',
            '(', '⦅', '{', '{'
        ]
 
        self._egroups = [
            '」', '』', ']', '】', '〗', '〙', '〛', '》', '〉', ']', '〕', ')',
            ')', '⦆', '}', '}'
        ]
        self._s2e = dict(zip(self._sgroups, self._egroups))
        self._e2s = dict(zip(self._egroups, self._sgroups))
 
    def __call__(self, text):
        return self._parse(text)
 
    def _parse(self, text):
        quotes = []
        for c in text:
            if c in self._e2s:
                if len(quotes) > 0 and quotes[-1] == self._e2s[c]:
                    quotes.pop(-1)
                else:
                    return False
            elif c in self._s2e:
                quotes.append(c)
        if quotes:
            return False
        else:
            return True

テスト

from quote_checker import JQuoteChecker


def test_checker():
    test_data = [
        ("てすと", True),
        ("「これはてすとです", False),
        ("「これはテストです」", True),
        ("」これはてすとです", False),
        ("」これはてすとです「", False),
        ("「これは「てすと」です」", True),
        ("「これは「てすとです」", False),
        ("「これは(てすと)です」", True),
        ("「これは(てすとです」)", False),
        ("「これは(てすとで【ございます】)」", True),
        ('「これ」は「てすと」でござる。', True),
        ('「これ」は」てすと「で「ござる」', False),
        ('あいう、「これは(てすと「です」)」えおか。', True)
    ]

    checker = JQuoteChecker()
    for text, expected in test_data:
        assert checker(text) == expected
pytest -v --cov=quote_checker --cov-report=term-missing

============================= test session starts ==============================
platform linux -- Python 3.6.7, pytest-5.3.0, py-1.8.0, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /root/work/normalize/scripts
plugins: cov-2.8.1
collected 1 item

quote_checker/test_qc.py::test_checker PASSED                            [100%]

----------- coverage: platform linux, python 3.6.7-final-0 -----------
Name                             Stmts   Miss  Cover   Missing
--------------------------------------------------------------
quote_checker/quote_checker.py      20      0   100%
quote_checker/test_qc.py             6      0   100%
--------------------------------------------------------------
TOTAL                               26      0   100%


============================== 1 passed in 0.02s ===============================

考察

これを作っている理由は、自動収集したパラレルコーパス内に不正な括弧パターンが頻出するからです。一方、英語のダブルクオーテーション(")については、出現回数が奇数であれば文法的におかしいとすることはできます。

しかし、これらの括弧やクオーテーションを、文法的に正しい方法で置換するのはもっと困難な問題です。単なるタイポだけではなく、文の一部がアラインメントのフェーズで欠損することによって始括弧が文とともに欠損している場合があります。

このような文を機械翻訳のデータとして使う場合、フィルタリングの過程で除外すべきでしょうか。それとも、そのまま入力するべきでしょうか。あるいは、なんらかの正規化を施すことによってきれいな文だけ残したり変換したりするべきでしょうか。

文の正規化やフィルタリングを評価するためには、それを施したデータと施していないデータでそれぞれ訓練した同一パラメータの機械翻訳のモデルの性能を比較する必要があります。

コーパス中の括弧の傾向

データを眺めてみると、カッコに関するエラーが存在しています。

傾向
先頭の始括弧だけがある 「これは宣戦布告です。
終端の終括弧だけがある 我々にとって大きな価値がある。」
複数の括弧があり、先頭の始括弧に対応する括弧がない 「バブルラップ : 『 もの派』があって、その後のアートムーブメントはいきなり「スーパーフラット」になっちゃうのだが、その間、つまりバブルの頃って、まだネーミングされてなくて、其処を「バブルラップ」って呼称するといろいろしっくりくると思います。
複数の括弧があり、終端の終括弧に対応する括弧がない Pro版(Cinematize 2 Pro)に追加された機能は、DVDからクリップを日常的に取り出す必要がある人なら誰にでも魅力のあるものでしょう。 」
対応関係は存在するが、一方がもう一方に含まれない内容を持つ "Ah" by Kotaro Sekiguchi 「デザインあ展」特別オブジェ 関口光太郎「あ」
一方には括弧がありもう一方には括弧がない "Ai and Ai" Indigo love" 愛と藍 Indigo love」
一方は括弧が閉じているが、もう一方は閉じていない "And with that, I now dedicate this Advanced Organization and Saint Hill Africa in the name of L. Ron Hubbard." この上級オーガニゼーションとセントヒル・アフリカは、 L. ロン ハバードの名の下に捧げられます。 」
一方にはネストされた括弧があり、もう一方にはない "Angel of Space, Blue" 「宇宙の天使(青 ) 」
一方の括弧記号がもう一方の括弧記号に一対一対応していない ""Sonare-jou Chapter Matsukaze"" supervised by Ito Masayoshi (published in 2002 by Izumi Shoin) includes the entire pictures of "Utai Ehon Matsukaze" in color and annotations by Kobayashi Kenji. 伊藤正義氏監修『磯馴帖 松風篇 』 ( 平成一四年、和泉書院刊)に『謡絵本松風』として全丁のカラー写真と小林健二氏による解題が載せられている。
ネストが存在しないのに、強調のために『』が使われている 『万年筆祭』を新館7階 イベントスペースにて開催します。
間違ったネスト "This is "primary" rule" he said.

ネストについては、Google翻訳も対処しきれていないです。(すべてダブルクオートでネストするのは誤り。)

https://translate.google.com/#view=home&op=translate&sl=ja&tl=en&text=%E3%80%8C%E3%81%9D%E3%82%8C%E3%81%AF%E3%80%8E%E6%B5%AE%E4%B8%96%E7%B5%B5%E3%80%8F%E3%81%A7%E3%81%99%E3%80%82%E3%80%8D%E3%81%A8%E5%85%88%E7%94%9F%E3%81%AF%E8%A8%80%E3%81%A3%E3%81%9F%E3%80%82

ネストの正式なルールらしきものは、Wikipediaで以下のように書かれています。

In languages that allow for nested quotes and use quotation mark punctuation to indicate direct speech, hierarchical quotation sublevels are usually punctuated by alternating between primary quotation marks and secondary quotation marks.

https://en.wikipedia.org/wiki/Quotation_mark#In_English

ネストする場合は、クォーテーションマーク1とクォーテーションマーク2を交互に使うようです。

Text normalization

text normalizationというタスクがありますが、任意の言語でノイズを正規化するためには、text2text系のモデルを使う方法があります。

この場合、ノイズありの文をソース文、ノイズを正規化した文をターゲット文としたパラレルコーパスを用意し、それを用いてtext2text系のモデルを訓練することで、正規化タスクを解きます。

これには一長一短あるような気はします。

このモデルの精度が高ければ、正規化後の文を翻訳タスクの訓練データとして利用することができるかもしれません。しかも、End2Endで実行でき、手法自体は言語に依存しません。うまくいけば、BLEUは向上する可能性があります。

しかし、正規化モデルの精度が悪い場合、新しいノイズを埋め込んでしまう可能性がある上に、正規化モデルを訓練するためのコーパスを用意する必要があります。

ノイジーテキストに関する正規化については、W-NUTというワークショップ内の情報が参考になるかもしれません。

W-NUT 2019: Workshop on Noisy User-generated Text (at EMNLP)

参考

  1. Quotation mark - Wikipedia
  2. Encoder-Decoder Methods for Text Normalization - ACL Anthology
  3. W-NUT 2019: Workshop on Noisy User-generated Text (at EMNLP)