pytorch 踩坑笔记

本来应该很早就写一个的,现在也不晚,慢慢记录吧

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._parametersself._buffers中的参数,而self.bais只是普通的Tensor对象,所以在这个网络中被转移的参数是self.weightsself.bias_0nn.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'))])

可以看到这两个OrderDictkeys并不一样,所以当然不能匹配了。

扫描二维码关注公众号,回复: 15569563 查看本文章

解决方法:

  1. 保存的时候就直接保存model.module.state_dict(),这样保存后的OrderDict里就不会有module这个东西了;
  2. 加在的时候前面加一句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或者分批次读取数据。

猜你喜欢

转载自blog.csdn.net/Huang_Fj/article/details/118103370
今日推荐