token은 단어와 어떻게 다른가

오늘의 질문

앞선 글에서 LLM이 “다음 token을 예측한다”는 동작을 반복한다고 설명했다. 그런데 여기서 자연스럽게 다음 질문이 따라온다.

token이 결국 단어 아닌가요? 왜 굳이 다른 말을 쓰나요?

결론부터 말하면 token은 단어와 다르다. 그리고 이 차이를 모르고 넘어가면, 나중에 “똑같은 문장인데 왜 비용이나 처리 속도가 다르게 나오지?” 같은 질문에서 막히게 된다. 이 글은 token이 단어와 정확히 어떻게 다른지, 그리고 그 차이가 왜 실무에서 중요한지를 다룬다.

먼저 답하기

한 문장으로 줄이면 이렇다.

token은 사람이 생각하는 “단어”가 아니라, tokenizer라는 별도의 도구가 미리 정해둔 규칙으로 문장을 쪼갠 조각이다. 그래서 같은 문장도 쪼개는 도구가 다르면 조각 개수가 달라진다.

“우산을 썼다”라는 문장을 사람은 “우산을”, “썼다” 두 단어로 나눈다. 하지만 모델이 실제로 보는 조각은 이것과 다를 수 있다. “썼다”가 통째로 하나의 조각일 수도 있고, “쓰”와 “었다”처럼 더 잘게 나뉠 수도 있다. 어느 쪽이든 사람의 “단어” 감각과는 별개로, tokenizer가 학습 과정에서 정해둔 규칙을 따른다.

이 차이가 왜 중요한가. 모델이 한 번에 처리할 수 있는 조각의 개수(맥락, context라고 부른다)에는 한계가 있고, 비용도 조각 개수에 비례해서 매겨지는 경우가 많다. 그래서 “이 문장이 몇 단어냐”가 아니라 “이 문장이 몇 token이냐”가 실무에서 훨씬 자주 등장하는 질문이 된다.

주제에 맞는 핵심 설명

원문 문장이 tokenizer를 거쳐 조각으로 나뉘고, 각 조각이 vocabulary에서 정수 id로 바뀌어 정수 배열이 모델 입력이 되는 과정을 보여주는 다이어그램

원문 문장이 tokenizer를 거쳐 조각으로 나뉘고, 그 조각들이 vocabulary라는 표에서 정수 id로 바뀌어야 비로소 모델이 받아들일 수 있는 입력이 된다. 사람은 이 과정을 보지 못하고 “문장을 넣었더니 답이 나온다”고만 느끼지만, 실제로는 이 세 단계를 거친다.

용어부터 정리하기

tokenizer란 문장을 token 단위로 쪼개는 도구를 말한다. 사람이 그때그때 감으로 나누는 게 아니라, 모델을 학습시키기 전에 미리 정해둔 규칙에 따라 기계적으로 쪼갠다.

**vocabulary(어휘 집합)**란 이 tokenizer가 만들어낼 수 있는 조각들의 전체 목록이다. 모델은 이 목록에 있는 조각만 알아볼 수 있고, 각 조각에는 고유한 정수 번호(id)가 매겨져 있다. tokenizer가 문장을 조각냈다고 끝나는 게 아니라, 그 조각을 vocabulary에서 찾아 정수로 바꾸는 과정까지 거쳐야 모델이 실제로 쓸 수 있는 입력이 된다.

**token length(token 길이)**란 문장 하나가 몇 개의 token으로 쪼개졌는지를 말한다. 같은 뜻을 가진 문장이라도 tokenizer가 다르면, 혹은 표현 방식이 다르면 token 길이가 달라질 수 있다.

**context(맥락)**란 모델이 한 번에 참고할 수 있는 token의 총량을 말한다. 지금까지 나온 대화나 문서 전체가 이 한도 안에 들어가야 모델이 참고할 수 있다. token 길이가 늘어난다는 건 이 한정된 자리를 그만큼 더 많이 차지한다는 뜻이다.

왜 subword로 쪼개는가

여기서 자연스러운 질문이 나온다. “그냥 단어 단위로 쪼개면 안 되나?”

Hugging Face의 tokenizer 관련 문서는 이 질문에 대한 답을 명확히 제시한다. 단어 단위로 쪼개면(word-level tokenization) 문제가 생기는데, “love”, “loving”, “loved”, “lovingly”처럼 같은 단어의 변형마다 전부 별도의 항목으로 vocabulary에 넣어야 해서 어휘 목록이 지나치게 커지고, 그 목록에 없는 새로운 단어는 아예 처리할 수 없게 된다. 반대로 글자 하나하나로 쪼개면(character-level tokenization) 어휘 목록은 작아지지만, 글자 하나는 담을 수 있는 의미가 너무 적어서 문장이 훨씬 길어지고 성능도 떨어진다.

그래서 실제로 널리 쓰이는 방식은 단어와 글자 사이 어딘가에서 쪼개는 subword(하위 단어) 방식이다. 자주 나오는 단어는 통째로 하나의 조각으로 남겨두고, 자주 나오지 않는 단어는 더 작은 조각으로 쪼갠다. Hugging Face 문서가 드는 예시를 보면, “annoyingly”라는 단어가 상황에 따라 ["annoying", "ly"]로 쪼개지기도 하고 ["annoy", "ing", "ly"]로 더 잘게 쪼개지기도 한다. Hugging Face의 tokenizer 요약 문서는 이런 subword 분리 방식으로 BPE(Byte-Pair Encoding), Unigram, WordPiece 세 가지를 소개하며, 이들은 방식은 다르지만 공통적으로 “자주 나오는 단어는 그대로 두고 드문 단어는 쪼갠다”는 원칙을 따른다.

한국어처럼 띄어쓰기만으로 단어 경계를 판단하기 애매한 언어를 위해서는 SentencePiece라는 방식도 쓰인다. 이 방식은 띄어쓰기 자체도 하나의 기호로 취급해서, 띄어쓰기 규칙이 다른 언어에도 같은 방식을 적용할 수 있게 만든다. 이 내용 역시 위 문서에 정리되어 있다.

작은 관찰: 같은 뜻, 다른 token 수

“학교에 갑니다”라는 문장을 생각해보자. 사람은 이걸 “학교에”, “갑니다” 두 단어로 느낀다. 그런데 tokenizer가 이걸 어떻게 쪼갤지는 그 tokenizer가 무엇을 학습했느냐에 달려 있다. “학교”가 자주 나오는 단어라 통째로 하나의 조각이 될 수도 있고, “갑니다”가 “가”, “ㅂ니다”처럼 문법 요소별로 쪼개질 수도 있다.

아래 표는 이 감각을 보여주기 위한 예시다. 실제 tokenizer를 돌려서 나온 결과가 아니라, “표현이 다르면 token 수도 달라질 수 있다”는 성질을 보여주기 위해 만든 가상의 예시라는 점을 밝혀둔다. 실제 조각의 형태와 개수는 어떤 tokenizer를 쓰느냐에 따라 이 표와 다르게 나올 수 있다.

문장(뜻은 같음) 표현 방식 token 개수(예시)
학교에 갑니다 표준어, 격식체 상대적으로 적음
학교 가요 구어체, 축약형 표현에 따라 달라짐
저는 오늘 학교에 갈 예정입니다 풀어 쓴 표현 상대적으로 많음

이 표에서 짚어야 할 부분은 정확한 숫자가 아니라 패턴이다. 뜻이 같아도 표현이 풀어져 있거나 격식체일수록 조각 수가 늘어나는 경향이 있고, 이 조각 수가 곧 context를 얼마나 차지하는지, 처리 비용이 얼마나 드는지와 직결된다.

흔한 오해 두 가지

오해 1: token 하나는 항상 글자 하나거나 단어 하나다. 아니다. token은 위에서 봤듯 단어보다 작을 수도 있고(subword), 어떤 경우엔 단어와 일치할 수도 있다. 고정된 크기가 없다. 그래서 “이 문장은 10글자니까 10token이다” 같은 계산은 성립하지 않는다.

오해 2: 모든 tokenizer는 같은 방식으로 쪼갠다. 아니다. 앞서 봤듯 BPE, Unigram, WordPiece는 서로 다른 기준으로 조각을 만든다. 그래서 같은 문장을 다른 모델에 넣으면 token 개수 자체가 다르게 나올 수 있다. “이 문장은 몇 token이다”라는 말은 항상 “어떤 tokenizer 기준으로”라는 전제가 숨어 있는 셈이다.

자주 묻는 질문

한국어가 영어보다 token을 더 많이 쓴다는 말을 들었는데, 사실인가요? 이 글이 인용한 Hugging Face 문서는 이 비교를 직접 다루지 않는다. 다만 문서가 설명하는 원리로 볼 때, tokenizer의 vocabulary가 어떤 언어의 텍스트로 더 많이 학습되었느냐에 따라 그 언어의 흔한 표현이 통째로 하나의 조각으로 남을 가능성이 높아진다는 점은 짚을 수 있다. 특정 언어가 항상 더 많은 token을 쓰는지는 tokenizer마다 다른 문제이므로, 실무에서 비용이나 성능이 중요하다면 실제로 쓰려는 tokenizer로 직접 확인하는 것이 정확하다.

같은 문장을 여러 번 보내면 token 수도 항상 같게 나오나요? 같은 tokenizer, 같은 문장이라면 그렇다. token화 자체는 정해진 규칙을 그대로 적용하는 과정이라 결과가 매번 달라지지 않는다. 다만 띄어쓰기, 문장부호, 줄바꿈처럼 사람 눈에는 사소해 보이는 차이도 token 결과를 바꿀 수 있으니, 같은 뜻이라고 해서 항상 같은 token 수가 나온다고 가정하면 안 된다.

입력이 길어지면 정확히 무엇이 문제가 되나요? 크게 두 가지다. 하나는 context 한도에 걸리는 경우다. 문서 전체의 token 수가 모델이 한 번에 참고할 수 있는 한도를 넘으면, 앞부분이 잘려나가거나 아예 처리가 거부될 수 있다. 다른 하나는 비용과 속도다. 많은 서비스가 처리한 token 수에 비례해서 비용을 매기기 때문에, 같은 내용을 더 많은 token으로 표현할수록 비용도 늘어난다. 그래서 실무에서는 “글자 수를 줄인다”가 아니라 “token 수를 줄인다”는 감각으로 입력을 다듬는 경우가 많다.