일상 대화 요약 대회

일상 대화 요약 대회

1. Abstract

  • Goal of the Competition
    • 학교 생활, 직장, 치료, 쇼핑, 여가, 여행 등 광범위한 일상 생활 중 하는 대화들에 대해 요약합니다.
  • Timeline
    • 2024.03.08 : 대회 시작
    • 2024.03.20 : 대회 종료

2. Process : Competition Model

  • 사용 library
    ```python

    in python

    import pandas as pd import os import re import json import yaml from glob import glob from tqdm import tqdm from pprint import pprint import torch import pytorch_lightning as pl from rouge import Rouge # 모델의 성능을 평가하기 위한 라이브러리입니다.

from torch.utils.data import Dataset , DataLoader from transformers import AutoTokenizer, BartForConditionalGeneration, BartConfig from transformers import Seq2SeqTrainingArguments, Seq2SeqTrainer from transformers import Trainer, TrainingArguments from transformers import EarlyStoppingCallback

import wandb



- 결과(개인)<br>
final_result = ROUGE-1, ROUGE-2, ROUGE-L의 평균<br>
public final_result : 41.7972<br>
private final_result : 38.9605

- 결과(팀)<br>
public final_result : 41.9246<br>
private final_result : 39.1958

## 3. Process : Issues

**Describe the issue that your team faced during the project.**

1. 데이터의 수가 작아 데이터가 더 필요했음
2. 관련한 다양한 모델을 사용하기에 batch size 문제가 발생했음

**Describe the possible solution to imporve your project.**

1. back translation을 이용해서 데이터의 수를 증강해볼 수 있었음<br>
데이터는 파파고 홈페이지에 데이터를 주고 받는 코드를 자동화하여 생성할 수 있었다
2. device에 올리는 batch size 크기를 줄이고, gradient를 더 자주 업데이트하게 만들면서 batch size 문제를 해결해보았음

    

## 4. Role

**Describe your role with task in your team.**
* Data Engineering
* Modeling
* EDA

(notion. 각자 EDA와 모델링을 진행하고 그 insight를 공유하는 식으로 대회를 진행하여, 서로 다른 모델을 만들었음)

**Describe papers or book chapeters you found relevant to the problem, references to see.**

<a href = 'https://arxiv.org/pdf/1910.13461.pdf'> BART : Denoising Sequence-to-Sequence Pre-training for Natural Language Generation, Translation, and Comprehension</a>

<a href = 'https://dacon.io/competitions/official/235673/data'>Dacon 한국어 문서 생성요약 AI 경진대회</a>

<a href = 'https://aiconnect.kr/competition/detail/223/task/272/community'>AIconnection 한국어 문서 생성 요약</a>

<a href = 'https://velog.io/@cjkangme/DL-%ED%95%9C%EA%B5%AD%EC%96%B4-%EB%AC%B8%EC%84%9C-%EC%83%9D%EC%84%B1-%EC%9A%94%EC%95%BD-%EA%B2%BD%EC%A7%84%EB%8C%80%ED%9A%8C-%ED%9B%84%EA%B8%B0-with-%EC%97%90%EC%9D%B4%EB%B8%94%EB%9F%AC'>한국어 문서 생성 요약 경진대회 후기 포스팅</a>

**Explain which are relevant for your Project.**

사용 모델인 BART 논문과, Dacon 한국어 문서 생성 요약 경진대회의 커뮤니티가 도움이 되었음
    

## 5. Results

**Write the main result of Competition**

- 결과(개인)<br>
final_result = ROUGE-1, ROUGE-2, ROUGE-L의 평균<br>
public final_result : 41.7972<br>
private final_result : 38.9605

- 결과(팀)<br>
public final_result : 41.9246<br>
private final_result : 39.1958

**Final standings of the Leaderboard**

**7등 기록**
    

## 6. Conclusion

**Describe your running code with its own advantages and disadvantages, in relation to other groups in the course.**

## ⚙️ 데이터 및 환경설정

### 1) 필요한 라이브러리 설치

- 필요한 라이브러리를 설치한 후 불러옵니다.


```python
import pandas as pd
import os
import re
import json
import yaml
from glob import glob
from tqdm import tqdm
from pprint import pprint
import torch
import pytorch_lightning as pl
from rouge import Rouge 

from torch.utils.data import Dataset , DataLoader
from transformers import AutoTokenizer, BartForConditionalGeneration, BartConfig
from transformers import Seq2SeqTrainingArguments, Seq2SeqTrainer
from transformers import Trainer, TrainingArguments
from transformers import EarlyStoppingCallback

import wandb 




```python
# config 설정에 tokenizer 모듈이 사용되므로 미리 tokenizer를 정의
tokenizer = AutoTokenizer.from_pretrained("digit82/kobart-summarization")
config_data = {
    "general": {
        "data_path": "../data/", 
        "model_name": "digit82/kobart-summarization", 
        "output_dir": "./"
    },
    "tokenizer": {
        "encoder_max_len": 512,
        "decoder_max_len": 100,
        "bos_token": f"{tokenizer.bos_token}",
        "eos_token": f"{tokenizer.eos_token}",
        # 특정 단어들이 분해되어 tokenization이 수행되지 않도록 special_tokens 지정
        "special_tokens": ['#Person1#', '#Person2#', '#Person3#', '#PhoneNumber#', '#Address#', '#PassportNumber#']
    },
    "training": {
        "overwrite_output_dir": True,
        "num_train_epochs": 20,
        "learning_rate": 1e-5,
        "per_device_train_batch_size": 50,
        "per_device_eval_batch_size": 32,
        "warmup_ratio": 0.1,
        "weight_decay": 0.01,
        "lr_scheduler_type": 'cosine',
        "optim": 'adamw_torch',
        "gradient_accumulation_steps": 1,
        "evaluation_strategy": 'epoch',
        "save_strategy": 'epoch',
        "save_total_limit": 5,
        "fp16": True,
        "load_best_model_at_end": True,
        "seed": 42,
        "logging_dir": "./logs",
        "logging_strategy": "epoch",
        "predict_with_generate": True,
        "generation_max_length": 100,
        "do_train": True,
        "do_eval": True,
        "early_stopping_patience": 3,
        "early_stopping_threshold": 0.001,
        "report_to": "wandb" 
    },

    "wandb": {
        "entity": "wandb_repo",
        "project": "project_name",
        "name": "run_name"
    },
    "inference": {
        "ckt_path": "model ckt path", # 사전 학습이 진행된 모델의 checkpoint를 저장할 경로 설정
        "result_path": "./prediction/",
        "no_repeat_ngram_size": 2,
        "early_stopping": True,
        "generate_max_length": 100,
        "num_beams": 4,
        "batch_size" : 32,
        # 정확한 모델 평가를 위해 제거할 불필요한 생성 토큰 정의
        "remove_tokens": ['<usr>', f"{tokenizer.bos_token}", f"{tokenizer.eos_token}", f"{tokenizer.pad_token}"]
    }
}
# 모델의 구성 정보를 YAML 파일로 저장
config_path = "./config.yaml"
with open(config_path, "w") as file:
    yaml.dump(config_data, file, allow_unicode=True)

3) Configuration 불러오기

# 저장된 config 파일을 불러오기
config_path = "./config.yaml"

with open(config_path, "r") as file:
    loaded_config = yaml.safe_load(file)

# 불러온 config 파일의 전체 내용 확인
pprint(loaded_config)
# 실험에 쓰일 데이터의 경로, 사용될 모델, 모델의 최종 출력 결과를 저장할 경로에 대해 확인
loaded_config['general']
# 이곳에 사용자가 저장한 데이터 dir 설정
# loaded_config['general']['data_path'] = "data_path"
# 데이터 전처리를 하기 위해 tokenization 과정에서 필요한 정보 확인
loaded_config['tokenizer']
# 모델이 훈련 시 적용될 매개변수 확인
loaded_config['training']
# 모델 학습 과정에 대한 정보를 제공해주는 wandb 설정 내용 확인
loaded_config['wandb']
# (선택) 이곳에 사용자가 사용할 wandb config 설정
loaded_config['wandb']['entity'] = "jinintg"
loaded_config['wandb']['name'] = "1"
loaded_config['wandb']['project'] = "Upstage_NLP_Project1"
# 모델이 최종 결과를 출력하기 위한 매개변수 정보 확인
loaded_config['inference']

4) 데이터 확인

# config에 저장된 데이터 경로를 통해 train과 validation data를 불러오기
data_path = loaded_config['general']['data_path']

# train data의 구조와 내용 확인
train_df = pd.read_csv(os.path.join(data_path,'train.csv'))
train_df.tail()
# validation data의 구조와 내용 확인
val_df = pd.read_csv(os.path.join(data_path,'dev.csv'))
val_df.tail()

1. 데이터 가공 및 데이터셋 클래스 구축

  • csv file 을 불러와서 encoder 와 decoder의 입력형태로 가공
  • 가공된 데이터를 torch dataset class 로 구축하여 모델에 입력가능한 형태로 변환
# 데이터셋을 데이터프레임으로 변환하고 인코더와 디코더의 입력생성
class Preprocess:
    def __init__(self,
            bos_token: str,
            eos_token: str,
        ) -> None:

        self.bos_token = bos_token
        self.eos_token = eos_token

    @staticmethod
    # 실험에 필요한 컬럼 가져오기
    def make_set_as_df(file_path, is_train = True):
        if is_train:
            df = pd.read_csv(file_path)
            train_df = df[['fname','dialogue','summary']]
            return train_df
        else:
            df = pd.read_csv(file_path)
            test_df = df[['fname','dialogue']]
            return test_df

    # BART 모델의 입력, 출력 형태를 맞추기 위해 전처리 진행
    def make_input(self, dataset,is_test = False):
        if is_test:
            encoder_input = dataset['dialogue']
            decoder_input = [self.bos_token] * len(dataset['dialogue'])
            return encoder_input.tolist(), list(decoder_input)
        else:
            encoder_input = dataset['dialogue']
            decoder_input = dataset['summary'].apply(lambda x : self.bos_token + str(x)) # Ground truth를 디코더의 input으로 사용하여 학습
            decoder_output = dataset['summary'].apply(lambda x : str(x) + self.eos_token)
            return encoder_input.tolist(), decoder_input.tolist(), decoder_output.tolist()

# Train에 사용되는 Dataset 클래스 정의
class DatasetForTrain(Dataset):
    def __init__(self, encoder_input, decoder_input, labels, len):
        self.encoder_input = encoder_input
        self.decoder_input = decoder_input
        self.labels = labels
        self.len = len

    def __getitem__(self, idx):
        item = {key: val[idx].clone().detach() for key, val in self.encoder_input.items()} # item[input_ids], item[attention_mask]
        item2 = {key: val[idx].clone().detach() for key, val in self.decoder_input.items()} # item2[input_ids], item2[attention_mask]
        item2['decoder_input_ids'] = item2['input_ids']
        item2['decoder_attention_mask'] = item2['attention_mask']
        item2.pop('input_ids')
        item2.pop('attention_mask')
        item.update(item2) #item[input_ids], item[attention_mask] item[decoder_input_ids], item[decoder_attention_mask]
        item['labels'] = self.labels['input_ids'][idx] #item[input_ids], item[attention_mask] item[decoder_input_ids], item[decoder_attention_mask], item[labels]
        return item

    def __len__(self):
        return self.len

# Validation에 사용되는 Dataset 클래스 정의
class DatasetForVal(Dataset):
    def __init__(self, encoder_input, decoder_input, labels, len):
        self.encoder_input = encoder_input
        self.decoder_input = decoder_input
        self.labels = labels
        self.len = len

    def __getitem__(self, idx):
        item = {key: val[idx].clone().detach() for key, val in self.encoder_input.items()} # item[input_ids], item[attention_mask]
        item2 = {key: val[idx].clone().detach() for key, val in self.decoder_input.items()} # item2[input_ids], item2[attention_mask]
        item2['decoder_input_ids'] = item2['input_ids']
        item2['decoder_attention_mask'] = item2['attention_mask']
        item2.pop('input_ids')
        item2.pop('attention_mask')
        item.update(item2) #item[input_ids], item[attention_mask] item[decoder_input_ids], item[decoder_attention_mask]
        item['labels'] = self.labels['input_ids'][idx] #item[input_ids], item[attention_mask] item[decoder_input_ids], item[decoder_attention_mask], item[labels]
        return item

    def __len__(self):
        return self.len

# Test에 사용되는 Dataset 클래스 정의
class DatasetForInference(Dataset):
    def __init__(self, encoder_input, test_id, len):
        self.encoder_input = encoder_input
        self.test_id = test_id
        self.len = len

    def __getitem__(self, idx):
        item = {key: val[idx].clone().detach() for key, val in self.encoder_input.items()}
        item['ID'] = self.test_id[idx]
        return item

    def __len__(self):
        return self.len

# tokenization 과정까지 진행된 최종적으로 모델에 입력될 데이터 출력
def prepare_train_dataset(config, preprocessor, data_path, tokenizer):
    train_file_path = os.path.join(data_path,'train.csv')
    val_file_path = os.path.join(data_path,'dev.csv')

    # train, validation에 대해 각각 데이터프레임 구축
    train_data = preprocessor.make_set_as_df(train_file_path)
    val_data = preprocessor.make_set_as_df(val_file_path)

    print('-'*150)
    print(f'train_data:\n {train_data["dialogue"][0]}')
    print(f'train_label:\n {train_data["summary"][0]}')

    print('-'*150)
    print(f'val_data:\n {val_data["dialogue"][0]}')
    print(f'val_label:\n {val_data["summary"][0]}')

    encoder_input_train , decoder_input_train, decoder_output_train = preprocessor.make_input(train_data)
    encoder_input_val , decoder_input_val, decoder_output_val = preprocessor.make_input(val_data)
    print('-'*10, 'Load data complete', '-'*10,)

    tokenized_encoder_inputs = tokenizer(encoder_input_train, return_tensors="pt", padding=True,
                            add_special_tokens=True, truncation=True, max_length=config['tokenizer']['encoder_max_len'], return_token_type_ids=False)
    tokenized_decoder_inputs = tokenizer(decoder_input_train, return_tensors="pt", padding=True,
                        add_special_tokens=True, truncation=True, max_length=config['tokenizer']['decoder_max_len'], return_token_type_ids=False)
    tokenized_decoder_ouputs = tokenizer(decoder_output_train, return_tensors="pt", padding=True,
                        add_special_tokens=True, truncation=True, max_length=config['tokenizer']['decoder_max_len'], return_token_type_ids=False)

    train_inputs_dataset = DatasetForTrain(tokenized_encoder_inputs, tokenized_decoder_inputs, tokenized_decoder_ouputs,len(encoder_input_train))

    val_tokenized_encoder_inputs = tokenizer(encoder_input_val, return_tensors="pt", padding=True,
                        add_special_tokens=True, truncation=True, max_length=config['tokenizer']['encoder_max_len'], return_token_type_ids=False)
    val_tokenized_decoder_inputs = tokenizer(decoder_input_val, return_tensors="pt", padding=True,
                        add_special_tokens=True, truncation=True, max_length=config['tokenizer']['decoder_max_len'], return_token_type_ids=False)
    val_tokenized_decoder_ouputs = tokenizer(decoder_output_val, return_tensors="pt", padding=True,
                        add_special_tokens=True, truncation=True, max_length=config['tokenizer']['decoder_max_len'], return_token_type_ids=False)

    val_inputs_dataset = DatasetForVal(val_tokenized_encoder_inputs, val_tokenized_decoder_inputs, val_tokenized_decoder_ouputs,len(encoder_input_val))

    print('-'*10, 'Make dataset complete', '-'*10,)
    return train_inputs_dataset, val_inputs_dataset

2. Trainer 및 Trainingargs 구축하기

  • Huggingface 의 Trainer 와 Training arguments를 활용하여 모델 학습을 일괄적으로 처리해주는 클래스를 정의
# 모델 성능에 대한 평가 지표를 정의
def compute_metrics(config,tokenizer,pred):
    rouge = Rouge()
    predictions = pred.predictions
    labels = pred.label_ids

    predictions[predictions == -100] = tokenizer.pad_token_id
    labels[labels == -100] = tokenizer.pad_token_id

    decoded_preds = tokenizer.batch_decode(predictions, clean_up_tokenization_spaces=True)
    labels = tokenizer.batch_decode(labels, clean_up_tokenization_spaces=True)

    # 정확한 평가를 위해 미리 정의된 불필요한 생성토큰들을 제거
    replaced_predictions = decoded_preds.copy()
    replaced_labels = labels.copy()
    remove_tokens = config['inference']['remove_tokens']
    for token in remove_tokens:
        replaced_predictions = [sentence.replace(token," ") for sentence in replaced_predictions]
        replaced_labels = [sentence.replace(token," ") for sentence in replaced_labels]

    print('-'*150)
    print(f"PRED: {replaced_predictions[0]}")
    print(f"GOLD: {replaced_labels[0]}")
    print('-'*150)
    print(f"PRED: {replaced_predictions[1]}")
    print(f"GOLD: {replaced_labels[1]}")
    print('-'*150)
    print(f"PRED: {replaced_predictions[2]}")
    print(f"GOLD: {replaced_labels[2]}")

    # 최종적인 ROUGE 점수 계산
    results = rouge.get_scores(replaced_predictions, replaced_labels,avg=True)

    # ROUGE 점수 중 F-1 score를 통해 평가
    result = {key: value["f"] for key, value in results.items()}
    return result
# 학습을 위한 trainer 클래스와 매개변수를 정의합니다.
def load_trainer_for_train(config,generate_model,tokenizer,train_inputs_dataset,val_inputs_dataset):
    print('-'*10, 'Make training arguments', '-'*10,)
    # set training args
    training_args = Seq2SeqTrainingArguments(
                output_dir=config['general']['output_dir'], # model output directory
                overwrite_output_dir=config['training']['overwrite_output_dir'],
                num_train_epochs=config['training']['num_train_epochs'],  # total number of training epochs
                learning_rate=config['training']['learning_rate'], # learning_rate
                per_device_train_batch_size=config['training']['per_device_train_batch_size'], # batch size per device during training
                per_device_eval_batch_size=config['training']['per_device_eval_batch_size'],# batch size for evaluation
                warmup_ratio=config['training']['warmup_ratio'],  # number of warmup steps for learning rate scheduler
                weight_decay=config['training']['weight_decay'],  # strength of weight decay
                lr_scheduler_type=config['training']['lr_scheduler_type'],
                optim =config['training']['optim'],
                gradient_accumulation_steps=config['training']['gradient_accumulation_steps'],
                evaluation_strategy=config['training']['evaluation_strategy'], # evaluation strategy to adopt during training
                save_strategy =config['training']['save_strategy'],
                save_total_limit=config['training']['save_total_limit'], # number of total save model.
                fp16=config['training']['fp16'],
                load_best_model_at_end=config['training']['load_best_model_at_end'], # 최종적으로 가장 높은 점수 저장
                seed=config['training']['seed'],
                logging_dir=config['training']['logging_dir'], # directory for storing logs
                logging_strategy=config['training']['logging_strategy'],
                predict_with_generate=config['training']['predict_with_generate'], #To use BLEU or ROUGE score
                generation_max_length=config['training']['generation_max_length'],
                do_train=config['training']['do_train'],
                do_eval=config['training']['do_eval'],
                report_to=config['training']['report_to'] # (선택) wandb를 사용할 때 설정합니다.
            )

    # (선택) 모델의 학습 과정을 추적하는 wandb를 사용하기 위해 초기화
    wandb.init(
        entity=config['wandb']['entity'],
        project=config['wandb']['project'],
        name=config['wandb']['name'],
    )

    # (선택) 모델 checkpoint를 wandb에 저장하도록 환경 변수 설정
    os.environ["WANDB_LOG_MODEL"]="true"
    os.environ["WANDB_WATCH"]="false"

    # Validation loss가 더 이상 개선되지 않을 때 학습을 중단시키는 EarlyStopping 기능을 사용
    MyCallback = EarlyStoppingCallback(
        early_stopping_patience=config['training']['early_stopping_patience'],
        early_stopping_threshold=config['training']['early_stopping_threshold']
    )
    print('-'*10, 'Make training arguments complete', '-'*10,)
    print('-'*10, 'Make trainer', '-'*10,)

    # Trainer 클래스를 정의
    trainer = Seq2SeqTrainer(
        model=generate_model, # 사용자가 사전 학습하기 위해 사용할 모델 입력
        args=training_args,
        train_dataset=train_inputs_dataset,
        eval_dataset=val_inputs_dataset,
        compute_metrics = lambda pred: compute_metrics(config,tokenizer, pred),
        callbacks = [MyCallback]
    )
    print('-'*10, 'Make trainer complete', '-'*10,)

    return trainer
# 학습을 위한 tokenizer와 사전 학습된 모델을 불러오기
def load_tokenizer_and_model_for_train(config,device):
    print('-'*10, 'Load tokenizer & model', '-'*10,)
    print('-'*10, f'Model Name : {config["general"]["model_name"]}', '-'*10,)
    model_name = config['general']['model_name']
    bart_config = BartConfig().from_pretrained(model_name)
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    generate_model = BartForConditionalGeneration.from_pretrained(config['general']['model_name'],config=bart_config)

    special_tokens_dict={'additional_special_tokens':config['tokenizer']['special_tokens']}
    tokenizer.add_special_tokens(special_tokens_dict)

    generate_model.resize_token_embeddings(len(tokenizer)) #special token 재구성
    generate_model.to(device)
    print(generate_model.config)

    print('-'*10, 'Load tokenizer & model complete', '-'*10,)
    return generate_model , tokenizer

3. 모델 학습하기

  • 앞에서 구축한 클래스 및 함수를 활용하여 학습 진행합니다.
def main(config):
    # 사용할 device 정의
    device = torch.device('cuda:0' if torch.cuda.is_available()  else 'cpu')
    print('-'*10, f'device : {device}', '-'*10,)
    print(torch.__version__)

    # 사용할 모델과 tokenizer
    generate_model , tokenizer = load_tokenizer_and_model_for_train(config,device)
    print('-'*10,"tokenizer special tokens : ",tokenizer.special_tokens_map,'-'*10)

    # 학습에 사용할 데이터셋
    preprocessor = Preprocess(config['tokenizer']['bos_token'], config['tokenizer']['eos_token']) # decoder_start_token: str, eos_token: str
    data_path = config['general']['data_path']
    train_inputs_dataset, val_inputs_dataset = prepare_train_dataset(config,preprocessor, data_path, tokenizer)

    # Trainer 클래스
    trainer = load_trainer_for_train(config, generate_model,tokenizer,train_inputs_dataset,val_inputs_dataset)
    trainer.train()   # 모델 학습

    # (선택) 모델 학습이 완료된 후 wandb를 종료
    wandb.finish()
if __name__ == "__main__":
    main(loaded_config)

4. 모델 추론하기

# 이곳에 내가 사용할 wandb config 설정
loaded_config['inference']['ckt_path'] = "추론에 사용할 ckt 경로 설정"
  • test data를 사용하여 모델의 성능을 확인
# tokenization 과정까지 진행된 최종적으로 모델에 입력될 데이터를 출력
def prepare_test_dataset(config,preprocessor, tokenizer):

    test_file_path = os.path.join(config['general']['data_path'],'test.csv')

    test_data = preprocessor.make_set_as_df(test_file_path,is_train=False)
    test_id = test_data['fname']

    print('-'*150)
    print(f'test_data:\n{test_data["dialogue"][0]}')
    print('-'*150)

    encoder_input_test , decoder_input_test = preprocessor.make_input(test_data,is_test=True)
    print('-'*10, 'Load data complete', '-'*10,)

    test_tokenized_encoder_inputs = tokenizer(encoder_input_test, return_tensors="pt", padding=True,
                    add_special_tokens=True, truncation=True, max_length=config['tokenizer']['encoder_max_len'], return_token_type_ids=False,)
    test_tokenized_decoder_inputs = tokenizer(decoder_input_test, return_tensors="pt", padding=True,
                    add_special_tokens=True, truncation=True, max_length=config['tokenizer']['decoder_max_len'], return_token_type_ids=False,)

    test_encoder_inputs_dataset = DatasetForInference(test_tokenized_encoder_inputs, test_id, len(encoder_input_test))
    print('-'*10, 'Make dataset complete', '-'*10,)

    return test_data, test_encoder_inputs_dataset
# 추론 : tokenizer와 학습시킨 모델을 불러오기
def load_tokenizer_and_model_for_test(config,device):
    print('-'*10, 'Load tokenizer & model', '-'*10,)

    model_name = config['general']['model_name']
    ckt_path = config['inference']['ckt_path']
    print('-'*10, f'Model Name : {model_name}', '-'*10,)
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    special_tokens_dict = {'additional_special_tokens': config['tokenizer']['special_tokens']}
    tokenizer.add_special_tokens(special_tokens_dict)

    generate_model = BartForConditionalGeneration.from_pretrained(ckt_path)
    generate_model.resize_token_embeddings(len(tokenizer))
    generate_model.to(device)
    print('-'*10, 'Load tokenizer & model complete', '-'*10,)

    return generate_model , tokenizer
# 학습된 모델이 생성한 요약문의 출력 결과
def inference(config):
    device = torch.device('cuda:0' if torch.cuda.is_available()  else 'cpu')
    print('-'*10, f'device : {device}', '-'*10,)
    print(torch.__version__)

    generate_model , tokenizer = load_tokenizer_and_model_for_test(config,device)

    data_path = config['general']['data_path']
    preprocessor = Preprocess(config['tokenizer']['bos_token'], config['tokenizer']['eos_token'])

    test_data, test_encoder_inputs_dataset = prepare_test_dataset(config,preprocessor, tokenizer)
    dataloader = DataLoader(test_encoder_inputs_dataset, batch_size=config['inference']['batch_size'])

    summary = []
    text_ids = []
    with torch.no_grad():
        for item in tqdm(dataloader):
            text_ids.extend(item['ID'])
            generated_ids = generate_model.generate(input_ids=item['input_ids'].to('cuda:0'),
                            no_repeat_ngram_size=config['inference']['no_repeat_ngram_size'],
                            early_stopping=config['inference']['early_stopping'],
                            max_length=config['inference']['generate_max_length'],
                            num_beams=config['inference']['num_beams'],
                        )
            for ids in generated_ids:
                result = tokenizer.decode(ids)
                summary.append(result)

    #노이즈에 해당되는 스페셜 토큰을 제거
    remove_tokens = config['inference']['remove_tokens']
    preprocessed_summary = summary.copy()
    for token in remove_tokens:
        preprocessed_summary = [sentence.replace(token," ") for sentence in preprocessed_summary]

    output = pd.DataFrame(
        {
            "fname": test_data['fname'],
            "summary" : preprocessed_summary,
        }
    )
    result_path = config['inference']['result_path']
    if not os.path.exists(result_path):
        os.makedirs(result_path)
    output.to_csv(os.path.join(result_path, "output.csv"), index=False)

    return output

if __name__ == "__main__":
    output = inference(loaded_config)
output 

데이터 증강에 사용된 코드

from selenium import webdriver
from selenium.webdriver.common.by import By

from selenium import webdriver
from selenium.webdriver.common.by import By
import time



parsing_dict = {'한국어' : 2,
                '영어' : 3,
                '일본어' : 4,
                '중국어(간체)' : 5,
                '중국어(번체)' : 6,
                '스페인어' : 7,
                '프랑스어' : 8,
                '독일어' : 9,
                '러시아어' : 10,
                '포르투갈어' : 11,
                '이탈리아어' : 12,
                '베트남어' : 13,
                '태국어' : 14,
                '인도네시아어' : 15,
                '힌디어' : 16,
                '아랍어' : 17}
with open("translation.txt", 'r', encoding = 'utf-8') as f:
    inp = f.readlines()

src = inp[0].split('->')[0]
dest = inp[0].split('->')[1].rstrip()
output_li = []
browser = webdriver.Chrome()
browser.get("https://papago.naver.com/")
time.sleep(2)
browser.find_element(By.XPATH, f'/html/body/div/div/div[1]/section/div/div[1]/div[2]/div/div[2]/div[1]/div/div[1]/button[2]/span').click()
browser.find_element(By.XPATH, f'/html/body/div/div/div[1]/section/div/div[1]/div[2]/div/div[2]/div[1]/div/div[2]/ul/li[{parsing_dict[src]}]').click()
browser.find_element(By.XPATH, f'/html/body/div/div/div[1]/section/div/div[1]/div[3]/div/div[1]/div/div/div[1]/button[2]/span').click()
browser.find_element(By.XPATH, f'/html/body/div/div/div[1]/section/div/div[1]/div[3]/div/div[1]/div/div/div[2]/ul/li[{parsing_dict[dest] - 1}]').click()
for idx, value in enumerate(inp):
    if idx == 0:
        continue
    browser.find_element(By.XPATH, f'/html/body/div/div/div[1]/section/div/div[1]/div[2]/div/div[3]/label/textarea').click()
    browser.find_element(By.XPATH, f'/html/body/div/div/div[1]/section/div/div[1]/div[2]/div/div[3]/label/textarea').send_keys(value.strip())
    time.sleep(3)
    output = browser.find_element(By.XPATH, f'/html/body/div/div/div[1]/section/div/div/div[3]/div/div[5]/div').text
    output_li.append(output)
    browser.find_element(By.XPATH, '/html/body/div/div/div[1]/section/div/div/div[2]/div/div[3]/button').click()
with open('result.txt', 'w') as f:
    for i in range(len(output_li)):
        f.write(output_li[i] + "\n")

Sum up your project and suggest future extensions and improvements.

BART를 이용해서 문서 요약을 도전해보았다.

NLP 대회를 제대로 참가해보는 것이 처음이었고, 배울 수 있는 것이 참 많은 대회였다.
하지만 개인 사정으로 인해 대회 간 4일 정도 대회에 집중을 하지 못해 더 많은 실험을 못 해본 점이 매우 아쉽다.

다음에 기회가 된다면 해당 코드를 한번 더 발전시켜 블로그에 기록해보고 싶다.