nikkie-ftnextの日記

イベントレポートや読書メモを発信

huggingface/tokenizersのNormalizer観察記 〜処理の部品化と統一されたインターフェース〜

はじめに

う〜ん、おいし〜😋1 、nikkieです🐟

PythonOSSソースコードを読むのが好きで、そこで得られた知見(新しく知った書き方や設計例)を実装の参考にします。
自然言語処理の前処理について参考にしたく、huggingface/tokenizersソースコード(厳密には型定義のスタブ)を読みました。
今回は読んで考えたことをメモレベルでアウトプットします。

目次

huggingface/tokenizers

ライブラリtransformersで有名なHugging Face社ですが、他にもdatasetsevaluatetokenizersなどを公開しています。
tokenizersはTransformer系モデルのトークナイザを提供します2

tokenizersライブラリの非常にニッチな領域にフォーカスします。
文字列をnormalizeする処理群です。

Normalizersのソースコードリーディング

tokenizers自体はRustで実装されています(pip installするのはPythonバインディングの認識)。
Rustの実装は満足に読めないのですが、リポジトリに置かれたpyiファイルを見るだけでも、設計について気付きがありました。

pyiファイルについてはスタブ(型の定義だけ記載したファイル)という理解です3

すべてのnormalizerのベースクラスNormalizer

以下の2つのメソッドが定義されています。

  • normalizeメソッド:NormalizedStringオブジェクトをインプレースにnormalizeする
  • normalize_strメソッド:strオブジェクトをnormalizeしたstrオブジェクトを返す

Normalizerクラスを継承した具体的なnormalizerたち

Lowercase

小文字にするnormalizerです。

NFKC

正規形NFKCでUnicode正規化するnormalizerです。
unicodedata.normalize4相当の処理と思われます5

他にもまだまだ(列挙)

以下のような具体的なnormalizerがあります:

  • Replace
  • Strip
  • StripAccents
  • NFC
  • NFD
  • NFKD
  • Nmt
  • Precompiled

Normalizerクラスを継承することで各種正規化処理はインターフェースが揃っています。

いくつかの正規化処理をまとめるnormalizer:Sequence

小文字化+NFKCでUnicode正規化のように、複数のnormalizerをまとめて適用したいケースもあります。
そんなときに重宝するのがSequence

Allows concatenating multiple other Normalizer as a Sequence.
All the normalizers run in sequence in the given order

(nikkie訳)Sequence以外のnormalizer複数を結合することを可能にします。
与えられた順番でnormalizerが適用されます。

Sequenceの初期化では、まとめたいnormalizerのリストを渡します。
中では、順番に適用する実装となっているのでしょう。

Sequence自体はNormalizerクラスを継承していますから、normalizeメソッドとnormalize_strメソッドを持ちます。

インターフェースが揃った各種正規化処理、これらを部品のように組み合わせて、複数をまとめたnormalizerをSequenceで実現していると理解しました。
Sequence自体も、部品としている正規化処理とインターフェースが揃っています(Sequenceオブジェクトを渡して新しいSequenceとしてインスタンス化もできるかもしれませんね)。

BertNormalizer

Sequenceを使う具体例と考えられるのがBertNormalizer
初期化時に以下の引数(いずれもbool)を指定します。

  • clean_text
  • handle_chinese_chars
  • strip_accents
  • lowercase

これらのフラグの値に応じて、Sequenceインスタンスを初期化して、それをBertNormalizerに持たせる形で実装できそうですよね。
例えばstrip_accentslowercaseがともにTrueならば、Sequence([StripAcccents(), Lowercase()])を持たせるということです。

Pythonで実装を試してみた

気づいたことを試してみました。

終わりに

huggingface/tokenizersのnormalizersについてstubファイルを元に設計を考えました。
参考になったのは2点です:

  • 処理の部品化:具体的なnormalize処理を表す個々のクラス、そしてそれらをまとめられるSequence
  • 統一されたインターフェース:どのnormalizerもベースクラスNormalizerを継承しており、normalizenormalize_strメソッドを持つ

直近のミノ駆動本_読書pyで「ポリシーパターン」を知ったのですが、個々のnormalizerの部品化には通底するものを感じます。
思考がまとまったら(もしくは整理する目的で)今後アウトプットしてみたいなと思います。


  1. この冬、白い砂のアクアトープが全部見られる! 10:00のあたりです🐟。ここのくくるの表情、ほんとよい〜
  2. 今回はこれ以上は立ち入らないのですが、BERTの事前訓練をColabで動かしてみました(『Transformerによる自然言語処理』3章写経) - nikkie-ftnextの日記などで扱いました
  3. https://docs.python.org/ja/3/library/unicodedata.html#unicodedata.normalize
  4. CはcomposeのC(DはdecomposeのD)とUnicode正規化も最近学びがあったのですが、そのアウトプットはまたの機会に…