Programming

Mercari Price Suggestion— 대규모 텍스트 + 카테고리 데이터를 활용한 가격 예측 실전 프로젝트

Lucas.Kim 2026. 2. 14. 14:12
반응형

1. 개요

Mercari Price Suggestion 문제는 상품의 텍스트 정보와 메타 데이터를 활용하여
상품 가격을 예측하는 회귀(Regression) 문제입니다.

이 문제의 핵심 난이도는 다음과 같습니다.

  • 데이터 규모가 매우 큼 (약 148만 건)
  • 텍스트 피처(name, description) 비중이 큼
  • 범주형 피처가 매우 고차원
  • 평가 지표가 RMSLE로, 로그 변환이 필수

본 글에서는
전처리 → 희소 행렬 기반 피처 엔지니어링 → Ridge / LightGBM → 앙상블
까지의 전체 파이프라인을 정리합니다.

2. 데이터 로딩 및 타깃 분포 확인

mercari_df = pd.read_csv('./mercari/train.tsv', sep='\t')
print(mercari_df.shape)
  • 총 데이터 수: 1,482,535
  • 타깃 변수: price

2.1 가격 분포의 문제점

원본 가격 분포는 오른쪽으로 긴 꼬리(long tail) 를 가지며,
고가 상품이 소수 존재합니다.

sns.histplot(mercari_df['price'], bins=100)

2.2 로그 변환(Log Transformation)

RMSLE 기반 문제이므로 log1p 변환을 적용합니다.

mercari_df['price'] = np.log1p(mercari_df['price'])

👉 로그 변환 후 분포가 정규 분포에 가까워지며
👉 모델이 고가 상품에 과도하게 끌려가지 않도록 합니다.

3. 범주형 데이터 전처리

3.1 category_name 분해 (대 / 중 / 소)

Men/Tops/T-shirts 형태의 문자열을 분해합니다.

def split_cat(category_name):
    try:
        return category_name.split('/')
    except:
        return ['Other_Null','Other_Null','Other_Null']
        
        
mercari_df['cat_dae'], mercari_df['cat_jung'], mercari_df['cat_so'] = \
zip(*mercari_df['category_name'].apply(split_cat))

이렇게 분리하는 이유는
👉 카테고리의 계층 정보를 모델에 명시적으로 전달하기 위함입니다.

4. 결측치 처리 전략

mercari_df['brand_name'] = mercari_df['brand_name'].fillna('Other_Null')
mercari_df['category_name'] = mercari_df['category_name'].fillna('Other_Null')
mercari_df['item_description'] = mercari_df['item_description'].fillna('Other_Null')
  • 결측 자체도 하나의 정보로 판단
  • 별도 제거 없이 "Other_Null"로 통합

5. 텍스트 피처 벡터화 전략

Mercari 문제의 핵심은 텍스트 처리 전략 분리입니다.

5.1 name → CountVectorizer

cnt_vec = CountVectorizer()
X_name = cnt_vec.fit_transform(mercari_df['name'])

cnt_vec = CountVectorizer()
X_name = cnt_vec.fit_transform(mercari_df['name'])

5.2 item_description → TF-IDF

tfidf_descp = TfidfVectorizer(
    max_features=50000,
    ngram_range=(1,3),
    stop_words='english'
)
X_descp = tfidf_descp.fit_transform(mercari_df['item_description'])
  • 설명은 길고 문맥 정보 중요
  • n-gram + TF-IDF가 성능 향상에 결정적

6. 범주형 피처 원-핫 인코딩 (희소 행렬)

고차원 범주형 데이터는 반드시 희소 행렬(sparse matrix) 로 처리합니다.

from sklearn.preprocessing import OneHotEncoder

oh_encoder = OneHotEncoder()

X_brand = oh_encoder.fit_transform(mercari_df['brand_name'].values.reshape(-1,1))
X_item_cond_id = oh_encoder.fit_transform(mercari_df['item_condition_id'].values.reshape(-1,1))
X_shipping = oh_encoder.fit_transform(mercari_df['shipping'].values.reshape(-1,1))
X_cat_dae = oh_encoder.fit_transform(mercari_df['cat_dae'].values.reshape(-1,1))
X_cat_jung = oh_encoder.fit_transform(mercari_df['cat_jung'].values.reshape(-1,1))
X_cat_so = oh_encoder.fit_transform(mercari_df['cat_so'].values.reshape(-1,1))

👉 결과적으로 160,000+ 차원의 희소 행렬이 생성됩니다.

7. 모든 피처 결합 (Sparse HStack)

from scipy.sparse import hstack

X_features_sparse = hstack([
    X_name, X_descp, X_brand,
    X_item_cond_id, X_shipping,
    X_cat_dae, X_cat_jung, X_cat_so
]).tocsr()
  • CSR 형식은 연산 및 메모리 효율이 가장 좋음
  • 실무 대규모 텍스트 문제의 표준 방식

8. 평가 지표: RMSLE

def rmsle(y,y_pred):
    return np.sqrt(np.mean(np.power(np.log1p(y)-np.log1p(y_pred),2)))
  • 가격 예측 문제에서 상대 오차를 평가
  • 고가 상품의 과도한 영향 억제

9. Ridge 회귀 모델 실험

9.1 Item Description 제외

Ridge rmsle ≈ 0.498

9.2 Item Description 포함

Ridge rmsle ≈ 0.468

👉 상품 설명 텍스트가 가격 예측에 매우 중요함을 확인할 수 있습니다.

10. LightGBM 회귀 모델

lgbm_model = LGBMRegressor(
    n_estimators=200,
    learning_rate=0.5,
    num_leaves=125
)

LightGBM rmsle ≈ 0.457
  • 비선형 관계를 잘 학습
  • 범주형/텍스트 피처 혼합에 강함

11. Ridge + LightGBM 앙상블

preds = lgbm_preds * 0.45 + linear_preds * 0.55

최종 RMSLE ≈ 0.447

👉 서로 다른 모델의 오차 특성이 상호 보완
👉 단일 모델 대비 안정적 성능 확보


12. 결론 정리

  • Mercari 문제는 대규모 희소 행렬 처리 능력이 핵심
  • 텍스트 피처 분리 전략이 성능을 좌우
  • Ridge는 안정적 베이스라인
  • LightGBM은 비선형 보완
  • 앙상블이 최종 성능을 끌어올림
반응형