본문 바로가기
AI/DeepLearning

35년치 뉴스 데이터 분류기 개발기 (1): 30개 라벨을 6개로, 그리고 첫 번째 CUDA Assert 삽질기

by Quantrol 2025. 11. 8.
반응형

5년치(1990-2025)에 달하는 방대한 뉴스 데이터를 분류하는 AI 모델을 개발하는

전 과정을 기록하는 기술 보고서의 첫 번째 편입니다.

1. 🚀 프로젝트의 시작: 30개의 라벨과 2개의 질문

모든 프로젝트는 '왜?'라는 질문에서 시작합니다. 우리가 가진 combined_articles.parquet 데이터는 수십만 건에 달하지만, 그대로 사용하기엔 두 가지 큰 문제가 있었습니다.

  1. 라벨 문제 (Complication 1): sector1 라벨이 30개가 넘었습니다. '경제', '부동산', '머니랩'이 섞여있고, '피플', '세상과 함께' 등 기준이 모호한 라벨도 많았습니다.
  2. 시계열 문제 (Complication 2): 데이터가 1990년부터 2025년까지 분포해, 시대별로 사용하는 용어, 문체, 토픽이 완전히 다를 것이 분명했습니다.

그래서 두 가지 핵심 질문(Question)을 정의했습니다.

  1. 30개 라벨을 6개 대분류('경제', '정치', '사회', '국제', '문화/스포츠', '기타')로 통합하고, 2020년 이후 최신 데이터로 훈련하면 강력한 분류기를 만들 수 있을까?
  2. 이 '최신 전문가' 모델이, 1990년대의 낯선 과거 데이터를 만났을 때 '시계열 드리프트'를 극복하고 성능을 유지할 수 있을까?

 


2. 🛠️ 훈련 셋업: 6개 라벨 통합과 모델 선정

첫 번째 질문에 답하기 위해, 먼저 2020년 이후 데이터로 모델을 훈련하기로 했습니다.

sector1의 30개 라벨을 6개로 통합하기 위해 map_to_main_sector 함수를 정의하고, LabelEncoder를 이용해 6개 클래스로 인코딩했습니다.

Python
 
def map_to_main_sector(sector_str):
    if not isinstance(sector_str, str):
        return '기타' # None, NaN 등 처리
    if sector_str in ['경제', '부동산', '돈 버는 재미', '머니랩']:
        return '경제'
    if sector_str in ['정치', '더 북한']:
        return '정치'
    # ... (이하 생략) ...
    return '기타'

# 6개 클래스(경제, 정치, 사회, 국제, 문화/스포츠, 기타)로 집계
label_encoder = LabelEncoder()
label_encoder.fit(agg_labels_raw)

모델은 두 가지를 준비했습니다.

  • Baseline: TF-IDF + Logistic Regression
  • Main: klue/roberta-base (A100 GPU 활용)

3. 💣 첫 번째 난관: 'CUDA device-side assert triggered'

A100 GPU에서 klue/roberta-base 훈련 스크립트를 실행하자마자, 첫 번째 벽에 부딪혔습니다.

TypeError (evaluation_strategy -> eval_strategy) 같은 간단한 오류는 금방 잡았지만, **AcceleratorError: CUDA device-side assert triggered**라는 치명적인 GPU 오류가 발생했습니다.

처음엔 fp16=True (혼합 정밀도) 옵션이 A100 GPU와 충돌하는 것으로 의심했습니다. False로 변경하고 1시간 넘게 다시 훈련을 돌렸지만, 동일한 오류가 재발생했습니다.

근본 원인은 토큰 길이였습니다.

klue/roberta-base 모델 아키텍처의 최대 토큰 길이는 **512**입니다. 하지만 훈련 스크립트의 토크나이징 코드에서 최대 길이를 1024로 잘못 설정했습니다.

Python
 
# [수정 전 - BUG]
def tokenize_text(examples):
    # klue/roberta-base는 512가 한계인데 1024를 요청
    return tokenizer(examples['text'], padding="max_length", truncation=True, max_length=1024)

모델이 처리할 수 없는 513번째 이후의 토큰 위치값을 찾으려다 GPU 어설션 오류가 발생한 것입니다. max_length를 512로 수정하자 거짓말처럼 오류가 사라졌습니다.


4. 😅 또 다른 복병: 훈련은 성공, 저장은 실패?

CUDA Assert 오류를 잡고 훈련이 무사히 끝나는가 싶었지만,

시각화 섹션(Confusion Matrix)에서 AttributeError: ticklabels라는 사소한 오타가 또 발생했습니다.

문제는 이 오류 때문에 11번(최종 모델 저장) 셀이 실행되지 않아 1시간 46분 훈련 결과가 증발할 뻔했다는 것입니다.

 
# [수정 전 - BUG]
def plot_confusion_matrix_custom(ax, y_true, y_pred, classes, title):
    cm = confusion_matrix(y_true, y_pred, normalize='true')
    # 'ticklabels'는 없는 인자. 'xticklabels'와 'yticklabels'로 나눠야 함.
    sns.heatmap(cm, ..., ticklabels=classes, ...)

# [수정 후]
    sns.heatmap(cm, ..., xticklabels=classes, yticklabels=classes, ...)

5. 마무리: 안정적인 훈련 스크립트 확보

프로젝트의 목표를 수립하고,

훈련을 방해하던 3가지 주요 버그(TypeError, CUDA Assert, AttributeError)를 모두 해결했습니다.

 

이로써 A100 GPU에서 안정적으로 훈련/저장할 수 있는 train_model.py 스크립트를 확보했습니다.

 

하지만 이 스크립 CUDA #CUDAAssertError #max_length #디버깅 트를 A100에서 본격적으로 실행하자마자 '뭘 해도 1시간 46분'이라는 두 번째 벽에 부딪혔습니다.

반응형