자연어 처리 — 토큰화 이론 & KoNLPy & SentencePiece & 벡터화 실습 정리

syeom·2026년 6월 1일

멀티캠퍼스 데이터분석 6월 1일 수업 내용 — 토큰화 이론, KoNLPy(Okt/Kkma/Komoran), SentencePiece, 통계 기반 NLP, 영화 리뷰 감성 분류 실습


📌 목차

이론
1. 자연어 처리 & 토큰화 개념
2. 토큰화의 종류와 이론적 배경
3. 왜 한국어 NLP는 어려울까?
4. KoNLPy — 한국어 형태소 분석기
5. Okt (Open Korean Text)
6. Kkma (꼬꼬마) & Komoran (코모란)
7. KoNLPy의 한계 — OOV & 서브워드 토큰화
8. SentencePiece
9. 통계 기반 자연어 처리

실습

  1. 토큰화 실습 — Okt / Kkma / Komoran / SentencePiece
  2. 벡터화 & 감성 분류 실습 — 네이버 영화 리뷰

이론

1. 자연어 처리 & 토큰화 개념

토큰화 란 컴퓨터가 자연어(인간의 언어)를 이해하고 분석할 수 있도록, 긴 텍스트를 의미가 있는 가장 작은 단위(토큰) 로 나누는 전처리 과정입니다.

  • 컴퓨터는 텍스트 전체를 한 번에 통째로 처리할 수 없습니다
  • 단어들의 등장 빈도, 순서, 문맥을 수학적(통계적)으로 계산하기 위한 가장 기초적인 데이터를 만드는 과정입니다

자연어 예측 모델 순서

1. 데이터 로드
2. 문자 토큰화
3. 토큰화된 데이터 벡터화
4. 모델 학습
5. 평가

2. 토큰화의 종류와 이론적 배경



3. 왜 한국어 NLP는 어려울까?


4. KoNLPy — 한국어 형태소 분석기


💡 KoNLPy는 내부적으로 Java(JDK 17 권장) 를 사용합니다.
설치 전 JDK가 설치되어 있어야 합니다.

# !pip install konlpy

5. Okt (Open Korean Text)

SNS, 댓글 등 일상적인 한글 데이터 형태소 분석에 특화된 분석기입니다.

Okt 주요 품사 태그

태그품사예시
Noun명사데이터, 분석
Verb동사하다, 먹다
Adjective형용사재미있다, 좋다
Josa조사은, 는, 이, 가
Adverb부사정말, 매우
KoreanParticle한국어 파티클/이모티콘ㅋㅋㅋ, ㅠㅠ
Punctuation구두점. , ? ~
Number숫자
Alpha영문자
Modifier관형사새, 헌, 첫
Foreign외국어/한자
Hashtag해시태그#태그

Okt 주요 매개변수

매개변수설명
norm=True정규화 — 비표준어, 오타, 불필요한 반복을 다듬음
stem=True어간 추출 — 동사/형용사의 어미를 잘라내고 기본형으로 통일

6. Kkma (꼬꼬마) & Komoran (코모란)

Kkma (꼬꼬마)

  • 국어사전 기반의 깐깐한 언어 분석기
  • 구두점이 없어도 문맥을 파악하여 문장 분리(sentences) 가능
  • 세종 태그셋 사용

Komoran (코모란)

  • Kkma와 동일하게 단어장 기반 토큰화
  • 사용자 정의 사전(userdic) 으로 신조어 추가 가능
  • 세종 태그셋 사용, 분석 불가 단어는 NA 처리

세종 태그셋 주요 태그

태그설명예시
NNG일반 명사데이터, 분석
NNP고유 명사서울, 컴퓨터
NNB의존 명사것, 수, 만큼
NP대명사나, 너, 우리
VV동사배우다, 먹다
VA형용사좋다, 빠르다
MAG일반 부사매우, 잘, 빨리
SL외국어

분석 목적별 품사 선택 가이드

목적권장 태그
감성/의도/리뷰 분석NNG, NNP, VV, VA, MAG, SL
명사 기반 분류NNG, NNP, NR, NP
의미 있는 단어 최대 포함NNG, NNP, VV, VA, MAG, MAJ, IC, SL

7. KoNLPy의 한계 — OOV & 서브워드 토큰화

서브워드(Subword) 토큰화

  • 기존 형태소 분석기의 OOV(Out-Of-Vocabulary) 문제를 해결
  • 단어를 더 작은 서브워드 단위로 분리하여 미등록 단어도 처리 가능

8. SentencePiece

  • 문자의 덩어리를 학습하여 글자 간 상호작용으로 단어장을 생성
  • Train() 으로 모델과 vocab을 생성

SentencePiece 마법의 기호 — 언더바(▁)

  • (U+2581) — 단어의 시작을 표시하는 특수 기호 (키보드 언더바 _ 와 다름)
  • 원본 복원(decode) 시 를 공백으로 치환하여 사용

Train() 주요 매개변수

매개변수설명
input학습할 텍스트 파일 경로 (.txt)
model_prefix저장할 모델 이름
vocab_size단어 사전 크기 (8000 / 16000 / 32000 권장)
model_type토큰 생성 방식 (unigram / bpe / char / word)
character_coverage학습에 포함할 문자 종류 비율 (0.9995 권장)
shuffle_input_sentence문장 순서 셔플 — 특정 순서 편향 방지
input_sentence_size학습 문장 샘플링 수

model_type 비교

설명주요 사용
unigram한 단어씩 잘라서 표현BERT, KoGPT 등
bpe빈도 기반 병합GPT-2 (한글 부적합)
char문자 단위 분리정보가 너무 잘게 쪼개짐
word단어 단위 분리한국어 부적합

9. 통계 기반 자연어 처리

N-gram

Bag of Words (BoW)

TF-IDF (Term Frequency - Inverse Document Frequency)

동시 출현 행렬 (Co-occurrence Matrix)

PMI (Pointwise Mutual Information)

LSA (Latent Semantic Analysis, 잠재 의미 분석)

유사도


실습

10. 토큰화 실습 — Okt / Kkma / Komoran / SentencePiece

Okt 실습

from konlpy.tag import Okt

okt  = Okt()
text = '파이썬에서 자바의 환경 변수 설정은 너무 힘이 듭니다. ㅠㅠ 오프라인 수업이 그립습니닼ㅋㅋ'

# 형태소 분리
tokens = okt.morphs(text)
print(tokens)

# 명사만 추출
nouns = okt.nouns(text)
print(nouns)

# 품사 태깅 — (단어, 품사) 튜플 리스트 반환
print(okt.pos(text))

# 조사, 구두점 제외하고 필터링
new_list = []
for t, p in okt.pos(text):
    if p not in ['Josa', 'Punctuation']:
        new_list.append(t)

# 정규화 + 어간 추출
okt.pos(text, norm=True, stem=True)

Kkma 실습

from konlpy.tag import Kkma

kkma         = Kkma()
text_formal  = '인공지능 데이터 분석가 과정에서 오신것을 환영합니다 오늘부터 자연어 처리를 배우고 다음 주에는 딥러닝 자연어 처리를 배울 예정입니다'

# 문장 분리 (구두점 없어도 문맥 파악)
sentence = kkma.sentences(text_formal)
print(sentence)

# 형태소 분리 & 명사 추출 & 품사 태깅
kkma.morphs(text)
print(kkma.nouns(text))
print(kkma.pos(text))

Komoran + 사용자 사전 실습

from konlpy.tag import Komoran

komoran = Komoran()
text3   = '요즘 혼자 여행을 다니면서 챗지피티를 이용해서 여행지를 추천받고 혼자 식사를 하기 위해서 배달의 민족을 사용합니다'

komoran.morphs(text3)
komoran.nouns(text3)
komoran.pos(text3)

# 사용자 사전 적용 (user_dic.txt — 신조어\t품사 형식)
komoran2 = Komoran(userdic='user_dic.txt')
komoran2.morphs(text3)

💡 user_dic.txt 형식

챗지피티\tNNP
배달의민족\tNNP

신조어와 품사를 탭(\t) 으로 구분하여 등록합니다.

SentencePiece 실습

# !pip install sentencepiece
import sentencepiece as spm

# 모델 학습
spm.SentencePieceTrainer.Train(
    input                  = 'test.txt',
    model_prefix           = 'ko_unigram',
    vocab_size             = 8000,
    model_type             = 'unigram',
    character_coverage     = 0.9995,
    shuffle_input_sentence = True
)

# 모델 로드
sp = spm.SentencePieceProcessor()
sp.load('ko_unigram.model')

text5 = '스토리 개연성이 부족하지만 배우들의 연기가 뛰어났다'

# 서브워드 분리
print(sp.encode_as_pieces(text5))

# ID로 인코딩 & 디코딩
ids = sp.encode_as_ids(text5)
print(ids)
print(sp.decode_ids(ids))

# 원본 복원
tokens  = sp.encode_as_pieces(text5)
print('원본 복원:', sp.decode_pieces(tokens))

# ▁ 기호 처리 (U+2581 — 키보드 언더바와 다름)
char     = '\u2581'
restored = ''.join(tokens).replace(char, ' ')[1:]
print(restored)

11. 벡터화 & 감성 분류 실습 — 네이버 영화 리뷰

데이터 로드 & 전처리

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split, StratifiedKFold, GridSearchCV
from sklearn.metrics import classification_report
from sklearn.pipeline import Pipeline
from sklearn.svm import LinearSVC
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from konlpy.tag import Okt

df = pd.read_csv('../data/ratings_train.txt', sep='\t')

# 공백 문자열을 결측치로 처리
df['document'] = df['document'].str.strip()
df.loc[df['document'] == '', 'document'] = np.nan
df.dropna(inplace=True)

# 중복 리뷰 제거
df.drop_duplicates('document', inplace=True)

# id 컬럼 제거 (학습에 불필요)
df.drop('id', axis=1, inplace=True)

x = df['document']
y = df['label']

X_train, X_test, y_train, y_test = train_test_split(
    x, y, test_size=0.2, stratify=y, random_state=42
)

💡 공백 문자열 처리 주의사항
' ' (공백)은 isna() 로 감지되지 않습니다.
str.strip() 으로 공백을 제거한 뒤 '' == '' 조건으로 결측치 처리해야 합니다.

토큰화 함수 정의

okt = Okt()

allow_pos = ['Noun', 'Verb', 'Adjective', 'KoreanParticle', 'Alpha']
stop_word  = ['하다', '되다']
len_word   = 2   # 최소 글자 수

def tokenize(input_text):
    result = []
    for word, pos in okt.pos(input_text, norm=True, stem=True):
        if (pos in allow_pos) and (word not in stop_word) and (len(word) >= len_word):
            result.append(word)
    return result

CountVectorizer 벡터화 & LinearSVC 모델

# CountVectorizer — 단어의 존재 여부(binary=True) 또는 빈도수
vectorizer  = CountVectorizer(tokenizer=tokenize, lowercase=False, binary=True)

x_train_vec = vectorizer.fit_transform(X_train)
x_test_vec  = vectorizer.transform(X_test)

model = LinearSVC(C=1.0)
model.fit(x_train_vec, y_train)

pred = model.predict(x_test_vec)
print(classification_report(y_test, pred))

Pipeline + GridSearchCV

pipe = Pipeline([
    ('vec',   CountVectorizer(tokenizer=tokenize, lowercase=False)),
    ('model', LinearSVC())
])

param_grid = {
    'vec__binary' : [True, False],
    'model__C'    : [0.9, 1.0]
}

cv = StratifiedKFold(n_splits=3, shuffle=True, random_state=42)

grid = GridSearchCV(
    estimator  = pipe,
    param_grid = param_grid,
    cv         = cv,
    verbose    = 1
)

# Train(20%) / Validation(72%) / Test(8%) 3분할
X_train, X_test, y_train, y_test = train_test_split(
    x, y, test_size=0.8, stratify=y, random_state=42
)
X_vali, X_test, y_vali, y_test = train_test_split(
    X_test, y_test, test_size=0.1, stratify=y_test, random_state=42
)

grid.fit(X_train, y_train)
print('최적의 파라미터:', grid.best_params_)

pred = grid.predict(X_test)
print(classification_report(pred, y_test))

CountVectorizer vs TfidfVectorizer 비교

CountVectorizerTfidfVectorizer
방식단어 등장 빈도/유무빈도 × 역문서 빈도
특징단순, 빠름중요 단어에 높은 가중치
binary=True등장 여부만 0/1-
주요 사용단순 분류검색, 문서 유사도

📎 핵심 개념 요약

개념설명
토큰화텍스트를 의미 있는 최소 단위(토큰)로 분리
형태소의미를 가지는 가장 작은 언어 단위
OOVOut-Of-Vocabulary — 사전에 없는 미등록 단어
서브워드 토큰화단어를 더 작은 단위로 분리 — OOV 문제 해결
okt.morphs()형태소 분리
okt.nouns()명사만 추출
okt.pos()품사 태깅 — (단어, 품사) 튜플 리스트 반환
norm=True비표준어/오타 정규화
stem=True동사/형용사 어간 추출 (기본형 통일)
kkma.sentences()구두점 없어도 문장 분리
Komoran(userdic='파일')사용자 정의 사전으로 신조어 등록
spm.SentencePieceTrainer.Train()SentencePiece 모델 학습
sp.encode_as_pieces()텍스트 → 서브워드 리스트
sp.encode_as_ids()텍스트 → ID 리스트
sp.decode_pieces() / decode_ids()서브워드/ID → 원본 복원
(U+2581)SentencePiece 단어 시작 표시 기호
BoWBag of Words — 단어 등장 빈도 기반 벡터화
TF-IDF단어 빈도 × 역문서 빈도 — 중요 단어 가중치 부여
CountVectorizerBoW 벡터화 (binary=True → 등장 여부만)
TfidfVectorizerTF-IDF 벡터화
vectorizer.fit_transform()Train 데이터 학습 + 변환
vectorizer.transform()Test 데이터 변환 (Train 기준)
str.strip()앞뒤 공백 제거 — 공백 문자열 결측치 처리 전 필수
LinearSVC선형 SVM 분류 모델 — 텍스트 분류에 자주 사용
profile
공부 기록

0개의 댓글