ML,DL

[파이토치 트랜스포머를 활용한 자연어 처리와 컴퓨터비전 심층학습] chapter5 토큰화

yeeunnnn 2026. 3. 14. 22:20

🚨해당 게시글에 포함된 이미지 중 출처가 쓰여있지 않은 이미지는 모두 직접 그렸습니다.

5.0. 서론

  • 자연어(Natural Language) : 인공적으로 만들어진 프로그래밍 언어와 다르게 사람들이 쓰는 언어 활동을 위해 만들어진 언어
  • 자연어 처리(Natural Language Processing, NLP) : 컴퓨터가 인간의 언어를 이해하고 해석 및 생성하기 위한 기술

인간 언어의 구조, 의미, 맥락을 분석하고 이해할 수 있는 알고리즘과 모델을 개발하기 위해 해결할 문제

  • 모호성(Ambiguity) : 인간의 언어는 단어와 구와 사용되는 맥락에 따라 여러 의미를 갖게 되어 모호한 경우가 많음
  • 가변성(Variability) : 다양한 사투리, 강세, 신조어, 작문 스타일로 인해 매우 가변적
  • 구조(Structure) : 문장이나 구의 의미를 이해할 때 구문을 파악하여 의미를 해석

→ 말뭉치(corpus)를 일정한 단위인 토큰(Token)으로 나누는 과정을 토큰화(Tokenization)

토큰화를 위해 텍스트 문자열을 토큰으로 나누는 알고리즘 또는 소프트웨어인 토크나이저(Tokenizer)를 사용


5.1. 단어 및 글자 토큰화

5.1.1. 단어 토큰화

단어 토큰화(Word Tokenization) : 자연어 처리 분야에서 핵심적인 전처리 작업 중 하나로 텍스트 데이터를 의미 있는 단위인 단어로 분리하는 작업

대부분의 언어는 띄어쓰기나 문장 부호를 활용해 문장을 표현함 → 띄어쓰기, 문장 부호, 대소문자 등의 특정 구분자를 활용해 토큰화가 수행됨

review = "현실과 구분 불가능한 cg. 시각적 즐거움은 최고! 더불어 ost는 더더욱 최고!!"
tokenized = review.split()
print(tokenized)

출력 결과를 확인해보면 ‘최고!’ 와 ‘최고!!’가 느낌표 하나 차이로 다른 의미로 나뉘었다.

이처럼 단어 토큰화는 한국어 접사, 문장 부호, 오타 혹은 띄어쓰기 오류 등에 취약하다.

5.1.2. 글자 토큰화

글자 토큰화(Character Tokenizer) : 띄어쓰기뿐만 아리나 글자 단위로 문장을 나누는 방식

review = "현실과 구분 불가능한 cg. 시각적 즐거움은 최고! 더불어 ost는 더더욱 최고!!"
tokenized = list(review)
print(tokenized)

한글의 경우 하나의 글자는 여러 자음과 모음의 조합으로 이루어져있다. → 자소 단위로 나누어 자소 단위 토큰화를 수행

  • 자모 변환 함수 : 한글 문자열을 유니코드 U+1100~U+11FE 사이의 조합형 한글 자모로 변환하는 함수
    • 글자를 자모 단위로 나눠 인코딩한 뒤 이를 조합해 한글을 표현하는 조합형
    • retval = jamo.h2j(hangul_string)
    • 조합된 글자 자체에 값을 부여해 인코딩하는 완성형
    • retval = jamo.j2hcj(jamo)
  • 자소단위 토큰화
from jamo import h2j, j2hcj

review = "현실과 구분 불가능한 cg. 시각적 즐거움은 최고! 더불어 ost는 더더욱 최고!!"
decomposed = j2hcj(h2j(review))
tokenized = list(decomposed)
print(tokenized)


5.2. 형태소 토큰화

형태소 토큰화(Morpheme Tokenization) : 텍스트를 형태소 단위로 언어의 문법과 구조를 고려해 단어를 분리하고 이를 의미있는 단위로 분류하는 작업

  • 자립 형태소 : 그, 나, 인사
  • 의존 형태소 : -는, -에게, -를, 했-, -다

5.2.1. 형태소 어휘 사전

형태소 어휘 사전(Morpheme Vocabulary) : 자연어 처리에서 사용되는 단어의 집합인 어휘 사전 중에서도 각 단어의 형태소 정보를 포함하는 사전

품사 태깅(POS tagging) : 텍스트 데이터를 형태소 분석하여 각 형태소에 해당하는 품사(Part Of Speech, POS)를 태깅하는 작업

5.2.2. KoNLPy

koNLPy : 한국어 자연어 처리를 위해 개발된 라이브러리로 명사 추출, 형태소 분석, 품사 태깅 등의 기능을 제공

  • okt 토큰화
from konlpy.tag import Okt

okt = Okt()

sentence = "무엇이든 상상할 수 있는 사람은 무엇이든 만들어 낼 수 있다."

nouns = okt.nouns(sentence)
phrases = okt.phrases(sentence)
morphs = okt.morphs(sentence)
pos = okt.pos(sentence)

print("명사 추출 :", nouns)
print("구 추출 :", phrases)
print("형태소 추출 :", morphs)
print("품사 태깅 :", pos)

  • 꼬꼬마 토큰화
from konlpy.tag import Kkma

kkma = Kkma()

sentence = "무엇이든 상상할 수 있는 사람은 무엇이든 만들어 낼 수 있다."

nouns = kkma.nouns(sentence)
sentences = kkma.sentences(sentence)
morphs = kkma.morphs(sentence)
pos = kkma.pos(sentence)

print("명사 추출 :", nouns)
print("구 추출 :", sentences)
print("형태소 추출 :", morphs)
print("품사 태깅 :", pos)

5.2.3. NLTK

NLTK(Natural Language Toolkit) : 토큰화, 형태소 분석, 구문 분석, 개체명 인식, 감성 분석 들과 같은 기능을 제공

  • 영문 토큰화
from nltk import tokenize

sentence = "Those who can imagine anything, can create the impossible."

word_tokens = tokenize.word_tokenize(sentence)
sent_tokens = tokenize.sent_tokenize(sentence)

print(word_tokens)
print(sent_tokens)

  • 영문 품사 태깅
from nltk import tag
from nltk import tokenize

sentence = "Those who can imagine anything, can create the impossible."

word_tokens = tokenize.word_tokenize(sentence)
pos = tag.pos_tag(word_tokens)

print(word_tokens)
print(pos)

5.2.4. spaCy

spaCy : Cython 기반으로 개발된 오픈 소스 라이브러리

  • spaCy 품사 태깅
import spacy

nlp = spacy.load("en_core_web_sm")
sentence = "Those who can imagine anything, can create the impossible."
doc = nlp(sentence)

for token in doc:
    print(f"[{token.pos_:5}-{token.tag_:3}]:{token.text}")

NLTK : 학습 목적으로 자연어 처리에 대한 알고리즘과 예제 제공
spaCy : 효율적인 처리 속도와 높은 정확도 제공 (모델이 더 크고 복잡하며 많은 리소스 요구)

5.3. 하위 단어 토큰화

현대 자연어 처리에서는 신조어의 발생, 오탈자, 축약어 등을 고려해야 하기 때문에 분석할 단어의 양이 많아져 어려움을 겪는다. 이를 해결하기 위한 방법 중 하나로 **하위 단어 토큰화(Subword Tokenization)**가 있다.

하위 단어 토큰화 : 하나의 단어가 빈번하게 사용되는 **하위 단어(Subword)**의 조합으로 나누어 토큰화하는 방법

5.3.1. 바이트 페어 인코딩

바이트 페어 인코딩(Byte Pair Encoding, BPE) : 다이그램 코딩이라고도 하며 하위 단어 토큰화의 한 종류

연속된 글자 쌍이 더 이상 나타나지 않거나 정해진 어휘 사전 크기에 도달할 때까지 조합 탐지와 부호화를 반복 → 자주 등장하는 단어는 하나의 토큰으로 토큰화, 덜 등장하는 단어는 여러 토큰의 조합으로 표현

  • 예를 들어 다음과 같은 빈도 사전과 어휘 사전이 있다고 하자.
    • 빈도 사전 : (’low’, 5), (’lower’, 2), (’newest’, 6), (’widest’, 3)
    • 어휘 사전 : [’low’, ‘lower’, ’newest’, ‘widest’]
    1. 빈도 사전을 바이트 페어 인코딩으로 재구성하기 위해 빈도 사전 내 모든 단어를 글자 단위로 나눈다
    • 빈도 사전 : (’l’, ’o’, ’w’, 5), (’l’, ’o’, ’w’, ’e’, ’r’, 2), (’n’, ’e’, ’w’, ’e’, ’s’, ’t’, 6), (’w’, ’i’, ’d’, ’e’, ’s’, ’t’, 3)
    • 어휘 사전 : [’d’ ,’e’, ’i’, ’l’, ’n’, ’o’, ’r’, ’s’, ’t’, ’w’]
    1. 빈도 사전에서 ‘e’, ’s’ 쌍이 ‘newest’에서 6번, ‘widest’에서 3번 등장해 총 9번으로 가장 많이 등장했으므로 빈도사전에서 ‘es’로 병합하고 어휘 사전에 ‘es’를 추가한다.
    • 빈도 사전 : (’l’, ’o’, ’w’, 5), (’l’, ’o’, ’w’, ’e’, ’r’, 2), (’n’, ’e’, ’w’, ’es’, ’t’, 6), (’w’, ’i’, ’d’, ’es’, ’t’, 3)
    • 어휘 사전 : [’d’, ’e’, ’i, ’l’, ’n’, ’o’, ’r’, ’s’, ’t’, ’w’, ’es’]
    동일한 과정 반복하여 빈도수가 많은 ‘es’와 ‘t’쌍을 ‘est’로 병합하고 ‘est’를 어휘 사전에 추가한다.
    • 빈도 사전 : (’l’, ’o’, ’w’, 5), (’l’, ’o’, ’w’, ’e’, ’r’, 2), (’n’, ’e’, ’w’, ’es’, ’t’, 6), (’w’, ’i’, ’d’, ’es’, ’t’, 3)
    • 어휘 사전 : [’d’, ’e’, ’i, ’l’, ’n’, ’o’, ’r’, ’s’, ’t’, ’w’, ’es’, ‘est’]
    이러한 과정을 10번 반복했다라고 가정한다면,
    • 빈도 사전 : (’l’, ’o’, ’w’, 5), (’l’, ’o’, ’w’, ’e’, ’r’, 2), (’n’, ’e’, ’w’, ’es’, ’t’, 6), (’w’, ’i’, ’d’, ’es’, ’t’, 3)
    • 어휘 사전 : [’d’, ’e’, ’i, ’l’, ’n’, ’o’, ’r’, ’s’, ’t’, ’w’, ’es’, ‘est’, ’lo’, ’low’, ’ne’, ’new’, ’newest’, ’wi’, ’wid’, ’widest’]
  • 센텐스피스
    • 센텐스피스는 구글에서 개발한 오픈소스 하위 단어 토크나이저 라이브러리이며, 바이트 페어 인코딩과 유사한 알고리즘을 사용해 입력 데이터를 토큰화하고 단어 사전을 생성한다.
    • 코포라는 국립국어원이나 AI hub에서 제공하는 말뭉치 데이터를 쉽게 사용할 수 있게 제공하는 오픈소스 라이브러리이다.
  • 토크나이저 모델 학습

1. 청와대 청원 데이터 다운로드

from Korpora import Korpora

corpus = Korpora.load("korean_petitions")
dataset = corpus.train #train을 하는 게 아니라 train set를 불러온 것
petition = dataset[0]

print("청원 시작일 :", petition.begin)
print("청원 종료일 :", petition.end)
print("청원 동의 수 :", petition.num_agree)
print("청원 범주 :", petition.category)
print("청원 제목 :", petition.title)
print("청원 본문 :", petition.text[:30])

2. 학습 데이터 세트 생성

from Korpora import Korpora

corpus = Korpora.load("korean_petitions")
petitions = corpus.get_all_texts()
with open("../datasets/corpus.txt", "w", encoding="utf-8") as f:
    for petition in petitions:
        f.write(petition + "\n")

 

get_all_texts 메서드로 본문 데이터세트를 한 번에 불러올 수 있다.

3. 토크나이저 모델 학습

from sentencepiece import SentencePieceTrainer

SentencePieceTrainer.Train(
    "--input=../datasets/corpus.txt\ #말뭉치 텍스트 파일의 경로
    --model_prefix=petition_bpe\ #모델 파일 이름
    --vocab_size=8000 model_type=bpe" #어휘 사전 크기, 토크나이저 알고리즘
)

토크나이저 모델 학습토크나이저 모델 학습이 완료되면 petition_bpe.model과 petition_bpe.vocab 파일이 생성된다. model 파일은 학습된 토크나이저가, vocab 파일은 어휘 사전이 저장된 파일이다.

4. 바이트 페어 인코딩 토큰화

from sentencepiece import SentencePieceProcessor

tokenizer = SentencePieceProcessor()
tokenizer.load("petition_bpe.model")

sentence = "안녕하세요, 토그나이저가 잘 학습되었군요!"
sentences = ["이렇게 입력값을 리스트로 받아서","쉽게 토크나이저를 사용할 수 있답니다"]

tokenized_sentence = tokenizer.encode_as_pieces(sentence)
tokenized_sentences = tokenizer.encode_as_pieces(sentences)

print("단일 문장 토큰화 : ",tokenized_sentence)
print("다중 문장 토큰화 : ",tokenized_sentences)

ecodeded_sentence=tokenizer.encode_as_ids(sentence)
encoded_sentences=tokenizer.encode_as_ids(sentences)

print("단일 문장 인코딩 : ",ecodeded_sentence)
print("다중 문장 인코딩 : ",encoded_sentences)

decode_ids=tokenizer.decode_ids(ecodeded_sentence)
decode_pieces=tokenizer.decode_ids(encoded_sentences)

print("정수 인코딩에서 문장 변환 : ",decode_ids)
print("하위 단어 토큰에서 문장 변환 : ",decode_pieces)

SentencePieceProcessor 클래스로 토큰화를 수행한다.
tokenizer.load 메서드를 통해 petition_bpe.model 모델을 불러온다.
encode_as_pieces 메서드는 문장을 토큰화, encode_as_ids 메서드는 토큰을 정수로 인코딩
decode_ids 메서드와 decode_pieces 메서드를 통해 문자열 데이터로 변환 가능

5. 어휘 사전을 불러와 딕셔너리 형태로 매핑

from sentencepiece import SentencePieceProcessor

tokenizer = SentencePieceProcessor()
tokenizer.load("petition_bpe.model")

vocab = {idx: tokenizer.id_to_piece(idx) for idx in range(tokenizer.get_piece_size())}
print(list(vocab.items())[:5])
print("vocab size :", len(vocab))

get_piece_size 메서드는 센텐스피스 모델에서 생성된 하위 단어의 개수를 반환

id_to_piece 메서드는 정숫값을 하위 단어로 변환

5.3.2. 워드피스

워드피스(Workpiece) 토크나이저 : 바이트 페어 인코딩 토크나이저와 유사한 방법으로 학습되지만, 빈도 기반이 아닌 확률 기반으로 글자 쌍을 병합

모델이 새로운 하위 단어를 생성할 때 이전 하위 단어와 함께 나타날 확률을 계산하는 방식이다.

  • 각 글자 쌍에 대한 점수$f$ : 빈도를 나타내는 함수, $x$, $y$ : 병합하려는 하위 단어, $f(x,y)$ : $xy$글자쌍의 빈도 → score : $x$와 $y$를 병합하는 것이 적절한지를 판단하기 위한 점수
    • 예를 들어 다음과 같은 빈도 사전과 어휘 사전이 있다고 하자.
      • 빈도 사전 : (’l’, ’o’, ’w’, 5), (’l’, ’o’, ’w’, ’e’, ’r’, 2), (’n’, ’e’, ’w’, ’e’, ’s’, ’t’, 6), (’w’, ’i’, ’d’, ’e’, ’s’, ’t’, 3)
      • 어휘 사전 : [’d’ ,’e’, ’i’, ’l’, ’n’, ’o’, ’r’, ’s’, ’t’, ’w’]
      1. 가장 빈번하게 등장한 쌍은 9번 등장한 ‘e’, ‘s’ → ‘e’는 17번, ’s’는 9번 등장 → 점수 : 0.06 ‘i’와 ‘d’쌍은 3번씩 등장 → 점수 : 0.33 따라서, ‘e’, ‘s’ 쌍 대신 ‘i’와 ’d’쌍을 병합
      • 빈도 사전 : (’l’, ’o’, ’w’, 5), (’l’, ’o’, ’w’, ’e’, ’r’, 2), (’n’, ’e’, ’w’, ’e’, ’s’, ’t’, 6), (’w’, ’id’, ’e’, ’s’, ’t’, 3)
      • 어휘 사전 : [’d’ ,’e’, ’i’, ’l’, ’n’, ’o’, ’r’, ’s’, ’t’, ’w’, ‘id’]
      1. 동일한 과정을 반복
      • 빈도 사전: (’lo’, ’w’, 5), (’lo’, ’w’, ’e’, ’r’, 2), (’n’, ’e’, ’w’, ’e’, ’s’, ’t’, 6), (’w’, ’id’, ’e’, ’s’, ’t’, 3)
      • 어휘 사전: [’d’, ’e’, ’i’, ’i’, ’n’, ’o’, ’r’, ’s’, ’t’, ’w’, ’id’, ’lo’]
      1. 위 과정을 반복해 연속된 글자 쌍이 더 이상 나타나지 않거나 정해진 어휘 사전 크기에 도달할 때까지 학습한다.
  • $$ score={f(x,y)\over{f(x),f(y)}} $$
  • 토크나이저스(Tokenizers) : 토크나이저스 라이브러리는 정규화(Normalization), **사전 토큰화(Pre-tokenization)**를 제공 정규화는 일관된 형식으로 텍스트를 표준화하고 모호한 경우를 방지하기 위해 일부 문자를 대체 혹은 제거의 작업을 수행, 사전 토큰화는 입력 문장을 토큰화하기 전에 단어와 같은 작은 단위로 나누는 기능을 제공
  • 워드피스 토크나이저 학습
from tokenizers import Tokenizer
from tokenizers.models import WordPiece
from tokenizers.normalizers import Sequence, NFD, Lowercase
from tokenizers.pre_tokenizers import Whitespace

tokenizer = Tokenizer(WordPiece())
tokenizer.normalizer = Sequence([NFD(), Lowercase()])
tokenizer.pre_tokenizer = Whitespace()

tokenizer.train(["../corpus.txt"])
tokenizer.save("../petition_wordpiece.json")

토크나이저스 라이브러리의 토크나이저(Tokenizer)로 워드피스(WordPiece) 모델을 불러오고, 정규화 방식과 사전 토큰화 방식을 설정

정규화 방식은 nomarlizers 모듈에 포함된 클래스를 불러와 시퀀스 형식으로 인스턴스를 전달 (NFD : 유니코드 정규화 , Lowercase : 소문자 변환)

사전 토큰화 방식은 pre_tokenizers 모듈에 포함된 클래스를 불러와 적용

train 메서드를 통해 학습에 사용하려는 데이터 경로를 전달하고 save 메서드로 학습결과 저장

  • 워드피스 토큰화
from tokenizers import Tokenizer
from tokenizers.decoders import WordPiece as WordPieceDecoder

tokenizer=Tokenizer.from_file("../datasets/models/petition_wordpiece.json")
tokenizer.decoder=WordPieceDecoder() 

sentence = "안녕하세요. 워드피스 토크나이저입니다."
sentences=["이렇게 입력값을 리스트로 받아서", "쉽게 토크나이저를 사용할 수 있답니다"]

encoded_sentence=tokenizer.encode(sentence)
encoded_sentences=tokenizer.encode_batch(sentences) 

print("인코터 형식: ", type(encoded_sentence))

print("단일 문장 토큰화 : ",encoded_sentence.tokens)
print("여러 문장 토큰화 : ", [enc.tokens for enc in encoded_sentences])

print("단일 문장 정수 인코딩 : ",encoded_sentence.ids)
print("여러 문장 정수 인코딩 : ", [enc.ids for enc in encoded_sentences])
#ids : 토큰 정수

print("정수 인코딩에서 문장 변환: ", tokenizer.decode(encoded_sentence.ids))

모델 결과가 저장된 petition_wordpiece.json 파일을 불러와 Tokenizer 객체 생성

WordPieceDecoder() 를 사용해 Tokenizer의 디코더를 워드피스 디코더로 설정

encode 메서드로 문장을 토큰화, encode_batch 메서드로 여러 문장을 한꺼번에 토큰화 가능

토큰화된 데이터는 tokens 속성을 통해 값을 확인할 수 있으며, ids 속성으로 ID 출력 가능

정수를 다시 문장으로 변환하는 경우 decode 메서드로 출력 가능