【エンジニアリング実践】EDA(Easy Data Augmentation)を使ったデータ拡張

        エンジニアリング プロジェクトでは、データ量が不十分なため、データ拡張テクノロジが必要になることがよくあります。データ拡張には EDA を使用してみてください。  

1. EDA の概要

        EDA は、EMNLP-IJCNLP 2019 カンファレンスで米国の Protago Laboratory によって発表された、シンプルだが非常に効果的なテキスト データ強化手法です。EDA は、論文「EDA: テキスト分類タスクのパフォーマンスを向上させる簡単なデータ拡張テクニック」に由来しています。

        テキスト分類タスクのパフォーマンスを向上させる簡単なデータ拡張手法として、この論文では、同義語置換ランダム挿入ランダム交換、およびランダム削除を含む 4 つのデータ拡張スキームを提案します。また、深層学習モデル RNN と CNN について、5 つのデータセットを使用してテキスト分類実験の比較研究を行いました. 実験では、著者は、使用されるデータセットのサイズに応じてトレーニングセットを 3 つのスケールに分割しましたトレーニング データ セット内の EDA テクノロジーのサイズを比較し、その影響を比較します。実験では、EDA がテキスト分類の効果を向上させることも示されています。

2. 強化方法

2-1.同義語置換(SR)

        1. テキストからストップ ワード セットに属さない n 個の単語をランダムに選択し、その同義語をランダムに選択して置換します。

        2. ストップワードに関係なく、文中の単語をランダムに n 個選択し、同義語辞書から同義語をランダムに選択して置換します。同義語に関しては、オープンソースの同義語リスト + ドメインのカスタム語彙を使用して作成できます。

        注:同義語の選択を完了するには、同義語ライブラリを使用する必要があります。

def synonym_replacement(words, n):
    new_words = words.copy()
    random_word_list = list(set([word for word in words if word not in stop_words]))
    random.shuffle(random_word_list)
    num_replaced = 0
    for random_word in random_word_list:
        synonyms = get_synonyms(random_word)
        if len(synonyms) >= 1:
            synonym = random.choice(synonyms)
            new_words = [synonym if word == random_word else word for word in new_words]
            num_replaced += 1
        if num_replaced >= n:
            break

    sentence = ' '.join(new_words)
    new_words = sentence.split(' ')

    return new_words

def get_synonyms(word):
    return synonyms.nearby(word)[0]

2-2. ランダム挿入(ランダム挿入、RI)

        停止語彙にない単語をテキストからランダムに選択し、その同義語セットから単語をランダムに選択し、それを文内のランダムな位置に挿入し、このステップを n 回繰り返します。

def random_insertion(words, n):
    new_words = words.copy()
    for _ in range(n):
        add_word(new_words)
    return new_words

def add_word(new_words):
    synonyms = []
    counter = 0
    while len(synonyms) < 1:
        random_word = new_words[random.randint(0, len(new_words) - 1)]
        synonyms = get_synonyms(random_word)
        counter += 1
        if counter >= 10:
            return
    random_synonym = random.choice(synonyms)
    random_idx = random.randint(0, len(new_words) - 1)
    new_words.insert(random_idx, random_synonym)

2-3.ランダムスワップ(RS)

        文内で 2 つの単語がランダムに選択され、それらの位置が交換されます。このプロセスは n 回繰り返すことができます。(2 つのシーケンス添字は swap_word 関数内でランダムに生成されます。それらが同じであれば、最大 3 回再生成されます。)

def random_swap(words, n):
    new_words = words.copy()
    for _ in range(n):
        new_words = swap_word(new_words)
    return new_words


def swap_word(new_words):
    random_idx_1 = random.randint(0, len(new_words) - 1)
    random_idx_2 = random_idx_1
    counter = 0
    while random_idx_2 == random_idx_1:
        random_idx_2 = random.randint(0, len(new_words) - 1)
        counter += 1
        if counter > 3:
            return new_words
    new_words[random_idx_1], new_words[random_idx_2] = new_words[random_idx_2], new_words[random_idx_1]
    return new_words

2-4.ランダム削除(Random Deletion、RD)

        確率 p でテキストから単語をランダムに削除します。文中に単語が 1 つしかない場合は、それを直接返します。文内のすべての単語が省略された場合は、ランダムに単語を返します。

def random_deletion(words, p):
    if len(words) == 1:
        return words

    new_words = []
    for word in words:
        r = random.uniform(0, 1)
        if r > p:
            new_words.append(word)

    if len(new_words) == 0:
        rand_int = random.randint(0, len(words) - 1)
        return [words[rand_int]]

    return new_words

3. 問題の概要

3-1.文中の複数の単語が変更された場合、文の元のラベル カテゴリは引き続き有効ですか? ​​​​​​​​

        EDA法により生成されたデータが元のデータの特性と一致しているかどうかを検証するために、著者はPro-Conデータセット上のデータの比較分析を実行します。

        具体的な方法: まず、EDA を使用していないデータセットで RNN を使用してトレーニングし、次にテスト セットで EDA 増幅を実行し、元の各文を 9 つの拡張文に増幅し、これらの文をテスト セットとして入力します。 RNN; 最後に、最後の完全に接続された層から出力ベクトルを取得します。t-SNE テクノロジーを適用して、これらのベクトルを 2 次元形式で表現します。以下に示すように。下の図では、大きな三角形と大きな丸が元の文、小さな三角形と小さな丸がEDA技術によって強化された文を表しており、元のデータのほとんどがEDA強化データと一致していることがわかります。つまり、意味シフトは存在しないため、この論文で提案する 4 つのデータ拡張手法はテキストの元のラベルに影響を与えません。

3-2. EDAの各手法において、個別のプロモーションの効果はどのようなものですか?

        4 つのデータ拡張方法のどれがパフォーマンス向上の役割を果たし、どの方法がより大きな役割を果たすかを判断するために、著者はそれぞれの方法を個別に使用してアブレーション研究を実行しました。 実験研究のためのデータ拡張アプローチ。そして、次のような実験結果が得られます。

        上図のパラメータ α は、原文の長さに対する 4 つのデータ拡張方法で変更された単語数の割合を表しており、実験では α={0.05, 0.1, 0.2, 0.3, 0.4, 0.5} となります。 。

同義語置換(SR)        では、α が小さいと実験のパフォーマンスが大幅に向上しますが、α が大きくなるとパフォーマンスが低下します。これはおそらく、あまりにも多くの単語が置き換えられると元のテキストの意味が変わってしまうためと考えられます。

ランダム挿入(RI)        の場合、α の値が上記の範囲にあると、実験のパフォーマンスが比較的安定します。これはおそらく、ランダム挿入法が元のテキスト内の単語の順序を比較的安定に保つためです。

ランダム交換(RS) の場合、α ≤ 0.2 の場合、実験のパフォーマンスは大幅に向上しますが、α ≥ 0.3 の場合、パフォーマンスは低下します。これは、単語位置の交換が多すぎると、元のテキストの全体的な順序が乱れ、テキストの意味が変わってしまうためです        。;

ランダム削除(RD)        の場合、α が小さい場合、実験パフォーマンスは最高に達しますが、α が大きくなると、単語が削除されすぎると文が理解しにくくなり、実験パフォーマンスが大幅に低下する可能性があります。テキストは意味情報を失います。

        アブレーション実験では、各方法において、小さなデータセットで達成される効果がより顕著であると結論付けています。α が大きすぎると、モデルのパフォーマンスがさらに低下するため、α=0.1 が最適な値と思われます。

3-3.強化文の適切な数を選択するにはどうすればよいですか?

        データセットが小さい場合、モデルは過剰適合する傾向があるため、もう少し多くのコーパスを生成すると、より良い結果が得られます。データ セットが大きい場合、1 文あたり 4 文を超える文を生成しても、モデルの効果を向上させるのにはあまり役立ちません。したがって、実際の使用では、以下の表に示すいくつかのパラメータの選択を推奨します。

 naug:元の文ごとに強化された文の数、Ntrain: トレーニング セットのサイズ

3-4. テキスト分類の効果を高めるための EDA の原理とは何ですか?

        1. 元のデータに類似した拡張データを生成すると、ある程度のノイズが導入され、過剰適合の防止に役立ちます。

        2. EDA を使用すると、同義語の置換操作やランダムな挿入操作を通じて新しい語彙を導入でき、テスト セットにはあるがトレーニング セットには含まれていない単語にモデルを一般化できます。

4. EDA データ拡張コードの実装

4-1 説明

       コードの実装では、jieba 単語の分割、ストップボキャブラリー (HIT ストップボキャブラリーがデフォルトで使用されます)、および同義語を提供するパッケージ (Synonyms) が必要です。

4-2 コードの実装

import pandas as pd
import json
from tqdm import tqdm

# !/usr/bin/env python
# -*- coding: utf-8 -*-
import jieba
import re
import random
from random import shuffle
random.seed(2019)
import synonyms 
# 停用词列表,默认使用哈工大停用词表
f = open('/home/zhenhengdong/WORk/Classfier/Dates/stopWord.json', encoding='utf-8')
stop_words = list()
for stop_word in f.readlines():
    stop_words.append(stop_word[:-1])
# 文本清理
import re
def get_only_chars(line):
    #1.清除所有的数字

########################################################################
# 同义词替换
# 替换一个语句中的n个单词为其同义词
########################################################################

def synonym_replacement(words, n):
    new_words = words.copy()
    random_word_list = list(set([word for word in words if word not in stop_words]))
    random.shuffle(random_word_list)
    num_replaced = 0
    for random_word in random_word_list:
        synonyms = get_synonyms(random_word)
        if len(synonyms) >= 1:
            synonym = random.choice(synonyms)
            new_words = [synonym if word == random_word else word for word in new_words]
            num_replaced += 1
        if num_replaced >= n:
            break

    sentence = ' '.join(new_words)
    new_words = sentence.split(' ')

    return new_words

def get_synonyms(word):
    return synonyms.nearby(word)[0]

########################################################################
# 随机插入
# 随机在语句中插入n个词
########################################################################
def random_insertion(words, n):
    new_words = words.copy()
    for _ in range(n):
        add_word(new_words)
    return new_words


def add_word(new_words):
    synonyms = []
    counter = 0
    while len(synonyms) < 1:
        random_word = new_words[random.randint(0, len(new_words) - 1)]
        synonyms = get_synonyms(random_word)
        counter += 1
        if counter >= 10:
            return
    random_synonym = random.choice(synonyms)
    random_idx = random.randint(0, len(new_words) - 1)
    new_words.insert(random_idx, random_synonym)

########################################################################
# Random swap
# Randomly swap two words in the sentence n times
########################################################################

def random_swap(words, n):
    new_words = words.copy()
    for _ in range(n):
        new_words = swap_word(new_words)
    return new_words


def swap_word(new_words):
    random_idx_1 = random.randint(0, len(new_words) - 1)
    random_idx_2 = random_idx_1
    counter = 0
    while random_idx_2 == random_idx_1:
        random_idx_2 = random.randint(0, len(new_words) - 1)
        counter += 1
        if counter > 3:
            return new_words
    new_words[random_idx_1], new_words[random_idx_2] = new_words[random_idx_2], new_words[random_idx_1]
    return new_words

########################################################################
# 随机删除
# 以概率p删除语句中的词
########################################################################
def random_deletion(words, p):
    if len(words) == 1:
        return words

    new_words = []
    for word in words:
        r = random.uniform(0, 1)
        if r > p:
            new_words.append(word)

    if len(new_words) == 0:
        rand_int = random.randint(0, len(words) - 1)
        return [words[rand_int]]

    return new_words

########################################################################
# EDA函数
def eda_func(sentence, alpha_sr = 0.35, alpha_ri = 0.35, alpha_rs = 0.35, p_rd = 0.35, num_aug = 12):
    seg_list = jieba.cut(sentence)
    seg_list = " ".join(seg_list)
    words = list(seg_list.split())
    num_words = len(words)

    augmented_sentences = []
    num_new_per_technique = int(num_aug / 4)
    n_sr = max(1, int(alpha_sr * num_words))
    n_ri = max(1, int(alpha_ri * num_words))
    n_rs = max(1, int(alpha_rs * num_words))

    # print(words, "\n")
    # 同义词替换sr
    for _ in range(num_new_per_technique):
        a_words = synonym_replacement(words, n_sr)
        augmented_sentences.append(''.join(a_words))
    # 随机插入ri
    for _ in range(num_new_per_technique):
        a_words = random_insertion(words, n_ri)
        augmented_sentences.append(''.join(a_words))
    #
    # 随机交换rs
    for _ in range(num_new_per_technique):
        a_words = random_swap(words, n_rs)
        augmented_sentences.append(''.join(a_words))
    #
    #
    # 随机删除rd
    for _ in range(num_new_per_technique):
        a_words = random_deletion(words, p_rd)
        augmented_sentences.append(''.join(a_words))

    # print(augmented_sentences)
    shuffle(augmented_sentences)

    if num_aug >= 1:
        augmented_sentences = augmented_sentences[:num_aug]
    else:
        keep_prob = num_aug / len(augmented_sentences)
        augmented_sentences = [s for s in augmented_sentences if random.uniform(0, 1) < keep_prob]

    # augmented_sentences.append(seg_list)

def Data_Augmentation(item,num):
    augmented_sentence_dataframe = pd.DataFrame()
    for join_class in tqdm(stations_dict[item]):
        for index in range(len(new_data)):
            if new_data.loc[index].联合分类 == join_class:
                augmented_sentences = eda_func(sentence = new_data.loc[index]['内容'])[:num]
                for augmented_sentence in augmented_sentences:
                    creat_new_data = pd.DataFrame()
                    creat_new_data['内容'] = [augmented_sentence]
                    creat_new_data['反馈类型'] = [new_data.loc[index]['反馈类型']]
                    creat_new_data['一级分类'] = [new_data.loc[index]['一级分类']]
                    creat_new_data['二级分类'] = [new_data.loc[index]['二级分类']]
                    creat_new_data['联合分类'] = [new_data.loc[index]['联合分类']]
                    augmented_sentence_dataframe = pd.concat([augmented_sentence_dataframe, creat_new_data], ignore_index=True)
    print(len(augmented_sentence_dataframe))          
    return augmented_sentence_dataframe

if __name__ == '__main__':
    new_data = pd.read_csv('./Temp_data.csv')
    stations_dict = {}
    for index,key_values in enumerate(new_data.联合分类.value_counts().items()):
        if 1500 > key_values[1] > 1000:
            stations_dict.setdefault('1000', []).append(key_values[0])
        if 1000 > key_values[1] > 800:
            stations_dict.setdefault('800', []).append(key_values[0])
        if 800 > key_values[1] > 600:
            stations_dict.setdefault('600', []).append(key_values[0])
        if 600 > key_values[1] > 500:
            stations_dict.setdefault('500', []).append(key_values[0])
        if 500 > key_values[1] > 400:
            stations_dict.setdefault('400', []).append(key_values[0])
        if 400 > key_values[1] > 300:
            stations_dict.setdefault('300', []).append(key_values[0])
        if 300 > key_values[1] > 0:
            stations_dict.setdefault('0', []).append(key_values[0])
    Temp_data = pd.DataFrame()
    for item in stations_dict:
        if item == '1000':#13642
            augmented_sentence_dataframe = Data_Augmentation(item,num = 2)
            Temp_data = pd.concat([Temp_data, augmented_sentence_dataframe], ignore_index=True)
        if item == '800':#16503
            augmented_sentence_dataframe = Data_Augmentation(item,num = 3)
            Temp_data = pd.concat([Temp_data, augmented_sentence_dataframe], ignore_index=True)
        if item == '600':#23684
            augmented_sentence_dataframe = Data_Augmentation(item,num = 4)
            Temp_data = pd.concat([Temp_data, augmented_sentence_dataframe], ignore_index=True)
        if item == '500':#15186
            augmented_sentence_dataframe = Data_Augmentation(item,num = 6)
            Temp_data = pd.concat([Temp_data, augmented_sentence_dataframe], ignore_index=True)
        if item == '400':#20400
            augmented_sentence_dataframe = Data_Augmentation(item,num = 8)
            Temp_data = pd.concat([Temp_data, augmented_sentence_dataframe], ignore_index=True)
        if item == '300':#7137
            augmented_sentence_dataframe = Data_Augmentation(item,num = 9)
            Temp_data = pd.concat([Temp_data, augmented_sentence_dataframe], ignore_index=True)
        if item == '0':#3897
            augmented_sentence_dataframe = Data_Augmentation(item,num = 9)
            Temp_data = pd.concat([Temp_data, augmented_sentence_dataframe], ignore_index=True)
    #将合并的data存储
    Temp_data.to_csv('./Temp_data_single_sample.csv',index = False,encoding='utf8')

参照:

1.https://www.zhihu.com/question/341361292/answer/2916784123

2. NLP でのデータ拡張: UDA、EDA_eda データ拡張_Happy Little Code Farmer のブログ - CSDN ブログ

おすすめ

転載: blog.csdn.net/weixin_44750512/article/details/131675191