23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
지난 1월, AI 업계는 그야말로 DeepSeek R1 광풍이 휩쓸고 지나갔습니다.
언론은 물론, AI 기업들의 주가까지 출렁이게 만들 정도로 DeepSeek R1 추론 모델에 대한 관심이 뜨거웠었죠.
돌이켜보면, 2024년 OpenAI의 o1 모델이 추론 모델의 새로운 도약을 알리는 신호탄이었습니다.
기존 AI 모델과 차별화된 '사고 후 응답' 패러다임을 도입하며, 복잡한 문제를 작은 단계로 나눠 해결하는 방식으로 주목받았죠.
그 결과, 수학, 과학, 코딩처럼 높은 수준의 논리적 사고가 필요한 분야에서 탁월한 성능을 보였습니다.
이런 성과에 고무되어 DeepSeek R1과 Gemini 2.0 같은 강력한 추론 모델들이 잇달아 등장했습니다.
특히, DeepSeek R1은 비용 효율적인 모델 개발과 오픈 소스 공개라는 두 가지 측면에서 큰 반향을 일으켰고, 핵심 알고리즘까지 공개되면서 open-r1 같은 복원 프로젝트까지 생겨날 정도입니다.
그러나 R1과 같은 추론 모델을 만들려면 대규모 데이터, 막대한 GPU 자원, 복잡한 강화학습 과정이 필요하기 때문에 결코 쉬운 일이 아니죠.
그런데 최근, 스탠포드 대학교 연구진이 S1 모델을 공개하며 주목을 받고 있습니다.
S1은 Test-Time Scaling 기법을 응용해서 상대적으로 적은 비용으로도 높은 추론 성능을 달성할 수 있는 효과적인 방법론을 제시했습니다.
이번 글에서는 S1 모델의 핵심 원리를 살펴보고, 간단한 코드 구현을 통해 그 구조와 작동 방식까지 쉽게 이해해 보려 합니다.
이 글에서 다루는 내용
Train-Time Scaling vs Test-Time Scaling 비교
S1 연구의 의의와 핵심 방법론
추론 모델 만들어 보기
본문에 사용된 코드는 아래 GitHub 링크에서 다운로드할 수 있습니다.
S1 모델은 SFT 학습으로 진행합니다. SFT가 생소하시다면 아래 글을 참조해보세요
Reasoning이라는 용어는 일반적으로 "추론"으로 번역되지만, 이는 Inference(추론)과도 겹쳐 혼동될 수 있습니다.
따라서 이 글에서는 Reasoning을 “사고”로 명시하여 구분하도록 하겠습니다.
LLM의 성능 항샹을 위해서는 어떤 방법 있을까요?
지금까지 우리는 대부분 모델을 만드는 과정, 즉 학습에 포커스를 맞춰서 성능 향상을 생각해왔습니다.
즉 더 많은 데이터와 더 큰 모델로 더 많이 학습할수록 성능이 증가한다는 다소 직관적인 방법이죠. 하지만 LLM의 성능 향상은 추론 과정에서도 발현 될 수 있습니다.
LLM의 성능 향상에 있어 기존의 학습 중심 접근법과 새롭게 주목받는 추론 중심 접근법에 대해 간단히 살펴보겠습니다.
LLM을 만드는 과정을 다시 상기해보겠습니다.
첫 번째는 사전 학습(Pre-Training)*단계로, 방대한 양의 텍스트 데이터를 사용해 모델이 언어의 패턴과 구조를 학습하는 과정입니다.
이 과정에서 모델은 단순히 문장을 암기하는 것이 아니라, 확률적 언어 모델링을 통해 문맥을 이해하고 다음 단어를 예측하는 능력을 갖추게 됩니다.
두번째는 후처리학습(Post-Training) 과정입니다.
이전 포스팅에서 살펴본 SFT (Supervised Fine-Tuning), RLHF(Reinforcement Learning form Human Feedback), DPO(Direct Preference Optimization)등의
기법들을 통해서 모델이 우리가 원하는 형태대로 출력을 하도록 미세 조정을 하는 과정입니다.
이 두 과정에서 중요한 점은 모델의 성능을 높이기 위해서는 더 많은 데이터를 사용하고, 더 큰 모델을 학습해야 한다는 점입니다.
때문에 이러한 방식은 필연적으로 더 많은 데이터와 연산 자원이 필요하게 됩니다.
이렇게 학습 과정에서 자원과 연산량을 증가시켜 성능을 확장하는 방법을 학습 시점 확장(Train-Time Scaling)이라고 합니다.
그렇다면, Train-Time Scaling 방식의 단점은 무엇일까요?
모델이 내놓는 대답이 학습한 데이터로 제한된다는 점도 문제이지만, 모델의 출력이 한번에 이루어진다는 점입니다.
이 때문에 잘못된 대답을 내놓아도 추론 과정 중 수정할 수 없으며, 할루시네이션 같은 문제를 근본적으로 해결하기도 어렵습니다.
이러한 오류를 바로잡으려면 잘못된 출력을 바로 잡도록 튜닝된 데이터로 모델을 다시 학습해야 합니다.
이런 튜닝 과정을 통해 모델의 성능을 향상시키고 오류를 잡을 수 있지만, 이는 지속적인 자원과 비용을 요구한다는 문제가 있습니다.
최근 테스트 시점 확장(Test-Time Scaling) 이라는 새로운 접근 방식이 주목받고 있습니다.
이는 Train-Time Scaling이 성능 향상을 위해 학습 과정에서 추가적인 컴퓨팅 자원을 투입하는 것과 달리, 추론 과정(즉, 문제 해결 단계)에서 컴퓨팅 자원을 활용하여 성능을 개선하는 방법입니다.
대표적인 사례로, 작년에 OpenAI에서 공개한 o1 모델이 있습니다.
이 모델은 사용자의 질문에 대해 단순히 즉각적인 답변을 제공하는 것이 아니라, 여러 차례 추론 과정을 반복하며 자기 검증을 수행하는 방식을 채택하고 있습니다.
즉, 모델이 충분한 시간을 들여 여러 단계를 거쳐 “깊이 생각”할 수 있도록 하면, 결과적으로 사고(Reasoning) 능력이 향상된다는 사실이 확인된 것입니다.
이러한 접근 방식은 우리에게 익숙한 CoT(Chain of Thought) 프롬프팅과 유사해 보일 수 있으나, 중요한 차이점이 존재합니다.
CoT 프롬프팅은 모델이 논리적인 사고 과정을 따르도록 유도하지만, 추론 과정의 중간 단계에 대한 검증이 불가능합니다.
LLM은 기본적으로 입력에 대해 다음 토큰을 예측하는 방식으로 작동하기 때문입니다.
반면, Test-Time Scaling은 반복적인 자기 개선 기법을 활용합니다. 즉, 모델이 생성한 추론 과정의 출력을 다시 입력으로 활용하여 자기 검증을 수행하도록 설계되어 있습니다.
이렇게 반복적인 검증 과정을 통해 더 정확한 답변을 찾아 갈 수 있다는 것입니다.
이러한 방식은 모델이 단순히 문장을 완성하는 것이 아니라, 스스로 오류를 줄이고 최적의 답변을 도출하는 데 집중하도록 유도한다는 점에서 기존 접근 방식과 차별화됩니다.
그렇다면 Test-Time Scaling 의 한계는 무엇일까요?
더 오래 생각하는 것은 확실히 기존의 오류를 발견하고 수정하는데 있어 효과적인 방법론입니다.
그런데 원래의 모델이 가지고 있는 지식이 적은 상태라면 이 방식이 유효하지 않을 수 있습니다.
예를 들어, 초등학교 수준의 수학 지식을 가진 모델이 대학 수준의 수학 문제를 푸는 상황을 가정해봅시다.
이 경우, 단순히 더 오래 생각한다고 해서 정답을 도출할 수 있는 것은 아니죠.
즉, Test-Time Scaling이 효과적으로 작동하려면, 문제 해결을 위한 능력과 지식이 모델에 이미 내재되어 있어야 합니다.
추론 시간을 늘린다고 해서 무조건 답변의 질이 향상되는 것은 아니라는 점이 Test-Time Scaling의 주요 한계로 작용할 수 있습니다.
S1 연구는 이미지넷으로 유명한 Fei-Fei Li 교수의 스탠포드 연구진에 의해 진행되었으며, Test-Time Scaling 방식을 통해 추론 능력을 향상시키는 비용 효율적이고 직관적인 방법을 제시했습니다.
이 연구는 대규모 강화 학습 파이프라인 대신 잘 정제된 1,000개의 예제만을 사용해 모델을 파인튜닝하고, 추론 시점에 동적인 방식으로 최적화하는 Budget Forcing 기법을 제시합니다.
놀랍게도 S1 모델은 OpenAI의 o1-preview를 고급 수학과 과학 벤치마크에서 능가하는 성능을 기록했습니다. 이는 S1의 방법론이 매우 효과적임을 증명해낸것이죠.
S1 연구자들은 방대한 추론 데이터를 수집하기보다는 ‘S1k’라는 작지만 잘 정제된 데이터셋을 만드는 데 집중했습니다.
이 데이터셋은 1,000개의 고급 추론 문제에 대한 질문, 사고 과정, 답변으로 이루어져 있으며, 이러한 고품질의 데이터셋이 모델의 사고 능력을 높이는 데 효과적이라는 것을 보여줍니다.
더군다나 이렇게 작은 데이터셋으로 인해 학습에 들어가는 비용도 획기적으로 줄일 수 있었는데, S1-32B 모델은 16개의 H100 GPU에서 단 26분 만에 학습을 완료했다고 합니다.
https://huggingface.co/datasets/simplescaling/s1K-1.1
Budget Forcing은 모델의 추론 과정을 디코딩 단계에서 간단하게 제어하는 방법입니다.
한국어로 번역하면 ‘예산 강제’로 다소 어색하지만, 용어의 의미를 해석하자면 다음과 같습니다.
Budget : 모델이 추론 과정에서 사용할 수 있는 계산 자원 (토큰수, 시간)
Forcing : 모델이 이 Budget 내에서 작동하도록 강제 한다는 의미
Budget forcing 은 두가지 주요 매커니즘에 따라 동작합니다.
최대 사고(Thinking) 토큰 수 제한
모델이 설정된 최대 토큰 수를 초과하여 추론할 경우, 강제로 “end-of-thinking” 토큰과 최종 답변 문자열 “Final Answer:” 삽입
이를 통해 모델의 사고 과정을 조기에 종료시키고 최종 답변을 생성하도록 유도
최소 사고(Thinking) 토큰 수 보장
모델이 충분한 추론 없이 너무 빨리 답변을 종료하려 할 때 사용
“end-of-thinking” 토큰 생성을 억제하고 “Wait”이라는 단어를 추론 과정 중간에 삽입
이를 통해 모델이 자신의 추론을 계속하고 더 깊게 생각하도록 유도
결국 충분하게 생각하지 않은 경우 해당 출력에 "Wait" 이라는 단어를 삽입해서 다시 모델에 입력을 넣으면 "Wait"이란 단어 다음으로 생성이 이어지면서 통해
모델이 다시 한번 검증하는 과정을 거치게 될 것이고, 이 과정을 반복하면서 반복 사고를 하게 된다는 다소 직관적인 아이디어 입니다.
논문에 나온 예시를 보면 좀 더 직관적으로 이해할 수 있습니다.
질문으로 라즈베리에 “r”이 몇개인지를 물어봅니다.
"r" 몇개인지 나름의 논리로 추론을 합니다. 하지만 답변은 “2”로 틀렸습니다.
최소 사고 토큰수가 안되므로 출력에 "Wait"이라는 단어를 붙여서 다시 입력으로 넣습니다.
Wait 이라는 단어를 기점으로 다시 한번 검증 하는 모델의 출력이 이어집니다.
이러한 과정을 통해 최종 출력 (Final Answer:)에 정답을 맞추게 됩니다.
[ 그림 1. budget forcing ]
논문 : * https://arxiv.org/abs/2501.19393
다음과 같이 Qwen2.5-7B-Instruct 모델에 사고를 요하는 문제를 물어봤습니다.
문제를 풀어 나가는 과정도 볼 수 있도록 프롬프트에 단계별로 생각해보라는 문구를 넣어서 작성했습니다.
다음 나열된 숫자들에서 B에 들어갈 숫자는? 61 52 63 94 46 B 차근 차근 단계별로 생각해봐출력 결과를 살펴 볼까요?
이 문제를 해결하기 위해 차근차근 단계별로 생각해볼 수 있습니다. 주어진 숫자열은 61, 52, 63, 94, 46, B입니다. 이 숫자열에서 B에 들어갈 숫자를 찾기 위해 다음과 같은 단계를 따르겠습니다.
1. 차이 계산: 먼저 주어진 숫자들 사이의 차이를 계산해봅시다.
- 61에서 52는 9
- 52에서 63은 11
- 63에서 94는 31
- 94에서 46은 -48
2. 차이의 패턴 찾기: 계산한 차이들을 살펴보면, 9, 11, 31, -48입니다. 이 차이들 사이에는 명확한 패턴이 보이지 않지만, 그 차이들의 변화를 살펴보면, 2, 20, -79라는 패턴이 보입니다. 이는 각 차이가 직전 차이에 대해 큰 변화를 보이고 있습니다.
3. 추측과 검증: 주어진 패턴을 기반으로, 다음 차이를 예측해봅시다. 예를 들어, -48에서 다음 차이를 계산해보면, -48에서 2, 20, -79를 고려하여 다음 차이는 -48 + 2 = -46, -46 + 20 = -26, -26 - 79 = -105 등이 될 수 있습니다. 하지만 이는 복잡해지므로, 단순한 패턴을 찾아보겠습니다.
4. 단순 패턴 찾기: 주어진 숫자열에서 가장 간단한 패턴을 찾아보면, 61에서 52는 9, 52에서 63은 11, 63에서 94는 31, 94에서 46은 -48입니다. 이 중에서 가장 간단한 패턴은 61에서 52는 -9, 52에서 63은 +11, 63에서 94는 +31, 94에서 46은 -48입니다. 이 패턴을 이어가면, 46에서 B는 +20이 될 수 있습니다.
따라서, B에 들어갈 숫자는 46 + 20 = 66입니다.
결론적으로, B에 들어갈 숫자는 66 입니다.Qwen 2.5-7B instruction는 Insturction 튜닝이 된 모델이라서 질문에 대해 단계별로 풀어나가는 모습을 보이네요.
전체적으로 과정 자체는 논리적으로 보이지만 아쉽게도 정답은 틀렸습니다.
이렇게 풀이 과정과 답이 틀리는 경우는 이러한 패턴에 대한 데이터셋을 다시 만들어 학습을 시켜야 제대로 된 추론 과정과 답변을 만들어 낼 수 있습니다.
자, 이제 S1에서 사용한 방법론을 적용하여 모델의 사고 능력을 강화해 보겠습니다.
전체 코드는 서두에 링크된 코드를 참조하시고, 이 글에서는 주요한 부분만 짚어 보도록 하겠습니다.
모델을 학습 및 튜닝하기 위해 필요한 라이브러리를 설치합니다.
S1 레시피의 핵심 기법인 Budget Forcing은 다단계 추론이 필요하므로, 추론 속도를 높이기 위해 vLLM 라이브러리를 사용합니다.
!pip install --upgrade transformers bitsandbytes peft accelerate datasets trl flash_attn vllm Qwen2.5 토크나이저를 로드할 때 vocab에 정의된 토큰중에 PAD로 사용할만한 적절한 토큰을 지정합니다.
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B-Instruct")
tokenizer.pad_token = "<|fim_pad|>"
tokenizer.pad_token_id = 151662
tokenizer.padding_side = 'left'S1 모델 학습에 사용된 1K 학습 데이터가 허깅페이스에 공개되어 있지만, 우리는 한글 모델을 만들어 볼 것입니다.
마침 허깅페이스 Dataset hub에 S1k 데이터셋과 동일한 형식의 한글 데이터셋이 업로드 되었네요.
이 한글 학습 데이터셋를 사용해 보겠습니다.
dataset = load_dataset("werty1248/s1k-1.1-Ko-ReGenerated-Formatted", split='train')데이터셋을 확인해보면 다음과 같은 필드가 포함되어 있습니다.
question_ko: 질문
reasoning_ko: 논리적인 사고 과정
answer_ko: 최종 답변
각 샘플은 질문, 문제 해결을 위한 논리적 사고 과정, 그리고 정리된 최종 답변으로 구성되어 있습니다.
특히, reasoning_ko 필드는 주어진 문제를 단계별로 분석하고 추론하는 과정을 상세히 담고 있으며, 자기 검토를 통해 오류를 수정하는 단계도 포함하고 있습니다.
즉, 이 데이터셋은 모델이 단순히 정답을 예측하는 것이 아니라, 문제를 논리적으로 분석하고 해결하는 능력을 학습하도록 설계되었음을 직관적으로 확인할 수 있습니다.
S1 레시피에서는 프롬프트 템플릿에서 사고 과정과 최종 답변을 구분하는 별도의 태그를 사용합니다.
Reasoning(사고) 과정 구분자: <|im_start|>think
최종 답변 구분자: <|im_start|>answer
따라서, Qwen 2.5 Chat 템플릿 형식에 맞춰 위의 구분자를 적용하는 변환 함수를 작성해야 합니다.
QUERY_TEMPLATE_NOANSWER = """{Question}""".strip()
def process_cot_example(example: Dict, tokenizer):
thinking_trajectory = example["reasoning_ko"]
question = example["question_ko"]
answer = example["answer_ko"]
prompt = QUERY_TEMPLATE_NOANSWER.format(Question=question)
answer = "Answer: " + answer if "Answer:" not in answer else answer
text = tokenizer.apply_chat_template([
{"role": "user", "content": prompt},
{
"role": "assistant",
"content": "<|im_start|>think\n" + thinking_trajectory.strip() +
"\n<|im_start|>answer\n" + answer.strip()
}
], tokenize=False)
return dict(text=text)만들어진 데이터의 샘플 하나를 열어 보죠
print(dataset["text"][0])
<|im_start|>system
You are Qwen, created by Alibaba Cloud. You are a helpful assistant.<|im_end|>
<|im_start|>user
유리수를 주면, 이를 기약 분수 형태로 나타내고, 그 결과 분자와 분모의 곱을 계산하세요. 0과 1 사이의 유리수 중에서 얼마의 유리수에 대해 그 결과 곱이 $20_{}^{}!$이 되겠습니까?<|im_end|>
<|im_start|>assistant
<|im_start|>think
좋아, 이 문제를 풀어봐야겠다. 문제는 0과 1 사이의 유리수를 기약 분수 형태로 나타내고, 그 분자와 분모의 곱이 20!이 되는 경우의 수를 구하는 거야.
먼저, 유리수는 a/b 형태로 나타낼 수 있고, 0과 1 사이이니까 a < b이고, a와 b는 서로소여야 기약 분수겠지. 그리고 분자와 분모의 곱이 20!이 되어야 하니까 a * b = 20! 이라는 조건이 생기는 거야.
그러면 문제는 결국 a와 b가 서로소이고, a < b, a*b = 20!을 만족하는 (a, b) 쌍의 개수를 찾는 거네.
..중략..
잠깐, 이게 맞나? 예를 들어 N이 제곱수인 경우에는 a와 b가 같은 경우가 있을 수 있지만, 이 문제에서는 a와 b가 서로소이고 a*b=20!이므로 a와 b가 같을 수는 없어. 왜냐하면 a = b라면 a^2 = 20!이 되고, a는 20!의 제곱근이어야 하는데, 20!은 완전제곱수가 아니기 때문이야. 따라서 모든 쌍은 a ≠ b이고, 대칭쌍이므로 절반으로 나누는 게 맞아.
그래서 2^8 / 2 = 128이 맞는 것 같아.
그럼 최종 답은 128?
<|im_start|>answer
Answer: 0과 1 사이의 유리수를 기약 분수 $\frac{a}{b}$로 나타낼 때, $a$와 $b$는 서로소이며 $a < b$이고 $a \cdot b = 20!$을 만족해야 합니다. $20!$의 소인수는 20 이하의 소수인 2, 3, 5, 7, 11, 13, 17, 19로 총 8개입니다. 각 소인수는 $a$ 또는 $b$ 중 하나에만 포함되어야 하므로, 경우의 수는 $2^8 = 256$입니다. 이 중 $a < b$인 경우는 대칭쌍의 절반이므로 $256 / 2 = 128$입니다.
$\boxed{128}$<|im_end|>우리가 의도한 대로 데이터셋이 잘 만들어 졌네요.
잠깐, 학습을 시작하기 전에 데이터셋의 분포를 살펴 봅시다.
[그림 2. 데이터셋 분포]
S1 모델에서는 Sequence Length를 32,768로 설정했지만, 이렇게 긴 Sqeunce Length는 많은 GPU 메모리를 요구하게 되며,
제한된 GPU 자원으로 학습 하려는 경우 이 제약으로 학습이 진행되지 않을 수 있습니다.
따라서 데이터셋 분포를 보고 Squence Length를 적절한 길이로 제한하는 것이 좋습니다.
Jupyter notebook 학습 예제 코드는 A100 Single GPU에서 학습이 가능하도록 LoRA 어댑터로 진행했습니다.
더불어 메모리 사용을 좀 더 효과적으로 하기 위해 Training Hyper Parameter도 메모리 효율적으로 조정 되어 있습니다.
하지만 안타깝게도 7B라는 작은 크기를 갖는 모델에, 길고 복잡한 추론 데이터를 학습하기에는 LoRA 학습이 원하는 성능을 끌어내지 못해서 부득히 2 GPU를 통해 Full 파인 튜닝을 진행했습니다.
이후 기술되는 추론 결과는 모두 Full 파인튜닝된 모델의 결과 입니다.
Test-Time Scaling에서도 언급했듯이, S1 레시피가 효과적으로 작동하려면 모델의 기본 성능이 일정 수준 이상 보장되어야 합니다.
이번 예제는 최소한의 자원으로 학습하는 것을 목표로 했기 때문에 Qwen 7B 모델을 사용했지만, Full 파인튜닝을 거치더라도 기대한 수준의 사고 능력이 충분히 발현되지 않았습니다.
S1과 유사한 결과를 얻으려면 동일한 32B 모델을 사용해 파인튜닝 해볼 것을 권장합니다.
모델이 학습이 되었다면, 이제 추론을 실행해보겠습니다.
먼저 서두에 말씀 드린 것 처럼 빠른 추론을 위해 vllm 라이브러리를 로드 합니다.
from vllm import LLM, SamplingParams
from vllm.lora.request import LoRARequest먼저, 얼마나 사고를 반복 할 지와 총 출력 길이를 제한하는 변수를 지정합니다.
MAX_TOKENS_THINKING = 20000
NUM_IGNORE = 2MAX_TOKENS_THINKING : 모델이 사고하는 동안 허용되는 최대 토큰 수, 이 값이 너무 작으면 모델이 충분한 정보를 출력하지 못할 수 있으며, 너무 크면 불필요한 토큰이 출력될 수도 있습니다.
NUM_IGNORE : 지정된 횟수 만큼 stop_token_ids를 무시하고 추가적인 생각 단계를 하도록 합니다.
모델에 질문을 입력하면 첫번째 추론이 진행됩니다.
prompt = "<|im_start|>system\nYou are Qwen, created by Alibaba Cloud. You are a helpful assistant.<|im_end|>\n"
prompt += "<|im_start|>user\n" + prompts[0] + "<|im_end|>\n<|im_start|>assistant\n"
stop_token_ids = tok("<|im_start|><|im_end|>")["input_ids"]
sampling_params = SamplingParams(
max_tokens=MAX_TOKENS_THINKING,
min_tokens=0,
stop_token_ids=stop_token_ids,
skip_special_tokens=False,
temperature=0,
)
prompt += "<|im_start|>think"
o = model.generate(
prompt,
sampling_params=sampling_params,
# lora_request=s1_adapter
)
print(prompt + o[0].outputs[0].text)출력 결과를 살펴보죠
<|im_start|>system
You are Qwen, created by Alibaba Cloud. You are a helpful assistant.<|im_end|>
<|im_start|>user
다음 나열된 숫자들에서 B에 들어갈 숫자는? 61 52 63 94 46 B<|im_end|>
<|im_start|>assistant
<|im_start|>think
오, 이 문제는 숫자 패턴을 찾아서 B에 맞는 숫자를 찾는 거네. 일단 주어진 숫자들을 하나씩 살펴봐야겠어.
.. 중략 ..
그렇다면 B=18이 맞는 것 같다. 이 방법이 가장 합리적인 추론인 것 같아. 다른 가능성은 없을까?
예를 들어, 뒤집은 숫자가 제곱수가 되는 것 외에 다른 규칙이 있을 수 있지만, 이 방법이 가장 명확해 보여.
그래서 최종 답은 18이 될 것 같다.출력 내용이 길어서 중간 부분을 생략했지만. S1 학습 데이터에서 확인 한 것 처럼 단계별로 사고 과정을 이어나가고 있는 것을 확인할 수 있습니다.
출력된 추론 내용을 검증하는 단계를 반복합니다.
S1 레시피에서는 igonre_str에 “Wait” 이라는 단어를 주었지만, 한글로 튜닝된 만큼 한글로 “잠깐,” 이라는 단어를 사용하겠습니다.
이어지는 프롬프트에 “잠깐,” 이라는 단어가 들어감으로써 모델은 출력을 종료하지 않고, 다시 한번 검토 하는 과정을 거치게 될 것입니다.
NUM_IGNORE를 2로 설정했으므로, 사고 과정이 두 번 반복됩니다.
ignore_str = "잠깐,"
max_tokens_thinking_tmp = MAX_TOKENS_THINKING
for i in range(NUM_IGNORE):
max_tokens_thinking_tmp -= len(o[0].outputs[0].token_ids)
prompt += o[0].outputs[0].text + ignore_str
sampling_params = SamplingParams(
max_tokens=max_tokens_thinking_tmp,
min_tokens=0,
stop_token_ids=stop_token_ids,
skip_special_tokens=False,
temperature=0.0,
)
o = model.generate(
prompt,
sampling_params=sampling_params
)
print(prompt + o[0].outputs[0].text)출력된 결과를 살펴보면 “잠깐,” 이라는 단어가 나오고 자연스럽게 지금까지 추론한 결과를 다시 한번 검토합니다.
마치 사람이 문제를 풀면서 답안지에 최종 마킹을 하기 전에 “잠깐만 혹시 계산이 틀리진 않았겠지?” 하며 다시 한번 검토하는 모습과 닯아 있습니다.
이렇게 2번씩 더 검토한다면 좀 더 정확한 답을 찾아 갈 수 있겠죠.
잠깐, 다시 확인해보자. 각 숫자를 뒤집었을 때 4,5,6,7,8,9의 제곱이 되어야 한다. 61 → 16 (4²), 52 →25 (5²), 63 →36 (6²), 94 →49 (7²), 46 →64 (8²). 그러면 B'은 81 (9²)이 되어야 하므로 B는 18이 된다. 맞는 것 같다. 그럼 답은 18.
잠깐, 46을 뒤집으면 64가 되는데, 64는 8²이 맞아. 그러면 B'은 81이 되어야 하므로 B는 18. 이게 맞는 것 같다. 그러면 B=18.이렇게 반복 사고를 진행한 후 학습한 형식대로 최종 Answer를 출력 하게 됩니다.
Answer: 주어진 숫자열 61, 52, 63, 94, 46, B에서 각 숫자를 뒤집으면 16, 25, 36, 49, 64, B'이 됩니다. 이는 각각 4², 5², 6², 7², 8²에 해당하며, 다음 숫자의 뒤집edList B'은 9²인 81이 됩니다. 따라서 B는 81을 뒤집은 18이 됩니다.
$\boxed{18}$정답이네요!
추론 모델 학습을 거친 후에는 제대로 된 사고 과정과 답을 도출해 내고 있습니다.
예시 코드는 S1에서 진행한 코드를 기반으로, S1을 복원하는 목적에 맞춰 동일한 형식을 사용했습니다.
그런데 좀 더 개선을 하자면, ignore_str에 "Wait"을 사용하는 대신, 오류 가능성을 점검하는 더 구체적인 프롬프트를 넣거나,
모델이 스스로 reasoning이 충분하다고 판단하도록 유도하는 프롬프트를 추가하는 방법도 고려할 수 있을 것 같네요
이를 통해, 추론 검증 과정의 반복 횟수를 동적으로 조정하여 필요에 따라 줄이거나 늘릴 수도 있을 것입니다.
이번 글에서는 S1 모델이 제안한 Test-Time Scaling 방식과 Budget Forcing 기법이 어떻게 모델의 사고 능력을 향상시키는지를 살펴보았습니다.
전통적인 Train-Time Scaling 방식이 아닌, 추론 과정에서 사고 능력을 강화하는 방법론이 실제로 효과적임을 실험을 통해 확인할 수 있었습니다.
특히, 기존 모델이 가진 한계를 극복하기 위해 자기 검토를 반복하는 방식이 인간의 사고 방식과 유사하게 작동한다는 점이 인상적이었습니다.
단순히 데이터와 모델 크기를 늘리는 접근법에서 벗어나, 모델이 스스로 답을 검토하고 수정하는 방향으로 나아가는 것은 앞으로의 AI 모델 발전의 중요한 흐름이 될 것입니다.
이제 완연한 봄이 찾아온 것 같습니다. 따뜻한 날씨만큼이나 좋은 일만 가득하시길 바랍니다.
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.