Upgrade to Pro — share decks privately, control downloads, hide ads and more …

NLPの研究を加速させるAllenNLP入門 / Introduction to AllenNLP to Accelerate NLP Research

NLPの研究を加速させるAllenNLP入門 / Introduction to AllenNLP to Accelerate NLP Research

爆速でNLPの研究を進める上でオススメな AllenNLP を紹介します。

■ イベント:【NLP Hacks vol.6】『実装』に特化した、NLP勉強会コミュニティ開催!
https://nlp-hacks.connpass.com/event/250755/

■ 登壇概要
タイトル:NLPの研究を加速させるAllenNLP入門

Shunsuke KITADA

July 22, 2022
Tweet

More Decks by Shunsuke KITADA

Other Decks in Technology

Transcript

  1. NLPの研究を加速させる
    AllenNLP入門
    北田 俊輔
    理工学研究科 応用情報工学専攻 博士後期課程 3 年
    彌冨研究室 所属
    『実装』に特化した、NLPコミュニティ「NLP Hacks」, Jul. 22nd, 2022

    View Slide

  2. ● 理工学研究科 応用情報工学専攻 博士3年
    ● 2021年度から学振特別研究員 DC2 🎓
    研究分野: 人工知能・深層学習
    ● 🤖 自然言語処理 (Natural Language Processing; NLP)
    ○ 文字形状を考慮した自然言語処理
    ■ 日本語の漢字に着目 [Kitada+ AIPRW’18, Aoki+ AACL SRW’20]
    ■ アラビア語のアラビア文字に着目 [Daif+ ACL SRW’20]
    ○ 摂動に頑健で解釈可能な深層学習 [Kitada+ IEEE Access’21, Kitada+ CoRR’21]
    ● 🏥 医用画像処理 (コンピュータビジョン)
    ○ 皮膚画像を用いた画像認識による悪性黒色腫の自動診断システム [Kitada+ CoRR’18]
    ● 📝 計算機広告 (マルチモーダル)
    ○ 配信効果の高い広告クリエイティブの作成支援 [Kitada+ KDD’19] (データサイエンスの最難関国際会議)
    ○ 配信効果の低い広告クリエイティブの停止支援 [Kitada+ Appl. Sci.‘22]
    自己紹介
    北田 俊輔 Shunsuke KITADA
    ホームページ: shunk031.me
    2
    就活中...?

    View Slide

  3. AllenNLP 開発終了のお知らせ
    3

    View Slide

  4. AllenNLP, 開発終了してメンテナンスモードに入るってよ
    ● Disable dependabot, add notice of future sunsetting by
    epwalsh · Pull Request #5685 · allenai/allennlp
    https://github.com/allenai/allennlp/pull/5685
    ○ 2022年の12月16日で allennlp は新規開発終了し
    メンテナンスモードへ
    ● 後継の allenai/tango も allennlp の思想を受け継いでいる
    ○ せっかくチュートリアル資料を用意してきたので、
    追悼の意味も込めて発表します 😂
    4

    View Slide

  5. AllenNLPについて
    5

    View Slide

  6. “ : PyTorchをベースにした
    オープンソースのNLPライブラリ” がやっぱり凄い
    ● 🥰 NLPのための深層学習モデル構築を簡単に
    ○ Allen Institute for AI (AI2)によって開発された
    PyTorchベースの OSS (AI2 はNLPのトップ会議に多数論文採択)
    ○ 高品質な深層 NLP モデルを構築したいと考える
    研究者・エンジニア・学生 などを対象として設計
    ● 🥰 NLPタスクに対する高レベルな抽象化とAPIを提供
    ○ 一般的なコンポーネントや (最新) モデルをサポート
    ○ NLP実験の実行と管理を容易にする、拡張可能な枠組み
    ● 😂 日本語での情報がまだまだ少ない?だから今日改めて紹介!!
    ○ v1.0ぐらいまでの資料しかない(2022/07現在は v2.10.0)
    ■ v2.0 になってからも、allennlp は進化しています
    ○ 便利すぎてロックインされてしまう (良いのか悪いのか…)
    6
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  7. のよさと最近のアップデートを少しだけ
    ● 👍 バグが少なく・爆速で実験用のコードが書ける
    ○ NLP関連のコードを自分で書くと高確率でバグる・遅い
    ■ 可変長のデータの取り扱いは極力ライブラリに頼りたい
    ● 👍 注力すべき実装部分にのみ集中して取り組める
    ○ 興味のある ”モデル構築” や ”データのロード” 部分だけ
    に集中して取り組みたいのに... いろいろ書かなくちゃいけないコードが沢山…
    ○ 学習ループやロギング周りってだいたい面倒(でも重要)
    ■ tensorboard や wandb 用のコードって綺麗に書けないがち…
    ● 👍 Vision&Language の盛り上がりにより NLP の
    ライブラリなのに画像を扱うタスクにも容易に適用可能
    ○ マルチモーダルなタスクや V&L タスクが簡単に
    ■ allennlp のみで画像分類もできちゃいます
    7
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  8. 深層学習モデル (特に NLP)
    の実験コードを書くときに困ること
    ● プロジェクトの root にオレオレスクリプトが生えがち
    ○ 愉快な preprocess.py, preprocess_01.py,
    train.py, predict.py, train_sugoi.py
    みたいな仲間たちが爆誕🔥しやすい
    ● スクリプトのディレクトリ構成になやむ
    ○ とりあえず util.py みたいなものを作って突っ込む!?
    ● vocabulary を管理したり可変長テキストを頑張ったり
    ○ NLP実験におけるバグの温床。いつも秘伝のタレを使う
    ■ padding とか masking まわり、愚直に書くと遅いがち
    ● (固定長の画像を扱うような研究はやりやすそう…)
    ● そのままシュッとAPIサーバ化してデモを作りたい
    ○ なんも考えずに実験コード書いてたら、無理です 😂
    8
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  9. AllenNLP の全体像 と 本日話す範囲
    9
    データ
    セットの
    読み込み
    データ
    セットの
    前処理
    モデルの
    構築
    モデルの
    学習と評価
    モデルの
    API化
    allennlp で
    サポート
    allennlp-server で
    サポート
    allennlp DatasetReader allennlp Model
    $ allennlp train /path/to/config.jsonnet -s /path/to/serialization-dir $ allennlp serve …
    集中して実装するのはここだけ!
    allennlp Trainer
    ● AllenNLP の一般的な実装のお作法
    ○ DatasetReader と Model を実装し、allennlp train でポン!
    ● AllenNLP の枠組みに乗ったいい感じの前処理コマンドの実装法
    ○ Subcommand を使ってプロジェクトrootを汚さずに前処理等を書く!
    ● allennlp-server で学習済みモデルを API 化する方法
    ○ 「そのままそれデモにしてよ〜」という無茶振りに対応する!
    AllenNLPを使う上での
    前処理ベストプラクティス
    のようなものを話します
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  10. 深層学習モデル (特に NLP)
    の実験コードを書くときに困ること
    ● プロジェクトの root にオレオレスクリプトが生えがち 問題
    ➜ AllenNLP の Subcommand を使って、前処理用の
    コマンド (例えば: allennlp preprocess-dataset 的な) を準備する!
    ■ 前処理以外にも必要そうなコマンドを自前で用意可能
    ● スクリプトのディレクトリ構成になやむ 問題
    ➜ AllenNLP ライブラリのディレクトリ構成を踏襲する!
    ■ 深層学習モデルを扱うアプリケーションは全部これでいい
    ● vocabulary を管理したり可変長テキストを頑張ったり 問題
    ➜ すべて AllenNLP に頼る!
    ■ 必要な操作はほとんどすべて AllenNLP にあります
    ● そのままシュッとAPIサーバ化してデモを作りたい 問題
    ➜ allennlp-server を使えばそのまますぐ API 化できる!
    10
    を使うと解決!
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  11. 深層学習モデル (特に NLP)
    の実験コードを書くときに困ること
    ● プロジェクトの root にオレオレスクリプトが生えがち 問題
    ➜ AllenNLP の Subcommand を使って、前処理用の
    コマンド (例えば: allennlp preprocess-dataset 的な) を準備する!
    ■ 前処理以外にも必要そうなコマンドを自前で用意可能
    ● スクリプトのディレクトリ構成になやむ 問題
    ➜ AllenNLP ライブラリのディレクトリ構成を踏襲する!
    ■ 深層学習モデルを扱うアプリケーションは全部これでいい
    ● vocabulary を管理したり可変長テキストを頑張ったり 問題
    ➜ すべて AllenNLP に頼る!
    ■ 必要な操作はほとんどすべて AllenNLP にあります
    ● そのままシュッとAPIサーバ化してデモを作りたい 問題
    ➜ allennlp-server を使えばそのまますぐ API 化できる!
    11
    を使うと解決!
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  12. 深層学習モデル (特に NLP)
    の実験コードを書くときに困ること
    12
    を使うと解決!
    ● スクリプトのディレクトリ構成になやむ
    ➜ AllenNLP ライブラリの
    ディレクトリ構成を踏襲する!
    ■ 深層学習モデルを扱う
    アプリケーションは全部これでいい
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  13. AllenNLP ことはじめ
    13

    View Slide

  14. AllenNLPことはじめ Your first model @AllenNLP Guide
    14
    ● テキスト分類を例に What is text classification
    ● 入出力を定義 Defining input and output
    ● データを読み込む Reading data
    ● DatasetReaderを実装 Making a DatasetReader
    ● Modelを構築 Building your model
    ● Modelを実装 Implementing the model
    - the constructor
    Implementing the model
    - the forward method
    AllenNLP を用いたテキストの分類について説明
    テキスト分類について簡単に説明した後、
    映画のレビューが肯定的な感情を表しているか
    否定的な感情を表しているかを判断する分類器を実装
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  15. AllenNLPことはじめ: テキスト分類を例に
    15
    ● テキスト分類は最も一般的なNLPタスクの一つ
    ○ 入力テキストから、モデルは入力からラベルを予測
    ● テキスト分類のアプリケーションは様々
    ○ スパムフィルタリング、感情分析、トピック検出 等
    アプリケーション どんなタスク? 入力 出力
    スパムフィルタリング スパムメールを検出 電子メール スパム / スパムではない
    感情分析 テキストの極性を検出 ツイート・レビュー ポジティブ / ネガティブ
    トピック検出 テキストのトピックを検出 ニュース・ブログ記事 経済 / 技術 / スポーツ
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  16. AllenNLPことはじめ: 入出力を定義
    16
    ● AllenNLP ではそれぞれのデータは Instance として扱う
    ○ Instance は 1 つ以上の Field からなる
    ■ 各 Field はモデルで使われるデータ (入力/出力) を表す
    ○ Field はテンソルに変換され、モデルに入力される
    ● テキスト分類の場合、入力と出力はシンプル
    ○ 入力はテキストを表す TextField
    ○ 出力はラベルを表す LabelField
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand
    Instance
    # Input
    text: TextField
    # Input
    text: LabelField
    torch.Tensor へ
    torch.Tensor へ

    View Slide

  17. AllenNLPことはじめ: データを読み込む
    17
    ● データを読み込むために DatasetReader を使う
    ○ 生データ (e.g., txt, csv, json) から入出力の
    仕様に合うよう Instance に変換する役割
    # Input
    text: TextField
    # Output
    label: LabelField
    I like this movie a lot! [TAB] positive
    This was a monstrous waste of time [TAB] negative
    AllenNLP is amazing [TAB] positive
    Why does this have to be so complicated? [TAB] negative
    This sentence expresses no sentiment [TAB] neutral
    ● 生データは以下のような形式を仮定: [text] [TAB] [label]
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  18. AllenNLPことはじめ: DatasetReaderを実装
    18
    ● DatasetReader を継承して独自の DatasetReader を実装
    ○ 最低限必要なのは _read() メソッドの override
    @DatasetReader.register('classification-tsv')
    class ClassificationTsvReader(DatasetReader):
    def __init__(self):
    self.tokenizer = SpacyTokenizer()
    self.token_indexers = {'tokens': SingleIdTokenIndexer()}
    def _read(self, file_path: str) -> Iterable[Instance]:
    with open(file_path, 'r') as lines:
    for line in lines:
    text, label = line.strip().split('\t')
    text_field = TextField(self.tokenizer.tokenize(text),
    self.token_indexers)
    label_field = LabelField(label)
    fields = {'text': text_field, 'label': label_field}
    yield Instance(fields)
    ● reader.read(file)を呼ぶと Instance のリストが返却
    ○ 入力ファイルの各行に対して tokenizer を使ってテキストを
    単語に分割し、token_indexers を使って単語IDに変換
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  19. @DatasetReader.register('classification-tsv')
    class ClassificationTsvReader(DatasetReader):
    def __init__(self):
    self.tokenizer = SpacyTokenizer()
    self.token_indexers = {'tokens': SingleIdTokenIndexer()}
    def _read(self, file_path: str) -> Iterable[Instance]:
    with open(file_path, 'r') as lines:
    for line in lines:
    text, label = line.strip().split('\t')
    text_field = TextField(self.tokenizer.tokenize(text),
    self.token_indexers)
    label_field = LabelField(label)
    fields = {'text': text_field, 'label': label_field}
    yield Instance(fields)
    AllenNLPことはじめ: DatasetReaderを実装
    19
    ● DatasetReader を継承して独自の DatasetReader を実装
    ○ 最低限必要なのは _read() メソッドの override
    ● reader.read(file)を呼ぶと Instance のリストが返却
    ○ 入力ファイルの各行に対して tokenizer を使ってテキストを
    単語に分割し、token_indexers を使って単語IDに変換
    Instance に渡される
    fields 辞書で設定
    されている text と
    label の key に注目
    これらの key は、のち
    に Model にテンソルを
    渡すときに仮引数名
    として使われます
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  20. AllenNLPことはじめ: Modelを構築
    20
    ● 入力から出力を予測し、loss を計算する Model を構築
    ○ Model は Instance の batch を受け取る
    ● DatasetReader の Fields に text や label を
    使用していたことを思い出してください
    ○ AllenNLP は Field の key を仮引数名として渡します
    # Input
    text: TextField
    # Output
    label: LabelField
    allenai/allennlp/gradient_descent_trainer.py#L403
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  21. AllenNLPことはじめ: Modelを実装 (1)-1
    21
    ● AllenNLP で Model がどのように動作するか
    ○ AllenNLP の Model は PyTorch の nn.Module がベース
    ○ forward()メソッドを実装し、出力は Dict[str, Any]
    ○ 出力には学習時の loss key が含まれ、モデルの最適化に利用
    ● 学習ループは以下の通り:
    ○ Instance の batch を
    Model.forward() に通す
    ○ 結果の辞書から loss key の値を取得
    ○ backprop を使って勾配を計算しモデルのパラメータを更新
    ● 学習ループを明示的に実装する必要なし!
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  22. AllenNLPことはじめ: Modelを実装 (1)-2
    22
    ● Model のコンストラクタでは、学習させたいすべての
    パラメータをインスタンス化する必要あり
    ○ AllenNLP ではこれらのパラメータのほとんどを
    コンストラクタの引数として渡すことを推奨している
    ■ モデルの実装自体を変更せずにモデルの動作を設定可能
    ■ モデルの動作をより高いレベルで考えられるようになる
    @Model.register('simple_classifier')
    class SimpleClassifier(Model):
    def __init__(self,
    vocab: Vocabulary,
    embedder: TextFieldEmbedder,
    encoder: Seq2VecEncoder):
    super().__init__(vocab)
    self.embedder = embedder
    self.encoder = encoder
    num_labels = vocab.get_vocab_size("labels")
    self.classifier = torch.nn.Linear(encoder.get_output_dim(), num_labels)
    この SimpleClassifier を例に
    順にコードを見ていきます
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  23. AllenNLPことはじめ: Modelを実装 (1)-2
    23
    ● Model のコンストラクタでは、学習させたいすべての
    パラメータをインスタンス化する必要あり
    ○ AllenNLP ではこれらのパラメータのほとんどを
    コンストラクタの引数として渡すことを推奨している
    ■ モデルの実装自体を変更せずにモデルの動作を設定可能
    ■ モデルの動作をより高いレベルで考えられるようになる
    @Model.register('simple_classifier')
    class SimpleClassifier(Model):
    def __init__(self,
    vocab: Vocabulary,
    embedder: TextFieldEmbedder,
    encoder: Seq2VecEncoder):
    super().__init__(vocab)
    self.embedder = embedder
    self.encoder = encoder
    num_labels = vocab.get_vocab_size("labels")
    self.classifier = torch.nn.Linear(encoder.get_output_dim(), num_labels)
    AllenNLP では 型アノテーション
    が重要な役割を担っている
    ● コードが読みやすくなる
    ● (最高機能)型アノテーションを使って
    設定ファイルから embedder や encoder を
    自動的に構築することができる!!!(後述)
    allennlp train コマンドを設定ファイルと共に
    使っているならば、追加の実装なしで簡単に実現可能
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  24. AllenNLPことはじめ: Modelを実装 (1)-3
    24
    ● 語彙を司る Vocabulary インスタンスを渡す
    ○ Vocabulary は語彙に関連する情報 (単語, ラベル etc.) と
    そのID間のマッピングを管理している
    ○ 学習ループでは、学習データを読み込んだ後に AllenNLP が
    Vocabulary を使って語彙を作成し、Model に渡す (隠蔽済み)
    @Model.register('simple_classifier')
    class SimpleClassifier(Model):
    def __init__(self,
    vocab: Vocabulary,
    embedder: TextFieldEmbedder,
    encoder: Seq2VecEncoder):
    super().__init__(vocab)
    self.embedder = embedder
    self.encoder = encoder
    num_labels = vocab.get_vocab_size("labels")
    self.classifier = torch.nn.Linear(encoder.get_output_dim(), num_labels)
    ● Vocabulary は学習に使用される
    すべての token と label を見つけてくる
    ○ それぞれを別の名前空間でIDを付与する
    ■ Token ID: 0 -> [CLS]
    ■ Label ID: 0 -> positive
    ● DatasetReader では、ラベルをデフォルトの
    labels 名前空間に置いている
    ○ “labels” を指定して、ラベル数を取得
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  25. AllenNLPことはじめ: Modelを実装 (1)-4
    25
    ● TextFieldEmbedder を使って単語IDから埋め込みを得る
    ○ TextField によって作られたテンソルを受け取って埋め込む
    ■ NLPで頻発するこの処理をAllenNLPでは高度に抽象化
    ● 実装を変更することなく、設定ファイルから切り替え可能
    ○ forward()で得られるtextパラメータに適用することで
    入力トークンに対して埋め込みテンソルが得られる
    @Model.register('simple_classifier')
    class SimpleClassifier(Model):
    def __init__(self,
    vocab: Vocabulary,
    embedder: TextFieldEmbedder,
    encoder: Seq2VecEncoder):
    super().__init__(vocab)
    self.embedder = embedder
    self.encoder = encoder
    num_labels = vocab.get_vocab_size("labels")
    self.classifier = torch.nn.Linear(encoder.get_output_dim(), num_labels)
    ● embedder を通して得られる
    テンソルのサイズは
    (batch_size, num_tokens, embedding_dim)
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  26. AllenNLPことはじめ: Modelを実装 (1)-5
    26
    ● Seq2VecEncoder を適用する
    ○ 複数の token embeddings を1つ sentence embedding に
    まとめるため、AllenNLP における Seq2VecEncoder を使用
    ■ 名前の通り sequence から vector を返す抽象クラス
    ■ RNN, CNN を始め Transformerベースのモデルも使用可能
    @Model.register('simple_classifier')
    class SimpleClassifier(Model):
    def __init__(self,
    vocab: Vocabulary,
    embedder: TextFieldEmbedder,
    encoder: Seq2VecEncoder):
    super().__init__(vocab)
    self.embedder = embedder
    self.encoder = encoder
    num_labels = vocab.get_vocab_size("labels")
    self.classifier = torch.nn.Linear(encoder.get_output_dim(), num_labels)
    ● encoder を通して得られる
    テンソルのサイズは
    (batch_size, encoding_dim)
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  27. AllenNLPことはじめ: Modelを実装 (1)-6
    27
    ● これまで計算してきた特徴ベクトルから分類する
    ○ 分類用のレイヤーを全結合層で定義
    ■ Seq2VecEncoder の出力を logit に変換し
    各ラベルごとに1つの値を出力する
    ■ これらの値は後に確率分布に変換されて損失計算に使用
    @Model.register('simple_classifier')
    class SimpleClassifier(Model):
    def __init__(self,
    vocab: Vocabulary,
    embedder: TextFieldEmbedder,
    encoder: Seq2VecEncoder):
    super().__init__(vocab)
    self.embedder = embedder
    self.encoder = encoder
    num_labels = vocab.get_vocab_size("labels")
    self.classifier = torch.nn.Linear(encoder.get_output_dim(), num_labels)
    ● Seq2VecEncoder.get_output() により
    出力次元を得ることができるため、
    その値を使って全結合層を定義
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  28. AllenNLPことはじめ: Modelを実装 (2)-1
    28
    ● Model.forward() を実装する
    ○ 入力を受け取り、予測を出力し、損失を計算する役割
    ● これまで扱ってきた SimpleClassifier と入出力を再度確認:
    ○ DatasetReader で作成した Instance の Field key と
    一致するように forward() の引数を定義する
    @Model.register('simple_classifier')
    class SimpleClassifier(Model):
    def __init__(self,
    vocab: Vocabulary,
    embedder: TextFieldEmbedder,
    encoder: Seq2VecEncoder):
    super().__init__(vocab)
    self.embedder = embedder
    self.encoder = encoder
    num_labels = vocab.get_vocab_size("labels")
    self.classifier = torch.nn.Linear(encoder.get_output_dim(), num_labels)
    # Input
    text: TextField
    # Output
    label: LabelField
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  29. AllenNLPことはじめ: Modelを実装 (2)-2
    29
    ● Model.forward() の詳細
    ○ コンストラクタで作成したパラメータを使って、
    入力を出力へと変換する。出力をした後、損失を計算する
    class SimpleClassifier(Model):
    def forward(self,
    text: TextFieldTensors,
    label: torch.Tensor) -> Dict[str, torch.Tensor]:
    # Shape: (batch_size, num_tokens, embedding_dim)
    embedded_text = self.embedder(text)
    # Shape: (batch_size, num_tokens)
    mask = util.get_text_field_mask(text)
    # Shape: (batch_size, encoding_dim)
    encoded_text = self.encoder(embedded_text, mask)
    # Shape: (batch_size, num_labels)
    logits = self.classifier(encoded_text)
    # Shape: (batch_size, num_labels)
    probs = torch.nn.functional.softmax(logits)
    # Shape: (1,)
    loss = torch.nn.functional.cross_entropy(logits, label)
    return {'loss': loss, 'probs': probs}
    # Input
    text: TextField
    # Output
    label: LabelField
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  30. AllenNLPことはじめ: Modelを実装 (2)-2
    30
    ● Model.forward() の詳細
    ○ コンストラクタで作成したパラメータを使って、
    入力を出力へと変換する。出力をした後、損失を計算する
    class SimpleClassifier(Model):
    def forward(self,
    text: TextFieldTensors,
    label: torch.Tensor) -> Dict[str, torch.Tensor]:
    # Shape: (batch_size, num_tokens, embedding_dim)
    embedded_text = self.embedder(text)
    # Shape: (batch_size, num_tokens)
    mask = util.get_text_field_mask(text)
    # Shape: (batch_size, encoding_dim)
    encoded_text = self.encoder(embedded_text, mask)
    # Shape: (batch_size, num_labels)
    logits = self.classifier(encoded_text)
    # Shape: (batch_size, num_labels)
    probs = torch.nn.functional.softmax(logits)
    # Shape: (1,)
    loss = torch.nn.functional.cross_entropy(logits, label)
    return {'loss': loss, 'probs': probs}
    # Input
    text: TextField
    # Output
    label: LabelField
    メソッドの入力に注目💡
    ● AllenNLPの学習ループでは、DatasetReader

    作った Field 名を forward で同じ Field 名を
    持つインスタンスのバッチを渡す
    ● Field 名として text と label を使ったので、
    forward の引数も同じように名前をつける
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  31. AllenNLPことはじめ: Modelを実装 (2)-2
    31
    ● Model.forward() の詳細
    ○ コンストラクタで作成したパラメータを使って、
    入力を出力へと変換する。出力をした後、損失を計算する
    class SimpleClassifier(Model):
    def forward(self,
    text: TextFieldTensors,
    label: torch.Tensor) -> Dict[str, torch.Tensor]:
    # Shape: (batch_size, num_tokens, embedding_dim)
    embedded_text = self.embedder(text)
    # Shape: (batch_size, num_tokens)
    mask = util.get_text_field_mask(text)
    # Shape: (batch_size, encoding_dim)
    encoded_text = self.encoder(embedded_text, mask)
    # Shape: (batch_size, num_labels)
    logits = self.classifier(encoded_text)
    # Shape: (batch_size, num_labels)
    probs = torch.nn.functional.softmax(logits)
    # Shape: (1,)
    loss = torch.nn.functional.cross_entropy(logits, label)
    return {'loss': loss, 'probs': probs}
    # Input
    text: TextField
    # Output
    label: LabelField
    引数の型に注目💡
    ● 各 Field は自身を torch.Tensor に変換する
    方法が実装されているため、Instance のバッチ
    から torch.Tensor のバッチに変換される
    ● text と label は TextField と LabelField
    TextFieldTensor と torch.Tensor へ変換
    ● TextFieldEmbedder は TextFieldTensor を
    期待し、出力として torch.Tensor を返す
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  32. AllenNLPことはじめ: Modelを実装 (2)-3
    32
    ● TextFieldEmbedder を用いてテキストを埋め込む
    ○ テキストを埋め込んで各入力トークンに対してベクトルを取得
    ○ 抽象化により、様々な埋め込み方法を実装の変更なく試行可能
    class SimpleClassifier(Model):
    def forward(self,
    text: TextFieldTensors,
    label: torch.Tensor) -> Dict[str, torch.Tensor]:
    # Shape: (batch_size, num_tokens, embedding_dim)
    embedded_text = self.embedder(text)
    # Shape: (batch_size, num_tokens)
    mask = util.get_text_field_mask(text)
    # Shape: (batch_size, encoding_dim)
    encoded_text = self.encoder(embedded_text, mask)
    # Shape: (batch_size, num_labels)
    logits = self.classifier(encoded_text)
    # Shape: (batch_size, num_labels)
    probs = torch.nn.functional.softmax(logits)
    # Shape: (1,)
    loss = torch.nn.functional.cross_entropy(logits, label)
    return {'loss': loss, 'probs': probs}
    ● 埋め込み取得の操作は高度に隠蔽されている
    ○ コンストラクタで設定した
    TextFieldEmbedder が操作するとだけ記述
    ● この抽象化によって実装を大きく変更することなく
    Word2Vec, ELMo, BERT 等を使用可能
    ○ 実験の変更に対する頑健性が大きく向上
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  33. AllenNLPことはじめ: Modelを実装 (2)-4
    33
    ● Seq2VecEncoder を適用する
    ○ token embeddings を 1つの sentence embedding へ変換する
    ○ 可変長テキストをバッチ化したときの padding まわりの処理がラク
    class SimpleClassifier(Model):
    def forward(self,
    text: TextFieldTensors,
    label: torch.Tensor) -> Dict[str, torch.Tensor]:
    # Shape: (batch_size, num_tokens, embedding_dim)
    embedded_text = self.embedder(text)
    # Shape: (batch_size, num_tokens)
    mask = util.get_text_field_mask(text)
    # Shape: (batch_size, encoding_dim)
    encoded_text = self.encoder(embedded_text, mask)
    # Shape: (batch_size, num_labels)
    logits = self.classifier(encoded_text)
    # Shape: (batch_size, num_labels)
    probs = torch.nn.functional.softmax(logits)
    # Shape: (1,)
    loss = torch.nn.functional.cross_entropy(logits, label)
    return {'loss': loss, 'probs': probs}
    ● 異なる長さのテキストはバッチ化されるときに
    パディングされているため、その箇所を mask する
    ○ get_text_field_mask() が便利
    ● encoder に embedded_text を渡すときは、
    padding の箇所を示す mask も忘れずに渡す
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  34. class SimpleClassifier(Model):
    def forward(self,
    text: TextFieldTensors,
    label: torch.Tensor) -> Dict[str, torch.Tensor]:
    # Shape: (batch_size, num_tokens, embedding_dim)
    embedded_text = self.embedder(text)
    # Shape: (batch_size, num_tokens)
    mask = util.get_text_field_mask(text)
    # Shape: (batch_size, encoding_dim)
    encoded_text = self.encoder(embedded_text, mask)
    # Shape: (batch_size, num_labels)
    logits = self.classifier(encoded_text)
    # Shape: (batch_size, num_labels)
    probs = torch.nn.functional.softmax(logits)
    # Shape: (1,)
    loss = torch.nn.functional.cross_entropy(logits, label)
    return {'loss': loss, 'probs': probs}
    AllenNLPことはじめ: Modelを実装 (2)-4
    34
    ● これまで計算した特徴ベクトルを元に予測を出力
    ○ nn.Linear 層で定義された classifier を使いラベルの候補ごとに
    スコア (一般的に logit と呼ばれる) を出力させる
    ● logits に softmax を適用して確率分布を得る
    ○ 最終的にモデルの予測結果を見るときに便利
    ● logits に cross_entropy を適用して損失を得る
    ○ PyTorch の cross entropy は内部で
    softmax を適用していることに注意
    (よく間違えます😂)
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  35. AllenNLP でモデルを学習・予測
    35

    View Slide

  36. AllenNLP でモデルを学習・予測させる
    Training and prediction @AllenNLP Guide
    36
    ● 自作スクリプトによるモデルの学習 Training the model
    with your own script
    ● allennlp trainによるモデルの学習 Training the model
    with your own script
    ● モデルの評価 Evaluating the model
    ● ラベルのない入力に対する予測
    Making predictions
    for unlabeled data
    テキスト分類モデルを学習し、新しい入力に対して予測させる2通りの方法を説明します
    ● 自分でスクリプトを書いてDatasetReaderとModelを構築し、学習ループを実行する方法
    ● 設定ファイルを書いて allennlp train コマンドを使う方法
    AllenNLP は allennlp train コマンドを使うことでさらに強力な実験PDCAサイクルを実現します!
    スクリプトを書くなら pytorch lightning とかでもいいのでは?とも思います😂
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  37. def build_model(vocab: Vocabulary) -> Model:
    print("Building the model")
    vocab_size = vocab.get_vocab_size("tokens")
    embedder = BasicTextFieldEmbedder(
    {"tokens": Embedding(10, vocab_size)})
    encoder = BagOfEmbeddingsEncoder(embedding_dim=10)
    return SimpleClassifier(vocab, embedder, encoder)
    AllenNLP学習予測: allennlp trainコマンドで (1)
    37
    ● 設定ファイルから DatasetReader や Model を定義
    ○ json 形式の設定ファイルに、様々なオブジェクトの
    コンストラクタ・パラメータを定義しておく
    ● 👈の json は👇の python script と同じ意味
    ○ type に合わせてコンストラクタパラメータを定義
    ○ SimpleClassifier は Model の subclass で
    あり、BagOfEmbeddingsEncoder は
    Seq2VecEncoder の subclass であることに注意
    ■ Model.register 等で登録されている
    "model": {
    "type": "simple_classifier",
    "embedder": {
    "token_embedders": {
    "tokens": {
    "type": "embedding",
    "embedding_dim": 10
    }
    }
    },
    "encoder": {
    "type": "bag_of_embeddings",
    "embedding_dim": 10
    }
    }
    json
    python script
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  38. AllenNLP学習予測: allennlp trainコマンドで (2)
    38
    ● 設定ファイルを用いて allennlp train でモデルを学習させる
    $ allennlp train /path/to/config.json -s /path/to/serialization-dir
    ● json 設定ファイルから学習に必要なコンポーネントを
    自動的に構成する Registrable 機構
    ○ DatasetReader, Model, その他の component
    を allennlp コマンドで認識させるためには
    .register() の呼び出しを実行する必要あり
    ■ 各componentをインスタンス化する際に実行
    ○ Registrable 機構を活用するためには
    以下のどちらかが必要:
    ■ 実行時に --include-package
    [my_python_module] というフラグを追加
    ■ allennlp の plugin 機構を利用 (オススメ; 後述)
    "model": {
    "type": "simple_classifier",
    "embedder": {
    "token_embedders": {
    "tokens": {
    "type": "embedding",
    "embedding_dim": 10
    }
    }
    },
    "encoder": {
    "type": "bag_of_embeddings",
    "embedding_dim": 10
    }
    }
    json
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  39. class SimpleClassifier(Model):
    def forward(self,
    text: TextFieldTensors,
    label: torch.Tensor) -> Dict[str, torch.Tensor]:
    # Shape: (batch_size, num_tokens, embedding_dim)
    embedded_text = self.embedder(text)
    # Shape: (batch_size, num_tokens)
    mask = util.get_text_field_mask(text)
    # Shape: (batch_size, encoding_dim)
    encoded_text = self.encoder(embedded_text, mask)
    # Shape: (batch_size, num_labels)
    logits = self.classifier(encoded_text)
    # Shape: (batch_size, num_labels)
    probs = torch.nn.functional.softmax(logits)
    # Shape: (1,)
    loss = torch.nn.functional.cross_entropy(logits, label)
    self.accuracy(logits, label)
    return {'loss': loss, 'probs': probs}
    AllenNLP学習予測: モデルの評価 (1)
    39
    ● AllenNLP の評価指標まわりを抽象化した Metric を使用
    ○ 学習中の各種メトリクスを追跡するのに便利な機能を提供
    ■ 正解率を追跡する CategoricalAccuracy の使用例
    @Model.register('simple_classifier')
    class SimpleClassifier(Model):
    def __init__(self,
    vocab: Vocabulary,
    embedder: TextFieldEmbedder,
    encoder: Seq2VecEncoder):
    super().__init__(vocab)
    self.embedder = embedder
    self.encoder = encoder
    num_labels = vocab.get_vocab_size("labels")
    self.classifier = torch.nn.Linear(
    encoder.get_output_dim(), num_labels)
    self.accuracy = CategoricalAccuracy()
    👇コンストラクタで追加して
    👉 各 forward pass で
    logit と label を渡して更新
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  40. class SimpleClassifier(Model):
    def forward(self,
    text: TextFieldTensors,
    label: torch.Tensor) -> Dict[str, torch.Tensor]:
    # Shape: (batch_size, num_tokens, embedding_dim)
    embedded_text = self.embedder(text)
    # Shape: (batch_size, num_tokens)
    mask = util.get_text_field_mask(text)
    # Shape: (batch_size, encoding_dim)
    encoded_text = self.encoder(embedded_text, mask)
    # Shape: (batch_size, num_labels)
    logits = self.classifier(encoded_text)
    # Shape: (batch_size, num_labels)
    probs = torch.nn.functional.softmax(logits)
    # Shape: (1,)
    loss = torch.nn.functional.cross_entropy(logits, label)
    self.accuracy(logits, label)
    return {'loss': loss, 'probs': probs}
    AllenNLP学習予測: モデルの評価 (2)
    40
    ● allennlp evaluate コマンドで評価(のみ)を実行
    ○ allennlp train コマンドの最後にも評価が行われる
    @Model.register('simple_classifier')
    class SimpleClassifier(Model):
    def __init__(self,
    vocab: Vocabulary,
    embedder: TextFieldEmbedder,
    encoder: Seq2VecEncoder):
    super().__init__(vocab)
    self.embedder = embedder
    self.encoder = encoder
    num_labels = vocab.get_vocab_size("labels")
    self.classifier = torch.nn.Linear(
    encoder.get_output_dim(), num_labels)
    self.accuracy = CategoricalAccuracy()
    評価結果は指定した serialization_dir
    (allennlp train コマンド時),
    output_file (allennlp evaluate
    コマンド時) に json 形式で保存される
    {'accuracy': 0.855, 'loss': 0.3686505307257175}
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  41. AllenNLP学習予測: ラベルのない入力に対する予測 (1)
    41
    ● Predictor を実装する
    ○ AllenNLP はラベル無しデータに対して予測を出力するような
    デモ環境での予測用に、Predictor でモデルを wrap する
    ■ json形式の入力を受け取り、DatasetReader を使って
    Instance に変換したのちに Model へと入力する
    ■ 最終的な予測結果を再度 json 形式に変換して返却
    @Predictor.register("sentence_classifier")
    class SentenceClassifierPredictor(Predictor):
    def _json_to_instance(self, json_dict: JsonDict) -> Instance:
    sentence = json_dict["sentence"]
    return self._dataset_reader.text_to_instance(sentence)
    def predict(self, sentence: str) -> JsonDict:
    # This method is implemented in the base class.
    return self.predict_json({"sentence": sentence})
    学習済み Model を wrap する
    Predictor があるおかげで、
    容易に allennlp-server を
    使用した API 化が可能
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  42. allennlp-server でラクラク API 化
    42

    View Slide

  43. allennlp-server でラクラク API 化
    43
    ● 学習済み allennlp Model を使って爆速でデモを作る
    ○ allennlp では allennlp train コマンドで設定した
    serialization_dir にモデルと学習状態を tar.gz 形式で出力
    ● allennlp-server でデモサーバーを構築する (参考: allenai/allennlp-demo)
    ○ とても簡単な
    2 step で完結
    python allennlp-server/server_simple.py \
    --archive-path model/model.tar.gz \
    --predictor sentence_classifier \
    --field-name sentence
    --include-package my_text_classifier
    pip install allennlp-server
    curl --header 'Accept:application/json' \
    --header 'Content-Type:application/json' \
    -v -X POST -d '{"sentence": "Did Uriah honestly think he could beat The Legend of Zelda in
    under three hours?"}' http://localhost:8000/predict/named-entity-recognition
    いい感じの demo 画面が立ち上がり、
    シュッと結果を確認可能
    curl 等を使って API として使うことも可能
    (1)
    (2)
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  44. AllenNLPを支える Registable 機構
    44

    View Slide

  45. AllenNLP を支える Registrable 機構 (1)
    ● 複数ある前処理やモデルを “名前” で選んで実行したい
    ○ 比較実験をする際
    様々な組み合わせが存在
    ■ dict に “名前” と前処理や
    モデルの定義を詰めた
    比較実験用 registry みたいなものを爆誕させる
    ■ 便利な一方で、逐一この dict を更新しなくてはならず󰷹
    45
    MODELS: Dict[str, type(nn.Module)] = {"bert": BERT, "roberta": RoBERTa, "gpt": GPT}
    bert = MODELS["bert"].from_pretrained('bert-base-uncased')
    PREPROCESS_FUNCS: Dict[str, Callable] = {
    "prep01": preprocess01_func,
    "prep02": preprocess02_func,
    "prep03": preprocess03_func,
    }
    ret = PREPROCESS_FUNCS['prep02'](*args, **kwargs)
    ● Registrableを継承したクラスはそのサブクラスに
    対して名前付き registry へのアクセス得る
    ○ registry へ登録するにはデコレータ
    BaseClass.register(name) をつけるだけ
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  46. class Registrable(FromParams):
    _registry: ClassVar[DefaultDict[type,
    _SubclassRegistry]] = defaultdict(dict)
    @classmethod
    def register(
    cls, name: str, ...
    ) -> Callable[[Type[_T]], Type[_T]]:
    registry = Registrable._registry[cls]
    ...
    AllenNLP を支える Registrable 機構 (2)
    46
    ● Registrable クラス内部では同じように
    dict に追加している
    ○ Registrableの_registry
    に追加されたクラスの情報
    が管理されている
    ● Registrable を使用する
    ことで、デコレータで登録
    したクラスを簡単に呼び出し可能
    ○ 設定ファイルの type を読んで、インスタンス化
    @Model.register('simple_classifier')
    class SimpleClassifier(Model):
    def __init__(self,
    ...
    @DatasetReader.register('classification-tsv')
    class ClassificationTsvReader(DatasetReader):
    def __init__(self):
    self.tokenizer = SpacyTokenizer()
    ...
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  47. Registrable クラスを継承した
    サブクラスを構築する上でのハマりポイント
    47
    allennlp コマンド実行時に、対象のサブクラスが import
    されるようにする(2パターンあり)
    ● --include-package オプションで指定する
    ○ 指定したディレクトリ配下の import を再帰的に実行
    ■ __init__.py に実装したサブクラスを書いておけば OK
    ● (オススメ) .allennlp_plugins で指定する
    ○ パッケージ名を書いておくことで、
    そのパッケージの __init__.py が読み込まれる
    ■ パッケージ root の __init__.py に実装したサブクラスを
    書いておく必要あり
    ● ref. allenai/allennlp-template-config-files
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  48. Registrable な Subcommand を元に
    オレオレ前処理コマンドを実装する
    48

    View Slide

  49. Registrable な Subcommand を元に
    オレオレ前処理コマンドを実装する
    ● プロジェクト root に
    オレオレ前処理用スクリプトの
    残骸が散りばりがち問題
    ○ 試行錯誤しているうちに増殖
    ○ 最初に見るプロジェクト root が
    汚いとやる気が無くなり、実験が
    嫌になり、研究も嫌になる…
    ● AllenNLP の Subcommand を
    使いつつ、前処理スクリプト等
    を綺麗なディレクトリ構造を
    保って実装していきたい
    49
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand
    📁 datasets
    📁 utils
    📝 train.py
    📝 preprocess.py
    📝 preprocess_01.py
    📝 preprocess_02_sugoi.py
    📁 datasets
    📁 configs
    📁 my_project
    📝 .allennlp_plugins
    Before w/o AllenNLP
    After w/ AllenNLP

    View Slide

  50. AllenNLP のサブコマンドと Subcommand クラス
    50
    ● AllenNLP には複数のコマンドが存在する
    ○ allennlp train, evaluate, predict, …
    ■ これらは Registrable な Subcommand を継承して
    構築されている
    ● Subcommand クラス
    ○ AllenNLP で使われるサブコマンドを表す抽象クラス
    ■ 内部では argparse が使われている
    ○ 独自のコマンドを作りたい場合は、このクラスを継承
    しつつ、Subcommand.register(name) のようにして
    コマンドを登録する必要あり
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand

    View Slide

  51. Subcommand を元にオレオレサブコマンドを実装 (1)
    51
    世の中にあまりサンプルコードがなかったので実装を公開
    shunk031/allennlp-custom-subcommand-sample
    ● hello-subcommand というコマンドを作ってみる
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand
    $ allennlp --help
    2022-07-13 22:12:44,209 - INFO - allennlp.common.plugins - Plugin my_project available
    usage: allennlp [-h] [--version] ...
    Run AllenNLP
    optional arguments:
    -h, --help show this help message and exit
    --version show program's version number and exit
    Commands:
    train Train a model.
    evaluate Evaluate the specified model + dataset(s).
    predict Use a trained model to make predictions.
    ...
    hello-subcommand
    This is the first custom subcommand
    $ allennlp hello-subcommand --message world!
    2022-07-13 22:09:51,665 - INFO - allennlp.co…
    Hello world!
    実際に実行すると...

    View Slide

  52. ● .allennlp_plugins を作成
    ○ プロジェクトの root に作成し、
    パッケージ名 (例: my_project)
    を記述する
    ● my_project/commands 以下に
    ディレクトリとファイルを作成
    ○ hello-subcommand 用のディレクトリ
    ● my_project/commands/hello_
    subcommand 以下にファイルを作成
    ○ function.py と sub_command.py
    の2つに分けて作成することを
    おすすめします
    Subcommand を元に
    オレオレサブコマンドを実装 (2)
    52
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand
    📁 my_project
    📝 .allennlp_plugins
    project root dir
    📁 commands
    📝 __init__.py
    my_project/
    📁 hello_subcommand
    📝 __init__.py
    my_project/commands/
    📝 __init__.py
    📝 function.py
    📝 sub_command.py
    my_project/commands/hello_
    subcommand

    View Slide

  53. @Subcommand.register("hello-subcommand")
    class HelloSubcommand(Subcommand):
    def add_subparser(
    self, parser: argparse._SubParsersAction
    ) -> argparse.ArgumentParser:
    description = "custom subcommand for just say hello with your message"
    # create subparser from the parser
    subparser = parser.add_parser(
    self.name,
    description=description,
    help="This is the first custom subcommand",
    )
    # add arguments
    subparser.add_argument("--message", type=str, default="world")
    # set the function to be called when this subcommand is executed.
    subparser.set_defaults(func=lambda args: hello_function(msg=args.message))
    return subparser
    ● my_project/commands/hello_
    subcommand 以下のファイルを実装
    Subcommand を元に
    オレオレサブコマンドを実装 (3)
    53
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand
    my_project/commands/hello
    _subcommand
    📝 __init__.py
    📝 function.py
    📝 sub_command.py
    def hello_function(msg: str) -> None:
    print(f"Hello {msg}")
    sub_command.py:
    Registrable な Subcommand を継承して
    必要なメソッドをオーバーライド
    function.py:
    サブコマンドのロジックを実装

    View Slide

  54. ● .allennlp_plugins で指定した
    パッケージから再帰的に実装した
    サブコマンドが読み込まれるように準備する
    ● allennlp コマンド実行時、
    .allennlp_plugins に記述された
    my_project の __init__.py を
    import しにいくので、最終的には
    HelloSubcommand が import される
    allennlp コマンド実行時に実装した
    サブコマンドが読み込まれるようにする
    54
    AllenNLP について > ことはじめ > 学習・予測 > API 化 > Registrable 機構 > オレオレ Subcommand
    from my_project.commands import * # NOQA
    from my_project.commands.hello\
    _subcommand.sub_command import HelloSubcommand # NOQA
    📁 my_project
    📝 .allennlp_plugins
    project root dir
    📁 commands
    📝 __init__.py
    my_project/
    📁 hello_subcommand
    📝 __init__.py
    my_project/commands/
    📝 __init__.py
    📝 function.py
    📝 sub_command.py
    my_project/commands/hello_
    subcommand

    View Slide

  55. まとめ
    55

    View Slide

  56. AllenNLPをマスターすると研究が爆速進捗します
    ● いくつか allennlp のお作法があるものの、
    使いこなすとサクッとモデルを構築できます
    ○ DatasetReader や Model を実装するだけ
    ○ 拡張性も高いので、柔軟にオレオレモデルが構築可能
    ● 実験コードにありがちなハウルの動く城なコードが
    allennlp の流儀に従うと綺麗なまま保つことが出来ます
    ○ Subcommand 等を組み合わせながら前処理等も
    整理整頓されながらコードを書くことができます
    ○ Model はそのまま Predictor に渡すことで API 化
    ● 残念ながら開発終了してしまいましたが
    ○ コードの品質は高く、コードリーディングすると楽しい
    ○ 後継の tango も allennlp の Registrable な思想あり
    56

    View Slide

  57. AllenNLPを入門する上で参考になる資料たち
    ● A Guide to Natural Language Processing With AllenNLP
    ○ AllenAi が公開している AllenNLP のチュートリアル。
    まずはここを眺めて手を動かして動作を確認することを
    おすすめ。本資料の作成にも大変お世話になりました。
    ● 実践!AllenNLPによるディープラーニングを用いた
    自然言語処理 - Speaker Deck
    ○ 具体的なタスクと紐付け、付属の notebook を用いて
    実際にコードを動かせる高品質な説明資料です。
    ● AllenNLPを使った開発 - Speaker Deck
    ○ AllenNLPの全体を把握するのにわかりやすい資料です。
    今回の資料を作る前はこちらの資料を借りて説明していました。
    ● kagglerのためのAllenNLPチュートリアル - Speaker Deck
    ○ 研究もそうですが、Kaggle でも使える AllenNLP!
    57

    View Slide