AI 이해하기
TMTAI 모델이 어떻게 만들어지는지 더 잘 이해하고 싶었습니다. 전문가가 되려는 것은 아니고, 매일 사용하는 추상화에 대한 감을 얻고자 했습니다.
이 글은 지금까지 제가 배운 내용을 강조합니다. 신경망, 딥러닝, 트랜스포머 같은 주제에 익숙하지 않은 다른 엔지니어들을 위해 작성되었습니다.
Machine Learning
소프트웨어는 결정론적입니다. 어떤 입력이 주어지면, 프로그램을 다시 실행해도 같은 출력을 얻습니다. 개발자가 각 경우를 처리하도록 명시적으로 코드를 작성합니다.
대부분의 AI 모델1은 그렇지 않습니다 — 확률적입니다. 개발자가 명시적으로 지시사항을 프로그래밍할 필요가 없습니다.
Machine learning은 소프트웨어가 데이터로부터 패턴을 인식하도록 가르칩니다. 어떤 입력이 주어졌을 때, 동일한 출력을 얻지 못할 수도 있습니다2. GPT(OpenAI), Claude(Anthropic), Gemini(Google) 같은 AI 모델은 인터넷 문서의 큰 덩어리로 “학습”됩니다. 이들 모델은 학습 과정에서 패턴을 배웁니다.
그 다음에는 모델과 대화할 수 있는 API 또는 채팅 인터페이스가 있습니다. 어떤 입력을 기반으로 문장, 이미지, 오디오를 예측하고 생성할 수 있습니다. 머신러닝은 더 넓은 AI 범주의 부분집합으로 생각할 수 있습니다.
Neural Networks
AI 모델은 neural networks에 기반합니다 — 예제로부터 학습하는 거대한 의사결정 경로의 그물망이라고 생각하세요. 신경망은 무엇에나 사용할 수 있지만, 저는 언어 모델에 집중하겠습니다.
이러한 네트워크는 정보를 처리하는 상호 연결된 뉴런의 층으로 구성됩니다:
- 입력층(input layer)은 데이터가 시스템에 들어오는 곳입니다. 입력은 단어 또는 토큰의 수치적 표현으로 변환됩니다(토큰은 뒤에서 더 설명합니다).
- 많은 은닉층(hidden layer)은 시스템의 패턴에 대한 이해를 형성합니다. 층 내부의 뉴런은 입력 데이터에 가중치를 적용하고 결과를 활성화 함수3에 통과시킵니다. 이 함수는 종종 0과 1 사이의 값을 출력하며, 뉴런의 활성화 수준을 나타냅니다.
- 출력층(output layer)은 문장에서 다음 단어를 예측하는 것과 같은 최종 결과를 생성합니다. 이 단계의 출력은 종종 logits라고 하며, 확률로 변환되는 원시 점수입니다.
예를 들어 입력이 “San”이라면, 모델은 다음 단어로 “Francisco”에 높은 확률을 할당하고, “kitten”과 같은 관련 없는 단어에는 거의 0에 가까운 확률을 할당합니다.
제가 얻은 큰 깨달음은: 이건 그냥 수학이라는 점입니다. 선형대수, 미적분, 통계를 사용해 최초 원리에서 신경망을 구축할 수 있습니다. PyTorch 같은 유용한 추상화가 있을 때는 아마 그렇게 하지는 않겠지만, 내부에서 무슨 일이 일어나는지 신비감을 걷어내는 데 도움이 됩니다.
Deep Learning
딥러닝은 많은 층을 가진 신경망을 포함하는 머신러닝의 부분집합입니다 — 그래서 “딥”이라는 용어가 붙습니다. 간단한 신경망은 하나 또는 두 개의 은닉층만 있을 수 있지만, 딥러닝 모델은 수백 개의 층을 가질 수 있습니다.
이러한 추가 층은 네트워크가 복잡한 패턴을 학습하도록 합니다. 예를 들어, 언어 모델은 다국어 데이터셋으로 학습되었습니다. 이를 통해 여러 언어로 텍스트를 이해하고, 생성하고, 번역할 수 있습니다.
영어로 질문을 하면, 일본어로 응답을 받을 수 있습니다.
Tokenization
신경망이 텍스트를 처리하기 전에, 텍스트는 **토크나이제이션(tokenization)**이라는 과정을 통해 수치 데이터로 변환되어야 합니다.
- 토크나이제이션은 텍스트를 토큰이라고 하는 더 작은 단위로 분해합니다.
- 각 토큰은 모델이 처리할 수 있는 수치 값에 매핑됩니다.
- 모델은 이 토큰들 간의 통계적 관계를 이해하도록 학습하며, 이를 통해 주어진 시퀀스에서 다음 토큰을 예측하고 생성하는 데 도움이 됩니다.
인간 언어의 복잡성—희귀 단어와 철자 오류 포함—을 처리하기 위해, 모델은 Byte Pair Encoding(BPE)와 같은 서브워드 토크나이제이션 기법을 사용합니다. BPE는 개별 문자로 시작하여 가장 빈번한 심볼 쌍을 반복적으로 병합해 새로운 토큰을 형성합니다.
예를 들어 “unbelievable”이라는 단어는 tokenized되어 ["un", "believ", "able"]가 될 수 있습니다. 이는 모델이 학습 중에 명시적으로 보지 못한 단어도 이해하고 생성할 수 있도록 합니다. 최신 모델은 수학과 코드 처리를 위한 특수 토크나이저도 개발했는데, 둘 다 고유한 문법과 구조를 갖고 있기 때문입니다.
Pretraining
대규모 언어 모델(LLMs)은 방대한 양의 데이터로 학습됩니다.
이 데이터를 수집하고 정제하는 것은 단순히 웹사이트를 긁어오는 것보다 훨씬 더 복잡합니다. 질이 낮은 데이터를 모델에 먹이면, 결과도 질이 낮습니다. 쓰레기를 넣으면, 쓰레기가 나옵니다.
Training, 즉 pretraining은 네트워크가 입력 데이터의 패턴을 인식하도록 가르치는 과정입니다. 본질적으로, 우리는 인터넷 텍스트의 큰 덩어리를 모델이 조정할 수 있는 노브인 가중치와 바이어스로 압축하고 있습니다(통칭하여 parameters라고 합니다).
파라미터의 수는 종종 모델의 성능을 측정하는 지표로 사용됩니다. 예를 들어, Llama 같은 오픈소스 모델은 70억과 700억 파라미터 버전이 있으며(후자가 더 강력합니다).
학습은 여러 번 반복되는 큰 for 루프처럼 생각할 수 있습니다. 매번 가중치와 바이어스를 조금씩 조정합니다. 전체 학습 데이터셋을 한 번 완전히 통과하는 것을 epoch라고 합니다. 하지만 네트워크는 이러한 값을 어떻게 조정해야 하는지 어떻게 알까요?
학습 과정은 몇 가지 핵심 단계로 이루어집니다:
- Forward Pass: 네트워크는 입력 데이터를 받아 층을 통과시키며 출력을 생성합니다. 이를 포워드 패스라 합니다. 이 단계에서 네트워크는 현재의 가중치와 바이어스를 기반으로 예측을 수행합니다.
- Loss Function: 출력을 얻은 후, 예측이 얼마나 좋거나 나쁜지 측정해야 합니다. 이는 예측을 측정하고 우리가 최소화하려는 값을 제공하는 손실 함수를 사용하여 수행됩니다.
- Backward Pass(Backpropagation): 네트워크 성능을 개선하기 위해 손실을 줄이도록 가중치와 바이어스 조정을 해야 합니다. 이는 손실 함수의 각 가중치와 바이어스에 대한 그라디언트를 계산하는 것을 포함합니다. 그라디언트는 손실을 최소화하기 위해 파라미터를 조정해야 하는 방향과 크기를 나타냅니다.
- Gradient Descent: 계산된 그라디언트를 사용해 가중치와 바이어스를 업데이트합니다. 이 과정은 손실 함수를 최소화하기 위해 파라미터를 반복적으로 조정하는 경사하강법이라는 최적화 알고리즘4입니다.
Training Pseudocode
weights, biases = init_random_parameters()
epochs = 10
learning_rate = 0.01 # See ⁴
for epoch in range(epochs):
for input_data, target in training_dataset:
predictions = forward_pass(input_data, weights, biases)
loss = loss_function(predictions, target)
gradients = backpropagation(loss, weights, biases)
# Step 4: Gradient Descent (Parameter Update)
weights -= learning_rate * gradients["weights"]
biases -= learning_rate * gradients["biases"]
print(f"Epoch {epoch + 1}/{epochs}, Loss: {loss}")이러한 단계들을 여러 에폭에 걸쳐 반복 수행함으로써, 네트워크는 더 나은 예측을 하도록 학습합니다. 역전파 동안 계산된 그라디언트를 사용해 내부 파라미터를 조정하며, 효과적으로 손실 함수를 최소화하고 시간이 지남에 따라 성능을 개선합니다.
참고: 스케일링 법칙이란?
대규모 언어 모델 학습 맥락에서 스케일링 법칙에 대해 이야기하는 것을 들을 수 있습니다. OpenAI의 연구자들은 ”Scaling Laws for Neural Language Models”에서, 모델을 더 크게 만들고 더 많은 데이터와 계산 자원으로 학습시키면, 성능이 예측 가능한 방식으로 개선되는 경향이 있음을 보여주었습니다.
본질적으로, 문제에 더 많은 계산을 투입하고 더 큰 모델을 구축함으로써 우리는 더 나은 결과를 달성할 수 있었습니다. 그러나 이 접근법의 한계에 가까워지고 있는지에 대해서는 지속적인 논쟁이 있습니다. 일부 전문가들은 우리가 “벽에 부딪히고 있다”고 제안하며, 모델 크기와 계산을 증가시키는 것이 상당한 개선을 계속해서 가져오지 않을 수 있다고 말합니다. 하지만 판결은 아직 나지 않았습니다.
Transformers
“Attention Is All You Need”는 언어 모델을 구축하는 더 효율적이고 강력한 방식인 트랜스포머 아키텍처를 소개했습니다.
처리 전에, 단어는 embeddings라고 하는 수치 벡터로 변환됩니다. 임베딩은 의미 정보를 포착하며, 유사한 단어는 고차원 공간에서 유사한 벡터로 표현됩니다.
예를 들어, “king”과 “queen”의 관계는 “man”과 “woman”의 관계와 유사합니다. “king”에서 “man” 벡터를 빼고 “woman”을 더하면, “queen”에 가까운 결과가 나옵니다.
트랜스포머는 어텐션 메커니즘, 특히 셀프 어텐션을 사용합니다. 어텐션은 이전 논문과 모델에서 사용되었지만, 그때는 시퀀스를 단어별로 한 번에 하나씩만 읽었습니다. 트랜스포머는 전체 입력 시퀀스를 한 번에 고려할 수 있습니다.
이는 사람이 문장이나 문단의 문맥을 고려하여 언어를 이해하는 것처럼, 모델이 출력을 생성할 때 입력의 다른 부분에 집중할 수 있도록 합니다.
예를 들어 “The cat sat on the mat because it was tired.“라는 문장에서 “it”은 “cat”을 가리킵니다. 셀프 어텐션은 관련 단어에 더 높은 가중치를 할당함으로써 이 연결을 확립하도록 도와주며, “it”이 “mat”이 아닌 “cat”을 가리킨다는 것을 이해하게 합니다.
셀프 어텐션을 사용하면, 트랜스포머는 데이터를 한 요소씩이 아니라 전체 시퀀스를 병렬로 처리할 수 있어, 특히 GPU에서 학습이 더 빠르고 효율적입니다.
Fine-tuning
프리트레이닝 이후, 모델은 특정 작업에 맞게 조정되거나 인간의 가치에 맞도록 정렬되어야 할 수 있습니다. 파인튜닝은 사전학습된 모델을 고유한 스타일을 갖게 하거나 특정 도메인에서 잘 수행하도록 조정하는 과정입니다.
파인튜닝은 해당 작업과 관련된 더 작고 고품질의 데이터셋을 사용하는 것을 포함합니다. 이 데이터는 원하는 결과에 맞는 품질과 정렬을 보장하기 위해 종종 사람이 검토합니다. 파인튜닝 동안, 앞서 논의한 모델의 파라미터—가중치와 바이어스—는 이 새로운 데이터셋에서 성능을 향상시키기 위해 약간 조정됩니다.
Reinforcement Learning
모델의 유용성을 더 향상시키고 바람직한 방식으로 동작하도록 보장하기 위해, **인간 피드백을 활용한 강화학습(RLHF)**이라는 기술이 사용됩니다. RLHF는 몇 가지 단계로 이루어집니다:
- AI 모델이 주어진 입력이나 질문에 대해 여러 가능한 응답을 생성합니다.
- 인간 평가자가 이러한 응답을 비교하고, 정확성, 유용성, 정렬과 같은 기준에 따라 최선에서 최악까지 순위를 매깁니다.
- 인간의 순위를 사용해, “보상 모델”이라고 불리는 별도의 모델을 학습시켜 응답의 품질을 예측하도록 합니다.
- 원래 AI 모델은 보상 모델에 의해 안내되는 강화학습 기법을 사용해 파인튜닝됩니다.
RLHF는 수백에서 수천 개의 고품질 인간 평가 예시로도 효과적일 수 있습니다. 각 예시는 인간의 선호에 의해 신중하게 판단되므로, 양보다 질에 중점을 둡니다.
예를 들어, 고객 지원을 위한 언어 모델을 원한다면, 고객 서비스 상호작용의 대화록으로 모델을 파인튜닝하고, RLHF를 사용해 공손하게 응답하고 고객 문제를 효과적으로 해결하도록 보장할 수 있습니다. 점점 더, AI는 초기 콘텐츠를 생성하고 인간이 이 피드백 루프를 통해 이를 정제하는 데 도움을 줍니다.
Inference
학습과 파인튜닝 이후, 모델은 새로운 입력으로부터 예측하거나 출력을 생성하는 과정인 **추론(inference)**을 수행할 준비가 됩니다. 추론 중 모델은 학습한 내용을 적용해 응답을 생성합니다. 모델은 멀티모달 입력(텍스트, 이미지, 오디오 등)과 출력을 지원할 수 있습니다.
추론에서 중요한 파라미터 중 하나는 temperature로, 모델의 출력이 얼마나 무작위적이거나 집중되어 있는지를 제어합니다. 낮은 temperature는 모델을 더 결정론적으로 만들고, 높은 temperature는 창의성과 무작위성을 증가시킵니다.
현대의 언어 모델은 chain of thought 같은 기법을 사용해 복잡한 추론 작업을 수행할 수 있습니다. 이는 모델이 최종 답에 도달하기 전에 중간 추론 단계를 생성하는 것으로, 여러분이 수학 문제를 단계별로 풀어가는 방식과 비슷합니다5. CoT는 프롬프트 기법으로 시작했지만, 최신의 추론 모델은 이 행동을 모델 자체에 직접 학습시켰습니다.
| temerature 0.1 | temperature 1 | temperature 2 |
|---|---|---|
Distillation
때로는 더 작은 기기에서 효율적으로 실행되거나 더 낮은 추론 비용으로 수백만 개의 요청을 처리할 수 있는 모델이 필요합니다. 그럴 때 **모델 증류(distillation)**가 사용됩니다. 이는 더 작고 빠르면서도 성능이 좋은 모델을 만드는 기술입니다.
이 과정은 더 작은 “학생” 모델이 더 큰 “교사” 모델을 모방하도록 학습하는 방식으로 작동합니다. 원시 데이터로부터 학습하는 대신, 학생은 교사의 출력을 통해 학습합니다. 지식의 압축 버전이라고 생각하세요. 일부 세부 정보는 잃지만, 중요한 대부분은 유지합니다.
예를 들어, Llama는 오픈소스 모델로, 증류된 버전이 많이 있습니다. 증류 버전은 더 빠르게 실행되고 추론 비용이 더 낮지만, 특정 작업에서는 여전히 매우 뛰어납니다. 대가로는 일반적으로 약간의 품질 저하가 발생하지만, 효율성에서 상당한 이득을 얻습니다.
Evaluation
모델이 좋은지 어떻게 알 수 있을까요? 여기서 평가(종종 “evals”로 줄여 부름)가 중요해집니다. 마치 테스트 없이 코드를 배포하지 않듯(그렇죠…), 평가 없이 AI 모델을 배포해서는 안 됩니다.
평가는 정답이 알려진 작업 집합에 대해 모델을 테스트하는 것을 포함합니다. 이는 코딩 작업을 위한 SWE-bench 같은 표준화된 벤치마크일 수도 있고, 사용 사례에 특화된 커스텀 평가일 수도 있습니다. 핵심은 여러분의 앱에 대해 실제로 중요한 것을 측정하는 것입니다.
좋은 평가는 엣지 케이스를 잡아낼 만큼 포괄적이어야 하며, 동시에 실제 사용 사례를 대표해야 합니다. 여러분의 도메인에 맞춘 커스텀 평가를 구축하는 것은 모델 학습만큼이나 중요합니다. 그렇지 않으면 품질이 회귀하는지 알 수 없습니다.
Example: Coding Assistant
개발 팀을 위한 맞춤 코딩 어시스턴트를 구축하고 싶다고 합시다:
- 이미 코드를 이해하는 기존 모델로 시작할 가능성이 높습니다. 예를 들어 Qwen Coder 또는 유사한 오픈소스 모델입니다. 이러한 모델은 방대한 양의 공개 코드로 사전학습되어, 문법과 일반적인 패턴을 이해합니다.
- 그 다음에는 팀의 특정 코드베이스로 이 모델을 파인튜닝할 것입니다. 여기에는 비공개 저장소, 내부 문서, 코딩 표준, 아키텍처 패턴 등이 포함될 수 있습니다. 모델은 팀이 코드를 작성하는 방식의 뉘앙스를 학습합니다.
- 어시스턴트가 유용한 제안을 제공하도록 보장하기 위해 RLHF를 구현할 수 있습니다. 개발자가 모델의 코드 제안을 평가하여, 기술적으로는 맞지만 비실용적이거나 주관적으로 좋지 않은 코드와 대비해 실제로 유용한 보완을 학습하도록 돕습니다.
- 마지막으로, 이 어시스턴트를 모든 개발자의 노트북에 배포해야 한다면, 값비싼 GPU 없이 로컬에서 실행할 수 있도록 모델을 더 작은 크기로 증류할 수 있습니다.
- 이 과정 전반에 걸쳐, 자동화된 테스트(생성된 코드가 컴파일되는가? 테스트를 통과하는가?)와 인간 피드백(개발자가 유용하다고 느끼는가?)을 사용하여 모델을 지속적으로 평가할 것입니다.
그런 다음 모델 품질을 계속 반복 개선합니다. 개발 팀은 모델의 제안에 대한 피드백을 제공할 수 있으며, 이는 파인튜닝 또는 RLHF를 위한 더 많은 데이터가 됩니다. 수집한 피드백의 양과 모델이 개선되어야 하는 정도에 따라 월간 또는 분기별로 재학습할 수 있습니다.
Resources
여전히 관심이 있으신가요? 배울 것이 너무도 많습니다. 제 글이 여러분의 관심을 불러일으켰길 바랍니다. 다른 자료가 있으시다면 연락 주시면 여기 추가하겠습니다.
- Andrej Karpathy on Neural Nets
- 3Blue1Brown on Neural Nets
- Machine Learning for Web Developers
- Build a Large Language Model (From Scratch)
Footnotes
-
과거의 일부 AI 모델은 그렇지 않았습니다 — 1964년에 등장한 최초의 챗봇인 ELIZA를 참고하시기 바랍니다. ↩
-
여기서 예측 가능성을 더하기 위해 **평가(evals)**가 사용되며, AI에 대한 종단간 테스트처럼 생각하실 수 있습니다. 또한 모델의 **온도(temperature)**를 조절할 수 있습니다. 낮은 온도는 출력을 더 결정론적으로 만들어 가장 가능성이 높은 응답에 집중하게 하고, 높은 온도는 무작위성을 증가시켜 보다 창의적인 출력을 가능하게 합니다. ↩
-
활성화 함수는 뉴런이 다음 층으로 얼마나 많은 정보를 전달할지 결정하며, 종종 값을 0에서 1(또는 -1에서 1) 범위로 압축한다는 점에서, 뉴런의 “켜짐/꺼짐” 정도를 조절하는 스위치나 dimmer와 같다고 생각하시면 됩니다. 예로는 ReLU(정류 선형 단위)와 시그모이드 함수가 있습니다. ↩
-
기본적인 경사하강법에서는 고정된 학습률이 손실 함수를 최소화하기 위해 이동하는 단계의 크기를 결정합니다. 이 학습률을 설정하는 일은 까다로운데, 너무 높으면 최소값을 지나쳐 버릴 수 있고 너무 낮으면 학습이 매우 느려질 수 있습니다. 더 발전된 최적화 알고리즘은 학습률을 상황에 맞게 자동으로 조정하여 초기에 더 큰 보폭으로 이동하고 이후에는 더 작은 보폭으로 이동하게 합니다. ↩
-
프롬프트 엔지니어링은 AI 모델로부터 최적의 출력을 얻기 위해 효과적인 입력을 설계하는 기술입니다. 구체적으로 표현하기, 예시를 제공하기(퓨샷 러닝), 요청을 명확하게 구조화하기 등의 기법이 포함됩니다. 엔지니어에게는 새로운 모델을 학습시키는 것보다 프롬프트 엔지니어링을 숙달하는 것이 실용적인 경우가 많습니다. ↩