463. chaii - Hindi and Tamil Question Answering | chaii-hindi-and-tamil-question-answering
首先感谢 Kaggle 和组织者举办这次比赛。
比赛数据的质量不尽如人意,因此无法构建与排行榜相关性良好的交叉验证(CV)。公开的训练数据不干净,而隐藏的私有测试数据却是干净的,这意味着我们要么使用不同的模型并将它们组合在一起,要么使用像 muril-large 这样强大的单一模型,并在其他人共享的各种数据集上进行训练。这两种方法我们都采用了。
第一步是将训练数据划分为 5 折。之后,我们在 SQuAD v2 的子集、@tkm2261 分享的 Google 翻译版 SQuAD v2、所有 TyDi QA、MLQA 的印地语部分、XQUAD 以及每一折 5-10 倍过采样的 Chaii 数据集上训练我们的最终模型。我们发现过采样 Chaii 数据集有助于提高 CV 和 LB 分数。请注意,我们在计算 CV 时仅使用了原始的验证折。
所有模型都训练了 1-2 个 epoch,基于 CV Jaccard 分数进行早停,参数设置为 max_length = 384 和 doc_stride = 128。我们确实尝试了其他长度,如 400、512 以及其他步长如 192,但在我们的交叉验证策略中表现不佳。
由于不同的模型具有不同的分词方式,简单的平均集成无法实现,因此尝试使用预测分数从不同模型中选择最佳预测。这给我们带来了一点提升。
接下来我们尝试将所有模型的分数归一化到同一尺度。我们也看到了一点提升,但我们知道这并不是组合模型的最佳方式。
原始版本失败了,因为不同的模型具有不同的 logits 尺度。为了克服这个问题,我想到了一个自定义 softmax,它可以跨不同的上下文分割对 logits 进行归一化。基本代码附在下面:
def CustomSoftmax(examples, features, raw_predictions):
all_start_logits, all_end_logits = raw_predictions
# 建立 example 到其对应 features 的映射。
example_id_to_index = {k: i for i, k in enumerate(examples["id"])}
features_per_example = collections.defaultdict(list)
for i, feature in enumerate(features):
features_per_example[example_id_to_index[feature["example_id"]]].append(i)
for example_index, example in enumerate(tqdm(examples)):
feature_indices = features_per_example[example_index]
start_logits_sum = 0
end_logits_sum = 0
logCs = None
logCe = None
# 在上下文中寻找最大 logit 值
for feature_index in feature_indices:
if logCs is None:
logCs = all_start_logits[feature_index].max()
else:
logCs = max(logCs, all_start_logits[feature_index].max())
if logCe is None:
logCe = all_end_logits[feature_index].max()
else:
logCe = max(logCe, all_end_logits[feature_index].max())
# 在应用指数运算前减去所有 logits 中的最大值以保持数值稳定性
# 并对所有上下文分割求和
for feature_index in feature_indices:
all_start_logits[feature_index] -= logCs
all_end_logits[feature_index] -= logCe
start_logits_sum += np.exp(all_start_logits[feature_index]).sum()
end_logits_sum += np.exp(all_end_logits[feature_index]).sum()
# Softmax
for feature_index in feature_indices:
all_start_logits[feature_index] = np.exp(all_start_logits[feature_index])/start_logits_sum
all_end_logits[feature_index] = np.exp(all_end_logits[feature_index])/end_logits_sum
return all_start_logits, all_end_logits
这是我们在最后两天想到的最终主意,它将我们的公开分数从 0.809 提高到了 0.821!
我们遵循了与公开笔记本类似的预处理技术。虽然我们观察到删除某些符号会损害性能,因此我们决定使用自定义标点符号列表('!"#$&\'()*+,-/:;<=>?@[\\]^_`{|}~')。
我们还尝试解决 @jdoesv 在 讨论帖 中指出的泰米尔语预测问题,