李宏毅机器学习特训营机器学习作业3-食物图片分类

2024-01-02 12:10

本文主要是介绍李宏毅机器学习特训营机器学习作业3-食物图片分类,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 项目描述
    • 数据集介绍
    • 项目要求
  • 数据增强
  • 读取数据
    • 获取训练集的信息,包括读取路径和对应的标签
    • 自定义数据读取器
  • 定义模型(VGG)
    • 修改vgg11
  • 训练
    • 再训练所有数据
  • 残差网络
  • 预测
    • 用训练好的vgg模型进行推理

项目描述

作业基于paddle2.0

训练一个简单的卷积神经网络,实现食物图片的分类。

数据集介绍

本次使用的数据集为food-11数据集,共有11类 标签为0-10

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张

数据格式
下载 zip 档后解压缩会有三个资料夹,分别为training、validation 以及 testing
training 以及 validation 中的照片名称格式为 [类别]_[编号].jpg,例如 3_100.jpg 即为类别 3 的照片(编号不重要)

项目要求

  • 请使用 CNN 搭建 model
  • 不能使用额外 dataset
  • 禁止使用 pre-trained model(只能自己手写CNN)
  • 请不要上网寻找 label
#导入相关库
import os
import random
import re
import matplotlib.pyplot as plt
import PIL.Image as Imageimport paddle
from paddle.vision.transforms import Compose, ColorJitter, Resize,Transpose,Normalize,RandomHorizontalFlip,RandomVerticalFlip,RandomRotation,RandomCrop
import numpy as np
from paddle.io import Dataset
import paddle.vision.transforms
#看一下类别 0 的事物图片
path1='work/food-11/training/0_106.jpg'
path2='work/food-11/training/0_170.jpg'img1 = Image.open(path1)
print(img1.size)
plt.imshow(img1)          #根据数组绘制图像
plt.show()查看数据img2 = Image.open(path2)
print(img2.size)
plt.imshow(img2)          #根据数组绘制图像
plt.show()               #显示图像

在这里插入图片描述

#查看当前文件夹中有多张训练图片(包括数据增强后的图片)
fPath = 'work/food-11/training'
trainfood_list = os.listdir(fPath)
print(trainfood_list[0:2])
print(len(trainfood_list))['4_315.jpg', '0_448.jpg']
15769
fPath = 'work/food-11/training'trainfood_list = os.listdir(fPath)
for i in trainfood_list:if '.jpg.jpg' in i:os.remove(fPath+'/'+i)   # 先删除.jpg.jpg图片
trainfood_list = os.listdir(fPath)
print(trainfood_list[0:2])
print(len(trainfood_list))['4_767.jpg', '2_1205.jpg']
9866

数据增强

数据增强需要合理,不然会出现下面增强不合理的情况。。

在这里插入图片描述

#数据增强函数
def dataAugmentation(path_list):for each in path_list:if(random.random()>0.4): #每张图片有0.4的概率被处理img = Image.open(fPath+'/'+each)transform = Compose([ColorJitter(0.5,0.07,0.2,0.05), #调整图像的亮度,对比度,饱和度和色调。RandomHorizontalFlip(0.4)])img = transform(img)if(random.random()>0.5):transform1 =RandomRotation(30)img = transform1(img)   savePath =  fPath+'/'+each+'.jpg'img.save(savePath) #例如:work/food-11/training/0_106.jpg.jpg 多一个.jpg用于区分原图,方便本地查看
#先删掉以前已经存在的经过增强后的图片
trainfood_list = os.listdir(fPath)
for i in trainfood_list:if '.jpg.jpg' in i:os.remove(fPath+'/'+i)   # 先删除.jpg.jpg图片
trainfood_list = os.listdir(fPath)
print(len(trainfood_list))
dataAugmentation(trainfood_list)
trainfood_list = os.listdir(fPath)
print(len(trainfood_list))  #查看经过数据增强后,总共有多少张训练图片
li = []
for i in trainfood_list:if '.jpg.jpg' in i:li.append(i)
print(li[0:2])

一下经过处理后的图片

#看一下经过处理后的图片
path1='work/food-11/training/4_186.jpg'
path2='work/food-11/training/4_186.jpg.jpg'
path3='work/food-11/training/5_306.jpg'
path4='work/food-11/training/5_306.jpg.jpg'img1 = Image.open(path1)
plt.imshow(img1)          #根据数组绘制图像
plt.show()
img2 = Image.open(path2)
plt.imshow(img2)          #根据数组绘制图像
plt.show()               #显示图像
img3 = Image.open(path3)
plt.imshow(img3)          
plt.show()               
img4 = Image.open(path4)
plt.imshow(img4)          
plt.show() 

在这里插入图片描述

读取数据

获取训练集的信息,包括读取路径和对应的标签

data_list = [] #用个列表保存每个训练样本的读取路径以及标签
for each in trainfood_list:data_list.append([fPath+'/'+each,int(re.findall(r"(\d+)_",each)[0])]) #利用正则表达式获取 _ 前的图片标签
print(len(data_list))
print(data_list[0:3])#按文件顺序读取,可能造成很多属种图片存在序列相关,用random.shuffle方法把样本顺序彻底打乱。
random.shuffle(data_list)
print(len(data_list))
print(data_list[0:3]) #查看打乱顺序后的信息

打印如下:
在这里插入图片描述

data_vali_list = [] #用个列表保存每个测试样本的读取路径以及标签
vali_fPath = 'work/food-11/validation'
valifood_list = os.listdir(vali_fPath)
for each in valifood_list:data_vali_list.append([vali_fPath+'/'+each,int(re.findall(r"(\d+)_",each)[0])]) #利用正则表达式获取 _ 前的图片标签
print(len(data_vali_list))
print(data_vali_list[0:3])3430
[['work/food-11/validation/4_315.jpg', 4], ['work/food-11/validation/2_56.jpg', 2], ['work/food-11/validation/0_87.jpg', 0]]

自定义数据读取器

#图像预处理函数
def preprocess(img):img = img.resize((224, 224), Image.ANTIALIAS)img = np.array(img).astype("float32")img = img.transpose((2, 0, 1))img = img / 255.0           # 将图像数据归一化return img#自定义数据读取器
class Reader(Dataset):def __init__(self, datalist):super().__init__()self.samples = datalistdef __getitem__(self, idx):#处理图像img_path = self.samples[idx][0] #得到某样本的路径img = Image.open(img_path)if img.mode != 'RGB':img = img.convert('RGB')img = preprocess(img)#图片预处理#处理标签label = self.samples[idx][1] #得到某样本的标签label = np.array([label], dtype="int64") #把标签数据类型转成int64return img, labeldef __len__(self):#返回每个Epoch中图片数量return len(self.samples)#生成训练数据集实例
train_dataset = Reader(data_list)
#打印一个训练样本
print(train_dataset[1136][0].shape)
print(train_dataset[1136][1])#生成测试数据集实例
vali_dataset = Reader(data_vali_list)
#打印一个训练样本
print(vali_dataset[1136][0].shape)
print(vali_dataset[1136][1])(3, 224, 224)
[8]
(3, 224, 224)
[3]

定义模型(VGG)

修改vgg11

最初用vgg16的结构训练,发现验证集只能达到55%的正确率。于是模型结构修改为vgg11,然后把最后的MaxPool2D层换成AdaptiveAvgPool2D,把连续的三个全连接层换成一个全连接层。

import paddle.nn.functional as F
#定义模型
class My_vgg(paddle.nn.Layer):def __init__(self, num_classes):super().__init__()self.num_classes = num_classesnet = []# block 1net.append(paddle.nn.Conv2D(3, 64, (3, 3),padding=1,  stride=1))net.append(paddle.nn.ReLU())# net.append(paddle.nn.Conv2D(64, 64, (3, 3),padding=1,  stride=1))# net.append(paddle.nn.ReLU())net.append(paddle.nn.MaxPool2D(kernel_size=2, stride=2))# block 2net.append(paddle.nn.Conv2D(64, 128, (3, 3), stride=1, padding=1))net.append(paddle.nn.ReLU())# net.append(paddle.nn.Conv2D(128, 128, (3, 3), stride=1, padding=1))# net.append(paddle.nn.ReLU())net.append(paddle.nn.MaxPool2D(kernel_size=2, stride=2))# block 3net.append(paddle.nn.Conv2D(128, 256, (3, 3), stride=1,padding=1))net.append(paddle.nn.ReLU())net.append(paddle.nn.Conv2D(256, 256, (3, 3),  stride=1,padding=1))net.append(paddle.nn.ReLU())# net.append(paddle.nn.Conv2D(256, 256, (3, 3),  stride=1,padding=1))# net.append(paddle.nn.ReLU())net.append(paddle.nn.MaxPool2D(kernel_size=2, stride=2))# block 4net.append(paddle.nn.Conv2D(256, 512, (3, 3), stride=1, padding=1))net.append(paddle.nn.ReLU())net.append(paddle.nn.Conv2D(512,  512,  (3, 3), stride=1, padding=1))net.append(paddle.nn.ReLU())# net.append(paddle.nn.Conv2D( 512,  512,  (3, 3), stride=1, padding=1))# net.append(paddle.nn.ReLU())net.append(paddle.nn.MaxPool2D(kernel_size=2, stride=2))# block 5net.append(paddle.nn.Conv2D( 512,  512,  (3, 3), stride=1, padding=1))net.append(paddle.nn.ReLU())net.append(paddle.nn.Conv2D( 512,  512,  (3, 3), stride=1, padding=1))net.append(paddle.nn.ReLU())# net.append(paddle.nn.Conv2D( 512,  512,  (3, 3), stride=1, padding=1))# net.append(paddle.nn.ReLU())#net.append(paddle.nn.MaxPool2D(kernel_size=2, stride=2))net.append(paddle.nn.AdaptiveAvgPool2D(output_size=1))# 组网#顺序容器。子Layer将按构造函数参数的顺序添加到此容器中。self.conv = paddle.nn.Sequential(*net)classifier = []# classifier.append(paddle.nn.Linear( 512*7*7,  4096))# classifier.append(paddle.nn.ReLU())# classifier.append(paddle.nn.Dropout(p=0.5))# classifier.append(paddle.nn.Linear( 4096,  4096))# classifier.append(paddle.nn.ReLU())# classifier.append(paddle.nn.Dropout(p=0.5))# classifier.append(paddle.nn.Linear( 4096,  self.num_classes))classifier.append(paddle.nn.Dropout(p=0.5))classifier.append(paddle.nn.Linear( 512,  self.num_classes))# 组网#顺序容器。子Layer将按构造函数参数的顺序添加到此容器中。self.classifier = paddle.nn.Sequential(*classifier)def forward(self, x):features = self.conv(x)features = paddle.reshape(features, [features.shape[0], -1])classify_result = self.classifier(features)return classify_result
network = My_vgg(11)  # 模型实例化
paddle.summary(network, (2,3,224,224)) 

在这里插入图片描述

#定义输入
input_define = paddle.static.InputSpec(shape=[-1,3,224,224], dtype="float32", name="img")
label_define = paddle.static.InputSpec(shape=[-1,1], dtype="int64", name="label")

训练

训练集的准确率上升到了71%左右

#实例化网络对象并定义优化器等训练逻辑
model = My_vgg(num_classes=11)
model = paddle.Model(model,inputs=input_define,labels=label_define) #用Paddle.Model()对模型进行封装
optimizer = paddle.optimizer.AdamW(learning_rate=0.0001, parameters=model.parameters(),weight_decay=0.01)model.prepare(optimizer=optimizer, #指定优化器loss=paddle.nn.CrossEntropyLoss(), #指定损失函数metrics=paddle.metric.Accuracy()) #指定评估方法model.fit(train_data=train_dataset,     #训练数据集eval_data=vali_dataset,         #测试数据集batch_size=128,                  #一个批次的样本数量epochs=50,                      #迭代轮次save_dir="/home/aistudio/pp", #把模型参数、优化器参数保存至自定义的文件夹save_freq=20,                    #设定每隔多少个epoch保存模型参数及优化器参数#log_freq=100,                     #打印日志的频率verbose=1,                        # 日志展示模式shuffle=True                    # 是否打乱数据集顺序)

在这里插入图片描述

再训练所有数据

利用最后保存的参数,在其基础上使用所有训练数据持续地训练

random.shuffle(data_vali_list)
data_list.extend(data_vali_list)#生成训练数据集实例
train_dataset2 = Reader(data_list)
#打印一个训练样本
print(train_dataset2[1136][0].shape)
print(train_dataset2[1136][1])(3, 224, 224)
[9]

加载模型再训练,只训练10个epoch

model = paddle.Model(My_vgg(num_classes=11),inputs=input_define,labels=label_define)
model.load('pp/final') #加载模型
optimizer = paddle.optimizer.AdamW(learning_rate=0.0001, parameters=model.parameters(),weight_decay=0.01)model.prepare(optimizer=optimizer, #指定优化器loss=paddle.nn.CrossEntropyLoss(), #指定损失函数metrics=paddle.metric.Accuracy()) #指定评估方法model.fit(train_data=train_dataset2,     #训练数据集batch_size=128,                  #一个批次的样本数量epochs=10,                      #迭代轮次save_dir="/home/aistudio/pp", #把模型参数、优化器参数保存至自定义的文件夹save_freq=10,                    #设定每隔多少个epoch保存模型参数及优化器参数#log_freq=100,                     #打印日志的频率verbose=1,                        # 日志展示模式shuffle=True                    # 是否打乱数据集顺序)

在这里插入图片描述

残差网络

采用平均池化和dropout,验证集的准确率只能达到60%

class solid_block(paddle.nn.Layer):#带实线部分的残差块def __init__(self,in_channels):super(solid_block, self).__init__()self.conv1 = paddle.nn.Conv2D(in_channels,in_channels,kernel_size=3,stride=1,padding=1)self.relu1 = paddle.nn.ReLU()self.conv2 = paddle.nn.Conv2D(in_channels,in_channels,kernel_size=3,stride=1,padding=1)self.relu2 = paddle.nn.ReLU()self.relu3 = paddle.nn.ReLU()def forward(self, x):y = self.conv1(x)y = self.relu1(x)y = self.conv2(y)y = self.relu2(y)return self.relu3(x+y)class dash_block(paddle.nn.Layer):#带虚线部分的残差块def __init__(self,in_channels,out_channels):super(dash_block, self).__init__()self.conv1 = paddle.nn.Conv2D(in_channels,out_channels,kernel_size=1,stride=2)self.conv2 = paddle.nn.Conv2D(in_channels,out_channels,kernel_size=3,stride=2,padding=1)self.relu1 = paddle.nn.ReLU()self.conv3 = paddle.nn.Conv2D(out_channels,out_channels,kernel_size=3,stride=1,padding=1)self.relu2 = paddle.nn.ReLU()def forward(self, x):z = self.conv1(x)y = self.conv2(x)y = self.relu1(y)y = self.conv3(y)return self.relu2(y+z)class my_resnst(paddle.nn.Layer):def __init__(self,num_classes=11):super(my_resnst, self).__init__()self.conv1 = paddle.nn.Conv2D(3,64,kernel_size=7, stride=2, padding=3)self.maxp1 = paddle.nn.MaxPool2D(kernel_size=3, stride=2, padding=1)self.resnet1 = solid_block(64)self.resnet2 = solid_block(64)self.resnet3 = dash_block(64,128)self.resnet4 = solid_block(128)self.resnet5 = dash_block(128,256)self.resnet6 = solid_block(256)self.resnet7 = dash_block(256,512)self.resnet8 = solid_block(512)self.relu = paddle.nn.ReLU()self.avgp1 = paddle.nn.AvgPool2D(7) #平均池化self.dropout = paddle.nn.Dropout(p=0.5)self.fullc = paddle.nn.Linear(512,num_classes)def forward(self,x) :x = self.conv1(x)x = self.maxp1(x)x = self.resnet1(x)x = self.resnet2(x)x = self.resnet3(x)x = self.resnet4(x)x = self.resnet5(x)x = self.resnet6(x)x = self.resnet7(x)x = self.resnet8(x)x = self.relu(x)x = self.avgp1(x)x = paddle.reshape(x, [x.shape[0], -1])x = self.dropout(x)x = self.fullc(x)return x
network = my_resnst()  # 模型实例化
paddle.summary(network, (1,3,224,224)) 

在这里插入图片描述

#实例化网络对象并定义优化器等训练逻辑
model = my_resnst()
model = paddle.Model(model,inputs=input_define,labels=label_define) #用Paddle.Model()对模型进行封装
optimizer = paddle.optimizer.Adam(learning_rate=0.000125, parameters=model.parameters())model.prepare(optimizer=optimizer, #指定优化器loss=paddle.nn.CrossEntropyLoss(), #指定损失函数metrics=paddle.metric.Accuracy()) #指定评估方法model.fit(train_data=train_dataset,     #训练数据集eval_data=vali_dataset,         #测试数据集batch_size=128,                  #一个批次的样本数量epochs=50,                      #迭代轮次save_dir="/home/aistudio/respp", #把模型参数、优化器参数保存至自定义的文件夹save_freq=50,                    #设定每隔多少个epoch保存模型参数及优化器参数#log_freq=100,                     #打印日志的频率verbose=1,                        # 日志展示模式shuffle=True                    # 是否打乱数据集顺序)

在这里插入图片描述

预测

定义测试数据读取器

用训练好的vgg模型进行推理

class TestDataset(Dataset):def __init__(self, img_path=None):super().__init__()if img_path:self.img_paths = [img_path]else:raise Exception("请指定需要预测对应图片路径")def __getitem__(self, index):# 获取图像路径img_path = self.img_paths[index]img = Image.open(img_path)if img.mode != 'RGB': img = img.convert('RGB') img = preprocess(img) # 数据预处理--这里仅包括简单数据预处理,没有用到数据增强return imgdef __len__(self):return len(self.img_paths)model = paddle.Model(My_vgg(num_classes=11),inputs=input_define)#读取刚刚训练好的参数
model.load('pp/final')#准备模型
model.prepare()#得到待预测数据集中每个图像的读取路径
test_list=[]
test_fPath = 'work/food-11/testing'
testfood_list = os.listdir(test_fPath)
for each in testfood_list:test_list.append(test_fPath+'/'+each) 
print(len(test_list))
print(test_list[0:3])3347
['work/food-11/testing/2760.jpg', 'work/food-11/testing/3215.jpg', 'work/food-11/testing/3011.jpg']
#查看一张测试图片
p='work/food-11/testing/3346.jpg'
img = Image.open(p)
plt.imshow(img)          #根据数组绘制图像
plt.show()

在这里插入图片描述
测试一下模型,查看模型对上面图片的预测结果

testpre = TestDataset(p)
result = model.predict(test_data=testpre)[0] #预测
result = paddle.to_tensor(result)
result = np.argmax(result.numpy()) #获得最大值所在的序号
print("类别:"+str(result))

在这里插入图片描述
预测正确,图片是属于汤类

对整个测试集进行预测

保存时只保存图片的文件名字。

#利用训练好的模型进行预测
results=[]
for each in test_list:test2pre = TestDataset(each)result = model.predict(test_data=test2pre)[0] #预测result = paddle.to_tensor(result)result = np.argmax(result.numpy()) #获得最大值所在的序号results.append([os.path.basename(each),result]) #保存该序号所对应的标签 如:[3364.jpg,9]
#把结果保存起来
with open("result.txt", "w") as f:for r in results:f.write("{}\n".format(r))

这篇关于李宏毅机器学习特训营机器学习作业3-食物图片分类的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/562403

相关文章

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

详解C#如何提取PDF文档中的图片

《详解C#如何提取PDF文档中的图片》提取图片可以将这些图像资源进行单独保存,方便后续在不同的项目中使用,下面我们就来看看如何使用C#通过代码从PDF文档中提取图片吧... 当 PDF 文件中包含有价值的图片,如艺术画作、设计素材、报告图表等,提取图片可以将这些图像资源进行单独保存,方便后续在不同的项目中使

Java实现文件图片的预览和下载功能

《Java实现文件图片的预览和下载功能》这篇文章主要为大家详细介绍了如何使用Java实现文件图片的预览和下载功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... Java实现文件(图片)的预览和下载 @ApiOperation("访问文件") @GetMapping("

基于Python开发批量提取Excel图片的小工具

《基于Python开发批量提取Excel图片的小工具》这篇文章主要为大家详细介绍了如何使用Python中的openpyxl库开发一个小工具,可以实现批量提取Excel图片,有需要的小伙伴可以参考一下... 目前有一个需求,就是批量读取当前目录下所有文件夹里的Excel文件,去获取出Excel文件中的图片,并

Java实现数据库图片上传与存储功能

《Java实现数据库图片上传与存储功能》在现代的Web开发中,上传图片并将其存储在数据库中是常见的需求之一,本文将介绍如何通过Java实现图片上传,存储到数据库的完整过程,希望对大家有所帮助... 目录1. 项目结构2. 数据库表设计3. 实现图片上传功能3.1 文件上传控制器3.2 图片上传服务4. 实现

Java进阶学习之如何开启远程调式

《Java进阶学习之如何开启远程调式》Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,:本文主要介绍Java进阶学习之如何开启远程调式的相关资料,需要的朋友... 目录概述Java远程调试的开启与底层原理开启Java远程调试底层原理JVM参数总结&nbsMbKKXJx

Java实现数据库图片上传功能详解

《Java实现数据库图片上传功能详解》这篇文章主要为大家详细介绍了如何使用Java实现数据库图片上传功能,包含从数据库拿图片传递前端渲染,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、前言2、数据库搭建&nbsChina编程p; 3、后端实现将图片存储进数据库4、后端实现从数据库取出图片给前端5、前端拿到

Python使用PIL库将PNG图片转换为ICO图标的示例代码

《Python使用PIL库将PNG图片转换为ICO图标的示例代码》在软件开发和网站设计中,ICO图标是一种常用的图像格式,特别适用于应用程序图标、网页收藏夹图标等场景,本文将介绍如何使用Python的... 目录引言准备工作代码解析实践操作结果展示结语引言在软件开发和网站设计中,ICO图标是一种常用的图像

SpringBoot集成图片验证码框架easy-captcha的详细过程

《SpringBoot集成图片验证码框架easy-captcha的详细过程》本文介绍了如何将Easy-Captcha框架集成到SpringBoot项目中,实现图片验证码功能,Easy-Captcha是... 目录SpringBoot集成图片验证码框架easy-captcha一、引言二、依赖三、代码1. Ea

如何使用CSS3实现波浪式图片墙

《如何使用CSS3实现波浪式图片墙》:本文主要介绍了如何使用CSS3的transform属性和动画技巧实现波浪式图片墙,通过设置图片的垂直偏移量,并使用动画使其周期性地改变位置,可以创建出动态且具有波浪效果的图片墙,同时,还强调了响应式设计的重要性,以确保图片墙在不同设备上都能良好显示,详细内容请阅读本文,希望能对你有所帮助...