Kaggle首战记录(5)-English Language Learning-cohesion维度的单独训练

前文提到了多个维度一同训练的弊端,包括数据增强的难处,以及不同维度相互牵制使得性能下降。本文单独处理了cohesion看看效果(由于batchsize无法很大,把batchnorm换成了dropout),效果印证了前文的猜测。

cohesion是什么?

说实话,只看英文翻译根本看不出来cohesion到底想要评什么分。

然后在网上查了一下,发现雅思有这个评分标准:

雅思写作评分标准之Cohesion(上)-明衔接 - 知乎 (zhihu.com)

大概意思是链接顺畅,连接词合理,使用代词,名词替换合理(最后这个我在实验前没看仔细,所以后文的处理方法有一丢丢小问题qaq,但是效果有提升就是了)。

数据增强

依照这个思路,理出数据增强的方法:

1.适当破坏或替换动词和名词(其实!正确方法应该是针对动词就行了,因为cohesion和连接词主要是副词、名词和代词都有关系!)

2.以句子为单位的窗口滑动,但要保证窗口不过于小以至于评分不匹配。

代码

简单地以段落为单位EDA一下:

1
2
3
4
5
6
7
8
9
10
11
12
import pandas as pd
data = pd.read_csv('./train.csv')
data = data[:3750]
import re

rx = r'\n\n'
df = data["full_text"].map(lambda x : len(re.findall(rx, x)) + 1)
print(df.mean())
print(df.median())
print(df.min())
print(df.max())
print(df[df>18].count())
1
2
3
4
5
5.5016
5.0
1
52
35

老规矩,看一下主代码先:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
for i in range(3750):
if (i + 1) % 100 == 0:
print(i)
text_ = data['full_text'][i].strip()
paragraph_count = get_paragraph_count(text_) #计算段落数
sen_count = get_sen_count(text_) #计算句子数
text = text_
for j in range(4): #一个数据增强3倍
if paragraph_count >= 11:
text = get_seg_para(text_, j) #对于段落数大于11的,按段落数来切
elif sen_count >= 18:
text = get_random_sen(text_) #否则如果句子数大于18,随机取里面的句子,否则不做处理

li = text.split(" ") #简单的分词
length = len(li)
for _ in range(length // 10): #对于十分之一的词来操作

while True:
index = random.choice(range(length))
pos = nltk.pos_tag([li[index]])[0][1] #分析词性,只操作名词和动词
if pos[0] in ["V", "N"]:
p = random.uniform(0, 1) #轮盘赌
if p > 0.5: #50%概率不做改变
pass
elif p > 0.3: #20%概率用编辑距离为1的破坏
li[index] = random.choice(edits1(li[index]))
else:
try:
li[index] = get_synonyms(li[index]) #30%概率用同义词破替换
except Exception:
li[index] = random.choice(edits1(li[index])) #当然如果没有同义词,破坏之
break
text = " ".join(li)
data.loc[len(data)] = data.loc[i]
data['full_text'][len(data)-1] = text

print("Finish.")

各函数如下(编辑距离为1的函数不表,前文已经提到):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import random


def get_paragraph_count(txt):
return len(re.findall("\n\n", txt)) + 1

def get_seg_para(txt, index):
li = txt.split("\n\n")
if len(li) <= 18: #段落数小于等于18,大于等于11的,用大小为5的滑动窗口,比较平均
seg_size = (len(li) - 5) // 3 #计算步长
delta = len(li) - 3 * seg_size - 5 if index == 3 else 0 #保证填满
return "\n\n".join(li[seg_size * index + delta : seg_size * index + 5 + delta])
else: #段落数过多了,就直接切分了,不用滑动窗口了
seg_size = len(li) // 4
delta = len(li) % 4 if index == 3 else 0
return "\n\n".join(li[seg_size * index : seg_size * (index + 1) + delta])

def get_sen_count(txt):
return len(re.findall(r'[.!?]', txt))

def get_random_sen(txt):
li = re.split('[.!?]', txt)
l = len(li)
a = 0
b = 0
while b - a <= l // 4: #随机取一个区间,但要保证取出来的句子数至少是原来的1/4
a = random.randint(0, l)
b = random.randint(0, l)
if a > b:
a, b = b, a
return ". ".join(li[a:b])

同义词(nltk这个库真的有点好用,虽然老是得手动下载一些语料库):

1
2
3
4
5
6
7
8
import nltk
from nltk.corpus import wordnet

def get_synonyms(word):
synonyms = []
for syn in wordnet.synsets(word):
synonyms.extend(lm.name() for lm in syn.lemmas())
return random.choice(list(set(synonyms))) #随机取同义词中的一个

实验效果的对比

首先,先来看一下之前六个维度训练时,cohesion维度的效果:

可以看到0.51476604,这个维度拖后腿比较严重(也验证了我说相互牵制的预测,cohesion比较看重上下文)。

然后roberta-base单独训练,不使用增强数据时,降到了0.50多(忘了截图了),还是挺好的

使用增强数据后,又到了0.5-了:

然后我还用deberta-base+增强数据训练了一下,不得不说deberta确实是yyds哈,这下已经到了0.472了(截至发文还没训练完):

四个点的增强还是很舒服的。


Kaggle首战记录(5)-English Language Learning-cohesion维度的单独训练
https://bebr2.com/2022/09/12/Kaggle首战记录(5)-English Language Learning-cohesion维度的单独训练/
作者
BeBr2
发布于
2022年9月12日
许可协议