boostcampaitech2 / mrc-level2-nlp-04

mrc-level2-nlp-04 created by GitHub Classroom
4 stars 5 forks source link

전체 Dataset 구성 for K-Fold #24

Open sangmandu opened 2 years ago

sangmandu commented 2 years ago

✨ 계획

✨ 수행

1. 주어진 데이터셋 합치고 새로운 공간 찾기

라이브러리 선언

import pandas as pd
from datasets import load_from_disk, concatenate_datasets, DatasetDict

데이터셋 로드

dataset = load_from_disk("../data/train_dataset/")
train_dataset = dataset['train']
valid_dataset = dataset['validation']

데이터셋은 다음과 같은 특징을 가집니다.

dataset
>>> DatasetDict({
    train: Dataset({
        features: ['__index_level_0__', 'answers', 'context', 'document_id', 'id', 'question', 'title'],
        num_rows: 3952
    })
    validation: Dataset({
        features: ['__index_level_0__', 'answers', 'context', 'document_id', 'id', 'question', 'title'],
        num_rows: 240
    })
})

type(dataset)
>>> datasets.dataset_dict.DatasetDict

k=5 일 때의 데이터셋 길이

len(train_dataset), len(valid_dataset), len(train_dataset)+len(valid_dataset)
>>> (3952, 240, 4192)

데이터셋 통합

combined_dataset = concatenate_datasets([train_dataset, valid_dataset])
len(combined_dataset)
>>> 4192

combined_dataset.save_to_disk('../data/combined_dataset')

2. get_data에서 데이터를 split할 수 있도록 하기

Kfold 간단히 알아보기

kf = KFold(n_splits=5, random_state=42, shuffle=True)
for train_index, valid_index in kf.split(dataset):
    print(set(train_index).intersection(valid_index))
set()
set()
set()
set()
set()

Kfold 수행 (참고링크)

from sklearn.model_selection import KFold

combined_datasets = load_from_disk('../data/combined_dataset')
kf = KFold(n_splits=5, random_state=42, shuffle=True)
for idx, (train_index, valid_index) in enumerate(kf.split(combined_datasets), 1):
    train_dataset, eval_dataset = map(Dataset.from_dict, [combined_datasets[train_index], combined_datasets[valid_index]])
    datasets = DatasetDict({'train' : train_dataset, 'validation' : eval_dataset})

실제 코드

last_checkpoint, max_seq_length = check_no_error(
        data_args, training_args, tokenizer
    )
    data_args.max_seq_length = max_seq_length
    if training_args.fold is False:
        datasets, train_dataset, eval_dataset, data_collator = get_data(training_args, model_args, data_args, tokenizer)
        # if "validation" not in datasets:
        #     raise ValueError("--do_eval requires a validation dataset")
        run_mrc(data_args, training_args, model_args, tokenizer, model,
                datasets, train_dataset, eval_dataset, data_collator, last_checkpoint)
    else:
        from transformers import DataCollatorWithPadding
        from data_processing import DataProcessor
        data_collator = DataCollatorWithPadding(
            tokenizer, pad_to_multiple_of=(8 if training_args.fp16 else None)
        )
        data_processor = DataProcessor(tokenizer, model_args, data_args)
        origin_output_dir = training_args.output_dir

        combined_datasets = load_from_disk('../data/combined_dataset')
        kf = KFold(n_splits=5, random_state=42, shuffle=True)
        for idx, (train_index, valid_index) in enumerate(kf.split(combined_datasets), 1):
            train_dataset, eval_dataset = map(Dataset.from_dict, [combined_datasets[train_index], combined_datasets[valid_index]])
            datasets = DatasetDict({'train' : train_dataset, 'validation' : eval_dataset})

            train_dataset = data_processor.train_tokenizer(train_dataset, train_dataset.column_names)
            eval_dataset = data_processor.valid_tokenizer(eval_dataset, eval_dataset.column_names)

            training_args.output_dir = origin_output_dir + f'{idx}'
            run_mrc(data_args, training_args, model_args, tokenizer, model,
                    datasets, train_dataset, eval_dataset, data_collator, last_checkpoint)

inference

여기서 가장 중요하게 생각한 부분은, 최대한의 수정 없이 재사용할 수 있는 부분을 고민하는 것입니다.

따라서, 목표를 코드를 재사용해서 cross하는 방법으로 세웠습니다. 바로, 기존의 nbest_prediction을 이용하는 것입니다.

다만, 여기에도 문제가 조금 있습니다. image

또한, 재사용성이라는 점의 초점을 맞추어, inference를 5번(fold 개수) 해야합니다. 물론 이는 train에서 자동으로 진행되게 설정했으며, 마지막에 combine.py를 실행해서 최종 결과를 얻을 수 있도록 했습니다.

✨ 결과

k = 1

image

k = 30

image

image

추측할 수 있는 경우는 다음과 같습니다.

결론

k fold를 사용하는 것이 무조건 유리한 것으로 보입니다.

개선점

현재는 kfold에서 retrieve를 비효율적으로 사용합니다. 쉽게 예를 들겠습니다. 학생 A, B, C, D, E 총 5명이 있습니다. 도서관에서 책을 빌리는데는 5원이들고 도서관에 책을 반납하는데도 5원이 듭니다. 책을 보는데 1인당 총 10원의 비용이 듭니다. 따라서 전체 인원이 책을 보는데는 50원의 비용이 소모됩니다. 근데 여기서, A가 책을 빌리고 A, B, C, D, E가 이 책을 나눠서 본다음에 E가 반납을 하면 총 10원의 비용이 소모됩니다. 위 예시와 같이, 각 fold마다 retrieve를 독립적으로 하는것이 아니라 retrieve를 하면 이것을 각 fold가 나누어서 처리할 수 있도록 개선해야 될 것입니다.