BERTにおけるテキストクレンジングを紹介[BERT]
汎用言語モデルBERTを使用する際に,テキストクレンジングを行う関数を見つけ,読んでみると勉強になったので記事にしてみた.
参考にしたのは,Google Researchの実装である.
まず,BERTのコード(tokenization.pyのFullTokenizerクラスのtokenize関数の中)で見つけたテキストクレンジングの関数を以下に貼る.
def _clean_text(self, text):"""Performs invalid character removal and whitespace cleanup on text."""output = []for char in text:cp = ord(char)if cp == 0 or cp == 0xfffd or _is_control(char):continueif _is_whitespace(char):output.append(" ")else:output.append(char)return "".join(output)処理の内容としては,不正な文字と空白文字を除去するシンプルな実装だが,処理の際,ordでASCIIコード*1に変換してから*2,ASCIIコードで条件分岐をさせている.また,6行目の条件分岐のcp==0はNull文字*3を表し,cp==0xfffdは�を表す*4.このcp==0xfffdは文字化けを判定する役目を果たしており,生テキストを扱う際には非常に有用である.さらに,もう1つの条件 _is_controlは,以下の関数によって定義される.
def _is_control(char):"""Checks whether `chars` is a control character."""# These are technically control characters but we count them as whitespace# characters.if char == "\t" or char == "\n" or char == "\r":return Falsecat = unicodedata.category(char)if cat in ("Cc", "Cf"):return Truereturn False_is_controlは,制御文字かどうかをチェックする関数である.まずは,“¥t”(タブ区切り),“¥n”(改行),“¥r”(復帰) *5 であるかどうかを判定.次にPythonのunicodedata.categoryによって,大文字か小文字か数字かなどのカテゴリを判定*6.もし,カテゴリが”Cc”(C0, C1 control codes)か”Cf”(format control character)なら制御文字フラグを立てる.
以上3つの条件で文字をチェックすることで,不正な文字を判定している.
さて,_clean_textに戻って,次の処理,_is_whitespaceをみてみよう.
def _is_whitespace(char):"""Checks whether `chars` is a whitespace character."""# \t, \n, and \r are technically contorl characters but we treat them# as whitespace since they are generally considered as such.if char == " " or char == "\t" or char == "\n" or char == "\r":return Truecat = unicodedata.category(char)if cat == "Zs":return Truereturn False_is_whitespaceは,空白文字かどうかを判定するための関数である.5行目の説明は先ほどしたため省略する.7行目について,ここでは,Pythonのunicodedata.categoryで”Zs”(Space character)にカテゴライズされた場合,空白文字フラグを立てる処理になっている.そして,_clean_textでは,_is_whitespaceで空白文字フラグが立つと,半角スペースに置き換わる処理になっているようだ.
以上で_clean_textの説明は終わりだ.一旦,ASCIIコードに変換してから条件分岐を行うところが非常に参考になった.
また,BERTのtokenization.pyでは,句読点を判定する関数も存在する.
def _is_punctuation(char):"""Checks whether `chars` is a punctuation character."""cp = ord(char)# We treat all non-letter/number ASCII as punctuation.# Characters such as "^", "#034;, and ”`” are not in the Unicode
Punctuation class but we treat them as punctuation anyways, for
consistency.
if ((cp >= 33 and cp <= 47) or (cp >= 58 and cp <= 64) or
(cp >= 91 and cp <= 96) or (cp >= 123 and cp <= 126)):
return True
cat = unicodedata.category(char)
if cat.startswith(“P”):
return True
return False
_is_punctuationは,句読点を判定する関数だが,8行目でordで変換したASCIIコードを不等号で条件分岐させている.ASCIIコードに変換する利点は,数値で範囲を指定できるところにあるのかもしれない.また,12行目でカテゴリ”P”(Punctuation)系に分類される文字が現れた場合に句読点フラグを立てている.ASCIIコードとunicodedata.categoryと2つの観点で判定を行なっているようだが,これには何の意味があるのだろうか?もしかすると,ASCIIコードで引っかからない文字もしくはordで変換できない文字が存在するのかもしれない.
また,_is_chinese_charでは,中国語を判定する処理を行なっていた.
def _is_chinese_char(self, cp):"""Checks whether CP is the codepoint of a CJK character."""# This defines a "chinese character" as anything in the CJK Unicode block:# https://en.wikipedia.org/wiki/CJK_Unified_Ideographs_(Unicode_block)## Note that the CJK Unicode block is NOT all Japanese and Korean characters,# despite its name. The modern Korean Hangul alphabet is a different block,# as is Japanese Hiragana and Katakana. Those alphabets are used to write# space-separated words, so they are not treated specially and handled# like the all of the other languages.if ((cp >= 0x4E00 and cp = 0x3400 and cp = 0x20000 and cp = 0x2A700 and cp = 0x2B740 and cp = 0x2B820 and cp = 0xF900 and cp = 0x2F800 and cp
[*1](#fn-4970baa5):ASCIIコード表 [http://www3.nit.ac.jp/~tamura/ex2/ascii.html](http://www3.nit.ac.jp/~tamura/ex2/ascii.html)
[*2](#fn-8eb14496):ord後にchrを実行すれば,元に戻せる [https://python.civic-apps.com/char-ord/](https://python.civic-apps.com/char-ord/)
[*3](#fn-45cab8b4):Null文字ってなんのためにあるの? [http://www.altima.jp/column/fpga_edison/null.html](http://www.altima.jp/column/fpga_edison/null.html)
[*4](#fn-81cc2fa8):Replacement Character [https://www.fileformat.info/info/unicode/char/fffd/index.htm](https://www.fileformat.info/info/unicode/char/fffd/index.htm)
[*5](#fn-ed9ae912):LFとCR [https://marusunrise2.blogspot.com/2014/06/lfcrcrlf.html](https://marusunrise2.blogspot.com/2014/06/lfcrcrlf.html)
[*6](#fn-da071a26):General Category [Value](http://d.hatena.ne.jp/keyword/Value) [http://www.unicode.org/reports/tr44/#General_Category_Values](http://www.unicode.org/reports/tr44/#General_Category_Values)
[*7](#fn-6cd8268c):[文字コード](http://d.hatena.ne.jp/keyword/%CA%B8%BB%FA%A5%B3%A1%BC%A5%C9)表 [シフトJIS](http://d.hatena.ne.jp/keyword/%A5%B7%A5%D5%A5%C8JIS) [http://charset.7jp.net/sjis.html](http://charset.7jp.net/sjis.html)