zoukankan      html  css  js  c++  java
  • cnn+pytorch实现食物分类

    食物品种共有11类:Bread, Dairy product, Dessert, Egg, Fried food, Meat, Noodles/Pasta, Rice, Seafood, Soup, and Vegetable/Fruit.
    Training set: 9866张
    Validation set: 3430张
    Testing set: 3347张

    一、读取数据

    ​ 想要解决这个问题,首先我们得先具备将数据读取进入程序的能力,程序如下:

    import os
    import sys
    import numpy as np
    import cv2
    import torch
    import torch.nn as nn
    import torchvision.transforms as transforms
    from torch.utils.data import DataLoader, Dataset
    import time
    import random
    
    def readfile(path, label, img_num):
        # label 是一个,代表需不需要传回 y 值
        image_dir = os.listdir(path)				# 获取path下的文件名列表
        random.shuffle(image_dir)					# 打乱顺序
        image_dir = sorted(image_dir[0:img_num])	# 选取指定数目,返回迭代器
        x = np.zeros((len(image_dir), 128, 128, 3), dtype=np.uint8)
        y = np.zeros((len(image_dir)), dtype=np.uint8)
        for i, file in enumerate(image_dir):
            img = cv2.imread(os.path.join(path, file))
            x[i, :, :] = cv2.resize(img,(128, 128))
            if label:
                y[i] = int(file.split("_")[0])
        if label:
          return x, y
        else:
          return x
    
    # 分别将 training set、validation set、testing set 用 readfile 读取
    workspace_dir = './food-11'
    print("Reading data")
    train_x, train_y = readfile(os.path.join(workspace_dir, "training"), True, 100)
    print("Size of training data = {}".format(len(train_x)))
    val_x, val_y = readfile(os.path.join(workspace_dir, "validation"), True, 25)
    print("Size of validation data = {}".format(len(val_x)))
    test_x = readfile(os.path.join(workspace_dir, "testing"), False, 50)
    print("Size of Testing data = {}".format(len(test_x)))
    
    

    二、制作数据集

    ​ 为了方便数据进行训练,参考pytorch的 torch.utils.data

    ​ torch.utils.data.Dataset 是一个表示数据集的抽象类,继承它实现自定义的数据集,注意的是其中 __ getitem __()和__len__() 两个函数必须重写。

    ​ torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False,...) 返回一个提供数据集数据的迭代器。

    # 图形变换
    transform = transforms.Compose([
        transforms.ToTensor()
    ])
    # 定义ImgDataset类,继承Dataset,实现数据读取方式
    class ImgDataset(Dataset):
        def __init__(self, x, y=None, transform=None):
            self.x = x
            self.y = y
            if y is not None:
                self.y = torch.LongTensor(y)
            self.transform = transform
        def __len__(self):
            return len(self.x)
        def __getitem__(self, index):
            X = self.x[index]
            if self.transform is not None:
                X = self.transform(X)
            if self.y is not None:
                Y = self.y[index]
                return X, Y
            else:
                return X
    
    batch_size = 10		# 一次训练所选取的样本数
    train_set = ImgDataset(train_x, train_y, transform)
    val_set = ImgDataset(val_x, val_y, transform)
    # 使用DataLoader(), 实现数据的批量读取,一个迭代器的作用
    train_loader = DataLoader(train_set, batch_size=batch_size, shuffle=True)
    val_loader = DataLoader(val_set, batch_size=batch_size, shuffle=False)
    

    三、定义模型及其优化

    ​ 由于是分类任务,首先使用CNN尝试一下效果,结构为5层3*3卷积层+3层全连接层,激活函数采用ReLU,并且模型使用交叉熵损失函数和Adam优化算法。

    class Classifier(nn.Module):
        def __init__(self):
            super(Classifier, self).__init__()
            # torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding)
            # torch.nn.MaxPool2d(kernel_size, stride, padding)
            # input 维度 [3, 128, 128]
            self.cnn = nn.Sequential(
                nn.Conv2d(3, 64, 3, 1, 1),  # [64, 128, 128]
                nn.BatchNorm2d(64),
                nn.ReLU(),
                nn.MaxPool2d(2, 2, 0),  # [64, 64, 64]
    
                nn.Conv2d(64, 128, 3, 1, 1),  # [128, 64, 64]
                nn.BatchNorm2d(128),
                nn.ReLU(),
                nn.MaxPool2d(2, 2, 0),  # [128, 32, 32]
    
                nn.Conv2d(128, 256, 3, 1, 1),  # [256, 32, 32]
                nn.BatchNorm2d(256),
                nn.ReLU(),
                nn.MaxPool2d(2, 2, 0),  # [256, 16, 16]
    
                nn.Conv2d(256, 512, 3, 1, 1),  # [512, 16, 16]
                nn.BatchNorm2d(512),
                nn.ReLU(),
                nn.MaxPool2d(2, 2, 0),  # [512, 8, 8]
    
                nn.Conv2d(512, 512, 3, 1, 1),  # [512, 8, 8]
                nn.BatchNorm2d(512),
                nn.ReLU(),
                nn.MaxPool2d(2, 2, 0),  # [512, 4, 4]
            )
            self.fc = nn.Sequential(
                nn.Linear(512 * 4 * 4, 1024),
                nn.ReLU(),
                nn.Linear(1024, 512),
                nn.ReLU(),
                nn.Linear(512, 11)
            )
    
        def forward(self, x):
            out = self.cnn(x)
            out = out.view(out.size()[0], -1)
            return self.fc(out)
        
    model = Classifier()
    loss = nn.CrossEntropyLoss()  	# 因为是分类任务,所以loss function使用 CrossEntropyLoss
    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)  # optimizer 使用 Adam
    
    

    四、训练数据集

    ​ 训练过程分成两个阶段,先是训练集跑模型,验证集验证模型性能,结果尚可,则训练集合验证集数据合并,训练出最终的模型。

    num_epoch = 10
    # 开始训练
    for epoch in range(num_epoch):
        epoch_start_time = time.time()
    
        train_acc = 0.0
        train_loss = 0.0
        val_acc = 0.0
        val_loss = 0.0
    
        model.train()  								# train model会开放Dropout和BN
        for i, data in enumerate(train_loader):
            optimizer.zero_grad()  					# 每次用前将梯度置零
            train_pred = model(data[0]) 			# 利用 model 的 forward 函数返回预测值
            batch_loss = loss(train_pred, data[1])  # 计算 loss
            batch_loss.backward()  					# tensor(item, grad_fn=<NllLossBackward>)
            optimizer.step()  						# 以 optimizer 用 gradient 更新参数
    
            train_acc += np.sum(np.argmax(train_pred.data.numpy(),axis=1) == data[1].numpy())
            train_loss += batch_loss.item()
    
        model.eval()
        with torch.no_grad():   					# 不跟踪梯度
            for i, data in enumerate(val_loader):
                val_pred = model(data[0])
                batch_loss = loss(val_pred, data[1])
    
                val_acc += np.sum(np.argmax(val_pred.data.numpy(),axis=1) == data[1].numpy())
                val_loss += batch_loss.item()
    
            #  打印结果
            print('[%03d/%03d] %2.2f sec(s) Train Acc: %3.6f Loss: %3.6f | Val Acc: %3.6f loss: %3.6f' % 
                  (epoch + 1, num_epoch, time.time() - epoch_start_time, 
                   train_acc / train_set.__len__(), train_loss / train_set.__len__(), val_acc / val_set.__len__(),
                   val_loss / val_set.__len__()))
    
    
    # 将训练集合验证集合并,再次训练
    train_val_x = np.concatenate((train_x, val_x), axis=0)
    train_val_y = np.concatenate((train_y, val_y), axis=0)
    train_val_set = ImgDataset(train_val_x, train_val_y, transform)
    train_val_loader = DataLoader(train_val_set, batch_size=batch_size, shuffle=True)
    
    model_best = Classifier()
    loss = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model_best.parameters(), lr=0.001) # optimizer 使用 Adam
    num_epoch = 10
    
    for epoch in range(num_epoch):
        epoch_start_time = time.time()
        train_acc = 0.0
        train_loss = 0.0
    
        model_best.train()
        for i, data in enumerate(train_val_loader):
            optimizer.zero_grad()
            train_pred = model_best(data[0])
            batch_loss = loss(train_pred, data[1])
            batch_loss.backward()
            optimizer.step()
    
            train_acc += np.sum(np.argmax(train_pred.data.numpy(),axis=1) == data[1].numpy())
            train_loss += batch_loss.item()
    
        #打印结果
        print('[%03d/%03d] %2.2f sec(s) Train Acc: %3.6f Loss: %3.6f' % 
          (epoch + 1, num_epoch, time.time()-epoch_start_time, 
          train_acc/train_val_set.__len__(), train_loss/train_val_set.__len__()))
    

    五、测试集预测

    ​ 基于上面的模型对测试集进行预测,提交结果。

    # 对测试集进行预测
    test_set = ImgDataset(test_x, transform=transform)
    test_loader = DataLoader(test_set, batch_size=batch_size, shuffle=False)
    model_best.eval()
    prediction = []
    with torch.no_grad():
        for i, data in enumerate(test_loader):
            test_pred = model_best(data)
            test_label = np.argmax(test_pred.data.numpy(), axis=1)
            for y in test_label:
                prediction.append(y)
                
                
                
    # 打印测试集预测值
    print(prediction)
    
    # 运行结果
    Reading data
    Size of training data = 100
    Size of validation data = 25
    Size of Testing data = 50
    [001/010] 29.65 sec(s) Train Acc: 0.110000 Loss: 0.381131 | Val Acc: 0.080000 loss: 0.291486
    [002/010] 24.25 sec(s) Train Acc: 0.090000 Loss: 0.254135 | Val Acc: 0.040000 loss: 0.279987
    [003/010] 24.85 sec(s) Train Acc: 0.120000 Loss: 0.234980 | Val Acc: 0.000000 loss: 0.276088
    [004/010] 28.55 sec(s) Train Acc: 0.200000 Loss: 0.218716 | Val Acc: 0.080000 loss: 0.274945
    [005/010] 27.35 sec(s) Train Acc: 0.330000 Loss: 0.202054 | Val Acc: 0.120000 loss: 0.277814
    [006/010] 27.55 sec(s) Train Acc: 0.290000 Loss: 0.202682 | Val Acc: 0.080000 loss: 0.279993
    [007/010] 25.17 sec(s) Train Acc: 0.320000 Loss: 0.185320 | Val Acc: 0.080000 loss: 0.315286
    [008/010] 23.11 sec(s) Train Acc: 0.400000 Loss: 0.168070 | Val Acc: 0.000000 loss: 0.293173
    [009/010] 23.24 sec(s) Train Acc: 0.370000 Loss: 0.164915 | Val Acc: 0.200000 loss: 0.275563
    [010/010] 24.79 sec(s) Train Acc: 0.480000 Loss: 0.149648 | Val Acc: 0.040000 loss: 0.342993
    [001/010] 28.93 sec(s) Train Acc: 0.160000 Loss: 0.371954
    [002/010] 28.82 sec(s) Train Acc: 0.080000 Loss: 0.258056
    [003/010] 27.99 sec(s) Train Acc: 0.240000 Loss: 0.230340
    [004/010] 28.19 sec(s) Train Acc: 0.224000 Loss: 0.223114
    [005/010] 28.35 sec(s) Train Acc: 0.232000 Loss: 0.209750
    [006/010] 27.52 sec(s) Train Acc: 0.376000 Loss: 0.197971
    [007/010] 28.17 sec(s) Train Acc: 0.328000 Loss: 0.188320
    [008/010] 27.53 sec(s) Train Acc: 0.448000 Loss: 0.182249
    [009/010] 29.18 sec(s) Train Acc: 0.344000 Loss: 0.203064
    [010/010] 27.91 sec(s) Train Acc: 0.472000 Loss: 0.167914
    [9, 10, 2, 5, 9, 2, 10, 10, 9, 10, 10, 0, 0, 0, 2, 0, 9, 5, 10, 4, 4, 0, 3, 0, 10, 2, 10, 4, 2, 4, 5, 9, 2, 10, 2, 5, 1, 10, 4, 9, 10, 10, 9, 9, 10, 10, 9, 10, 5, 2]
    
    
    
  • 相关阅读:
    delphi 数据导出 进度条自己生成
    在存储过程中编写正确的事务处理代码(SQL Server 2000 & 2005)
    在Delphi中如何获得SQL中存储过程的返回值?
    object-c中管理文件和目录:NSFileManager使用方法
    Delphi中动态链接库(DLL)的建立和使用
    开源免费天气预报接口API以及全国所有地区代码!!(国家气象局提供)
    wince 程序无法执行的原因
    如果我写一个播放器
    busybox配置
    switch case被人忽视的一点!zt
  • 原文地址:https://www.cnblogs.com/wys7541/p/13583024.html
Copyright © 2011-2022 走看看