PyTorch : AllenNLP チュートリアル : Getting Started – 実験の構成 (翻訳)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 09/27/2018 (v0.6.1)
* 本ページは、github 上の allenai/allennlp の Tutorials : Getting Started – Configuring Experiments を
動作確認・翻訳した上で適宜、補足説明したものです:
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
モデルをどのように訓練して評価するかを知った今、実験 configuration ファイル, tutorials/getting_started/simple_tagger.json, をより深く見てみましょう、
configuration は Jsonnet ファイルでこれは実験とモデルのための総てのパラメータを定義します。jsonnet に馴染みがなくても心配しないでください、任意の JSON ファイルは有効な jsonnet です ; 実際に、このチュートリアルで使用する configuration ファイルは丁度 JSON です。
このチュートリアルでは、configuration ファイルの各セクションを詳細に通り抜け、総てのパラメータが意味するところを説明します。
予備: Registrable と from_params
殆どの AllenNLP クラスは Registrable 基底クラスから継承され、これはそれらのサブクラスに名前付けられた registry をそれらに与えます。
これはもし Model(Registrable) 基底クラスを持ち (we do)、サブクラスを次のように修飾する場合 :
@Model.register("custom") class CustomModel(Model): ...
次を使用して CustomModel をリカバーできることを意味します :
Model.by_name("custom")
慣例により、総てのそのようなクラスは from_params ファクトリ・メソッドを持ちます、これは Params オブジェクトからインスタンスをインスタンス化することを可能にします、これは基本的には幾つかの追加された機能を持つパラメータの dict ですがここでは深入りしません。
これが AllenNLP がどのようにそれが必要とするオブジェクトをインスタンス化するために configuration ファイルを使用できるかです。それは (本質的に) 次を行なうことができます :
# Grab the part of the `config` that defines the model model_params = config.pop("model") # Find out which model subclass we want model_name = model_params.pop("type") # Instantiate that subclass with the remaining model params model = Model.by_name(model_name).from_params(model_params)
クラスはそれがロードされるまで登録されませんので、BaseClass.by_name(‘subclass_name’) を使用するどのようなコードもサブクラスのためのコードを既にインポートしていなければなりません。特に、これはひとたび貴方自身の名前付けられたモデルとヘルパー・クラスを作成し始めたら、含まれる allennlp.run コマンドはそれらを知らないことを意味しています。けれども、allennlp.run は単純に allennlp.commands.main 関数まわりのラッパーで、これは貴方のカスタム・クラスの総てをインポートして allennlp.commands.main() を呼び出す貴方自身のスクリプトを単に作成する必要があることを意味します。
Dataset と Instance と Field
Dataset 上でモデルを訓練して評価します。Dataset は Instance のコレクションです。タギング実験では、各 dataset はタグ付けされたセンテンスのコレクションで、各 instance はそれらのタグ付けされたセンテンスの一つです。
instance は Field から成り、それらの各々はモデルに供給するに適した配列として instance のある部分を表します。
タギングのセットアップでは、各 instance は (センテンスの単語/トークンを表わす) TextField と (対応する品詞タグを表わす) SequenceLabelField を含みます。
センテンスで満ちているテキストファイルをどのように Dataset に変換するのでしょう?configuration ファイルで指定される DatasetReader によってです。
DatasetReader
configuration ファイルの最初のセクションは dataset_reader を定義します :
"dataset_reader": { "type": "sequence_tagging", "word_tag_delimiter": "/", "token_indexers": { "tokens": { "type": "single_id", "lowercase_tokens": true }, "token_characters": { "type": "characters" } } },
ここで、名前 “sequence_tagging” のもとで登録された DatasetReader サブクラスを使用することを望むことを指定しました。予想できるように、これは SequenceTaggingDatasetReader サブクラスです。この reader は改行区切りセンテンスのテキストファイルを想定し、そこでは各センテンスは次のように見えます :
word1{wtd}tag1{td}word2{wtd}tag2{td}...{td}wordn{wtd}tagn
幾つかの “単語タグ・デリミタ (区切り文字)” {wtd} と幾つかの “トークン・デリミタ” {td}。
私達のデータ・ファイルは次のように見えます :
The/at detectives/nns placed/vbd Barco/np under/in arrest/nn
これは何故次を指定する必要があるかです :
"word_tag_delimiter": "/",
“token delimiter” のためには何も指定する必要はありません、何故ならばデフォルトの split-on-whitespace で既に正しいからです。
SequenceTaggingDatasetReader.read() のコードを見れば、それは各センテンスをトークンの TextField とタグの SequenceLabelField に変換しています。後者は実際には configurable ではありませんが、前者はトークンをどのように配列に変換するかを示す TokenIndexer の辞書を必要とします。
configuration は 2 つのトークン indexer を指定します :
"token_indexers": { "tokens": { "type": "single_id", "lowercase_tokens": true }, "token_characters": { "type": "characters" } }
最初の “tokens” は SingleIdTokenIndexer で各トークン (単語) を単一の整数としてだけ表します。configuration はまたエンコーディング前にトークンを小文字にすること ; つまり、このトークン indexer はケースを無視すべきであることも指定します。
2 番目の “token_characters” は TokenCharactersIndexer で各トークンを int-エンコードされた文字のリストとして表します。
これは 2 つの異なるエンコーディングを与えることに注意してください。各エンコーディングは名前を持ち、この場合は “tokens” と “token_characters” で、これらの名前は後でモデルから参照されます。
訓練と検証データ
次のセクションはモデルを (その上で) 訓練して検証するためのデータを指定します :
"train_data_path": "https://allennlp.s3.amazonaws.com/datasets/getting-started/sentences.small.train", "validation_data_path": "https://allennlp.s3.amazonaws.com/datasets/getting-started/sentences.small.dev",
それらは貴方のマシン上のローカルパスとしてか、例えば Amazon S3 上でホストされるファイルへの URL として指定できます。後者の場合には、AllenNLP はダウンロード・ファイルを ~/.allennlp/datasets にキャッシュ (そして再利用) して、ETag を使用して新しいバージョンをいつダウンロードするかを決定します。
モデル
次のセクションはモデルを configure します。
The next section configures our model.
"model": { "type": "simple_tagger",
これは、”simple_tagger” として登録された Model サブクラス、SimpleTagger モデルをを使用することを望むことを示します。
そのコードを見れば、それがテキストフィールドの出力を埋め込む TextFieldEmbedder、そのシークエンスを出力シークエンスに変換する Seq2SeqEncoder、そして出力シークエンスを予想されるタグの確率を表わすロジットに変換する線形層から成ることを見るでしょう (最後の層は configurable ではないので configuration ファイルには出現しません)。
Text Field Embedder
最初にテキストフィールド embedder の configuration を見てみましょう :
"text_field_embedder": { "tokens": { "type": "embedding", "embedding_dim": 50 }, "token_characters": { "type": "character_encoding", "embedding": { "embedding_dim": 8 }, "encoder": { "type": "cnn", "embedding_dim": 8, "num_filters": 50, "ngram_filter_sizes": [5] }, "dropout": 0.2 } },
それが TextField で名前付けられたエンコーディングの各々のためのエントリを持つことが見て取れるでしょう。各エントリはその名前でエンコードされたトークンをどのように埋め込むかを示す TokenEmbedder を指定します。TextFieldEmbedder の出力はこれらの embedding(s) の結合です。
“tokens” 入力 (これは入力の小文字化された単語の整数エンコーディングから成ります) は Embedding モジュールに供給されます、これは embedding_dim パラメータで指定されたように、語彙単語を 50-次元空間に埋め込みます。
“token_characters” 入力 (これは各単語の文字の整数シークエンス・エンコーディングから成ります) は TokenCharactersEncoder に供給されます、これは文字を 8-次元空間に埋め込みそして (50 フィルタを使用してまた 50-次元出力を生成する) CnnEncoder を適用します。このエンコーダはまた訓練の間に 20% dropout も使用することを見て取れます。
この TextFieldEmbedder の出力は “tokens” のための 50-次元ベクトルに “token_characters” のための 50-次元ベクトルが結合されます ; つまり、100-次元ベクトルです。
TextFields のエンコーディングと TextFieldEmbedder の両者はこのように configurable ですから、貴方のモデルへの入力として異なる単語表現で実験することは些細なことで、コードの単一行を変更することなく、単純な単語埋め込み、文字レベル CNN で連結した単語埋め込みを切り替えたり、あるいは word-in-context embedding を得るために事前訓練されたモデルを使用することさえできます。
Seq2SeqEncoder
TextFieldEmbedder の出力は “stacked encoder” により処理されます、これは Seq2SeqEncoder である必要があります :
"encoder": { "type": "lstm", "input_size": 100, "hidden_size": 100, "num_layers": 2, "dropout": 0.5, "bidirectional": true }
ここで “lstm” エンコーダは torch.nn.LSTM まわりの単に薄いラッパーで、そのパラメータは PyTorch コンストラクタに単純に渡されます。その入力サイズは前の埋め込み層の 100-次元出力サイズに適合する必要があります。
そして、上述のように、この層の出力は線形層に渡されて、これはどのような configuration も必要としません。モデルについてはこれで総てです。
モデルを訓練する
config ファイルの残りは訓練プロセス専用です。
"iterator": {"type": "basic", "batch_size": 32},
データをパッドしてそれをサイズ 32 のバッチで処理する BasicIterator を使用してデータセットに渡り iterate します。
"trainer": { "optimizer": "adam", "num_epochs": 40, "patience": 10, "cuda_device": -1 } }
最後にそのデフォルトパラメータを持つ torch.optim.Adam を使用して最適化します ; 40 エポックの間訓練を実行します ; 10 エポックの間改善が得られない場合には時期を早めて停止します ; そして CPU 上で訓練します。GPU 上で訓練することを望む場合、cuda_device をそのデバイス id に変更しましょう。一つだけの GPU を持つ場合にはそれは 0 であるべきです。
これが実験全体の configuration です。optimizer、バッチサイズ、埋め込み次元、あるいは任意の他のハイパーパラメータを変更することを望む場合には、行なう必要がある総てのことはこの config ファイルを修正して他のモデルを訓練することです。
訓練 configuration は常にモデル・アーカイブの一部としてセーブされます、これはセーブされたモデルがどのように訓練されたかをいつでも見ることができることを意味します。
以上