本来应该很早就写一个的,现在也不晚,慢慢记录吧
1、数据同时存在于CPU和GPU上
报错:RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu!
代码片段
class Linear(nn.Module):
def __init__(self,in_features=1024,out_features=100):
super().__init__()
self.weights = nn.Parameter(torch.Tensor(in_features,out_features))
self.bias = torch.Tensor(out_features)
self.bias_ = torch.Tensor(out_features)
self.register_buffer('bias_0', bias_)
def forward(self,x:torch.Tensor):
return torch.matmul(x, self.weights,)+self.bias_
if __name__ == "__main__":
img = torch.randn([2,1024]).cuda()
net = Linear().cuda()
preds = net(img)
原因分析: 在Linear.cuda()
的过程中,self.bias_
和self.bias
并没有传递到GPU中,他们虽然在计算图里,但是.cuda()
只转移self._parameters
和self._buffers
中的参数,而self.bais
只是普通的Tensor
对象,所以在这个网络中被转移的参数是self.weights
和self.bias_0
。nn.Module
的初始化如下:
def __init__(self):
"""
Initializes internal Module state, shared by both nn.Module and ScriptModule.
"""
torch._C._log_api_usage_once("python.nn_module")
self.training = True
self._parameters = OrderedDict()
self._buffers = OrderedDict()
self._non_persistent_buffers_set = set()
self._backward_hooks = OrderedDict()
self._is_full_backward_hook = None
self._forward_hooks = OrderedDict()
self._forward_pre_hooks = OrderedDict()
self._state_dict_hooks = OrderedDict()
self._load_state_dict_pre_hooks = OrderedDict()
self._modules = OrderedDict()
NOTE:torch.cuda()
和Module.cuda()
是不一样的,在nn.Module
中,self.register_buffer()
是将数据赋值到nn.Module._buffers
这个变量中,它是一个OrderDict
类,nn.Module._buffers
中的变量也可以通过self.XXX
直接调用,所以注册的buffer
不能与原变量同名,所以上面的self.bias_
与self.bias_0
实际上是不同的变量,他们存储在不同的内存区间。
Debug: forward
函数中的返回改为return torch.matmul(x, self.weights,)+self.bias_0
即可。
2、多GPU训练保存模型,单GPU测试时,加载模型出错
报错: Missing key(s) in state_dict: " ", Unexpected key(s) in state_dict " "
代码片段:
import torch
import torch.nn as nn
# 建立模型
model = nn.Linear(10,2)
model = nn.DataParallel(mode).cuda()
# 保存模型
dicts = model.state_dict()
torch.save(dicts, 'model.pth')
# 加载模型
net = nn.Linear(10,2).cuda()
net.load_state_dict('model.pth')
原因分析: 多GPU训练以后的模型直接保存时,会多出一个module
模块,存在于所有参数的前面,如果我们print(dicts)
会得到如下结果:
OrderedDict([('module.weight', tensor([[-0.1385, -0.1566, 0.1584, 0.0049, -0.3146, 0.2427, -0.0398, 0.3120,
-0.0398, -0.0402],
[ 0.2133, -0.2336, -0.1564, -0.0536, -0.2196, 0.1578, -0.1486, 0.0973,
0.2087, -0.0667]], device='cuda:0')), ('module.bias', tensor([0.1305, 0.1501], device='cuda:0'))])
而新建的net
模块的dict如下:
OrderedDict([('weight', tensor([[-0.1212, -0.0834, 0.1598, 0.1306, -0.2974, -0.2490, -0.2616, 0.1288,
0.1299, 0.0330],
[-0.2946, -0.0711, 0.0299, -0.1956, 0.2640, 0.2085, -0.2127, 0.1652,
-0.2295, -0.2742]], device='cuda:0')), ('bias', tensor([0.1843, 0.0104], device='cuda:0'))])
可以看到这两个OrderDict
的keys
并不一样,所以当然不能匹配了。
解决方法:
- 保存的时候就直接保存
model.module.state_dict()
,这样保存后的OrderDict
里就不会有module
这个东西了; - 加在的时候前面加一句
net = nn.DataParallel(net)
,这样相当于在现有模型的参数名字前加上module.
这个模块,也能和保存的OrderDict
参数一致。
3、DataLoader多线程加载时,Debug模式出错
报错: [IPKernelApp] WARNING | WARNING: attempted to send message from fork
代码片段:
for image,GT in loader:
原因分析:
多线程下,MyDataset(Dataset)
中的__getitem__()
函数里加了断点,导致程序一直重新开始执行,于是报错。更多的可以参考:https://github.com/pytorch/pytorch/issues/13246
解决办法:
如果只想运行,去掉MyDataset
中的断点即可;如果想要Debug,将data_loader
里的workers
设置为0即可。
4、pytorch将全部数据加载到内存时,报错
报错: OSError: [Errno 24] Too many open files
代码片段:
img = Image.open(img_path)
原因分析:
我想在构建Dataset的时候就直接将全部图片加载到内存中,提高后续读写速度。但是PIL.Image
在打开一张照片后,不会立刻自动关闭该文件。由于数据集图片张数较多,所以在读取每张图时都会占用一个线程,导致超过系统默认最大值,进而报错。解决该错误的方法是手动关闭之。
fp = open(img_path,'rb')
img = Image.open(fp)
fp.close()
但是,这么弄会产生新的问题,后续的数据增强过程中会报错“ValueError: seek of closed file”。是因为transforms.Resize(),transforms.ToTensor()
等在处理图片的过程中,文件必须是读取的状态。所以这里我理解是Image.open()
函数在后面数据增强过后才会自动关闭。原问题还是没有解决。于是我把数据读取方式换成了torchvision.io.read_image()
,它读取以后得到的就是torch.Tensor
类,但是是uint8
类别的数据。所以在无法用ToTensor()
,必须手动变成float32
类型。
解决办法:
img = torchvision.io.read_image(img_path)
由于transfroms.Normalize()
的输入必须是float32
类型所以必须手动添加。
if self.transform:
img = img.float().div(255)
img = self.transform(img)
完整数据集代码
import pandas as pd
from torch.utils.data import Dataset, DataLoader
from torchvision.transforms import transforms
from torchvision import datasets,io
class MyDataset(Dataset):
def __init__(self,list_name='trainLabels.csv',path='/home/data/kaggle_diabetic_retinopathy/train/',
transform=None,with_path=False,read_all=False):
super().__init__()
self.img_name = pd.read_csv(list_name)
self.transform = transform
self.path = path
self.with_path =with_path
self.read_all = read_all
if self.read_all:
self.total_img = []
self.total_label = self.img_name['level']
for i in range(len(self.img_name)):
img_path = self.path + str(self.img_name['image'][i])+ '.jpeg'
img = io.read_image(img_path)
self.total_img.append(img)
def __getitem__(self,index):
if self.read_all:
img = self.total_img[index]
label = self.total_label[index]
path = self.path + str(self.img_name['image'][index])+ '.jpeg'
else:
path = self.path + str(self.img_name['image'][index])+ '.jpeg'
label = self.img_name['level'][index]
img = Image.open(path)
if self.transform:
img = img.float().div(255)
img = self.transform(img)
if self.with_path:
return img,label,path
else:
return img, label
def __len__(self):
return len(self.img_name)
def data_loader(batch_size=32,img_size=512, crop_size=448, num_workers=4,csv_name='train.csv',
path='/home/data/kaggle_diabetic_retinopathy/train/',phase='train',with_path=False,read_all=False):
if phase == 'train':
transform = transforms.Compose([
transforms.Resize((img_size,img_size)),
transforms.RandomHorizontalFlip(),
transforms.RandomVerticalFlip(),
transforms.RandomResizedCrop((crop_size,crop_size)),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
dataset = MyDataset(list_name=csv_name, path=path, transform=transform,with_path=with_path,read_all=read_all)
else:
transform = transforms.Compose([
transforms.Resize((img_size,img_size)),
transforms.CenterCrop((crop_size,crop_size)),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
dataset = MyDataset(list_name=csv_name,path=path,transform=transform,with_path=with_path,read_all=read_all)
dataloader = DataLoader(dataset,batch_size=batch_size,shuffle=True,
num_workers=num_workers, pin_memory=True)
return dataloader
5、OSError: [Errno 12] Cannot allocate memory
报错: OSError: [Errno 12] Cannot allocate memory
代码片段:
for i, data in enumerate(train_loader):
原因分析: 也是数据在多线程加载是出现问题,无法分配内存有点奇怪。在dataloder
中设置num_workers=0
可以解决,但此时数据在CPU上的处理会很慢,极大延长训练时间。另外所有数据不是一次性加载的情况也不会报错。
解决方式: 设置num_workers=0
或者分批次读取数据。