什么是数据增强
数据增强可以简单理解为由少量数据生成大量数据的过程。一般比较成功的神经网络拥有大量参数,使这些参数正确工作需要用大量的数据进行训练,但实际情况中数据并没有那么多,因此需要做数据增强。
数据增强的作用
- 增加训练的数据量,提高模型的泛化能力
- 增加噪声数据,提升模型的鲁棒性
- 解决数据不足或数据不均衡问题
数据增强的分类
根据数据增强的对象可以将增强研究分类两类:
- 面向文本表示的增强研究: 主要是对原始文本的特征表示进行处理,比如在表示层注入随机噪音等方法,来获得增强后的文本表示。增强后的表示可以再进行解码获得增强文本或者直接用于训练模型。
- 面向原始文本的增强研究: 主要是通过对原始文本中的词进行同义词替换或者删除等操作来进行增强。大部分研究都通过引入各种外部资源来提升增强效果。
本文针对于面向原始文本的增强研究,总结以下几种方法。
EDA
ICLR 2019 workshop 论文《EDA: Easy Data Augmentation Techniques for Boosting Performance on Text Classification Tasks》介绍了几种NLP数据增强技术,并推出了EDA github代码。EDA github repo提出了四种简单的操作来进行数据增强,以防止过拟合,并提高模型的泛化能力。EDA 在个文本分类问题上带来性能的提升,但是其增强方式可能会破坏原始文本的句法结构和通顺性。
同义词替换
不考虑 stopwords,在句子中随机抽取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:
# get_synonyms 获取某个单词的同义词列表
synonyms = get_synonyms(random_word)
if len(synonyms) >= 1:
synonym = random.choice(list(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
随机删除
句子中的每个词,以概率p随机删除。有几种极端情况需要考虑:
- 如果句子中只有一个单词,则直接返回
- 如果句子中所有单词都被删掉,则随机返回一个单词。(这个策略可以根据自己项目进行自定义。)
def random_deletion(words, p):
# obviously, if there's only one word, don't delete it
if len(words) == 1:
return words
# randomly delete words with probability p
new_words = []
for word in words:
r = random.uniform(0, 1)
if r > p:
new_words.append(word)
# if you end up deleting all words, just return a random word
if len(new_words) == 0:
rand_int = random.randint(0, len(words) - 1)
return [words[rand_int]]
return new_words
随机交换
句子中,随机选择两个词,位置交换。该过程可以重复n次。
(swap_word 函数中随机产生两个序列下标,如果相同最多重新生成三次。)
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_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 = synonyms[0]
random_idx = random.randint(0, len(new_words) - 1)
new_words.insert(random_idx, random_synonym)
模型预打标
使用少量的数据训练一个模型或者将少量数据通过 Bert finetune 训练一个模型,在已有模型的基础上对无监督数据打标,选择置信度较高的数据作为伪标签(pseudo label)加入到已有数据中训练模型,循环迭代优化模型。
回译(Back Translation)
回译(Back Translation)是机器翻译中非常常用的数据增强的方式,其主要的思想就是通过翻译工具将一个句子翻译为另一种语言,再把这翻译的另一种语言再翻译为原来的语言,最后得到一个意思相近但表达方式不同的句子。这种方式也是目前相对靠谱的方式,这种方式不仅有同义词替换,词语增删,还具有对句子结构语序调整的效果,并还能保持与原句子意思相近,是目前一种非常有效的文本数据增强方式。
反向翻译的过程如下:
- 假设需要增强的数据是中文
- 借助第三方翻译软件,将中文翻译为法文、日文等多种语言。
- 将翻译的文本二次翻译为中文(可以选取不同的翻译接口)。
- 如果二次翻译的结果和原文本不一致,则加入到增强样本中。
我喜欢学习NLP,因为他存在极大的挑战。
- 百度翻译结果:I like learning NLP because it has great challenges.
- 将百度翻译结果通过谷歌翻译:我喜欢学习 NLP,因为它有很大的挑战。(与原句不同,则获得增强数据)
其他方法
- 加入无意义的词语:比如“忘记打卡怎么办”,加入无意义的词语“你好,忘记打卡怎么办”
参考链接
关于我
dreampai(公众号,简书,知乎同名),专注于 NLP和金融。