🧠 LLM 엔지니어링

제품 설명 기반 가격 예측 실험: Frontier 모델 테스트

개발자 린다씨 2025. 5. 3. 17:45
반응형

✨ 개요

이 문서는 제품 설명 텍스트만을 기반으로 가격을 예측하는 프로젝트의 일환으로, 최신 Frontier 모델들의 성능을 평가하는 과정을 정리한 것입니다.

 

먼저, 기존에 전처리된 테스트 데이터를 활용해 사람이 직접 예측한 결과를 기준 삼아 비교하고,
그 다음으로 다양한 Frontier 모델 (예: gpt-4o-mini, gpt-4o-2024-08-06, Claude 3.5 Sonnet)을 이용해
모델별 가격 예측 성능을 테스트합니다.

 

특히, Frontier 모델들은 별도의 추가 학습 없이 바로 테스트 데이터에 적용하며,
정확성, 응답 포맷 처리(get_price), 비용 이슈(호출 비용 1~2센트 발생) 등에 주의하여 진행합니다.

 

이 문서는 다음을 목표로 합니다:

  • Frontier 모델이 제품 가격 예측 문제에서 얼마나 잘 작동하는지 평가
  • 사람이 예측한 결과와 Frontier 모델 결과 비교
  • gpt와 Claude 계열 모델의 실제 응답 특성 및 테스트 결과 분석
  • 추후 모델 개발 및 최적화 방향에 대한 인사이트 확보

📌 주요 테스트 대상

  • Human Prediction (사람 직접 입력)
  • GPT-4o-mini
  • GPT-4o (2024-08-06 Frontier 버전)
  • Claude 3.5 Sonnet (2024년 6월 버전)

1. 실험 준비 (Setup)

라이브러리 임포트

# 📦 필수 라이브러리 임포트

import os  # 🧭 파일 경로 작업 및 환경 변수 관리 (예: API 키 불러오기)
import re  # 🔍 정규 표현식 처리 (문자열 검색, 치환 등에 사용)
import math  # ➗ 수학 관련 함수 제공 (루트, 로그, 삼각함수 등)
import json  # 📄 JSON 형식의 데이터 읽기/쓰기
import random  # 🎲 무작위 수 생성, 리스트 섞기 등 랜덤 작업
from dotenv import load_dotenv  # 🔐 .env 파일로부터 환경 변수 로드

# 🤖 AI 모델 및 서비스 관련 라이브러리

from huggingface_hub import login  # 🧠 HuggingFace Hub 로그인 (모델 다운로드/업로드에 필요)
import matplotlib.pyplot as plt  # 📊 데이터 시각화를 위한 그래프 라이브러리
import numpy as np  # 🔢 수치 계산 및 배열 연산을 위한 라이브러리
import pickle  # 💾 파이썬 객체를 파일로 저장하거나 불러오기 위한 직렬화 라이브러리
from collections import Counter  # 📈 리스트나 데이터 내 항목들의 빈도수 계산

# 🧠 LLM API 클라이언트

from openai import OpenAI  # 🛠️ OpenAI API 사용을 위한 클라이언트 라이브러리 (예: GPT 모델 호출)
from anthropic import Anthropic  # 🛠️ Anthropic API 사용을 위한 클라이언트 라이브러리 (예: Claude 모델 호출)

환경 변수 설정 및 API 키 불러오기

# 🌎 환경 변수 설정

load_dotenv(override=True)
# 🔐 .env 파일에 저장된 환경 변수들을 현재 실행 환경에 불러옵니다
# - override=True 옵션을 주면, 이미 존재하는 환경 변수도 덮어쓸 수 있습니다

# 📌 필요한 API 키를 환경 변수로 설정 (없을 경우 기본값 사용)

os.environ['OPENAI_API_KEY'] = os.getenv('OPENAI_API_KEY', 'your-key-if-not-using-env')
# - OPENAI_API_KEY를 환경 변수에서 불러와 설정합니다
# - 만약 .env 파일에 값이 없으면 'your-key-if-not-using-env'라는 기본 문자열을 사용합니다

os.environ['ANTHROPIC_API_KEY'] = os.getenv('ANTHROPIC_API_KEY', 'your-key-if-not-using-env')
# - ANTHROPIC_API_KEY도 동일한 방식으로 설정합니다 (Claude 모델 사용 시 필요)

os.environ['HF_TOKEN'] = os.getenv('HF_TOKEN', 'your-key-if-not-using-env')
# - HuggingFace Hub에 접근할 때 필요한 HF_TOKEN도 환경 변수로 설정합니다

HuggingFace Hub 로그인

# 🔐 HuggingFace Hub 로그인

hf_token = os.environ['HF_TOKEN']
# - 환경 변수에서 HuggingFace 액세스 토큰(HF_TOKEN)을 불러옵니다
# - 이 토큰은 모델 다운로드/업로드 등 HuggingFace 서비스 이용 시 필요합니다

login(hf_token, add_to_git_credential=True)
# - HuggingFace Hub에 로그인합니다
# - add_to_git_credential=True 옵션을 주면 Git 인증 정보에도 저장되어,
#   추후 Git 기반 명령어(HuggingFace 관련 pull/push 등) 사용 시 자동 로그인됩니다

🧪 테스트 기능 분리 및 호출

# 🧪 테스트 기능 분리 및 호출

# - 테스트 관련 코드를 별도의 패키지(testing)로 분리했습니다
# - 이제 Tester 클래스의 test() 메서드를 호출해서,
#   원하는 함수(function_name)와 테스트 데이터셋(test_dataset)을 넣어 테스트할 수 있습니다

from items import Item  # 📦 Item 클래스 가져오기 (개별 제품 정보를 다루는 클래스)
from testing import Tester  # 🧪 Tester 클래스 가져오기 (모델/함수 성능 테스트를 담당)

🤖 LLM(대형 언어 모델) 클라이언트 인스턴스 생성

# 🤖 LLM(대형 언어 모델) 클라이언트 인스턴스 생성

openai = OpenAI()
# - OpenAI API를 사용하기 위한 클라이언트 객체 생성
# - 예: GPT-4o, GPT-4, GPT-3.5 등의 모델 호출에 사용

claude = Anthropic()
# - Anthropic API를 사용하기 위한 클라이언트 객체 생성
# - 예: Claude 3 모델군(Claude 3 Opus, Sonnet, Haiku 등) 호출에 사용

📈 주피터 노트북(Jupyter Notebook)에서 그래프를 바로 출력하도록 설정

# 📈 주피터 노트북(Jupyter Notebook)에서 그래프를 바로 출력하도록 설정

%matplotlib inline
# - matplotlib으로 그린 그래프를 별도 창이 아닌,
#   노트북 셀 내부에 바로 표시해줍니다
# - 노트북 환경에서 시각화할 때 기본적으로 사용하는 매직 명령어입니다

📦 데이터셋 불러오기 (train/test pickle)

# 📦 이전에 저장해둔 데이터 불러오기 (피클 파일 사용)

# - 시간이 오래 걸리는 데이터 전처리 과정을 다시 수행하지 않기 위해
#   미리 저장해둔 'train.pkl'과 'test.pkl' 파일을 불러옵니다

with open('train.pkl', 'rb') as file:
    train = pickle.load(file)
    # 🛠️ 'train.pkl' 파일을 읽어서 train 데이터셋으로 복원

with open('test.pkl', 'rb') as file:
    test = pickle.load(file)
    # 🛠️ 'test.pkl' 파일을 읽어서 test 데이터셋으로 복원

2. 사람 예측 결과 준비 (Preparing Human Predictions)

📝 테스트 세트 일부를 CSV로 저장

# 📄 CSV 파일로 테스트 데이터 일부 저장

import csv

with open('human_input.csv', 'w', encoding="utf-8") as csvfile:
    writer = csv.writer(csvfile)
    for t in test[:250]:
        writer.writerow([t.test_prompt(), 0])
        # - 각 테스트 샘플의 프롬프트를 가져와 CSV에 기록
        # - 두 번째 열(0)은 기본 정답 값(placeholder)로 입력

test 데이터셋에서 앞 250개 항목을 골라 CSV 파일로 저장합니다.

 

각 줄에는 프롬프트 텍스트 (t.test_prompt())와 임시 레이블(0) 이 함께 기록됩니다.

 

이 CSV 파일은 나중에 사람이 직접 읽거나, 다른 평가용 입력으로 사용할 수 있습니다.

📥 사람 예측 결과(human_output.csv) 읽어오기

# 📄 CSV 파일에서 사람 예측 결과 불러오기

human_predictions = []

with open('human_output.csv', 'r', encoding="utf-8") as csvfile:
    reader = csv.reader(csvfile)
    for row in reader:
        human_predictions.append(float(row[1]))
        # - CSV 파일의 각 행(row)에서 두 번째 열(row[1]) 값을 가져옴
        # - 값을 float(실수형)으로 변환해서 human_predictions 리스트에 추가

human_output.csv 파일을 열어, 두 번째 열(사람이 입력한 예측값) 을 읽고,

human_predictions 리스트에 실수형(float) 으로 저장합니다.

 

이렇게 하면 사람이 작성한 결과를 코드 안에서 사용할 수 있게 됩니다.

🧠 사람 가격 예측 함수(human_pricer) 정의

# 🧮 특정 아이템에 대한 사람 예측 가격 반환 함수

def human_pricer(item):
    idx = test.index(item)
    # - 주어진 item이 test 리스트에서 몇 번째(index)인지 찾는다

    return human_predictions[idx]
    # - 해당 인덱스에 대응하는 사람 예측 결과를 반환한다

주어진 item테스트 데이터(test) 안에서 몇 번째인지 찾은 다음,

같은 순서에 있는 human_predictions 값을 찾아서 반환하는 함수입니다.

 

즉, 사람이 예상한 가격을 코드에서 쉽게 불러올 수 있게 해줍니다.

🧪 사람 예측 결과 테스트(Tester.test(human_pricer, test))

# 🧪 Human Pricer 함수 테스트

Tester.test(human_pricer, test)
# - human_pricer 함수를 test 데이터셋에 대해 평가합니다
# - Tester 클래스가 내부적으로 정확도, 오류율 등을 계산해줍니다

human_pricer 함수를 테스트 데이터에 적용해서, 사람이 예측한 가격이 얼마나 잘 맞는지 자동으로 평가합니다.

 

결과는 아마 RMSE, MAE 같은 수치로 나올 가능성이 높아요.


3. Frontier 모델용 프롬프트 준비 (Preparing Prompts for Frontier Models)

🛠️ Frontier 모델을 위한 프롬프트 포맷(messages_for)

# 🧹 Frontier 모델에 맞는 깔끔한 프롬프트 만들기

# - 우리가 직접 모델을 학습시킬 때는 문제를 단순하게 만들 필요가 있지만,
# - Frontier 모델(OpenAI, Anthropic 등)은 고성능이기 때문에
#   별도의 문제 단순화 없이 바로 복잡한 입력을 받을 수 있습니다.

def messages_for(item):
    system_message = "You estimate prices of items. Reply only with the price, no explanation"
    # - 시스템 프롬프트: 역할 지시 (설명 없이 가격만 답변하도록 제한)

    user_prompt = item.test_prompt().replace(" to the nearest dollar", "").replace("\n\nPrice is $", "")
    # - 사용자 입력 생성: 기존 프롬프트에서 "to the nearest dollar" 문구와
    #   "Price is $" 부분을 제거해서 더 자연스럽고 간결하게 만듭니다

    return [
        {"role": "system", "content": system_message},
        {"role": "user", "content": user_prompt},
        {"role": "assistant", "content": "Price is $"}
        # - assistant 역할이 답변을 "Price is $" 형태로 시작하도록 유도
    ]

Frontier 모델용 프롬프트를 준비하는 함수입니다.

 

가격만 깔끔하게 답변하도록 시스템 메시지를 설정하고, 사용자 입력(user_prompt)에서는 불필요한 문구를 제거해서 모델이 더 정확하게 예측하도록 돕습니다.

 

이렇게 만들어진 프롬프트 리스트는 Frontier API 호출 시 바로 사용할 수 있어요!

🧪 프롬프트 테스트 예시

# 🧪 test 데이터 중 첫 번째 아이템으로 프롬프트 생성 테스트

messages_for(test[0])
# - test 리스트에서 첫 번째 아이템(test[0])을 가져와
#   messages_for() 함수를 통해 Frontier 모델용 프롬프트 형식을 생성합니다
# - 출력 결과를 통해 프롬프트 구성이 잘 되었는지 확인할 수 있습니다

테스트 데이터 중 하나를 사용해 messages_for 함수가 올바르게 프롬프트를 만드는지 직접 확인하는 과정입니다.

 

이 결과는 Frontier 모델(OpenAI GPT, Anthropic Claude 등) 호출할 때 바로 사용할 수 있습니다.


4. 가격 추출 유틸리티 함수 (Utility Function for Price Extraction)

🛠️ 문자열에서 가격 추출 함수(get_price)

# 🔎 문자열 안에서 가격(숫자)만 추출하는 함수

def get_price(s):
    s = s.replace('$', '').replace(',', '')
    # - 문자열 안의 달러 기호($)와 쉼표(,)를 제거합니다

    match = re.search(r"[-+]?\d*\.\d+|\d+", s)
    # - 정규 표현식을 사용해 숫자(정수 또는 소수)를 찾습니다

    return float(match.group()) if match else 0
    # - 숫자를 찾았다면 float형으로 변환해 반환하고,
    # - 찾지 못했다면 기본값 0을 반환합니다

문자열 안에 있는 달러 기호($), 쉼표(,) 를 없애고, 순수한 숫자(가격) 만 추출해서 float 형식으로 돌려주는 함수입니다.

 

만약 숫자가 하나도 없으면 기본으로 0을 반환합니다.

🧪 가격 추출 테스트 예시

# 🔍 get_price 함수 테스트

get_price("The price is roughly $99.99 because blah blah")
# - 문자열 안에 있는 "$99.99"를 인식하고
# - 달러 기호를 제거한 후 숫자 99.99를 float 타입으로 반환합니다
# - 예상 결과: 99.99

주어진 문장에서 가격($99.99) 을 정확히 추출해서 99.99라는 실수(float) 값으로 변환해 반환합니다.

 

get_price() 함수가 실제 데이터에서도 제대로 동작하는지 간단히 검증하는 과정입니다.

5. Frontier 모델별 테스트 (Testing Frontier Models)

5-1-1. 🤖 gpt-4o-mini 예측 함수(gpt_4o_mini)

# 🤖 gpt-4o-mini 모델로 가격 예측하는 함수

def gpt_4o_mini(item):
    response = openai.chat.completions.create(
        model="gpt-4o-mini",
        messages=messages_for(item),
        seed=42,
        max_tokens=5
    )
    # - gpt-4o-mini 모델을 호출해 응답을 받습니다
    # - messages_for(item)을 통해 생성한 프롬프트를 입력으로 제공합니다
    # - seed=42로 설정해 결과를 재현 가능하게 만듭니다
    # - max_tokens=5로 응답 길이를 짧게 제한 (숫자 하나만 받으면 되기 때문)

    reply = response.choices[0].message.content
    # - 모델 응답에서 실제 텍스트(가격 예측 결과)만 추출합니다

    return get_price(reply)
    # - 추출한 텍스트에서 숫자(가격)만 골라내어 반환합니다

주어진 item에 대해 gpt-4o-mini 모델을 호출하여, 가격만 깔끔하게 추출해서 반환하는 함수입니다.

 

seedmax_tokens를 설정해서 결과를 일정하게 유지하고, get_price() 함수를 이용해 숫자만 정확히 뽑아냅니다.

5-1-2. 🔍 test 데이터 첫 번째 항목의 실제 가격 확인

# 🔎 test 데이터 첫 번째 항목의 실제 가격 확인

test[0].price
# - test 리스트에서 첫 번째 아이템(test[0])의 실제 가격을 가져옵니다
# - 모델이 예측한 가격과 비교할 기준값(정답값, ground truth)입니다

test[0]은 테스트 데이터셋의 첫 번째 제품을 의미합니다.

 

.price를 통해 그 제품의 실제 가격(정답)을 가져옵니다.

 

이 값은 모델이 예측한 가격과 정확도를 비교할 때 기준으로 사용됩니다.

5-1-3. 🧪 gpt-4o-mini 테스트 결과(Tester.test(gpt_4o_mini, test))

# 🧪 gpt-4o-mini 모델을 test 데이터셋에 대해 평가

Tester.test(gpt_4o_mini, test)
# - gpt_4o_mini 함수를 test 데이터셋 전체에 적용해 성능을 평가합니다
# - Tester 클래스가 자동으로 오차율, 정확도 등의 평가 지표를 계산합니다
# - 사람이 직접 예측한 결과와 Frontier 모델(gpt-4o-mini) 결과를 비교하는 데 활용할 수 있습니다

gpt-4o-mini 모델이 테스트 데이터에서 가격을 얼마나 잘 예측하는지 평가합니다.

 

Tester.test()가 자동으로 성능 지표를 계산해서, "모델이 실제 가격에 얼마나 근접했는지" 알려줍니다.

5-2-1. 🚀 gpt-4o-2024-08-06 예측 함수(gpt_4o_frontier)

# 🚀 gpt-4o Frontier 버전으로 가격 예측하는 함수

def gpt_4o_frontier(item):
    response = openai.chat.completions.create(
        model="gpt-4o-2024-08-06", 
        messages=messages_for(item),
        seed=42,
        max_tokens=5
    )
    # - 최신 gpt-4o-2024-08-06 Frontier 모델을 호출합니다
    # - messages_for(item) 함수로 생성한 프롬프트를 입력으로 전달합니다
    # - seed=42로 결과를 재현 가능하게 유지합니다
    # - max_tokens=5로 응답 길이를 짧게 제한합니다 (숫자만 받을 목적)

    reply = response.choices[0].message.content
    # - 모델 응답에서 실제 텍스트(가격 예측 결과)만 추출합니다

    return get_price(reply)
    # - 추출한 텍스트에서 숫자(가격)만 뽑아내어 반환합니다

주어진 item에 대해 최신 gpt-4o-2024-08-06 모델을 호출해서 가격만 깔끔하게 추출하여 반환하는 함수입니다.

 

Frontier 모델 버전이기 때문에 더 높은 정확도나 특성을 기대할 수 있습니다.

5-2-2 🧪 gpt-4o-Frontier 테스트 결과(Tester.test(gpt_4o_frontier, test))

# 🧪 최신 gpt-4o-2024-08-06 모델로 테스트 데이터 평가

# - 주의: 이 모델을 호출하는 데 약 1~2센트의 비용이 발생했습니다
#   (지역에 따라 요금이 다를 수 있습니다)
# - 비용이 걱정된다면 직접 실행하지 않고 제공된 결과만 참고할 수도 있습니다

Tester.test(gpt_4o_frontier, test)
# - gpt_4o_frontier 함수를 test 데이터셋 전체에 적용해 성능을 평가합니다
# - Tester가 자동으로 RMSE, MAE 등 평가 지표를 계산해줍니다

gpt-4o Frontier 모델을 사용해 테스트 데이터에 대해 가격 예측 성능을 평가합니다.

 

최신 모델을 사용하기 때문에 더 나은 결과를 기대할 수 있지만, API 호출 비용이 소량 발생할 수 있다는 점에 주의해야 합니다.

5-3-1. 🎵 Claude 3.5 Sonnet 예측 함수(claude_3_point_5_sonnet)

# 🎵 Claude 3.5 Sonnet 모델을 사용해 가격 예측하는 함수

def claude_3_point_5_sonnet(item):
    messages = messages_for(item)
    system_message = messages[0]['content']
    # - system 역할 메시지를 따로 추출합니다 (Claude API는 system 파라미터로 따로 보내야 함)

    messages = messages[1:]
    # - 나머지 메시지(user, assistant 부분만 남깁니다)

    response = claude.messages.create(
        model="claude-3-5-sonnet-20240620",
        max_tokens=5,
        system=system_message,
        messages=messages
    )
    # - Claude 3.5 Sonnet (2024년 6월 버전) 모델을 호출합니다
    # - system 메시지와 user 메시지를 각각 전달합니다
    # - 최대 토큰 수를 5로 제한해 응답을 짧게 만듭니다 (숫자만 받을 목적)

    reply = response.content[0].text
    # - Claude 응답 중 첫 번째 메시지의 텍스트를 가져옵니다

    return get_price(reply)
    # - 텍스트에서 숫자(가격)만 추출해 반환합니다

주어진 item에 대해 Claude 3.5 Sonnet 모델을 호출해서 가격 예측 결과를 받아오고, 숫자만 깔끔하게 뽑아 반환하는 함수입니다.

 

Claude API에서는 system 메시지와 user 메시지를 따로 지정해야 하므로 약간 처리 방식이 다릅니다.

5-3-2. 🧪 Claude 3.5 Sonnet 테스트 결과(Tester.test(claude_3_point_5_sonnet, test))

# 🧪 Claude 3.5 Sonnet 모델로 테스트 데이터 평가

# - 주의: 이 모델을 호출하는 데 약 1~2센트의 비용이 들었습니다
#   (지역에 따라 요금이 다를 수 있습니다)

Tester.test(claude_3_point_5_sonnet, test)
# - claude_3_point_5_sonnet 함수를 test 데이터셋 전체에 적용해 성능을 평가합니다
# - Tester 클래스가 자동으로 RMSE, MAE 등의 평가 지표를 계산해줍니다

Claude 3.5 Sonnet 모델을 사용해 테스트 데이터에 대한 가격 예측 성능을 평가합니다.

 

Frontier 모델 중 하나로, 가격만 예측하는 간단한 작업에서도 꽤 좋은 성능을 기대할 수 있습니다.

 

호출 시 소량의 API 사용 비용이 발생할 수 있습니다.

반응형