[NLP] 1-4. Solar API를 활용한 증강

Paper2Code·2025년 8월 7일

NLP Project

목록 보기
10/12
post-thumbnail

✅ 핵심 요점 정리

내용설명
🔁 LLM 활용한 증강 적극 권장단순 rule-based 증강보다 의미 있는 다양성 확보 가능
🧠 Solar API는 증강용으로 제공학습에 바로 쓰는 용도보다 데이터 다양화 목적
🔢 few-shot + 카테고리 정보 동시 제공요약 스타일 유지, 안정성 향상 효과
📈 summary만 증강할 경우에도 few-shot 활용 필요단일 문장만 넣으면 모델이 요약 스타일 벗어날 가능성 있음
🧪 프롬프트 엔지니어링은 json 등 포맷만 잘 잡으면 OK어떤 구조가 best인지는 샘플 실험 → 비교 후 결정
👥 팀원별 프롬프트 실험 권장다양한 스타일 실험 → best prompting 전략 도출

실전 적용 전략

1. 증강 목적에 따른 분류

목적증강 방식프롬프트 전략
dialogue 다양화동일 summary, dialogue paraphrasedialogue few-shot 사용
summary 다양화동일 dialogue, summary variationsummary few-shot 필요
전체 데이터 확장새로운 dialogue-sum 쌍 생성카테고리 + 두세 개 few-shot 제공

✅ client 파라미터 설정값 근거 (LLM generation tuning 관점)

temperature=1.1, top_p=0.9, frequency_penalty=0.1

파라미터의미설정한 이유
🔥 temperature=1.1창의성(랜덤성) 조절
높을수록 다양한 표현 시도
표현 다양화 목적이기 때문에 1.0 이상 설정이 타당
🎯 top_p=0.9nucleus sampling.
상위 90% 확률에 해당하는 토큰들 중에서 샘플링
높은 다양성을 유지하면서도 의미를 크게 벗어나지 않도록
🚫 frequency_penalty=0.1반복 억제.
이전에 나온 단어를 다시 생성하는 걸 약간 페널티
동일 문장/어절 반복 방지용. 0.1이면 적절히 억제하면서 자연스러움 유지

🔁 요약: "너무 무작위하지 않으면서도, 말하는 방식만 다르게 표현"하는 데 최적화된 설정입니다.

증강 사용 전략

항목설명검토 결과
✅ 입력 데이터 범위train.csv의 2000~3999행적절함
✅ 증강 방식 구분① dialogue 표현 다양화, ② summary 재생성전략 분리 좋음
✅ 스타일 고정dialogue: 구어체 / summary: 문어체 (존댓말)👍 일관성 유지 필수 조건 만족
✅ system_prompt역할 + 규칙 + 스타일 고정🧠 역할 분리 OK
✅ few-shot 구조user-assistant pair에 instruction 포함🌟 SFT 구조와 일치
✅ instruction 위치user message 내부🆗 HuggingFace/Solar 구조에 최적화

기능별 분리

  • system_prompt: 역할, 구어체/문어체 규칙, 출력 포맷 고정
  • build_prompt(mode="dialogue" or "summary"): 증강 목적별 프롬프트 생성
  • augment_data(mode="dialogue" or "summary"): 증강 실행
  • set_few_shot_sample(): 샘플 한 개 자동 추출
  • main(): train.csv에서 2000~3999행 불러와 증강 실행

증강 파이프라인 모듈화

import pandas as pd
import os
import sys
from dotenv import load_dotenv
from openai import OpenAI
from tqdm import tqdm

# === 경로 및 환경변수 설정 ===
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.append(ROOT_DIR)
load_dotenv()
UPSTAGE_API_KEY = os.getenv('UPSTAGE_API_KEY')

# === 전역 변수 ===
sample_dialogue1 = None
sample_summary1 = None

# === 샘플 1개 설정 ===
def set_few_shot_sample(df):
    global sample_dialogue1, sample_summary1
    row = df.sample(1).iloc[0]
    sample_dialogue1 = row["dialogue"]
    sample_summary1 = row["summary"]

# === 프롬프트 구성 ===
def build_prompt(input_text, mode="dialogue"):
    if mode == "dialogue":
        system_prompt = (
            "당신은 한국어 일상 대화를 자연스럽게 재작성하는 전문가입니다.\n"
            "주어진 대화의 의미는 유지하되, 표현을 자연스럽고 다르게 바꿔야 합니다.\n"
            "1. 대화는 구어체를 유지해야 합니다.\n"
            "2. 대화의 발화 수와 #PersonN# 포맷은 그대로 유지해야 합니다.\n"
            "3. 존댓말을 사용하세요."
        )

        few_shot_user = (
            "Instructions:\n"
            "1. 의미는 바꾸지 말고 표현만 다양화하세요.\n"
            "2. 구어체로 작성하세요.\n"
            "3. 발화 순서와 수를 바꾸지 마세요.\n\n"
            "Original Dialogue:\n"
            f"{sample_dialogue1.strip()}\n\n"
            "Rewritten Dialogue:\n"
        )
        few_shot_assistant = sample_dialogue1.strip()

        user_prompt = (
            "Original Dialogue:\n"
            f"{input_text.strip()}\n\n"
            "Rewritten Dialogue:\n"
        )

    elif mode == "summary":
        system_prompt = (
            "당신은 일상 대화를 문어체로 요약하는 요약 전문가입니다.\n"
            "대화는 구어체지만, 요약은 문어체로 간결하고 명확하게 작성해야 합니다.\n"
            "3인칭 관찰자 시점으로 존댓말을 사용하세요."
        )

        few_shot_user = (
            "Instructions:\n"
            "1. 요약은 문어체로 작성하세요.\n"
            "2. 대화의 핵심만 담고, 관찰자 시점으로 쓰세요.\n\n"
            "Dialogue:\n"
            f"{sample_dialogue1.strip()}\n\n"
            "Summary:\n"
        )
        few_shot_assistant = sample_summary1.strip()

        user_prompt = (
            "Dialogue:\n"
            f"{input_text.strip()}\n\n"
            "Summary:\n"
        )

    else:
        raise ValueError("mode must be 'dialogue' or 'summary'")

    return [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": few_shot_user},
        {"role": "assistant", "content": few_shot_assistant},
        {"role": "user", "content": user_prompt},
    ]

# === 증강 실행 ===
def augment_data(df, mode="dialogue"):
    client = OpenAI(api_key=UPSTAGE_API_KEY, base_url="https://api.upstage.ai/v1/solar")

    result = {
        "fname": [],
        "dialogue": [],
        "summary": [],
        "topic": [],
        "augmented": []
    }

    for idx, row in tqdm(df.iterrows(), total=len(df)):
        try:
            dialogue = row["dialogue"]
            summary = row["summary"]
            topic = row["topic"]
            fname = row["fname"]

            if mode == "dialogue":
                input_text = dialogue
            elif mode == "summary":
                input_text = dialogue
            else:
                raise ValueError("Invalid mode")

            messages = build_prompt(input_text, mode=mode)

            response = client.chat.completions.create(
                model="solar-1-mini-chat",
                messages=messages,
                temperature=1.1,
                top_p=0.9,
                frequency_penalty=0.1,
                stream=False
            )

            output = response.choices[0].message.content.strip()
            if not output:
                print(f"[{idx}] Empty response.")
                continue

            result["fname"].append(fname)
            result["dialogue"].append(dialogue)
            result["summary"].append(summary)
            result["topic"].append(topic)
            result["augmented"].append(output)

        except Exception as e:
            print(f"[{idx}] Error: {e}")
            continue

    suffix = "dialogue_aug" if mode == "dialogue" else "summary_aug"
    output_path = os.path.join(ROOT_DIR, "data", f"train_solar_{suffix}_2000_3999.csv")
    pd.DataFrame(result).to_csv(output_path, index=False)
    print(f"✅ 저장 완료: {output_path}")

# === 메인 실행 ===
if __name__ == "__main__":
    df = pd.read_csv(os.path.join(ROOT_DIR, "data", "train.csv"))
    df = df.iloc[2000:4000].reset_index(drop=True)
    set_few_shot_sample(df)

    # ✅ 여기를 바꿔서 두 번 돌릴 수 있음
    augment_data(df, mode="dialogue")   # → 대화 표현 다양화
    # augment_data(df, mode="summary")  # → 요약 재생성
profile
As I Imagine | 이론 정리 사이트 Tistory 링크 참조 ↓ 홈 아이콘 클릭)

0개의 댓글