N-gram을 이용해서 새로운 데이터 만들기
- 개발/LLM
- 2026. 5. 17. 01:20
N-gram 이해해보기
TL;DR; N그램을 설명한 글입니다. LLM from Scratch - Part 1. Statistical Language Models[LLM 바닥부터 만들기 - 파트1. 통계적 언어 모델의 원리] 부담 없이 즐기며 배우는 LLM 입문 강의 🚀오픈이벤트 50%할인! (~5
b-programmer.tistory.com
이전에 국립국어사전 데이터를 기반으로 N-gram을 적용한 적이 있습니다. 당시에는 총 548,385개의 단어를 추출하였으며, 한 글자 단어, 속담, 관용구, 구 형태의 표현, 신조어 등은 제외하였습니다. N-gram을 간단히 설명하자면, 이전 데이터(문맥)를 기반으로 다음에 어떤 데이터가 등장할지를 예측하는 방식입니다. 하지만 당시 실험을 진행하면서 한 가지 아쉬운 점이 있었습니다. 사전 중심으로 데이터를 구성하다 보니, 실제 LLM이 동작하는 방식과는 다소 거리가 있다고 느껴졌기 때문입니다. 실제 LLM은 단순 사전 데이터가 아니라, 문서와 문장 단위의 대규모 텍스트 데이터를 기반으로 학습이 이루어집니다. 그래서 이번에는 위키피디아와 같이 외부에 공개되어 있는 실제 문서 데이터를 기반으로 N-gram을 구성해보려고 합니다. 과연 실제 문서를 기반으로 새로운 문장을 생성했을 때, 얼마나 자연스럽게 동작할 수 있을까요? 그리고 어느 정도까지 문맥을 이해하는 것처럼 보일 수 있을까요?
이번에는 이를 직접 확인해보겠습니다.
위키 문서에서 문서를 추출해봅시다.
생각보다 학습에는 꽤 오랜 시간이 필요했습니다. 전체적으로 약 2~3시간 정도 문서를 수집하고 정리하는 작업이 진행된 것 같습니다. 문학 자료의 경우 약 1,531개를 추출하였으며, 비문학 자료는 무려 480,538개를 수집하였습니다. 데이터 출처는 주로 위키피디아와 위키문헌을 기반으로 구성하였습니다. 특히 비문학 자료는 수집된 문서 수 자체가 매우 많았기 때문에 데이터 정리와 전처리에 상당한 시간이 소요되었습니다. 반면 문학 자료의 경우에는 단순히 문서 수를 늘리는 것보다, 실제 순수 문학에 가까운 데이터만 남기기 위해 문학성이 낮거나 성격이 다른 자료들은 제외하는 방식으로 정리를 진행하였습니다.
테스트는 문학 자료를 중심으로 진행할 예정입니다. 특별한 이유가 있는 것은 아니며, 단순히 문학 문체에서 생성 결과가 어떻게 나타나는지 궁금했기 때문입니다.

이 데이터를 이용해서 uni-gram부터 N-gram까지 진행해보겠습니다. 문장이 최대한 자연스러워질때까지 진행할 거 같습니다.
테스트 시작
uni-gram
uni-gram부터 테스트를 진행해보겠습니다. 다만 uni-gram은 이전 문맥을 전혀 고려하지 않기 때문에, 사실상 자주 등장한 글자들을 기반으로 랜덤한 문자열을 생성하는 것에 가깝습니다. 즉, 어떤 글자가 얼마나 자주 등장했는지를 확인할 수는 있지만, 문맥 자체를 이해하지는 못하기 때문에 생성 결과에는 큰 의미가 없었습니다.

처음 테스트를 진행했을 때는 단어가 아니라 음절 단위로 계산하고 있었습니다. 하지만 음절 단위로 N-gram을 구성하게 되면, 이전에 사전 데이터를 기반으로 실험했던 방식과 큰 차이가 없다고 느껴졌습니다.
결국 실제 문장 흐름과 문맥을 조금 더 반영하기 위해서는 음절보다 단어 단위로 접근하는 것이 더 적절하다고 판단하였습니다. 그래서 여러 전처리 과정을 거친 뒤, 최종적으로 단어 기준으로 다시 계산을 진행하였습니다.
이것을 토대로 랜덤 문자열을 만들어보겠습니다.

역시 uni-gram은 순수하게 등장 빈도만 기반으로 랜덤 문자열을 생성하기 때문에, 실제로 생성된 결과가 무슨 의미인지 거의 이해할 수 없었습니다. 멀리서 보면 문장처럼 보이기는 합니다. 하지만 실제로 가까이에서 읽어보면 문맥과 의미가 전혀 이어지지 않는다는 것을 확인할 수 있었습니다. 게다가 문장의 종료 역시 일정한 규칙 없이 등장하였습니다. 즉, 모델이 문장의 흐름을 이해하고 마무리한다기보다, 단순히 등장 확률에 따라 종료 토큰이 우연히 선택되는 수준에 가까웠던 것입니다.
그렇다면 왜 이런 현상이 발생한 것일까요?
그 이유는 uni-gram이 앞뒤 문맥을 전혀 고려하지 않기 때문입니다. uni-gram은 단순히 각 단어가 얼마나 자주 등장했는지에 대한 빈도 정보만 알고 있을 뿐, 어떤 단어들이 서로 자연스럽게 연결되는지는 알 수 없습니다.
즉, 단어를 하나씩 무작위로 선택하게 되면 겉보기에는 한국어 단어들이기 때문에 문장처럼 보일 수는 있습니다. 하지만 실제로는 단어 사이의 관계가 존재하지 않기 때문에 의미를 이해할 수 없는 결과가 만들어지게 됩니다.
예를 들어 아무! 와 같이 어색한 위치에서 문장이 종료되는 것 역시 같은 이유입니다.
uni-gram은 <EOS>가 얼마나 자주 등장하는지는 알 수 있습니다. 하지만 어떤 단어 뒤에서 종료되어야 자연스러운지는 전혀 알지 못합니다. 결국 종료 토큰 역시 단순 확률에 의해 선택되기 때문에, 문장이 엉뚱한 위치에서 끝나는 현상이 발생하게 됩니다.
정리해보자면, uni-gram은 개별 단어의 등장 빈도만 학습하는 구조입니다. 그리고 n의 차원이 증가할수록 단순한 단어 나열에서 벗어나, 이전 문맥을 조금씩 따라가는 방향으로 발전하게 됩니다.
2-gram

'하고'로 시작하는 문장은 총 24,520회 등장하였으며, 전체 데이터 기준 약 0.3%로 1위를 차지하였습니다. 생각보다 굉장히 흥미로운 결과였습니다. 아마 문장 사이를 이어주는 접속 표현이나 설명형 문장에서 자주 사용되었기 때문으로 보입니다.
그렇다면 이제 이 데이터를 기반으로 실제 랜덤 문자열을 생성해보겠습니다.
모델이 커서 그런지 생각보다 오래걸리는 군요. 앞으로 3,4,5,6,7도 진행해야 하는데 걱정이 되는군요... ㅠㅠ

uni-gram으로 생성했을 때와 비교하면 확실히 문장에 가까운 형태로 생성되는 것을 확인할 수 있었습니다. 실제로 단어들의 연결 역시 이전보다 훨씬 자연스러워졌습니다. 하지만 생성된 결과를 자세히 읽어보면, 여전히 전체적으로 무슨 이야기를 하고 싶은 것인지는 이해하기 어려웠습니다. 즉, 문장 형태는 어느 정도 따라가기 시작했지만, 아직 의미와 흐름까지 연결되지는 못한 상태에 가까웠습니다.
3-gram


4-gram


4-gram까지 올리니 놀라운일이 발생하였습니다. 문장이 자연스럽게 이어졌습니다. 게다가 좀 짧은 문장들은 실제로 본문에서 찾아볼수 있었습니다. 예를 들어, '이번 사건은 나에게 가장 귀중한 교훈을 가르쳐 주었습니다'를 본문에서 조회를 해보면

실제로 생성된 결과 중 일부는 실제로 존재하는 문장이라는 것을 확인할 수 있었습니다. 다만 문장이 길어질수록 사전이나 원문 데이터에서 정확히 일치하는 결과는 거의 조회되지 않았습니다.
아마 문장이 길어질수록 단어 조합의 경우의 수가 급격하게 증가하기 때문으로 보입니다. 즉, 짧은 문장은 우연히 기존 데이터와 일치할 가능성이 있지만, 긴 문장은 새로운 조합으로 생성될 확률이 훨씬 높아지게 되는 것입니다.
5-gram
여기 부터는 원문을 더 많이 따라가는 구간으로 파일 크기와 희소성이 커지는 구간입니다.
희소성
가능한 조합의 수는 엄청나게 많지만, 실제로 관측된 조합은 극히 일부에 불과합니다.
예를 들어 2-gram은 다음과 같은 형태입니다.
나는 -> 갔다
나는 -> 보았다
나는 -> 말했다
즉, 앞 단어 1개만 참고하기 때문에 동일한 문맥이 여러 번 반복됩니다. 따라서 하나의 문맥에서 등장할 수 있는 다음 단어 후보 역시 비교적 다양하게 존재하게 됩니다. 하지만 5-gram은 이야기가 달라집니다.
그러나 은몽씨 나는 결코 -> 은몽씨가
이처럼 앞 단어 4개를 함께 참고하게 됩니다.
문제는 이런 긴 문맥 조합은 대부분 코퍼스 안에서 단 한 번만 등장하는 경우가 많다는 점입니다.
결국 모델은 다음과 같은 형태로 동작하게 됩니다.
그러나 은몽씨 나는 결코 -> 은몽씨가 (1회)
은몽씨 나는 결코 은몽씨가 -> 믿고 (1회)
나는 결코 은몽씨가 믿고 -> 있는것과 (1회)
이 상태가 되면 모델은 일반적인 패턴을 학습한다기보다, 원문 자체를 그대로 따라가는 방향에 가까워지게 됩니다.
정리해보면 다음과 같습니다.
- N이 작다
- 문맥 정보는 약함
- 하지만 후보 수는 많음
- 따라서 이상한 조합이 자주 등장함
- N이 크다
- 문맥 정보는 강함
- 하지만 후보 수는 매우 적음
- 결국 원문 암기에 가까워짐
즉, 5-gram에서 발생하는 희소성 문제(Sparsity)는 "앞 단어 4개가 동일한 경우" 자체가 거의 존재하지 않기 때문에, 다음 단어를 예측할 선택지가 급격하게 줄어드는 현상이라고 볼 수 있습니다.


솔직히 말씀드리자면, 차원을 계속 증가시켜 6-gram, 7-gram, 8-gram … 11-gram까지 테스트하는 것은 큰 의미가 없다고 느껴졌습니다. 물론 문맥 정확도 자체는 계속 상승할 수 있습니다. 하지만 그만큼 희소성 문제 역시 더욱 심해지게 됩니다. 결국, 모델은 새로운 패턴을 예측한다기보다, 기존 원문을 그대로 외우고 재현하는 방향에 가까워지게 됩니다. 즉, 문맥을 일반화해서 이해하는 것이 아니라 긴 문자열 패턴 자체를 기억하는 형태가 되어버리는 것입니다. 이렇게 된다면 언어 모델이 가져야 하는 "새로운 문장을 생성하고 예측하는 능력"은 오히려 약해질 수 있다고 느껴졌습니다. 결국 단순히 N을 높이는 것만으로는 좋은 언어 모델이 만들어지는 것은 아니라는 점을 확인할 수 있었습니다.
결론
저번 시간에는 사전 데이터를 기반으로 N-gram을 실습해보았습니다. 하지만 실제 LLM은 단순 사전 데이터가 아니라, 실제 문서와 문장 데이터를 기반으로 학습이 이루어진다는 점에서 차이가 존재하였습니다. 그래서 이번에는 문학 데이터를 직접 수집하여 N-gram을 다시 실습해보았습니다. uni-gram부터 시작하여 5-gram까지 차례대로 실험을 진행해보니, 초반에는 새로운 문자열을 생성해내는 모습을 확인할 수 있었습니다. 하지만 단어의 위치와 문맥이 뒤죽박죽이었기 때문에, 실제로 읽어보면 무슨 의미인지 이해하기 어려운 경우가 대부분이었습니다. 반대로 차원을 계속 증가시키자 훨씬 자연스러운 문장이 생성되기 시작하였습니다. 하지만 이번에는 또 다른 문제가 발생하였습니다. 문장이 자연스러워질수록 새로운 문장을 생성한다기보다, 원문 자체를 그대로 따라가는 형태에 가까워졌기 때문입니다. 결국 희소성 문제(Sparsity) 역시 함께 증가하게 되었고, 모델은 점점 더 긴 패턴을 기억하는 방향으로 동작하게 되었습니다. 개인적으로는 이 부분이 굉장히 아이러니하게 느껴졌습니다. 일반적으로 LLM이라면 새로운 문장을 창조할 것이라고 기대하게 됩니다. 하지만 N을 계속 증가시키게 되면 오히려 창의성과는 점점 거리가 멀어지고, 학습 데이터를 재현하는 방향에 가까워졌기 때문입니다. 그렇다고 해서 차원을 낮추게 되면 이번에는 문맥 자체를 거의 이해하지 못한 채, 의미를 알 수 없는 문장들만 생성하게 됩니다. 제가 느끼기에는 이것이 N-gram의 가장 큰 한계처럼 보였습니다. 그럼에도 불구하고 데이터를 기반으로 다음 데이터를 예측한다는 핵심 아이디어 자체는 굉장히 흥미로웠습니다. 실제로 이후 등장한 다양한 언어 모델들 역시, 결국은 이전 문맥을 기반으로 다음 데이터를 예측한다는 방향성 자체는 이어지고 있다고 느껴졌습니다.
출처
https://www.honglab.ai/courses/llmpt1
'개발 > LLM' 카테고리의 다른 글
| N-gram 이해해보기 (0) | 2026.05.14 |
|---|---|
| LLM이란 무엇일까? (1) | 2026.04.01 |