--- language: ko license: apache-2.0 library_name: transformers pipeline_tag: text-generation tags: - korean - rag - small-language-model - scratch-pretrained --- # kjh-model **Scratch pretrained. Llama-style architecture. No Meta weights used.** 김재현이 개인적으로 연구·학습 목적으로 개발한 로컬 한국어 언어모델이다. RAG(검색 증강 생성) 환경에서, 찾아온 자료를 근거로 답하도록 만들어졌다. --- ## 1. 이 모델의 출처 — 왜 랜덤 초기화부터 학습했나 이 모델의 가중치는 **랜덤 초기화에서 직접 사전학습(scratch pretrained)** 한 것이다. Meta 의 Llama 체크포인트를 내려받아 파인튜닝하거나 이어 학습한 것이 아니고, 어떤 형태로도 Llama 가중치를 포함하지 않는다. ### `architectures` / `model_type` 이 `"llama"` 인 이유 `config.json` 의 ```json "architectures": ["LlamaForCausalLM"], "model_type": "llama" ``` 는 **출처 표기가 아니라 구조 표기**다. `transformers` 라이브러리가 이 가중치 텐서를 어떤 forward 코드로 돌릴지 고르는 로더 지시자일 뿐, "이 모델이 Meta 에서 왔다"는 뜻이 아니다. Pre-norm Transformer + RMSNorm + SwiGLU + RoPE + GQA 라는 공개된(누구나 쓸 수 있는) 설계 조합을 쓰면, `transformers` 안에서의 구현 이름이 `llama` 가 된다. 이 조합 자체는 Meta 의 전유물이 아니라 여러 논문에서 조합해 쓰는 표준 구성이다. 그래서 TinyLlama, OpenLLaMA, SmolLM 처럼 완전히 자체적으로 사전학습한 모델들도 같은 이유로 `LlamaForCausalLM` 을 그대로 쓴다 — 이 프로젝트만의 예외적인 표기가 아니다. 이 표기를 커스텀 클래스 이름으로 바꾸려면 `auto_map` + `modeling_*.py` 를 직접 작성해 넣고 `trust_remote_code=True` 로 불러야 하는데, 그러면 vLLM · llama.cpp · GGUF 변환 같은 외부 도구와의 호환이 대부분 끊긴다. 그래서 **의도적으로 표준 표기를 유지**했다. (`architectures`, `model_type` 은 변경하지 않는다.) --- ## 2. 사양 | 항목 | 값 | | --- | --- | | 총 파라미터 | 약 172M (본체 107M + 임베딩/출력층 66M) | | 레이어 | 12 | | hidden / intermediate | 1024 / 2048 | | attention head | 16 (KV head 4, GQA) | | head_dim | 64 | | 컨텍스트 길이 | 4096 | | vocab | 32,001 (자체 토크나이저 + 특수토큰 1) | | dtype | float16 | | 학습 방식 | 랜덤 초기화 → scratch pretraining → RAG 형식 SFT | 크기부터가 Meta Llama 계열(최소 1B)에 존재하지 않는 구성이다. 참고로 파라미터를 `0.1B` 급으로 줄여 부르는 경우, 임베딩/출력층을 제외한 본체(107M) 기준이다 — 정확한 총량은 172M 이다. --- ## 3. 쓰는 법 — 반드시 `[자료] / [질문]` 형식으로 물을 것 이 모델은 근거 문서와 질문을 아래 형식으로 감싸 주는 것을 전제로 SFT 되었다. **이 형식을 지키지 않으면 아는 것도 지어내는 빈도가 크게 늘어난다.** ``` [자료] (검색해 왔거나 손으로 넣은 근거 문장들) [질문] (사용자의 질문) ``` ### Transformers 로 직접 불러 쓰기 ```python import torch from transformers import AutoTokenizer, AutoModelForCausalLM token = "hf_xxxxxxxxxxxx" # 본인 토큰 MODEL = "coderecode95/KJH" # 실제 리포지토리 경로로 바꿀 것 tok = AutoTokenizer.from_pretrained(MODEL, token=token) model = AutoModelForCausalLM.from_pretrained(MODEL, token=token, dtype=torch.float16).to("cuda").eval() # context = "" 참고자료를 넣을 경우 자료를 기반하여 답변합니다. question = "건강을 위해서는 어떻게 해야할까?" # prompt = f"[자료]\n{context}\n\n[질문] {question}" prompt = f"{question}" messages = [{"role": "user", "content": prompt}] inputs = tok.apply_chat_template( messages, add_generation_prompt=True, return_tensors="pt", return_dict=True, # 명시적으로 dict(BatchEncoding) 반환하도록 ).to("cuda") output = model.generate( **inputs, max_new_tokens=200, do_sample=True, temperature=0.55, top_p=0.85, repetition_penalty=1.15, no_repeat_ngram_size=4, pad_token_id=tok.pad_token_id or tok.eos_token_id, ) print(tok.decode(output[0][inputs["input_ids"].shape[-1]:], skip_special_tokens=True)) ``` 위 디코딩 파라미터(`temperature=0.55`, `repetition_penalty=1.15`, `no_repeat_ngram_size=4`)는 `generation_config.json` 에도 기본값으로 들어 있으므로, 별도로 지정하지 않고 `model.generate(input_ids, attention_mask=...)` 만 호출해도 같은 값이 적용된다. ### 근거 없이 물으면 어떻게 되나 `[자료]` 블록 없이 질문만 던지면(`"[질문] 세종대왕은 누구야?"` 또는 그냥 평문 질문) 모델이 갖고 있는 얕은 지식으로 답을 시도하며, 이 경우 사실 관계를 지어낼 확률이 뚜렷이 올라간다. 정확한 답이 필요한 질문은 반드시 근거를 함께 준다. ### 채팅 템플릿 ``` <|im_start|>{role} {content}<|endoftext|> <|im_start|>assistant ``` ChatML 과 유사한 형식이며, `tokenizer_config.json` 에 `chat_template.jinja` 로 등록되어 있어 `apply_chat_template()` 을 그대로 쓰면 된다. --- ## 4. 무엇을 잘하고, 무엇을 못하는가 (실측 기준) 작은 모델이라는 점을 숨기지 않는다. 수백 개의 질문을 직접 돌려 확인한 결과다. ### 잘하는 것 - **근거를 그대로 옮겨 짧게 답하기** — `[자료]` 에 실제 답이 있는 사실형 질문 ("OO 테이블에 어떤 컬럼이 있어?", "설립년도가 언제야?") - **정해진 형식의 반복 작업** — 인사, 자기소개, 정형화된 문서 요약 - **한국어 문장 생성** — 학습 데이터가 한국어 위주라 이 범위에서는 자연스럽다 ### 못하는 것 - **영어 질문** — 한국어 전용 데이터로 학습되어 영어로 물으면 답이 무너진다 (문법이 깨지거나 관련 없는 토큰을 늘어놓는다). - **긴 절차형 답변** — 레시피, 설치 순서처럼 단계 순서를 지켜야 하는 답변은 순서가 뒤섞이거나 없는 단계를 지어낸다. - **멀티턴 맥락 유지** — 몇 턴만 지나도 이전 대화 내용을 엉뚱하게 되짚어 답이 무너지는 경향이 뚜렷하다. 매 질문에 필요한 정보(주어 등)를 다시 넣는 것을 권장한다. - **고유명사·숫자를 정확히 그대로 베끼기** — 근거에 있는 이름이나 숫자를 비슷하지만 틀린 값으로 바꿔 쓰는 경우가 있다 (예: 회사명의 음절이 하나 빠짐, 연도가 한두 자리 바뀜). **법적·재무적으로 정확해야 하는 숫자·고유명사는 모델 출력을 그대로 신뢰하지 말고 원문 대조를 권장한다.** - **SQL·코드 생성** — 학습 범위 밖이라 문법이 깨진 코드를 낸다. (참고: 이 모델을 실제로 서비스에 얹을 때는 SQL 생성을 모델에 맡기지 않고, 스키마 정보를 코드로 파싱해 규칙 기반으로 조립하는 방식을 함께 썼다.) ### 요약 이 모델 단독으로는 "무엇이든 잘 답하는 어시스턴트"가 아니다. **근거를 붙여 좁은 질문에 짧게 답하게 하는 용도**로 설계되었고, 그 범위를 벗어나면 성능이 급격히 떨어진다. 정확도가 중요한 부분(숫자 계산, SQL, 스키마 조회 등)은 모델이 아니라 주변 코드가 담당하고, 모델은 "찾아온 문장을 자연스러운 한국어로 풀어 설명하는" 역할에 집중시키는 것을 권장한다. --- ## 5. 학습 개요 - **1단계 — 사전학습(pretraining)**: 랜덤 초기화 가중치에서 시작해 한국어 텍스트로 처음부터 학습. - **2단계 — RAG 형식 SFT**: `[자료]\n(근거)\n\n[질문] (질문)` 형식의 대화 데이터로 지도 미세조정. 근거 없이 묻는 일반 대화·인사·자기소개도 일부 포함. ## 6. 라이선스 Apache License 2.0. 상업적 이용을 포함해 자유롭게 사용·수정·재배포할 수 있으며, 원저작자 표시를 남겨야 한다. 자세한 조건은 리포지토리의 `LICENSE` 파일을 참고할 것. ## 7. 인용 ``` @misc{kjh-model, title = {kjh-model: A Scratch-Pretrained Korean Small Language Model for RAG}, author = {Kim, Jaehyeon}, year = {2026} } ``` ## 8. 실제 사용 예시 이 모델을 RAG 파이프라인에 적용해 성능을 확인한 데모 영상입니다. - [A에서 Z까지 직접 만든 지시기반 모델 — RAG 적용하여 성능 확인하기](https://www.youtube.com/watch?v=PE42VEW-nZo)