LiManshu's picture
Add files using upload-large-folder tool
bf6be45 verified
Raw
History Blame Contribute Delete
8.12 kB
"""优化器:AdamW / 学习率调度"""
# 2026-01-23
import math
import torch.optim as optim
from torch.optim.lr_scheduler import LambdaLR
def get_optimizer(model, config):
"""
获取优化器(AdamW)
参数:
model: 模型
config: 训练配置字典,应包含以下键:
- learning_rate: 学习率(默认: 1e-4)
- weight_decay: 权重衰减(默认: 0.01)
- beta1: Adam beta1(默认: 0.9)
- beta2: Adam beta2(默认: 0.999)
- eps: Adam epsilon(默认: 1e-8)
返回:
AdamW 优化器
"""
lr = float(config.get("learning_rate", 1e-4))
weight_decay = float(config.get("weight_decay", 0.01))
beta1 = float(config.get("beta1", 0.9))
beta2 = float(config.get("beta2", 0.999))
eps = float(config.get("eps", 1e-8))
optimizer = optim.AdamW(
model.parameters(),
lr=lr,
weight_decay=weight_decay,
betas=(beta1, beta2),
eps=eps,
)
return optimizer
def get_lr_scheduler(optimizer, config):
"""
获取学习率调度器(支持预热)
参数:
optimizer: 优化器
config: 训练配置字典,应包含以下键:
- lr_scheduler: 调度器类型,可选值:
- "cosine": 余弦退火(带预热)
- "linear": 线性衰减(带预热)
- "constant": 常数学习率
- warmup_steps: 预热步数(默认: 100)
- max_steps: 最大训练步数(默认: 10000)
返回:
学习率调度器(如果为 constant,返回 None)
"""
scheduler_type = config.get("lr_scheduler", "cosine")
max_steps = config.get("max_steps", 10000)
warmup_steps = config.get("warmup_steps", 100)
if scheduler_type == "cosine":
# 余弦退火调度(带预热)
def lr_lambda(step):
if step < warmup_steps:
# 预热阶段:线性增加从 0 到 1
return step / warmup_steps if warmup_steps > 0 else 1.0
else:
# 余弦退火阶段:从 1 衰减到 0
# 处理边界情况:如果 max_steps <= warmup_steps,直接返回最小值
if max_steps <= warmup_steps:
return 0.0
progress = (step - warmup_steps) / (max_steps - warmup_steps)
# 限制 progress 在 [0, 1] 范围内
progress = min(progress, 1.0)
# 余弦退火:0.5 * (1 + cos(π * progress))
return 0.5 * (1.0 + math.cos(progress * math.pi))
scheduler = LambdaLR(optimizer, lr_lambda)
elif scheduler_type == "linear":
# 线性衰减调度(带预热)
def lr_lambda(step):
if step < warmup_steps:
# 预热阶段:线性增加从 0 到 1
return step / warmup_steps if warmup_steps > 0 else 1.0
else:
# 线性衰减阶段:从 1 线性衰减到 0.1
# 处理边界情况:如果 max_steps <= warmup_steps,直接返回最小值
if max_steps <= warmup_steps:
return 0.1
progress = (step - warmup_steps) / (max_steps - warmup_steps)
# 限制 progress 在 [0, 1] 范围内
progress = min(progress, 1.0)
# 线性衰减:从 1.0 到 0.1
return 1.0 - 0.9 * progress
scheduler = LambdaLR(optimizer, lr_lambda)
elif scheduler_type == "constant":
# 常数学习率(无调度器)
scheduler = None
else:
raise ValueError(
f"未知的学习率调度器类型: {scheduler_type}。"
f"支持的类型: cosine, linear, constant"
)
return scheduler
if __name__ == "__main__":
import sys
import io
# 设置输出编码为 UTF-8(Windows 兼容)
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
print("=" * 60)
print("优化器和学习率调度器测试")
print("=" * 60)
# 创建一个简单的模型用于测试
import torch.nn as nn
class SimpleModel(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(10, 1)
def forward(self, x):
return self.linear(x)
model = SimpleModel()
# 测试配置
config = {
"learning_rate": 1e-3,
"weight_decay": 0.01,
"beta1": 0.9,
"beta2": 0.999,
"eps": 1e-8,
"lr_scheduler": "cosine",
"warmup_steps": 10,
"max_steps": 100,
}
# 测试优化器
print("\n1. 测试优化器创建")
optimizer = get_optimizer(model, config)
print(f" 优化器类型: {type(optimizer).__name__}")
print(f" 学习率: {optimizer.param_groups[0]['lr']}")
print(f" 权重衰减: {optimizer.param_groups[0]['weight_decay']}")
print(f" Beta1: {optimizer.param_groups[0]['betas'][0]}")
print(f" Beta2: {optimizer.param_groups[0]['betas'][1]}")
# 测试学习率调度器(cosine)
print("\n2. 测试余弦退火学习率调度器(带预热)")
scheduler = get_lr_scheduler(optimizer, config)
print(f" 调度器类型: {type(scheduler).__name__}")
print(f" 初始学习率: {optimizer.param_groups[0]['lr']:.6f}")
# 模拟训练步骤,观察学习率变化
print("\n3. 模拟训练步骤,观察学习率变化(cosine)")
lrs = []
for step in range(0, 101, 10):
current_lr = optimizer.param_groups[0]["lr"]
lrs.append(current_lr)
print(f" 步数 {step:3d}: 学习率 = {current_lr:.6f}")
# 模拟优化步骤(先 optimizer.step(),再 scheduler.step())
# 创建一个虚拟的损失并反向传播(仅用于测试)
dummy_loss = sum(p.sum() for p in model.parameters())
dummy_loss.backward()
optimizer.step()
optimizer.zero_grad()
if scheduler is not None:
scheduler.step()
# 测试线性调度器
print("\n4. 测试线性学习率调度器(带预热)")
config_linear = config.copy()
config_linear["lr_scheduler"] = "linear"
optimizer_linear = get_optimizer(model, config_linear)
scheduler_linear = get_lr_scheduler(optimizer_linear, config_linear)
print(f" 调度器类型: {type(scheduler_linear).__name__}")
print("\n5. 模拟训练步骤,观察学习率变化(linear)")
for step in range(0, 101, 10):
current_lr = optimizer_linear.param_groups[0]["lr"]
print(f" 步数 {step:3d}: 学习率 = {current_lr:.6f}")
# 模拟优化步骤(先 optimizer.step(),再 scheduler.step())
dummy_loss = sum(p.sum() for p in model.parameters())
dummy_loss.backward()
optimizer_linear.step()
optimizer_linear.zero_grad()
if scheduler_linear is not None:
scheduler_linear.step()
# 测试常数学习率
print("\n6. 测试常数学习率")
config_constant = config.copy()
config_constant["lr_scheduler"] = "constant"
optimizer_constant = get_optimizer(model, config_constant)
scheduler_constant = get_lr_scheduler(optimizer_constant, config_constant)
print(f" 调度器类型: {scheduler_constant}")
print(f" 学习率: {optimizer_constant.param_groups[0]['lr']:.6f}")
# 测试错误类型
print("\n7. 测试错误类型处理")
try:
config_error = config.copy()
config_error["lr_scheduler"] = "invalid"
scheduler_error = get_lr_scheduler(optimizer, config_error)
except ValueError as e:
print(f" 正确捕获错误: {e}")
print("\n" + "=" * 60)
print("所有测试完成!")
print("=" * 60)