for epoch in range(epochs):
model.train()
total_loss = 0
for x, y in train_loader:
tgt = torch.zeros_like(y)
optimizer.zero_grad()
output = model(x, tgt)
loss = criterion(output, y)
loss.backward()
optimizer.step()
total_loss += loss.item()
print(f"Epoch {epoch+1}, Loss: {total_loss / len(train_loader):.4f}")
# During training
for epoch in range(epochs):
model.train()
total_loss = 0
for x, y in train_loader:
tgt = y[:, :-1, :]
optimizer.zero_grad()
output = model(x, tgt)
loss = criterion(output, y[:, 1:, :])
loss.backward()
optimizer.step()
total_loss += loss.item()
print(f"Epoch {epoch+1}, Loss: {total_loss / len(train_loader):.4f}") 这两个什么区别
这两段代码的区别在于:训练时目标序列 tgt
的设置方式不同,影响模型学习目标和质量。
✅ 第一段代码:使用全零目标序列
pythonCopyEditfor x, y in train_loader:
tgt = torch.zeros_like(y) # tgt 是全为0的张量
...
output = model(x, tgt)
loss = criterion(output, y)
- tgt 输入的是全为0的张量,不含任何真实信息。
- 相当于你让模型“从一片空白中预测出未来走势”,这非常困难。
- Transformer Decoder 在训练时如果不看到真实值序列,很难学习到如何逐步生成序列。
- 会导致预测值 几乎一样或者无法收敛。
🛑 这种做法在时间序列建模中通常效果很差,不推荐。
✅ 第二段代码:使用 teacher forcing(教学强制)
pythonCopyEditfor x, y in train_loader:
tgt = y[:, :-1, :] # 使用真实目标序列的前 (n-1) 步
...
output = model(x, tgt)
loss = criterion(output, y[:, 1:, :]) # 与目标序列的后 (n-1) 步做对比
- 使用了真实的目标序列作为 Decoder 的输入,也就是所谓的 teacher forcing。
- 意思是:我告诉你第一天的值,你来预测第二天;然后告诉你第二天的值,你预测第三天……
- 然后比较模型预测的
[t+1, t+2, ..., t+n]
和真实的[t+1, t+2, ..., t+n]
。 - 这种方式训练更稳定,收敛更快,预测精度更高。
✅ 这是推荐的做法,尤其适合时间序列预测、机器翻译等任务。
🧠 简单类比帮助理解
模式 | 类比说明 |
---|---|
第一段(用0作为目标输入) | 让一个学生在不知道考试题目的前提下写完整份答案 |
第二段(用部分真实目标) | 老师给他看前几道题的答案,他再尝试写后面的题目 |
✅ 建议总结
项目 | 第一段(全零 tgt) | 第二段(真实 tgt) |
---|---|---|
输入给 decoder 的序列 | 全为 0 | 真实序列的前一部分 |
模型训练难度 | 高 | 低 |
收敛速度 | 慢或不收敛 | 快 |
模型预测能力 | 弱,容易输出重复值 | 强,能学到序列模式 |
推荐程度 | ❌ 不推荐 | ✅ 推荐 |
如需进一步提升效果,还可以在训练中逐渐减少 teacher forcing 的比例,转向模型自回归(scheduled sampling)。