自然言語処理で欠かせない前処理としてテキストの分かち書きとID化があります。分かち書きはテキストを分割するプロセスであり、文字や単語、サブワードといった単位でテキストを分割します。これらの分割後の要素はトークンと呼ばれます。一方、ID化はトークンを数値に変換するプロセスです。数値に変換することで、機械学習アルゴリズムにトークンを与えることができるようになります。本記事では、これらのプロセスをTensorFlow Datasetsの機能を使って実現する方法について紹介します。
準備
この記事ではTensorFlow Datasetsを使って分かち書きとID化を行います。TensorFlow DatasetsはIMDbやMNISTなどの機械学習でよく使われるデータセットを簡単に使い始められる形で提供してくれるパッケージです。その中の一部の機能として、テキストの分かち書きとID化をするための機能を提供しています。使い始めるために、以下のようにしてTensorFlow Datasetsをインストールしておく必要があります。
$ pip install tensorflow-datasets
また、後で日本語を扱うために、Janomeもインストールしておきます。
$ pip install janome
英語の場合
ではまずは、英語で書かれたテキストの分かち書きとID化をやってみましょう。はじめに、Tokenizer
を使ってテキストをトークンに分割します。得られたトークンからボキャブラリを作成し、それをTokenTextEncoder
に与えてエンコーダを作成します。
import tensorflow_datasets as tfds example_text = 'She sells seashells by the seashore' tokenizer = tfds.features.text.Tokenizer() tokenized_text = tokenizer.tokenize(example_text) vocabulary_list = list(set(tokenized_text)) encoder = tfds.features.text.TokenTextEncoder(vocabulary_list, lowercase=True)
エンコーダを作成したら、encode
メソッドを呼んで、トークンをID化します。また、ID化したトークンを文字列に戻すためにdecode
メソッドも呼びます。
encoded_example = encoder.encode(example_text) decoded_example = encoder.decode(encoded_example)
変換結果を表示するためのコードを書きます。
>>> print(tokenized_text) >>> print(encoded_example) >>> print(decoded_example) >>> print(encoder.vocab_size)
以下のような結果が得られます。分かち書きとID化ができていることを確認できます。
['She', 'sells', 'seashells', 'by', 'the', 'seashore'] [3, 4, 6, 2, 1, 5] she sells seashells by the seashore 8
作成したエンコーダはsave_to_file
メソッドを呼んで保存します。これにより、ボキャブラリを保存することができます。
filename_prefix = 'encoder' encoder.save_to_file(filename_prefix) encoder = tfds.features.text.TokenTextEncoder.load_from_file(filename_prefix) print(encoder.encode(example_text)) # [3, 4, 6, 2, 1, 5]
日本語の場合
日本語の場合もID化するところまでは変わりません。トークナイザーをJanomeに変えるだけです。
from janome.tokenizer import Tokenizer example_text = '太郎は花子に花束を渡した。' tokenizer = Tokenizer(wakati=True) tokenized_text = tokenizer.tokenize(example_text) vocabulary_list = list(set(tokenized_text)) encoder = tfds.features.text.TokenTextEncoder(vocabulary_list, tokenizer=tokenizer) encoded_example = encoder.encode(example_text)
変換結果を表示するためのコードを書きます。
>>> print(tokenized_text) >>> print(encoded_example) >>> print(decoded_example) >>> print(encoder.vocab_size)
結果は以下のようになります。
['太郎', 'は', '花子', 'に', '花束', 'を', '渡し', 'た', '。'] [3, 1, 4, 6, 7, 8, 9, 5, 2] 太郎 は 花子 に 花束 を 渡し た 。 11
しかし、エンコーダーを保存するときに問題が起きます。
>>> encoder.save_to_file(filename_prefix) AttributeError: 'Tokenizer' object has no attribute 'save_to_file'
これはJanomeのTokenizerにsave_to_file
メソッドが無いことにより起きる例外です。エンコーダーのsave_to_file
メソッドを呼ぶと、その内部でTokenizer
のsave_to_file
メソッドを呼び出します。しかし、Janomeにはsave_to_file
メソッドはありません。その結果、メソッドが存在しないというエラーが出るわけです。
対策として、tfds.features.text.Tokenizer
クラスを継承したトークナイザーを用意します。そこに何もしないsave_to_file
メソッドをもたせます。また、用意したトークナイザーを読み込めるようにするために、tfdds.features.text.TokenTextEncoder
を継承したエンコーダーを作成します。
class JapaneseTokenizer(tfds.features.text.Tokenizer): def __init__(self, alphanum_only=True, reserved_tokens=None): super().__init__(alphanum_only, reserved_tokens) self._t = Tokenizer(wakati=True) def tokenize(self, s): return self._t.tokenize(s) def save_to_file(self, filename_prefix): pass @classmethod def load_from_file(cls, filename_prefix): return cls() class ExtendedEncoder(tfds.features.text.TokenTextEncoder): @classmethod def load_from_file(cls, filename_prefix): filename = cls._filename(filename_prefix) vocab_lines, kwargs = cls._read_lines_from_file(filename) has_tokenizer = kwargs.pop("has_tokenizer", False) if has_tokenizer: kwargs["tokenizer"] = JapaneseTokenizer.load_from_file(filename) return cls(vocab_list=vocab_lines, **kwargs)
定義したクラスを使うことで、作成したボキャブラリが保存されるようになります。
tokenizer = JapaneseTokenizer() tokenized_text = tokenizer.tokenize(example_text) vocabulary_list = list(set(tokenized_text)) encoder = ExtendedEncoder(vocabulary_list, tokenizer=tokenizer) encoded_example = encoder.encode(example_text) encoder.save_to_file('encoder') encoder = ExtendedEncoder.load_from_file('encoder') print(encoded_example) # [3, 1, 4, 6, 7, 8, 9, 5, 2]
おわり。まだ様子見が必要。