返回列表

5th Place Solution

463. chaii - Hindi and Tamil Question Answering | chaii-hindi-and-tamil-question-answering

开始: 2021-08-11 结束: 2021-11-15 自然语言处理 数据算法赛
第5名解决方案

第5名解决方案

作者: Udbhav Bamba, Abhishek Thakur
比赛: 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,但在我们的交叉验证策略中表现不佳。

模型

  • XLM Roberta Large (公开 LB - 0.800)
  • Muril Large (公开 LB - 0.802)
  • RemBERT (公开 LB - 0.803)

模型集成

由于不同的模型具有不同的分词方式,简单的平均集成无法实现,因此尝试使用预测分数从不同模型中选择最佳预测。这给我们带来了一点提升。

接下来我们尝试将所有模型的分数归一化到同一尺度。我们也看到了一点提升,但我们知道这并不是组合模型的最佳方式。

原始版本失败了,因为不同的模型具有不同的 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 在 讨论帖 中指出的泰米尔语预测问题,

同比赛其他方案