# 옵티마이저와 스케줄러

옵티마이저는 학습 중에 모델 가중치를 업데이트합니다. 스케줄러는 옵티마이저를 감싸서 각 학습 단계마다 학습률을 조정합니다. [Trainer](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer)는 [create_optimizer_and_scheduler()](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer.create_optimizer_and_scheduler)를 호출할 때 이 둘을 모두 생성합니다.

```md
                                    ┌────────────┐         ┌──────────────┐
                                    │ Optimizer  │         │  Scheduler   │
                                    │ (adamw_torch_fused)◄─│  (linear)    │
                                    │            │         │              │
                                    │ param_groups         |              |
                                    │  └ lr       ◄────────┤              |
                                    │  └ weight_decay      │              │
                                    └──────┬─────┘         └──────────────┘
                                           │                      
  ┌──── EACH TRAINING STEP ───────────────────────────────────────────┐
  │                                        │                          │
  │   model(batch)                         │                          │
  │       │                                │                          │
  │       ▼                                │                          │
  │     loss ──► loss.backward() ──► param.grad                       │
  │                                        │                          │
  │                          ┌─────────────┘                          │
  │                          ▼                                        │
  │              optimizer.step()                                     │
  │                          │                                        │
  │                          ▼                                        │
  │                   param.data updated                              │
  │                          │                                        │
  │                          ▼                                        │
  │              lr_scheduler.step()  ──► recalculates lr             │
  │                          │            writes to optimizer         │
  │                          ▼            .param_groups['lr']         │
  │              model.zero_grad()                                    │
  │                                                                   │
  └───────────────────────────────────────────────────────────────────┘
```

`lr_scheduler_type()`와 `optim()`와 같은 옵티마이저 및 스케줄러 동작을 [TrainingArguments](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.TrainingArguments)에서 설정합니다. 기본값(`adamw_torch` 옵티마이저와 `linear` 웜업 스케줄러)은 대부분의 파인튜닝 실행에 좋은 출발점이 됩니다.

```py
from transformers import TrainingArguments

args = TrainingArguments(
    ...,
    # Optimizer
    optim="adamw_torch",          # or "adamw_torch_fused", "adafactor", "sgd", etc.
    learning_rate=2e-5,
    weight_decay=0.01,
    adam_beta1=0.9,
    adam_beta2=0.999,
    adam_epsilon=1e-8,
    # Scheduler
    lr_scheduler_type="cosine",   # "linear", "cosine", "constant_with_warmup", etc.
    warmup_steps=500,
    lr_scheduler_kwargs={"num_cycles": 3},  # scheduler-specific extras
)
```

## 메트릭 기반 스케줄러

일부 스케줄러는 고정된 스케줄을 따르는 대신 학습 동적 특성에 적응합니다.

[GreedyLR](https://huggingface.co/papers/2512.14527)는 평가 결과를 기반으로 학습률을 업데이트합니다. 지표가 계속 개선되면 `factor`로 나누어 학습률을 높이고, 지표가 개선되지 않으면 `factor`를 곱하여 학습률을 낮춥니다. 학습률이 `min_lr`에 도달한 후 `reset_start` 스텝 동안 개선되지 않으면, [GreedyLR](/docs/transformers/v5.23.0/ko/main_classes/optimizer_schedules#transformers.GreedyLR)는 초기 상태로 리셋되어 새로운 사이클을 시작합니다.

[GreedyLR](/docs/transformers/v5.23.0/ko/main_classes/optimizer_schedules#transformers.GreedyLR)는 학습 중 평가가 필요합니다. `eval_strategy`를 `"steps"` 또는 `"epoch"`으로 설정하세요.

```diff
args = TrainingArguments(
+   lr_scheduler_type="greedy",
+   lr_scheduler_kwargs={"patience": 10, "factor": 0.95, "min_lr": 1e-5},
+   eval_strategy="steps",
+   eval_steps=200,
    ...  # remaining args from the TrainingArguments intro config
)
```

> [!TIP]
> 기본 `mode="min"`은 loss에 적용됩니다. accuracy처럼 값이 높을수록 좋은 메트릭을 추적하는 경우, `lr_scheduler_kwargs`에 `"mode": "max"`를 전달하세요.

전체 설정 가능한 매개변수 목록은 [GreedyLR](/docs/transformers/v5.23.0/ko/main_classes/optimizer_schedules#transformers.GreedyLR) 클래스를 참조하세요.

## 옵티마이저 통합

Transformers는 특수한 학습 시나리오를 위해 서드파티 옵티마이저를 통합합니다.

| 옵티마이저 | 설치 | `optim="value"` | 설명 |
|---|---|---|---|
| APOLLO | `apollo-torch` | `apollo_adamw` | Memory-efficient full-param via random projections; rank-1 sufficient |
| GrokAdamW | `grokadamw` | `grokadamw` | 지연 일반화(그로킹)를 대상으로 합니다 |
| LOMO / AdaLomo | `lomo-optim` | `lomo` / `adalomo` | … |
| 스케줄 없음 | `schedulefree` | `schedule_free_adamw`, `schedule_free_radam`, `schedule_free_sgd` | LR annealing을 제거합니다. `lr_scheduler_type="constant"`와 함께 사용하세요 |
| GaLore | `galore-torch` | `galore_adamw`, `galore_adafactor`, `galore_adamw_8bit` | 그라디언트 저랭크 투영을 통한 전체 파라미터 학습 |
| StableAdamW | `torch-optimi` | `stable_adamw` | AdamW + AdaFactor 업데이트 클리핑; 그라디언트 클리핑 불필요 |

```bash
pip install apollo-torch
```

[Approximated Gradient Scaling for Memory Efficient LLM Optimization (APOLLO)](https://huggingface.co/papers/2412.05270)는 사전 학습 및 파인튜닝 시 전체 파라미터 학습을 위한 메모리 효율적인 옵티마이저입니다. SVD 대신 저렴한 랜덤 프로젝션을 사용하여 SGD 수준의 메모리 비용으로 AdamW와 동등한 성능을 달성합니다. 극단적인 메모리 절감이 필요할 때는 랭크-1 변형인 APOLLO-Mini를 사용하세요.

`optim_target_modules` 매개변수를 사용하여 학습할 레이어를 지정합니다.

```diff
args = TrainingArguments(
+   optim="apollo_adamw",
+   optim_target_modules=[r".*.attn.*", r".*.mlp.*"],
    ...  # remaining args from the TrainingArguments intro config
)
```

`optim_args`를 통해 추가 하이퍼파라미터를 전달합니다.

> [!TIP]
> `scale`을 `n/r`로 설정하세요. 여기서 `n`은 원래 공간의 차원, `r`은 저랭크 공간의 차원입니다. `scale`을 기본값으로 유지하면서 학습률을 조정하면 유사한 효과를 얻을 수 있습니다.

| parameter | 설명 | APOLLO | APOLLO-Mini |
|---|---|---|---|
| rank | rank | 256 | 1 |
| scale_type | 스케일 팩터가 적용되는 방식 | `channel` (채널별 스케일링) | `tensor` (텐서별 스케일링) |
| scale | 트레이닝을 안정화하기 위해 그라디언트 업데이트를 조정 | 1.0 | 128 |
| update_proj_gap | 투영 행렬을 업데이트하기 전 단계 | 200 | 200 |
| proj | projection type | `random` | `random` |

rank-1 구성으로 APOLLO-Mini를 활성화합니다.

```py
args = TrainingArguments(
    optim="apollo_adamw",
    optim_target_modules=[r".*.attn.*", r".*.mlp.*"],
    optim_args="proj=random,rank=1,scale=128.0,scale_type=tensor,update_proj_gap=200",
    ...  # remaining args from the TrainingArguments intro config
)
```

```bash
pip install grokadamw
```

[GrokAdamW](https://github.com/cognitivecomputations/grokadamw)는 느리게 변화하는 기울기로 인해 모델이 지연된 일반화를 보이는 *grokking* 현상을 대상으로 합니다.

```diff
args = TrainingArguments(
+   optim="grokadamw",
    ...  # remaining args from the TrainingArguments intro config
)
```

```bash
pip install lomo-optim
```

[Low-Memory Optimization (LOMO)](https://github.com/OpenLMLab/LOMO)는 저메모리 전체 파라미터 파인튜닝을 위한 두 가지 옵티마이저인 [LOMO](https://huggingface.co/papers/2306.09782)와 [AdaLomo](https://hf.co/papers/2310.10195)를 포함합니다. 두 옵티마이저 모두 그래디언트 계산과 파라미터 업데이트를 하나의 단계로 통합합니다. AdaLomo는 Adam과 유사하게 파라미터별 적응형 학습률을 추가합니다.

> [!TIP]
> AdaLomo는 `grad_norm` 없이 사용할 때 성능과 처리량이 향상되어 가장 효과적입니다.

```diff
args = TrainingArguments(
+   optim="adalomo",
    learning_rate=2e-6,
    ...  # remaining args from the TrainingArguments intro config
)
```

```bash
pip install schedulefree
```

[Schedule Free optimizer(SFO)](https://hf.co/papers/2405.15682)는 모멘텀을 평균화와 보간의 조합으로 대체하여 학습률 어닐링이 전혀 필요 없도록 합니다.

SFO는 RAdam(`schedule_free_radam`), AdamW(`schedule_free_adamw`), SGD(`schedule_free_sgd`) 옵티마이저를 지원합니다. RAdam 스케줄러는 `warmup_steps`가 필요하지 않습니다.

SFO를 `lr_scheduler_type="constant"`와 함께 사용하세요. 다른 스케줄러 유형도 작동하지만 SFO의 의도된 동작에 영향을 미칩니다.

```diff
args = TrainingArguments(
+   optim="schedule_free_radam",
+   lr_scheduler_type="constant",
    learning_rate=2e-6,
    ...  # remaining args from the TrainingArguments intro config
)
```

```bash
pip install torch-optimi
```

[StableAdamW](https://huggingface.co/papers/2304.13013)는 AdaFactor의 업데이트 클리핑을 AdamW에 이식하여 그라디언트 클리핑이 필요 없도록 합니다. 그 외에는 AdamW의 대체재로 바로 사용할 수 있습니다.

> [!TIP]
> 큰 배치 크기로 학습 중이거나 손실 스파이크가 여전히 관찰되는 경우, `beta_2`를 0.95에서 0.99 사이로 설정해 보세요.

```diff
args = TrainingArguments(
+   optim="stable_adamw",
    learning_rate=2e-6,
    ...  # remaining args from the TrainingArguments intro config
)
```

```bash
pip install galore-torch trl
```

[Gradient Low-Rank Projection (GaLore)](https://hf.co/papers/2403.03507)는 LLM 학습 시 메모리를 절감합니다. [LoRA](https://hf.co/papers/2106.09685)와 같은 저랭크 적응 방법과 달리, GaLore은 *전체 파라미터* 학습을 유지합니다.

[trl.SFTConfig](https://huggingface.co/docs/trl/v1.17.1/en/sft_trainer#trl.SFTConfig)의 `optim`을 GaLore 옵티마이저(`"galore_adamw"`, `"galore_adafactor"`, 또는 `"galore_adamw_8bit"`)로 설정합니다. `optim_target_modules`로 대상 모듈을 지정하고, `optim_args`를 통해 GaLore 전용 매개변수(`rank`, `update_proj_gap`, `scale`)를 설정합니다.

```py
from trl import SFTConfig

args = SFTConfig(
    output_dir="./galore",
    max_steps=100,
    optim="galore_adamw",
    optim_target_modules=[r".*.attn.*", r".*.mlp.*"],
    optim_args="rank=64, update_proj_gap=100, scale=0.10",
)
```

레이어별 최적화를 위해 옵티마이저 이름에 `_layerwise`를 추가합니다(`"galore_adamw_layerwise"`). GaLore의 대상인 선형 레이어만 저랭크 분해를 사용하며, 나머지 레이어는 일반 방식으로 최적화됩니다.

```py
from trl import SFTConfig, SFTTrainer

args = SFTConfig(
    output_dir="./galore",
    max_steps=100,
    optim="galore_adamw_layerwise",
    optim_target_modules=[r".*.attn.*", r".*.mlp.*"],
    optim_args="rank=64, update_proj_gap=100, scale=0.10",
)
```

Layerwise mode는 실험적 기능입니다. [단일 GPU](https://github.com/jiaweizzhao/GaLore?tab=readme-ov-file#train-7b-model-with-a-single-gpu-with-24gb-memory)에서만 실행되며, DistributedDataParallel(DDP)을 지원하지 않고, gradient clipping 및 DeepSpeed가 작동하지 않을 수 있습니다.

## 옵티마이저와 스케줄러 사용자 정의

아직 통합되지 않은 옵티마이저를 사용하거나, 레이어별 학습률을 조정하거나, 사용자 정의 로직을 적용하려면 사용자 정의 옵티마이저와 스케줄러를 생성하세요.

### 클래스와 kwargs를 전달

`~Trainer.optimizer_cls_and_kwargs`는 사용자 정의 optimizer 클래스를 허용하되, 파라미터 그룹화와 디바이스 배치는 [Trainer](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer)에 위임합니다.

[Trainer](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer)는 [create_optimizer()](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer.create_optimizer)가 실행될 때까지 옵티마이저 생성을 지연시키므로, 모델은 이미 올바른 장치에 배치되어 있습니다.

```py
import torch

trainer = Trainer(
    ...
    optimizer_cls_and_kwargs=(
        torch.optim.SGD,
        {"momentum": 0.9, "nesterov": True}
    ),
)
```

### prebuilt 인스턴스 전달

`~Trainer.optimizers`에 미리 정의된 optimizer와 scheduler를 전달합니다. [Trainer](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer)는 미리 생성된 인스턴스가 제공되면 [create_optimizer()](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer.create_optimizer)와 [create_scheduler()](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer.create_scheduler)를 건너뜁니다. scheduler를 전달하지 않으면 [Trainer](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer)가 자동으로 생성합니다.

> [!WARNING]
> 모델을 올바른 장치에 배치한 후 옵티마이저를 구성하세요. 매개변수는 `Trainer`가 모델을 이동하기 전에 구성 시점에 결정됩니다. 분산 학습에서는 장치 불일치가 조용히 잘못된 동작을 유발할 수 있습니다.

```py
import torch
from transformers import Trainer, get_cosine_schedule_with_warmup

optimizer = torch.optim.AdamW(model.parameters(), lr=2e-5)
scheduler = get_cosine_schedule_with_warmup(
    optimizer, num_warmup_steps=500, num_training_steps=10_000
)

trainer = Trainer(
    ...
    optimizers=(optimizer, scheduler),
)
```

Prebuilt instances는 [create_optimizer()](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer.create_optimizer)와 [create_scheduler()](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer.create_scheduler)를 우회하므로, 직접 parameter groups를 지정해야 합니다.

### 옵티마이저 및 스케줄러 메서드 오버라이드

전체 제어를 위해 [create_optimizer()](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer.create_optimizer)와 [create_scheduler()](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer.create_scheduler)를 서브클래스로 구현하세요. 두 메서드는 모두 [train()](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer.train) *실행 중*에 호출됩니다.

[SchedulerType](/docs/transformers/v5.23.0/ko/main_classes/optimizer_schedules#transformers.SchedulerType)에 없는 [OneCycleLR](https://docs.pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.OneCycleLR.html)와 같은 스케줄러를 사용하려면 [create_scheduler()](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer.create_scheduler)를 오버라이드하세요.

각 메서드에서 `self`에 할당하고 이를 반환하도록 확인하세요.

```py
import torch
from transformers import Trainer

class MyTrainer(Trainer):

    def create_scheduler(self, num_training_steps, optimizer=None):
        optimizer = optimizer or self.optimizer
        self.lr_scheduler = torch.optim.lr_scheduler.OneCycleLR(
            optimizer,
            max_lr=0.1,
            total_steps=num_training_steps,
        )
        return self.lr_scheduler
```

기본 옵티마이저가 작동하는 경우 [create_optimizer()](/docs/transformers/v5.23.0/ko/main_classes/trainer#transformers.Trainer.create_optimizer)를 오버라이드할 필요가 없습니다. 메서드를 완전히 교체하는 것보다 `super()`로 메서드를 확장하는 것이 더 쉽습니다. 예를 들어, 모든 다른 설정은 그대로 유지하면서 추가적인 파라미터 그룹을 추가할 수 있습니다.

```py
class MyTrainer(Trainer):
    def create_optimizer(self, model=None):
        super().create_optimizer(model)  # builds the default two param groups
        # add extra param group
        self.optimizer.add_param_group({
            "params": self.model.classifier.parameters(),
            "lr": self.args.learning_rate * 10,
        })
        return self.optimizer
```

