./results/에 저장 → 저장해두면 나중에 가져와서 재활용도 가능from transformers import AutoModelForSequenceClassification, AutoTokenizer, TrainingArguments, Trainer
# 토큰화 과정에서 패딩, 배치 처리할 거라 DataCollator 생략
import torch
import numpy as np
# 분류를 위해 사용할 모델 huggingface 경로
checkpoint = "klue/roberta-base"
# 토큰화 도구 생성
tokenizer = AutoTokenizer.from_pretrained(
checkpoint
)
# 모델 불러오기
model = AutoModelForSequenceClassification.from_pretrained(
checkpoint
, num_labels=len(train_dataset.features["label"].names)
)
# 토큰화(title)
def tokenizer_function (example):
return tokenizer(
example["title"]
, padding="max_length"
, truncation=True
# , max_length=128 → title이라 그렇게 길지 않을 것으로 추정되므로 따로 최대 길이 지정 X
)
train_dataset = train_dataset.map(tokenizer_function, batched=True, batch_size=1000)
valid_dataset = valid_dataset.map(tokenizer_function, batched=True, batch_size=1000)
test_dataset = test_dataset.map(tokenizer_function, batched=True, batch_size=1000)
print(train_dataset[0])
{
'title': 'SKT AR동물원 개장'
, 'label': 0
, 'label_str': 'IT과학'
, 'input_ids': [0, 19963, 13122, 27589, 2252, 8886, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
, 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
, 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
}
추가: 허깅페이스 map() function
# 평가 함수 정의
# 모델의 출력 logits을 통해 예측값 확인 후 실제 레이블과 비교하여 정확도 계산
def compute_metrics (eval_pred):
# eval_pred: HuggingFace에서 Trainer 객체를 사용할 때 자동으로 전달되는 값. 튜플 형태 → (logits, labels)
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1) # 모델의 출력값 중 최댓값의 인덱스를 출력
return {"accuracy": (predictions == labels).mean()} # 정확도 계산: predictions == labels → True(1), False(0) → 합의 평균
[batch_size, num_labels]logits = np.array([
[1.2, 0.3, 2.5], # 샘플 1에 대한 logit
0.1, 3.2, 0.8], # 샘플 2에 대한 logit
...
])
✅ Huggingface TrainingArguments 설정 요약
| 매개변수 | 설명 |
|---|---|
output_dir="./results" | 학습 결과 저장 디렉토리 (모델, 로그, 체크포인트 등 저장) |
num_train_epochs=10 | 전체 학습 데이터 반복 횟수 (Epoch 수) |
per_device_train_batch_size=4 | 각 디바이스(GPU/CPU)별 배치 크기 |
gradient_accumulation_steps=1 | 그래디언트 누적 스텝 수 (메모리 절약용) |
optim="paged_adamw_32bit" | 32비트 정밀도 사용 AdamW 옵티마이저 변형 |
save_steps=25 | 몇 스텝마다 모델을 저장할지 설정 |
logging_steps=25 | 몇 스텝마다 로그를 기록할지 설정 |
learning_rate=2e-4 | 학습률 설정 |
weight_decay=0.001 | 가중치 감소 계수 (정규화 효과로 과적합 방지) |
fp16=False | 16비트 부동소수점 정밀도 사용 여부 |
bf16=False | BF16 정밀도 사용 여부 (Brain Floating Point) |
max_grad_norm=0.3 | 그래디언트 최대 노름(norm) 값 설정 (폭발 방지) |
max_steps=-1 | 전체 학습 스텝 수 (-1은 전체 epoch만큼 학습) |
warmup_ratio=0.03 | 학습률 워밍업 비율 (초기 안정적 학습 유도) |
group_by_length=True | 입력 시퀀스 길이에 따라 배치 그룹화 (패딩 최적화) |
lr_scheduler_type="constant" | 학습률 스케줄러 유형 (constant는 고정) |
report_to="tensorboard" | TensorBoard에 로그 기록 여부 설정 |
# 모델 하이퍼파라미터 설정
training_args = TrainingArguments(
output_dir="./results/roberta-base-klue-ynat-classification"
, num_train_epochs=1
, per_device_train_batch_size=8
, per_device_eval_batch_size=8
, learning_rate=5e-5
, push_to_hub=False
)
# 학습 객체(트레이너) 생성
trainer = Trainer(
model=model
, args=training_args
, train_dataset=train_dataset
, eval_dataset=valid_dataset
, tokenizer=tokenizer
, compute_metrics=compute_metrics
)
# 모델 학습
trainer.train()

핵심 개념:
업스트림 태스크와 다운스트림 태스크 차이
파인 튜닝 vs 사전 학습(Pre-training / From scratch)
→ 파인 튜닝의 반대, 대비되는 개념은 “모델을 처음부터(제로 베이스) 학습시키는 것” 또는 “사전 학습(Pre-training, Pretrain)” (“From scratch(처음부터)”라는 표현도 자주 사용)
로짓 값과 정확도 계산 원리
평가 함수 구현 방법
핵심 단어:
프리트레인 / 다운스트림 / 로짓 / 토크나이저 / 트레이닝아규먼트
- Point:
- LLM의 업스트림, 다운스트림 개념
- 사전 학습 vs. 파인 튜닝
- 로짓 값을 이용한 정확도 평가 함수 구현
파인튜닝 관련 1
파인 튜닝(Fine-tuning)과 대비되는 가장 일반적인 용어는 사전 학습(Pre-training)
파인 튜닝은 이미 대규모 데이터로 사전 학습된(Pre-trained) 모델을, 특정 작업이나 도메인에 맞춰 추가로 미세하게 학습(fine-tune)시키는 과정
사전 학습(Pre-training): 매우 방대한 범용 데이터를 사용해서 모델이 기초적인 언어나 지식 능력을 습득하게 하는 첫 단계이고 파인 튜닝(Fine-tuning)은 그 사전 학습된 모델을 목적에 맞는 소량의 특화 데이터로 추가 학습시켜, 특정 응용(예: 법률, 의료 등)에 맞게 최적화하는 단계파인튜닝 관련 2: 'PLM을 이용한 성능 향상 방법'
Feature-based Approach는 Fine-tuning과 대비되는 대표적 전이학습(transfer learning) 방식 중 하나
Feature-based Approach(피처 기반 방식)는 사전학습(pre-trained) 모델의 파라미터는 고정(업데이트하지 않음)하고, 사전학습 모델이 출력한 임베딩(특징 벡터)을 새로운 다운스트림 과제에 맞는 레이어(예: 분류기)에 입력하여 그 레이어만 따로 학습시키는 방식
Fine-tuning과는 반대로, fine-tuning은 사전학습 모델 전체(혹은 대부분)의 파라미터를 다운스트림 과제 데이터로 다시 훈련시키는 방식
Feature-based Approach는 특히 데이터가 적거나, 사전학습된 모델이 이미 충분히 일반적일 때 자주 사용되고, 연산·메모리 효율성이 높음
- 즉,
- 사전학습 모델로부터 feature(임베딩)를 추출
- 그 feature를 입력으로 사용해 별도의 분류기(예: 로지스틱 회귀, SVM 등)만 훈련
- 사전학습 모델 자체는 변경(업데이트)되지 않음
- 정리:
- Fine-tuning: 임베딩 포함 전체(혹은 일부) 모델 파라미터를 모두 업데이트
- Feature-based: 사전학습 모델은 고정, feature만 추출해서 그 위에 얹은 새로운 레이어만 학습
trainer.evaluate(test_dataset)
{'eval_loss': 0.4929060637950897,
'eval_accuracy': 0.843,
'eval_runtime': 26.7698,
'eval_samples_per_second': 37.356,
'eval_steps_per_second': 4.669,
'epoch': 1.0}
eval_runtime은 평가에 소요된 시간(초 단위)# token 파일로 저장해두기: open 사용해 불러 사용할 수 있게 됨
# 폴더 생성 → api_key 저장
import os # 파일, 폴더 관리 시스템
if not os.path.exists("./key"):
os.mkdir('./key')
# 나의 api_key 등록
api_key = "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"
with open("./key/huggingface_api_key", 'w') as f:
f.write(api_key)
# 허깅페이스 로그인
from huggingface_hub import login
# 파일 형태의 api_key 불러오기
with open("./key/huggingface_api_key", 'r') as f:
api_key = f.read().strip()
login(token=api_key)
# 허깅페이스에 업로드
# repo_id(report id) == 이름: 사용모델, 데이터, task 기록
repo_id = f"be2be2/roberta-base-klue-ynat-classification"
trainer.save_model(repo_id)
model.save_pretrained(repo_id)
tokenizer.save_pretrained(repo_id)
trainer.push_to_hub(repo_id)
trainer.save_model(repo_id)tokenizer.save_pretrained(repo_id)model.save_pretrained(repo_id)trainer.push_to_hub(repo_id)# 업로드한 나의 모델 불러와서 사용하기
from transformers import pipeline
checkpoint_mymodel = "be2be2/roberta-base-klue-ynat-classification"
my_model = pipeline(task="text-classification", model=checkpoint_mymodel)
Device set to use cuda:0
# 테스트 데이터
test_dataset["title"][:5]
['게시판 우리금융 고종황제 묘소 참배로 새해 첫날 맞이',
'보해양조·한창제지에 시황변동 조회공시 요구',
'프로농구 개막 첫날부터 NBA 출신 맞대결…티그 vs 그레이',
'제30대 한국방송작가협회 이사장에 임기홍 작가',
'게시판 서울새활용플라자 어린이 위한 환경학습 키트']
result = my_model(test_dataset["title"][:5])
# 'IT과학', '경제', '사회', '생활문화', '세계', '스포츠', '정치'
def make_str_label(batch):
if batch['label'] == "LABEL_0":
batch['label'] = 'IT과학'
elif batch['label'] == "LABEL_1":
batch['label'] = '경제'
elif batch['label'] == "LABEL_2":
batch['label'] = '사회'
elif batch['label'] == "LABEL_3":
batch['label'] = '생활문화'
elif batch['label'] == "LABEL_4":
batch['label'] = '세계'
elif batch['label'] == "LABEL_5":
batch['label'] = '스포츠'
elif batch['label'] == "LABEL_6":
batch['label'] = '정치'
return batch
# 레이블을 실제 값으로 변환
str_result = [make_str_label(title) for title in result]
str_result
[{'label': '경제', 'score': 0.5693342685699463},
{'label': '경제', 'score': 0.9462602138519287},
{'label': '스포츠', 'score': 0.9923964142799377},
{'label': '사회', 'score': 0.7058424353599548},
{'label': '사회', 'score': 0.9218515157699585}]
# 뉴스 페이지에서 뉴스 제목 1개 가져와서 예측해보기
news_title = "SKT, 최신 GPU 클러스터 ‘해인’ 가동…엔비디아 B200 1000장 담았다"
# 예측
my_result = my_model(news_title)
my_result_str = [make_str_label(title) for title in my_result]
my_result_str
[{'label': 'IT과학', 'score': 0.9689147472381592}]
news_title = [
"세금 폭주하는 트럼프…'美 특허제도 개편시 韓기업 수수료 9.9배↑'"
, "[속보]하루 새 연인·지인 잇단 살해 혐의 50대 남성, 마창대교서 떨어져 숨져"
, '진성준 "아들 부동산 사준 적 없다"…김근식 "쿨하게 사과"'
, "조광래 대표 결국 사의 표명…이례적인 시즌 도중 ‘혁신안’, 대구를 바꿀 수 있나"
, '주영달 총감독 작심 쓴소리, “유리한 상황되면 급해져, 무한 반복 악순환”'
]
# 예측
my_result = my_model(news_title)
my_result_str = [make_str_label(title) for title in my_result]
my_result_str
[{'label': '세계', 'score': 0.9829148054122925},
{'label': '사회', 'score': 0.9756367802619934},
{'label': '정치', 'score': 0.7036828994750977},
{'label': '스포츠', 'score': 0.9919844269752502},
{'label': '사회', 'score': 0.7948532104492188}]
→ 마지막 뉴스 기사는 사실 '스포츠'임
핵심 개념:
허깅페이스에 모델 업로드 시 모델, 토크나이저, 사전 학습 정보 업로드 후 push_to_hub()
핵심 단어:
API key(token) / 모델 업로드 / 실제 뉴스 타이틀로 예측
- Point:
- 모델 학습 후 loss, accuracy 평가
- 허깅페이스에 모델 업로드
- API-key(token) 파일로 저장해 사용하기
- os 라이브러리를 이용한 폴더 관리
- 허깅페이스 로그인
- 저장한 api key 이용하는 법
- 허깅페이스에서 내가 업로드한 모델 불러오기
| 약어 | 의미 |
|---|---|
| B- | Begin: 개체의 시작 단어 |
| I- | Inside: 개체의 내부(연속) 단어 |
| O | Outside: 개체가 아님 |
| 약어 | 풀네임 (Full Name) | 의미 설명 |
|---|---|---|
| DT | Date | 날짜 (예: 2023년 8월 5일) |
| LC | Location | 위치 / 장소명 (예: 서울, 강릉) |
| OG | Organization | 조직 / 기관 (예: 삼성전자, 경찰청) |
| PS | Person | 사람 이름 (예: 이순신, 김철수) |
| QT | Quantity | 수량 / 수치 (예: 5개, 100명, 30%) |
| TI | Time | 시간 (예: 오전 9시, 오후 3시) |
| LV | Law (Legal Document) | 법률 / 조항명 (예: 민법 제1조, 정보보호법) |
개체 종류를 보면 해당 데이터는 뉴스 정보에 특화되어 있음을 알 수 있음
→ 의료 용어 데이터 분석을 원할 경우 다른 데이터셋을 사용해야 함!
→ 도메인 또는 목적에 특화되도록 개체명 인식을 정확하게 하는 방법 중 하나는 기존에 공개된 개체명 인식기를 사용하는 것이 아니라, 직접 목적에 맞는 데이터를 준비하여 모델을 만드는 것
✅ 예시
| 토큰 | 태그 |
|---|---|
| 삼성전자 | B-OG |
| 는 | O |
| 2021년 | B-DT |
| 3월 | I-DT |
| 15일에 | I-DT |
| 새로운 | O |
| 스마트폰을 | O |
| 출시했다 | O |
| . | O |
from datasets import load_dataset
klue_ner = load_dataset("klue", "ner")
# 일부 데이터만 사용
klue_ner["train"] = klue_ner["train"].select(range(250))
klue_ner["validation"] = klue_ner["validation"].select(range(50))
klue_ner["train"]
Dataset({
features: ['sentence', 'tokens', 'ner_tags'],
num_rows: 250
})
print(klue_ner["train"][0])
{
'sentence': '특히 <영동고속도로:LC> <강릉:LC> 방향 <문막휴게소:LC>에서 <만종분기점:LC>까지 <5㎞:QT> 구간에는 승용차 전용 임시 갓길차로제를 운영하기로 했다.'
, 'tokens': ['특', '히', ' ', '영', '동', '고', '속', '도', '로', ' ', '강', '릉', ' ', '방', '향', ' ', '문', '막', '휴', '게', '소', '에', '서', ' ', '만', '종', '분', '기', '점', '까', '지', ' ', '5', '㎞', ' ', '구', '간', '에', '는', ' ', '승', '용', '차', ' ', '전', '용', ' ', '임', '시', ' ', '갓', '길', '차', '로', '제', '를', ' ', '운', '영', '하', '기', '로', ' ', '했', '다', '.']
, 'ner_tags': [12, 12, 12, 2, 3, 3, 3, 3, 3, 12, 2, 3, 12, 12, 12, 12, 2, 3, 3, 3, 3, 12, 12, 12, 2, 3, 3, 3, 3, 12, 12, 12, 8, 9, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12]
}
klue_ner['train'].features['ner_tags'].feature.names
['B-DT',
'I-DT',
'B-LC',
'I-LC',
'B-OG',
'I-OG',
'B-PS',
'I-PS',
'B-QT',
'I-QT',
'B-TI',
'I-TI',
'O']
| 숫자 | BIO 태그 | 설명 |
|---|---|---|
| 0 | B-DT | 날짜 시작 |
| 1 | I-DT | 날짜 내부 |
| 2 | B-LC | 위치 시작 |
| 3 | I-LC | 위치 내부 |
| 4 | B-OG | 조직 시작 |
| 5 | I-OG | 조직 내부 |
| 6 | B-PS | 사람 시작 |
| 7 | I-PS | 사람 내부 |
| 8 | B-QT | 수량 시작 |
| 9 | I-QT | 수량 내부 |
| 10 | B-TI | 시간 시작 |
| 11 | I-TI | 시간 내부 |
| 12 | O | 개체 아님 |
| 13 | B-LV | 법률 시작 |
| 14 | I-LV | 법률 내부 |
# 각 정수형 NER 라벨과 실제 이름 매핑하여 확인
label_list = klue_ner['train'].features['ner_tags'].feature.names
mapping_tags = [
"날짜 시작 (Date)"
, "날짜 내부"
, "장소명 시작 (Location)"
, "장소명 내부"
, "조직명 시작 (Organization)"
, "조직명 내부"
, "인명 시작 (Person)"
, "인명 내부"
, "수량 시작 (Quantity)"
, "수량 내부"
, "시간 시작 (Time)"
, "시간 내부"
, "개체명 아님"
]
for idx, label in enumerate(label_list):
print(f"{idx}: {label} → {mapping_tags[idx]}")
0: B-DT → 날짜 시작 (Date)
1: I-DT → 날짜 내부
2: B-LC → 장소명 시작 (Location)
3: I-LC → 장소명 내부
4: B-OG → 조직명 시작 (Organization)
5: I-OG → 조직명 내부
6: B-PS → 인명 시작 (Person)
7: I-PS → 인명 내부
8: B-QT → 수량 시작 (Quantity)
9: I-QT → 수량 내부
10: B-TI → 시간 시작 (Time)
11: I-TI → 시간 내부
12: O → 개체명 아님
{
'tokens': ['서', '울', '중', '앙', '지', '검', '은', '홍', '길', '동', '을', '기', '소', '했','다', '.']
, 'ner_tags': [2, 3, 4, 5, 5, 5, 12, 6, 7, 7, 12, 12, 12, 12, 12]
}
| 토큰 | 서 | 울 | 중 | 앙 | 지 | 검 | 은 | 홍 | 길 | 동 | 을 | 기 | 소 | 했 | 다 | . | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 태그 | 2 | 3 | 4 | 5 | 5 | 5 | 12 | 6 | 7 | 7 | 12 | 12 | 12 | 12 | 12 | 12 | |
| 해석 | B-LC | I-LC | B-OG | I-OG | I-OG | I-OG | 객체x | B-PS | I-PS | I-PS | 객체x | 객체x | 객체x | 객체x | 객체x | 객체x |
[0, 1, 12, 3, 3, 12, 12, ...]# NER Task를 위하여 필수!
label2id = {label: str(idx) for idx, label in enumerate(label_list)}
id2label = {str(idx): label for idx, label in enumerate(label_list)}
print(label2id)
print(id2label)
{'B-DT': '0', 'I-DT': '1', 'B-LC': '2', 'I-LC': '3', 'B-OG': '4', 'I-OG': '5', 'B-PS': '6', 'I-PS': '7', 'B-QT': '8', 'I-QT': '9', 'B-TI': '10', 'I-TI': '11', 'O': '12'}
{'0': 'B-DT', '1': 'I-DT', '2': 'B-LC', '3': 'I-LC', '4': 'B-OG', '5': 'I-OG', '6': 'B-PS', '7': 'I-PS', '8': 'B-QT', '9': 'I-QT', '10': 'B-TI', '11': 'I-TI', '12': 'O'}
from transformers import AutoTokenizer
checkpoint = "monologg/koelectra-base-v3-discriminator"
# 토크나이저 객체 불러오기
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
예시
'huggingface', 'is', 'cool''hugging', '##face', 'is', 'cool'##의 의미: 앞 토큰에 붙는 조각 단어임을 의미["HuggingFace", "is", "cool"]["Hugging", "##Face", "is", "cool"][0, 0, 1, 2] → 각 서브워드가 원래 단어 내에서 몇 번째 단어인지 인덱스로 관리장점
핵심 개념:
개체명 인식은 문장에서 특정 개체를 토큰 단위로 식별하는 과정
BIO 태그 체계로 레이블링 필수
서브워드는 단어를 쪼개어 처리 → 희귀 단어와 신조어 대응에 효과적
핵심 단어:
NER, BIO Tag, KLUE-NER dataset, label mapping, KoELECTRA, Subword, tokenizer, word_ids
- Point:
- 개체명 인식(NER)
- KLUE-NER
- 레이블과 ID 간 매핑 작업은 모델 학습 및 결과 해석에 필수적
- 서브워드: 단어보다 작은 단위로 분해
- 서브워드 기반 토크나이저: 최신 자연어 처리 모델에서 필수적으로 사용됨
- 패딩, 특수 토큰은 학습에서 제외됨








학습 동기 부여를 위한 배지 시스템

자격증 일람 ★★★

















