引言
Pytorch中DataLoader输出精度问题–“ RuntimeError: Input type (double) and bias type (float) should be the same"和“RuntimeError: expected scalar type Double but found Float”来源很简单,在使用DataLoader进行数据集读取后,得到的Batch数据通过神经网络报错
RuntimeError: expected scalar type Double but found Float
,经查看是因为Pytorch中神经网络的参数默认精度是torch.float32
,而送入的测试数据精度是torch.float64
导致报错。但实际上笔者并没有显式对输入数据的精度进行调整,只是在Dataset构造时从本地读取了文件,再以此构建了DataLoader类,因此进行了一番排错。经实验发现,这算是Pytoch DataLoader的考虑不周。
代码环境复现
首先我们本地存特征和标签:
from torch import nn
import numpy as np
from torch.utils.data import DataLoader,Dataset
X=np.random.rand(10,3,8,8)
import os
os.makedirs('./test')
np.save(X,'./test/feature.npy')
y=np.random.randint(2,size=(8,))
np.save('./test/label.npy',y)
然后构建相应的DataLoader:
class CustomData(Dataset):
def __init__(self, data_path, label_path, transform=None):
super(CustomData,self).__init__()
self.feature = np.load(data_path)
self.label = np.load(label_path)
self.transform = transform
def __len__(self):
return self.feature.shape[0]
def __getitem__(self, item):
X = self.feature[item]
y = self.label[item]
if self.transform:
X = self.transform(X)
return X, y
cusData=CustomData('./test/feature.npy','./test/label.npy')
cusDataLoader=DataLoader(cusData)
再抽取一个batch送入模型验证:
model=nn.Conv2d(3,6,3,1,1)
sample=next(iter(cusDataLoader))
res=model(sample[0])
可以看到这时候就会报错:
RuntimeError: Input type (double) and bias type (float) should be the same
如果使用的不是conv2d
函数而是直接初始化了Paramter
和输入进行相乘,则会发生类似的报错:
“RuntimeError: expected scalar type Double but found Float”
原因
实际上这是因为在DataLoader的输出过程中会自动的对Dataset的item值进行张量转换,把数据的格式由np.array
转为torch.Tensor
,但这一过程存在着缺陷–即它保持了原有的数据精度不变。如果原来np.array
的精度为了float64
,转换完成后还是float64
,也就与神经网络的默认精度不兼容了。输出前述案例的数据dtype
如下:
print(sample[0].dtype)
torch.float64
print(X.dtype)
float64
print(model.weight.dtype)
torch.float32
解决方案如下:
1. Dataset 处理
由于这一Tensor的准换或者说排查过程是在DataLoader类构造时出现的,那么我们在Dataset构造时提前解决精度问题即可。手动对输入数据进行张量化:torch.Tensor(X)
默认返回float32
精度数据;也可以直接调整np.array
精度.
即在Dataset里修改:
class CustomData(Dataset):
def __init__(self, data_path, label_path, transform=None):
super(CustomData,self).__init__()
self.feature = np.load(data_path)
self.label = np.load(label_path)
self.transform = transform
def __len__(self):
return self.feature.shape[0]
def __getitem__(self, item):
X = self.feature[item]
y = self.label[item]
if self.transform:
X = self.transform(X)
return torch.Tensor(X), y
2. DataLoader处理
也可以在读取batch的时候进行修改,添加额外的处理函数来对数据格式进行检查和转换:
def _prepare_batch(batch, device=None, non_blocking=False):
"""
对dataloader每个batch的输出进行进一步的处理
:param batch: dataloader输出
:param device:
:param non_blocking:
:return:
"""
device = "cuda:" + str(device)
x,y = batch
x=x.float()
y=y.float().view(-1,1)
return x,y
x,y=_prepare_batch((sample[0],sample[1]))
res=model(x)