目录
1.找到分隔符的index
我们写个测试一下
输出值为
匹配到的:,有一个span 就是找到的标点符号的起始位置和结束位置
为了展示的更清楚一些span所表示的东西 我们再写个测试
可以看到在第一个输出中span[0]就是,的下标 span[1]是后面的‘预’的下标
后面就表示为m.span[0] (在循环中)
2.过滤特殊字符串 同时也是将文本分割为长短句
考虑多种不能作为分隔符的特殊情况
为了判断标点符号前有\t换行符 是否应该断开我们写一个测试
if __name__ == '__main__':
files = os.listdir(train_dir)
files = set([file.split('.')[0] for file in files])
for file in files:
path=os.path.join(train_dir,file+'.txt')
with open(path,'r',encoding='utf8') as f:
text = f.read()
pattern1 = '。|,|,|;|;|\.|\?'
for m in re.finditer(pattern1, text):
idx = m.span()[0] # 就是标点符号的下标 然后根据各个情况判断是否要断开 就是根据标点符号后面的内容决定的
if text[idx - 1] == '\n':
print(file, text[idx-10:idx+10]) # 如果前面是换行符 就打印出当前文章的file 然后打印出前后的内容 idx+-10
然后就要找到文档中找到对应的文段 判断能不能分 后面也可以依照这个是否断开看对整个模型的性能有没有提升
在我们查看相关文档后判断为 不作为分割点 所以直接continue就行了
同样的 我们继续找特殊情况
files = os.listdir(train_dir)
files = set([file.split('.')[0] for file in files])
for file in files:
path=os.path.join(train_dir,file+'.txt')
with open(path,'r',encoding='utf8') as f:
text = f.read()
pattern1 = '。|,|,|;|;|\.|\?'
for m in re.finditer(pattern1, text):
idx = m.span()[0] # 就是标点符号的下标 然后根据各个情况判断是否要断开 就是根据标点符号后面的内容决定的
if text[idx-1].isdigit() and text[idx+1].isdigit() :
print(file, text[idx-20:idx+20])
如果标点符号前后都是数字 再输出一下
很容易看出 这种情况也不应该split
同样的 数字加空格加数字的类型 也不能断开
完整过滤特殊字符代码:
def split_text(text):
split_index = []
pattern1 = '。|,|,|;|;|\.|\?'
for m in re.finditer(pattern1, text):
idx = m.span()[0] # 就是标点符号的下标 然后根据各个情况判断是否要断开 就是根据标点符号后面的内容决定的
if text[idx-1]=='\n':
continue
if text[idx-1].isdigit() and text[idx+1].isdigit(): # 如果标点符号的前后都是数字的话 怎么办
continue
if text[idx-1].isdigit() and text[idx+1].isspace() and text[idx+2].isdigit():
continue
if text[idx-1].islower() and text[idx+1].islower():
continue
if text[idx-1].islower() and text[idx+1].isdigit():
continue
if text[idx-1].isupper() and text[idx+1].isdigit():
continue
if text[idx - 1].isdigit() and text[idx + 1].islower():
continue
if text[idx - 1].isdigit() and text[idx + 1].isupper():
continue
if text[idx+1] in set('.。;;,,'):
continue
if text[idx-1].isspace() and text[idx-2].isspace() and text[idx-3]=='C':
continue
if text[idx-1].isspace() and text[idx-2]=='C':
continue
if text[idx-1].isupper() and text[idx+1].isupper() :#前大些后大写
continue
if text[idx]=='.' and text[idx+1:idx+4]=='com':#域名
continue
split_index.append(idx+1)
# 还有存在(7) 一、这种情况
pattern2='\([一二三四五六七八九零十]\)|[一二三四五六七八九零十]、|'
pattern2 += '注:|附录 |表 \d|Tab \d+|\[摘要\]|\[提要\]|表\d[^。,,;]+?\n|图 \d|Fig \d|'
pattern2 += '\[Abstract\]|\[Summary\]|前 言|【摘要】|【关键词】|结 果|讨 论|'
pattern2 += 'and |or |with |by |because of |as well as '
# 如果是pattern1 应该在后面切 如果是pattern2 应该在前面切
for m in re.finditer(pattern2, text):
idx = m.span()[0]
if (text[idx:idx+2] in ['or','by'] or text[idx:idx+3]=='and' or text[idx:idx+4]=='with')\
and (text[idx-1].islower() or text[idx-1].isupper()):
continue
split_index.append(idx)
pattern3 = '\n\d\.'
for m in re.finditer(pattern2, text):
idx = m.span()[0] # 就是标点符号的下标 然后根据各个情况判断是否要断开 就是根据标点符号后面的内容决定的
# if text[idx+1] in set('.。;;,,'):
# print(file, text[idx-20:idx+20]) # 如果前面是换行符 就打印出当前文章的file 然后打印出前后的内容 idx+-10
if ischinese(text[idx + 3]):
split_index.append(idx+1) # 作为切分点
#再处理一下带括号的
for m in re.finditer('\n\(\d\)', text): # 换行 正括号 数字 反括号
idx = m.span()[0]
split_index.append(idx+1) # 匹配到的是/n所以要+1
split_index = list(sorted(set([0, len(text)] + split_index)))
# split_index有大有小 要排序 且要把0,1放进去
for i in range(len(split_index)-1):
print(i, '||||', text[split_index[i] : split_index[i+1]])
还要判断一个字是不是中文
def ischinese(char):
# 判断一个数是不是中文 如果在这个范围内的话就是中文
if '\u4e00' <= char <= '\u9fff':
return True
return False
3.处理长短句问题
最长的句子有256个字 太长的句子我们的循环神经网络抓不到信息 LSTM并不能处理长文本 抓不到那么长的信息 LSTM差不多能抓到前面十几个字 RNN顶多就三四个字 所以我们要将文本尽量的断成短句子 但是断成短句子又有一个问题 可能让一些句子断掉了 这就涉及到我们后面需要做的事情:样本增强 就是把句子拆成一个一个短句以后 我们把连续的几个短句组成一个新句子 所以我们现在要做的就是尽量的把句子拆短 nlp中的文本增强方法 只有复制 在我们发现某一类特殊样本训练效果非常差时 我们可以多复制一些 让这些样本多训练几次
完整data_process代码
import os
import re
train_dir='datas/ruijin_round1_train2_20181022'
def get_entities(dir):
"""
返回实体类别统计字典
:param dir: 文件目录
:return:
"""
entities={}#用来存储实体名
files=os.listdir(dir)
files=list(set([file.split('.')[0] for file in files]))
for file in files:
path=os.path.join(dir,file+'.ann')
with open(path,'r',encoding='utf8') as f:
for line in f.readlines():
name=line.split('\t')[1].split(' ')[0]
if name in entities:
entities[name]+=1
else:
entities[name]=1
return entities
def get_labelencoder(entities):
"""
功能是得到标签和下标的映射
:param entities:
:return:
"""
entities = sorted(entities.items(), key=lambda x: x[1], reverse=True)
entities = [x[0] for x in entities]
id2label=[]
id2label.append('O')
for entity in entities:
id2label.append('B-'+entity)
id2label.append('I-'+entity)
label2id={id2label[i]:i for i in range(len(id2label))}
return id2label,label2id
def ischinese(char):
# 判断一个数是不是中文 如果在这个范围内的话就是中文
if '\u4e00' <= char <= '\u9fff':
return True
return False
def split_text(text):
split_index = []
pattern1 = '。|,|,|;|;|\.|\?'
for m in re.finditer(pattern1, text):
idx = m.span()[0] # 就是标点符号的下标 然后根据各个情况判断是否要断开 就是根据标点符号后面的内容决定的
if text[idx-1]=='\n':
continue
if text[idx-1].isdigit() and text[idx+1].isdigit(): # 如果标点符号的前后都是数字的话 怎么办
continue
if text[idx-1].isdigit() and text[idx+1].isspace() and text[idx+2].isdigit():
continue
if text[idx-1].islower() and text[idx+1].islower():
continue
if text[idx-1].islower() and text[idx+1].isdigit():
continue
if text[idx-1].isupper() and text[idx+1].isdigit():
continue
if text[idx - 1].isdigit() and text[idx + 1].islower():
continue
if text[idx - 1].isdigit() and text[idx + 1].isupper():
continue
if text[idx+1] in set('.。;;,,'):
continue
if text[idx-1].isspace() and text[idx-2].isspace() and text[idx-3]=='C':
continue
if text[idx-1].isspace() and text[idx-2]=='C':
continue
if text[idx-1].isupper() and text[idx+1].isupper() :#前大些后大写
continue
if text[idx]=='.' and text[idx+1:idx+4]=='com':#域名
continue
split_index.append(idx+1)
# 还有存在(7) 一、这种情况
pattern2='\([一二三四五六七八九零十]\)|[一二三四五六七八九零十]、|'
pattern2 += '注:|附录 |表 \d|Tab \d+|\[摘要\]|\[提要\]|表\d[^。,,;]+?\n|图 \d|Fig \d|'
pattern2 += '\[Abstract\]|\[Summary\]|前 言|【摘要】|【关键词】|结 果|讨 论|'
pattern2 += 'and |or |with |by |because of |as well as '
# 如果是pattern1 应该在后面切 如果是pattern2 应该在前面切
for m in re.finditer(pattern2, text):
idx = m.span()[0]
if (text[idx:idx+2] in ['or','by'] or text[idx:idx+3]=='and' or text[idx:idx+4]=='with')\
and (text[idx-1].islower() or text[idx-1].isupper()):
continue
split_index.append(idx)
pattern3 = '\n\d\.'
for m in re.finditer(pattern2, text):
idx = m.span()[0] # 就是标点符号的下标 然后根据各个情况判断是否要断开 就是根据标点符号后面的内容决定的
# if text[idx+1] in set('.。;;,,'):
# print(file, text[idx-20:idx+20]) # 如果前面是换行符 就打印出当前文章的file 然后打印出前后的内容 idx+-10
if ischinese(text[idx + 3]):
split_index.append(idx+1) # 作为切分点
#再处理一下带括号的
for m in re.finditer('\n\(\d\)', text): # 换行 正括号 数字 反括号
idx = m.span()[0]
split_index.append(idx+1) # 匹配到的是/n所以要+1
split_index = list(sorted(set([0, len(text)] + split_index)))
# split_index有大有小 要排序 且要把0,1放进去
other_index = []
for i in range(len(split_index)-1):
begin = split_index[i]
end = split_index[i+1]
if text[begin] in '一二三四五六七八九零十' or text[begin] =='('and text[begin+1] in '一二三四五六七八九零十':
for j in range(begin, end):
if text[j] =='\n':
other_index.append(j+1)
split_index += other_index
split_index = list(sorted(set([0, len(text)] + split_index)))
other_index=[]
for i in range(len(split_index)-1):
b = split_index[i]
e = split_index[i+1]
if e-b>150: # 句子超过150就能拆分
for j in range(b, e):
if (j+1-other_index[-1]) > 15: #保证句子长度在15以上
if text[j] =='\n':
other_index.append(j+1)
if text[j] ==' ' and text[j-1].isnumeric() and text[j+1].isnumeric():
other_index.append(j+1)
split_index += other_index
split_index = list(sorted(set([0, len(text)] + split_index)))
# 如果前面是个空格
for i in range(1, len(split_index)-1):
idx = split_index[i]
while idx>split_index[i-1] -1 and text[idx-1].isspace():
idx -= 1
split_index[i] = idx
split_index = list(sorted(set([0, len(text)] + split_index)))
# 处理短句子
temp_idx = []
i = 0
while i < len(split_index)-1: # 0 10 30 45
b = split_index[i]
e = split_index[i+1]
num_ch=0
num_en=0
if e - b < 15:
for ch in text[b:e]:
if ischinese(ch):
num_ch += 1
elif ch.islower() or ch.isupper():
num_en += 1
if num_ch + 0.5 * num_en > 5: # 如果汉字加英文超过5个 则单独成为句子
temp_idx.append(b)
i += 1
break
if num_ch + 0.5 * num_en <= 5: # 如果汉字加英文不到5个 和后面一个句子合并
temp_idx.append(b)
i += 2
else:
temp_idx.append(b)
i += 1
split_index = list(sorted(set([0, len(text)] + temp_idx)))
result = []
for i in range(len(split_index) - 1):
result.append(text[split_index[i]:split_index[i + 1]])
# 做一个检查
s = ''
for r in result:
s += r
assert len(s) == len(text)
return result
# lens=[split_index[i+1]-split_index[i] for i in range(len(split_index)-1)][:-1]
# print(max(lens),min(lens))
# for i in range(len(split_index)-1):
# print(i,'||||',text[split_index[i]:split_index[i+1]])
# lens = [split_index[i+1] - split_index[i] for i in range(len(split_index)-1)] # 每一句话的终止位置减去每一句话的起始位置
# print(max(lens), min(lens))
#
# for i in range(len(split_index)-1):
# print(i, '||||', text[split_index[i] : split_index[i+1]])
if __name__ == '__main__':
# with open('./datas/ruijin_round1_train2_20181022/0.txt') as f:
# text = f.read()
# pattern = '。|,|,|;|;|\.|\?'
# for m in re.finditer(pattern,text):
# #print(m)
# start = m.span()[0] -5
# end = m.span()[1] + 5
# print('**********', text[start:end])
# print(text[start+5])
files = os.listdir(train_dir)
files = list(set([file.split('.')[0] for file in files]))
# for file in files:
# path=os.path.join(train_dir,file+'.txt')
# with open(path,'r',encoding='utf8') as f:
# text = f.read()
# pattern2 = '\n\(\d\)'
# for m in re.finditer(pattern2, text):
# idx = m.span()[0] # 就是标点符号的下标 然后根据各个情况判断是否要断开 就是根据标点符号后面的内容决定的
# # if text[idx+1] in set('.。;;,,'):
# # print(file, text[idx-20:idx+20]) # 如果前面是换行符 就打印出当前文章的file 然后打印出前后的内容 idx+-10
# # if ischinese(text[idx+4]):
# print(file+'||||',text[idx-40:idx+40])
path=os.path.join(train_dir,files[0]+'.txt')
with open(path, 'r', encoding='utf8') as f:
text = f.read()
split_text(text)