노트북:
09-01. tokenization.ipynb(셀 18개, 코드 셀 12개)
예상 시간: 약 12분 · 짝꿍 글:4.3_토큰화.md
Don't, Mr., Ph.D. 같은 아포스트로피와 마침표다.mecab.morphs()가 바로 이것이다.⚠️ 셀 0을 그대로 실행하면 셀 3에서 멈출 수 있다. 최신 nltk(3.9 이상)는 word_tokenize에 punkt가 아니라 punkt_tab 이 필요하다. 그래서 LookupError: Resource 'punkt_tab' not found가 난다. pos_tag도 averaged_perceptron_tagger_eng 를 요구한다. 셀 0에 두 줄을 미리 추가해 두자.
nltk.download('punkt_tab')
nltk.download('averaged_perceptron_tagger_eng')
nltk 3.10에서 직접 확인했다. 두 줄을 넣으면 나머지 출력은 노트북에 저장된 것과 완전히 같다.
셀 2는 tensorflow.keras.preprocessing.text를 임포트한다. 코랩에는 텐서플로가 깔려 있고, Keras 3에도 이 함수가 레거시로 남아 있어서 그대로 돈다.
셀 11·15의 pip install kss, pip install konlpy는 앞에 !가 없지만 코랩에서는 돌아간다. kss는 설치가 1분 넘게 걸리니 세션 전에 미리 실행해 두자.
| 파트 | 셀 | 한 줄 메시지 | 시간 |
|---|---|---|---|
| 1. 영어 단어 토큰화 3종 비교 | 0~5 | 같은 문장도 도구마다 다르게 자른다 | 3분 |
| 2. 트리뱅크 규칙 | 7 | 하이픈은 붙이고, 줄임말은 뗀다 | 1분 |
| 3. 문장 토큰화 | 9~12 | 마침표 = 문장 끝이 아니다 | 2분 |
| 4. 품사 태깅 | 14 | 토큰에 품사를 붙인다 | 1분 |
| 5. 한국어 형태소 분석 | 15~17 | 한국어는 형태소로 자른다 → 4.5로 연결 | 4분 |
읽는 법: 🗣 = 그대로 말해도 되는 문장, ❓ = 나올 만한 질문과 답
import nltk
nltk.download('punkt') # 문장/단어 토큰화 규칙
nltk.download('averaged_perceptron_tagger') # 품사 태깅 모델
# (추가) nltk.download('punkt_tab'); nltk.download('averaged_perceptron_tagger_eng')
from nltk.tokenize import word_tokenize
from nltk.tokenize import WordPunctTokenizer
from tensorflow.keras.preprocessing.text import text_to_word_sequence
s = "Don't be fooled by the dark sounding name, Mr. Jone's Orphanage is as cheery as cheery goes for a pastry shop."
word_tokenize(s)
WordPunctTokenizer().tokenize(s)
text_to_word_sequence(s)
결과에서 갈리는 부분만 모으면 이렇다.
| 원문 | word_tokenize | WordPunctTokenizer | text_to_word_sequence |
|---|---|---|---|
Don't | Do, n't | Don, ', t | don't |
Mr. | Mr. | Mr, . | mr |
Jone's | Jone, 's | Jone, ', s | jone's |
, . | 남김 | 남김 | 지움 |
| 대소문자 | 그대로 | 그대로 | 전부 소문자 |
| 토큰 수 | 25 | 28 | 21 |
word_tokenize → 영어 문법을 안다. n't(not), 's(소유격)처럼 뜻이 있는 단위로 뗀다.WordPunctTokenizer → 규칙이 단순하다. 구두점을 무조건 따로 뗀다. 그래서 Don, ', t처럼 뜻이 없는 조각이 생긴다.text_to_word_sequence → 소문자로 바꾸고 구두점을 지운 뒤 띄어쓰기로만 자른다. 가장 거칠지만 가장 단순하다.🗣 "같은 문장인데 토큰 수가 25, 28, 21로 다 달라요. 토큰화에는 정답이 없고, 어떤 단위가 우리 문제에 맞는지 고르는 거예요."
❓ 뭘 써야 해? 문제에 따라 다르다. 감성 분류처럼 "좋다/싫다"만 보면 거친 토큰화로도 충분하다. 번역처럼 문법이 중요하면 n't 같은 단위를 살리는 게 낫다.
from nltk.tokenize import TreebankWordTokenizer
tokenizer = TreebankWordTokenizer()
text = "Starting a home-based restaurant may be an ideal. it doesn't have a food chain or restaurant of their own."
tokenizer.tokenize(text)
['Starting', 'a', 'home-based', 'restaurant', 'may', 'be', 'an', 'ideal.', 'it', 'does', "n't", ..., 'own', '.']
home-based가 한 토큰이다.doesn't → does, n't.ideal.의 마침표가 안 떨어졌다. 트리뱅크는 문자열 맨 끝의 마침표만 뗀다. 문장 중간 마침표는 단어에 붙은 채로 남는다. 그래서 보통은 문장 토큰화를 먼저 하고 단어 토큰화를 한다(word_tokenize가 안에서 그렇게 한다).🗣 "ideal 뒤 마침표가 안 떨어진 게 보이시죠. 트리뱅크는 문장 하나를 넣는다고 가정해서 맨 끝 마침표만 떼요. 그래서 문장부터 나누고 단어를 자르는 순서가 필요합니다."
from nltk.tokenize import sent_tokenize
sent_tokenize("His barber kept his word. But keeping such a huge secret ...") # 5문장으로 잘 나뉨
sent_tokenize("I am actively looking for Ph.D. students. and you are a Ph.D student.")
# → ['I am actively looking for Ph.D. students.', 'and you are a Ph.D student.']
Ph.D.의 마침표에서는 자르지 않았다. 약어 목록을 학습해 둔 덕분이다.and로 시작하는데도 잘 나눴다.import kss
kss.split_sentences('딥 러닝 자연어 처리가 재미있기는 합니다. 그런데 문제는 영어보다 한국어로 할 때 너무 어렵습니다. 이제 해보면 알걸요?')
# → ['딥 러닝 자연어 처리가 재미있기는 합니다.', '그런데 문제는 ...', '이제 해보면 알걸요?']
WARNING ... mecab ...은 에러가 아니다. "환경에 mecab이 있어서 그걸 백엔드로 쓴다"는 안내다.🗣 "마침표가 곧 문장 끝은 아니에요. Ph.D.처럼 약어에도 마침표가 들어가니까, 문장 토큰화도 규칙이나 학습이 필요합니다."
from nltk.tag import pos_tag
tokenized_sentence = word_tokenize("I am actively looking for Ph.D. students. and you are a Ph.D. student.")
pos_tag(tokenized_sentence)
[('I', 'PRP'), ('am', 'VBP'), ('actively', 'RB'), ('looking', 'VBG'), ('for', 'IN'),
('Ph.D.', 'NNP'), ('students', 'NNS'), ('.', '.'), ...]
| 태그 | 뜻 | 예 |
|---|---|---|
| PRP | 인칭 대명사 | I, you |
| VBP / VBG | 동사 현재형 / 동명사·현재분사 | am / looking |
| RB | 부사 | actively |
| IN | 전치사 | for |
| NNP / NNS / NN | 고유명사 / 복수명사 / 단수명사 | Ph.D. / students / student |
| DT | 한정사 | a |
from konlpy.tag import Okt, Kkma
okt, kkma = Okt(), Kkma()
s = "열심히 코딩한 당신, 연휴에는 여행을 가봐요"
okt.morphs(s); okt.pos(s); okt.nouns(s)
kkma.morphs(s); kkma.pos(s); kkma.nouns(s)
| Okt | 꼬꼬마(Kkma) | |
|---|---|---|
morphs | 열심히 / 코딩 / 한 / 당신 / , / 연휴 / 에는 / 여행 / 을 / 가봐요 | 열심히 / 코딩 / 하 / ㄴ / 당신 / , / 연휴 / 에 / 는 / 여행 / 을 / 가보 / 아요 |
| 토큰 수 | 10 | 13 |
nouns | 코딩, 당신, 연휴, 여행 | 코딩, 당신, 연휴, 여행 |
morphs → 형태소 단위로 자른 리스트. 4.5에서 쓰는 게 이것이다(분석기는 Mecab).pos → 형태소 + 품사 튜플.nouns → 명사만 뽑는다.코딩한 → 코딩 / 하 / ㄴ, 가봐요 → 가보 / 아요.한을 조사(Josa) 로 태깅했다. 실제로는 하다의 활용형(하 + ㄴ)이다. 분석기마다 틀리는 곳이 다르다.🗣 "한국어는 '연휴에는'을 그대로 두면 '연휴', '연휴에', '연휴를'이 전부 다른 단어가 돼요. 그래서 형태소로 떼서 '연휴'를 같은 단어로 만드는 겁니다. 4.5에서 mecab.morphs 한 줄이 이 역할을 해요."
❓ 4.5에서는 왜 Mecab을 써? 빠르기 때문이다. 4.5는 리뷰 19만 개를 토큰화한다. Mecab은 노트북 기준 14만 5천 개를 10초 정도에 끝낸다. 메서드 이름(morphs)은 Okt, 꼬꼬마와 같다.
🗣 "토큰화는 문장을 RNN에 넣을 단위로 자르는 거고, 정답은 없고 도구마다 규칙이 달라요. 영어는 아포스트로피와 마침표 처리가 갈리고, 한국어는 조사 때문에 형태소 분석기가 필요합니다. 4.5에서는 Mecab으로 형태소를 자르고, 조사 같은 불용어를 빼고 씁니다."
치트시트
| 도구 | 언어 | 단위 | 특징 |
|---|---|---|---|
word_tokenize | 영어 | 단어 | n't, 's 분리. 가장 무난 |
WordPunctTokenizer | 영어 | 단어 | 구두점 전부 분리 |
text_to_word_sequence | 영어 | 단어 | 소문자화 + 구두점 삭제 |
TreebankWordTokenizer | 영어 | 단어 | 하이픈 유지, 끝 마침표만 분리 |
sent_tokenize | 영어 | 문장 | 약어 마침표 처리 |
kss.split_sentences | 한국어 | 문장 | |
Okt / Kkma / Mecab .morphs() | 한국어 | 형태소 | 4.5는 Mecab |