天天看点

kaggle Dogs_and_Cats实践记录数据下载transfer learning代码下载

kaggle Dogs_and_Cats实践记录

  • 数据下载
  • transfer learning
    • train-set, dev-set准备
    • fine-tuning resnet18
    • 预测并将结果存至submission.csv
  • 代码下载

数据下载

kaggle Dogs_and_Cats数据集下载,可以在配置好kaggle 好之后,从相应的Competition:Dogs_and_Cats 中下载数据。

原数据包含了train.zip & test1.zip & sampleSubmission.csv

解压后,train.zip包含25000张图片,12500为Dogs, 12500为Cats, test1.zip解压之后为待预测的数据,12500张。

transfer learning

总体思路: Dogs, Cats与ImageNet中的数据有重合,因此,采用 transfer learning的方式来做。

train-set, dev-set准备

首先,训练需使用train.zip中的数据,为更快地操作,结合torchvision ImageFolder(folder.py)类,将原数据分别放进dogs , cats目录,并且使用下述脚本,将train分成train-set, dev-set.

import os
import sys
import shutil
import glob
import os.path as osp

dev_num = 2500

def split_data(root, target_dir):
    img_files = glob.glob(root+"/*.jpg")
    dev_files = img_files[-dev_num:]
    print('dev_files:', dev_files)

    for dev_file in dev_files:
        shutil.move(dev_file, osp.join(target_dir, osp.basename(dev_file)))
        


if __name__ == "__main__":
    root = sys.argv[1]
    target_dir = sys.argv[2]
    split_data(root, target_dir)
           

fine-tuning resnet18

构建模型,因要分类的数据不复杂,现采用resnet18作为基础网格,并且借用已有的预训练权重,只对最后一层fc层进行fine-tune.代码如下:

import torch
import torchvision

import torch.optim as optim
from torch.utils.data import *
from torchvision import models, transforms, datasets
import torch.nn as nn
import matplotlib.pyplot as plt

from torch.utils.tensorboard import SummaryWriter


writer = SummaryWriter(log_dir='runs')


def imshow(inp, title=None):
    """ Imshow for Tensor"""
    print(inp.size())
    inp = inp.numpy().transpose((1,2,0))
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])

    inp = inp*std+mean
    inp = np.clip(inp, 0, 1)
    plt.imshow(inp)
    if title is not None:
        plt.title(title)
    plt.show()

def test_show_data():
    # Test Showing, Get a batch of training data
    inputs, classes = next(iter(train_dataloader))
    # Make a grid from batch
    out = torchvision.utils.make_grid(inputs)
    imshow(out, title=[class_names[x] for x in classes])

data_transforms = {
    'train' : transforms.Compose([
        transforms.RandomResizedCrop(224),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'val' : transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
}

train_dataset = datasets.ImageFolder(root="/home/jingwenlai/data/kaggle/dogs_and_cats/train", transform=data_transforms['train'])
train_dataloader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=4)
val_dataset = datasets.ImageFolder(root="/home/jingwenlai/data/kaggle/dogs_and_cats/dev", transform=data_transforms['val'])
val_dataloader = DataLoader(val_dataset, batch_size=32, shuffle=False, num_workers=4)

class_names = ['cat', 'dog']
device = torch.device('cuda:0' if torch.cuda.is_available() else "cpu")

# Now, because the val set have problem, only use trainset
def train_model(model, criterion, optimizer, lr_scheduler, num_epoches):
    best_model = None
    best_acc = 0.0

    for epoch in range(0, num_epoches):
        print("---------Training {}/{}---------".format(epoch, num_epoches))
        model.train()
        running_loss = 0.0
        running_corrects = 0

        #Iterate over data
        for inputs, labels in train_dataloader:
            inputs = inputs.to(device)
            labels = labels.to(device)

            # Zero the parameters gradients
            optimizer.zero_grad()

            #forward 
            outputs = model(inputs)            
            _, preds = torch.max(outputs, 1)
            
            loss = criterion(outputs, labels)

            #backward + optimize only in training phase
            loss.backward()
            optimizer.step()

            # statistics
            running_loss += loss.item() * inputs.size(0)
            running_corrects += torch.sum(preds==labels.data)

        lr_scheduler.step()

        epoch_train_loss = running_loss/len(train_dataset)
        epoch_train_acc = running_corrects.double()/len(train_dataset)
        print('epoch train loss:{:.4f}, epoch train acc: {:.4f}'.format(epoch_train_loss, epoch_train_acc))
        #writer.add_scalar('epoch_train_loss',epoch_train_loss, epoch)
        #writer.add_scalar('epoch_train_acc', epoch_train_acc, epoch)

        model.eval()
        val_loss = 0.0
        val_corrects = 0

        with torch.no_grad():
            for inputs, labels in val_dataloader:
                inputs = inputs.to(device)
                labels = labels.to(device)

                outputs = model(inputs)
                _, preds = torch.max(outputs, 1)

                loss = criterion(outputs, labels)

                val_loss += loss.item() * inputs.size(0)
                val_corrects += torch.sum(preds == labels.data)

        epoch_val_loss = val_loss/len(val_dataset)
        epoch_val_acc = val_corrects.double()/len(val_dataset)
        print('epoch val loss: {:.4f}, epoch val acc: {:.4f}'.format(epoch_val_loss, epoch_val_acc))
        #writer.add_scalar('epoch_val_loss', epoch_val_loss, epoch)
        #writer.add_scalar('epoch_val_acc', epoch_val_acc, epoch)

        writer.add_scalars('loss', {'train_loss': epoch_train_loss, 'val_loss': epoch_val_loss}, epoch)
        writer.add_scalars('acc', {'train_acc': epoch_train_acc, 'val_acc': epoch_val_acc}, epoch)

        if epoch_val_acc > best_acc:
            best_acc = epoch_val_acc
            ckpt_state = {
                'epoch': epoch,
                'best_acc' : best_acc,
                'model_state': model.state_dict(),
                'optimizer_state' : optimizer.state_dict(),
                'loss': loss
            }
            torch.save(ckpt_state, 'ckpt_{}_model.pt'.format(epoch));
            best_model = model.state_dict()
        
    return best_model

model = models.resnet18(pretrained=True)
for param in model.parameters(): # set all pretrained weights are kept & not update that gradients
    param.requires_grad = False

numfts = model.fc.in_features
model.fc = nn.Linear(numfts, 2)
model = model.to(device)
print("modified model:", model)

criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.fc.parameters(), lr=0.001, momentum = 0.9)
lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.1)

best_model = train_model(model, criterion, optimizer, lr_scheduler, num_epoches=25)

torch.save(best_model, 'best_model.pt')
           

训练出来的结果,达到97%~98%的准确率

---------Training 0/25---------
epoch train loss:0.3792, epoch train acc: 0.8478
epoch val loss: 0.1733, epoch val acc: 0.9582
---------Training 1/25---------
epoch train loss:0.2315, epoch train acc: 0.9216
epoch val loss: 0.1223, epoch val acc: 0.9684
---------Training 2/25---------
epoch train loss:0.2033, epoch train acc: 0.9257
epoch val loss: 0.1008, epoch val acc: 0.9732
---------Training 3/25---------
epoch train loss:0.1848, epoch train acc: 0.9318
epoch val loss: 0.0871, epoch val acc: 0.9764
---------Training 4/25---------
epoch train loss:0.1739, epoch train acc: 0.9332
epoch val loss: 0.0825, epoch val acc: 0.9764
---------Training 5/25---------
epoch train loss:0.1670, epoch train acc: 0.9355
epoch val loss: 0.0759, epoch val acc: 0.9774
---------Training 6/25---------
epoch train loss:0.1631, epoch train acc: 0.9352
epoch val loss: 0.0712, epoch val acc: 0.9786
---------Training 7/25---------
epoch train loss:0.1623, epoch train acc: 0.9341
epoch val loss: 0.0731, epoch val acc: 0.9778
---------Training 8/25---------
epoch train loss:0.1615, epoch train acc: 0.9353
epoch val loss: 0.0718, epoch val acc: 0.9782
---------Training 9/25---------
epoch train loss:0.1600, epoch train acc: 0.9360
epoch val loss: 0.0721, epoch val acc: 0.9770
---------Training 10/25---------
epoch train loss:0.1590, epoch train acc: 0.9356
epoch val loss: 0.0710, epoch val acc: 0.9776
---------Training 11/25---------
epoch train loss:0.1624, epoch train acc: 0.9354
epoch val loss: 0.0714, epoch val acc: 0.9776
---------Training 12/25---------
epoch train loss:0.1611, epoch train acc: 0.9355
epoch val loss: 0.0698, epoch val acc: 0.9786
---------Training 13/25---------
epoch train loss:0.1566, epoch train acc: 0.9371
epoch val loss: 0.0708, epoch val acc: 0.9782
---------Training 14/25---------
epoch train loss:0.1547, epoch train acc: 0.9397
epoch val loss: 0.0704, epoch val acc: 0.9780
---------Training 15/25---------
epoch train loss:0.1590, epoch train acc: 0.9371
epoch val loss: 0.0707, epoch val acc: 0.9780
---------Training 16/25---------
epoch train loss:0.1612, epoch train acc: 0.9358
epoch val loss: 0.0707, epoch val acc: 0.9770
---------Training 17/25---------
epoch train loss:0.1611, epoch train acc: 0.9358
epoch val loss: 0.0707, epoch val acc: 0.9784
---------Training 18/25---------
epoch train loss:0.1596, epoch train acc: 0.9361
epoch val loss: 0.0698, epoch val acc: 0.9780
---------Training 19/25---------
epoch train loss:0.1590, epoch train acc: 0.9354
epoch val loss: 0.0701, epoch val acc: 0.9780
---------Training 20/25---------
epoch train loss:0.1599, epoch train acc: 0.9347
epoch val loss: 0.0701, epoch val acc: 0.9774
---------Training 21/25---------
epoch train loss:0.1588, epoch train acc: 0.9381
epoch val loss: 0.0695, epoch val acc: 0.9776
---------Training 22/25---------
epoch train loss:0.1567, epoch train acc: 0.9385
epoch val loss: 0.0691, epoch val acc: 0.9784
---------Training 23/25---------
epoch train loss:0.1591, epoch train acc: 0.9358
epoch val loss: 0.0695, epoch val acc: 0.9790
---------Training 24/25---------
epoch train loss:0.1618, epoch train acc: 0.9345
epoch val loss: 0.0745, epoch val acc: 0.9760
           

预测并将结果存至submission.csv

使用predict_and_save.py,对test1.zip解压出的图片进行预测,留意到这里用了list.sort(key=…)这个接口,对文件进行排序。默认情况下glob.glob(…)接口获取的文件列表不是数字升序的。

import torch
import torchvision

import torch.optim as optim
from torch.utils.data import *
from torchvision import models, transforms, datasets
import torch.nn as nn
import matplotlib.pyplot as plt
import glob
import os.path as osp

eval_transform = transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])

device = torch.device('cuda:0' if torch.cuda.is_available() else "cpu")
model = models.resnet18(pretrained=True)
for param in model.parameters(): # set all pretrained weights are kept & not update that gradients
    param.requires_grad = False

numfts = model.fc.in_features
model.fc = nn.Linear(numfts, 2)
model = model.to(device)
model.load_state_dict(torch.load('best_model.pt'))

from PIL import Image

csv_filename = "submission.csv"
csv_file = open(csv_filename, 'w')
csv_file.write("id,label"+"\n")

root="/home/jingwenlai/data/kaggle/dogs_and_cats/test1"
img_files = glob.glob(osp.join(root,"*.jpg"))
img_files.sort(key=lambda x: int(osp.basename(x)[:-4]))
    
model.eval()
with torch.no_grad():
    for img_file in img_files:
        basename = osp.basename(img_file)
        print('predicting...', basename)
        file_id = basename.split('.')[0]
        with open(img_file, 'rb') as f:
            img = Image.open(f)
            img = img.convert('RGB')
            img_tensor = eval_transform(img)
            img_tensor = img_tensor.unsqueeze(0).to(device)            

            outputs = model(img_tensor)
            _, preds = torch.max(outputs, 1)

            predict_result = preds.cpu().item()
            csv_file.write(str(file_id)+","+str(predict_result)+"\n")

csv_file.close()
           

代码下载

https://github.com/dreamway/Dogs_vs_Cats

继续阅读