Patrick's 데이터 세상

국내 유통대기업 성격유형별 문체 특성 기반 맞춤형 광고 메시지 생성 모델 재구현 프로젝트 본문

Deep Learning/NLP 개발

국내 유통대기업 성격유형별 문체 특성 기반 맞춤형 광고 메시지 생성 모델 재구현 프로젝트

patrick610 2023. 5. 15. 23:24
반응형
SMALL

 
 
 
 
2022년에 사내에서 국내 유통대기업과의 공동 연구로 진행했던 프로젝트입니다.
해당 프로젝트에 대해 공동 연구를 진행하고 나서 보유하고 있는 데이터와 논문을 활용하여 모델을 다시 재구현하는 것이 목적이었습니다.
MBTI를 ST, NT, SF, NF 총 4개로 분류하여 성격 유형으로 나누고 각각의 문체를 특성으로 하여 시즌 정보와 MBTI 성격 유형을 입력하면 앱푸쉬 광고 문자를 생성하는 모델을 만드는 태스크입니다.
예시

원문 시즌 정보 성향 모델 생성 광고 문구
10월이니까 1OOO포인트♬

브랜드별 할인 받고 1천P 적립까지 더! 알뜰하게 가을 옷 준비하려면 터치▶
가을맞이 SF 10월 OOOOO 포인트 혜택 도착♥

브랜드별 할인에 1천P 적립까지 다 받아 알뜰하게 쇼핑하자!

 
 

👉🏻 참고

https://textnet.kr/postscript/?q=YToxOntzOjEyOiJrZXl3b3JkX3R5cGUiO3M6MzoiYWxsIjt9&bmode=view&idx=12822619&t=board 

 

성격 유형별 마케팅 문구 생성용 학습 데이터 구축기 : 작업기

대화 설계 및 고도화, 대화 생성성격 유형별 마케팅 문구 생성용 학습 데이터 구축기고객사산업분류가공난이도생활/문화상담당자성지민, 신하은, 김보배, 김지원, 임승희TEXTNET은 지난 6월부터 B

textnet.kr


 

👉🏻 작업 환경

Google Colab Pro Plus
GPU type A100-SXM4-40GB
GPU count 4
CPU type Intel Xeon 2.2Ghz
RAM 13GB

 

👉🏻 Dataset

원문을 토대로 각각의 MBTI로 설계된 성격 문체 프레임워크 적용한 학습 데이터가 5,000세트 총 25,000건입니다.
왼쪽 5,000세트 25,000건 오른쪽 예시와 같이 Pair-wise()로 변환하여 원문→NT, NT→ST와 같이 parallel 구조로 총 100,000건의 활용 데이터로 증강합니다.

 
 
 

👉🏻 model

 

Model Structure

 

인코더에 입력으로 원문의 MBTI 성격, 변경될 MBTI 성격, 시즌 정보를 스페셜 토큰으로 그리고 토큰화된 원문을 인코더에 입력합니다.

디코더에서는 변경될 문구를 입력으로 받습니다.

 

 

Code

train.py

import hydra
import torch
import wandb
from dataloader import load
from transformers import (
    PreTrainedTokenizerFast,
    BartForConditionalGeneration,
    TrainingArguments,
    Trainer,
    default_data_collator,
)

device = "cuda" if torch.cuda.is_available() else "cpu"


@hydra.main(config_path="./", config_name="config")
def main(cfg):
    # tokenizer
    # insert special token - special_tokens_map.json, tokenizer.json added_tokens, model-vocab
    tokenizer = PreTrainedTokenizerFast.from_pretrained(cfg.PATH.tokenizer)

    # model
    model = BartForConditionalGeneration.from_pretrained(cfg.MODEL.name)

    # data loder
    train_dataset, eval_dataset = load(tokenizer=tokenizer, **cfg.DATASETS)

	...

if __name__ == "__main__":
    main()

먼저 토크나이저에 스페셜 토큰을 추가하였습니다.

tokenizer를 저장해서  문장 구분 토큰인 <sep>, 원문의 MBTI 성격 토큰 <ctrl1>, 변경될 MBTI 성격 토큰 <ctrl2>, 시즌 정보 토큰 <season>을 추가해야 합니다. special_tokens_map.json에 <sep> 토큰, tokenizer.json의 added_tokens와 model-vocab 사전에 해당 토큰들을 추가합니다.

 

Pretrained Model KoBART를 활용하여 구축하였습니다.

조건부 생성 BardForConditionalGeneration task를 사용하여 load합니다.

 

 

dataloader.py

from os.path import abspath, splitext
from typing import Optional

from datasets import load_dataset, logging

logging.set_verbosity(logging.ERROR)


def load(
    tokenizer,
    seq_len,
    train_data_path: str,
    eval_data_path: Optional[str] = None,
    train_test_split: Optional[float] = None,
    worker: int = 1,
    batch_size: int = 1000,
    shuffle_seed: Optional[int] = None,
):
    def _tokenize_function(e):
        result = dict()
        decoder_input = dict()
        label = dict()

        result = tokenizer(
            [
                "<season>"
                + t1
                + "<ctrl1>"
                + t2
                + "<ctrl2>"
                + t3
                + tokenizer.sep_token
                + t4
                for t1, t2, t3, t4 in zip(
                    e["season"], e["ctrl1"], e["ctrl2"], e["input"]
                )
            ],
            max_length=seq_len,
            padding="max_length",
            truncation=True,
            return_tensors="np",
        )
        decoder_input = tokenizer(
            [tokenizer.bos_token + t for t in e["label"]],
            max_length=seq_len,
            padding="max_length",
            truncation=True,
            return_tensors="np",
        )

        label = tokenizer(
            [t + tokenizer.eos_token for t in e["label"]],
            max_length=seq_len,
            padding="max_length",
            truncation=True,
            return_tensors="np",
        )
        result["decoder_input_ids"] = decoder_input["input_ids"]
        result["decoder_attention_mask"] = decoder_input["attention_mask"]
        result["labels"] = label["input_ids"]

        return result

    data = data.map(
        _tokenize_function,
        batched=True,
        batch_size=batch_size,
        num_proc=worker,
        remove_columns=data["train"].column_names,
    )

    return data["train"], (data["test"] if is_eval else None)

dataloader에서 중요 code 부분만 발췌.

인코더에 입력으로 <season>시즌 정보<ctrl1>원문의 MBTI 성격<ctrl2>변경될 MBTI 성격<sep>원문을 토큰화하여 담습니다.

디코더에 입력으로 <bos>변경될 문구, 레이블에 입력으로 변경될 문구<eos>을 넣습니다.

huggingface dataset map으로 배치만큼 토큰화 작업을 시행합니다.

 


train.py

import hydra
import torch
import wandb
from dataloader import load
from transformers import (
    PreTrainedTokenizerFast,
    BartForConditionalGeneration,
    TrainingArguments,
    Trainer,
    default_data_collator,
)

device = "cuda" if torch.cuda.is_available() else "cpu"


@hydra.main(config_path="./", config_name="config")
def main(cfg):

	...
    
    # wandb
    wandb.init(
        project=cfg.ETC.project,
        entity=cfg.ETC.entity,
        name=cfg.ETC.name,
    )

    # trainer
    args = TrainingArguments(
        **cfg.TRAININGARGS,
    )

    trainer = Trainer(
        model=model,
        args=args,
        train_dataset=train_dataset,
        eval_dataset=eval_dataset,
        tokenizer=tokenizer,
        data_collator=default_data_collator,
    )
    trainer.train()

    trainer.save_model(cfg.PATH.save_dir)


if __name__ == "__main__":
    main()

log wandb, huggingface trainingArguments, trainer 활용하여 finetuning 학습하였습니다.

 

 

Result

train/loss 0.0426

 

 

문체 적합성 Accuracy, 정보 유사성 BLUE, 자연스러움 PPL을 사용해서 만들어서 평가를 해야 하지만 시간적 문제와 재구현이 목적이었으므로 정성 평가로 평가하였고 약 0.8 정도로 평가되었습니다.

 

 

후기

loss만 보면 과적합이라고 판단되는데 결과는 생각보다 꽤 잘나왔습니다.

논문과 데이터를 활용하여 기존에 구현되었던 task의 모델을 재구현하는 프로젝트였고 이런 성격의 연구는 처음이었으나 결과가 어느 정도 잘 나오는 것을 보고 미리 설계된 데이터의 중요성을 다시 한번 느꼈습니다.

잘 설계된 데이터가 모델에서 어떠한 결과를 생성할 수 있는지 확인할 수 있는 프로젝트였습니다.

현재는 해당 연구를 기점으로 범용적으로 사업화할 수 있는 생성 모델을 연구하고 있고 완료가 되면 포스팅 예정입니다.

 

 

 

 

반응형
LIST
Comments