반응형

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은 비선형 보완
- 앙상블이 최종 성능을 끌어올림
반응형
'Programming' 카테고리의 다른 글
| Python Visualization Library 완전 정리 : Matplotlib (0) | 2026.02.15 |
|---|---|
| 데이터 시각화 핵심 개념 한 번에 정리 (0) | 2026.02.15 |
| 토픽 모델링(Topic Modeling) 완전 정리— LDA 이론부터 20 Newsgroups 실습까지 (0) | 2026.02.14 |
| 감성분석(Sentiment Analysis) 정리: 지도학습 vs 감성사전(SentiWordNet/VADER) + IMDB 실습 (0) | 2026.02.14 |
| CountVectorizer부터 Pipeline/GridSearchCV까지: 뉴스그룹 분류로 배우는 피처 벡터화 + 희소행렬(COO/CSR) (0) | 2026.02.14 |