[데이터분석] 부트캠프 TIL

20260316 TIL

myun0506 2026. 3. 16. 20:46

[Today I Learn]

- SQL codekata

- Python codekata

- BERT 텍스트 분류


[SQL codekata]

- 문제 1.

1. 문제 링크: https://leetcode.com/problems/students-and-examinations/

2. 오류 상황:

  1. students, subjects 테이블을 full outer join 하고 싶지만 공통 기준(on 조건)이 없음 --> CROSS JOIN 사용!
  2. examinations 테이블 left join 할 때 조인 조건에 st.student_id = e.student_id만 했더니 각 학생이 해당 과목의 시험을 몇번 봤는지에 대한 정보가 사라지고 모두 count = 6으로 출력됨 --> 조인 조건에 sb.subject_name = e.subject_name 추가!
  3. count(*)를 하면 시험을 보지 않은 경우라도 count = 1로 출력됨 --> count 안에 examinations 테이블의 컬럼을 넣을 경우 그 부분은 left join을 했기 때문에 NULL로 출력될 것임! 
cross join subjects sb 
left join examinations e 
on st.student_id = e.student_id
and sb.subject_name = e.subject_name

3. 정답 코드: 

select st.student_id, st.student_name, sb.subject_name, count(e.subject_name) as attended_exams
from students st
cross join subjects sb 
left join examinations e 
on st.student_id = e.student_id
and sb.subject_name = e.subject_name
group by 1, 2, 3
order by 1, 3

 

- CROSS JOIN

지금처럼 Students 와 Subjects 사이에 연결할 공통 컬럼이 없고, 무조건적인 모든 경우의 수(조합)를 만들어야 할 때 사용

CROSS JOIN을 사용하면 기준 조건 없이 두 테이블의 행을 하나씩 전부 곱해버림 (데카르트 곱, Cartesian Product)

SELECT st.student_id, st.student_name, sb.subject_name
FROM Students st
CROSS JOIN Subjects sb

 


[Python codekata] 

- 문제 1.

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/120837?language=python3

2. 정답 코드

def solution(hp):
    rest = hp
    jang, rest = divmod(rest,5)
    byung, rest = divmod(rest,3)
    il, rest = divmod(rest,1)
    return jang + byung + il
def solution(hp):
    answer = 0
    ants = [5, 3, 1]
    
    for ant in ants:
        count, hp = divmod(hp, ant)
        answer += count
        
    return answer
  • 개미종류가 더 늘어날 때를 대비해 확장성을 고려한 코드 --> 반복문 활용

TIL | BERT 텍스트분류 학습노트

오늘은 Hugging Face의 BERT/ELECTRA 모델을 활용한 텍스트 감성 분류를 학습했다. pipeline()을 이용한 추론부터, 직접 모델을 학습시키는 파인튜닝까지 전 과정을 실습했다.


1. BERT 모델 개요

TF-IDF는 단어의 문맥을 이해하지 못한다는 한계가 있다.

문장 TF-IDF가 보는 것 실제 의미

"이 영화 재미없다" "재미" => 긍정? 부정!
"이 영화 재미있다" "재미" => 긍정? 긍정!
"연기가 좋지 않다" "좋" => 긍정? 부정!

→ TF-IDF는 개별 단어만 보기 때문에 부정 표현, 문맥 의존적 의미를 놓친다.

그래서 등장한 BERT! (텍스트의 문맥을 양방향으로 이해)

  1. 사전학습: 위키피디아 등 대규모 텍스트로 언어의 일반적인 패턴을 미리 학습
  2. 파인튜닝: 감성 분류 등 특정 과제에 맞게 추가 학습
  3. 양방향 문맥 이해: 단어의 왼쪽과 오른쪽 문맥을 동시에 고려

Hugging Face

AI 모델을 공유하고 사용할 수 있는 오픈소스 플랫폼이다. 개발자들이 GitHub에서 코드를 공유하듯, AI 연구자들은 Hugging Face에서 학습된 모델을 공유한다.

  • Pipeline이란? Hugging Face의 pipeline()은 모델 + 토크나이저를 한 줄로 사용할 수 있게 해주는 편리한 도구
from transformers import pipeline
classifier = pipeline("sentiment-analysis", model="모델이름")
result = classifier("이 영화 정말 재밌어요!")
# => [{'label': '1', 'score': 0.9875}]  (1=긍정, 0=부정)

모델 찾는 방법:

  1. https://huggingface.co/models 에 접속
  2. Task 필터에서 Text Classification 선택
  3. Language 필터에서 데이터 셋에 맞는 언어(예: Korean) 선택
  4. 원하는 세부 작업을 검색창에 입력 (예: sentiment)
  5. 후보 모델의 모델 카드에서 평가 지표(Accuracy, F1 등) 확인
  6. 다운로드 수, 좋아요 수도 함께 참고

Hugging Face 덕분에 대규모 AI 모델을 직접 학습하지 않아도, 이미 학습된 모델을 pipeline() 한 줄로 바로 사용할 수 있다.


2. 환경 설정 및 영어 감성 분류 체험

en_classifier = pipeline("sentiment-analysis", device=device)
results = en_classifier("This movie is great!")
# => [{'label': 'POSITIVE', 'score': 0.9999}]
  • label: POSITIVE(긍정) 또는 NEGATIVE(부정)
  • score: 해당 라벨에 대한 모델의 확신도 (1에 가까울수록 확신)

3. 한국어 감성 분류 적용

ELECTRA vs BERT

  • BERT: 단어를 [MASK]로 가리고 맞히기 (빈칸 채우기 시험). 한 문장에서 [MASK]된 토큰(약 15%)만 학습에 활용
  • ELECTRA: 가짜 단어를 넣고 진짜/가짜 판별 (틀린 그림 찾기 시험). 한 문장의 모든 토큰을 학습에 활용 → 같은 크기에서 ELECTRA가 더 높은 성능
ko_classifier = pipeline(
    "sentiment-analysis",
    model="Copycats/koelectra-base-v3-generalized-sentiment-analysis",
    tokenizer="Copycats/koelectra-base-v3-generalized-sentiment-analysis",
    truncation=True, device=device
)

💡 BERT/ELECTRA에서는 전처리 과정을 생략한다!

  1. 자체 토크나이저 내장: BERT 계열 모델은 자체 토크나이저가 포함되어 있어 외부 형태소 분석기가 필요 없음
  2. 불용어가 문맥의 일부: '좋지 않다'에서 '않다'를 제거하면 의미가 반대로 바뀜. BERT는 불용어까지 포함한 전체 문맥을 이해하므로 제거하면 오히려 성능이 떨어짐
  3. 사전학습 데이터와 일치: BERT는 원본 텍스트로 사전학습되었기 때문에 같은 형태의 입력을 넣어야 성능이 잘 나옴

4. 분류 결과 평가

감성분류는 보통 긍정/부정이 비슷한 비율로 존재하므로 Accuracy만으로도 충분한 경우가 많다.


6. 직접 학습해보기: 랜덤초기화 vs 파인튜닝 vs 동결

지금까지는 이미 학습이 완료된 모델을 pipeline()으로 가져와서 바로 사용했다. 이제부턴 세 가지 방식으로 모델을 직접 학습시킨다.

방식 설명 비유

6-1. 랜덤 초기화 학습 모델 구조만 가져오고, 가중치는 랜덤으로 시작하여 처음부터 학습 백지 상태에서 한국어를 배우는 외국인
6-2. 파인튜닝 사전학습된 가중치를 가져와서, 소량의 데이터로 추가 학습 한국어를 이미 아는 사람이 영화 리뷰 분석법을 배우는 것
6-3. Encoder 동결 사전학습된 가중치를 고정하고, 분류 헤드만 학습 한국어 실력은 그대로 두고 리뷰 분류만 배우는 것

pipeline() vs 직접 학습의 차이

pipeline() (섹션 2~5) 직접 학습 (섹션 6)

토큰화 내부에서 자동 처리 직접 토큰화해서 숫자로 변환
데이터 형식 텍스트 리스트 그대로 전달 Dataset 객체로 변환 필요
모델 사용 추론(예측)만 가능 학습 + 추론 모두 가능
학습 설정 불필요 TrainingArguments로 하이퍼파라미터 지정

6-0. 학습 환경 세팅

from transformers import (
    AutoTokenizer, AutoModelForSequenceClassification,
    AutoConfig, Trainer, TrainingArguments
)
from datasets import Dataset as HFDataset

MODEL_NAME = "monologg/koelectra-small-v3-discriminator"
MAX_LENGTH = 64
BATCH_SIZE = 4
EPOCHS = 3
TRAIN_SIZE = 300

tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)

토큰화 과정:

def tokenize(example):
    return tokenizer(example["text"], truncation=True, padding="max_length", max_length=MAX_LENGTH)

train_dataset = train_dataset.map(tokenize, batched=True)
train_dataset.set_format("torch", columns=["input_ids", "attention_mask", "label"])

6-1. 랜덤 초기화 학습 (From Scratch)

모델의 구조(아키텍처)만 가져오고 가중치는 랜덤 값으로 초기화한다. 즉, 사전학습 없이 300건의 데이터만으로 처음부터 학습한다.

config = AutoConfig.from_pretrained(MODEL_NAME, num_labels=2)
model = AutoModelForSequenceClassification.from_config(config)
# from_config(): 구조만 사용하고 가중치는 랜덤 초기화

6-2. 파인튜닝 (Fine-tuning)

사전학습된 가중치를 그대로 가져와서 같은 300건으로 추가 학습한다. 이미 한국어의 문법, 단어 의미, 문맥 패턴을 학습했으므로 소량의 데이터만으로도 빠르게 적응할 수 있다.

model = AutoModelForSequenceClassification.from_pretrained(MODEL_NAME, num_labels=2)
# from_pretrained(): 사전학습된 가중치를 로드한 후 분류 헤드만 추가
# 6-1의 from_config()와 이 한 줄만 다르다!

6-3. Encoder 동결 학습 (Freeze)

파인튜닝의 변형으로, Encoder 본체의 가중치를 동결하고 분류 헤드만 학습하는 방식이다.

  • 원래 모델의 능력은 그대로 유지하면서, 데이터가 극소량이거나, 학습 시간을 줄이고 싶거나, 과적합을 방지하고 싶을 때 유용
  • 적은 메모리를 사용해서 빠른 훈련 가능
  • 모델의 가중치를 고정하고 헤더를 통해 필요한 능력만 따로 학습

7. 훈련된 모델 저장 및 불러오기

학습한 모델은 save_strategy="epoch" 설정으로 매 에포크마다 자동 저장되었다.

단계 코드 설명

1 from_pretrained(경로) 저장된 체크포인트에서 모델과 토크나이저를 불러온다
2 pipeline() 불러온 모델로 파이프라인을 생성한다
3 classifier(텍스트) 새로운 텍스트를 입력하면 바로 예측 결과를 얻는다
saved_classifier = pipeline(
    "text-classification",
    model="./best_model_finetune",
    tokenizer="./best_model_finetune",
)
results = saved_classifier(["이 영화 진짜 재미있다 강추합니다"])

8. 정리 및 핵심 요약

1. BERT/ELECTRA 모델의 핵심 개념

  • 사전학습으로 언어의 일반 패턴을 배우고 파인튜닝으로 특정 과제에 적용
  • 양방향 문맥 이해로 TF-IDF가 놓치는 문맥적 의미를 포착 가능
  • ELECTRA는 BERT와 같은 Encoder 구조이지만, 사전학습 방식이 더 효율적이라 같은 모델 크기에서 더 좋은 성능

2. Hugging Face Pipeline 활용

  • pipeline() 한 줄로 모델 로드부터 예측까지 간편하게 수행
  • 영어/한국어 모델을 동일한 인터페이스로 사용 가능

3. 텍스트 전처리 전략의 차이

  • TF-IDF는 정교한 전처리(정제, 형태소 분석, 불용어 제거)가 필수적
  • BERT는 자체 토크나이저가 내장되어 있어 원본 텍스트를 그대로 입력

4. 정량적 평가 방법

  • Accuracy, Precision, Recall, F1-Score로 분류 성능을 다각도로 평가
  • 혼동행렬로 모델이 어떤 유형의 오류를 범하는지 시각적으로 파악

5. 사전학습의 중요성 (전이 학습)

  • 랜덤 초기화로 처음부터 학습하면 소량 데이터로는 좋은 성능을 얻기 어려움
  • 사전학습된 모델을 파인튜닝하면 같은 소량 데이터로도 훨씬 높은 성능 달성
  • Encoder 동결은 사전학습 능력을 유지하면서 분류 헤드만 학습하는 경량 방식
  • 이것이 현대 NLP에서 사전학습 → 파인튜닝 패러다임이 표준이 된 이유!

모델 사용 절차 (추론 - 섹션 2~5)

이미 학습이 완료된 모델을 가져와서 바로 예측하는 방식이다.

단계 절차 설명

1 모델 선택 Hugging Face Models Hub에서 과제와 언어에 맞는 모델을 검색
2 파이프라인 생성 pipeline()에 과제명과 모델명을 지정하면 모델과 토크나이저가 자동 다운로드
3 텍스트 입력 원본 텍스트를 리스트로 전달하면, 내부에서 토큰화 => 모델 추론 => 후처리가 자동 수행
4 결과 확인 예측 라벨(label)과 확신도(score)가 딕셔너리 형태로 반환
5 성능 평가 정답 라벨과 비교하여 Accuracy, F1-Score, 혼동행렬 등으로 평가

별도의 전처리나 학습 설정 없이 텍스트만 넣으면 바로 결과를 얻을 수 있어, 빠른 프로토타이핑에 적합하다.

모델 훈련 절차 (학습 - 섹션 6)

사전학습된 모델(또는 빈 모델)을 우리 데이터에 맞게 직접 학습시키는 방식이다.

단계 절차 설명

1 토크나이저 로드 사전학습 모델에 맞는 토크나이저를 불러온다
2 데이터 준비 텍스트와 라벨을 딕셔너리로 묶어 HuggingFace Dataset 객체를 생성
3 토큰화 .map()으로 텍스트를 숫자(토큰 ID)로 일괄 변환
4 포맷 설정 .set_format("torch")로 PyTorch 텐서 형식으로 변환하고, 모델에 필요한 컬럼만 선택
5 모델 로드 from_pretrained() (파인튜닝) 또는 from_config() (랜덤 초기화)로 모델 생성
6 (선택) 가중치 동결 Encoder 본체의 파라미터를 requires_grad=False로 고정하면, 분류 헤드만 학습됨
7 학습 설정 TrainingArguments로 에포크 수, 배치 크기, 저장 전략 등 하이퍼파라미터 지정
8 학습 실행 Trainer에 모델, 설정, 데이터셋을 전달하고 .train()으로 학습 시작
9 평가 eval_strategy="epoch" 설정으로 매 에포크마다 자동 평가, log_history에서 결과 확인

Pipeline과 달리 각 단계를 직접 구성해야 하지만, 모델의 학습 방식(파인튜닝, 동결 등)을 자유롭게 제어할 수 있다.

'[데이터분석] 부트캠프 TIL' 카테고리의 다른 글

20260318 TIL  (0) 2026.03.18
20260317 TIL  (0) 2026.03.17
20260313 TIL  (0) 2026.03.13
20260312 TIL  (0) 2026.03.12
20260311 TIL  (0) 2026.03.11