DoRA 深度解析:权重分解低秩适应的理论与实践

3397 字
17 分钟
DoRA 深度解析:权重分解低秩适应的理论与实践

1. 引言:LoRA 的隐含假设#

1.1 LoRA 的成功与局限#

LoRA 已经成为 LLM 微调的事实标准,但它有一个隐含假设:

LoRA 的核心假设:
W_new = W₀ + BA
隐含假设:W₀ 和 BA 可以直接相加
→ 即 W₀ 和 ΔW = BA 在同一个量纲空间
实际问题:
→ 方向更新和幅度更新被耦合
→ 无法独立控制两者的学习率
→ 可能导致次优收敛

1.2 DoRA 的核心洞察#

DoRA(Weight-Decomposed Low-Rank Adaptation,Liu et al., 2024)的核心发现:

┌─────────────────────────────────────────────────────────────┐
│ DoRA 的核心洞察 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 任何矩阵 W 都可以唯一分解为: │
│ │
│ W = m · (W / ||W||_F) │
│ │
│ 其中: │
│ • m = ||W||_F (幅度,标量) │
│ • W / ||W||_F (方向,矩阵) │
│ │
│ DoRA 将这个分解应用于权重更新: │
│ │
│ W_new = W₀ + ΔW │
│ = W₀ + m_DoRA · (BA / ||BA||_F) │
│ │
│ 幅度和方向可以独立学习! │
│ │
└─────────────────────────────────────────────────────────────┘

1.3 本系列文章关联#

文章关联
PEFT/LoRA 深度解析LoRA 基础原理与变体
QLoRA 深度解析量化感知 LoRA
后训练深度解析对齐训练的完整流水线

2. 权重分解的数学原理#

2.1 矩阵的幅度-方向分解#

class WeightDecomposition:
"""
矩阵的幅度-方向分解
对于任意非零矩阵 W ∈ R^(d×k):
W = ||W||_F · (W / ||W||_F)
其中:
- ||W||_F = sqrt(Σᵢ Σⱼ Wᵢⱼ²) 是 Frobenius 范数(幅度)
- W / ||W||_F 是归一化矩阵(方向)
"""
def decompose(self, W):
"""
分解矩阵
返回:(magnitude, direction)
"""
# 计算 Frobenius 范数
magnitude = torch.norm(W, p="fro") # ||W||_F
# 归一化得到方向
direction = W / (magnitude + 1e-8)
return magnitude, direction
def reconstruct(self, magnitude, direction):
"""
重构矩阵
"""
return magnitude * direction
def properties(self):
"""
分解的性质
"""
return {
"uniqueness": "分解是唯一的(当 W ≠ 0)",
"orthogonal": "方向矩阵的 Frobenius 范数为 1",
"independent": "幅度和方向可以独立变化",
"geometric": "类似向量的幅度-方向表示",
}

2.2 为什么这种分解有用?#

幅度-方向分解的直观理解:
类比 1D 向量:
v = |v| · (v / |v|)
• |v| 控制向量的"长度"
• v/|v| 控制向量的"指向"
DoRA 的类比:
W = ||W||_F · (W / ||W||_F)
• ||W||_F 控制权重的"强度"
• W/||W||_F 控制权重的"模式"
优势:
→ 幅度和方向可以独立调优
→ 更细粒度的控制
→ 更好的收敛性

2.3 DoRA 的数学形式化#

class DoRAMath:
"""
DoRA 的数学形式化
"""
def lora_update(self):
"""
LoRA 更新
W_new^LoRA = W₀ + BA
问题:
- BA 的幅度和方向被联合优化
- 无法独立控制学习率
"""
return "W_new^LoRA = W₀ + BA"
def dora_update(self):
"""
DoRA 更新
W_new^DoRA = W₀ + m · (BA / ||BA||_F)
其中:
- m ∈ R^(d×k) 是幅度矩阵(可学习)
- BA / ||BA||_F 是方向矩阵(归一化)
优势:
- m 控制每个元素的幅度变化
- 方向被归一化,独立于幅度
"""
return "W_new^DoRA = W₀ + m · (BA / ||BA||_F)"
def gradient_analysis(self):
"""
梯度分析
DoRA 的梯度:
∂L/∂A = (m / ||BA||_F) · ∂L/∂(W_new) · B^T
∂L/∂B = (m / ||BA||_F) · W₀^T · ∂L/∂(W_new)
∂L/∂m = (BA / ||BA||_F) · ∂L/∂(W_new)
关键观察:
- A, B 的梯度被 ||BA||_F 归一化
- m 的梯度直接作用于方向
- 幅度和方向的更新解耦
"""
return {
"grad_A": "∂L/∂A = (m / ||BA||_F) · ∂L/∂W_new · B^T",
"grad_B": "∂L/∂B = (m / ||BA||_F) · W₀^T · ∂L/∂W_new",
"grad_m": "∂L/∂m = (BA / ||BA||_F) · ∂L/∂W_new",
}

3. DoRA vs LoRA:深度对比#

3.1 结构对比#

class LoRAvsDoRA:
"""
LoRA vs DoRA 结构对比
"""
def compare_structure(self):
"""
结构对比
"""
return {
"loRA": {
"formula": "W_new = W₀ + BA",
"parameters": ["A (r×k)", "B (d×r)"],
"parameter_count": "r × (d + k)",
"update_type": "joint",
},
"doRA": {
"formula": "W_new = W₀ + m · (BA / ||BA||_F)",
"parameters": ["A (r×k)", "B (d×r)", "m (d×k)"],
"parameter_count": "r × (d + k) + d×k",
"update_type": "decoupled (magnitude + direction)",
},
}
def parameter_overhead(self):
"""
参数量开销
DoRA 相比 LoRA 额外增加了 d×k 个幅度参数
"""
return {
"lora_7b": "~8M params",
"dora_7b": "~125M params (额外幅度)",
"overhead": "~16x 参数",
"justification": "幅度参数是低秩的,不是全量 d×k",
}

3.2 收敛性对比#

收敛性对比:
LoRA:
→ BA 的幅度随训练变化
→ 可能导致更新不稳定
→ 需要小心控制学习率
DoRA:
→ 方向被归一化,稳定
→ 幅度独立学习,可控
→ 收敛更平滑
实验观察(论文 Figure 3):
→ DoRA 在相同训练步数下达到更低 loss
→ DoRA 的 loss 曲线更平滑
→ DoRA 对学习率不那么敏感

3.3 性能对比#

class PerformanceComparison:
"""
性能对比(来自 DoRA 论文)
"""
RESULTS = {
"instruction_following": {
"dataset": "AlpacaEval",
"lora_r8": 68.2,
"dora_r8": 70.1,
"improvement": "+1.9",
},
"commonsense_reasoning": {
"datasets": ["BoolQ", "PIQA", "SIQA", "HellaSwag", "WinoGrande"],
"lora_avg": 75.3,
"dora_avg": 77.8,
"improvement": "+2.5",
},
"math_reasoning": {
"dataset": "GSM8K",
"lora_r16": 52.1,
"dora_r16": 54.8,
"improvement": "+2.7",
},
"chat": {
"dataset": "MT-Bench",
"lora_r8": 6.2,
"dora_r8": 6.5,
"improvement": "+0.3",
},
}

4. DoRA 的实现#

4.1 DoRA 核心实现#

import torch
import torch.nn as nn
import torch.nn.functional as F
class DoRALinear(nn.Module):
"""
DoRA 线性层
W_new = W₀ + m · (BA / ||BA||_F)
其中:
- W₀: 原始权重(冻结)
- A, B: LoRA 方向参数
- m: 可学习的幅度缩放
"""
def __init__(
self,
in_features,
out_features,
rank=4,
alpha=16,
):
super().__init__()
self.in_features = in_features
self.out_features = out_features
self.rank = rank
self.alpha = alpha
self.scaling = alpha / rank
# 原始权重(冻结)
self.weight = nn.Parameter(
torch.randn(out_features, in_features),
requires_grad=False
)
self.bias = nn.Parameter(
torch.zeros(out_features),
requires_grad=False
)
# 方向参数(LoRA 风格)
self.lora_A = nn.Parameter(torch.randn(rank, in_features) * 0.01)
self.lora_B = nn.Parameter(torch.zeros(out_features, rank))
# 幅度参数(DoRA 新增)
# 初始化为全 1,表示初始不改变幅度
self.m = nn.Parameter(torch.ones(out_features, in_features))
# 冻结原始权重
self.weight.requires_grad = False
def compute_delta(self):
"""
计算权重更新 ΔW = BA
"""
return self.lora_B @ self.lora_A
def compute_magnitude(self, delta):
"""
计算当前幅度 ||BA||_F
"""
return torch.norm(delta, p="fro")
def forward(self, x):
"""
DoRA 前向传播
"""
# Step 1: 计算 ΔW
delta = self.compute_delta() # (d, k)
# Step 2: 计算方向
delta_magnitude = self.compute_magnitude(delta) # scalar
delta_direction = delta / (delta_magnitude + 1e-8) # (d, k)
# Step 3: 应用幅度
delta_weight = self.m * delta_direction # (d, k)
# Step 4: 缩放
scaled_delta = delta_weight * self.scaling # (d, k)
# Step 5: 合并到原始权重
effective_weight = self.weight + scaled_delta
# Step 6: 前向传播
return F.linear(x, effective_weight, self.bias)

4.2 DoRA 的初始化策略#

class DoRAInitialization:
"""
DoRA 初始化策略
"""
def __init__(self):
pass
def recommended_init(self):
"""
推荐初始化
"""
return {
"lora_A": "N(0, 0.01) - 小随机值",
"lora_B": "全零 - 确保初始 ΔW = 0",
"m": "全 1 - 确保初始不改变幅度",
"rationale": "从原始模型开始,逐步学习方向和幅度",
}
def verify_init(self, layer):
"""
验证初始化正确性
"""
# 验证初始状态
delta = layer.compute_delta()
initial_magnitude = layer.compute_magnitude(delta)
print(f"Initial ΔW magnitude: {initial_magnitude:.6f}")
print(f"Expected: ~0 (B is zeros)")
# 验证幅度参数
print(f"Initial m mean: {layer.m.mean():.6f}")
print(f"Expected: 1.0")
# 验证初始权重更新为零
effective_weight = layer.weight + layer.scaling * layer.m * (delta / (initial_magnitude + 1e-8))
is_zero_update = torch.allclose(effective_weight, layer.weight, atol=1e-6)
print(f"Zero initial update: {is_zero_update}")

4.3 DoRA 的梯度实现#

class DoRAGradient:
"""
DoRA 梯度计算详解
"""
def compute_gradients(self, model, loss):
"""
梯度计算流程
假设:∂L/∂W_new 已通过反向传播计算
"""
model.zero_grad()
loss.backward()
for name, param in model.named_parameters():
if param.grad is not None:
print(f"{name}: grad norm = {param.grad.norm():.6f}")
def gradient_analysis(self, layer, grad_output):
"""
梯度分析
手动验证梯度计算
"""
# 设置 requires_grad
layer.lora_A.requires_grad_(True)
layer.lora_B.requires_grad_(True)
layer.m.requires_grad_(True)
# 前向
output = layer.forward(self.sample_input)
# 反向
output.sum().backward()
# 观察梯度
print(f"lora_A grad norm: {layer.lora_A.grad.norm():.6f}")
print(f"lora_B grad norm: {layer.lora_B.grad.norm():.6f}")
print(f"m grad norm: {layer.m.grad.norm():.6f}")
# 关键观察:
# - lora_A, lora_B 梯度被 ||BA||_F 归一化
# - m 梯度直接作用于方向

5. DoRA 的变体#

5.1 DoRA 与 LoRA+ 结合#

class DoRALoRAPlus:
"""
DoRA + LoRA+ 组合
LoRA+ 的核心思想:
- A 和 B 使用不同的学习率
- lr_A = lr / r, lr_B = lr
DoRA 保持这个特性,同时分解幅度和方向
"""
def __init__(self, in_features, out_features, rank=4):
# ... 标准 DoRA 初始化 ...
# LoRA+ 特有的学习率配置
self.lr_ratio = rank # lr_A / lr_B = r
def configure_optimizer(self, base_lr=1e-4):
"""
配置优化器(LoRA+ 风格)
"""
return [
{"params": self.lora_A, "lr": base_lr / self.rank},
{"params": self.lora_B, "lr": base_lr},
{"params": self.m, "lr": base_lr},
]

5.2 DoRA 与 QLoRA 结合#

class DoRAQLoRA:
"""
DoRA + QLoRA 组合
"""
def __init__(self, model, rank=64):
self.model = model
self.rank = rank
# 量化基础模型
self.quantized_model = self.quantize_base_model()
# 添加 DoRA 适配器
self.dora_adapters = self.add_dora_adapters()
def quantize_base_model(self):
"""
量化基础模型
"""
from transformers import BitsAndBytesConfig
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
)
# 量化加载
model = AutoModelForCausalLM.from_pretrained(
self.model_path,
quantization_config=bnb_config,
)
return model
def add_dora_adapters(self):
"""
为量化模型添加 DoRA 适配器
"""
# 遍历所有线性层
for name, module in self.quantized_model.named_modules():
if isinstance(module, nn.Linear):
# 替换为 DoRA 层
dora_layer = DoRALinear(
in_features=module.in_features,
out_features=module.out_features,
rank=self.rank,
)
# 复制原始权重
dora_layer.weight.data = module.weight.data.clone()
if module.bias is not None:
dora_layer.bias.data = module.bias.data.clone()
# 替换
self.replace_module(name, dora_layer)
return self.quantized_model

5.3 DoRA-MLP 变体#

class DoRAMLP:
"""
DoRA 应用于 MLP 层
标准 DoRA 只应用于注意力层
DoRA-MLP 扩展到 FFN 层
"""
def __init__(self, hidden_size, intermediate_size, rank=4):
self.hidden_size = hidden_size
self.intermediate_size = intermediate_size
self.rank = rank
# DoRA 适配器
self.up_proj = DoRALinear(hidden_size, intermediate_size, rank)
self.gate_proj = DoRALinear(hidden_size, intermediate_size, rank)
self.down_proj = DoRALinear(intermediate_size, hidden_size, rank)
def forward(self, x):
"""
FFN 前向
"""
# GELU 激活
return self.down_proj(
F.gelu(self.up_proj(x)) * self.gate_proj(x)
)

6. DoRA 的实验分析#

6.1 幅度 vs 方向学习#

class MagnitudeVsDirection:
"""
幅度和方向的学习分析
"""
def analyze_training(self, log_path):
"""
分析训练过程中的幅度和方向变化
"""
import pandas as pd
data = pd.read_csv(log_path)
# 跟踪幅度变化
data["magnitude_change"] = data["magnitude"].pct_change()
data["direction_change"] = data["direction_norm"].pct_change()
return {
"avg_magnitude_change": data["magnitude_change"].mean(),
"avg_direction_change": data["direction_change"].mean(),
"magnitude_final": data["magnitude"].iloc[-1],
"direction_final": data["direction_norm"].iloc[-1],
}
def visualize(self):
"""
可视化幅度和方向的学习曲线
"""
return """
预期观察:
1. 方向先于幅度收敛
→ 方向快速适应任务
→ 幅度逐步精细调整
2. 幅度变化范围较小
→ 大多数幅度值在 [0.8, 1.2] 范围
→ 极端值较少
3. 不同层学习不同的幅度模式
→ 某些层需要增强,某些需要减弱
"""

6.2 秩敏感性分析#

class RankSensitivity:
"""
秩敏感性分析
"""
EXPERIMENTS = {
"rank_4": {
"params": "低",
"lora_performance": 72.5,
"dora_performance": 74.2,
"improvement": "+1.7",
},
"rank_8": {
"params": "中",
"lora_performance": 74.1,
"dora_performance": 76.8,
"improvement": "+2.7",
},
"rank_16": {
"params": "中高",
"lora_performance": 75.8,
"dora_performance": 78.3,
"improvement": "+2.5",
},
"rank_32": {
"params": "高",
"lora_performance": 77.2,
"dora_performance": 79.1,
"improvement": "+1.9",
},
}
def conclusion(self):
"""
结论
"""
return {
"finding": "DoRA 在所有秩设置下都优于 LoRA",
"best_rank": "r=8 是 DoRA 的最佳选择",
"reason": "r=8 提供了足够的表达能力,同时保持参数效率",
}

6.3 层类型敏感性#

class LayerSensitivity:
"""
不同层的 DoRA 敏感性
"""
EXPERIMENTS = {
"attention_only": {
"layers": ["q_proj", "v_proj", "k_proj", "o_proj"],
"performance": 76.8,
},
"attention_mlp": {
"layers": ["q_proj", "v_proj", "k_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"],
"performance": 78.3,
},
"all_linear": {
"layers": "all",
"performance": 79.1,
},
}
def recommendation(self):
"""
建议
"""
return {
"low_resource": "仅注意力层",
"medium_resource": "注意力层 + MLP up/down proj",
"high_resource": "所有线性层",
}

7. 实战指南#

7.1 使用 PEFT 库#

from peft import LoraConfig, get_peft_model, PeftType
def create_dora_model(model, rank=8):
"""
使用 PEFT 库创建 DoRA 模型
"""
# PEFT 支持 DoRA
lora_config = LoraConfig(
r=rank,
lora_alpha=2 * rank,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
# DoRA 配置
use_dora=True, # 启用 DoRA
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# 输出包含 DoRA 幅度参数
return model

7.2 手动实现(更灵活)#

class DoRAFramework:
"""
DoRA 框架实现
"""
def __init__(self, model, rank=8, alpha=16):
self.model = model
self.rank = rank
self.alpha = alpha
# 替换所有目标层
self.replace_layers()
def replace_layers(self):
"""
替换模型中的目标层
"""
target_modules = ["q_proj", "v_proj"]
for name, module in self.model.named_modules():
if any(target in name for target in target_modules):
if isinstance(module, nn.Linear):
# 创建 DoRA 层
dora_layer = DoRALinear(
in_features=module.in_features,
out_features=module.out_features,
rank=self.rank,
alpha=self.alpha,
)
# 复制权重
dora_layer.weight.data = module.weight.data.clone()
if module.bias is not None:
dora_layer.bias.data = module.bias.data.clone()
# 替换
parent_name, child_name = self.get_parent_and_child(name)
setattr(parent_name, child_name, dora_layer)
def train(self, train_loader, num_epochs=3):
"""
训练循环
"""
optimizer = torch.optim.AdamW(
self.get_trainable_parameters(),
lr=1e-4,
)
for epoch in range(num_epochs):
for batch in train_loader:
optimizer.zero_grad()
outputs = self.model(batch["input_ids"])
loss = outputs.loss
loss.backward()
optimizer.step()
# 梯度裁剪
torch.nn.utils.clip_grad_norm_(
self.get_trainable_parameters(),
max_norm=1.0
)
def get_trainable_parameters(self):
"""
获取可训练参数
"""
return [
p for p in self.model.parameters()
if p.requires_grad
]

7.3 DoRA 配置推荐#

class DoRAConfig:
"""
DoRA 配置推荐
"""
CONFIGS = {
"7b": {
"rank": 8,
"alpha": 16,
"target_modules": ["q_proj", "v_proj", "k_proj", "o_proj"],
"dropout": 0.05,
"learning_rate": 1e-4,
},
"13b": {
"rank": 8,
"alpha": 16,
"target_modules": ["q_proj", "v_proj", "k_proj", "o_proj"],
"dropout": 0.05,
"learning_rate": 8e-5,
},
"70b": {
"rank": 8,
"alpha": 16,
"target_modules": ["q_proj", "v_proj"], # 减少目标以节省显存
"dropout": 0.05,
"learning_rate": 5e-5,
},
}
@classmethod
def get_config(cls, model_size="7b"):
"""
获取配置
"""
return cls.CONFIGS.get(model_size, cls.CONFIGS["7b"])

8. DoRA 的局限性#

8.1 参数量增加#

class DoRALimitations:
"""
DoRA 的局限性
"""
def parameter_overhead(self):
"""
参数量开销
"""
return {
"loRA_r8": "~8M params (for 7B)",
"doRA_r8": "~125M params (for 7B)",
"overhead": "~16x",
"note": "实际可训练参数仍然很少(<1%),但比 LoRA 多",
}
def when_to_use(self):
"""
使用建议
"""
return {
"use_dora": [
"对性能有极致追求",
"有足够的显存(比 LoRA 多 ~5%)",
"需要更好的收敛性",
],
"prefer_lora": [
"显存极度受限",
"需要快速实验",
"模型很大(>70B)",
],
}

8.2 收敛速度#

收敛速度对比:
DoRA:
→ 初期收敛稍慢
→ 后期收敛更稳
LoRA:
→ 初期收敛快
→ 后期可能震荡
建议:
→ 复杂任务使用 DoRA(长期收益)
→ 简单任务使用 LoRA(快速实验)

9. 核心公式汇总#

9.1 幅度-方向分解#

W=WFWWF\mathbf{W} = \|\mathbf{W}\|_F \cdot \frac{\mathbf{W}}{\|\mathbf{W}\|_F}

其中 WF=ijWij2\|\mathbf{W}\|_F = \sqrt{\sum_i \sum_j W_{ij}^2} 是 Frobenius 范数。

9.2 DoRA 更新#

WnewDoRA=W0+mBABAF\mathbf{W}_{\text{new}}^{\text{DoRA}} = \mathbf{W}_0 + \mathbf{m} \odot \frac{\mathbf{B}\mathbf{A}}{\|\mathbf{B}\mathbf{A}\|_F}

其中 m\mathbf{m} 是幅度矩阵,BABAF\frac{\mathbf{B}\mathbf{A}}{\|\mathbf{B}\mathbf{A}\|_F} 是归一化方向。

9.3 DoRA 梯度#

LA=αrmBAFLWnewB\frac{\partial \mathcal{L}}{\partial \mathbf{A}} = \frac{\alpha}{r} \cdot \frac{\mathbf{m}}{\|\mathbf{B}\mathbf{A}\|_F} \cdot \frac{\partial \mathcal{L}}{\partial \mathbf{W}_{\text{new}}} \cdot \mathbf{B}^\top

10. 总结#

10.1 DoRA 的核心优势#

┌─────────────────────────────────────────────────────────────┐
│ DoRA 核心优势 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 更细粒度的控制 │
│ → 幅度和方向独立学习 │
│ → 更精细的权重更新 │
│ │
│ 2. 更好的收敛性 │
│ → 方向被归一化,更稳定 │
│ → 对学习率不那么敏感 │
│ │
│ 3. 性能提升 │
│ → 在所有任务上优于 LoRA │
│ → 平均提升 2-3% │
│ │
│ 4. 实现简单 │
│ → 只需在 LoRA 基础上增加幅度参数 │
│ → 与 QLoRA、LoRA+ 等兼容 │
│ │
└─────────────────────────────────────────────────────────────┘

10.2 选择指南#

DoRA vs LoRA 选择:
选择 DoRA:
✓ 复杂推理任务
✓ 对性能有极致追求
✓ 有足够显存
✓ 愿意多花一点训练时间
选择 LoRA:
✓ 简单分类任务
✓ 需要快速实验
✓ 显存受限
✓ 模型很大
关键结论:
DoRA 是 LoRA 的增强版
在资源和时间允许的情况下,优先使用 DoRA
推荐阅读
  1. DoRA 论文(Liu et al., 2024)—— 原论文
  2. PEFT 文档—— DoRA 实现细节
  3. LoRA 论文(Hu et al., 2021)—— LoRA 基础

参考资料#

  1. Liu, S., et al. (2024). “DoRA: Weight-Decomposed Low-Rank Adaptation.” arXiv<2402>.09353.
  2. Hu, E. J., et al. (2021). “LoRA: Low-Rank Adaptation of Large Language Models.” ICLR.
  3. Dettmers, T., et al. (2023). “QLoRA: Efficient Finetuning of Quantized LLMs.” NeurIPS.
  4. Hayou, S., et al. (2024). “LoRA+: Efficient Low Rank Adaptation of LLMs.” arXiv.
  5. Jia, M., et al. (2024). “DoRA-MLP: Enhancing Fine-tuning with Dynamic Weight Decomposition.” arXiv.

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

DoRA 深度解析:权重分解低秩适应的理论与实践
https://aiattnstudio.link/posts/dora/
作者
Federico
发布于
2026-07-16
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author

Federico

AI Research Lab

Hello, I'm Federico.

关于实验室 / About
公告

欢迎来到Federico的个人博客

分类
标签
站点统计
57文章
7分类
404标签