👇 连享会 · 推文导航 | www.lianxh.cn
连享会课程 · 2023 暑期班
⛳ Stata 系列推文:
全部 | Stata入门 | Stata教程 | Stata资源 | Stata命令 计量专题 | 论文写作 | 数据分享 | 专题课程 结果输出 | Stata绘图 | 数据处理 | Stata程序 回归分析 | 面板数据 | 交乘项-调节 | IV-GMM 内生性-因果推断 | 倍分法DID | 断点回归RDD | PSM-Matching | 合成控制法 Probit-Logit | 时间序列 | 空间计量 | 分位数回归 | 生存分析 | SFA-DEA 文本分析-爬虫 | Python-R-Matlab | 机器学习 ☝ PDF下载 - 推文合集
作者 :张洪洋 (西南财经大学)邮箱 :1750410339@qq.com
温馨提示: 文中链接在微信中无法生效。请点击底部「阅读原文」 。或直接长按/扫描如下二维码,直达原文:
编者按 :本文主要参考自 西南财经大学国际联合实验室《利用Python学习NLP》课件 ,特此致谢!
目录
1. 文本数据截断
2. 数据扩充
3. 噪声技术
3.1 同义词替换
3.2 随机插入
3.3 随机删除
4. 相关推文
文本数据增强可以简单理解为由少量数据生成大量数据的过程。一般比较成功的神经网络拥有大量参数,使这些参数正确工作需要用大量的数据进行训练,但实际情况是数据并没有那么多,因此需要做数据增强。
相较于图像、语音方面的数据增强而言,文本的数据增强更具有挑战性,因为文本中微小的改动就可能带来语义的变化,从而破坏了原文本,导致数据质量降低。本文将介绍通用几种文本数据增强方法,除了这些通用的增强方法之外,大多数时候需要针对具体的 NLP 任务设计对应的文本数据增强方法。
1. 文本数据截断 在处理长文本的过程中,通常需要按照某个阈值对文本进行截断,但是又不能直接在当前阈值截断,可能会破坏句子结构。比较理想的方案是,在某一个阈值向前搜索并找到句子的末位 (例如句号) 处进行截断,剩下的若还超过阈值,则重复截断。下面以 example_text_data.csv 这个文件为例。
import pandas as pd data = pd.read_csv(r'https://file.lianxh.cn/data/e/example_text_data.csv' ) data['text_len' ] = data['text' ].map(len) data['text_len' ].describe()
count 600.0000 mean 223.5000 std 281.2513 min 25.0000 25 % 121.0000 50 % 163.0000 75 % 268.2500 max 5949.0000 Name: text_len, dtype: float64
可以看到大部分文本长度在 300 以内,存在少量超长文本。一般而言,为了避免维度过大以及提升计算效率,大多数 NLP 模型将文本的输入长度限制在了 512 或 1024 之内,这就意味着对于少量的超长文本,需要抛弃或者进行文本截断操作。
truncated_data = pd.DataFrame(columns=['id' ,'text' ]) #新建dataframe max_len = 300 #假设句子最长为300 for j in range(len(data)): text = data['text' ][j] #text left_index = 0 #设定句子的左索引,默认为0 temp_text_list = [] #list count = 0 df = {} #存放新增的内容 while len(text[left_index:])>= max_len : count +=1
for z in range(100 ): # 只需向前搜索一定步数,加快运算 if text[left_index+max_len-z] in ['!' ,'。' ,'>' ,';' ,'•' ,') ' ] : #出现list里的这些符号认为为句子末尾,可自定义 temp_text_list.append(text[left_index: left_index+300 -z+1 ]) #截断的第一个句子 left_index += max_len-z+1 # 将左索引右移到截断除 break if count>=40 : #可能会出现长文本里面没有结尾的情况,限制搜索范围为max_len*4个字节 break temp_text_list.append(text[left_index:]) #截断的句子存放处 for i in temp_text_list: df["id" ] = data['id' ][j] #装入df df["text" ] = i truncated_data = truncated_data.append(df,ignore_index=True ) # 向dataframe内添加行 truncated_data['text_len' ] = truncated_data['text' ].map(len) truncated_data['text_len' ].describe()
count 681.00000 mean 196.91630 std 193.08095 min 10.00000 25 % 121.00000 50 % 164.00000 75 % 258.00000 max 4266.00000 Name: text_len, dtype: float64
2. 数据扩充 机器学习模型需要足够的数据支撑才能进行更好地训练,但实际生活中,作为开发者往往无法获取大量的数据,而专业的数据采集和标注公司提供的数据服务也并不便宜。因此,解决此问题有一个较为不错的初级方案,那就是进行数据扩充。
数据扩充 (data augmentation) 的本质:缺少海量数据时,为了保证模型的有效训练,一分钱掰成两半花。这里介绍两种数据扩充的方法。
2.1 直接复制 在 Python 中,直接用等于符号或者运用 copy()
函数可以返回一个复制。然而单纯地运用等于符号和 copy()
函数,只会返回一个浅复制 (浅拷贝)。
对于浅复制而言,其中一个发生变化,另一个被复制的对象或原对象也会发生变化。而对于深复制(深拷贝)而言,一旦复制出来了,就是独立的,其中一个发生变化,另一个不会随之发生变化。因此,增加数据量最简单和直接的方法就是直接复制拷贝一份新的数据。
new_data = data.copy(deep=True ) #深复制 data = pd.concat([new_data,data], ignore_index = True ) #pd.concat将两个dataframe进行拼合 print(f"扩充后的数据data.shape: {data.shape} " )
扩充后的数据data.shape: (1200 , 3 )
2.2 反译 对于文本数据而言,将文本翻译成其他语言再翻译成回来后,利用翻译器的特点,使得翻译回来的文本与原来的文本语义高度相似,又引入了其他词汇,增加了模型可学习的内容。gooletrans 是一个免费的调用谷歌翻译库的 python api,可以通过以下代码安装:
!pip install googletrans==4.0 .0 rc1
下面利用 gooletrans 来进行中文的反译操作:
import csvfrom googletrans import Translator translator = Translator(service_urls=[ 'translate.google.cn' ]) #接入谷歌的翻译服务,设定service_urls为translate.google.cn即可翻译中文 translator.raise_Exception = True #避免ip被限制 chinese_text = '下面的代码,我只涉及到了“中文”和“英语”两种语言的循环翻译,没有涉及到其他的语言。' print(f"原句子: \n{chinese_text} \n" ) english_text = translator.translate(chinese_text).text # 调用translate方法 print(f"汉译英: \n{english_text} \n" ) re_chinese_text = translator.translate(english_text,dest='zh-CN' ).text # 设定为英译汉 print(f"英译汉:\n{re_chinese_text} \n" )
原句子: 下面的代码,我只涉及到了“中文”和“英语”两种语言的循环翻译,没有涉及到其他的语言。 汉译英: In the following code, I only involve cyclic translations of two languages: "Chinese" and "English" , which does not involve other languages. 英译汉: 在以下代码中,我仅涉及两种语言的循环翻译:“中文”和“英语”,不涉及其他语言。
将 example_data 进行批量的反译:
import pandas as pd data = pd.read_csv('example_text_data.csv' ) # 读入 trans_data = data.copy(deep = True ) #深拷贝 translator.raise_Exception = True #避免ip被限制 trans_data['trans_text' ] = trans_data['text' ][:5 ].map(lambda x: \ translator.translate(translator.translate(x).text,dest='zh-CN' ).text)# map和lambda函数是dataframe中非常重要的函数,可以很方便的灵活的对dataframe进行批量操作 display(trans_data.head())
3. 噪声技术 噪声技术旨在通过向文本中注入噪声数据,来生成新的文本,最后使得训练的模型对扰动具有鲁棒性。EDA (Easy Data Augmentation) 是一种通用且易于实现的 nlp 噪声技术,能显著提高卷积和递归神经网络的性能,仅使用 50% 的可用训练集进行 EDA 训练便达到了与使用所有可用数据进行正常训练相同的准确度。
EDA由四个简单但功能强大的操作组成:同义词替换、随机插入、随机交换和随机删除。
3.1 同义词替换 同义词替换(Synonym Replacement, SR)是指从句子中随机选取 n 个不属于非停止词,并随机选择其同义词替换它们。
!pip install synonyms #若未安装synonoyms,运行此代码,若synonyms安装错误,请检查是否有安装jieba等前置库 import jiebaimport randomfrom random import shuffleimport sysimport synonyms #一个中文近义词工具包,开源地址在https://github.com/chatopera/Synonyms #synonyms第一次调包可能会很慢,您可以选择将文件夹中的"words.vector.gz"文件放入该包路径".//synonyms//data//"中, #以anaconda为例,路径为应该为"//anaconda//lib//site-packages//synonyms//data//"
我们通过 synonyms.display()
和 synonyms.nearby()
来具体看一下 synonyms
是怎么展示中文同义词结果。
synonyms.display("飞机" ) #synonyms.display()将输出"飞机"的近义词组,并以单词相似性由高到低排序
'飞机' 近义词: 1. 飞机:1.0 2. 直升机:0.84233904 3. 客机:0.83930016 4. 滑翔机:0.7872388 5. 军用飞机:0.7832081 6. 水上飞机:0.77857226 7. 运输机:0.77247417 8. 航机:0.7664748 9. 航空器:0.76592904 10. 民航机:0.74209654
synonyms.nearby("飞机" ) #synonyms.nearby()返回一个包含"近义词"和"该近义词得分"的元组,二者均以列表的形式存储
(['飞机' , '直升机' , '客机' , '滑翔机' , '军用飞机' , '水上飞机' , '运输机' , '航机' , '航空器' , '民航机' ], [1.0 , 0.84233904 , 0.83930016 , 0.7872388 , 0.7832081 , 0.77857226 , 0.77247417 , 0.7664748 , 0.76592904 , 0.74209654 ])
接下来我们将利用同义词库去构建 EDA 中的相关方法,包括同义词替换和随机插入。
random.seed(2022 ) #设置随机种子 #构建停用词列表 with open('停用词.txt' , 'r' ,encoding='utf-8' ) as f: stop_words = [s.rstrip() for s in f.readlines()]#分词 sentence = "我也期待着能和你飞去同一片土地" words = jieba.lcut(sentence) # 调用jieba进行分词 print(words) # 分词结果
['我' , '也' , '期待' , '着' , '能' , '和' , '你' , '飞去' , '同' , '一片' , '土地' ]
进行同义词替换主要只进行二步,第一步是找到同义词列表,第二部是随机选择同义词列表中的一个词进行替换。
# 同义词替换:替换一个语句中的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 #被替换的词的数量,初始为0 for random_word in random_word_list: synonyms_word = synonyms.nearby(random_word)[0 ] #调用刚才定义的函数get_synonyms,返回意思最相近的词语 if len(synonyms_word) >= 1 : synonym = random.choice(synonyms_word) #在同义词列表中随机选择一个同义词 new_words = [synonym if word == random_word else word for word in new_words]
#将随机选择的同义词添加进词表,并替换原单词 num_replaced += 1 if num_replaced >= n: #当n个单词被替换后结束循环 break return new_words
print(f"替换前:\n{words} \n" ) print(f"替换后:\n{synonym_replacement(words, 1 )} \n" ) #调用synonym_replacement,设置n=1,即随机替换一个同义词
替换前: ['我' , '也' , '期待' , '着' , '能' , '和' , '你' , '飞去' , '同' , '一片' , '土地' ] 替换后: ['我' , '也' , '期待' , '着' , '能' , '和' , '你' , '飞去' , '同' , '一片' , '田地' ]
3.2 随机插入 随机插入 (Random Insertion,RI) 是指从句子中随机选择一个非停止词,然后找到它对应的同义词,并将其插入到句子中的一个随机位置
# 随机插入n个同义词 def add_word (new_words) : synonyms = [] # 同义词表 counter = 0 # 计数器 random_word_list = list(set([word for word in new_words if word not in stop_words])) #生成非停止词列表 while len(synonyms) 1: #这里主要指随机选择random_word_list中一个词 random_word = random_word_list[random.randint(0 , len(random_word_list)-1 )] #random.randint()方法随机返回指定范围内的一个整数, #并且可以取到右侧边界值,为防止索引超出列表容量,因此会减1 synonyms = get_synonyms(random_word) #调用上面定义的函数get_synonyms,返回一个包含10个同义词的list 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) #在随机索引处插入同义词 def random_insertion (words, n) : #调用随机插入方法,重复n次 new_words = words.copy() for _ in range(n): add_word(new_words) # 调用上面定义的函数add_word return new_words
print(f"插入前:\n{words} \n" ) print(f"插入后:\n{random_insertion(words, 1 )} \n" ) #随机插入n =1个同义词
插入前: ['我' , '也' , '期待' , '着' , '能' , '和' , '你' , '飞去' , '同' , '一片' , '土地' ] 插入后: ['我' , '也'
, '期待' , '着' , '能' , '和' , '你' , '飞去' , '同' , '盼望' , '一片' , '土地' ]
3.3 随机删除 随机删除 (Random Deletion,RD) 指以概率 随机删除句子中的单词。
def random_deletion (words, p) : if len(words) == 1 : #至少要大于1个单词才能随机删除 return words new_words = [] for word in words: r = random.uniform(0 , 1 ) #随机生成一个在 (0,1) 范围内的实数 if r > p: #以1-p的概率向新词列表添加单词 new_words.append(word) if len(new_words) == 0 : #若删除概率p为1,则随机保留词表中的一个单词 rand_int = random.randint(0 , len(words)-1 ) return [words[rand_int]] return new_words
print(f"删除前:\n{words} \n" ) print(f"删除后:\n{random_deletion(words, 0.1 )} \n" ) #以0.1的概率随机删除单词
删除前: ['我' , '也' , '期待' , '着' , '能' , '和' , '你' , '飞去' , '同' , '一片' , '土地' ] 删除后: ['我' , '也' , '期待' , '着' , '能' , '和' , '你' , '飞去' , '同' , '一片' , '土地' ]
4. 相关推文 Note:产生如下推文列表的 Stata 命令为: lianxh 文本, m
安装最新版 lianxh
命令: ssc install lianxh, replace
⏩ 专题课:文本分析-爬虫-机器学习-2022年4月 Stata文本分析:lsemantica-潜在语义分析的文本相似性判别 textfind:文本分析之词频分析-TF-IDF Stata文本分析之-tex2col-命令-文字变表格 Python文本分析:将词转换为向量-Word2Vec Python: 使用正则表达式从文本中定位并提取想要的内容
课程推荐:2023 暑期班 主讲老师:连玉君,王群勇 🍓 课程主页 :https://www.lianxh.cn/news/fdc69c3695aec.html
New! Stata 搜索神器:lianxh
和 songbl
GIF 动图介绍 搜: 推文、数据分享、期刊论文、重现代码 …… 👉 安装: . ssc install lianxh
. ssc install songbl
👉 使用: . lianxh DID 倍分法
. songbl all
🍏 关于我们 直通车: 👉【百度一下: 连享会 】即可直达连享会主页。亦可进一步添加 「知乎」,「b 站」,「面板数据」,「公开课」 等关键词细化搜索。