minibatch时,损失如何记录
目录
minibatch时,损失如何记录
报错:UnboundLocalError: local variable 'coef' referenced before assignment是什么回事
未溢出则不会报错,可以完整滴运行完成
indent 缩进
炫酷技能:一遍运行,一遍画图
实例1
解释1
实例2:来源于动手学深度学习CNN
这段代码定义了一个用于训练模型的函数 train_ch6,它使用了PyTorch框架。以下是代码的逐行解释:
等我想弄了弄,实时绘图ds
minibatch时,损失如何记录
在使用minibatch进行训练时,通常我们会记录两种类型的损失:
-
Minibatch损失:这是在每个小批量数据上计算得到的损失。由于minibatch的数据量较小,这个损失值可能会有较大的波动,因为它只反映了模型在那一小部分数据上的表现。画minibatch损失图时,你会看到随着每个minibatch的训练过程而频繁波动的线条。
-
Epoch损失:这是在整个数据集上计算得到的平均损失。在每个epoch结束时,我们会计算模型在当前epoch中所有minibatches的平均损失。这个损失值更加稳定,因为它考虑了整个数据集的性能。画epoch损失图时,线条会相对平滑,因为它是在更全面的数据集上评估得到的。
在实际应用中,通常我们会绘制epoch损失图,因为它提供了模型性能随时间变化的更清晰的视图,并且可以更好地看出模型是否在收敛。Minibatch损失图虽然也能提供信息,但由于波动较大,通常不作为主要的监控指标,但它在调试和了解训练过程中的动态时仍然是有用的。
总结来说,为了监控训练进度和模型的收敛性,你应该绘制每个epoch结束时的平均损失,即epoch损失图。
也就是画每个epoch结束后,在所有训练集上的平均损失,一个epoch一画。
报错:UnboundLocalError: local variable 'coef' referenced before assignment是什么回事
有人说是在函数内部修改了全局变量coef,
UnboundLocalError: local variable 'coef' referenced before assignment
是 Python 中的一种常见错误,它发生在以下情况下:
- 你在一个函数内部尝试修改一个变量,而这个变量在函数外部定义。
- 你在函数内部引用了一个变量,但是这个变量在函数内部没有被赋值。
这个错误的原因是 Python 的作用域规则。在 Python 中,如果一个变量在函数内部被赋值,那么它就被视为局部变量,除非你明确地声明它是全局变量。
以下是一个导致这种错误的例子:
python
复制
coef = 10 # coef 是一个全局变量
def update_coef():
coef += 5 # 尝试修改全局变量 coef,但 Python 认为它是局部变量
print(coef)
update_coef()
在这个例子中,当函数 update_coef
尝试执行 coef += 5
时,Python 会认为 coef
是一个局部变量,因为它在函数内部被赋值了。然而,由于在赋值之前就尝试使用 coef
,Python 抛出 UnboundLocalError
,因为 coef
在函数的局部作用域中还没有被定义。
为了解决这个问题,你有以下几种选择:
- 使用
global
关键字来声明coef
是一个全局变量:
python
复制
coef = 10
def update_coef():
global coef # 告诉 Python coef 是全局变量
coef += 5
print(coef)
update_coef()
- 将全局变量的值传递给函数,并在函数内部返回新的值:
coef = 10
def update_coef(coef):
coef += 5
return coef
coef = update_coef(coef)
print(coef)
- 在函数内部创建一个新的局部变量,并在函数外部使用这个新值:
coef = 10
def update_coef():
new_coef = coef + 5 # 创建一个新的局部变量
return new_coef
coef = update_coef() # 在函数外部使用新值
print(coef)
选择哪种方法取决于你的具体需求和代码的设计。
但是我的coef根本就没设置过这个变量
coef是我未曾见过的变量。
在训练了几轮,提示了这个:后报错。
未溢出则不会报错,可以完整滴运行完成
在机器学习中,“NaN” 代表 “Not a Number”,表示在数学运算过程中产生了未定义或无效的结果。当损失函数中出现 NaN 时,这意味着在某些迭代中,损失无法正常计算出来。这可能由多种原因造成,包括但不限于梯度爆炸、数值不稳定、初始化不当等。
关于损失的大小,NaN 本身并不代表损失的具体大小,而是表示损失无法计算。因此,无论损失原本应该是很大还是很小,一旦出现 NaN,就意味着当前的损失无法给出有效的数值评估。
解决 NaN 问题通常需要对模型进行调试,检查是否存在梯度爆炸、数值溢出等问题,并采取相应的措施,如调整学习率、使用梯度裁剪、重新初始化权重等,以确保损失的稳定性。
当你发现在将区间分成更多部分时出现 NaN 而在分成较少部分时没有问题时,这通常与数值稳定性有关。以下是一些可能导致这种情况的原因:
-
数值溢出:当区间被分成更多的部分时,某些计算可能会产生非常大的数值,超出浮点数可以表示的范围,从而导致溢出变成 NaN。
-
梯度爆炸:在反向传播过程中,如果梯度变得非常大,可能会导致权重和偏置的更新过大,使得后续的损失计算产生 NaN。
-
除以零:在某些情况下,当区间被更细致地划分时,可能会在计算中出现除以非常小的数(接近零)的情况,这可能导致数值不稳定。
-
函数的不连续性或奇点:如果你的损失函数或模型中的某些操作在特定的区间内有不连续点或奇点,那么在更细的区间划分中可能会遇到这些问题。
-
初始化问题:不良的参数初始化可能导致模型在训练初期就发散。
以下是一些可能的解决方案:
- 降低学习率:如果梯度爆炸是问题所在,尝试降低学习率可能会有所帮助。
- 梯度裁剪:如果梯度变得过大,可以尝试对梯度进行裁剪,限制其大小。
- 检查初始化:确保模型的权重和偏置有适当的初始化。
- 数值稳定性:检查损失函数和模型中的操作,确保它们在数值上是稳定的。例如,可以使用更稳定的函数来代替可能导致数值问题的操作。
- 调试:在出现 NaN 的地方添加调试语句,查看是哪些特定的操作或参数导致了 NaN 的产生。
- 使用不同的优化器:有时候,改变优化器(例如,从 SGD 到 Adam)可以提高训练的稳定性。
通过这些方法,你应该能够诊断出问题所在,并采取措施来解决 NaN 的问题。
indent 缩进
这个错误是由于Python对缩进非常敏感导致的。在Python中,代码块的缩进必须一致且符合语法要求。如果你看到这样的错误消息:“IndentationError: expected an indented block”,这意味着Python期望看到一个缩进的代码块,但你并没有提供一个有效的缩进。
在你的代码片段中,start_time = time.time()
这一行前面有一个波浪号(~)符号,这可能是因为你在某个编辑器或IDE中使用了某种高亮显示功能,但这并不是Python代码的一部分。此外,这一行似乎缺少了必要的缩进。
要解决这个问题,请确保以下几点:
-
确保所有代码块都按照Python的标准缩进方式进行了缩进。通常使用四个空格作为一次缩进级别。
-
检查是否有任何多余的字符或不必要的符号出现在代码行前。
-
如果你在使用Jupyter Notebook或其他类似的交互式环境,确保你没有意外地在单元格的开始处添加了额外的空白行或缩进。
-
如果你是在脚本文件中编写代码,确保整个文件的缩进风格是一致的。
修复这个问题后,你的代码应该能够正常执行。
炫酷技能:一遍运行,一遍画图
要在训练过程中实时绘制损失图,可以使用matplotlib库中的pyplot
模块来创建动态更新的图表。以下是一个简单的示例代码,展示了如何在Python中使用Keras进行模型训练,并在每个epoch后更新并显示损失图:
实例1
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers import Dense
import numpy as np
# 生成一些样本数据
X = np.random.rand(1000, 10)
y = np.random.randint(2, size=(1000, 1))
# 创建模型
model = Sequential()
model.add(Dense(12, input_dim=10, activation='relu'))
model.add(Dense(8, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# 编译模型
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# 设置图形参数
plt.ion() # 启用交互模式
fig, ax = plt.subplots()
def on_epoch_end(epoch, logs):
ax.clear()
ax.plot(logs['loss'])
ax.set_xlabel('Epoch')
ax.set_ylabel('Loss')
ax.set_title('Training Loss')
fig.canvas.draw()
plt.pause(0.01)
# 添加回调函数
callbacks = [on_epoch_end]
# 训练模型
history = model.fit(X, y, epochs=50, batch_size=32, callbacks=callbacks)
在这个例子中,我们定义了一个on_epoch_end
函数作为回调函数,该函数在每个epoch结束后被调用。在该函数内部,我们清除之前的绘图,然后重新绘制当前的损失值,最后通过draw()
和pause()
方法实现图形的动态更新。
请注意,这段代码需要在支持GUI的环境(如Jupyter Notebook)中运行才能看到实时的图形更新。如果在命令行环境中运行,可能需要安装额外的包以支持图形界面。
解释1
编译模型,指定损失函数为binary_crossentropy(适用于二分类问题),优化器为Adam,评估指标为准确率。
# 设置图形参数
plt.ion() # 启用交互模式
开启matplotlib的交互模式,允许动态更新图表。
fig, ax = plt.subplots()
创建一个新的图形和坐标轴,用于绘制图表。
def on_epoch_end(epoch, logs):
定义一个函数,该函数在每个epoch结束时被调用,参数epoch是当前epoch的索引,logs是一个字典,包含当前epoch的损失和指标。
ax.clear()
清除坐标轴上的图形,为新的绘图做准备。
ax.plot(logs['loss'])
在坐标轴上绘制损失值。
ax.set_xlabel('Epoch')
设置x轴标签为’Epoch’。
ax.set_ylabel('Loss')
设置y轴标签为’Loss’。
ax.set_title('Training Loss')
设置图表标题为’Training Loss’。
fig.canvas.draw()
绘制图表。
plt.pause(0.01)
暂停一小段时间,以便于动态更新图表。
# 添加回调函数
callbacks = [on_epoch_end]
创建一个回调函数列表,包含我们定义的在每个epoch结束时调用的函数。
# 训练模型
history = model.fit(X, y, epochs=50, batch_size=32, callbacks=callbacks)
使用数据X和y训练模型,指定训练的epoch数为50,每个batch的大小为32,并使用我们定义的回调函数。返回一个History对象,其中包含训练过程中的损失和指标。
实例2:来源于动手学深度学习CNN
#@save
def train_ch6(net, train_iter, test_iter, num_epochs, lr, device):
"""用GPU训练模型(在第六章定义)"""
def init_weights(m):
if type(m) == nn.Linear or type(m) == nn.Conv2d:
nn.init.xavier_uniform_(m.weight)
net.apply(init_weights)
print('training on', device)
net.to(device)
optimizer = torch.optim.SGD(net.parameters(), lr=lr)
loss = nn.CrossEntropyLoss()
animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs],
legend=['train loss', 'train acc', 'test acc'])
timer, num_batches = d2l.Timer(), len(train_iter)
for epoch in range(num_epochs):
# 训练损失之和,训练准确率之和,样本数
metric = d2l.Accumulator(3)
net.train()
for i, (X, y) in enumerate(train_iter):
timer.start()
optimizer.zero_grad()
X, y = X.to(device), y.to(device)
y_hat = net(X)
l = loss(y_hat, y)
l.backward()
optimizer.step()
with torch.no_grad():
metric.add(l * X.shape[0], d2l.accuracy(y_hat, y), X.shape[0])
timer.stop()
train_l = metric[0] / metric[2]
train_acc = metric[1] / metric[2]
if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:
animator.add(epoch + (i + 1) / num_batches,
(train_l, train_acc, None))
test_acc = evaluate_accuracy_gpu(net, test_iter)
animator.add(epoch + 1, (None, None, test_acc))
print(f'loss {train_l:.3f}, train acc {train_acc:.3f}, '
f'test acc {test_acc:.3f}')
print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec '
f'on {str(device)}')
这段代码定义了一个用于训练模型的函数 train_ch6
,它使用了PyTorch框架。以下是代码的逐行解释:
#@save
def train_ch6(net, train_iter, test_iter, num_epochs, lr, device):
定义一个名为 train_ch6
的函数,它接受以下参数:
net
: 要训练的神经网络模型。train_iter
: 训练数据集的迭代器。test_iter
: 测试数据集的迭代器。num_epochs
: 训练的轮数(epoch)。lr
: 学习率。device
: 用于训练的设备,如CPU或GPU。
"""用GPU训练模型(在第六章定义)"""
这是函数的文档字符串,简要描述了函数的作用。
def init_weights(m):
if type(m) == nn.Linear or type(m) == nn.Conv2d:
nn.init.xavier_uniform_(m.weight)
定义一个内部函数 init_weights
,用于初始化网络层的权重。如果层是线性层或卷积层,使用Xavier初始化方法。
net.apply(init_weights)
对网络模型 net
应用权重初始化。
print('training on', device)
打印出训练所使用的设备。
net.to(device)
将网络模型移动到指定的设备上。
optimizer = torch.optim.SGD(net.parameters(), lr=lr)
创建一个优化器对象,这里使用的是随机梯度下降(SGD),并设置学习率。
loss = nn.CrossEntropyLoss()
定义损失函数,这里使用的是交叉熵损失,适用于分类问题。
animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs],
legend=['train loss', 'train acc', 'test acc'])
创建一个动画绘制器对象,用于绘制训练过程中的损失和准确率。
timer, num_batches = d2l.Timer(), len(train_iter)
创建一个计时器,并计算训练迭代器的批次数。
for epoch in range(num_epochs):
开始一个循环,迭代指定的轮数(epoch)。
# 训练损失之和,训练准确率之和,样本数
metric = d2l.Accumulator(3)
创建一个累加器,用于累加训练损失、准确率和样本数。
net.train()
将网络模型设置为训练模式。
for i, (X, y) in enumerate(train_iter):
遍历训练迭代器,获取数据和标签。
timer.start()
开始计时。
optimizer.zero_grad()
清空之前的梯度。
X, y = X.to(device), y.to(device)
将数据和标签移动到指定的设备上。
y_hat = net(X)
通过网络模型前向传播数据,得到预测结果。
l = loss(y_hat, y)
计算预测结果和真实标签之间的损失。
l.backward()
对损失进行反向传播,计算梯度。
optimizer.step()
更新网络的权重。
with torch.no_grad():
metric.add(l * X.shape[0], d2l.accuracy(y_hat, y), X.shape[0])
在不计算梯度的情况下,更新累加器中的损失和准确率。
timer.stop()
停止计时。
train_l = metric[0] / metric[2]
train_acc = metric[1] / metric[2]
计算平均训练损失和准确率。
if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:
animator.add(epoch + (i + 1) / num_batches,
(train_l, train_acc, None))
每完成一定比例的批次或最后一个批次,更新动画绘制器。
test_acc = evaluate_accuracy_gpu(net, test_iter)
在测试数据集上评估网络的准确率。
animator.add(epoch + 1, (None, None, test_acc))
将测试准确率添加到动画绘制器。
print(f'loss {train_l:.3f}, train acc {train_acc:.3f}, '
f'test acc {test_acc:.3f}')
打印最终的训练损失、训练准确率和测试准确率。
print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec '