目录

  • 1、比赛介绍:
  • 2、数据划分:
  • 3、图像增广:
  • 4、读取数据:
  • 5、模型构造:
  • 6、计算损失:
  • 7、模型训练:
  • 8、模型推理:
  • 9、上传预测结果到kaggle:

本文内容来源于《动手深度学习》一书。跟着沐神做kaggle比赛。

注:以下代码都在jupyter中完成。

1、比赛介绍:

该任务是:给出树叶的图片,将给出的树叶分成176类。

数据如下图所示,通过下面的网址,下载训练数据和测试数据:

比赛地址及数据下载地址:https://www.kaggle.com/c/classify-leaves/data

2、数据划分:

在开始之前,我们先看看数据集长什么样子:


其中.csv中存放的是图片的地址和对应的标签。

首先我们读取一下.csv中的数据,看看长什么样子:

# 下面两行是防止内核挂掉加的
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"import collections
import math
import os
import shutil
import pandas as pd
import torch
import torchvision
from torch import nn
from d2l import torch as d2l
train_data = pd.read_csv(r"../data/classify-leaves/train.csv") # 训练数据
test_data = pd.read_csv(r"../data/classify-leaves/test.csv") # 测试数据
print(train_data.shape)
print(test_data.shape)
train_data # 训练数据有标签

输出:

test_data # 测试数据没有标签

输出:

我们可以观察到,train.csv中存放了训练数据集的图片相对地址和对应的标签,而test.csv中只存放了测试数据集的图片相对地址,标签需要我们预测么,当然没有了。

对数据有了一个直观的理解之后,接下来我们开始读取数据:读取数据集所在地址、并整理数据集。这里要注意,训练数据和测试数据都放在image文件夹下,我们需要通过train.csv和test.csv中的图片地址来将他们划分开。

整理数据集思路很简单:将训练数据集和测试数据集划分开,然后将训练数据集中每一个类建立一个文件夹,并把标签对应的图片复制一份到里面。(这里我们用到了验证数据集,和训练数据集操作是一样的)

下面的几个函数就是对上面的描述进行操作,都在注释中。

def read_csv_labels(fname):"""读取 `fname` 来给标签字典返回一个文件名。"""with open(fname, 'r') as f:lines = f.readlines()[1:]tokens = [l.rstrip().split(',') for l in lines]return dict(((name, label) for name, label in tokens))labels = read_csv_labels(os.path.join(data_dir, 'train.csv')) # 存放训练集标签的文件def copyfile(filename, target_dir):"""将文件复制到目标目录。"""os.makedirs(target_dir, exist_ok=True)shutil.copy(filename, target_dir)def reorg_train_valid(data_dir, labels, valid_ratio):# 下面的collections.Counter就是统计label这个字典中有几个类别(返回字典);.most_common()则转换成元组;[-1][1]则返回最后一个元组的第二个值(因为这个类别数量最小)n = collections.Counter(labels.values()).most_common()[-1][1] # n就是数量最少类别对应的数量n_valid_per_label = max(1, math.floor(n * valid_ratio)) # 根据最小类别数量,得出验证集的数量label_count = {}for train_file in labels: # 返回训练集中的图片名字列表(我们看到,训练标签转换成的字典,key就是训练集的图片名字)label = labels[train_file] # 每张图片 对应的标签fname = os.path.join(data_dir, train_file) # 每个图片的完整路径copyfile(fname,os.path.join(data_dir, 'train_valid_test', 'train_valid', label)) # 将图片复制到指定的目录下,这个是为了交叉验证使用,这里和训练集没区别if label not in label_count or label_count[label] < n_valid_per_label: # 制作验证集。注:标签名作为key,value为每个标签的图片数量copyfile(fname,os.path.join(data_dir, 'train_valid_test', 'valid', label))label_count[label] = label_count.get(label, 0) + 1 # 统计每个标签的图片数量else: # 制作训练集copyfile(fname,os.path.join(data_dir, 'train_valid_test', 'train', label))return n_valid_per_label # 返回验证集的数量# 在预测期间整理测试集,以方便读取
def reorg_test(data_dir):test = pd.read_csv(os.path.join(data_dir, 'test.csv'))for test_file in test['image']: # 获取测试集图片的名字,复制到指定文件夹下copyfile(os.path.join(data_dir, test_file),os.path.join(data_dir, 'train_valid_test', 'test', 'unknown'))# 调用前面定义的函数,进行整理数据集
def reorg_leave_data(data_dir, valid_ratio):labels = read_csv_labels(os.path.join(data_dir, 'train.csv')) # 是个字典reorg_train_valid(data_dir, labels, valid_ratio) # 生成训练集和验证集reorg_test(data_dir) # 生成测试集batch_size = 128
valid_ratio = 0.1 # 验证集的比例
if not os.path.exists(data_dir + "\\" + "train_valid_test"): # 判断是否已经制作好了数据集print("start!")reorg_leave_data(data_dir, valid_ratio)
else:print("Already exists!")
print('finish!')

3、图像增广:

接下来对图像进行变换,也就是图像增广: 这里需要说下,图像增广,这里并没有把每张图片变成多张,保存下来。而是每次读入的时候,随机的变换成一张,然后送入模型。从整个模型运行的角度看,实际上就是将数据集变大了,因为每次送入的图片大概率是不一样的(随机变换的)

transform_train = torchvision.transforms.Compose([torchvision.transforms.RandomResizedCrop(224, scale=(0.08, 1.0), # 从图片的中心点剪裁出24*24的图片ratio=(3.0 / 4.0, 4.0 / 3.0)),torchvision.transforms.RandomHorizontalFlip(), # 左右翻转torchvision.transforms.ColorJitter(brightness=0.4, contrast=0.4,saturation=0.4),# 加入随机噪音torchvision.transforms.ToTensor(),torchvision.transforms.Normalize([0.485, 0.456, 0.406], # 对图片的每个通道做均值和方差[0.229, 0.224, 0.225])])transform_test = torchvision.transforms.Compose([torchvision.transforms.Resize(256),torchvision.transforms.CenterCrop(224),# 加入随机噪音torchvision.transforms.ToTensor(),torchvision.transforms.Normalize([0.485, 0.456, 0.406], # 依然 对图片的每个通道做均值和方差[0.229, 0.224, 0.225])])

4、读取数据:

我们前面把数据集划分成训练集、验证集和测试集,并每一个类建立了一个文件夹。接下来我们使用 torchvision 的ImageFolder方法,将训练集、验证集和测试集读取进来。

# ImageFolder按照文件夹顺序,进行打标签# 训练集和交叉验证集
train_ds, train_valid_ds = [ torchvision.datasets.ImageFolder(os.path.join(data_dir, 'train_valid_test', folder),transform=transform_train) for folder in ['train', 'train_valid']]# 验证集和测试集
valid_ds, test_ds = [torchvision.datasets.ImageFolder(os.path.join(data_dir, 'train_valid_test', folder),transform=transform_test) for folder in ['valid', 'test']]# 把前面的数据放入torch的DataLoader,则每次迭代时,读取一个batch
train_iter, train_valid_iter = [torch.utils.data.DataLoader(dataset, batch_size, shuffle=True,drop_last=True)for dataset in (train_ds, train_valid_ds)]valid_iter = torch.utils.data.DataLoader(valid_ds, batch_size, shuffle=False,drop_last=True)test_iter = torch.utils.data.DataLoader(test_ds, batch_size, shuffle=False,drop_last=False)

到目前为止,数据的处理总算完事了,接下来看看模型是如何构造的。

5、模型构造:

我为了练习微调(迁移学习的一种)的做法,选用了resnet50预训练模型,作为这次比赛的模型。这么做也有点道理,因为resnet系列的预训练模型都是在ImageNet数据集上训练的,而ImageNet数据集,我们都知道100万的图片,分类为1000类,有树叶的分类,因此,可以使用迁移学习的方法做。

也可以不用微调,直接把resnet50重新训练一遍,应该效果会更好吧。

下面看看微调的具体做法:

# 微调
def get_net(devices):finetune_net = nn.Sequential()finetune_net.features = torchvision.models.resnet50(pretrained=True) # 使用了resnet50的预训练模型finetune_net.output_new = nn.Sequential(nn.Linear(1000, 512), nn.ReLU(), # 在后面输出层中,又增加了几层,目的是维度降到类别数nn.Linear(512, 256), nn.ReLU(),nn.Linear(256, 176)) # 树叶分类有176类finetune_net = finetune_net.to(devices[0]) # 将模型送入gpufor param in finetune_net.features.parameters(): # 固定住预训练模型中的参数,只调节我们新加的几个层的参数param.requires_grad = Falsereturn finetune_net

6、计算损失:

# 下面这个是交叉熵损失,我们前面在模型的最后面没有用softmax(),是因为下面这个包含了
loss = nn.CrossEntropyLoss(reduction='none') # reduction='none'表示返回n个样本的lossdef evaluate_loss(data_iter, net, devices): # 这个是在评估模型的时候使用l_sum, n = 0.0, 0for features, labels in data_iter:features, labels = features.to(devices[0]), labels.to(devices[0])outputs = net(features)l = loss(outputs, labels)l_sum += l.sum()n += labels.numel() # numel()函数:返回数组中元素的个数return l_sum / n

7、模型训练:

def train(net, train_iter, valid_iter, num_epochs, lr, wd, devices, lr_period, lr_decay):"""wd:权衰量,用于防止过拟合lr_period:每隔几个epoch降低学习率lr_decay:降低学习率的比例"""net = nn.DataParallel(net, device_ids=devices).to(devices[0]) # 使用多gputrainer = torch.optim.SGD( # 随机梯度下降(param for param in net.parameters() if param.requires_grad), lr=lr,momentum=0.9, weight_decay=wd)scheduler = torch.optim.lr_scheduler.StepLR(trainer, lr_period, lr_decay) # 使用学习率衰减num_batches, timer = len(train_iter), d2l.Timer()legend = ['train loss']if valid_iter is not None:legend.append('valid loss')animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs],legend=legend) # 画图for epoch in range(num_epochs):metric = d2l.Accumulator(2)for i, (features, labels) in enumerate(train_iter):timer.start()features, labels = features.to(devices[0]), labels.to(devices[0]) # 特征和标签都放到gputrainer.zero_grad() # 梯度清零output = net(features) # 得出预测结果l = loss(output, labels).sum()l.backward() # 反向传播,求梯度trainer.step() # 更新参数metric.add(l, labels.shape[0])timer.stop()if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:animator.add(epoch + (i + 1) / num_batches,(metric[0] / metric[1], None))measures = f'train loss {metric[0] / metric[1]:.3f}'if valid_iter is not None: # 计算验证集的lossvalid_loss = evaluate_loss(valid_iter, net, devices)animator.add(epoch + 1, (None, valid_loss.cpu().detach()))scheduler.step() # 一个epoch完了,衰减学习率if valid_iter is not None:measures += f', valid loss {valid_loss:.3f}'print(measures + f'\n{metric[1] * num_epochs / timer.sum():.1f}'f' examples/sec on {str(devices)}')# 调用上面的函数:训练和验证模型
devices, num_epochs, lr, wd = d2l.try_all_gpus(), 10, 1e-4, 1e-4
lr_period, lr_decay, net = 2, 0.9, get_net(devices)
train(net, train_iter, valid_iter, num_epochs, lr, wd, devices, lr_period, lr_decay)

训练结果:

8、模型推理:

对测试集进行分类并提交结果。

devices, num_epochs, lr, wd = d2l.try_all_gpus(), 20, 2e-4, 5e-4
lr_period, lr_decay= 4, 0.9net, preds = get_net(devices), []
train(net, train_valid_iter, None, num_epochs, lr, wd, devices, lr_period, lr_decay)test = pd.read_csv(os.path.join(data_dir, 'test.csv')) # 存放 测试集图片地址 的文件
for X, _ in test_iter:y_hat = net(X.to(devices[0]))preds.extend(y_hat.argmax(dim=1).type(torch.int32).cpu().numpy())
sorted_ids = test['image'] # 对应的id
df = pd.DataFrame({'image': sorted_ids, 'label': preds}) # 转换成pandas的DF格式# .apply()函数:遍历DataFrame的元素(一行数据或者一列数据),默认列遍历
# ImageFolder返回对象的.classes属性:用一个 list 保存类别名称
# 这个的作用是:模型预测出来是概率最大的那个数的下标,在保存文件时,需要把数字类别转换为字符串类别,
# train_valid_ds.classes就是获取字符串类别名(返回的是一个列表),然后使用apply一行一行读取出来,把数字类别转换为字符串类别
df['label'] = df['label'].apply(lambda x: train_valid_ds.classes[x])
df.to_csv(r'..\data\classify-leaves\submission.csv', index=False)

结果:

9、上传预测结果到kaggle:


下面是我的得分,分不高,以学习为主。

kaggle比赛:Classify Leaves(使用resnet50预训练模型进行:图片树叶分类)相关推荐

  1. 玩转Kaggle:Classify Leaves(叶子分类)——模型设计与训练

    文章目录 一.数据加载 二.模型构建与训练 1. resnet 1.1 ResNet-50模型微调+冻结 1.2 五折交叉验证 1.3 模型预测 1.4 利用K-Fold结果投票获取最终提交数据 1. ...

  2. 玩转Kaggle:Classify Leaves(叶子分类)——数据分析篇

    文章目录 一.Classify Leaves竞赛介绍 二.数据分析 2.1 训练数据信息统计和查看 2.2 测试数据统计和分析 2.3 可视化训练数据 三.整理 一.Classify Leaves竞赛 ...

  3. 使用预训练模型进行句对分类(Paddle、PyTorch)

    文章目录 1. Paddle 2. PyTorch 3. 提交结果 分别使用两种框架,加载预训练模型,对句对进行分类 数据下载:千言数据集:文本相似度 1. Paddle 可以使用 paddlenlp ...

  4. 使用Bert预训练模型进行中文文本分类(基于pytorch)

    前言 最近在做一个关于图书系统的项目,需要先对图书进行分类,想到Bert模型是有中文文本分类功能的,于是打算使用Bert模型进行预训练和实现下游文本分类任务 数据预处理 2.1 输入介绍 在选择数据集 ...

  5. Pytorch深度学习基础 实战天气图片识别(基于ResNet50预训练模型,超详细)

    文章目录 一.概述 二.代码编写 1. 数据处理 2. 准备配置文件 3. 自定义DataSet和DataLoader 4. 构建模型 5. 训练模型 6. 编写预测模块 三.效果展示 四.源码地址 ...

  6. Pytorch-使用Bert预训练模型微调中文文本分类

    语料链接:https://pan.baidu.com/s/1YxGGYmeByuAlRdAVov_ZLg 提取码:tzao neg.txt和pos.txt各5000条酒店评论,每条评论一行. 安装tr ...

  7. Keras预训练模型下载及迁移学习

    Keras 预训练模型简介 在 Keras 中,包含有一个辅助应用模块 keras.applications,其提供了带有预训练权重的 Keras 模型.你可以直接使用这些模型,或者像本文一样对模型进 ...

  8. Tensorflow——使用预训练模型进行 猫狗 图像分类

    前言 预训练模型顾名思义就是使用别人已经训练好的模型参数放到自己的任务里面进行特定任务的微调.这里的模型参数包括:神经网络的结构.神经网络的权值参数. 本博客将尝试使用预训练模型进行猫狗分类. 代码地 ...

  9. Paddle预训练模型应用工具PaddleHub

    Paddle预训练模型应用工具PaddleHub • 本文主要介绍如何使用飞桨预训练模型管理工具PaddleHub,快速体验模型以及实现迁移学习.建议使用GPU环境运行相关程序,可以在启动环境时,如下 ...

最新文章

  1. Hadoop学习笔记—7.计数器与自定义计数器
  2. 使用Azure SDK 1.4.1中的Web Deploy
  3. SAP Fiori应用中事务锁的实现 - Transaction Lock实现机制
  4. 文档词频矩阵_论文理解:从词嵌入到文档距离
  5. redis基本类型和使用
  6. opencv android
  7. 训练集山准确率高测试集上准确率很低_拒绝DNN过拟合,谷歌准确预测训练集与测试集泛化差异,还开源了数据集 | ICLR 2019...
  8. 基于JAVA+SpringMVC+MYSQL的记账管理系统
  9. vue 第四天 (计算属性的使用)
  10. 利用whistle调试移动端页面
  11. 矩阵计算器+求线性代数n阶行列式代码
  12. stvd c语言编译器,STM8--STVD编译工具安装与程序下载
  13. 在WebServer中如何实现CGI技术
  14. linux密码weak,linux中__weak关键字
  15. Mac OS high Sierra 10.13.6 安装cuda
  16. 2022爱分析·人工智能厂商全景报告
  17. postman接口自动化(三)变量设置与使用
  18. php表格链接地址,php代码链接数据库并以表格形式输出数据库中的数据功能
  19. 计算机无法保存其他共享用户,Win7共享打印机出现 无法保存打印机设置 操作无法完成 错误0x000006d9解决方法...
  20. 全国主要城市经纬度表

热门文章

  1. Jboot框架excel导入导出模板下载的简单封装
  2. MSYS2/MinGW-w64安装
  3. java 泛化_(转)Java中的泛化,依赖,关联,聚合,合成
  4. openApi 使用
  5. 视频检测、视频分类等视频相关算法
  6. 生产制造|产品繁多,物料明细不清晰,仓库究竟怎么管?
  7. PAT(B) 1037 在霍格沃茨找零钱(Java)
  8. fota 差分包_移动物联网模块之FOTA更新攻击
  9. 2021年安全生产模拟考试(特种作业低压电工模拟考试题库一)
  10. Win10重置网络后wifi没了