YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
- ๐ฐ๐ท Korean LLM Advanced v3
- ๐ ํ๋ก์ ํธ ๊ฐ๋ฐ๊ธฐ
- ์ ๊ท ๋ฒ์ V4 ์ถ์
- ๐ ๊ฐ์
- ๐ ์ฃผ์ ํน์ง
- ๐พ VRAM ์ฌ์ฉ๋ ๋น๊ต
- ๐ ๋น ๋ฅธ ์์
- ๐๏ธ ํ๋ก์ ํธ ๊ตฌ์กฐ
- ๐ ๋ฒ์ ํ์คํ ๋ฆฌ
- ๐ง ๊ธฐ์ ์คํ
- ๐ก ์ฌ์ฉ ์์
- โ FAQ (์์ฃผ ๋ฌป๋ ์ง๋ฌธ)
- Q1: ์ด ๋ชจ๋ธ์ ์ถ๋ก (inference)๋ง ํ๋ ค๋ฉด?
- Q2: ๋ด GPU ๋ฉ๋ชจ๋ฆฌ๊ฐ 9GB ๋ฏธ๋ง์ด๋ฉด?
- Q3: ํ์ต ์ค๋จ ํ ์ฌ๊ฐํ๋ ค๋ฉด?
- Q4: ๋ค๋ฅธ ํ๊ตญ์ด ๋ฐ์ดํฐ์ ์ ์ฌ์ฉํ ์ ์๋?
- Q5: ์๋์ฐ์์ ์คํํ๋ฉด ์๋ฌ๊ฐ ๋์
- Q6: VRAM ์ฌ์ฉ๋์ ๋ ์ค์ผ ์ ์๋?
- Q7: ์์ฑ๋ ํ ์คํธ ํ์ง์ด ๋ฎ์ผ๋ฉด?
- Q8: ๋ชจ๋ธ์ ONNX๋ ๋ค๋ฅธ ํ์์ผ๋ก ๋ณํํ๋ ค๋ฉด?
- Q9: ๊ฐ๋ฐ์๊ฐ ํ๋ฐํ ์ง์ํ๋?
- Q10: ์์ฉ ํ๋ก์ ํธ์ ์ฌ์ฉ ๊ฐ๋ฅํ๊ฐ?
- ๐ ๏ธ ํธ๋ฌ๋ธ์ํ
- ๐ ์ฑ๋ฅ ์ต์ ํ ํ
- ๐ ์ฐ๋ฝ์ฒ ๋ฐ ์ ๋ณด
- ๐ ๋ผ์ด์ ์ค
- ๐ค ๊ธฐ์ฌํ๊ธฐ
- ๐ ๊ฐ์ฌ์ ๋ง
- ๐ ์ฐธ๊ณ ์๋ฃ
- ๐ ํ๋ก์ ํธ ๊ฐ๋ฐ๊ธฐ
- ๐บ๐ธ Korean LLM Advanced v3
- ๐ Project Development Journey
- ๐ Overview
- ๐ Key Features
- ๐พ VRAM Usage Comparison
- ๐ Quick Start
- ๐๏ธ Project Structure
- ๐ Version History
- ๐ง Technology Stack
- ๐ก Usage Examples
- โ FAQ (Frequently Asked Questions)
- Q1: What if I only want to run inference?
- Q2: What if my GPU memory is less than 9GB?
- Q3: How do I resume training after interruption?
- Q4: Can I use a different Korean dataset?
- Q5: I get errors when running on Windows
- Q6: Can I reduce VRAM usage even more?
- Q7: Generated text quality is low
- Q8: How do I convert the model to ONNX or other formats?
- Q9: Is the developer actively supporting this?
- Q10: Can I use this in commercial projects?
- ๐ ๏ธ Troubleshooting
- ๐ Performance Optimization Tips
- ๐ Contact & Information
- ๐ License
- ๐ค Contributing
- ๐ Acknowledgments
- ๐ References
- ๐ Project Development Journey
๐ฐ๐ท Korean LLM Advanced v3
ํ๊ตญ์ด ํนํ ๋๊ท๋ชจ ์ธ์ด๋ชจ๋ธ - ํ์คํฌ๋์น ๊ตฌํ ๋ฐ ์์ํ ์ ์ฉ
๐ ํ๋ก์ ํธ ๊ฐ๋ฐ๊ธฐ
์ฒ์์๋ ๊ธฐ์ ๋ค์ ๋ฌด๋ฃ ํ๋๊ฐ ๋นก์ธ์ง๊ณ '๋ฐ์ด๋ธ ์ฝ๋ฉ'์ ํ๊ธฐ์๋ ํ๋์ ํ๊ณ๊ฐ ์ฐพ์์์ต๋๋ค. Ollama๋ฅผ ํ์ฉํด ๋ก์ปฌ๋ก ๋๋ ค๋ณด๊ธฐ๋ ํ์ง๋ง, ๋ชจ๋ธ์ด ๋๋ฌด ๋ฌด๊ฑฐ์ ์ปดํจํฐ๊ฐ ๋ฒ๊ฑฐ์ํ์ฃ . ๊ทธ๋ ๋ฌธ๋ '์ด๋ด ๋ฐ์ ๋ด๊ฐ ์ง์ ๋ง๋ค์ด๋ณผ๊น?'๋ผ๋ ์๊ฐ์ด ๋ค์์ต๋๋ค. ํ์ง๋ง ์ ๋ ์คํ๊ต 2ํ๋ ์ด์๊ณ ์ธ๊ณต์ง๋ฅ ๋ชจ๋ธ์ ๋ํด ์๋ ๊ฒ์ด๋ผ๊ณค ๋ชจ๋ธ ํฌ๊ธฐ๋ฅผ ๋ํ๋ด๋ 'B(Billion)'๋ผ๋ ๊ฐ๋ ์ด ์ ๋ถ์๊ณ , ๊ธฐ๋ณธ์ด ์๋๋ผ ๊ณ ๊ธ๊ฐ๋ ์ ์งค ์ ์์์ฃ . ๊ฒฐ๊ตญ ํ์์ฒ๋ผ ChatGPT์ ๋ค์ด๊ฐ โ๋ ๋ ์์ ์ธ ํ๊ตญ์ด LLM ๋ชจ๋ธ ๋ง๋ค๋!โ๋ผ๋ ํ๋ง๋๋ฅผ ๋์ง๋ฉฐ ๋ฌด๋ชจํ ๋์ ์ ์์ํ์ต๋๋ค. GPT์ ๋์์ ๋ฐ์ผ๋ฉด์๋ ํ๊ณ๋ ๊ณ์ ์ฐพ์์์ต๋๋ค. ์ฒ์์๋ ๊ทธ์ GPT๊ฐ ์ค ์ฝ๋ ์กฐ๊ฐ๋ค์ ๋ชจ์ ์ฐจ์ ์ค๋ฅ(Dimension Error)๊ฐ ๋์ง ์๊ธฐ๋ง์ ๋ฐ๋ผ๋ฉฐ ์ด์ฌํ ๋๋ ค๋ณผ ๋ฟ์ด์์ต๋๋ค. ๋ฐ์ดํฐ๋ฅผ ์์งํ๊ณ ์ ์ ํ๋ฉฐ ์จ์ข ์ผ ์ปดํจํฐ ๋ชจ๋ํฐ๋ง ๋ฐ๋ผ๋ณด์์ต๋๋ค. ๊ทธ๋ ๊ฒ ํ์ด๋ ์ ์ฒซ ์ํ(v1 ์ด์ ๋ฒ์ )์ ์ํคํผ๋์ ๋ฐ์ดํฐ๋ก ํ์ตํ, ๊ณ ์ 50M(5์ฒ๋ง ํ๋ผ๋ฏธํฐ) ํฌ๊ธฐ์ ๊ทน์ํ ๋ชจ๋ธ์ด์์ต๋๋ค. ์ง๊ธ์ ๋จ์์์ง ์์ง๋ง์. ๋น๋ก ๋ํ๋ ๋ถ๊ฐ๋ฅํ์ง๋ง, ์ด๋ ์ ๋ ๋ฌธ๋ฒ์ ๋ง๋ ๋ฌธ์ฅ์ ๊ตฌ์ํ๋ ๋ชจ์ต์ ๋ณด์์ต๋๋ค. ๊ทธ ์์ ์ฑ๊ณต์ด ๋๋ฌด ๊ธฐ๋ป์ ์ด๋๋ถํฐ ๋ณธ๊ฒฉ์ ์ผ๋ก '์ฑํ ํ ๋ชจ๋ธ'์ ๋ง๋๋ ๋ฐ๋ง ๋ชฐ์ ํ์ต๋๋ค. ๊ทธ๋ ๊ฒ v1์ ์ต์ข ๋ฒ์ ์ธ 541M ํฌ๊ธฐ์ ๋ชจ๋ธ๊น์ง ๋ฐ์ ์์ผฐ์ต๋๋ค. ๋ช ๊ฐ์ง ๋ฒ๊ทธ๊ฐ ๋ฐ๊ฒฌ๋์์ง๋ง, ์ฐ์ ๋ฒ๊ทธ๋ฅผ ํด๊ฒฐํ ๋ค ๊ณง๋ฐ๋ก ๋ชจ๋ธ์ ์ฒด๊ธ์ ํค์ฐ๊ธฐ๋ก ๊ฒฐ์ฌํ์ต๋๋ค. ์ค๋ฅ๋ค์ ์์ ํ๊ณ , ๋ชจ๋ธ ํฌ๊ธฐ๋ฅผ 2๋ฐฐ๊ฐ๋ ํค์ ๋๋์ด 1.09B(10์ต 9์ฒ๋ง ํ๋ผ๋ฏธํฐ) ํฌ๊ธฐ์ ๋ชจ๋ธ์ ๊ตฌ์ถํ์ต๋๋ค. ๋ฐฉํ ๊ธฐ๊ฐ ๋ด๋ด ์๊ฐ์ด ๋ ๋๋ง๋ค ์ปดํจํฐ๋ฅผ ์ผ๊ณ ํ์ต์ ๋๋ ธ์ต๋๋ค. ๊ทธ๋ ๊ฒ ๋์ด ๋ณด์ด์ง ์๋ ํ์ต์ด ์ด๋๋ง 44,000 ์คํ ์ ๋๋ฌํ์ต๋๋ค. ์ค๋ ๋ ๋ง์์ผ๋ก ํ ์คํธ๋ฅผ ์ํด ์ฑํ ์ฐฝ์ "์๋ ?"์ด๋ผ๊ณ ์ ๋ ฅํ์ต๋๋ค.
"์๋ ํ์ธ์! ์ค๋์ ๋ฌด์์ ๋์๋๋ฆด๊น์?"
๋ชจ๋ธ์ด ์ฌ๋ฐ๋ฅธ ๋ต๋ณ์ ํ๋ฉด์ ๋์ด ๊ทธ ์๊ฐ, ๋ง๋ก ํํํ ์ ์์ ๋งํผ ๊ธฐ๋ปค์ต๋๋ค. ํ์ง๋ง ๊ธฐ์จ๋ ์ ์, ๋ค๋ฅธ ์ง๋ฌธ์ ๋์ง์ ์ ํ ์๋ฑํ ๋๋ต์ ์์๋ด๊ธฐ ์์ํ์ต๋๋ค. AI์ ํจ๊ป ๋ฐค์ ์ฝ๋๋ฅผ ๋ถ์ํ ๊ฒฐ๊ณผ, ๋ชจ๋ธ์ด ์ฌ์ฉ์์ ์ง์์ฌํญ์ ๋ฌด์ํด ๋ฒ๋ฆฌ๋ ์น๋ช ์ ์ธ ๋ฒ๊ทธ๊ฐ ๋ฐ์ํ ๊ฒ์ด์์ต๋๋ค. ๊ฐ์ด์ด ์ํ ์ง๋ง, ๋ ์๋ฒฝํ ๋ชจ๋ธ์ ์ํด ์ง๊ธ๊น์ง ํ์ตํ ๊ฒฐ๊ณผ๋ฌผ์ ๊ณผ๊ฐํ ํ๊ธฐํ์ต๋๋ค. ๋๋ดํ์ง ์๊ณ v2์ ๋ฒ๊ทธ๋ฅผ ์์ ํ ํด๊ฒฐํ ๋ค, ๋ค์ ๋ฌธ์ ์ ๋์ ํ์ต๋๋ค. 1B ์ฒด๊ธ์ ๋ชจ๋ธ์ VRAM์ ๋ฌด๋ ค 23GB๋ ์ฐจ์งํ์ฌ ์ผ๋ฐ์ ์ธ ํ๊ฒฝ์์ ๋๋ฆฌ๊ธฐ ๋๋ฌด ๋ฌด๊ฑฐ์ ๊ธฐ ๋๋ฌธ์ ๋๋ค. ์ด๋ฅผ 10GB ์ดํ๋ก ์ค์ฌ๋ณด๊ฒ ๋ค๋ ๋ชฉํ๋ฅผ ์ธ์ ๊ณ , ๋ง์นจ๋ด v3์์ ์์ํ์ ์ฑ๊ณตํ์ต๋๋ค. ๋ฐฉํ์ด ๋๋ฌ๋ค ๋ณด๋ ์ ๊ฐ ํ์ตํ๊ธฐ์๋ ์ด๋ ต์ต๋๋ค. v2 ๋ฒ๊ทธ ์ดํ ํ์ตํ ๊ฑด ์์ ์๊ณ ์๊ฐ์ด ๋ ๋ ๋ค์ ํ์ตํด๋ณด๊ฒ ์ต๋๋ค. ์ด ํ๋ก์ ํธ๋ ์ค์ง "๋ด ์์ผ๋ก ์ง์ LLM์ ๋ง๋ค๊ณ ์ถ๋ค"๋ ๊ณ ์ง ํ๋๋ก ์์ฑํด ๋ธ, ์ ์ธ์ ์ต๊ณ ์ ์ํ์ ๋๋ค. ์ด ๋ชจ๋ธ์ ์ ์ฌ์ฉํ์๊ณ , ๋ง์์ ๋์ จ๋ค๋ฉด ์คํ(โญ) ๋ฒํผ ํ ๋ฒ์ฉ ๊ผญ ๋๋ฌ์ฃผ์ธ์! ๊ฐ์ฌํฉ๋๋ค!
์ฒ์๋ถํฐ ๋๊น์ง ํ๊ตญ์ด๋ก ํ์ต๋ 1.09B ํ๋ผ๋ฏธํฐ LLM์ผ๋ก, VRAM ์ต์ ํ ๊ธฐ๋ฒ์ ์ ๊ทน ํ์ฉํ์ต๋๋ค.
๐ ์ฃผ์ ํน์ง โข ๐ ๋น ๋ฅธ ์์ โข ๐พ ๊ธฐ์ ์คํ โข ๐ ๋ฒ์ ํ์คํ ๋ฆฌ
์ ๊ท ๋ฒ์ V4 ์ถ์
๐ ๊ฐ์
Korean LLM Advanced v3๋ ํ๊ตญ์ด ์์ฐ์ด ์ฒ๋ฆฌ์ ์ต์ ํ๋ ๊ฒฝ๋ ๋๊ท๋ชจ ์ธ์ด๋ชจ๋ธ์ ๋๋ค. ์ ํ๋ GPU ๋ฉ๋ชจ๋ฆฌ ํ๊ฒฝ์์๋ ํจ์จ์ ์ผ๋ก ํ์ตํ๊ณ ์ถ๋ก ํ ์ ์๋๋ก ์ค๊ณ๋์์ต๋๋ค.
ํต์ฌ ๋ชฉํ
- โ ํ๊ตญ์ด ํ ์คํธ ์์ฑ ๋ฐ ์ดํด ๋ฅ๋ ฅ
- โ VRAM ํจ์จ์ฑ (9GB ๊ธฐ์ค)
- โ ๋น ๋ฅธ ํ์ต ์๋
- โ ์ฌ์ด ๋ฐฐํฌ ๋ฐ ํ์ฉ
๐ ์ฃผ์ ํน์ง
๐ฏ ๋ชจ๋ธ ๊ตฌ์กฐ
| ํญ๋ชฉ | ์ค๋ช |
|---|---|
| ๋ชจ๋ธ ํฌ๊ธฐ | 1.09B ํ๋ผ๋ฏธํฐ |
| ์๋์ธต ํฌ๊ธฐ | 1,920์ฐจ์ |
| ๋ ์ด์ด ์ | 20๊ฐ |
| ์ดํ ์ ํค๋ | 10๊ฐ |
| ์ต๋ ์ํ์ค ๊ธธ์ด | 2,048 ํ ํฐ |
| ์ดํ์ง ํฌ๊ธฐ | ๋์ (ํ ํฌ๋์ด์ ๊ธฐ์ค) |
๐ง ์ต์ ํ ๊ธฐ๋ฒ
1๏ธโฃ BF16 ์๋ ํผํฉ ์ ๋ฐ๋
ํ์ค FP32์ ๋น๊ตํด ์ฝ 50% VRAM ์ ์ฝ
- ๋ฉ๋ชจ๋ฆฌ ํจ์จ: โฌ๏ธ 12GB โ 6GB
- ์ฐ์ฐ ์๋: โก๏ธ ๋๋ฑ ๋๋ ํฅ์
2๏ธโฃ 8๋นํธ AdamW ์ตํฐ๋ง์ด์ (bitsandbytes)
์ตํฐ๋ง์ด์ ์ํ ๋ฉ๋ชจ๋ฆฌ 75% ๊ฐ์
- ํ์ค AdamW: ~2.2GB (1B ๋ชจ๋ธ)
- 8-bit AdamW: ~0.55GB (1B ๋ชจ๋ธ)
3๏ธโฃ ์์ํ (Quantization) โญ
๋ชจ๋ธ ๊ฐ์ค์น ๋์ ์์ํ ์ง์
- INT8 ์์ํ: ํฌ๊ธฐ 4๋ฐฐ ๊ฐ์
- ์ถ๋ก ์๋: 1.5~2๋ฐฐ ํฅ์
4๏ธโฃ ๊ทธ๋๋์ธํธ ๋์ (Gradient Accumulation)
ํจ๊ณผ์ ๋ฐฐ์น ํฌ๊ธฐ ์ฆ๋
- ์ค์ : batch_size=2, accumulation_steps=8
- ํจ๊ณผ: ๋ฐฐ์น ํฌ๊ธฐ 16 ํจ๊ณผ
5๏ธโฃ ๊ทธ๋๋์ธํธ ์ฒดํฌํฌ์ธํ
ํ์ฑํ(Activation) ๋ฉ๋ชจ๋ฆฌ ๊ฐ์
- ์ฌ๊ณ์ฐ ๋น์ฉ: ~30% ์๋ ์ ํ
- ๋ฉ๋ชจ๋ฆฌ ์ ์ฝ: 30~40%
๐พ VRAM ์ฌ์ฉ๋ ๋น๊ต
| ๋ฒ์ | ํ๋ผ๋ฏธํฐ | VRAM ์ฌ์ฉ๋ | ์ต์ ํ ๊ธฐ๋ฒ |
|---|---|---|---|
| v1 | 541M | ~11GB | ๊ธฐ๋ณธ FP32 |
| v2 | ~1.1B | ~23GB | BF16 + Gradient Checkpoint |
| v3 | 1.09B | ~9GB โจ | BF16 + 8๋นํธ ์ตํฐ๋ง์ด์ + ์์ํ |
v3์ v2 ๋๋น VRAM 60% ๊ฐ์, v1๋ณด๋ค๋ ๋ชจ๋ธ ํฌ๊ธฐ 2๋ฐฐ ํ๋
๐ ๋น ๋ฅธ ์์
๐ ์ฌ์ ์๊ตฌ์ฌํญ
Python 3.9 ์ด์
CUDA 11.8 ์ด์ (GPU ํ์)
GPU ๋ฉ๋ชจ๋ฆฌ: ์ต์ 9GB ๊ถ์ฅ
1๏ธโฃ ์ค์น
# ์ ์ฅ์ ํด๋ก
git clone https://github.com/seoan1024/korean-llm-v3.git
cd korean-llm-v3
# ํ์ ํจํค์ง ์ค์น
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
pip install transformers datasets tqdm pandas matplotlib
# ์์ํ ์ง์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (์ ํ)
pip install bitsandbytes
2๏ธโฃ ๋ฐ์ดํฐ์ ์ค๋น
์ฝ๋๊ฐ ์๋์ผ๋ก ๋ค์ ๋ฐ์ดํฐ์ ์ ๋ค์ด๋ก๋ํฉ๋๋ค:
- ๐น
nlpai-lab/kullm-v2- ํ๊ตญ์ด ๋ช ๋ น์ด ํ๋ ๋ฐ์ดํฐ - ๐น
beomi/KoAlpaca-v1.1a- ํ๊ตญ์ ์ํ์นด ๋ฐ์ดํฐ์
# ๋ฐ์ดํฐ์
์ด ์๋ ๋ค์ด๋ก๋๋๋ฏ๋ก ๋ณ๋ ์์
๋ถํ์
# ์บ์ ๋๋ ํ ๋ฆฌ: ./datasets/cache/
3๏ธโฃ ํ์ต ์คํ
# ๊ธฐ๋ณธ ์ค์ ์ผ๋ก ํ์ต ์์
python korean_llm_advanced_v3.py
# ๋๋ ์ปค์คํ
์ค์ ์ผ๋ก ์คํ
python korean_llm_advanced_v3.py \
--batch-size 2 \
--max-steps 50000 \
--learning-rate 5e-5
4๏ธโฃ ๋ชจ๋ํฐ๋ง
ํ์ต ์ค ์๋์ผ๋ก GUI ๋ชจ๋ํฐ๋ง ์ฐฝ์ด ์ด๋ฆฝ๋๋ค:
- ๐ ์ค์๊ฐ ์์ค๊ฐ(Loss) ๊ทธ๋ํ
- ๐ฌ ์ธํฐ๋ํฐ๋ธ ์ฑํ (์์ฑ ํ ์คํธ)
- ๐ ๋ก๊ทธ ๋ทฐ์ด
๐๏ธ ํ๋ก์ ํธ ๊ตฌ์กฐ
korean-llm-v3/
โโโ korean_llm_advanced_v3.py # ๋ฉ์ธ ํ์ต ์คํฌ๋ฆฝํธ
โโโ README.md # ์ด ํ์ผ
โโโ LICENSE # GPL-3.0 ๋ผ์ด์ ์ค
โ
โโโ checkpoints/ # ์ ์ฅ๋ ๋ชจ๋ธ ์ฒดํฌํฌ์ธํธ
โ โโโ korean_llm_*.pth
โ
โโโ datasets/ # ๋ฐ์ดํฐ์
์บ์
โ โโโ cache/ # ๋ค์ด๋ก๋๋ ๋ฐ์ดํฐ์
โ โโโ datasets_manifest.json # ๋ฉํ๋ฐ์ดํฐ
โ
โโโ logs/ # ํ์ต ๋ก๊ทธ ๋ฐ ๊ทธ๋ํ
โโโ training.log # ์์ธ ๋ก๊ทธ
โโโ loss_history.json # ์์ค๊ฐ ๊ธฐ๋ก
๐ ๋ฒ์ ํ์คํ ๋ฆฌ
v1 (์ด๊ธฐ ๋ฒ์ )
- 541M ํ๋ผ๋ฏธํฐ ๋ชจ๋ธ
- VRAM ์ฌ์ฉ๋: ~11GB
- ๊ธฐ๋ณธ FP32 ํ์ต
v2 (์ต์ ํ v1)
- 1.1B ํ๋ผ๋ฏธํฐ๋ก ํ๋
- VRAM ์ฌ์ฉ๋: ~23GB (์ด๊ธฐ 1.2๋ฐฐ ์ฆ๊ฐ)
- BF16 + Gradient Checkpoint ์ ์ฉ
v3 (ํ์ฌ) โญ
- 1.09B ํ๋ผ๋ฏธํฐ (v2 ์์ค)
- VRAM ์ฌ์ฉ๋: ~9GB (v2 ๋๋น 60% ๊ฐ์!)
- ์ฃผ์ ๊ฐ์ ์ฌํญ:
- 8๋นํธ AdamW ์ตํฐ๋ง์ด์
- ๋์ ์์ํ ์ง์
- ํฅ์๋ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ
- ๋ ๋น ๋ฅธ ํ์ต ์๋
๐ง ๊ธฐ์ ์คํ
ํต์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ | ๋ฒ์ | ์ฉ๋ |
|---|---|---|
| PyTorch | 2.0+ | ๋ฅ๋ฌ๋ ํ๋ ์์ํฌ |
| Transformers | 4.30+ | ํ ํฌ๋์ด์ ๋ฐ ์ ํธ๋ฆฌํฐ |
| Datasets | 2.10+ | ํ๊ตญ์ด ๋ฐ์ดํฐ์ ๋ก๋ |
| bitsandbytes | 0.40+ | 8๋นํธ ์์ํ ์ต์ ํ |
| tqdm | 4.60+ | ์งํ๋ฅ ํ์ |
์ ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ | ์ฉ๋ |
|---|---|
| matplotlib | ์์ค๊ฐ ๊ทธ๋ํ ์๊ฐํ |
| tkinter | GUI ๋ชจ๋ํฐ๋ง (๋ด์ฅ) |
| pandas | ๋ฐ์ดํฐ ์ฒ๋ฆฌ |
๐ก ์ฌ์ฉ ์์
๋ชจ๋ธ ๋ก๋ ๋ฐ ํ ์คํธ ์์ฑ
import os, argparse
from pathlib import Path
from typing import Optional, Tuple, List
import torch, torch.nn as nn, torch.nn.functional as F
from transformers import AutoTokenizer
class RMSNorm(nn.Module):
def __init__(self, dim, eps=1e-6):
super().__init__()
self.eps = eps
self.weight = nn.Parameter(torch.ones(dim))
def forward(self, x):
return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps) * self.weight
def precompute_freqs_cis(head_dim: int, end: int, theta: float = 10000.0) -> Tuple[torch.Tensor, torch.Tensor]:
freqs = 1.0 / (theta ** (torch.arange(0, head_dim, 2)[:head_dim // 2].float() / head_dim))
t = torch.arange(end, dtype=freqs.dtype)
freqs = torch.outer(t, freqs)
return torch.cos(freqs), torch.sin(freqs)
def apply_rotary_emb(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
head_dim_2 = cos.shape[-1]
head_dim = head_dim_2 * 2
x1 = x[..., :head_dim // 2]
x2 = x[..., head_dim // 2:]
cos = cos.unsqueeze(0).unsqueeze(0)
sin = sin.unsqueeze(0).unsqueeze(0)
return torch.cat([x1 * cos - x2 * sin, x1 * sin + x2 * cos], dim=-1)
class SwiGLU(nn.Module):
def __init__(self, dim: int, hidden_dim: int):
super().__init__()
self.w1 = nn.Linear(dim, hidden_dim, bias=False)
self.w2 = nn.Linear(hidden_dim, dim, bias=False)
self.w3 = nn.Linear(dim, hidden_dim, bias=False)
def forward(self, x):
return self.w2(F.silu(self.w1(x)) * self.w3(x))
class Attention(nn.Module):
def __init__(self, dim: int, n_heads: int):
super().__init__()
assert dim % n_heads == 0
self.n_heads = n_heads
self.head_dim = dim // n_heads
self.wq = nn.Linear(dim, dim, bias=False)
self.wk = nn.Linear(dim, dim, bias=False)
self.wv = nn.Linear(dim, dim, bias=False)
self.wo = nn.Linear(dim, dim, bias=False)
def forward(self, x: torch.Tensor, f_cos: torch.Tensor, f_sin: torch.Tensor, kv_cache: Optional[Tuple[torch.Tensor, torch.Tensor]] = None):
b, s, d = x.shape
q = self.wq(x).view(b, s, self.n_heads, self.head_dim).transpose(1, 2)
k = self.wk(x).view(b, s, self.n_heads, self.head_dim).transpose(1, 2)
v = self.wv(x).view(b, s, self.n_heads, self.head_dim).transpose(1, 2)
q = apply_rotary_emb(q, f_cos, f_sin)
k = apply_rotary_emb(k, f_cos, f_sin)
if kv_cache is not None:
pk, pv = kv_cache
k = torch.cat([pk, k], dim=2)
v = torch.cat([pv, v], dim=2)
new_kv = (k.detach(), v.detach())
out = F.scaled_dot_product_attention(q, k, v, attn_mask=None, is_causal=(s > 1))
out = out.transpose(1, 2).contiguous().view(b, s, d)
return self.wo(out), new_kv
class TransformerBlock(nn.Module):
def __init__(self, dim: int, n_heads: int, hidden_dim: int):
super().__init__()
self.attention = Attention(dim, n_heads)
self.feed_forward = SwiGLU(dim, hidden_dim)
self.attention_norm = RMSNorm(dim)
self.ffn_norm = RMSNorm(dim)
def forward(self, x, f_cos, f_sin, kv_cache=None):
normed_x = self.attention_norm(x)
h, new_kv = self.attention(normed_x, f_cos, f_sin, kv_cache=kv_cache)
x = x + h
x = x + self.feed_forward(self.ffn_norm(x))
return x, new_kv
class KoreanLLM(nn.Module):
def __init__(self, vocab_size: int, pad_token_id: int, dim: int = 1920, n_layers: int = 20, n_heads: int = 10, max_seq_len: int = 512):
super().__init__()
self.vocab_size = vocab_size
self.pad_token_id = pad_token_id
self.dim = dim
self.n_heads = n_heads
self.head_dim = dim // n_heads
self.max_seq_len = max_seq_len
self.embed = nn.Embedding(vocab_size, dim)
self.layers = nn.ModuleList([TransformerBlock(dim, n_heads, int(dim * 2.5)) for _ in range(n_layers)])
self.norm = RMSNorm(dim)
self.output = nn.Linear(dim, vocab_size, bias=False)
self.output.weight = self.embed.weight
f_cos, f_sin = precompute_freqs_cis(self.head_dim, max_seq_len * 2)
self.register_buffer("f_cos", f_cos)
self.register_buffer("f_sin", f_sin)
def _get_freqs(self, f, start, length):
end = start + length
if end > f.shape[0]:
raise ValueError(f"ํ์ฌ ์ปจํ
์คํธ๊ฐ ๋๋ฌด ๊น๋๋ค: {end} > {f.shape[0]}")
return f[start:end]
@torch.no_grad()
def forward(self, tokens: torch.Tensor, kv_caches=None):
b, s = tokens.shape
x = self.embed(tokens)
start_pos = 0
if kv_caches is not None and len(kv_caches) > 0 and kv_caches[0][0] is not None:
start_pos = kv_caches[0][0].shape[2]
f_cos = self._get_freqs(self.f_cos, start_pos, s)
f_sin = self._get_freqs(self.f_sin, start_pos, s)
new_kv_caches = []
for i, layer in enumerate(self.layers):
cache = kv_caches[i] if kv_caches is not None else None
x, kv = layer(x, f_cos, f_sin, kv_cache=cache)
new_kv_caches.append(kv)
x = self.norm(x)
logits = self.output(x)
return logits, new_kv_caches
def find_latest_checkpoint(checkpoint_dir="checkpoints"):
checkpoint_dir = Path(checkpoint_dir)
if not checkpoint_dir.exists(): return None
files = list(checkpoint_dir.glob("korean_llm_*.pth"))
if not files: return None
def step_number(path):
try: return int(path.stem.split("_")[-1])
except ValueError: return -1
files.sort(key=step_number)
return files[-1]
def load_checkpoint(model, checkpoint_path, device):
print(f"๐ฆ ์ฒดํฌํฌ์ธํธ ๋ก๋ฉ:\n {checkpoint_path}")
checkpoint = torch.load(checkpoint_path, map_location=device)
if "model_state_dict" in checkpoint:
state_dict = checkpoint["model_state_dict"]
step = checkpoint.get("step", "?")
else:
state_dict = checkpoint
step = "?"
model.load_state_dict(state_dict, strict=True)
print(f"โ
๋ชจ๋ธ ๋ก๋ ์๋ฃ\n ํ์ต step: {step}")
return step
@torch.no_grad()
def generate(model, tokenizer, prompt, device, max_tokens=256, temperature=0.6, top_k=40, top_p=0.95, repetition_penalty=1.15, context_limit=512):
model.eval()
prompt_text = f"### ์ง๋ฌธ: {prompt}\n### ์๋ต:"
tokens = tokenizer.encode(prompt_text, add_special_tokens=False, return_tensors="pt").to(device)
if tokens.shape[1] >= context_limit:
tokens = tokens[:, -context_limit + 1:]
output_tokens = tokens
kv_caches = None
eos_id = tokenizer.eos_token_id
for _ in range(max_tokens):
input_tokens = output_tokens if kv_caches is None else output_tokens[:, -1:]
logits, kv_caches = model(input_tokens, kv_caches=kv_caches)
next_logits = logits[:, -1, :]
temperature = max(float(temperature), 1e-5)
next_logits = next_logits / temperature
if repetition_penalty != 1.0:
used_tokens = set(output_tokens[0].tolist())
for token_id in used_tokens:
if token_id < next_logits.shape[-1]:
if next_logits[0, token_id] < 0:
next_logits[0, token_id] *= repetition_penalty
else:
next_logits[0, token_id] /= repetition_penalty
if top_k > 0:
k = min(int(top_k), next_logits.shape[-1])
threshold = torch.topk(next_logits, k).values[..., -1, None]
next_logits = torch.where(next_logits < threshold, torch.full_like(next_logits, float("-inf")), next_logits)
probs = F.softmax(next_logits, dim=-1)
if 0 < top_p < 1.0:
sorted_probs, sorted_indices = torch.sort(probs, descending=True, dim=-1)
cumulative = torch.cumsum(sorted_probs, dim=-1)
remove = cumulative > top_p
remove[..., 0] = False
indices_to_remove = torch.zeros_like(probs, dtype=torch.bool)
indices_to_remove.scatter_(-1, sorted_indices, remove)
probs = probs.masked_fill(indices_to_remove, 0.0)
probs = probs / (probs.sum(dim=-1, keepdim=True) + 1e-10)
if not torch.isfinite(probs).all():
next_token = torch.argmax(next_logits, dim=-1, keepdim=True)
else:
next_token = torch.multinomial(probs, num_samples=1)
output_tokens = torch.cat([output_tokens, next_token], dim=1)
if eos_id is not None and next_token.item() == eos_id: break
if output_tokens.shape[1] >= context_limit: break
generated_text = tokenizer.decode(output_tokens[0], skip_special_tokens=True)
if "### ์๋ต:" in generated_text:
response = generated_text.split("### ์๋ต:", 1)[1]
else:
response = generated_text
if "### ์ง๋ฌธ:" in response:
response = response.split("### ์ง๋ฌธ:", 1)[0]
return response.strip()
def main():
parser = argparse.ArgumentParser(description="KoreanLLM ์ฒดํฌํฌ์ธํธ ์ฑํ
")
parser.add_argument("--checkpoint", type=str, default="latest", help="์ฒดํฌํฌ์ธํธ ๊ฒฝ๋ก ๋๋ latest")
parser.add_argument("--tokenizer", type=str, default="beomi/Llama-3-Open-Ko-8B")
parser.add_argument("--max-tokens", type=int, default=256)
parser.add_argument("--temperature", type=float, default=0.6)
parser.add_argument("--top-k", type=int, default=40)
parser.add_argument("--top-p", type=float, default=0.95)
parser.add_argument("--repetition-penalty", type=float, default=1.15)
parser.add_argument("--cpu", action="store_true", help="๊ฐ์ ๋ก CPU ์ฌ์ฉ")
args = parser.parse_args()
if args.cpu:
device = torch.device("cpu")
else:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("=" * 65)
print("๐ฐ๐ท KoreanLLM v3 Checkpoint Chat")
print("=" * 65)
print(f"๐ฅ๏ธ Device: {device}")
if device.type == "cuda":
print(f"๐ฎ GPU: {torch.cuda.get_device_name(0)}")
else:
print("โ ๏ธ CPU ๋ชจ๋์
๋๋ค. 1920-dim / 20-layer ๋ชจ๋ธ์ด๋ผ ์์ฑ์ด ๋๋ฆด ์ ์์ต๋๋ค.")
print("\n๐ค ํ ํฌ๋์ด์ ๋ก๋ฉ...")
tokenizer = AutoTokenizer.from_pretrained(args.tokenizer, clean_up_tokenization_spaces=False)
if tokenizer.pad_token is None or tokenizer.pad_token_id == tokenizer.eos_token_id:
tokenizer.add_special_tokens({"pad_token": "<|pad|>"})
vocab_size = len(tokenizer)
pad_token_id = tokenizer.pad_token_id
print(f" vocab_size = {vocab_size}")
print(f" eos_token_id = {tokenizer.eos_token_id}")
print(f" pad_token_id = {pad_token_id}")
checkpoint_path = args.checkpoint
if checkpoint_path.lower() == "latest":
checkpoint_path = find_latest_checkpoint()
if checkpoint_path is None:
print("\nโ checkpoints ํด๋์์ ์ฒดํฌํฌ์ธํธ๋ฅผ ์ฐพ์ง ๋ชปํ์ต๋๋ค.")
print("์: python chat_korean_llm.py --checkpoint checkpoints/korean_llm_50000.pth")
return
checkpoint_path = Path(checkpoint_path)
if not checkpoint_path.exists():
print(f"\nโ ์ฒดํฌํฌ์ธํธ๊ฐ ์์ต๋๋ค:\n {checkpoint_path}")
return
print("\n๐ง ๋ชจ๋ธ ์์ฑ ์ค...")
model_config = dict(vocab_size=vocab_size, pad_token_id=pad_token_id, dim=1920, n_layers=20, n_heads=10, max_seq_len=512)
dtype = torch.bfloat16 if device.type == "cuda" else torch.float32
model = KoreanLLM(**model_config).to(device)
if device.type == "cuda":
model = model.to(dtype=dtype)
try:
step = load_checkpoint(model, checkpoint_path, device)
except RuntimeError as e:
print("\nโ ์ฒดํฌํฌ์ธํธ์ ํ์ฌ ๋ชจ๋ธ ๊ตฌ์กฐ๊ฐ ๋ง์ง ์์ต๋๋ค.")
print(" ํนํ tokenizer์ vocab_size / pad_token ์ค์ ์ ํ์ธํ์ธ์.")
print(f"\n์์ธ ์ค๋ฅ:\n{e}")
return
model.eval()
params = sum(p.numel() for p in model.parameters())
print(f"๐ Parameters: {params / 1e6:.1f}M")
print("\n" + "=" * 65)
print("๐ฌ ์ฑํ
์์")
print(" /exit ์ข
๋ฃ")
print(" /clear ๋ํ ์
๋ ฅ ๊ธฐ๋ก ์ด๊ธฐํ")
print(" /info ๋ชจ๋ธ ์ ๋ณด")
print("=" * 65)
while True:
try:
prompt = input("\n๋ > ").strip()
except (KeyboardInterrupt, EOFError):
print("\n\n๐ ์ข
๋ฃํฉ๋๋ค.")
break
if not prompt: continue
if prompt.lower() in {"/exit", "/quit", "exit", "quit"}:
print("๐ ์ข
๋ฃํฉ๋๋ค.")
break
if prompt == "/clear":
print("๐งน ์
๋ ฅ ์ํ๋ฅผ ์ด๊ธฐํํ์ต๋๋ค.")
continue
if prompt == "/info":
print(f"\n์ฒดํฌํฌ์ธํธ : {checkpoint_path}\nํ์ต step : {step}\nDevice : {device}\nParameters : {params / 1e6:.1f}M\nTemperature : {args.temperature}\nTop-k : {args.top_k}\nTop-p : {args.top_p}")
continue
print("\n๋ชจ๋ธ > ", end="", flush=True)
try:
response = generate(model=model, tokenizer=tokenizer, prompt=prompt, device=device, max_tokens=args.max_tokens, temperature=args.temperature, top_k=args.top_k, top_p=args.top_p, repetition_penalty=args.repetition_penalty, context_limit=512)
print(response)
except torch.cuda.OutOfMemoryError:
print("\nโ CUDA ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ถ์กฑํฉ๋๋ค.")
print(" --max-tokens ๊ฐ์ ๋ฎ์ถ๊ฑฐ๋ ๋ค๋ฅธ GPU์์ ์คํํด๋ณด์ธ์.")
except Exception as e:
print(f"\nโ ์์ฑ ์ค๋ฅ: {type(e).__name__}: {e}")
if __name__ == "__main__":
main()
์ปค์คํ ํ์ต ์ค์
from korean_llm_advanced_v3 import TrainingConfig, main
config = TrainingConfig(
batch_size=4, # ๋ฐฐ์น ํฌ๊ธฐ
accumulation_steps=4, # ๊ทธ๋๋์ธํธ ๋์ ์คํ
max_steps=100000, # ์ต๋ ํ์ต ์คํ
warmup_steps=1000, # ์๋ฐ์
์คํ
learning_rate=3e-5, # ํ์ต๋ฅ
eval_interval=5000, # ํ๊ฐ ๊ฐ๊ฒฉ
use_bfloat16=True, # BF16 ์ฌ์ฉ ์ฌ๋ถ
resume_from_checkpoint='latest' # ์ต์ ์ฒดํฌํฌ์ธํธ์์ ์ฌ๊ฐ
)
main(config)
โ FAQ (์์ฃผ ๋ฌป๋ ์ง๋ฌธ)
Q1: ์ด ๋ชจ๋ธ์ ์ถ๋ก (inference)๋ง ํ๋ ค๋ฉด?
A: ํ์ต๋ ์ฒดํฌํฌ์ธํธ๊ฐ ์๋ค๋ฉด ๋ค์์ฒ๋ผ ๊ฐ๋จํ:
import torch
from korean_llm_advanced_v3 import KoreanLLM, generate
from transformers import AutoTokenizer
model = KoreanLLM(...).to(device)
checkpoint = torch.load("checkpoints/korean_llm_50000.pth", map_location=device)
model.load_state_dict(checkpoint['model_state_dict'])
model.eval()
response = generate(model, tokenizer, prompt="์๋
?", max_tokens=50)
Q2: ๋ด GPU ๋ฉ๋ชจ๋ฆฌ๊ฐ 9GB ๋ฏธ๋ง์ด๋ฉด?
A: ๋ค์ ๋ฐฉ๋ฒ๋ค์ ์๋ํด๋ณด์ธ์:
- ๋ฐฐ์น ํฌ๊ธฐ๋ฅผ
1๋ก ๊ฐ์ - ์ํ์ค ๊ธธ์ด๋ฅผ
1024๋ก ๋จ์ถ - ๊ทธ๋๋์ธํธ ๋์ ๋จ๊ณ๋ฅผ
16์ผ๋ก ์ฆ๊ฐ - 8๋นํธ ์์ํ ํ์ฑํ
Q3: ํ์ต ์ค๋จ ํ ์ฌ๊ฐํ๋ ค๋ฉด?
A: ์๋์ผ๋ก ์ต์ ์ฒดํฌํฌ์ธํธ๋ฅผ ๊ฐ์งํฉ๋๋ค:
config = TrainingConfig(
resume_from_checkpoint='latest' # ๋๋ ํน์ ๊ฒฝ๋ก
)
main(config)
Q4: ๋ค๋ฅธ ํ๊ตญ์ด ๋ฐ์ดํฐ์ ์ ์ฌ์ฉํ ์ ์๋?
A: ๋ค! DatasetManager ํด๋์ค์ DATASETS_CONFIG๋ฅผ ์์ ํ๋ฉด ๋ฉ๋๋ค:
DATASETS_CONFIG = [
{
"name": "your-dataset/path",
"split": "train",
"text_keys": ["input", "output"]
}
]
Q5: ์๋์ฐ์์ ์คํํ๋ฉด ์๋ฌ๊ฐ ๋์
A: num_workers ์ค์ ์ 0์ผ๋ก ๋ณ๊ฒฝํด๋ณด์ธ์:
loader = DataLoader(dataset, batch_size=2, num_workers=0)
Q6: VRAM ์ฌ์ฉ๋์ ๋ ์ค์ผ ์ ์๋?
A: ๋ค์ ์ต์ ์ ์กฐํฉํด๋ณด์ธ์:
- ๋ฉ๋ชจ๋ฆฌ ํจ์จ ๋ชจ๋:
use_bfloat16=True - ๋ ๊น์ ์์ํ: INT4 (์ถ๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์)
- LoRA ํ์ธํ๋: ์ ํ์ ๋ ์ด์ด๋ง ํ์ต
Q7: ์์ฑ๋ ํ ์คํธ ํ์ง์ด ๋ฎ์ผ๋ฉด?
A: ๋ค์์ ํ์ธํ์ธ์:
- ํ์ต ์คํ ์ด ์ถฉ๋ถํ๊ฐ? (์ต์ 10,000 ์คํ ๊ถ์ฅ)
- Learning rate ์ค์ ์ด ์ ์ ํ๊ฐ?
- ๋ฐ์ดํฐ์ ํ์ง์ด ์ข์๊ฐ?
temperatureํ๋ผ๋ฏธํฐ ์กฐ์ (0.5~1.0 ๊ถ์ฅ)
Q8: ๋ชจ๋ธ์ ONNX๋ ๋ค๋ฅธ ํ์์ผ๋ก ๋ณํํ๋ ค๋ฉด?
A: PyTorch์์ ONNX๋ก ๋ณํ ๊ฐ๋ฅ:
import torch.onnx
dummy_input = torch.randint(0, 50000, (1, 2048)).to(device)
torch.onnx.export(
model, dummy_input, "korean_llm.onnx",
input_names=['input_ids'],
output_names=['output']
)
Q9: ๊ฐ๋ฐ์๊ฐ ํ๋ฐํ ์ง์ํ๋?
A: ๋ค! ์ด์๋ ํผ๋๋ฐฑ์ ์ด๋ฉ์ผ(seoan102410@gmail.com)๋ก ์ฐ๋ฝ์ฃผ์ธ์! ๐
Q10: ์์ฉ ํ๋ก์ ํธ์ ์ฌ์ฉ ๊ฐ๋ฅํ๊ฐ?
A: GPL-3.0 ๋ผ์ด์ ์ค์ด๋ฏ๋ก, ์์ ์ฌํญ์ ๊ณต๊ฐํด์ผ ํฉ๋๋ค. ์์ธํ ๋ด์ฉ์ LICENSE ํ์ผ์ ํ์ธํ์ธ์.
๐ ๏ธ ํธ๋ฌ๋ธ์ํ
โ CUDA Out of Memory ์๋ฌ
์ฆ์: RuntimeError: CUDA out of memory
ํด๊ฒฐ์ฑ :
# ๋ฐฐ์น ํฌ๊ธฐ ๊ฐ์
config.batch_size = 1
# ์ต๋ ์ํ์ค ๊ธธ์ด ๊ฐ์
config.max_seq_len = 1024
# ๊ทธ๋๋์ธํธ ๋์ ์ฆ๊ฐ
config.accumulation_steps = 16
โ bitsandbytes ์ค์น ์คํจ
ํด๊ฒฐ์ฑ :
# CUDA Toolkit ๊ฒฝ๋ก ๋ช
์
CUDA_HOME=/usr/local/cuda pip install bitsandbytes
โ ๋ฐ์ดํฐ์ ๋ค์ด๋ก๋ ์คํจ
ํด๊ฒฐ์ฑ :
# ์บ์ ์ด๊ธฐํ ํ ์ฌ์๋
rm -rf datasets/cache/*
python korean_llm_advanced_v3.py
๐ ์ฑ๋ฅ ์ต์ ํ ํ
- ๋ฐฐ์น ํฌ๊ธฐ ์กฐ์ : ๋๋ฌด ์์ผ๋ฉด ํ์ต์ด ๋๋ฆฌ๊ณ , ๋๋ฌด ํฌ๋ฉด VRAM ๋ถ์กฑ
- ๊ทธ๋๋์ธํธ ๋์ : ํจ๊ณผ์ ์ธ ๋ฐฐ์น ํฌ๊ธฐ ์ฆ๋์ ํต์ฌ
- Learning Rate ์ค์ผ์ค๋ง: Cosine Annealing์ผ๋ก ์๋ ด ํฅ์
- ํผํฉ ์ ๋ฐ๋: BF16 ์ฌ์ฉ์ผ๋ก ์๋์ ๋ฉ๋ชจ๋ฆฌ ๋์ ๊ฐ์
- ์ฒดํฌํฌ์ธํธ: ์ ๊ธฐ์ ์ผ๋ก ์ ์ฅํ์ฌ ํ์ต ์ฌ๊ฐ ๊ฐ๋ฅ
๐ ์ฐ๋ฝ์ฒ ๋ฐ ์ ๋ณด
- ๊ฐ๋ฐ์: seoan1024
- ์ด๋ฉ์ผ: seoan102410@gmail.com
- GitHub: seoan1024
๐ ๋ผ์ด์ ์ค
์ด ํ๋ก์ ํธ๋ GPL-3.0 ๋ผ์ด์ ์ค ํ์ ๋ฐฐํฌ๋ฉ๋๋ค.
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2024 seoan1024
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
๐ ์ ์ฒด ๋ผ์ด์ ์ค: LICENSE
๐ค ๊ธฐ์ฌํ๊ธฐ
๋ฒ๊ทธ ๋ฆฌํฌํธ, ๊ธฐ๋ฅ ์ ์, ํ ๋ฆฌํ์คํธ๋ ์ธ์ ๋ ํ์ํฉ๋๋ค!
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
๐ ๊ฐ์ฌ์ ๋ง
- ๐ฏ ํ๊ตญ์ด LLM ์ปค๋ฎค๋ํฐ - ๊ท์คํ ํผ๋๋ฐฑ๊ณผ ๊ธฐ์ฌ
- ๐ Hugging Face - Transformers & Datasets ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- ๐ง bitsandbytes - ์์ํ ๋ฐ ์ต์ ํ ์๋ฃจ์
- ๐ PyTorch - ์คํ์์ค ๋ฅ๋ฌ๋ ํ๋ ์์ํฌ
๐ ์ฐธ๊ณ ์๋ฃ
ํ๊ตญ์ด NLP
์ต์ ํ ๊ธฐ๋ฒ
๋๊ท๋ชจ ์ธ์ด๋ชจ๋ธ
โญ ์ด ํ๋ก์ ํธ๊ฐ ๋์์ด ๋์๋ค๋ฉด ๋ณโญ์ ๋๋ฌ์ฃผ์ธ์!
๐บ๐ธ Korean LLM Advanced v3
Korean-Optimized Large Language Model - Scratch Implementation & Quantization Applied
๐ Project Development Journey
At first, free API quotas became tight, and there was a limit to "vibe coding." I tried running Ollama locally, but the models were too heavy for my computer to handle. Then suddenly, the thought struck me: "Why not just build it myself?"
However, I was only in 8th grade and knew almost nothing about AI models except the concept of 'B (Billion)' for model size. I couldn't code advanced concepts, only the basics.
So, as usual, I went into ChatGPT and boldly declared: "I want to create my own independent Korean LLM model!" and started this ambitious challenge.
While receiving help from GPT, I kept hitting limitations. Initially, I just collected code snippets from GPT and desperately hoped to avoid Dimension Errors. I spent entire days collecting and cleaning data, staring at my computer monitor.
The first version of my work (pre-v1) was an ultra-lightweight 50M (50 million parameter) model trained on Wikipedia dataโit no longer exists. Although proper conversation was impossible, it did show signs of constructing grammatically correct sentences. I was so happy with that small success that I became obsessed with creating a "chatbot-type model." This led me to develop v1 to its final version: a 541M-sized model. Despite some bugs, I decided to fix them and immediately scale up the model.
After fixing errors and roughly doubling the model size, I finally built a 1.09B (1.09 billion parameter) model. Throughout the vacation, I kept my computer running for training whenever I had time.
Before I knew it, the seemingly endless training had reached 44,000 steps. With excitement, I typed "์๋ ?" (Hello?) into the chat for testing.
"์๋ ํ์ธ์! ์ค๋์ ๋ฌด์์ ๋์๋๋ฆด๊น์?" (Hello! What can I help you with today?)
The moment the model displayed the correct response on screen, I felt indescribable joy.
But joy was short-lived. As I asked different questions, it started spitting out completely wrong answers. After analyzing code with AI all night, I discovered a critical bug: the model was ignoring user instructions. Heartbroken, I bravely discarded all training results for a more perfect model.
Without losing hope, I completely fixed v2's bugs and tackled the next challenge. A 1B-class model consumed a whopping 23GB of VRAM, making it too heavy to run in typical environments. I set a goal to reduce this to 10GB or less, and finally succeeded with quantization in v3.
As vacation ended, it became difficult for me to keep training. I haven't done any training since the v2 bug fix, and I'll resume when time allows. This project is my life's greatest work, completed solely through sheer stubbornness to build an LLM with my own hands.
Please use this model well, and if you like it, don't forget to click the star button (โญ)! Thank you!
A 1.09B parameter LLM trained entirely in Korean from scratch, making aggressive use of VRAM optimization techniques.
๐ Key Features โข ๐ Quick Start โข ๐พ Technology Stack โข ๐ Version History
๐ Overview
Korean LLM Advanced v3 is a lightweight large language model optimized for Korean natural language processing. It is designed to train and perform inference efficiently even in limited GPU memory environments.
Core Goals
- โ Korean text generation and comprehension
- โ VRAM efficiency (9GB baseline)
- โ Fast training speed
- โ Easy deployment and utilization
๐ Key Features
๐ฏ Model Architecture
| Item | Description |
|---|---|
| Model Size | 1.09B Parameters |
| Hidden Dimension | 1,920 |
| Number of Layers | 20 |
| Attention Heads | 10 |
| Max Sequence Length | 2,048 Tokens |
| Vocabulary Size | Dynamic (based on tokenizer) |
๐ง Optimization Techniques
1๏ธโฃ BF16 Automatic Mixed Precision
~50% VRAM savings compared to standard FP32
- Memory efficiency: โฌ๏ธ 12GB โ 6GB
- Computation speed: โก๏ธ Equivalent or improved
2๏ธโฃ 8-bit AdamW Optimizer (bitsandbytes)
75% reduction in optimizer state memory
- Standard AdamW: ~2.2GB (1B model)
- 8-bit AdamW: ~0.55GB (1B model)
3๏ธโฃ Quantization โญ
Dynamic quantization of model weights
- INT8 Quantization: 4x size reduction
- Inference speed: 1.5~2x improvement
4๏ธโฃ Gradient Accumulation
Effective batch size increase
- Configuration: batch_size=2, accumulation_steps=8
- Effect: Equivalent to batch size 16
5๏ธโฃ Gradient Checkpointing
Activation memory reduction
- Recomputation cost: ~30% speed decrease
- Memory savings: 30~40%
๐พ VRAM Usage Comparison
| Version | Parameters | VRAM Usage | Optimization Techniques |
|---|---|---|---|
| v1 | 541M | ~11GB | Basic FP32 |
| v2 | ~1.1B | ~23GB | BF16 + Gradient Checkpoint |
| v3 | 1.09B | ~9GB โจ | BF16 + 8-bit Optimizer + Quantization |
v3 achieves 60% VRAM reduction compared to v2, with 2x model size expansion vs v1
๐ Quick Start
๐ Prerequisites
Python 3.9 or higher
CUDA 11.8 or higher (GPU required)
GPU Memory: Minimum 9GB recommended
1๏ธโฃ Installation
# Clone the repository
git clone https://github.com/seoan1024/korean-llm-v3.git
cd korean-llm-v3
# Install essential packages
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
pip install transformers datasets tqdm pandas matplotlib
# Quantization support library (optional)
pip install bitsandbytes
2๏ธโฃ Dataset Preparation
The code automatically downloads the following datasets:
- ๐น
nlpai-lab/kullm-v2- Korean instruction-tuning data - ๐น
beomi/KoAlpaca-v1.1a- Korean Alpaca dataset
# Datasets are automatically downloaded, no separate action needed
# Cache directory: ./datasets/cache/
3๏ธโฃ Start Training
# Start training with default settings
python korean_llm_advanced_v3.py
# Or run with custom configuration
python korean_llm_advanced_v3.py \
--batch-size 2 \
--max-steps 50000 \
--learning-rate 5e-5
4๏ธโฃ Monitoring
A GUI monitoring window automatically opens during training:
- ๐ Real-time loss graph
- ๐ฌ Interactive chat (generation testing)
- ๐ Log viewer
๐๏ธ Project Structure
korean-llm-v3/
โโโ korean_llm_advanced_v3.py # Main training script
โโโ README.md # This file
โโโ LICENSE # GPL-3.0 License
โ
โโโ checkpoints/ # Saved model checkpoints
โ โโโ korean_llm_*.pth
โ
โโโ datasets/ # Dataset cache
โ โโโ cache/ # Downloaded datasets
โ โโโ datasets_manifest.json # Metadata
โ
โโโ logs/ # Training logs and graphs
โโโ training.log # Detailed log
โโโ loss_history.json # Loss history
๐ Version History
v1 (Initial Version)
- 541M parameter model
- VRAM usage: ~11GB
- Basic FP32 training
v2 (Optimization v1)
- Expanded to 1.1B parameters
- VRAM usage: ~23GB (1.2x initial increase)
- BF16 + Gradient Checkpoint applied
v3 (Current) โญ
- 1.09B parameters (v2 level)
- VRAM usage: ~9GB (60% reduction from v2!)
- Major Improvements:
- 8-bit AdamW optimizer
- Dynamic quantization support
- Enhanced memory management
- Faster training speed
๐ง Technology Stack
Core Libraries
| Library | Version | Purpose |
|---|---|---|
| PyTorch | 2.0+ | Deep learning framework |
| Transformers | 4.30+ | Tokenizer and utilities |
| Datasets | 2.10+ | Korean dataset loading |
| bitsandbytes | 0.40+ | 8-bit quantization optimization |
| tqdm | 4.60+ | Progress display |
Optional Libraries
| Library | Purpose |
|---|---|
| matplotlib | Loss graph visualization |
| tkinter | GUI monitoring (built-in) |
| pandas | Data processing |
๐ก Usage Examples
Model Loading and Text Generation
import os, argparse
from pathlib import Path
from typing import Optional, Tuple, List
import torch, torch.nn as nn, torch.nn.functional as F
from transformers import AutoTokenizer
class RMSNorm(nn.Module):
def __init__(self, dim, eps=1e-6):
super().__init__()
self.eps = eps
self.weight = nn.Parameter(torch.ones(dim))
def forward(self, x):
return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps) * self.weight
def precompute_freqs_cis(head_dim: int, end: int, theta: float = 10000.0) -> Tuple[torch.Tensor, torch.Tensor]:
freqs = 1.0 / (theta ** (torch.arange(0, head_dim, 2)[:head_dim // 2].float() / head_dim))
t = torch.arange(end, dtype=freqs.dtype)
freqs = torch.outer(t, freqs)
return torch.cos(freqs), torch.sin(freqs)
def apply_rotary_emb(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
head_dim_2 = cos.shape[-1]
head_dim = head_dim_2 * 2
x1 = x[..., :head_dim // 2]
x2 = x[..., head_dim // 2:]
cos = cos.unsqueeze(0).unsqueeze(0)
sin = sin.unsqueeze(0).unsqueeze(0)
return torch.cat([x1 * cos - x2 * sin, x1 * sin + x2 * cos], dim=-1)
class SwiGLU(nn.Module):
def __init__(self, dim: int, hidden_dim: int):
super().__init__()
self.w1 = nn.Linear(dim, hidden_dim, bias=False)
self.w2 = nn.Linear(hidden_dim, dim, bias=False)
self.w3 = nn.Linear(dim, hidden_dim, bias=False)
def forward(self, x):
return self.w2(F.silu(self.w1(x)) * self.w3(x))
class Attention(nn.Module):
def __init__(self, dim: int, n_heads: int):
super().__init__()
assert dim % n_heads == 0
self.n_heads = n_heads
self.head_dim = dim // n_heads
self.wq = nn.Linear(dim, dim, bias=False)
self.wk = nn.Linear(dim, dim, bias=False)
self.wv = nn.Linear(dim, dim, bias=False)
self.wo = nn.Linear(dim, dim, bias=False)
def forward(self, x: torch.Tensor, f_cos: torch.Tensor, f_sin: torch.Tensor, kv_cache: Optional[Tuple[torch.Tensor, torch.Tensor]] = None):
b, s, d = x.shape
q = self.wq(x).view(b, s, self.n_heads, self.head_dim).transpose(1, 2)
k = self.wk(x).view(b, s, self.n_heads, self.head_dim).transpose(1, 2)
v = self.wv(x).view(b, s, self.n_heads, self.head_dim).transpose(1, 2)
q = apply_rotary_emb(q, f_cos, f_sin)
k = apply_rotary_emb(k, f_cos, f_sin)
if kv_cache is not None:
pk, pv = kv_cache
k = torch.cat([pk, k], dim=2)
v = torch.cat([pv, v], dim=2)
new_kv = (k.detach(), v.detach())
out = F.scaled_dot_product_attention(q, k, v, attn_mask=None, is_causal=(s > 1))
out = out.transpose(1, 2).contiguous().view(b, s, d)
return self.wo(out), new_kv
class TransformerBlock(nn.Module):
def __init__(self, dim: int, n_heads: int, hidden_dim: int):
super().__init__()
self.attention = Attention(dim, n_heads)
self.feed_forward = SwiGLU(dim, hidden_dim)
self.attention_norm = RMSNorm(dim)
self.ffn_norm = RMSNorm(dim)
def forward(self, x, f_cos, f_sin, kv_cache=None):
normed_x = self.attention_norm(x)
h, new_kv = self.attention(normed_x, f_cos, f_sin, kv_cache=kv_cache)
x = x + h
x = x + self.feed_forward(self.ffn_norm(x))
return x, new_kv
class KoreanLLM(nn.Module):
def __init__(self, vocab_size: int, pad_token_id: int, dim: int = 1920, n_layers: int = 20, n_heads: int = 10, max_seq_len: int = 512):
super().__init__()
self.vocab_size = vocab_size
self.pad_token_id = pad_token_id
self.dim = dim
self.n_heads = n_heads
self.head_dim = dim // n_heads
self.max_seq_len = max_seq_len
self.embed = nn.Embedding(vocab_size, dim)
self.layers = nn.ModuleList([TransformerBlock(dim, n_heads, int(dim * 2.5)) for _ in range(n_layers)])
self.norm = RMSNorm(dim)
self.output = nn.Linear(dim, vocab_size, bias=False)
self.output.weight = self.embed.weight
f_cos, f_sin = precompute_freqs_cis(self.head_dim, max_seq_len * 2)
self.register_buffer("f_cos", f_cos)
self.register_buffer("f_sin", f_sin)
def _get_freqs(self, f, start, length):
end = start + length
if end > f.shape[0]:
raise ValueError(f"ํ์ฌ ์ปจํ
์คํธ๊ฐ ๋๋ฌด ๊น๋๋ค: {end} > {f.shape[0]}")
return f[start:end]
@torch.no_grad()
def forward(self, tokens: torch.Tensor, kv_caches=None):
b, s = tokens.shape
x = self.embed(tokens)
start_pos = 0
if kv_caches is not None and len(kv_caches) > 0 and kv_caches[0][0] is not None:
start_pos = kv_caches[0][0].shape[2]
f_cos = self._get_freqs(self.f_cos, start_pos, s)
f_sin = self._get_freqs(self.f_sin, start_pos, s)
new_kv_caches = []
for i, layer in enumerate(self.layers):
cache = kv_caches[i] if kv_caches is not None else None
x, kv = layer(x, f_cos, f_sin, kv_cache=cache)
new_kv_caches.append(kv)
x = self.norm(x)
logits = self.output(x)
return logits, new_kv_caches
def find_latest_checkpoint(checkpoint_dir="checkpoints"):
checkpoint_dir = Path(checkpoint_dir)
if not checkpoint_dir.exists(): return None
files = list(checkpoint_dir.glob("korean_llm_*.pth"))
if not files: return None
def step_number(path):
try: return int(path.stem.split("_")[-1])
except ValueError: return -1
files.sort(key=step_number)
return files[-1]
def load_checkpoint(model, checkpoint_path, device):
print(f"๐ฆ ์ฒดํฌํฌ์ธํธ ๋ก๋ฉ:\n {checkpoint_path}")
checkpoint = torch.load(checkpoint_path, map_location=device)
if "model_state_dict" in checkpoint:
state_dict = checkpoint["model_state_dict"]
step = checkpoint.get("step", "?")
else:
state_dict = checkpoint
step = "?"
model.load_state_dict(state_dict, strict=True)
print(f"โ
๋ชจ๋ธ ๋ก๋ ์๋ฃ\n ํ์ต step: {step}")
return step
@torch.no_grad()
def generate(model, tokenizer, prompt, device, max_tokens=256, temperature=0.6, top_k=40, top_p=0.95, repetition_penalty=1.15, context_limit=512):
model.eval()
prompt_text = f"### ์ง๋ฌธ: {prompt}\n### ์๋ต:"
tokens = tokenizer.encode(prompt_text, add_special_tokens=False, return_tensors="pt").to(device)
if tokens.shape[1] >= context_limit:
tokens = tokens[:, -context_limit + 1:]
output_tokens = tokens
kv_caches = None
eos_id = tokenizer.eos_token_id
for _ in range(max_tokens):
input_tokens = output_tokens if kv_caches is None else output_tokens[:, -1:]
logits, kv_caches = model(input_tokens, kv_caches=kv_caches)
next_logits = logits[:, -1, :]
temperature = max(float(temperature), 1e-5)
next_logits = next_logits / temperature
if repetition_penalty != 1.0:
used_tokens = set(output_tokens[0].tolist())
for token_id in used_tokens:
if token_id < next_logits.shape[-1]:
if next_logits[0, token_id] < 0:
next_logits[0, token_id] *= repetition_penalty
else:
next_logits[0, token_id] /= repetition_penalty
if top_k > 0:
k = min(int(top_k), next_logits.shape[-1])
threshold = torch.topk(next_logits, k).values[..., -1, None]
next_logits = torch.where(next_logits < threshold, torch.full_like(next_logits, float("-inf")), next_logits)
probs = F.softmax(next_logits, dim=-1)
if 0 < top_p < 1.0:
sorted_probs, sorted_indices = torch.sort(probs, descending=True, dim=-1)
cumulative = torch.cumsum(sorted_probs, dim=-1)
remove = cumulative > top_p
remove[..., 0] = False
indices_to_remove = torch.zeros_like(probs, dtype=torch.bool)
indices_to_remove.scatter_(-1, sorted_indices, remove)
probs = probs.masked_fill(indices_to_remove, 0.0)
probs = probs / (probs.sum(dim=-1, keepdim=True) + 1e-10)
if not torch.isfinite(probs).all():
next_token = torch.argmax(next_logits, dim=-1, keepdim=True)
else:
next_token = torch.multinomial(probs, num_samples=1)
output_tokens = torch.cat([output_tokens, next_token], dim=1)
if eos_id is not None and next_token.item() == eos_id: break
if output_tokens.shape[1] >= context_limit: break
generated_text = tokenizer.decode(output_tokens[0], skip_special_tokens=True)
if "### ์๋ต:" in generated_text:
response = generated_text.split("### ์๋ต:", 1)[1]
else:
response = generated_text
if "### ์ง๋ฌธ:" in response:
response = response.split("### ์ง๋ฌธ:", 1)[0]
return response.strip()
def main():
parser = argparse.ArgumentParser(description="KoreanLLM ์ฒดํฌํฌ์ธํธ ์ฑํ
")
parser.add_argument("--checkpoint", type=str, default="latest", help="์ฒดํฌํฌ์ธํธ ๊ฒฝ๋ก ๋๋ latest")
parser.add_argument("--tokenizer", type=str, default="beomi/Llama-3-Open-Ko-8B")
parser.add_argument("--max-tokens", type=int, default=256)
parser.add_argument("--temperature", type=float, default=0.6)
parser.add_argument("--top-k", type=int, default=40)
parser.add_argument("--top-p", type=float, default=0.95)
parser.add_argument("--repetition-penalty", type=float, default=1.15)
parser.add_argument("--cpu", action="store_true", help="๊ฐ์ ๋ก CPU ์ฌ์ฉ")
args = parser.parse_args()
if args.cpu:
device = torch.device("cpu")
else:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("=" * 65)
print("๐ฐ๐ท KoreanLLM v3 Checkpoint Chat")
print("=" * 65)
print(f"๐ฅ๏ธ Device: {device}")
if device.type == "cuda":
print(f"๐ฎ GPU: {torch.cuda.get_device_name(0)}")
else:
print("โ ๏ธ CPU ๋ชจ๋์
๋๋ค. 1920-dim / 20-layer ๋ชจ๋ธ์ด๋ผ ์์ฑ์ด ๋๋ฆด ์ ์์ต๋๋ค.")
print("\n๐ค ํ ํฌ๋์ด์ ๋ก๋ฉ...")
tokenizer = AutoTokenizer.from_pretrained(args.tokenizer, clean_up_tokenization_spaces=False)
if tokenizer.pad_token is None or tokenizer.pad_token_id == tokenizer.eos_token_id:
tokenizer.add_special_tokens({"pad_token": "<|pad|>"})
vocab_size = len(tokenizer)
pad_token_id = tokenizer.pad_token_id
print(f" vocab_size = {vocab_size}")
print(f" eos_token_id = {tokenizer.eos_token_id}")
print(f" pad_token_id = {pad_token_id}")
checkpoint_path = args.checkpoint
if checkpoint_path.lower() == "latest":
checkpoint_path = find_latest_checkpoint()
if checkpoint_path is None:
print("\nโ checkpoints ํด๋์์ ์ฒดํฌํฌ์ธํธ๋ฅผ ์ฐพ์ง ๋ชปํ์ต๋๋ค.")
print("์: python chat_korean_llm.py --checkpoint checkpoints/korean_llm_50000.pth")
return
checkpoint_path = Path(checkpoint_path)
if not checkpoint_path.exists():
print(f"\nโ ์ฒดํฌํฌ์ธํธ๊ฐ ์์ต๋๋ค:\n {checkpoint_path}")
return
print("\n๐ง ๋ชจ๋ธ ์์ฑ ์ค...")
model_config = dict(vocab_size=vocab_size, pad_token_id=pad_token_id, dim=1920, n_layers=20, n_heads=10, max_seq_len=512)
dtype = torch.bfloat16 if device.type == "cuda" else torch.float32
model = KoreanLLM(**model_config).to(device)
if device.type == "cuda":
model = model.to(dtype=dtype)
try:
step = load_checkpoint(model, checkpoint_path, device)
except RuntimeError as e:
print("\nโ ์ฒดํฌํฌ์ธํธ์ ํ์ฌ ๋ชจ๋ธ ๊ตฌ์กฐ๊ฐ ๋ง์ง ์์ต๋๋ค.")
print(" ํนํ tokenizer์ vocab_size / pad_token ์ค์ ์ ํ์ธํ์ธ์.")
print(f"\n์์ธ ์ค๋ฅ:\n{e}")
return
model.eval()
params = sum(p.numel() for p in model.parameters())
print(f"๐ Parameters: {params / 1e6:.1f}M")
print("\n" + "=" * 65)
print("๐ฌ ์ฑํ
์์")
print(" /exit ์ข
๋ฃ")
print(" /clear ๋ํ ์
๋ ฅ ๊ธฐ๋ก ์ด๊ธฐํ")
print(" /info ๋ชจ๋ธ ์ ๋ณด")
print("=" * 65)
while True:
try:
prompt = input("\n๋ > ").strip()
except (KeyboardInterrupt, EOFError):
print("\n\n๐ ์ข
๋ฃํฉ๋๋ค.")
break
if not prompt: continue
if prompt.lower() in {"/exit", "/quit", "exit", "quit"}:
print("๐ ์ข
๋ฃํฉ๋๋ค.")
break
if prompt == "/clear":
print("๐งน ์
๋ ฅ ์ํ๋ฅผ ์ด๊ธฐํํ์ต๋๋ค.")
continue
if prompt == "/info":
print(f"\n์ฒดํฌํฌ์ธํธ : {checkpoint_path}\nํ์ต step : {step}\nDevice : {device}\nParameters : {params / 1e6:.1f}M\nTemperature : {args.temperature}\nTop-k : {args.top_k}\nTop-p : {args.top_p}")
continue
print("\n๋ชจ๋ธ > ", end="", flush=True)
try:
response = generate(model=model, tokenizer=tokenizer, prompt=prompt, device=device, max_tokens=args.max_tokens, temperature=args.temperature, top_k=args.top_k, top_p=args.top_p, repetition_penalty=args.repetition_penalty, context_limit=512)
print(response)
except torch.cuda.OutOfMemoryError:
print("\nโ CUDA ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ถ์กฑํฉ๋๋ค.")
print(" --max-tokens ๊ฐ์ ๋ฎ์ถ๊ฑฐ๋ ๋ค๋ฅธ GPU์์ ์คํํด๋ณด์ธ์.")
except Exception as e:
print(f"\nโ ์์ฑ ์ค๋ฅ: {type(e).__name__}: {e}")
if __name__ == "__main__":
main()
Custom Training Configuration
from korean_llm_advanced_v3 import TrainingConfig, main
config = TrainingConfig(
batch_size=4, # Batch size
accumulation_steps=4, # Gradient accumulation steps
max_steps=100000, # Maximum training steps
warmup_steps=1000, # Warmup steps
learning_rate=3e-5, # Learning rate
eval_interval=5000, # Evaluation interval
use_bfloat16=True, # Whether to use BF16
resume_from_checkpoint='latest' # Resume from latest checkpoint
)
main(config)
โ FAQ (Frequently Asked Questions)
Q1: What if I only want to run inference?
A: If you have a trained checkpoint, it's simple:
import torch
from korean_llm_advanced_v3 import KoreanLLM, generate
from transformers import AutoTokenizer
model = KoreanLLM(...).to(device)
checkpoint = torch.load("checkpoints/korean_llm_50000.pth", map_location=device)
model.load_state_dict(checkpoint['model_state_dict'])
model.eval()
response = generate(model, tokenizer, prompt="์๋
?", max_tokens=50)
Q2: What if my GPU memory is less than 9GB?
A: Try these approaches:
- Reduce batch size to
1 - Shorten sequence length to
1024 - Increase gradient accumulation steps to
16 - Enable 8-bit quantization
Q3: How do I resume training after interruption?
A: It automatically detects the latest checkpoint:
config = TrainingConfig(
resume_from_checkpoint='latest' # Or specify a specific path
)
main(config)
Q4: Can I use a different Korean dataset?
A: Yes! Modify DATASETS_CONFIG in the DatasetManager class:
DATASETS_CONFIG = [
{
"name": "your-dataset/path",
"split": "train",
"text_keys": ["input", "output"]
}
]
Q5: I get errors when running on Windows
A: Try changing num_workers setting to 0:
loader = DataLoader(dataset, batch_size=2, num_workers=0)
Q6: Can I reduce VRAM usage even more?
A: Try combining these options:
- Memory Efficient Mode:
use_bfloat16=True - Deeper Quantization: INT4 (additional library required)
- LoRA Fine-tuning: Train only selective layers
Q7: Generated text quality is low
A: Check these:
- Is the training step count sufficient? (Minimum 10,000 steps recommended)
- Is the learning rate setting appropriate?
- Is dataset quality good?
- Adjust
temperatureparameter (0.5~1.0 recommended)
Q8: How do I convert the model to ONNX or other formats?
A: Convert from PyTorch to ONNX:
import torch.onnx
dummy_input = torch.randint(0, 50000, (1, 2048)).to(device)
torch.onnx.export(
model, dummy_input, "korean_llm.onnx",
input_names=['input_ids'],
output_names=['output']
)
Q9: Is the developer actively supporting this?
A: Yes! Contact via email (seoan102410@gmail.com) for issues or feedback! ๐
Q10: Can I use this in commercial projects?
A: It's under GPL-3.0 license, so you must disclose modifications. See the LICENSE file for details.
๐ ๏ธ Troubleshooting
โ CUDA Out of Memory Error
Symptom: RuntimeError: CUDA out of memory
Solution:
# Reduce batch size
config.batch_size = 1
# Reduce maximum sequence length
config.max_seq_len = 1024
# Increase gradient accumulation
config.accumulation_steps = 16
โ bitsandbytes Installation Failure
Solution:
# Specify CUDA Toolkit path
CUDA_HOME=/usr/local/cuda pip install bitsandbytes
โ Dataset Download Failure
Solution:
# Clear cache and retry
rm -rf datasets/cache/*
python korean_llm_advanced_v3.py
๐ Performance Optimization Tips
- Batch Size Adjustment: Too small slows training, too large causes VRAM shortage
- Gradient Accumulation: Key to increasing effective batch size
- Learning Rate Scheduling: Cosine Annealing improves convergence
- Mixed Precision: BF16 improves both speed and memory
- Checkpointing: Regular saving enables training resumption
๐ Contact & Information
- Developer: seoan1024
- Email: seoan102410@gmail.com
- GitHub: seoan1024
๐ License
This project is distributed under GPL-3.0 License.
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2024 seoan1024
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
๐ Full License: LICENSE
๐ค Contributing
Bug reports, feature suggestions, and pull requests are always welcome!
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
๐ Acknowledgments
- ๐ฏ Korean LLM Community - Valuable feedback and contributions
- ๐ Hugging Face - Transformers & Datasets libraries
- ๐ง bitsandbytes - Quantization and optimization solutions
- ๐ PyTorch - Open-source deep learning framework
๐ References
Korean NLP
Optimization Techniques
Large Language Models
โญ If this project was helpful, please click the star button!