🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

개체명 인식 NER 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 2. · 18 Views

개체명 인식 NER 완벽 가이드

텍스트에서 사람, 장소, 조직 등을 자동으로 찾아내는 개체명 인식(NER) 기술을 초급 개발자도 이해할 수 있도록 실무 예제와 함께 설명합니다. BIO 태깅부터 한국어 NER 모델 학습, spaCy 연동까지 단계별로 안내합니다.


목차

  1. NER_태스크_정의
  2. BIO_태깅_체계
  3. 토큰_분류_모델_구조
  4. 한국어_NER_데이터셋
  5. Fine-tuning_실습
  6. spaCy와_연동

1. NER 태스크 정의

어느 날 김개발 씨는 뉴스 기사 데이터를 분석하는 업무를 맡게 되었습니다. 수천 개의 기사에서 언급된 인물, 기업, 지역 정보를 일일이 추출해야 하는데, 사람이 직접 하기엔 며칠이 걸릴 것 같았습니다.

"이걸 자동으로 해주는 기술이 없을까요?" 선배에게 물어보니 돌아온 대답은 "NER을 써보세요"였습니다.

**개체명 인식(Named Entity Recognition, NER)**은 텍스트에서 의미 있는 정보 단위를 자동으로 찾아 분류하는 자연어 처리 기술입니다. 마치 형광펜으로 중요한 단어에 색칠하듯, 문장 속에서 사람 이름, 조직명, 위치, 날짜 등을 식별합니다.

이 기술을 익히면 대량의 텍스트 데이터에서 핵심 정보를 빠르게 추출할 수 있습니다.

다음 코드를 살펴봅시다.

# NER 태스크의 기본 구조를 이해하는 예제
from transformers import pipeline

# 사전 학습된 NER 파이프라인 로드
ner_pipeline = pipeline("ner", model="bert-base-multilingual-cased")

# 분석할 텍스트
text = "삼성전자의 이재용 회장이 서울에서 기자회견을 열었다."

# NER 수행 - 각 토큰에 개체 유형 태그 부여
entities = ner_pipeline(text)

# 결과 출력
for entity in entities:
    print(f"단어: {entity['word']}, 유형: {entity['entity']}, 점수: {entity['score']:.3f}")

김개발 씨는 입사 6개월 차 데이터 엔지니어입니다. 이번 주 과제는 지난 1년간의 경제 뉴스에서 언급된 모든 기업명과 인물명을 추출하는 것이었습니다.

기사 수만 해도 5만 건이 넘었습니다. 처음에는 정규표현식으로 해결하려 했습니다.

"주식회사"나 "대표"라는 키워드 앞뒤의 단어를 추출하면 되지 않을까 생각했죠. 하지만 금세 한계에 부딪혔습니다.

"네이버가 카카오와 협력한다"라는 문장에서 "네이버"와 "카카오"가 기업명이라는 것을 정규표현식만으로는 알아낼 수 없었습니다. 선배 박시니어 씨가 다가와 말했습니다.

"그런 작업에는 NER을 써야 해요. Named Entity Recognition, 우리말로 개체명 인식이라고 하죠." 그렇다면 NER이란 정확히 무엇일까요?

쉽게 비유하자면, NER은 마치 숙련된 기자가 기사를 읽으며 중요한 이름에 형광펜을 치는 것과 같습니다. 기자는 오랜 경험을 통해 "삼성전자"는 회사, "이재용"은 사람, "서울"은 장소라는 것을 즉시 알아챕니다.

NER 모델도 마찬가지로 대량의 텍스트를 학습하여 이런 판단 능력을 갖추게 됩니다. NER이 인식하는 **개체(Entity)**의 종류는 다양합니다.

가장 흔한 것은 PER(인물), ORG(조직), LOC(위치), DATE(날짜) 등입니다. 프로젝트에 따라 제품명, 금액, 비율 같은 도메인 특화 개체를 정의하기도 합니다.

NER이 없던 시절에는 어땠을까요? 분석가들이 직접 문서를 읽으며 엑셀에 정보를 입력해야 했습니다.

시간도 오래 걸리고, 사람마다 기준이 달라 일관성도 떨어졌습니다. 무엇보다 데이터 양이 늘어날수록 비용은 기하급수적으로 증가했습니다.

바로 이런 문제를 해결하기 위해 NER 기술이 발전했습니다. 현대의 NER 모델은 딥러닝, 특히 Transformer 아키텍처를 기반으로 합니다.

BERT, RoBERTa 같은 사전 학습 모델을 NER 태스크에 맞게 미세 조정하면 높은 정확도를 얻을 수 있습니다. 위의 코드를 살펴보겠습니다.

먼저 Hugging Face의 transformers 라이브러리에서 pipeline 함수를 불러옵니다. 이 함수는 복잡한 모델 로딩과 전처리를 한 줄로 해결해줍니다.

ner_pipeline에 텍스트를 넣으면 각 토큰에 대한 개체 유형과 신뢰도 점수가 반환됩니다. 실제 현업에서 NER은 어디에 활용될까요?

금융권에서는 뉴스에서 기업명을 추출해 주가 예측에 활용합니다. 의료 분야에서는 의학 논문에서 약품명과 질병명을 자동으로 태깅합니다.

법률 분야에서는 계약서에서 당사자명과 금액을 추출합니다. 하지만 주의할 점도 있습니다.

NER 모델은 학습 데이터에 없는 새로운 개체를 인식하지 못할 수 있습니다. 예를 들어 신생 스타트업 이름이나 신조어는 제대로 인식되지 않을 수 있습니다.

따라서 도메인에 맞는 추가 학습이 필요한 경우가 많습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

NER에 대한 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "아, 그래서 그냥 키워드 검색으로는 안 되는 거였군요!" 이제 그는 NER을 활용해 5만 건의 뉴스를 몇 시간 만에 분석할 수 있게 되었습니다.

실전 팁

💡 - NER 태스크는 결국 각 토큰을 정해진 카테고리로 분류하는 시퀀스 레이블링 문제입니다

  • 도메인에 따라 인식해야 할 개체 유형이 다르므로, 프로젝트 시작 전 개체 정의를 명확히 하세요

2. BIO 태깅 체계

김개발 씨는 NER 데이터셋을 열어보다가 이상한 표기를 발견했습니다. "B-PER", "I-PER", "O"...

이게 대체 무슨 암호일까요? 선배에게 물어보니 "BIO 태깅을 모르면 NER을 제대로 이해할 수 없어요"라는 대답이 돌아왔습니다.

BIO 태깅은 NER에서 개체의 경계를 표시하는 표준 레이블링 방식입니다. B는 개체의 시작(Beginning), I는 개체의 내부(Inside), O는 개체가 아님(Outside)을 의미합니다.

이 체계 덕분에 여러 단어로 이루어진 개체명도 정확히 인식할 수 있습니다.

다음 코드를 살펴봅시다.

# BIO 태깅 예제 - 문장을 토큰별로 레이블링
sentence = "삼성전자 이재용 회장이 서울에서 발표했다"
tokens = ["삼성전자", "이재용", "회장이", "서울에서", "발표했다"]

# BIO 태그 할당
bio_tags = [
    "B-ORG",    # 삼성전자 - 조직명의 시작
    "B-PER",    # 이재용 - 인물명의 시작
    "O",        # 회장이 - 개체 아님
    "B-LOC",    # 서울에서 - 위치명의 시작
    "O"         # 발표했다 - 개체 아님
]

# 토큰과 태그를 함께 출력
for token, tag in zip(tokens, bio_tags):
    print(f"{token:12} -> {tag}")

김개발 씨는 NER 모델을 학습시키기 위해 데이터셋을 준비하고 있었습니다. 그런데 레이블을 어떻게 달아야 할지 막막했습니다.

"삼성전자"는 하나의 토큰이니 그냥 "ORG"라고 태그하면 될 것 같은데, "현대자동차그룹"처럼 여러 토큰으로 쪼개지는 경우는 어떻게 해야 할까요? 박시니어 씨가 화이트보드에 그림을 그리며 설명했습니다.

"NER에서 가장 널리 쓰이는 태깅 방식이 BIO 태깅이에요. Begin, Inside, Outside의 약자죠." BIO 태깅을 도서관에 비유해볼까요?

책장에 책이 꽂혀 있다고 상상해보세요. 어떤 책은 한 권짜리이고, 어떤 책은 전집으로 여러 권이 이어져 있습니다.

전집의 첫 번째 책에는 "시작"이라는 스티커를, 나머지 책에는 "연속"이라는 스티커를 붙입니다. 전집이 아닌 일반 책에는 아무 스티커도 붙이지 않습니다.

BIO 태깅도 마찬가지입니다. 구체적으로 살펴보겠습니다.

B는 개체의 첫 번째 토큰을 의미합니다. "B-PER"이라면 인물명 개체가 여기서 시작된다는 뜻입니다.

I는 개체가 계속된다는 표시입니다. "I-PER"은 앞선 B-PER에 이어지는 인물명의 일부입니다.

O는 어떤 개체에도 속하지 않는 일반 토큰입니다. 예를 들어 "대한민국 서울시 강남구"라는 텍스트를 토큰화하면 어떻게 될까요?

만약 "대한민국"이 하나의 위치 개체라면 "B-LOC"이 됩니다. "서울시"와 "강남구"가 별개의 위치라면 각각 "B-LOC"이 됩니다.

하지만 "서울시 강남구"가 하나의 위치 개체라면 "서울시"는 "B-LOC", "강남구"는 "I-LOC"이 됩니다. BIO 외에도 변형된 태깅 체계가 있습니다.

BIOES 체계는 단일 토큰 개체를 위한 S(Single)와 개체의 마지막을 표시하는 E(End)를 추가합니다. 더 정밀한 경계 표시가 가능하지만, 레이블 수가 늘어나는 단점이 있습니다.

왜 이런 복잡한 체계가 필요할까요? 단순히 개체 유형만 태깅하면 연속된 동일 유형 개체를 구분할 수 없기 때문입니다.

"김철수 이영희가 만났다"라는 문장에서 두 사람 이름 모두 "PER"로만 태깅하면, 이것이 하나의 긴 이름인지 두 개의 이름인지 알 수 없습니다. B 태그가 새로운 개체의 시작을 알려주기 때문에 구분이 가능해집니다.

위의 코드를 다시 살펴보겠습니다. "삼성전자"는 조직명이므로 "B-ORG"입니다.

"이재용"은 인물명이므로 "B-PER"입니다. "회장이"는 직함이지만 보통 개체로 인식하지 않으므로 "O"입니다.

물론 도메인에 따라 직함도 개체로 정의할 수 있습니다. 실무에서 BIO 태깅 작업을 할 때 주의할 점이 있습니다.

토크나이저의 분리 방식에 따라 같은 텍스트도 다르게 토큰화됩니다. BERT의 WordPiece 토크나이저는 "삼성전자"를 "삼성", "##전자"로 분리할 수 있습니다.

이 경우 "삼성"은 "B-ORG", "##전자"는 "I-ORG"가 되어야 합니다. 김개발 씨는 이제 BIO 태깅의 원리를 이해했습니다.

"결국 개체의 경계를 명확히 하기 위한 약속이군요!" 맞습니다. 이 약속 덕분에 모델은 개체가 어디서 시작하고 끝나는지 정확히 학습할 수 있습니다.

실전 팁

💡 - 토크나이저가 단어를 서브워드로 분리하면, 서브워드에도 I 태그를 붙여야 합니다

  • BIOES 체계가 BIO보다 약간 더 좋은 성능을 보이는 경우도 있으니 실험해보세요

3. 토큰 분류 모델 구조

김개발 씨는 NER 모델을 직접 만들어보기로 결심했습니다. 그런데 막상 모델 구조를 보니 복잡해 보였습니다.

"BERT 위에 뭘 얹는다는 건가요?" 토큰 분류 모델의 구조를 이해해야 커스텀 NER 모델을 만들 수 있습니다.

토큰 분류 모델은 사전 학습된 언어 모델 위에 분류 레이어를 추가한 구조입니다. BERT 같은 인코더가 각 토큰의 문맥 정보를 담은 벡터를 출력하면, 분류 헤드가 이를 BIO 태그로 변환합니다.

이 구조를 이해하면 다양한 NER 모델을 설계할 수 있습니다.

다음 코드를 살펴봅시다.

from transformers import BertForTokenClassification, BertTokenizer
import torch

# 레이블 정의 (BIO 태그)
label_list = ["O", "B-PER", "I-PER", "B-ORG", "I-ORG", "B-LOC", "I-LOC"]
num_labels = len(label_list)

# 토큰 분류 모델 로드 - BERT + 분류 헤드
model = BertForTokenClassification.from_pretrained(
    "bert-base-multilingual-cased",
    num_labels=num_labels  # 분류할 태그 수
)

tokenizer = BertTokenizer.from_pretrained("bert-base-multilingual-cased")

# 입력 텍스트 토큰화
text = "김철수가 네이버에 입사했다"
inputs = tokenizer(text, return_tensors="pt", padding=True)

# 모델 추론 - 각 토큰에 대한 로짓 출력
outputs = model(**inputs)
predictions = torch.argmax(outputs.logits, dim=2)

김개발 씨는 Hugging Face에서 제공하는 NER 파이프라인을 잘 사용하고 있었습니다. 하지만 어느 날 특수한 요구사항이 생겼습니다.

법률 문서에서 "계약 당사자", "계약 금액", "계약 기간" 같은 도메인 특화 개체를 인식해야 했습니다. 기존 모델로는 불가능했고, 직접 모델을 학습시켜야 했습니다.

박시니어 씨가 화이트보드에 그림을 그리기 시작했습니다. "NER 모델의 구조는 생각보다 단순해요.

크게 두 부분으로 나뉘죠." 첫 번째 부분은 인코더입니다. BERT, RoBERTa, ELECTRA 같은 사전 학습된 언어 모델이 이 역할을 합니다.

인코더는 각 토큰을 문맥 정보가 담긴 벡터로 변환합니다. 예를 들어 "사과"라는 단어가 "나는 사과를 먹었다"에서는 과일을, "진심으로 사과드립니다"에서는 사죄를 의미한다는 것을 벡터에 담습니다.

쉽게 비유하자면, 인코더는 마치 통역사와 같습니다. 문장 전체의 맥락을 파악해서 각 단어의 진정한 의미를 해석해줍니다.

이 통역 결과가 바로 hidden states 또는 토큰 임베딩이라고 불리는 벡터입니다. 두 번째 부분은 분류 헤드입니다.

인코더가 출력한 벡터를 입력받아 각 토큰의 BIO 태그를 예측합니다. 구조적으로는 단순한 선형 레이어(Linear Layer)입니다.

768차원(BERT 기준)의 벡터를 레이블 개수만큼의 차원으로 변환합니다. 이 구조를 레스토랑에 비유해볼까요?

인코더는 셰프입니다. 재료(토큰)를 받아 맛있는 요리(의미 있는 벡터)로 변환합니다.

분류 헤드는 서빙 직원입니다. 완성된 요리를 보고 어느 테이블(레이블)로 가져갈지 결정합니다.

위의 코드를 살펴보겠습니다. BertForTokenClassification은 Hugging Face에서 제공하는 토큰 분류용 모델 클래스입니다.

num_labels 파라미터로 분류할 레이블 수를 지정합니다. 이 값에 따라 분류 헤드의 출력 차원이 결정됩니다.

모델에 텍스트를 입력하면 outputs.logits가 반환됩니다. 이 로짓은 각 토큰에 대해 모든 레이블의 점수를 담고 있습니다.

torch.argmax로 가장 높은 점수의 레이블 인덱스를 선택하면 예측 결과가 됩니다. 학습 과정에서는 CrossEntropyLoss를 주로 사용합니다.

실제 레이블과 예측 로짓 사이의 차이를 계산하여 모델을 업데이트합니다. 특수 토큰([CLS], [SEP], [PAD])에는 레이블이 없으므로 손실 계산에서 제외해야 합니다.

한 가지 주의할 점이 있습니다. BERT의 WordPiece 토크나이저는 단어를 서브워드로 분리합니다.

"삼성전자"가 "삼성", "##전자"로 분리되면, 원래 단어 하나에 두 개의 예측이 나옵니다. 일반적으로 첫 번째 서브워드의 예측만 사용하거나, 서브워드 예측을 병합하는 후처리가 필요합니다.

최근에는 CRF(Conditional Random Field) 레이어를 추가하는 방식도 많이 사용됩니다. CRF는 레이블 간의 전이 확률을 학습합니다.

예를 들어 "I-PER" 다음에 "I-ORG"가 오는 것은 비정상이므로, 이런 패턴의 확률을 낮춥니다. 김개발 씨는 모델 구조를 이해하고 나서야 비로소 자신만의 NER 모델을 설계할 수 있었습니다.

"인코더는 그대로 두고, 분류 헤드만 바꾸면 되는 거군요!" 정확합니다. 이것이 바로 **전이 학습(Transfer Learning)**의 핵심입니다.

실전 팁

💡 - 서브워드 토큰에 대한 레이블 처리 전략을 학습 전에 명확히 정의하세요

  • CRF 레이어를 추가하면 레이블 시퀀스의 일관성이 높아집니다

4. 한국어 NER 데이터셋

김개발 씨는 한국어 NER 모델을 학습시키려 했지만, 어디서 데이터를 구해야 할지 몰랐습니다. 영어 데이터셋은 많은데 한국어는 찾기 어려웠습니다.

"한국어 NER 데이터셋은 어디서 구하나요?" 선배에게 물었습니다.

한국어 NER 모델 학습에는 양질의 한국어 NER 데이터셋이 필수입니다. 국립국어원, AI Hub, KorNER 등에서 공개 데이터셋을 제공합니다.

각 데이터셋마다 개체 유형과 태깅 기준이 다르므로, 프로젝트 목적에 맞는 데이터셋을 선택해야 합니다.

다음 코드를 살펴봅시다.

from datasets import load_dataset

# Hugging Face에서 한국어 NER 데이터셋 로드
dataset = load_dataset("klue", "ner")

# 데이터셋 구조 확인
print(f"학습 데이터 수: {len(dataset['train'])}")
print(f"검증 데이터 수: {len(dataset['validation'])}")

# 샘플 데이터 확인
sample = dataset['train'][0]
print(f"\n문장: {sample['sentence']}")
print(f"토큰: {sample['tokens']}")
print(f"NER 태그: {sample['ner_tags']}")

# KLUE-NER 레이블 목록
labels = dataset['train'].features['ner_tags'].feature.names
print(f"\n레이블 종류: {labels}")

김개발 씨는 의료 분야 NER 프로젝트를 맡게 되었습니다. 환자 기록에서 질병명, 약품명, 증상 등을 추출해야 했습니다.

처음에는 일반 NER 모델을 적용했지만 결과가 좋지 않았습니다. "일반 개체명과 의료 개체명은 다르군요..." 박시니어 씨가 조언했습니다.

"한국어 NER 데이터셋은 여러 종류가 있어요. 각각의 특성을 알아야 적절한 선택을 할 수 있죠." 가장 널리 사용되는 것은 KLUE-NER 데이터셋입니다.

KLUE(Korean Language Understanding Evaluation)는 네이버, 카카오, 업스테이지 등 여러 기관이 협력하여 만든 한국어 자연어 이해 벤치마크입니다. NER 태스크에는 약 2만 개의 문장이 포함되어 있으며, PS(인물), LC(위치), OG(조직), DT(날짜), TI(시간), QT(수량) 등의 개체 유형을 제공합니다.

AI Hub에서도 다양한 NER 데이터셋을 제공합니다. 특히 도메인별 데이터셋이 풍부합니다.

의료, 법률, 금융 등 특화된 개체명 데이터셋을 찾을 수 있습니다. 다만 데이터 이용 신청 절차가 필요하고, 상업적 이용에 제한이 있을 수 있습니다.

국립국어원 모두의 말뭉치도 좋은 선택입니다. 개체명 분석 말뭉치에는 뉴스, 문어, 구어 등 다양한 장르의 텍스트가 포함되어 있습니다.

개체 유형도 세분화되어 있어서 더 정밀한 분류가 가능합니다. 데이터셋을 선택할 때 고려해야 할 점이 있습니다.

첫째, 개체 유형입니다. 프로젝트에서 인식해야 할 개체와 데이터셋의 개체 유형이 일치하는지 확인해야 합니다.

의료 프로젝트에 일반 NER 데이터셋을 사용하면 약품명이나 질병명을 인식하지 못합니다. 둘째, 데이터 품질입니다.

태깅 일관성이 중요합니다. 같은 개체가 어떤 문장에서는 태깅되고 다른 문장에서는 태깅되지 않으면 모델 성능이 떨어집니다.

가능하다면 데이터셋의 일부를 직접 검토해보세요. 셋째, 데이터 양입니다.

딥러닝 모델은 많은 데이터를 필요로 합니다. 하지만 사전 학습 모델을 사용한 전이 학습 덕분에, 수천 개 문장만으로도 괜찮은 성능을 얻을 수 있습니다.

위의 코드는 Hugging Face datasets 라이브러리를 사용해 KLUE-NER을 로드하는 예제입니다. load_dataset 함수 하나로 학습 데이터와 검증 데이터를 모두 가져올 수 있습니다.

각 샘플에는 문장, 토큰 리스트, BIO 태그 리스트가 포함되어 있습니다. 만약 공개 데이터셋으로 부족하다면 직접 데이터를 구축해야 합니다.

**라벨 스튜디오(Label Studio)**나 프로디지(Prodigy) 같은 어노테이션 도구를 사용하면 효율적으로 태깅 작업을 수행할 수 있습니다. 김개발 씨는 결국 AI Hub에서 의료 분야 NER 데이터셋을 신청했습니다.

일반 KLUE-NER로 기본 모델을 학습시킨 후, 의료 데이터로 추가 학습하는 전략을 선택했습니다. "데이터가 핵심이군요!" 맞습니다.

좋은 모델도 좋은 데이터가 있어야 빛을 발합니다.

실전 팁

💡 - 도메인 특화 프로젝트라면 해당 도메인의 데이터셋을 우선 찾아보세요

  • 데이터가 부족하면 일반 NER 모델을 먼저 학습하고, 도메인 데이터로 추가 학습하는 전략이 효과적입니다

5. Fine-tuning 실습

김개발 씨는 드디어 직접 NER 모델을 학습시켜볼 준비가 되었습니다. 데이터셋도 준비했고, 모델 구조도 이해했습니다.

"그럼 이제 실제로 코드를 작성해볼까요?" 키보드에 손을 올리며 혼잣말했습니다.

Fine-tuning은 사전 학습된 모델을 특정 태스크에 맞게 추가 학습시키는 과정입니다. Hugging Face Transformers 라이브러리를 사용하면 몇 줄의 코드로 NER 모델을 학습시킬 수 있습니다.

학습률, 배치 크기, 에폭 수 등 하이퍼파라미터 조정이 성능에 큰 영향을 미칩니다.

다음 코드를 살펴봅시다.

from transformers import (
    AutoModelForTokenClassification,
    AutoTokenizer,
    TrainingArguments,
    Trainer,
    DataCollatorForTokenClassification
)
from datasets import load_dataset

# 데이터셋과 모델 준비
dataset = load_dataset("klue", "ner")
model_name = "klue/bert-base"
tokenizer = AutoTokenizer.from_pretrained(model_name)

# 레이블 매핑
label_list = dataset["train"].features["ner_tags"].feature.names
model = AutoModelForTokenClassification.from_pretrained(
    model_name, num_labels=len(label_list)
)

# 학습 설정
training_args = TrainingArguments(
    output_dir="./ner_model",
    num_train_epochs=3,
    per_device_train_batch_size=16,
    learning_rate=2e-5,
    evaluation_strategy="epoch"
)

김개발 씨는 구글 코랩을 열고 GPU 런타임을 설정했습니다. 선배가 말하길, NER fine-tuning은 그리 오래 걸리지 않지만 GPU가 있으면 훨씬 빠르다고 했습니다.

박시니어 씨가 옆에서 지켜보며 설명했습니다. "Fine-tuning의 핵심은 사전 학습된 지식을 최대한 활용하는 거예요.

BERT 같은 모델은 이미 언어의 문법과 의미를 잘 알고 있으니까, 우리는 NER이라는 특정 작업만 가르치면 됩니다." 이것은 마치 이미 한국어를 잘 하는 사람에게 법률 용어를 가르치는 것과 같습니다. 처음부터 한국어를 배우는 것보다 훨씬 빠르죠.

사전 학습 모델은 이미 단어의 의미, 문장 구조, 문맥 파악 능력을 갖추고 있습니다. 코드를 단계별로 살펴보겠습니다.

먼저 필요한 라이브러리를 불러옵니다. AutoModelForTokenClassification은 토큰 분류용 모델을 자동으로 로드해주는 클래스입니다.

모델 이름만 지정하면 적절한 모델 구조를 선택합니다. 다음으로 데이터셋과 토크나이저를 준비합니다.

중요한 점은 모델과 토크나이저가 동일한 기반이어야 한다는 것입니다. "klue/bert-base" 모델에는 같은 모델의 토크나이저를 사용해야 합니다.

실제로 학습을 돌리려면 추가 코드가 필요합니다. 토큰화된 데이터를 모델에 맞게 전처리하는 함수, 평가 메트릭을 계산하는 함수 등을 작성해야 합니다.

특히 서브워드 토큰과 레이블을 맞추는 작업이 까다롭습니다. TrainingArguments에서 설정하는 하이퍼파라미터를 살펴보겠습니다.

num_train_epochs는 전체 데이터셋을 몇 번 반복할지 결정합니다. NER의 경우 보통 3~5 에폭이면 충분합니다.

per_device_train_batch_size는 한 번에 처리할 샘플 수입니다. GPU 메모리에 맞게 조절해야 합니다.

learning_rate는 매우 중요한 파라미터입니다. Fine-tuning에서는 보통 2e-5에서 5e-5 사이의 작은 값을 사용합니다.

너무 크면 사전 학습된 지식이 망가지고, 너무 작으면 학습이 느립니다. 학습이 끝나면 모델을 평가해야 합니다.

NER에서 자주 사용되는 평가 지표는 precision, recall, F1 score입니다. 특히 seqeval 라이브러리를 사용하면 개체 단위로 정확한 평가가 가능합니다.

흔히 하는 실수 중 하나는 토큰 단위와 개체 단위 평가를 혼동하는 것입니다. 토큰 단위로는 정확도가 높아도, 개체 경계를 잘못 잡으면 실제 성능은 낮을 수 있습니다.

항상 개체 단위 F1 score를 확인하세요. 학습된 모델은 model.save_pretrained()로 저장할 수 있습니다.

나중에 from_pretrained()로 불러와서 바로 사용할 수 있습니다. 모델과 함께 토크나이저도 저장하는 것을 잊지 마세요.

김개발 씨는 첫 번째 학습을 마치고 결과를 확인했습니다. F1 score 0.82.

나쁘지 않은 시작이었습니다. "하이퍼파라미터를 더 조정해보면 성능이 올라갈 것 같아요!" 박시니어 씨가 고개를 끄덕였습니다.

실전 팁

💡 - 학습률은 2e-5에서 시작해서 조금씩 조정해보세요

  • 서브워드 토큰의 레이블 처리를 위한 전처리 함수를 꼼꼼히 작성해야 합니다

6. spaCy와 연동

김개발 씨의 NER 모델이 드디어 완성되었습니다. 이제 이 모델을 실제 서비스에 적용해야 하는데, 선배가 말합니다.

"프로덕션에서는 spaCy를 많이 써요. spaCy와 연동해두면 나중에 편해요."

spaCy는 산업용 자연어 처리 라이브러리로, 빠른 속도와 쉬운 사용성으로 유명합니다. Hugging Face 모델을 spaCy 파이프라인에 통합하면 토큰화, 품사 태깅, 의존 구문 분석과 함께 NER을 일관된 인터페이스로 사용할 수 있습니다.

다음 코드를 살펴봅시다.

import spacy
from spacy.tokens import DocBin

# 한국어 spaCy 모델 로드 (사전 설치 필요: python -m spacy download ko_core_news_sm)
nlp = spacy.load("ko_core_news_sm")

# 텍스트 분석
text = "삼성전자의 이재용 회장이 서울 강남구에서 기자회견을 열었다."
doc = nlp(text)

# 인식된 개체 출력
print("인식된 개체:")
for ent in doc.ents:
    print(f"  텍스트: {ent.text:15} 레이블: {ent.label_:10} 위치: {ent.start_char}-{ent.end_char}")

# 커스텀 개체 규칙 추가
from spacy.pipeline import EntityRuler
ruler = nlp.add_pipe("entity_ruler", before="ner")
patterns = [{"label": "PRODUCT", "pattern": "갤럭시 S24"}]
ruler.add_patterns(patterns)

김개발 씨는 학습시킨 NER 모델을 웹 서비스에 배포해야 했습니다. 단순히 모델만 돌리면 될 줄 알았는데, 생각보다 복잡한 문제들이 있었습니다.

토큰화는 어떻게 할지, 다른 NLP 기능과 어떻게 연동할지, 속도는 어떻게 최적화할지... 박시니어 씨가 말했습니다.

"그럴 때 spaCy를 쓰면 돼요. NLP 파이프라인을 깔끔하게 구성할 수 있거든요." spaCy는 마치 요리사의 조리 도구 세트와 같습니다.

칼, 도마, 냄비가 각각 따로 놓여있으면 불편하지만, 잘 정리된 세트로 있으면 요리가 수월해집니다. spaCy는 토큰화, 품사 태깅, 개체명 인식, 의존 구문 분석 등을 하나의 파이프라인으로 묶어줍니다.

spaCy의 핵심 개념은 Doc 객체입니다. 텍스트를 nlp() 함수에 넣으면 Doc 객체가 반환됩니다.

이 객체에는 분석된 모든 정보가 담겨 있습니다. doc.ents로 개체명을, doc.sents로 문장을, 각 토큰의 품사와 의존 관계도 조회할 수 있습니다.

위 코드를 살펴보겠습니다. 먼저 ko_core_news_sm 모델을 로드합니다.

이것은 spaCy에서 제공하는 한국어 기본 모델입니다. 크기가 작아서 빠르지만, 정확도는 다소 떨어질 수 있습니다.

더 나은 성능이 필요하면 ko_core_news_lg를 사용하세요. doc.ents를 순회하면 인식된 모든 개체를 확인할 수 있습니다.

각 개체는 텍스트(ent.text), 레이블(ent.label_), 문자 위치(ent.start_char, ent.end_char) 정보를 가지고 있습니다. spaCy의 강력한 기능 중 하나는 EntityRuler입니다.

규칙 기반으로 개체를 추가할 수 있습니다. 예를 들어 회사의 제품명 리스트가 있다면, 이를 패턴으로 등록해서 항상 인식되도록 할 수 있습니다.

머신러닝 모델과 규칙 기반 방식을 함께 사용하면 더 좋은 결과를 얻을 수 있습니다. Hugging Face Transformers 모델을 spaCy에 통합하려면 spacy-transformers 라이브러리를 사용합니다.

설치 후 설정 파일을 작성하면 BERT 기반 NER 모델을 spaCy 파이프라인의 일부로 사용할 수 있습니다. 프로덕션 환경에서 spaCy를 사용할 때는 몇 가지 최적화 팁이 있습니다.

첫째, 필요없는 파이프라인 컴포넌트는 비활성화하세요. NER만 필요하다면 nlp.disable_pipes("parser", "tagger")로 다른 컴포넌트를 끌 수 있습니다.

속도가 크게 향상됩니다. 둘째, 대량의 텍스트를 처리할 때는 nlp.pipe()를 사용하세요.

배치 처리로 효율이 높아집니다. 셋째, GPU를 활용하려면 spacy-cupy를 설치하면 됩니다.

시각화도 쉽습니다. displacy 모듈을 사용하면 개체명이 하이라이트된 HTML을 생성할 수 있습니다.

분석 결과를 보고서나 웹 페이지에 바로 삽입할 수 있어 편리합니다. 김개발 씨는 spaCy를 도입한 후 개발 속도가 훨씬 빨라졌습니다.

"토큰화부터 NER까지 한 번에 처리되니까 코드가 훨씬 깔끔해졌어요!" 박시니어 씨가 웃으며 말했습니다. "그게 spaCy의 매력이죠."

실전 팁

💡 - 필요없는 파이프라인 컴포넌트는 비활성화하여 속도를 높이세요

  • EntityRuler로 도메인 특화 개체를 규칙 기반으로 추가하면 정확도가 올라갑니다

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Python#NER#Transformers#spaCy#자연어처리#NER,NLP,Sequence Labeling

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.