本文内容多是个人理解,如有问题望多多指正
一、误差:
在论文中,使用了三个标准误差测量法来估计单目深度预测的准确性,这三个标准误差测量法是目前最先进的方法[21,22,14,6]中常用的。这些误差是为每个像素定义的,并对图像中的所有像素和数据集中的所有图像进行平均。
三个标准误差测量法如下:
对训练数据量的鲁棒性: 为了评估对训练量的敏感性,文中通过逐步减少训练数据来模拟该方法在两个数据集上的行为。并没有使用完整的训练集,而是随机抽取一小部分训练数据进行训练。下图中的曲线表明,当训练数据量逐渐减少时,误差值(‘ rel’和‘ log10’)的变化。即可知,当随着培训数据量的逐渐减少,这两个误差指标都缓慢增加,
所以误差值越小越好
。
二、checkpoint.pth.tar:
其实训练一个深度神经网络是需要挺长的时间的,即使是在高性能服务器上,有些训练也要持续几天之久。
如果当你遇到这种尴尬的情况:花了一天时间**好不容易训练模型到 60% 啦,突然,**机房要停电?学长要占用服务器?购买的 GPU 计算时间用完了等等。
这时候该怎么办呢??
训练了一大半的模型不能功亏一篑呀,所以这时候就要未雨绸缪,回头有机会了再加载接着训练,所以这时候就要将模型保存为一个文件:
#未雨绸缪,防止丢失
if err < best_val_err:
best_val_err = err#当前最好精度
torch.save({
'epoch': start_epoch + epoch + 1,#保存的当前轮数
'state_dict': model.state_dict(),#训练好的参数
'optimizer': optimizer.state_dict(),#优化器参数,为了后续的resume
}, 'checkpoint.pth.tar')#保存模型到checkpoint.pth.tar
然后再重新加载:
resume_file = 'checkpoint.pth.tar'
if resume_from_file:
if os.path.isfile(resume_file):
print("=> loading checkpoint '{}'".format(resume_file))
checkpoint = torch.load(resume_file)
start_epoch = checkpoint['epoch']#epoch,可以用于更新学习率等
model.load_state_dict(checkpoint['state_dict'])#模型参数
print("=> loaded checkpoint '{}' (epoch {})"
.format(resume_file, checkpoint['epoch']))
else:
print("=> no checkpoint found at '{}'".format(resume_file))
这样就可以保存(save)&加载(load)训练中的 PyTorch 模型啦,而且这样也不怕服务器突然掉链子,还能够把训练各个阶段的模型都保存下来,用于研究模型训练的各个步骤。
三、nyu_depth_v2_labeled.mat数据集:
由于是.mat文件,所以我用matlab把该文件打开后如下图:
其中列举一些变量的含义:
(1)depths-HxWxN维度的矩阵深度图,其中H和W分别为高度和宽度,N为图像的个数。深度元素的值是米。
(2)images-HxWx3xN RGB图像矩阵,其中H和W分别是高度和宽度,N是图像的数量
(3)labels-HxWxN标签矩阵,其中H和W分别是高度和宽度,N是图像数量。 标签范围从1…C,其中C是类的总数。 标签的范围从1…C是类的总数。如果一个像素的标签值为0,那么这个像素就没有标记。
代码中首先应用交叉验证将训练集划分为两部分,训练集,验证集。
训练集:用于训练模型
验证集:在使用测试集得出模型误差之前,用验证测试模型的误差
先给定一个分隔位置,然后确定测试集和验证集:
val_start_idx = int(len(train_lists) * 0.8) #交叉验证集
val_lists = train_lists[val_start_idx:-1]
train_lists = train_lists[0:val_start_idx]
后再用torch.utils.data.DataLoader()迭代取该数据集(一个一个batch读入),该接口主要用来将自定义的数据读取接口的输出或者PyTorch已有的数据读取接口的输入按照batch size封装成Tensor,后续只需要再包装成Variable即可作为模型的输入
train_loader = torch.utils.data.DataLoader(NyuDepthLoader(data_path, train_lists),
batch_size=batch_size, shuffle=True, drop_last=True)
val_loader = torch.utils.data.DataLoader(NyuDepthLoader(data_path, val_lists),
batch_size=batch_size, shuffle=True, drop_last=True)
test_loader = torch.utils.data.DataLoader(NyuDepthLoader(data_path, test_lists),
batch_size=batch_size, shuffle=True, drop_last=True)
再根据以下代码获得原始图和深度图,注意要提取深度图像,要记得取max
for input, depth in val_loader:
input_var = Variable(input.type(dtype))#模型的输入
depth_var = Variable(depth.type(dtype))
output = model(input_var)#resnet50模型处理后的输出
input_rgb_image = input_var[0].data.permute(1, 2, 0).cpu().numpy().astype(np.uint8)#原始图像
input_gt_depth_image = depth_var[0][0].data.cpu().numpy().astype(np.float32)#depth图像
pred_depth_image = output[0].data.squeeze().cpu().numpy().astype(np.float32)
input_gt_depth_image /= np.max(input_gt_depth_image)#提取depth图像
pred_depth_image /= np.max(pred_depth_image)#提取预测的深度图像
plot.imsave('input_rgb_epoch_0.png', input_rgb_image)
plot.imsave('gt_depth_epoch_0.png', input_gt_depth_image, cmap="viridis")
plot.imsave('pred_depth_epoch_0.png', pred_depth_image, cmap="viridis")
四、test模块误差和阈值部分的代码理解:
这里误差计算中pred_depth_image多表示观测值,input_gt_depth_image则多表示真值,阈值Threshold部分没有查到太多与该代码有关的相关资料,我理解的是他给定一个阈值部分,这样图像的深浅或许会有不同的显现,
在这里等待大家不同的理解
初始化:
Threshold_1_25 = 0
Threshold_1_25_2 = 0
Threshold_1_25_3 = 0
RMSE_linear = 0.0
RMSE_log = 0.0
RMSE_log_scale_invariant = 0.0
ARD = 0.0
SRD = 0.0
计算:(注释中也有部分理解)
#观测次数n
n = np.sum(input_gt_depth_image > 1e-3)
idxs = (input_gt_depth_image <= 1e-3)
pred_depth_image[idxs] = 1
input_gt_depth_image[idxs] = 1
pred_d_gt = pred_depth_image / input_gt_depth_image
pred_d_gt[idxs] = 100
gt_d_pred = input_gt_depth_image / pred_depth_image
gt_d_pred[idxs] = 100
# 设定阈值
Threshold_1_25 += np.sum(np.maximum(pred_d_gt, gt_d_pred) < 1.25) / n
Threshold_1_25_2 += np.sum(np.maximum(pred_d_gt, gt_d_pred) < 1.25 * 1.25) / n
Threshold_1_25_3 += np.sum(np.maximum(pred_d_gt, gt_d_pred) < 1.25 * 1.25 * 1.25) / n
log_pred = np.log(pred_depth_image)#求观测值的log,为了便于下面求误差
log_gt = np.log(input_gt_depth_image)#求真实值的log,为了便于下面求误差
d_i = log_gt - log_pred
#下为不同误差计算
#均方根误差(RMSE),它是观测值与真值偏差的平方和观测次数n比值的平方根,pred_depth_image:观测值,input_gt_depth_image:真值
RMSE_linear += np.sqrt(np.sum((pred_depth_image - input_gt_depth_image) ** 2) / n)#均方根误差
RMSE_log += np.sqrt(np.sum((log_pred - log_gt) ** 2) / n)#rms(log)
RMSE_log_scale_invariant += np.sum(d_i ** 2) / n + (np.sum(d_i) ** 2) / (n ** 2)
ARD += np.sum(np.abs((pred_depth_image - input_gt_depth_image)) / input_gt_depth_image) / n#rel,相对误差
SRD += np.sum(((pred_depth_image - input_gt_depth_image) ** 2) / input_gt_depth_image) / n
Threshold_1_25 /= num_samples
Threshold_1_25_2 /= num_samples
Threshold_1_25_3 /= num_samples
RMSE_linear /= num_samples
RMSE_log /= num_samples
RMSE_log_scale_invariant /= num_samples
ARD /= num_samples
SRD /= num_samples
print('Threshold_1_25: {}'.format(Threshold_1_25))
print('Threshold_1_25_2: {}'.format(Threshold_1_25_2))
print('Threshold_1_25_3: {}'.format(Threshold_1_25_3))
print('RMSE_linear: {}'.format(RMSE_linear))
print('RMSE_log: {}'.format(RMSE_log))
print('RMSE_log_scale_invariant: {}'.format(RMSE_log_scale_invariant))
print('ARD: {}'.format(ARD))
print('SRD: {}'.format(SRD))
五、NYU_ResNet-UpProj.npy:
模型文件(.npy)部分内容如下:由一个字典组成,字典中的每一个键对应一层网络模型参数。
由于numpy版本存在不兼容的问题,所以我用如下代码在控制台上输出了.npy文件的内容:
import numpy as np
old = np.load
np.load = lambda *a,**k: old(*a,**k,allow_pickle=True)
test=np.load('NYU_ResNet-UpProj.npy',encoding = "latin1") #加载文件
#doc = open('1.txt', 'a') #打开一个存储文件,并依次写入
#print(test, file=doc) #将打印内容写入文件中
print(test)
输出的部分内容如下:
或写入文件,内容如图:
所以可直接利用如下语句加载模型权重:
weights_file = "NYU_ResNet-UpProj.npy"
model_params = model.state_dict()
data_dict = np.load(weights_file, encoding='latin1').item()#加载npy文件格式
end~~~