Juman++, SentencePiece, BERT tokenizerの分かち書きを同じコードで書くための抽象クラス
0. 動機
自然言語処理のためには, 入力文を分かち書きし, 各トークンを数値に変換しなくてはなりません。
分かち書きのためのモジュールは Janome(MeCab), Juman++, SentencePiece, BERT tokenizer など色々提供されています。
しかし, 厄介なことに, これらは
- 形態素レベルの分かち書きを行うもの
- 形態素よりも細かいレベルの分かち書きを行うもの
- 分かち書きをするが, IDへの変換はしないもの
- 分かち書きをせず, IDへの変換だけを行うもの
- 分かち書きとIDへの変換を同時に行うもの
など, 分かち書きをどの粒度で行うのか, 処理をどの段階まで行うのかの点でバラバラです。ややこしいですね。
さらに後述しますが, BERTの登場により, 1つの前処理なのに2つ以上の分かち書き器を組み合わせて使わなければならない場面も登場してきました。ますますややこしいですね。
そんなときに, どんな分かち書き器に対しても共通のコードで分かち書きをしたい!というのが動機です。
(もう一つの動機は, 単に私がPythonで抽象クラスを書く練習がしたかったというだけ)
注: 自然言語処理で tokenize, tokenizer という場合, 分かち書きを指すのか, IDへの変換も含めて意味するのかは文脈により曖昧な印象があります。このため, 一般的な表記ではないかもしれませんが本記事では分かち書き, 分かち書き器のように呼びます。
1. 基底クラスの定義
あらゆる分かち書き器に対応するための抽象クラスを定義しましょう。
まず, 使いたい分かち書き器が搭載している機能のうち
がどんなものであるかを指示するための空のメソッドを用意しておきます(@abstractmethod
でデコレートされているもの)。
これらの空のメソッドはクラス継承時に必ずoverrideしなければなりませんが, そのoverrideさえきちんと行えば, 実際の分かち書きやBERT入力形式への変換作業はあらかじめ基底クラスに定義してあるため, あらためて考えなくてもよい設計にしています。
from abc import ABCMeta, abstractmethod class BaseTextProcessorForDataField(object): """ raw text -┐[1] [2]┌- cleaned text <┘ └> [words] -┐[3] [4]┌- [wordpieces] <┘ └> [token ids] -┐[5] BERT input <┘ [1]: text cleaning [2]: base tokenization [3]: wordpiece tokenization [4]: convert to token ids [5]: convert to BERT input """ __metaclass__ = ABCMeta def __init__(self): self.unk_id = -1 self.cls_id = -1 self.sep_id = -1 self.pad_id = -1 self.fix_length = 0 self._enable_base_punctuation = True self._enable_wordpiece_punctuation = True self._enable_punctuation = True self._enable_tokenization = True self.errmsg = '{} is not offered in the tokenizer in use in the first place.' # MeCab, Juman++, SentencePiece, BertTokenizer等の処理工程の違いに対応する準備 # 抽象クラスのメソッドを用意する # 抽象クラス継承時には必ずoverrideする必要がある @abstractmethod def clean(self, text): # [1] pass @abstractmethod def punctuate_base(self, text): # [2] pass @abstractmethod def punctuate_wordpiece(self, word): # [3] pass @abstractmethod def punctuate(self, text): # [2] + [3] pass @abstractmethod def convert_token_to_id(self, token): # [4] pass @abstractmethod def tokenize_at_once(self, text): # [2] + [3] + [4] pass # 以下は実際に分かち書きするためのメソッド(override不要) def to_words(self, text): """ Apply only basic tokenization to text. ------------ raw-text | <- (this function) cleaned-text | <- (this function) [tokens_words] | [tokens_wordpieces] | [token-ids] | [cls-id token-ids pad-ids sep-ids] ------------ Inputs: text(str) - raw passage Outs: (list(str)) - passage split into tokens """ # [1] + [2] if self._enable_base_punctuation: # Base puncuation を行う return self.punctuate_base(self.clean(text)) else: print(self.errmsg.format('Base punctuation')) def to_wordpieces(self, text): """ Apply basic tokenization & wordpiece tokenization to text. ------------ raw-text | <- (this function) cleaned-text | <- (this function) [tokens_words] | <- (this function) [tokens_wordpieces] | <- (this function) [token-ids] | [cls-id token-ids pad-ids sep-ids] ------------ Inputs: text(str) - raw passage Outs: (list(str)) - passage split into tokens """ # [1] + [2] + [3] if self._enable_punctuation: # Base puncuation と Wordpiece Punctuation を行う # 分かち書き器が[2]+[3]を単一メソッドで提供している場合 return self.punctuate(self.clean(text)) elif self._enable_base_punctuation and self._enable_wordpiece_punctuation: # Base puncuation と Wordpiece Punctuation を行う # 分かち書き器が[2],[3]を別メソッドで提供している場合 wordpieces = [] for word in self.puncuate_base(self.clean(text)): wordpieces += self.punctuate_wordpiece(word) return wordpieces elif self._enable_base_punctuation: # Base puncuation のみ行う # 分かち書き器がWordpiece Punctuationに対応していない場合 return self.to_words(text) def to_token_ids(self, text): """ Apply cleaning, punctuation and id-conversion to text. ------------ raw-text | <- (this function) cleaned-text | <- (this function) [tokens_words] | <- (this function) [tokens_wordpieces] | <- (this function) [token-ids] | <- (this function) [cls-id token-ids pad-ids sep-ids] ------------ Inputs: text(str) - raw passage Outs: (list(int)) - list of token ids """ # [1] + [2] + [3] + [4] if self._enable_tokenization: # 分かち書き器が[2]+[3]+[4]を単一メソッドで提供している場合 return self.tokenize_at_once(self.clean(text)) else: # 分かち書き器が[2]+[3]+[4]を単一メソッドで提供していない場合 return [ self.convert_token_to_id(token) for token in self.to_wordpieces(text) ] def to_bert_input(self, text): """ Obtain BERT style token ids from text. ------------ raw-text | <- (this function) cleaned-text | <- (this function) [tokens_words] | <- (this function) [tokens_wordpieces] | <- (this function) [token-ids] | <- (this function) [cls-id token-ids pad-ids sep-ids] ------------ Inputs: text(str) - raw passage Outs: (list(int)) - list of token ids """ # [1] + [2] + [3] + [4] + [5] # [CLS] <入力文のトークンID列> [PAD] ... [PAD] [SEP] 形式のID列にして返す padded = [self.cls_id] + self.to_token_ids(text) + [self.pad_id] * self.fix_length return padded[:self.fix_length-1] + [self.sep_id]
2. 実践
これで, どのような仕様の分かち書き器を使ったとしても
の好きな段階まで処理を行うことができ, コードを共通化することができます!
この抽象クラスを継承して, さまざまな分かち書きに対するコードを共通化させていきましょう。
2-1. Juman++ & BERT の場合
一般に, 訓練済みモデルをfine-tuningして使いたい場合, 分かち書きは訓練済みモデルの事前学習に使われたのと同じ方法で行われなければなりません。
これは, 京大黒橋研から公開されている訓練済み日本語BERTモデルを使いたい場合に特に問題になってきます。
京大黒橋研モデルはどのような分かち書きで事前学習されているのでしょうか? 公式HPによると
と説明されています。
つまり, 京大黒橋研モデルは事前学習時に Juman++とBERT wordpiece tokenizer を組み合わせた分かち書きを使っているため, fine-tuning時にも同じように Juman++とBERT wordpiece tokenizer を組み合わせなければなりません。
そこで, 先ほどの抽象クラスを継承した JumanppBERTTextProcessor
クラスを定義していきましょう。
クラスの定義時に行うのは, 抽象クラスのメソッドのoverrideと, [UNK], [CLS], [SEP], [PAD]にあたるID番号を与えることです。
なお, PyKNPとHugging Face Transformersはすでにインストールされているものとします。
from pyknp import Juman from transformers import BertTokenizer PATH_VOCAB = './vocab.txt' # 1行に1語彙が書かれたtxtファイル jpp = Juman() tokenizer = BertTokenizer(PATH_VOCAB, do_lower_case=False, do_basic_tokenize=False) class JumanppBERTTextProcessor(BaseTextProcessorForDataField): """ JumanppBERTTextProcessor(jppmodel, bertwordpiecetokenizer, fix_length) -> object Inputs ------ jppmodel(pyknp.Juman): Juman++ tokenizer. bertwordpiecetokenizer(transformers.BertTokenizer): BERT tokenizer offered by huggingface.co transformers. This must be initialized with do_basic_tokenize=False. fix_length(int): Desired length of resulting BERT input including [CLS], [PAD] and [SEP]. Longer sentences will be truncated. Shorter sentences will be padded. """ def __init__(self, jppmodel, bertwordpiecetokenizer, fix_length): self.unk_id = bertwordpiecetokenizer.vocab['[UNK]'] self.cls_id = bertwordpiecetokenizer.vocab['[CLS]'] self.sep_id = bertwordpiecetokenizer.vocab['[SEP]'] self.pad_id = bertwordpiecetokenizer.vocab['[PAD]'] self.fix_length = fix_length self.enable_base_punctuation = True self.enable_wordpiece_punctuation = True self.enable_punctuation = False self.enable_tokenization = False # abstractメソッドのoverride def clean(self, text): return text def punctuate_base(self, text): return [mrph.midasi for mrph in jppmodel.analysis(self.clean(text)).mrph_list()] def punctuate_wordpiece(self, word): return bertwordpiecetokenizer.tokenize(word) def punctuate(self, text): pass def convert_token_to_id(self, token): try: return bertwordpiecetokenizer.vocab[token] except KeyError: return self.unk_id def tokenize_at_once(self, text): pass
さて, これで「Juman++で形態素へ, 続いてBERT tokenizerでサブワードへ分かち書きする」処理を短いコードで書くことができます。
jbp = JumanppBERTTextProcessor(jpp, bertwordpiecetokenizer, fix_length=256) passage = '胸部単純CTを撮像しました。' jbp.to_words(passage) # ['胸部', '単純', 'CT', 'を', '撮像', 'し', 'ました'] jbp.to_wordpieces(passage) # ['胸部', '単純', '[UNK]', 'を', '撮', '##像', 'し', 'ました'] jbp.to_token_ids(passage) # [15166, 8420, 1, 10, 17015, 55083, 31, 4561] jbp.to_bert_input(passage) # [2, 15166, 8420, 1, 10, 17015, 55083, 31, 4561, 0, ..., 0, 3]
2-2. SentencePieceの場合
SentencePiece の場合はもっと単純です。
日本語の文法的な概念としての "単語" にこだわらない設計になっており, すべてがサブワードとして扱われます。
したがってBase tokenization と Wordpiece tokenization の区別もありません。
import sentencepiece as sp SPM_MODEL_PATH = '' # 訓練済みSentencePieceモデルのパス spm = SentencePieceProcessor() spm.Load(SPM_MODEL_PATH) class SentencePieceTextProcessor(BaseTextProcessorForDataField): def __init__(self, spmodel, fix_length): super().__init__() self.unk_id = spmodel.PieceToId('<unk>') self.cls_id = spmodel.PieceToId('[CLS]') self.sep_id = spmodel.PieceToId('[SEP]') self.pad_id = spmodel.PieceToId('[PAD]') self.fix_length = fix_length self._enable_base_punctuation = False self._enable_wordpiece_punctuation = False self._enable_punctuation = True self._enable_tokenization = True self.spmodel = spmodel def clean(self, text): # ここではstopword除去などは行わない return text def punctuate_base(self, text): pass def punctuate_wordpiece(self, text): pass def punctuate(self, text): return self.spmodel.EncodeAsPieces(text) def convert_token_to_id(self, token): return self.spmodel.PieceToId(token) def tokenize_at_once(self, text): return self.spmodel.EncodeAsIds(text)
実際に SentencePiece で入力文を BERT入力形式に変換すると以下のようになります。
stp = SentencePieceTextProcessor(spm, fix_length=256) passage = '胸部単純CTを撮像しました' stp.to_words(passage) # ['▁', '胸部', '単純', 'C', 'T', 'を', '撮', '像', 'しま', 'した'] stp.to_wordpieces(passage) # ['▁', '胸部', '単純', 'C', 'T', 'を', '撮', '像', 'しま', 'した'] stp.to_token_ids(passage) # [9, 20854, 9947, 4167, 0, 18, 10032, 1164, 3899, 29] stp.to_bert_input(passage) # [4, 9, 20854, 9947, 4167, 0, 18, 10032, 1164, 3899, 29, 3, ..., 3, 5]
3. 終わりに
どんな分かち書き器に対してもなるべく同じコードで分かち書きができるような抽象クラスを作りました。
利点
- 単語IDの辞書が分かち書き器自体に保持されている場合にも, 外部にある場合にも, 全く同じコードで分かち書きできる
- 京大黒橋研日本語BERTのように2つの分かち書き器を組み合わせなければならない場面でもコードが短くなる
欠点
- 簡単なことを難しく書いている印象は否めない
Notebook環境でGPUメモリ使用量をリアルタイム監視する
TL;DR
GPUメモリの使用量をすぐ取得できるようなPython関数をつくってみた
はじめに
GPUメモリの利用状況を確認するためには nvidia-smi
や nvidia-smi -q -d MEMORY
などの各種コマンドを利用できます。
$ nvidia-smi -q -d MEMORY >>> ==============NVSMI LOG============== Timestamp : Sat Nov 30 16:02:56 2019 Driver Version : 418.67 CUDA Version : 10.1 Attached GPUs : 1 GPU 00000000:00:04.0 FB Memory Usage Total : 11441 MiB Used : 0 MiB Free : 11441 MiB BAR1 Memory Usage Total : 16384 MiB Used : 2 MiB Free : 16382 MiB
さらに watch
コマンドと組み合わせると指定秒毎に情報を取得することができます。
$ watch -n 10 'nvidia-smi -q -d MEMORY' # 10秒ごとに表示を繰り返す
これらの機能は, GPUメモリを溢れさせないためのモニタリングに適しています。
しかし問題なのは, どんな時でも使える訳ではなさそうという点です。
機械学習をシェルから走らせる場合にはいいかもしれませんが, Jupyter Notebook, Google Colab, Kaggle KernelなどのNotebook環境でこれをやろうとすると監視の無限ループだけが回り続けて他のセルが実行できなくなるので使い物になりません。
実装してみる
さっそく実装してみました。コードはこちらです。
import subprocess import shlex def gpuinfo(): """ Returns size of total GPU RAM and used GPU RAM. Parameters ---------- None Returns ------- info : dict Total GPU RAM in integer for key 'total_MiB'. Used GPU RAM in integer for key 'used_MiB'. """ command = 'nvidia-smi -q -d MEMORY | sed -n "/FB Memory Usage/,/Free/p" | sed -e "1d" -e "4d" -e "s/ MiB//g" | cut -d ":" -f 2 | cut -c2-' commands = [shlex.split(part) for part in command.split(' | ')] for i, cmd in enumerate(commands): if i==0: res = subprocess.Popen(cmd, stdout=subprocess.PIPE) else: res = subprocess.Popen(cmd, stdin=res.stdout, stdout=subprocess.PIPE) total, used = map(int, res.communicate()[0].decode('utf-8').strip().split('\n')) info = {'total_MiB':total, 'used_MiB':used} return info
実行してみます。
In: gpuinfo() Out: {'total_MiB': 11441, 'used_MiB': 0}
GPUのメモリのサイズと使用量が取得できました。
自動的に繰り返すとまではいきませんが, これでNotebookの好きな箇所でGPU使用状況を確認することができます。
表示を洗練させてみる
ログに残しやすいよう, 少し手を加えてメッセージ文字列を出力させてみます。
def gpulife(): """ Returns GPU usage information in string. Parameters ---------- None Returns ------- msg: str """ def _gpuinfo(): command = 'nvidia-smi -q -d MEMORY | sed -n "/FB Memory Usage/,/Free/p" | sed -e "1d" -e "4d" -e "s/ MiB//g" | cut -d ":" -f 2 | cut -c2-' commands = [shlex.split(part) for part in command.split(' | ')] for i, cmd in enumerate(commands): if i==0: res = subprocess.Popen(cmd, stdout=subprocess.PIPE) else: res = subprocess.Popen(cmd, stdin=res.stdout, stdout=subprocess.PIPE) return tuple(map(int, res.communicate()[0].decode('utf-8').strip().split('\n'))) total, used = _gpuinfo() percent = int(used / total * 100) msg = 'GPU RAM Usage: {} {}/{} MiB ({:.1f}%)'.format('|' * (percent // 5) + '.' * (20 - percent // 5), used, total, used/total*100) return msg
このようになりました。
In: gpulife() Out: GPU RAM Usage: .................... 0/11441 MiB (0.0%)
なお, 上記のコードはいまのところ1GPUの場合にしか対応していません。
複数GPUの場合はそのままでは使えませんので, ご注意ください。
また, 動作確認はGoogle Colabでしか行っていません。その点もご容赦ください。
詳細
nvidia-smi -q -d MEMORY
の出力結果をストリームエディタで加工しているだけです。
シェルのコマンドは subprocess
モジュールを使えばPythonで実行できますが, パイプラインがある場合は単に subprocess.run()
に渡すだけでは実行できないため, Python上でパイプラインを構築しています。
参考資料
公式ドキュメント subprocess --- サブプロセス管理
[Python]可読性を上げるための、docstringの書き方を学ぶ(NumPyスタイル)
Livedoorニュースコーパスを文書分類にすぐ使えるように整形する
はじめに
日本語文書分類タスクのための代表的なコーパスの1つ,Livedoorニュースコーパス。
Livedoorニュースのニュース記事を収集して生成されており,9種類のニュース記事が計7367本収載されています。
登録なしで無償利用でき,便利なのですが,そのままでは機械学習で使えるデータセットの形になっていません。
そこで,シェルでささっと整形していきたいと思います。
本題
コーパスの入手&展開
早速コーパスを入手しましょう。
専用のdataset
ディレクトリを作り, その内部にtarアーカイブをダウンロードし, 展開します。
$ mkdir dataset $ cd dataset $ wget https://www.rondhuit.com/download/ldcc-20140209.tar.gz $ tar zxvf ldcc-20140209.tar.gz
するとこのような構成のディレクトリが出来上がります。
dataset └ text ├ CHANGES.txt ├ README.txt ├ dokujo-tsushin │ ├ LICENSE.txt │ ├ dokujo-tsushin-4778030.txt │ │ ... │ └ dokujo-tsushin-6915005.txt ├ it-life-hack │ ├ LICENSE.txt │ ├ it-life-hack-6292880.txt │ ... ├ kaden-channel │ ├ LICENSE.txt │ ├ kaden-channel-6054293.txt │ ... ├ livedoor-homme │ ├ LICENSE.txt │ ├ livedoor-homme-4568088.txt │ ... ├ movie-enter │ ├ LICENSE.txt │ ├ movie-enter-5840081.txt │ ... ├ peachy │ ├ LICENSE.txt │ ├ peachy-4289213.txt │ ... ├ smax │ ├ LICENSE.txt │ ├ smax-6507397.txt │ ... ├ sports-watch │ ├ LICENSE.txt │ ├ sports-watch-4597641.txt │ ... └ topic-news ├ LICENSE.txt ├ topic-news-5903225.txt ...
各カテゴリのニュース記事はdokujo-tsushin
からtopic-news
までの該当するディレクトリに格納されています。
ファイル名はいずれも <ディレクトリ名>-xxxxxxx.txt
という形式です。
tsvファイルの作成
ここから,各ニュース記事の ①ファイル名,②本文,③カテゴリのone-hot encoding を格納したtsvファイルを作っていきましょう。
まず,こんな感じの↓カラムだけを作っていこうと思います。
filename | article | dokujo-tsushin | it-life-hack | kaden-channel | livedoor-homme | movie-enter | peachy | smax | sports-watch | topic-news |
---|---|---|---|---|---|---|---|---|---|---|
これはワンライナーで書くことができます。
カラムができたらdataset/text/livedoor.tsv
に格納しましょう。
$ echo -e "filename\tarticle"$(for category in $(basename -a `find ./text -type d` | grep -v text | sort); do echo -n "\t"; echo -n $category; done) > ./text/livedoor.tsv
続いて,dokujo-tsushin
からtopic-news
までの各ディレクトリ内のニュース記事の情報をtsvファイルに追記していきます。
こちらも頑張れば1行で書けそうですが,今回はカテゴリ毎に別々のコマンドとして実行します。
$ for filename in `basename -a ./text/dokujo-tsushin/dokujo-tsushin-*`; do echo -n "$filename"; echo -ne "\t"; echo -n `sed -e '1,3d' ./text/dokujo-tsushin/$filename`; echo -e "\t1\t0\t0\t0\t0\t0\t0\t0\t0"; done >> ./text/livedoor.tsv $ for filename in `basename -a ./text/it-life-hack/it-life-hack-*`; do echo -n "$filename"; echo -ne "\t"; echo -n `sed -e '1,3d' ./text/it-life-hack/$filename`; echo -e "\t0\t1\t0\t0\t0\t0\t0\t0\t0"; done >> ./text/livedoor.tsv $ for filename in `basename -a ./text/kaden-channel/kaden-channel-*`; do echo -n "$filename"; echo -ne "\t"; echo -n `sed -e '1,3d' ./text/kaden-channel/$filename`; echo -e "\t0\t0\t1\t0\t0\t0\t0\t0\t0"; done >> ./text/livedoor.tsv $ for filename in `basename -a ./text/livedoor-homme/livedoor-homme-*`; do echo -n "$filename"; echo -ne "\t"; echo -n `sed -e '1,3d' ./text/livedoor-homme/$filename`; echo -e "\t0\t0\t0\t1\t0\t0\t0\t0\t0"; done >> ./text/livedoor.tsv $ for filename in `basename -a ./text/movie-enter/movie-enter-*`; do echo -n "$filename"; echo -ne "\t"; echo -n `sed -e '1,3d' ./text/movie-enter/$filename`; echo -e "\t0\t0\t0\t0\t1\t0\t0\t0\t0"; done >> ./text/livedoor.tsv $ for filename in `basename -a ./text/peachy/peachy-*`; do echo -n "$filename"; echo -ne "\t"; echo -n `sed -e '1,3d' ./text/peachy/$filename`; echo -e "\t0\t0\t0\t0\t0\t1\t0\t0\t0"; done >> ./text/livedoor.tsv $ for filename in `basename -a ./text/smax/smax-*`; do echo -n "$filename"; echo -ne "\t"; echo -n `sed -e '1,3d' ./text/smax/$filename`; echo -e "\t0\t0\t0\t0\t0\t0\t1\t0\t0"; done >> ./text/livedoor.tsv $ for filename in `basename -a ./text/sports-watch/sports-watch-*`; do echo -n "$filename"; echo -ne "\t"; echo -n `sed -e '1,3d' ./text/sports-watch/$filename`; echo -e "\t0\t0\t0\t0\t0\t0\t0\t1\t0"; done >> ./text/livedoor.tsv $ for filename in `basename -a ./text/topic-news/topic-news-*`; do echo -n "$filename"; echo -ne "\t"; echo -n `sed -e '1,3d' ./text/topic-news/$filename`; echo -e "\t0\t0\t0\t0\t0\t0\t0\t0\t1"; done >> ./text/livedoor.tsv
これでtsvファイルの準備ができました。
dataset/text/livedoor.tsv
をPandasで開くと次のような表ができているはずです。あとは存分に機械学習していきましょう!
filename | article | dokujo-tsushin | it-life-hack | kaden-channel | livedoor-homme | movie-enter | peachy | smax | sports-watch | topic-news |
---|---|---|---|---|---|---|---|---|---|---|
dokujo-tsushin-4778030.txt | <記事の本文> | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
topic-news-6907153.txt | <記事の本文> | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
注意
Jupyter NotebookやGoogle Colaboratoryのセル内で実行させた場合,ディレクトリの遷移状態の違いによるエラーが起こることがあります。
その場合は上記スクリプト中のパス表記を ./text/hogehoge
から /dataset/text/hogehoge
のように修正してください。
自然言語処理タスクを概観する(6) Multi-modal task
NLP Progress という素晴らしいリポジトリを見つけました。整理の意味を込めてまとめます。
NLPの種々のタスクとそのSOTAが掲載されています。
NLPのベンチマークとなる有名なデータセットも一緒に紹介されており,NLP論文を読むうえで大きな助けとなってくれるでしょう。
ここまで6本の記事でNLPタスクを概観してきましたが, これでいよいよ最後です。
マルチモーダルタスクを概観していきます。
近年可能となってきた, 文体を扱うタスクもここで見ていきます。
そろそろ NLP Progress のみでは内容が不足気味となってきました。ここでは Papers With Code の内容も織り交ぜていきます。
10. Text Style
10-1. 文体変換 Text Style Transfer
- 概要
- 入力文の内容を変えずに文体のみを変えたものを出力する.
- タスク設定によって, 変換後の文体のparallel corpusが存在する場合としない場合がある.
- 何をもって評価するか自体も議論の対象になっている. (例: Remi et al., NAACL 2019)
- データセット例
- 英語
- GYAFC (Grammarly's Yahoo Answers Formality Corpus) Dataset (2018) (PAPER)
- あまり大規模なShared Taskは行われていない印象がある.
- 英語
- 上位モデル例
- 近年はEncoder-Decoderベースの手法がよくとられている. Encode後の潜在空間でどのような写像を構成するかが鍵?
- Variational Autoencoder Based: Hu et al., 2017 etc.
- Encoder-Decoder + Cross Projection in Latent Space: Mingyune et al. 2019 etc.
- こちらのリポジトリに最新論文がよくまとまっています.
11. Multi-modal Task
11-1. 言語×画像
11-1-1. 画像のキャプション生成 (Image Captioning)
- 概要
- 与えられた画像に対するキャプションを自動生成する.
- 何をもって良いキャプションとするかは難しく, 人間による評価や, BLEU, METEOR, ROUGE, CIDERなどの種々の指標が用いられる.
- データセット例
- 英語
- Pascal VOC 2008
- 1000画像に5文ずつキャプションが付与されたデータセット. サイズが小さいためtestにのみ用いられる.
- Flickr8k, Flickr30k
- それぞれ8000, 3万画像に5文ずつキャプションが付与されたデータセット.
- MSCOCO (Microsoft Common Objects in Context)
- 本来は物体認識などのためのデータセットだが, 約16万画像に5文ずつキャプションが付与されてもいる.
- SBU
- Pascal VOC 2008
- 英語
- 上位モデル例
- Attention-based: BUTD (Bottom-up and Top-down attention)
- Encoder-Decoder: Vision Deep CNN + Language Generating RNN
11-1-2. VQA (Visual Question Answering)
- 概要
- 画像とそれに関する質問文が与えられ, 正しい解答を出力する. タスクによって択一式解答か短文による解答かは異なる.
- キャプション生成よりも定量的評価がしやすいためか, shared taskの対象となりやすい.
データセット例
- 英語
- NLVR (Natural Language and Vision Readoning) (2017) (PAPER)
- 丸や三角などの図形が多数配置された画像と, テキストが与えられ, テキストが画像の正しい描写になっているかどうかを True/False の二択で答えるデータセット.
- VQA (Visual Question Answering) Dataset (2015) (PAPER)
- MS COCO Dataset (主に物体認識などに特化) の約20万画像に, Abstract Scenes Datasetとして約5万画像を加えて構成している.
- 解答形式は択一式と短文の両方に対応している.
- VQA v2.0 Dataset (2017) (PAPER)
- VQAの偏りを改善したデータセット.
- 具体的には, 似たような画像群に対する正答の分布に生じていたバラつきを解消させるようなデータを追加している.
- VG (Visual Genome) Dataset (2016) (PAPER)
- 画像と言語のさまざまなデータ対が与えられたデータセット.
- 画像全体に対し, scene graphが付与されている.
- 画像中の各instanceに対し, bounding box・描写文・region graphが付与されている.
- さらに, 質問応答のためのデータとして,
- Region-Based QA: あるinstanceに関する質問文とそれに対する正答(正しい対象instanceと解答文)が付与されている.
- Free-Form QA: 特にinstanceを限定しない質問文とそれに対する正答(解答文)が付与されている.
- GQA Dataset (CVPR 2019) (PAPER)
- NLVR (Natural Language and Vision Readoning) (2017) (PAPER)
- 英語
上位モデル例
- Transformer系: LXMERT, ViLBERT, Visual-BERT etc.
- Attention-based: BUTD (Bottom-up and Top-down Attention) etc.
11-1-3. Visual Entailment
- 概要
- 含意関係認識 のマルチモーダルバージョン.
- 前提 (premise) と仮説 (hypothesis) が両方テキストで用意されるのではなく,前提 (premise) が画像で仮説 (hypothesis) がテキストになっている.
- つまり,画像とテキストが矛盾していないかどうかを Entailment, Neutral, Contradiction の3択で答えるタスク.
データセット例
- 英語
上位モデル例
- Transformer系: UNITER (ECCV 2020)
11-2. 言語×データ
11-2-1. Data-to-Text Generation
- 概要
- 表形式のデータベースなどから, そのデータを過不足なく含んだ自然な文章を生成する.
- このタスクでは変換元のデータは MRs (Meaning Representations) と呼ばれる.
- データセット例
- 上位モデル例
- Seq2seq+Attention+CNN+Ensembling: Juraska et al., 2018 etc.
- E2E NLG Challengeの結果はこの論文(INLG 2018)やこのページ(Papers With Code)に掲載されています.
まとめ
Cross-modalなタスクに対しても, CNN+RNN → Attention → Transformerという進化の流れは同様に起きています.
今まさにホットな分野であるためか, 進歩が速く, NLP ProgressやPapers With Codeなどへの掲載が追いついていない最新の成果も多くみられます.
ここまで, 全6記事でNLPの各種タスクを概観してきました.
以前よりもNLPの全体像がよりよく描けるようになった気がしています.
自然言語処理タスクを概観する(5) 言語モデル, 情報抽出, 意味, 知識など
NLP Progress という素晴らしいリポジトリを見つけました。整理の意味を込めてまとめます。
NLPの種々のタスクとそのSOTAが掲載されています。
NLPのベンチマークとなる有名なデータセットも一緒に紹介されており,NLP論文を読むうえで大きな助けとなってくれるでしょう。
ついにここまでやって来ました。言語の意味などを扱う,より高度なタスクを概観していきます。
7. 言語モデル Language Modeling
- 概要
- データセット例
- 英語
- 単語単位
- PTB (Penn Treebank)
- WikiText-2
- Wikipedia英語版の記事から構成したもので, 約200万語彙からなる.
- WikiText-103
- Wikipedia英語版の記事から構成したもので, 約26.8万語彙からなる.
- 全単語が3回以上登場するように構成されている.
- 1B Words / Google Billion Word benchmark
- ニュース解説サイトから構成したもので, 約8.3億トークン, 約80万語彙からなる.
- 文脈の影響を排除するために文は意図的に並べ替えてある.
- 文字単位
- 単語単位
- 日本語
- BCCWJ (日本語書き言葉均衡コーパス)
- Google 日本語 n-gram コーパス
- 言語資源協会 (GSK) 経由で配布されている.
- 新聞コーパス
- 英語
- 上位モデル例
- Transformer系: Transformer XL etc.
8. 意味解析 Semantic Parsing
8-1. 抽象的意味表現 AMR (Abstract Meaning Representation)
概要
- 意味を, 有向グラフの集合として表現する.
データセット例
上位モデル例:
- 複数のモデルの組み合わせが用いられる.
8-2. SQL parsing
- 概要
- 入力された質問文から, それに正しく応答するためのSQL文を自動生成する.
- こちらは生成するのが文であるため, seq2seqなアプローチが利用可能.
- データセット例
- 英語
- ATIS
- Advising
- GeoQuery
- Scholar
- Spider
- cross-domainなデータセット.
- WikiSQL
- 英語
- 上位モデル例
- RNN系: Seq2Seq + Copying etc.
9. 知識獲得など
9-1. Taxonomy Learning
- 概要
- データセット例
- 英語
- SemEval 2018 Task 9
- general domain
- medical domain
- MEDLINE (Medical Literature Analysis and Retrieval System) に収載の医学書や医学論文abstractから構成したデータセット.
- music domain
- SemEval 2018 Task 9
- 英語
- 上位モデル例
9-2. Common Sense
- 概要
- 単なるパターン認識を超え, 常識を利用した推論をめざすタスク.
- データセット例
- 英語
- Event2Mind
- 複数の人物が登場する, 日常生活の出来事を描写した文が与えられる.
- 登場人物の行動について, その意図や受け手の反応を推定する.
- SWAG (Situations with Adversarial Generations)
- 動画キャプション生成用データセットの, キャプション文のみから作成したもの.
- LSMDC (Large Scale Movie Description Challenge)
- Activity Net
- キャプションの前半部分が与えられ, その続きとして整合性のあるものを4つの選択肢から (動画情報は使わずに) 正しく選ぶ.
- 動画キャプション生成用データセットの, キャプション文のみから作成したもの.
- Winograd Schema Challenge
- WNLI (Winograd Schema Challenge NLI)
- VCR (Visual Commonsense Reasnoning)
- 視覚情報の理解をめざしたデータセット.
- 画像とその内容についての質問文が与えられ, 質問への正答を選択肢からえらぶ.
- さらに, なぜその選択肢を選んだのかという根拠もあわせて択一式で答えさせる.
- ReCoRD (Reading Comprehension with Commonsense Reasnoning Dataset)
- CNN / Daily News の記事から構成したデータセット.
- 問題設定は機械読解のタスクと似ているが, 質問文の作成を自動化してある.
- Event2Mind
- 英語
- 上位モデル例
- Transformer系: RoBERTa, XLNet, BERT etc.
9-3. 情報抽出 IE (Information Extraction)
9-3-1. Open Knowledge Graph Canonicalization
- 概要
- ウェブ上の情報から知識データベースを構成するための標準化作業を行う.
- 例えば, {Barack Obama, was born in, Honolulu} と {Obama, took birth in, Honolulu} が同一であることを正しく認識する.
- データセット例
- ReVerb, NELLなどの既存の OpenIE (Open Information Extraction) が抽出した情報を利用して構成している.
- Base, Ambiguous, ReVerb45Kなどの種類がある.
- ReVerb, NELLなどの既存の OpenIE (Open Information Extraction) が抽出した情報を利用して構成している.
- 上位モデル例
まとめ
ここまで来るとタスクが高度かつ複雑になるため,単一のできあいのモデルをそのまま使うだけでは太刀打ちできません(問題設定やデータセットにもよりますが)。
次回がこのシリーズの最後となる予定です。
自然言語処理タスクを概観する(4) 系列変換, 生成, 対話
NLP Progress という素晴らしいリポジトリを見つけました。整理の意味を込めてまとめます。
NLPの種々のタスクとそのSOTAが掲載されています。
NLPのベンチマークとなる有名なデータセットも一緒に紹介されており,NLP論文を読むうえで大きな助けとなってくれるでしょう。
対話システムについては私はほとんど知識がなく, ごく簡素にしか書いていません。
5. 系列変換・生成タスクとその変形
5-1. 文法誤り訂正 GEC(Grammatical Error Correction)
- 概要
- 入力文の文法誤りを検出する. または, 訂正した結果を出力する.
- データセット例
- 英語
- CoNLL-2014
- JFLEG (2017)
- BEA Shared Task 2019
- Unrestricted Track: どんなデータセットを用いてもよい.
- Restricted Track: 以下の4つのデータセットのみ使用可.
- W&I+LOCNESS (1998)
- FEC (2011)
- NAIST Lang-8 Corpus of Learner English (2011)
- NUCLE (2013)
- Low Resource Track: W&I+LOCNESSのみ使用可.
- 日本語
- 英語
- 上位モデル例
- Transformer系: Transformer+Pretrain with Pseudo Data, Copy-Augmented Transformer etc.
- CNN系: CNN + Seq2Seq etc.
- ほか: SMT + BiGRU
5-2. 語彙正規化 Lexical Normalization
- 概要
- 標準的でない語彙を標準的な語彙に変換する.
- ソーシャルメディアの文書などが対象となることが多い.
- 標準化に伴って文章の単語長が変化することもありうるが, そのような変換はこのタスクでは考慮しない.
- つまり, 単語ごとの逐次的な変換のみを行い, 単語の挿入/削除/順序入れ替えを伴うような変換は行わない.
- 例: new pix comming tomoroe -> new pictures coming tomorrow
- データセット例
- 英語
- LexNorm
- アノテーション付きのツイートからなる.
- LexNorm
- 英語
- 上位モデル例
- 非ニューラルな手法が用いられている.
5-3. 機械翻訳 Machine Translation
- 概要
- Source Language の入力文を Target Language に翻訳して出力する.
- 評価指標には BLEU, METEOR などがあるが, 一長一短ある.
- データセット例
- 英語-ドイツ語
- WMT 2014 EN-DE
- 英語-フランス語
- WMT 2014 EN-FR
- 英語-ドイツ語
- 上位モデル例
- Transformer系: Transformer Big + Back-Translation etc.
- CNN系: ConvS2S
5-4. 平易化 Simplification
- 概要
- 入力文の意味を変えずに, 初学者などにとってより可読性の高い文に変換する.
- 具体例:
- Unusual concept を説明する.
- 例: small mammals -> small mammals (such as mice or rats)
- Unusual word を Familiar term/phrase に置き換える.
- 例: comprising -> there are about
- 重文や複文をいくつかの単文に分離する.
- 重要でない情報を省略する.
- Unusual concept を説明する.
- 系列ラベリング, 文書要約, 機械翻訳, 情報抽出などの前処理としても用いられる.
- データセット例
- 英語
- Main-Simple English Wikipedia
- PWKP/WikiSmall
- Turk Corpus
- Newsela
- Splits
- 英語
- 上位モデル例
- 汎用的なモデルはあまり用いられていない印象がある.
5-5. 文書要約 Summarization
- 概要
- 1つまたは複数の文書の内容を要約した新たな短い文書を出力する.
- 評価指標には METEOR, ROUGE などを用いるが, 限界も多い.
- データセット例
- 上位モデル例
- GAN, 強化学習, BERT, Convolutional Seq2Seqなど.
5-6. 圧縮 Sentence Compression
- 概要
- 入力文から, 文法的な正しさと重要な情報は保持したまま冗長な部分だけを削除する.
- つまり出力文は入力文のsubsetとなる.
- 評価指標は F1 score, Compression Rateなど.
- データセット例
- 英語
- Google Dataset (2013)
- 英語
- 上位モデル例
- RNN系: BiRNNLM, BiLSTM etc.
6. 対話 Dialogue
6-1. Dialogue Act Classification
- 概要
- 対話中における各発言の役割 (act) を正しく推定する.
- 役割は Speech Act Theory に詳しい.
- データセット例
- 英語
- 上位モデル例
- Attention-based: CRF+Attentive Structure Network etc.
- RNN系: BiLSTM+CRF etc.
6-2. Dialogue State Tracking
- 概要
- 対話の各場面で刻々と変化するユーザーの要求を正しく推定する.
- データセット例
- 英語
- DSTC2 (The Second Dialogue Systems Technology Challenges)
- WoZ 2.0 (Wizard-of-Oz)
- MultiWOZ
- 英語
- 上位モデル例
- 単純な汎用モデルをそのまま適用している事例は少ない.
6-3. Retrieal-based Chatbots
- 概要
- 対話システムのうち, 最適な応答を既存の選択肢から選んで出力するもの.
- データセット例
- 上位モデル例
- Transformer系: BERT etc.
- RNN系: ELMo
6-4. Generative-based Chatbots
- 概要
- 対話システムのうち, 最適な応答文を生成して出力するもの.
- データセット例
- 英語
- ConvAI2 (The COnversational Intelligence Challenge 2)
- 英語
- 上位モデル例
- Transformer系: Transformerの転移学習モデルetc.
- RNN系: Seq2Seq2+Attention etc.
6-5. Disentanglement
- 概要
- 同一のチャンネル上で同時進行している複数の対話をそれぞれの対話に正しく分離する.
- データセット例
- 上位モデル例
- Transformer系: Transformerの転移学習モデルetc.
- RNN系: Seq2Seq2+Attention etc.
まとめ
系列変換タスクでもBERT族は強さを発揮しています。
一方, 対話システムは対話履歴などを利用しなければならないため複数のモデルを組み合わせる傾向にあるようです。
自然言語処理タスクを概観する(3) 系列ラベリングとその変形
NLP Progress という素晴らしいリポジトリを見つけました。整理の意味を込めてまとめます。
NLPの種々のタスクとそのSOTAが掲載されています。
NLPのベンチマークとなる有名なデータセットも一緒に紹介されており,NLP論文を読むうえで大きな助けとなってくれるでしょう。
4. 系列ラベリングとその変形
4-1. 品詞タグ付け POS(Part-of-speech) Tagging
- 概要
- データセット例
- 英語
- PTB (Penn Treebank)
- Penn Treebankの収載コーパスのうちWall Street Journalの記事にアノテーションを施したもの.
- 品詞タグの候補は全部で48種類ある (The Penn Treebank POS tagset).
- Ritter et al. の Dataset
- PTB (Penn Treebank)
- 多言語
- UD (Universal Dependencies)
- 2019年11月現在は90ヶ国語が利用可能.
- UD (Universal Dependencies)
- 英語
- 上位モデル例
- Transformer系: Multilingual BERT etc.
- RNN系: Meta BiLSTM, Char BiLSTM, BiLSTM+CRF etc.
- CNN系: CNN+CRF etc.
- ニューラル以前の手法としてはHMM, 最大エントロピーモデル, MEMM, CRF, ビーム探索などがある.
- 補足: 日本語形態素解析器の手法
4-2. 浅い構文解析 Shallow Syntax2
- 概要
- データセット例
- 英語
- PTB (Penn Treebank)
- 英語
- 上位モデル例
- RNN系: Flair embeddings + BiLSTM + CRF
4-3. 構文解析 Parsing (Syntactic Analysis)
4-3-1. 句構造解析 Constituency Parsing (Phrase Structure Analysis)
- 概要
- データセット例
- 英語
- PTB (Penn Treebank)
- 英語
- 上位モデル例
- Transformer系: Label Attention Layer + HPSG + XLNet etc.
- RNN系: Flair embeddings + BiLSTM + CRF
4-3-2. 依存構造解析(係り受け解析) Dependency Parsing3
- 概要
- データセット例
- 英語
- PTB (Penn Treebank)
- 元のデータは句構造ツリーであるため, Stanford typed dependencies manual に基づいて係り受け木に構成しなおしたものが用いられる.
- 依存関係ラベルはおよそ50種類定義されている.
- PTB (Penn Treebank)
- 多言語
- 英語
- 上位モデル例
- Transformer系: Label Attention Layer + HPSG + XLNet etc.
- 補足 - 日本語係り受け解析器の手法
4-3-2a. Cross-Lingual Zero-Shot Dependency Parsing
- 概要
- Source Languageで教師あり学習した依存構造解析器を, 新たな教師データなしでTarget Languageに使用する.
- 上位モデル例
- RNN系: Cross-Lingual ELMo
4-3-2b. 教師なし依存構造解析 Unsupervised Dependency Parsing
- 概要
- ラベル付き教師データを使用することなく依存構造解析を行う.
- 上位モデル例
- 非ニューラルアプローチが用いられる.
4-4. 深い構文解析
- 概要
- 通常の文脈自由文法 (CFG (Context Free Grammar)) による構文解析では扱い切れない曖昧性を扱う手法.
4-4-1. 組み合わせ範疇文法 CCG (Combinatory Categorical Grammar)
- 概要
- 品詞ラベルの発展形であるカテゴリラベルを利用して導出 Derivation を作成する.
- 導出とは CCG における独特の用語で, 意味は構文木と同様.
4-4-1a. 組み合わせ範疇文法による構文解析
4-4-1b. 組み合わせ範疇文法によるsupertagging
- 概要
- 処理の高速化を狙ってカテゴリラベルの付与だけを先に行ってしまう.
- データセット例
- CCGBank
- 上位モデル例
- RNN系: BiLSTM + Cross View Training etc.
4-4-1c. 構文木への変換 Conversion to PTB
4-5. 意味役割付与(述語項構造解析) Semantic Role Labeling
- 概要
- 入力文から形容詞や動詞を同定し, さらにそれらが掛かる箇所について, 正しい格に対応したBIOラベルを付与する.
- 用意するラベルは表層格にもとづく場合と深層格にもとづく場合がある.
- 英語などでは表層格を対象とすることは少ない (語順の情報が利用できるため)
- データセット例
- 英語
- 深層格にもとづくもの
- FrameNet
- PropBank
- OntoNotes
- 深層格にもとづくもの
- 日本語
- 表層格にもとづくもの
- 深層格にもとづくもの
- 他言語
- Universal Propbank: 中国語, フランス語, ドイツ語, イタリア語, スペイン語, ポルトガル語, フィンランド語.
- 英語
- 上位モデル例
- LSTM系: BiLSTM+ELMo etc.
4-6. 共参照解析 Coreference Resolution
- 概要
- 文中のトークンのうち同一の実体 (Entity) を指すものの組を正しく同定する.
- 共参照 coreference と照応 anaphora は共通する部分もあるが, 基本的には別の概念.
- データセット例
- 英語
- CoNLL 2012
- 英語
- 上位モデル例
- ELMo+α.
4-7. 固有表現抽出 NER (Named Entity Recognition)
- 概要
- 入力文から固有表現 (人名, 地名など) を正しく同定する.
- 具体的には, 各トークンに固有表現の種類に応じたBIOラベルを付与する.
- データセット例
- 英語
- CoNLL 2003
- WNUT 2017
- OntoNotes v5
- 英語
- 上位モデル例
- Transformer系: LSTM+CRF+ELMo+BERT+Flair etc.
- CNN系: CNN Large etc.
- LSTM系: BiLSTM+CRF+ELMo etc.
4-8. エンティティリンキング Entity Linking
- 概要
- 固有表現抽出と語義曖昧性解消がセットになったようなタスク.
- 文中の語にWikipediaなどへのリンクを適切に付与する.
- End-to-End approach: 固有表現抽出とリンクの付与を同時に行う
- Disambiguation-Only approach: 抽出済みの固有表現にリンクの付与を行う
- データセット例
- 英語
- AIDA CoNLL-YAGO Dataset
- 英語
- 上位モデル例
- RNN系: DeepType
まとめ
ここまで来ると, トークンごとの多クラス分類問題もしくは系列変換としての性格が強くなってきますが, やはりBERT族が圧倒的な強さを発揮します。
個人的には教師なし学習がどのように進化していくのかが気になります。
さらに続きます。