이번주에는 토크나이징 ➡️ 벡터화 ➡️ 임베딩 ➡️ RNN/LSTM/GRU ➡️ Seq2Seq 까지 NLP 처리 과정에대해 배웠다.
📌 어휘 사전(Vocabulary)과 Out Of Vocabulary (OOV)
어휘사전(Vocab)은 토크나이저(Tokenizer)가 사용하는 모든 토큰의 집합이며, 각 토큰을 고유한 정수 ID에 매핑한 사전이다.
OOV는 어휘사전에 포함되지 않은 토큰으로 [UNK]로 대체해서 처리한다.
Korpora : 한국어 NLP 실습을 위한 다양한 말뭉치 패키지
Kiwi : 형태소 분석/토크나이저 패키지
📌 Subword Tokenization(하위 단어 토큰화)
- BPE (Byte-Pair Encoding)
: 자주 등장하는 문자 쌍 (Byte-Pair)을 반복적으로 병합하여 subword를 만든다. GPT-2에서 사용.
continuing_subword_prefix="##" 을 설정해줘서 중간 단어임을 명시해줘야한다. (default : "", 예: ##ing, ##ed
# Tokenizer (어휘사전을 바탕으로 토큰화를 처리하는 객체.)
from tokenizers import Tokenizer
from tokenizers.models import BPE
# Pre Tokenizer 로 Subword 방식 토큰화 전에 미리 나누는 방식을 지정.
from tokenizers.pre_tokenizers import Whitespace
# Trainer (토크나이저 모델 학습기) - 알고리즘에 맞춰 trainer를 제공.
from tokenizers.trainers import BpeTrainer
# 토크나이저 객체 생성
tokenizer = Tokenizer(
BPE(unk_token="[UNK]") # Unknown Token(모르는 단어를 표현할 토큰)을 설정.
)
# 토크나이저에 Pre tokenizer 설정
## BPE 알고리즘으로 토큰화 하기 전에 적용할 나누는 방법-Whitespace(): 공백기준으로 미리 나눈다.
tokenizer.pre_tokenizer = Whitespace()
# Trainer 생성 - 학습 하이퍼 파라미터들을 설정
trainer = BpeTrainer(
vocab_size=10000, # 어휘사전의 최대크기.
min_frequency=10, # 최소 출연 횟수. 지정한 횟수(10) 이하로 나온 쌍(pair)는 단어사전에서 제외.
special_tokens=["[UNK]", "[PAD]"], # 어휘사전에 추가할 특수목적 토큰. unk_token은 반드시 추가한다.
continuing_subword_prefix="##"
)
- WordPiece
: 확률(likelihood) 기반 문자 쌍을 병합한다. BERT가 사용한 방식.
continuing_subword_prefix (default : "##") - Unigram
: 초기 vocab에서부터 시작하여 전체 확률(likelihood)를 최소화하는 subword만 남기고 제거하는 방식. T5, LLaMA 등에서 사용.
SentencePiece이므로 공백(token) 처리까지 자동화
continuing_subword_prefix (default : "__")
📌 Text Vector (Vectorization)
BoW (Bag Of Words)
: 단어의 출현 빈도 count에 집중해 단어를 표현하는 방법, 단어 순서는 고려되지 않는다.
- Document-Term-Matrix(DTM) / Term-Document-Matrix(TDM)
문서를 구성하는 단어들이 몇 번 나왔는지를 표현하는 행렬로 Vector화 한다.
DTM과 TDM은 서로 전치행렬. DTM은 행이 문서. TDM은 열(columns)이 문서.
DTM 구조 : 1번 문서는 야구와 관련된 문서임을 알 수 있고, 3번 문서는 축구와 관련된 문서임을 예측할 수 있다.
| 골 | 골키퍼 | 방어율 | 타자 | 투수 | |
| 1번 문서 | 0 | 0 | 10 | 30 | 30 |
| 2번문서 | 1 | 0 | 20 | 40 | 40 |
| 3번문서 | 10 | 40 | 0 | 0 | 0 |
- Terms(단어) Frequency - (Document Frequency) (TF-IDF)
다른 문서에도 자주 나오는 토큰에대해서는 주제와 크게 관련 없는 토큰이라고 판단해서 패널티를 주는 방식
-> 적은 문서에 나올 수록 값은 커지고 많은 문서에 나올 수록 값이 작아진다.
-> 예 : "##이다", "##은/는" 등
-> 값이 너무 커지면 안되니까 log화. 0이 나오면 안되니까 분자 분모에 모두 1씩 더해주고 계산.
$$TF * \left(\log \cfrac{\text{전체 문서수 + 1}}{\text{해당 단어가 나오는 문서수 + 1}} + 1\right)$$
# CountVectorizer 를 이용해 전처리.
from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer(
tokenizer=tokenizer, # 문서를 받아서 토큰화 처리하는 callable. 생략: 공백/구두점기준으로 토큰화
token_pattern=None, # 토큰화 기준문자열의 패턴(정규표현식)지정. tokenizer설정시 무시됨.
)
# n-gram 적용
cv_ngram = CountVectorizer(
tokenizer=tokenizer,
ngram_range=(1,3) # 1-gram, 2-gram, 3-gram
)
☑️ n-gram 이란?
텍스트를 연속된 n개의 단어(또는 문자) 묶음으로 잘라내는 방식.
"나는 밥을 먹었다"
1-gram : ["나는", "밥을", "먹었다"]
2-gram : ["나는 밥을", "밥을 먹었다"]
3-gram : ["나는 밥을 먹었다"]
기존 BoW는 단어 순서가 무시되기 때문에 단어 순서와 문맥을 보존하기 위해 사용하는 방법.
감정 분석/스팸 분류에 사용했을 때 성능 향상된다. 이유는 "무료 당첨", "쿠폰 지급" 같은 단어를 캐치할 수 있기때문에.
ngram_range=(1,3) 으로 했을때 ["나는", "밥을", "먹었다", "나는 밥을", "밥을 먹었다", "나는 밥을 먹었다"] 모두 feature로 등록된다.
단점은 vocab 크기가 매우 커지고 데이터 계산량이 증가하고 noise가 많아질 수 있음.
📌 Word Embedding
단어를 고정길이의 실수 벡터(dense vector)로 변환하는 방법. 텍스트는 컴퓨터가 이해할 수 없고, 컴퓨터가 이해할 수 있는 숫자로 변환한다.
벡터 공간에서 표현하며 단어 의미의 유사성을 처리 가능하게 해준다.
Queen과 King, Woman과 Man의 차이는 벡터 공간 차이가 서로 유사하다는 것을 알 수 있음.
분산표현(Distributed Representation)의 Word2Vec : CBOW와 Skip-gram
분산표현 : 단어의 의미는 그 주변 단어의 분포로부터 파악할 수 있음.
➡️ CBOW/Skip-gram 모델 학습 데이터셋은 모두 token들이 one-hot encoding 되 있어야 한다.
- CBOW 모델은 주변 단어들로부터 중심 단어를 예측하는 방식이다.
"나는 __ 먹었다"
- Skip-gram 모델은 중심 단어로부터 주변 단어들을 예측하는 방식이다.
"____ 밥을 ______"

Sliding Window 방식 : 지정한 개수(window) 만큼씩 이동하면서 어떤 작업을 진행하는 것


Skip-gram이 CBOW보다 더 어려운 상황에서 훈련하는 모델인만큼, 단어의 분산표현이 더 뛰어날 가능성이 커진다.
이미 가중치를 학습시킨 모델을 사용하는 편. 그 중 유명한 Gensim 라이브러리 : https://wikidocs.net/233744
✨ Word2Vec의 한계 : 동음이의어가 구분이 안된다. (예 : 먹는 배와 타는배, 배수의 2배-3배)
Contextualized Word Embedding(문맥 기반 단어 임베딩)
단어가 문장에서 실제로 어떤 의미로 사용되었는지(문맥·맥락)를 반영하여 벡터를 산출하는 방식 (같은 "배"여도 뜻이 다르면 다른 벡터값을 가지게 된다.)
사용되는 모델 구조
- RNN (+LSTM, GRU)
- Transformer
📌 RNN
Sequential 데이터 : 데이터의 순서가 있고 그 순서 정보가 중요한 데이터셋

- 현재 단어 $x_t$ 와 이전 hidden state $h_{t-1}$ 더한 값을 가지고 현재 시점 문맥인 $h_t$ 를 만든다.
- 시퀀스 길이가 길면 초반 문맥이 출력까지 전달되지 않는 한계가 있음.
- 현재 시점의 문맥을 알기 위해선 이전 hidden state 값이 필요하므로 병렬이 불가능하고 순차적으로만 처리가 가능하다.
- 활성화 함수 : tanh 사용.
- sigmoid 활성화 함수를 사용하지 않는 이유 : 기울기 소실(Vanishing Gradient) 문제를 가지고 있기때문.
tanh의 미분값 0~1 / sigmoid의 미분값 : 0~0.25
시퀀스길이가 길면 미분값이 0에 가까워지는 현상을 기울기 소실이라고 함.
- ReLU 활성화 함수를 사용하지 않는 이유 : Dying ReLU 문제 발생 가능.
ReLU의 출력 0, 미분값 0
음수일 때 기울기가 아예 전달되지 않아, 학습 자체가 이루어지지 않는 현상을 Dying ReLU라고함.
LeakyReLU 해결법도 있지만 ReLU 자체가 출력 제한 없이 크기가 커질 수 있으므로, 안됨.
-> hidden state는 다시 input으로 들어가야하기때문에 tanh 활성화 함수가 적합.
- cos/sin은 주기 함수라 예전 정보를 기억하는 것이 중요한 RNN에는 적합하지 않음.

- RNN Bidirectional : 양방향 적용 여부. Default : False
- Foward RNN : 왼쪽 -> 오른쪽 문맥
- Backward RNN : 오른쪽 -> 왼쪽 문맥을 반영
최종 출력 = concat(forward, backward)
전체 문장을 모두 보고 판단하는 작업할 때만 True 해야함. (예 : 요약, 감정 분석 등)
미래를 예측하는 AutoRegressive 모델(다음 토큰 생성)은 무조건 False. (예 : Decoder)

📌 DAKI
DROP
- 중복 이론 정리 줄이기
- 실습 없이 이론만 보는 접근 버리기
- 과도한 미분/수식 파기 중단 -> 중요한 부분과 코드 실습에 집중하자.
ADD
- RNN, LSTM, GRU 실습
- Tokenizer 학습 실습
- Word2Vec embedding 시각화
- 작은 NLP 프로젝트 추가
KEEP
- 단계적 학습 흐름을 이해할 수 있었음.
Tokenizer → Embedding → RNN → LSTM → Seq2Seq - 강사님 말씀에서 나오는 개념들을 메모하는 것.
- "왜 필요한지", "대체 가능한지" 실전 중심 이해 방식
IMPROVE
- 코드 실습 늘려야함.
- 개념 쪼개서 비교해봐야함 : bidirectional true/false 비교, RNN/LSTM/GRU 비교
'AI > AI TECH' 카테고리의 다른 글
| [플레이데이터 SK네트웍스 Family AI 캠프 21기] 11월 4주차 회고 - 2차 단위프로젝트 (0) | 2025.12.01 |
|---|---|
| [sklearn.metrics] 분류형 평가지표 자세히 분석하기 (0) | 2025.11.23 |
| [플레이데이터 SK네트웍스 Family AI 캠프 21기] 11월 1주차 회고 (0) | 2025.11.10 |
| 모델 성능을 높이기 위한 데이터 전처리 ✨ (0) | 2025.11.09 |
| [플레이데이터 SK네트웍스 Family AI 캠프 21기] 10월 4주차 회고 - 1차 단위프로젝트 (0) | 2025.10.26 |