李宏毅深度学习作业三_2020_pytorch实现

一、作业三说明:

目的:食物的分类,给出一张食物的照片,预测出它是11种食物中的哪一种。十一种label分别为:Bread, Dairy product, Dessert, Egg, Fried food, Meat, Noodles/Pasta, Rice, Seafood, Soup, and Vegetable/Fruit
在这里插入图片描述
方法:建立CNN(卷积神经网络),训练,测试
要求:10min跑完,验证集上准确率90%以上
数据集:
链接:https://pan.baidu.com/s/1RwQ0wuu1mXFzNPIlqa3Qdw
提取码:d6to

大小1.1G,food-11目录下有三个文件夹,三个文件夹下就是所需的图片数据。
testing:编号0000.jpg-3346.jpg,没有label,用来测试;
training:编号0_0.jpg-10_708.jpg,文件名首位数字就是label(红色下划线),从0-10,一共11个label;
validation:编号0_0.jpg-10_231.jpg,和训练集类似,文件名首位数字就是label,从0-10,一共11个label。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、思路与代码实现

1、导入需要的库

import torch
import torch.nn as nn
import numpy as np
import cv2
import os
import pandas as pd
import torchvision.transforms as transforms
from torch.utils.data import DataLoader,Dataset
import time

2、文件读取和处理
通过前面观察testing、training、validation文件下的图片命名规则,training、validation里的图片可以用一种方式读取,testing下的文件命名与前两个就差了一个label。所以,定义了一个读取文件的函数,一个位置参数,和一个用以区分是否有label的参数。

def readfile(path,own_label):#后面的参数用来区分训练集和测试集
    image_dir = sorted(os.listdir(path)) #对path下的文件进行排序后输出
    x = np.zeros((len(image_dir),128,128,3),dtype=np.uint8) #初始化,用来保存训练数据
    y = np.zeros((len(image_dir)),dtype=np.uint8) #初始化,用来保存label
    for i,file in enumerate(image_dir): #循环,将图片信息保存到x,y
        img = cv2.imread(os.path.join(path,file))
        x[i,:,:] = cv2.resize(img,(128,128))
        if own_label: #此处来判断是训练还是测试,True为训练,False为测试
            y[i] = int(file.split("_")[0]) #如果是True,则给y赋值,文件名的第一个数
    if own_label:#有label,返回x,y
        return x,y
    else:#无label,返回x
        return x

接下来就可以用定义好的函数载入数据:

#分别读取训练、验证和测试的数据,保存到相应的变量里
workspace_dir = './food-11'  #数据保存的路径,改为自己的路径
print("Reading data") #提示读取数据
train_x, train_y = readfile(os.path.join(workspace_dir,"training"),True)#读取训练集数据
print("Size of training data = {}".format(len(train_x))) #输出训练集数据的长度
val_x ,val_y = readfile(os.path.join(workspace_dir,"validation"),True) #读取验证集数据
print("Size of validation data = {}".format(len(val_x))) #输出验证集数据的长度
test_x = readfile(os.path.join(workspace_dir,"testing"),False) #读取测试集数据,没有label
print("Size of Testing data = {}".format(len(test_x))) #输出测试集数据长度

然后,进行一下数据增强:

#图像增强
train_transform = transforms.Compose([
    transforms.ToPILImage(),
    transforms.RandomHorizontalFlip(),#随机将图片水平翻转
    transforms.RandomRotation(15),#随机旋转图片
    transforms.ToTensor(),#将图片转化为Tensor,并归一化
])

#测试集图像处理
test_transform = transforms.Compose([
    transforms.ToPILImage(),
    transforms.ToTensor(),
])

使用Dataset类进行数据集的处理,同样可以用一个参数来区分训练和测试
可以参考:https://blog.csdn.net/Zhanglw882/article/details/109255908
下的第七个代码
在这里插入图片描述

#数据集类处理
class ImgDataset(Dataset):
    def __init__(self,x,y = None,transform = None):
        self.x = x
        self.y = y
        if y is not None: #label必须为长整形
            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) #有,则取变换后的X
        if self.y is not None: #判断是否有y(label)
            Y = self.y[index]
            return X,Y #有,则返回X,Y
        else:
            return X #无y(label),返回X

#初始化batch_size
batch_size = 128

#实例化训练和验证的数据集
train_set = ImgDataset(train_x,train_y,train_transform)
val_set = ImgDataset(val_x,val_y,test_transform)
#载入训练和验证数据集
train_loader = DataLoader(train_set,batch_size = batch_size,shuffle = True)
val_loader = DataLoader(val_set,batch_size=batch_size,shuffle = False)

文件读写和处理阶段结束。

3、建网络模型

#建网络模型
class Classifier(nn.Module):
    def __init__(self):
        super(Classifier,self).__init__()

        #建立卷积网络层
        self.cnn = nn.Sequential(
            nn.Conv2d(3,64,3,1,1),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.MaxPool2d(2,2,0),

            nn.Conv2d(64,128,3,1,1),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.MaxPool2d(2,2,0),

            nn.Conv2d(128,256,3,1,1),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.MaxPool2d(2,2,0),

            nn.Conv2d(256,512,3,1,1),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2,2,0),

            nn.Conv2d(512,512,3,1,1),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2,2,0),
        )

        #线性全连接网络
        self.fc = nn.Sequential(
            nn.Linear(512*4*4,1024),
            nn.ReLU(),
            nn.Linear(1024,512),
            nn.ReLU(),
            nn.Linear(512,11)
        )
    #前馈,经过cnn-->view-->fc
    def forward(self,x):
        out = self.cnn(x)
        out = out.view(out.size()[0],-1)
        return self.fc(out)

4、训练
很常规的训练,代码有注释

#训练过程
model = Classifier().cuda() #实例化,并使用显卡
loss = nn.CrossEntropyLoss() #损失函数
optimizer = torch.optim.Adam(model.parameters(),lr=0.001)#优化函数
num_epoch = 30 #训练次数

#开始训练
for epoch in range(num_epoth):
    #初始化一些数值
    epoch_start_time = time.time()
    train_acc = 0.0
    train_loss = 0.0
    val_acc = 0.0
    val_loss = 0.0

    model.train()#启用 BatchNormalization 和 Dropout
    for i,data in enumerate(train_loader):
        optimizer.zero_grad() #梯度归零
        train_pred = model(data[0].cuda()) #前馈
        batch_loss = loss(train_pred,data[1].cuda()) #损失计算
        batch_loss.backward() #反馈
        optimizer.step() #优化

        train_acc += np.sum(np.argmax(train_pred.cpu().data.numpy(),axis=1)==data[1].numpy())#计算准确率,得到的数和label是否相等
        train_loss += batch_loss.item() #损失累加

    #在验证集上测试准确率
    model.eval()#不启用 BatchNormalization 和 Dropout
    with torch.no_grad():#不求导,节省内存
        for i,data in enumerate(val_loader):
            val_pred = model(data[0].cuda()) #前馈
            batch_loss = loss(val_pred,data[1].cuda()) #计算损失

            val_acc += np.sum(np.argmax(val_pred.cpu().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__()))

5、正常的过程是不断的调整优化Classifier的网络结构,得到最好的网络结构参数(用什么层,用多少个卷积层等)后,用训练和验证集合并,在得到的最好的网络结构上来训练,得到好的网络参数(这个参数不是结构参数,而是网络的内部参数)。

代码大部分和第4部分相同,注释有的就省略了。

train_val_x = np.concatenate((train_x,val_x),axis=0) #合并训练集和验证集的X
train_val_y = np.concatenate((train_y,val_y),axis=0) #合并训练集和验证集的Y
train_val_set = ImgDataset(train_val_x,train_val_y,train_transform) #实例化
train_val_loader = DataLoader(train_val_set,batch_size=batch_size,shuffle=True) #载入数据

model_best = Classifier().cuda() #此处的Classifier应该是你自己调整后,网络结构最好的网络
loss = nn.CrossEntropyLoss() #损失函数
optimizer = torch.optim.Adam(model_best.parameters(),lr=0.001) #优化函数
num_epoch = 30 #训练次数

#开始训练
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].cuda())
        batch_loss = loss(train_pred,data[1].cuda())
        batch_loss.backward()
        optimizer.step()

        train_acc += np.sum(np.argmax(train_pred.cpu().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__()))

再强调一下,这里的model_best是指网络结构参数最好的网络,你可以不断改变第3部分的网络结构,然后找到好的网络,如果不想试着调调参,直接用第3部分的网络,在合并后的数据集上训练也行。

6、测试

#测试
test_set = ImgDataset(test_x,transform=test_transform) #测试集实例化
test_loader = DataLoader(test_set,batch_size=batch_size,shuffle=False) #载入测试集

model_best.eval() #不启用 BatchNormalization 和 Dropout
prediction = [] #新建列表,用来保存测试结果
with torch.no_grad(): #不求导
    for i,data in enumerate(test_loader):
        test_pred = model_best(data.cuda()) #前馈(预测)
        test_label = np.argmax(test_pred.cpu().data.numpy(),axis=1) #即预测的label
        for y in test_label:
            prediction.append(y)

7、保存测试的数据:

with open("predict.csv",'w') as f:
    f.write('Id,Categroy\n')
    for i,y in enumerate(prediction):
        f.write('{},{}\n'.format(i,y))

所有的代码分析完了,用1080Ti跑完整个过程,10min内,准确率91%左右。
下面的整体的代码(我将在训练集上训练,验证集上看准确率的代码注释了,直接用训练集+验证集来训练Classifier,需要的自行更改)。

#导入需要用到的库
import torch
import torch.nn as nn
import numpy as np
import cv2
import os
import pandas as pd
import torchvision.transforms as transforms
from torch.utils.data import DataLoader,Dataset
import time

#定义读写文件的函数
def readfile(path,own_label):#后面的参数用来区分训练集和测试集
    image_dir = sorted(os.listdir(path))
    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 own_label:
            y[i] = int(file.split("_")[0])
    if own_label:
        return x,y
    else:
        return x

#分别读取训练、验证和测试的数据,保存到相应的变量里
workspace_dir = './food-11'  #数据保存的路径
print("Reading data") #提示读取数据
train_x, train_y = readfile(os.path.join(workspace_dir,"training"),True)#读取训练集数据
print("Size of training data = {}".format(len(train_x))) #输出训练集数据的长度
val_x ,val_y = readfile(os.path.join(workspace_dir,"validation"),True) #读取验证集数据
print("Size of validation data = {}".format(len(val_x))) #输出验证集数据的长度
test_x = readfile(os.path.join(workspace_dir,"testing"),False) #读取测试集数据,没有label
print("Size of Testing data = {}".format(len(test_x))) #输出测试集数据长度

#图像增强
train_transform = transforms.Compose([
    transforms.ToPILImage(),
    transforms.RandomHorizontalFlip(),#随机将图片水平翻转
    transforms.RandomRotation(15),#随机旋转图片
    transforms.ToTensor(),#将图片转化为Tensor,并归一化
])

#测试集图像处理
test_transform = transforms.Compose([
    transforms.ToPILImage(),
    transforms.ToTensor(),
])

#数据集类处理
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) #有,则取变换后的X
        if self.y is not None: #判断是否有y(label)
            Y = self.y[index]
            return X,Y #有,则返回X,Y
        else:
            return X #无y(label),返回X

#初始化batch_size
batch_size = 128

#实例化数据集
train_set = ImgDataset(train_x,train_y,train_transform)
val_set = ImgDataset(val_x,val_y,test_transform)
#载入数据集
train_loader = DataLoader(train_set,batch_size = batch_size,shuffle = True)
val_loader = DataLoader(val_set,batch_size=batch_size,shuffle = False)

#建网络模型
class Classifier(nn.Module):
    def __init__(self):
        super(Classifier,self).__init__()

        #建立卷积网络层
        self.cnn = nn.Sequential(
            nn.Conv2d(3,64,3,1,1),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.MaxPool2d(2,2,0),

            nn.Conv2d(64,128,3,1,1),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.MaxPool2d(2,2,0),

            nn.Conv2d(128,256,3,1,1),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.MaxPool2d(2,2,0),

            nn.Conv2d(256,512,3,1,1),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2,2,0),

            nn.Conv2d(512,512,3,1,1),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2,2,0),
        )

        #线性全连接网络
        self.fc = nn.Sequential(
            nn.Linear(512*4*4,1024),
            nn.ReLU(),
            nn.Linear(1024,512),
            nn.ReLU(),
            nn.Linear(512,11)
        )
    #前馈,经过cnn-->view-->fc
    def forward(self,x):
        out = self.cnn(x)
        out = out.view(out.size()[0],-1)
        return self.fc(out)

##训练过程
#model = Classifier().cuda() #实例化,并使用显卡
#loss = nn.CrossEntropyLoss() #loss
#optimizer = torch.optim.Adam(model.parameters(),lr=0.001)#优化
#num_epoch = 30 #训练次数
#
##开始训练
#for epoch in range(num_epoth):
#    #初始化一些数值
#    epoch_start_time = time.time()
#    train_acc = 0.0
#    train_loss = 0.0
#    val_acc = 0.0
#    val_loss = 0.0
#
#    model.train()#启用 BatchNormalization 和 Dropout
#    for i,data in enumerate(train_loader):
#        optimizer.zero_grad() #梯度归零
#        train_pred = model(data[0].cuda()) #前馈
#        batch_loss = loss(train_pred,data[1].cuda()) #损失计算
#        batch_loss.backward() #反馈
#        optimizer.step() #优化
#
#        train_acc += np.sum(np.argmax(train_pred.cpu().data.numpy(),axis=1)==data[1].numpy())#计算准确率,得到的数和label是否相等
#        train_loss += batch_loss.item() #损失累加
#
#    #在验证集上测试准确率
#    model.eval()#不启用 BatchNormalization 和 Dropout
#    with torch.no_grad():#不求导,节省内存
#        for i,data in enumerate(val_loader):
#            val_pred = model(data[0].cuda()) #前馈
#            batch_loss = loss(val_pred,data[1].cuda()) #计算损失
#
#            val_acc += np.sum(np.argmax(val_pred.cpu().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) #合并训练集和验证集的X
train_val_y = np.concatenate((train_y,val_y),axis=0) #合并训练集和验证集的Y
train_val_set = ImgDataset(train_val_x,train_val_y,train_transform) #实例化
train_val_loader = DataLoader(train_val_set,batch_size=batch_size,shuffle=True) #载入数据

model_best = Classifier().cuda() #此处的Classifier应该是你自己调整后,网络结构最好的网络
loss = nn.CrossEntropyLoss() #损失函数
optimizer = torch.optim.Adam(model_best.parameters(),lr=0.001) #优化函数
num_epoch = 30 #训练次数

#开始训练
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].cuda())
        batch_loss = loss(train_pred,data[1].cuda())
        batch_loss.backward()
        optimizer.step()

        train_acc += np.sum(np.argmax(train_pred.cpu().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=test_transform) #测试集实例化
test_loader = DataLoader(test_set,batch_size=batch_size,shuffle=False) #载入测试集

model_best.eval() #不启用 BatchNormalization 和 Dropout
prediction = [] #新建列表,用来保存测试结果
with torch.no_grad(): #不求导
    for i,data in enumerate(test_loader):
        test_pred = model_best(data.cuda()) #前馈(预测)
        test_label = np.argmax(test_pred.cpu().data.numpy(),axis=1) #即预测的label
        for y in test_label:
            prediction.append(y)

with open("predict.csv",'w') as f:
    f.write('Id,Categroy\n')
    for i,y in enumerate(prediction):
        f.write('{},{}\n'.format(i,y))
  • 2
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值