日本語自然言語処理のData Augmentationライブラリdaajaを作りました
概要
こんにちは@kajyuuenです。
日本語自然言語処理のData Augmentationライブラリdaaja
を作成しました。
この記事ではdaaja
が実装しているData Augmentation手法についての解説とその使い方について紹介します。
また、このライブラリはPyPIに公開しているのでpip install daaja
でインストールが可能です。
はじめに
Data Augmentationとは
Data Augmentationとは元のデータから新しいデータを生成し、データ数を増やす手法です。 日本語ではデータ拡張という名前で知られています。 ラベル付きデータを擬似的に増やすことによって、アノテーションコストを必要とせずにモデルの汎化性能や精度の向上が期待できます。
対応している手法
現在daaja
は、次の2つの論文で紹介されているData Augmentation手法を実装しています。
- EDA: Easy Data Augmentation Techniques for Boosting Performance on Text Classification Tasks
- An Analysis of Simple Data Augmentation for Named Entity Recognition
EDAが文書分類タスク向け、SDAが固有表現抽出向けのData Augmentation手法です。それぞれの手法について、詳しく説明していきます。
EDA: Easy Data Augmentation Techniques for Boosting Performance on Text Classification Tasks
この論文では自然言語処理の分類タスクに使える4つのData Augmentation手法について紹介しています。それぞれの手法について、詳細とdaaja
を使った呼び出し方を記載します。
各手法について
Synonym Replacement
これは文章中のストップワードを除いた$N$つの単語を同義語に置き換える手法です。ハイパーパラメータ$\alpha$と文章中の単語数$l$によって、この$N$は定まり、$N=l\alpha$と計算されます。$N$はこのあと紹介する手法のRandom Insertion, Synonym Replacementでも同じように算出されます。
from daaja.augmentors.sentence.synonym_replace_augmentor import \ SynonymReplaceAugmentor augmentor = SynonymReplaceAugmentor(alpha=0.1) text = "日本語でデータ拡張を行う" print(augmentor.augment(text)) # => 日本語でデータ拡張をする
Random Insertion
文章中のストップワードを除いた$N$つの単語の同義語をランダムに挿入する手法です。
from daaja.augmentors.sentence.randam_insert_augmentor import \ RandamInsertAugmentor augmentor = RandamInsertAugmentor(alpha=0.1) text = "日本語でデータ拡張を行う" print(augmentor.augment(text)) # => 日本語でデータ拡張押し広げるを行う
Random Swap
文章中の2つの単語をランダムに選び、入れ替える処理を$N$回行う手法です。
from daaja.augmentors.sentence.randam_swap_augmentor import RandamSwapAugmentor augmentor = RandamSwapAugmentor(alpha=0.1) text = "日本語でデータ拡張を行う" print(augmentor.augment(text)) # => データで日本語拡張を行う
Random Deletion
文章中の各単語を確率$p$でランダムに削除する手法です。この$p$はハイパーパラメータで、論文中では$p=\alpha$としています。
from daaja.augmentors.sentence.randam_delete_augmentor import \ RandamDeleteAugmentor augmentor = RandamDeleteAugmentor(p=0.1) text = "日本語でデータ拡張を行う" print(augmentor.augment(text)) # => 日本語でデータを行う
EasyDataAugmentor
EasyDataAugmentor
はそれぞれの手法のハイパーパラメータ$\alpha$と生成する文章数$n$を指定し、今まで紹介した4つの手法を一度に実行するクラスです。
from daaja.methods.eda.easy_data_augmentor import EasyDataAugmentor augmentor = EasyDataAugmentor(alpha_sr=0.1, alpha_ri=0.1, alpha_rs=0.1, p_rd=0.1, num_aug=4) text = "日本語でデータ拡張を行う" print(augmentor.augments(text)) # => ['日本語でを拡張データ行う', '日本語でデータ押広げるを行う', '日本語でデータ拡張を行う', '日本語で智見拡張を行う', '日本語でデータ拡張を行う']
An Analysis of Simple Data Augmentation for Named Entity Recognition
この論文では自然言語処理の固有表現抽出タスクに使える4つのData Augmentation手法について紹介しています。
各手法について
Label-wise token replacement (LwTR)
文章中の各単語を二項分布${\rm Bin}(p)$に基づいて別の単語に置き換えます。 置き換え先の単語はデータセット中の同じラベルを持つ他の単語から選択されます。
from daaja.augmentors.sequence_labeling.labelwise_token_replacement_augmentor import \ LabelwiseTokenReplacementAugmentor from daaja.augmentors.sequence_labeling.utils import get_token2prob_in_label tokens_list = [ ["私", "は", "田中", "と", "いい", "ます"], ] labels_list = [ ["O", "O", "B-PER", "O", "O", "O"], ] augmentor = LabelwiseTokenReplacementAugmentor(get_token2prob_in_label(tokens_list, labels_list), p=0.1) target_tokens = ["君", "は", "吉田", "さん", "かい"] target_labels = ["O", "O", "B-PER", "O", "O"] print(augmentor.augment(target_tokens, target_labels)) # => (['私', 'は', '田中', 'さん', 'かい'], ['O', 'O', 'B-PER', 'O', 'O'])
Synonym replacement
LwTRと同様に文章中の各単語を二項分布${\rm Bin}(p)$に基づいて別の単語に置き換えます。 置き換え先の単語は置き換え元の単語の同義語から選ばれます。
from daaja.augmentors.sequence_labeling.synonym_replacement_augmentor import \ SynonymReplacementAugmentor augmentor = SynonymReplacementAugmentor(p=0.5) target_tokens = ["君", "は", "吉田", "さん", "かい"] target_labels = ["O", "O", "B-PER", "O", "O"] print(augmentor.augment(target_tokens, target_labels) # => (['雇い主', 'は', '田中', '君', 'かい'], ['O', 'O', 'B-PER', 'O', 'O'])
Mention replacement
文中の各固有表現を二項分布${\rm Bin}(p)$に基づいて、同じタイプの固有表現に置き換えます。
LwTRがラベル単位(B-LOC
やI-LOC
など)の置き換えだったのに対して、こちらはタイプ単位(LOC
など)で置き換えが発生します。
置き換わる単語はデータセット中の同じタイプの固有表現から選ばれます。
from daaja.augmentors.sequence_labeling.mention_replacement_augmentor import \ MentionReplacementAugmentor from daaja.augmentors.sequence_labeling.utils import get_entity_dict tokens_list = [ ["私", "は", "田中", "太郎", "です"], ] labels_list = [ ["O", "O", "B-PER", "I-PER", "O"], ] entity_dict = get_entity_dict(tokens_list, labels_list) augmentor = MentionReplacementAugmentor(entity_dict, p=1) target_tokens = ["君", "は", "吉田", "さん", "かい"] target_labels = ["O", "O", "B-PER", "O", "O"] print(augmentor.augment(target_tokens, target_labels)) # => (['君', 'は', '田中', '太郎', 'さん', 'かい'], ['O', 'O', 'B-PER', 'I-PER', 'O', 'O'])
Shuffle within segments
文章を固有表現のタイプで区切り、各区切りについてシャッフルするか二項分布${\rm Bin}(p)$に基づいて決定します。
from daaja.augmentors.sequence_labeling.shuffle_within_segments_augmentor import \ ShuffleWithinSegmentsAugmentor augmentor = ShuffleWithinSegmentsAugmentor(p=1) target_tokens = ["君", "が", "東京", "出身", "の", "田中", "君", "かい"] target_labels = ["O", "O", "B-LOC", "O", "O", "B-PER", "O", "O"] print(augmentor.augment(target_tokens, target_labels)) # => (['が', '君', '東京', '出身', 'の', '田中', '君', 'かい'], ['O', 'O', 'B-LOC', 'O', 'O', 'B-PER', 'O', 'O'])
SimpleDataAugmentationforNER
SimpleDataAugmentationforNER
はそれぞれの手法のハイパーパラメータと生成する文章数を指定、今まで紹介した4つの手法を一度に実行するクラスです。
from daaja.methods.ner_sda.simple_data_augmentation_for_ner import \ SimpleDataAugmentationforNER tokens_list = [ ["私", "は", "田中", "と", "いい", "ます"], ["筑波", "大学", "に", "所属", "して", "ます"], ] labels_list = [ ["O", "O", "B-PER", "O", "O", "O"], ["B-ORG", "I-ORG", "O", "O", "O", "O"], ] augmentor = SimpleDataAugmentationforNER(tokens_list=tokens_list, labels_list=labels_list, p_power=1, p_lwtr=1, p_mr=1, p_sis=1, p_sr=1, num_aug=4) tokens = ["吉田", "さん", "は", "株式", "会社", "A", "に", "出張", "予定", "だ"] labels = ["B-PER", "O", "O", "B-ORG", "I-ORG", "I-ORG", "O", "O", "O", "O"] augmented_tokens_list, augmented_labels_list = augmentor.augments(tokens, labels) print(augmented_tokens_list) # => [ # ['田中', 'さん', 'ます', '筑波', '大学', '大学', '所属', 'は', 'ます', 'に'], # ['吉田', 'さん', 'は', 'ストック', '企業', 'A', 'に', '出張', '心積り', 'だ'], # ['田中', 'さん', 'は', '筑波', '大学', 'に', '出張', '予定', 'だ'], # ['吉田', 'さん', 'は', '会社', 'A', '株式', '出張', 'に', '予定', 'だ'], # ['吉田', 'さん', 'は', '株式', '会社', 'A', 'に', '出張', '予定', 'だ']] print(augmented_labels_list) # => [ # ['B-PER', 'O', 'O', 'B-ORG', 'I-ORG', 'I-ORG', 'O', 'O', 'O', 'O'], # ['B-PER', 'O', 'O', 'B-ORG', 'I-ORG', 'I-ORG', 'O', 'O', 'O', 'O'], # ['B-PER', 'O', 'O', 'B-ORG', 'I-ORG', 'O', 'O', 'O', 'O'], # ['B-PER', 'O', 'O', 'I-ORG', 'I-ORG', 'B-ORG', 'O', 'O', 'O', 'O'], # ['B-PER', 'O', 'O', 'B-ORG', 'I-ORG', 'I-ORG', 'O', 'O', 'O', 'O']]
おわりに
今回は日本語向けのData Augmentationライブラリdaaja
を紹介しました。
手元でEDAについて軽く実験したところ、性能の向上がみられたので、SDAの実験も終わり次第別記事で公開したいと思います。
参考
ライブラリと実験の実装時に参考にさせていただきました。ありがとうございます。