9. Pytorch入门教程——在Kaggle给出的更大的测试集进行预测

原始的CIFAR10数据集有60000张图,其中训练集50000张,测试集10000张。然而,kaggle提供了300000张图的大型数据集用来测试CIFAR10。这里是kaggle网站关于这些图片的介绍:

“为了阻止某些形式的作弊(如手动标签),我们在测试集中添加了290,000张垃圾图片。这些图片在评分中被忽略。我们还对官方的10,000个测试图像做了一些微不足道的修改,以防止通过文件哈希查找它们。这些修改不会明显影响得分。你应该预测所有300,000个图像的标签。”

一、对于Kaggle测试图片,创建我们自己的定制数据集

为了处理这个数据集,我们编写了自己的自定义dataset类,该类派生自Pytorch的基本dataset类。然后我们将这个数据集对象传递给Pytorch Dataloader。这使得处理这个大型数据集非常方便。它还为我们创建自己的图像数据集提供了良好的实践。 下面是我们自定义数据集类的代码。代码非常简单。

  • 典型的客户数据集包含一个“init”方法、一个将其转换为迭代器的“getitem”方法和一个使Python的len()函数在数据集上工作的“len”方法。
    我们的自定义数据集类假设有关数据集图像文件的信息包含在一个CSV文件中;
  • 图像id包含在文件的第0列中,而图像文件名(路径)在第1列中,图像的文本标签(如在文件中可用)在第2列中,例如bird、plane等;
  • 我们的Kaggle测试集没有标签,因为Kaggle使用它来打分,因此不为测试集提供标签;
  • 我们使用Panda Dataframe来处理CSV文件,然后创建实际的图像集。
import pandas as pd
from PIL import Image
import glob

class ImageDataset(Dataset):

    def __init__(self, csv_path,
                 transforms=None,
                 labels=False):
       
        self.labels = None
        self.transforms = None

        '''
        我们读取Pandas DataFrame中的csv文件,并从其列中提取图像id、图像文件路径和标签(如果存在),假设它们分别包含在列0、1和2中。
        '''
        self.df = pd.read_csv(csv_path)
        
        self.ids = np.asarray(self.df.iloc[:, 0])
        
        self.images = np.asarray(self.df.iloc[:, 1])
        
        if labels:
            self.labels = np.asarray(self.df.iloc[:, 1])
        
        '''
        我们设置数据集的长度,并在传递时设置转换
        '''
        self.data_len = len(self.df.index)
        if transforms is not None:
            self.transforms = transforms
            
        #print(self.data_len)

    '''
    在__ getitem__中,我们必须根据所请求的索引从文件中读取一张图片。请记住,在创建一个批时,Dataloader将调用此函数。它会为每一个批构造循环调用它。我们在每次这样的
    调用中返回图像和位置索引。
    '''
    def __getitem__(self, index):
        
        image_name = self.images[index]
        id_ = self.ids[index]
        img_ = Image.open(image_name)
        
        '''
        对图片每个通道进行转换,[:3,:,:]表示三个通道的所有行和列
        '''
        if self.transforms is not None:
            img_ = self.transforms(img_)[:3,:,:]
        
        '''
        为了保持API的一致性并总是返回一个双值元组,即使没有标签,为每个图像返回0作为标签。假设这个方法的调用者知道是否需要标签。
        '''
        label = 0
        if self.labels is not None:
            label = self.labels[index]
        
        '''
        我们的数据集对象返回一个三元组(id,image,label)
        '''
        return (id_,img_,label)

    def __len__(self):
        return self.data_len
def create_csv_from_folder(folder_path,outfile,cols=['id','path']):
    
    f = glob.glob(folder_path+'/*.*')
    
    ids = []
    for elem in f:
        t = elem[elem.rfind('/')+1:]
        ids.append(t[:t.rfind('.')])
    data = {cols[0]:ids,cols[1]:f}    
    df = pd.DataFrame(data,columns=cols)
    df.to_csv(outfile,index=False)

使用此函数,我们可以创建csv文件,并将文件夹和所需的输出csv文件名作为参数传递给它。我们已经将来自Kaggle的测试图像放在cifar10测试文件夹中

create_csv_from_folder('test','cifar10-test.csv')

查看csv文件中的几个样本

df = pd.read_csv('cifar10-test.csv')
df[:10]

在这里插入图片描述

len(df.index)

300000

二、测试和准备提交Kaggle测试集文件

现在我们需要做的就是创建定制数据集和一个Dataloader,并用我们的模型进行评估。

test_transform_cifar10 = transforms.Compose([transforms.Resize((224,224)),
                                     transforms.ToTensor(),
                                     transforms.Normalize(cifar10_mean,cifar10_std)
                                    ])

cifar10_test_dset = ImageDataset('cifar10-test.csv',transforms=test_transform_cifar10)
len(cifar10_test_dset)

300000
数据集有300000张图

cifar10_test_dset.df[:10]

在这里插入图片描述

cifar10_test_testloader = DataLoader(cifar10_test_dset, batch_size=50,num_workers=0)
dataiter = iter(cifar10_test_testloader)
id_,images_,_ = dataiter.next()
images_.shape

torch.Size([50, 3, 224, 224])
如我们期望的那样,Dataloader的一个批有正确的维数

三、结果输出到CSV

要提交给Kaggle,我们需要创建一个csv文件,第一列是带有图片-id(名字)的,第二列是带有标签(请参阅竞赛网页,以查看提交文件示例)。

最简单的方法是再次使用Panda DataFrame准备结果和文件。

下面是正常的Dataloader循环:

  • 我们得到下一批数据。记住,我们的Dataset对象返回一个三元组(id,image,label)。在本例中,我们忽略标签,所以总是返回0;
  • 我们首先使用集成进行预测,将预测张量转换回CPU,然后将其转换为numpy,再使用numpy提供的flatten方法将其变平,最后将其转换为一个简单的Python列表。这将为我们提供整个批的预测类;
  • 我们继续在列表中收集预测,并在另一个列表中收集相应的标签(使用image_ids在类字典中查找);
  • 最后,我们创建了一个包含两个必需列的Pandas DataFrame,并将其作为CSV文件写入磁盘。为了精确匹配所需的格式,我们将索引设置为False;
  • 接下来,我们按照示例文件所示的ids对值进行排序,并重写CSV文件。

由于图片数量多,输出速度有点慢。

predictions = []
image_ids = []
for ids_,images_,_ in cifar10_test_testloader:
    preds_ = ensemble.predict(images_).cpu().numpy().flatten().tolist()
    predictions += [class_dict[pred] for pred in preds_]
    image_ids += ids_.numpy().flatten().tolist()

pd.DataFrame({'id':image_ids,'label':predictions}).to_csv('submission.csv',index=False)

最后对结果按ID进行排序

df = pd.read_csv('submission.csv')
df = df.sort_values('id')
df.to_csv('submission.csv',index=False)

四、结论

我提交给Kaggle后,得到了以下结果:

  • 我获得了第三名,在CIFAR10的几个网站上发布的基准上也相当高;
  • 花了我少于75分钟的时间来训练所有模型和创建集成模型;
  • 当然,如果你多花点时间玩玩,我相信你可以打败最高的基准。

在试图在CIFAR10上实现高准确度的过程中,我们创建了一组可重复的类和实用函数,可以用于任何图像分类任务

获得高准确度的关键是:

  • 如果图像集足够大,请使用图像集本身的平均值和std来进行标准化,而不要使用ImageNet;
  • 使用自适应学习率算法作为优化器。到目前为止,Adadelta在CIFAR10和flowers上的表现是最好;
  • 解冻后训练3到5个epochs。然后保存,重置notebook,再次解冻,再训练3到5个epochs,然后冻结,再训练2到3个epochs,你就可以完成了;
  • 试验额外的FC层,看看是否有任何不同;
  • 训练多个模型,保存它们,并将集成它们来进行最终预测;
  • 也许最重要的是,把应用机器学习看作是一门软件工程学科,同时也是一门统计和数学学科。这意味着大多数软件工程最佳实践仍然适用。以这种方式考虑ML问题将引导您设计良好的可重用组件,如类和实用程序函数,以创建您自己的API,即使您一开始就得到了一些代码;
  • 重构代码,并将其放入您自己的类中,并相应地对其进行修改,来创建干净的接口和抽象。这将节省在其他数据集上的大量重复工作,并且您也可以在其他更大的项目和任务中使用您的类作为组件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值