网站备案 厦门,黑白摄影网站,铁力seo,seo网站管理招聘一.数据集准备
新建一个项目文件夹ResNet#xff0c;并在里面建立data_set文件夹用来保存数据集#xff0c;在data_set文件夹下创建新文件夹flower_data#xff0c;点击链接下载花分类数据集https://storage.googleapis.com/download.tensorflow.org/example_i…一.数据集准备
新建一个项目文件夹ResNet并在里面建立data_set文件夹用来保存数据集在data_set文件夹下创建新文件夹flower_data点击链接下载花分类数据集https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz会下载一个压缩包将它解压到flower_data文件夹下执行split_data.py脚本自动将数据集划分成训练集train和验证集val。 split.py如下
import os
from shutil import copy, rmtree
import randomdef mk_file(file_path: str):if os.path.exists(file_path):# 如果文件夹存在则先删除原文件夹在重新创建rmtree(file_path)os.makedirs(file_path)def main():# 保证随机可复现random.seed(0)# 将数据集中10%的数据划分到验证集中split_rate 0.1# 指向你解压后的flower_photos文件夹cwd os.getcwd()data_root os.path.join(cwd, flower_data)origin_flower_path os.path.join(data_root, flower_photos)assert os.path.exists(origin_flower_path), path {} does not exist..format(origin_flower_path)flower_class [cla for cla in os.listdir(origin_flower_path)if os.path.isdir(os.path.join(origin_flower_path, cla))]# 建立保存训练集的文件夹train_root os.path.join(data_root, train)mk_file(train_root)for cla in flower_class:# 建立每个类别对应的文件夹mk_file(os.path.join(train_root, cla))# 建立保存验证集的文件夹val_root os.path.join(data_root, val)mk_file(val_root)for cla in flower_class:# 建立每个类别对应的文件夹mk_file(os.path.join(val_root, cla))for cla in flower_class:cla_path os.path.join(origin_flower_path, cla)images os.listdir(cla_path)num len(images)# 随机采样验证集的索引eval_index random.sample(images, kint(num*split_rate))for index, image in enumerate(images):if image in eval_index:# 将分配至验证集中的文件复制到相应目录image_path os.path.join(cla_path, image)new_path os.path.join(val_root, cla)copy(image_path, new_path)else:# 将分配至训练集中的文件复制到相应目录image_path os.path.join(cla_path, image)new_path os.path.join(train_root, cla)copy(image_path, new_path)print(\r[{}] processing [{}/{}].format(cla, index1, num), end) # processing barprint()print(processing done!)if __name__ __main__:main()之后会在文件夹下生成train和val数据集到此完成了数据集的准备。 二.定义网络
新建model.py参照ResNet的网络结构和pytorch官方给出的代码对代码进行略微的修改即可首先定义了两个类BasicBlock和Bottleneck分别对应着ResNet18、34和ResNet50、101、152从下面这个图就可以区别开来。
可见18和34层的网络他们的conv2_x,conv3_x,conv4_x,conv5_x是相同的不同的是每一个block的数量[2 2 2 2]和[3 4 6 3]50和101和152层的网络多了1*1卷积核block数量也不尽相同。 接着定义了ResNet类进行前向传播。对于34层的网络这里借用了知乎牧酱老哥的图18和34的block相同所以用18的进行讲解conv2_x和conv3_x对应的残差块对应的残差快在右侧展示出来可以注意一下stride当计算特征图尺寸时要特别注意。在下方代码计算尺寸的部分我都进行了注释。 pytorch官方ResNet代码
修改后的train.py
import torch.nn as nn
import torchclass BasicBlock(nn.Module): #18 34层残差结构 残差块expansion 1def __init__(self, in_channel, out_channel, stride1, downsampleNone, **kwargs):super(BasicBlock, self).__init__()self.conv1 nn.Conv2d(in_channelsin_channel, out_channelsout_channel, kernel_size3, stridestride, padding1, biasFalse)self.bn1 nn.BatchNorm2d(out_channel)self.relu nn.ReLU()self.conv2 nn.Conv2d(in_channelsout_channel, out_channelsout_channel, kernel_size3, stride1, padding1, biasFalse)self.bn2 nn.BatchNorm2d(out_channel)self.downsample downsampledef forward(self, x):identity xif self.downsample is not None:identity self.downsample(x) # 不为none对应虚线残差结构下需要1*1卷积调整维度为none对应实线残差结构不需要1*1卷积out self.conv1(x) out self.bn1(out)out self.relu(out)out self.conv2(out) out self.bn2(out)out identityout self.relu(out)return outclass Bottleneck(nn.Module): #50 101 152层残差结构注意原论文中在虚线残差结构的主分支上第一个1x1卷积层的步距是2第二个3x3卷积层步距是1。但在pytorch官方实现过程中是第一个1x1卷积层的步距是1第二个3x3卷积层步距是2这么做的好处是能够在top1上提升大概0.5%的准确率。可参考Resnet v1.5 https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorchexpansion 4def __init__(self, in_channel, out_channel, stride1, downsampleNone, groups1, width_per_group64):super(Bottleneck, self).__init__()width int(out_channel * (width_per_group / 64.)) * groups# squeeze channelsself.conv1 nn.Conv2d(in_channelsin_channel, out_channelswidth, kernel_size1, stride1, biasFalse) self.bn1 nn.BatchNorm2d(width)# -----------------------------------------self.conv2 nn.Conv2d(in_channelswidth, out_channelswidth, groupsgroups,kernel_size3, stridestride, biasFalse, padding1)self.bn2 nn.BatchNorm2d(width)# -----------------------------------------# unsqueeze channelsself.conv3 nn.Conv2d(in_channelswidth, out_channelsout_channel*self.expansion, kernel_size1, stride1, biasFalse) self.bn3 nn.BatchNorm2d(out_channel*self.expansion)self.relu nn.ReLU(inplaceTrue)self.downsample downsampledef forward(self, x):identity xif self.downsample is not None: # 不为none对应虚线残差结构下需要1*1卷积调整维度为none对应实线残差结构不需要1*1卷积identity self.downsample(x)out self.conv1(x)out self.bn1(out)out self.relu(out)out self.conv2(out)out self.bn2(out)out self.relu(out)out self.conv3(out)out self.bn3(out)out identityout self.relu(out)return outclass ResNet(nn.Module):def __init__(self, block, blocks_num, num_classes1000, include_topTrue, groups1, width_per_group64):super(ResNet, self).__init__()self.include_top include_topself.in_channel 64self.groups groupsself.width_per_group width_per_group# channel height widthself.conv1 nn.Conv2d(3, self.in_channel, kernel_size7, stride2, padding3, biasFalse) # (3 224 224) - (64 112 112)self.bn1 nn.BatchNorm2d(self.in_channel)self.relu nn.ReLU(inplaceTrue)self.maxpool nn.MaxPool2d(kernel_size3, stride2, padding1) # (64 112 112) - (64 56 56)# 对于每一个block第一次的两个卷积层stride1和1第二次stride1和1self.layer1 self._make_layer(block, 64, blocks_num[0]) # (64 56 56) - (64 56 56)# 对于每一个block第一次的两个卷积层stride2和1第二次stride1和1self.layer2 self._make_layer(block, 128, blocks_num[1], stride2) # (64 56 56) - (128 28 28)self.layer3 self._make_layer(block, 256, blocks_num[2], stride2) # (128 28 28) - (256 14 14)self.layer4 self._make_layer(block, 512, blocks_num[3], stride2) # (256 28 28) - (512 14 14)if self.include_top:self.avgpool nn.AdaptiveAvgPool2d((1, 1)) # output size (1, 1)self.fc nn.Linear(512 * block.expansion, num_classes)for m in self.modules():if isinstance(m, nn.Conv2d):nn.init.kaiming_normal_(m.weight, modefan_out, nonlinearityrelu)def _make_layer(self, block, channel, block_num, stride1): # channel为当前block所使用的卷积核个数downsample None if stride ! 1 or self.in_channel ! channel * block.expansion: # 18和32不满足判断条件会跳过50 101 152会执行这部分downsample nn.Sequential(nn.Conv2d(self.in_channel, channel * block.expansion, kernel_size1, stridestride, biasFalse),nn.BatchNorm2d(channel * block.expansion))layers []layers.append(block(self.in_channel,channel,downsampledownsample,stridestride,groupsself.groups,width_per_groupself.width_per_group))self.in_channel channel * block.expansionfor _ in range(1, block_num):layers.append(block(self.in_channel,channel,groupsself.groups,width_per_groupself.width_per_group))return nn.Sequential(*layers)def forward(self, x):x self.conv1(x)x self.bn1(x)x self.relu(x)x self.maxpool(x)x self.layer1(x)x self.layer2(x)x self.layer3(x)x self.layer4(x)if self.include_top:x self.avgpool(x)x torch.flatten(x, 1)x self.fc(x)return xdef resnet34(num_classes1000, include_topTrue):# https://download.pytorch.org/models/resnet34-333f7ec4.pthreturn ResNet(BasicBlock, [3, 4, 6, 3], num_classesnum_classes, include_topinclude_top)def resnet50(num_classes1000, include_topTrue):# https://download.pytorch.org/models/resnet50-19c8e357.pthreturn ResNet(Bottleneck, [3, 4, 6, 3], num_classesnum_classes, include_topinclude_top)def resnet101(num_classes1000, include_topTrue):# https://download.pytorch.org/models/resnet101-5d3b4d8f.pthreturn ResNet(Bottleneck, [3, 4, 23, 3], num_classesnum_classes, include_topinclude_top)def resnext50_32x4d(num_classes1000, include_topTrue):# https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pthgroups 32width_per_group 4return ResNet(Bottleneck, [3, 4, 6, 3],num_classesnum_classes,include_topinclude_top,groupsgroups,width_per_groupwidth_per_group)def resnext101_32x8d(num_classes1000, include_topTrue):# https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pthgroups 32width_per_group 8return ResNet(Bottleneck, [3, 4, 23, 3],num_classesnum_classes,include_topinclude_top,groupsgroups,width_per_groupwidth_per_group)if __name__ __main__:resnet ResNet(BasicBlock, [3, 4, 6, 3], num_classes5)in_data torch.randn(1, 3, 224, 224)out resnet(in_data)print(out)
完成网络的定义之后可以单独执行一下这个文件用来验证网络定义的是否正确。如果可以正确输出就没问题。
在这里输出为
tensor([[-0.4490, 0.5792, -0.5026, -0.6024, 0.1399]], grad_fnAddmmBackward0)
说明网络定义正确。
三.开始训练 加载数据集
首先定义一个字典用于用于对train和val进行预处理包括裁剪成224*224大小训练集随机水平翻转一般验证集不需要此操作转换成张量图像归一化。
然后利用DataLoader模块加载数据集并设置batch_size为16同时设置数据加载器的工作进程数nw加快速度。
import os
import sys
import jsonimport torch
import torch.nn as nn
import torch.optim as optim
from torchvision import transforms, datasets
from torch.utils.data import DataLoader
from tqdm import tqdmfrom model import resnet34def main():device torch.device(cuda if torch.cuda.is_available() else cpu)print(fusing {device} device.)data_transform {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])])}# 获取数据集路径image_path os.path.join(os.getcwd(), data_set, flower_data)assert os.path.exists(image_path), f{image_path} path does not exist.# 加载数据集准备读取train_dataset datasets.ImageFolder(rootos.path.join(image_path, train), transformdata_transform[train])validate_dataset datasets.ImageFolder(rootos.path.join(image_path, val), transformdata_transform[val])nw min([os.cpu_count(), 16 if 16 1 else 0, 8]) # number of workers加速图像预处理print(fUsing {nw} dataloader workers every process)# 加载数据集train_loader torch.utils.data.DataLoader(train_dataset, batch_size16, shuffleTrue, num_workersnw)validate_loader torch.utils.data.DataLoader(validate_dataset, batch_size16, shuffleFalse, num_workersnw)train_num len(train_dataset)val_num len(validate_dataset)print(fusing {train_num} images for training, {val_num} images for validation.)
生成json文件
将训练数据集的类别标签转换为字典格式并将其写入名为class_indices.json的文件中。
从train_dataset中获取类别标签到索引的映射关系存储在flower_list变量中。使用列表推导式将flower_list中的键值对反转得到一个新的字典cla_dict其中键是原始类别标签值是对应的索引。使用json.dumps()函数将cla_dict转换为JSON格式的字符串设置缩进为4个空格。使用with open()语句以写入模式打开名为class_indices.json的文件并将JSON字符串写入文件 # {daisy:0, dandelion:1, roses:2, sunflower:3, tulips:4} 雏菊 蒲公英 玫瑰 向日葵 郁金香# 从训练集中获取类别标签到索引的映射关系存储在flower_list变量flower_list train_dataset.class_to_idx# 使用列表推导式将flower_list中的键值对反转得到一个新的字典cla_dictcla_dict dict((val, key) for key, val in flower_list.items())# write dict into json filejson_str json.dumps(cla_dict, indent4)with open(class_indices.json, w) as json_file:json_file.write(json_str)
加载预训练模型开始训练
首先定义网络对象net在这里我们使用了迁移学习来使网络训练效果更好使用net.fc nn.Linear(in_channel, 5)设置输出类别数这里为5训练10轮并使用train_bar tqdm(train_loader, filesys.stdout)来可视化训练进度条之后再进行反向传播和参数更新;同时每一轮训练完成都要进行学习率更新之后开始对验证集进行计算精确度完成后保存模型。 # load pretrain weights# download url: https://download.pytorch.org/models/resnet34-333f7ec4.pthnet resnet34()model_weight_path ./resnet34-pre.pthassert os.path.exists(model_weight_path), ffile {model_weight_path} does not exist.net.load_state_dict(torch.load(model_weight_path, map_locationcpu))# change fc layer structurein_channel net.fc.in_featuresnet.fc nn.Linear(in_channel, 5)net.to(device)loss_function nn.CrossEntropyLoss()optimizer optim.Adam([p for p in net.parameters() if p.requires_grad], lr0.0001)epochs 10best_acc 0.0train_steps len(train_loader)for epoch in range(epochs):# trainnet.train()running_loss 0.0train_bar tqdm(train_loader, filesys.stdout)for step, data in enumerate(train_bar):images, labels dataoptimizer.zero_grad()logits net(images.to(device))loss loss_function(logits, labels.to(device))loss.backward()optimizer.step()# print statisticsrunning_loss loss.item()train_bar.desc train epoch[{epoch 1}/{epochs}] loss:{loss:.3f}# validatenet.eval()acc 0.0 # accumulate accurate number / epochwith torch.no_grad():val_bar tqdm(validate_loader, filesys.stdout)for val_data in val_bar:val_images, val_labels val_dataoutputs net(val_images.to(device))# loss loss_function(outputs, test_labels)predict_y torch.max(outputs, dim1)[1]acc torch.eq(predict_y, val_labels.to(device)).sum().item()val_bar.desc fvalid epoch[{epoch 1}/{epochs}]val_accurate acc / val_numprint([epoch %d] train_loss: %.3f val_accuracy: %.3f %(epoch 1, running_loss / train_steps, val_accurate))if val_accurate best_acc:best_acc val_accuratetorch.save(net, ./resnet.pth)print(Finished Training)最后对代码进行整理完整的train.py如下
import os
import sys
import jsonimport torch
import torch.nn as nn
import torch.optim as optim
from torchvision import transforms, datasets
from torch.utils.data import DataLoader
from tqdm import tqdmfrom model import resnet34def main():device torch.device(cuda if torch.cuda.is_available() else cpu)print(fusing {device} device.)data_transform {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])])}# 获取数据集路径image_path os.path.join(os.getcwd(), data_set, flower_data)assert os.path.exists(image_path), f{image_path} path does not exist.# 加载数据集准备读取train_dataset datasets.ImageFolder(rootos.path.join(image_path, train), transformdata_transform[train])validate_dataset datasets.ImageFolder(rootos.path.join(image_path, val), transformdata_transform[val])nw min([os.cpu_count(), 16 if 16 1 else 0, 8]) # number of workers加速图像预处理print(fUsing {nw} dataloader workers every process)# 加载数据集train_loader torch.utils.data.DataLoader(train_dataset, batch_size16, shuffleTrue, num_workersnw)validate_loader torch.utils.data.DataLoader(validate_dataset, batch_size16, shuffleFalse, num_workersnw)train_num len(train_dataset)val_num len(validate_dataset)print(fusing {train_num} images for training, {val_num} images for validation.)# {daisy:0, dandelion:1, roses:2, sunflower:3, tulips:4} 雏菊 蒲公英 玫瑰 向日葵 郁金香# 从训练集中获取类别标签到索引的映射关系存储在flower_list变量flower_list train_dataset.class_to_idx# 使用列表推导式将flower_list中的键值对反转得到一个新的字典cla_dictcla_dict dict((val, key) for key, val in flower_list.items())# write dict into json filejson_str json.dumps(cla_dict, indent4)with open(class_indices.json, w) as json_file:json_file.write(json_str)# load pretrain weights# download url: https://download.pytorch.org/models/resnet34-333f7ec4.pthnet resnet34()model_weight_path ./resnet34-pre.pthassert os.path.exists(model_weight_path), ffile {model_weight_path} does not exist.net.load_state_dict(torch.load(model_weight_path, map_locationcpu))# change fc layer structurein_channel net.fc.in_featuresnet.fc nn.Linear(in_channel, 5)net.to(device)loss_function nn.CrossEntropyLoss()optimizer optim.Adam([p for p in net.parameters() if p.requires_grad], lr0.0001)epochs 10best_acc 0.0train_steps len(train_loader)for epoch in range(epochs):# trainnet.train()running_loss 0.0train_bar tqdm(train_loader, filesys.stdout)for step, data in enumerate(train_bar):images, labels dataoptimizer.zero_grad()logits net(images.to(device))loss loss_function(logits, labels.to(device))loss.backward()optimizer.step()# print statisticsrunning_loss loss.item()train_bar.desc train epoch[{epoch 1}/{epochs}] loss:{loss:.3f}# validatenet.eval()acc 0.0 # accumulate accurate number / epochwith torch.no_grad():val_bar tqdm(validate_loader, filesys.stdout)for val_data in val_bar:val_images, val_labels val_dataoutputs net(val_images.to(device))# loss loss_function(outputs, test_labels)predict_y torch.max(outputs, dim1)[1]acc torch.eq(predict_y, val_labels.to(device)).sum().item()val_bar.desc fvalid epoch[{epoch 1}/{epochs}]val_accurate acc / val_numprint([epoch %d] train_loss: %.3f val_accuracy: %.3f %(epoch 1, running_loss / train_steps, val_accurate))if val_accurate best_acc:best_acc val_accuratetorch.save(net, ./resnet.pth)print(Finished Training)if __name__ __main__:main()四.模型预测
新建一个predict.py文件用于预测将输入图像处理后转换成张量格式img torch.unsqueeze(img, dim0)是在输入图像张量 img 的第一个维度上增加一个大小为1的维度因此将图像张量的形状从 [通道数 高度 宽度 ] 转换为 [1, 通道数 高度 宽度]。然后加载模型进行预测并打印出结果同时可视化。
import os
import jsonimport torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as pltfrom model import resnet34def main():device torch.device(cuda if torch.cuda.is_available() else cpu)data_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])])# load imageimg Image.open(./2536282942_b5ca27577e.jpg)plt.imshow(img)# [N, C, H, W]img data_transform(img)# expand batch dimension# 在输入图像张量 img 的第一个维度上增加一个大小为1的维度# 将图像张量的形状从 [通道数 高度 宽度 ] 转换为 [1, 通道数 高度 宽度]img torch.unsqueeze(img, dim0)# read class_indictwith open(./class_indices.json, r) as f:class_indict json.load(f)# create modelmodel resnet34(num_classes5).to(device)model torch.load(./resnet34.pth)# predictionmodel.eval()with torch.no_grad():# predict classoutput torch.squeeze(model(img.to(device))).cpu()predict torch.softmax(output, dim0)predict_class torch.argmax(predict).numpy()print_result fclass: {class_indict[str(predict_class)]} prob: {predict[predict_class].numpy():.3}plt.title(print_result)for i in range(len(predict)):print(fclass: {class_indict[str(i)]:10} prob: {predict[i].numpy():.3})plt.show()if __name__ __main__:main()预测结果 五.模型可视化
将生成的pth文件导入netron工具可视化结果为 发现很不清晰因此将它转换成多用于嵌入式设备部署的onnx格式
编写onnx.py
import torch
import torchvision
from model import resnet34device torch.device(cuda if torch.cuda.is_available() else cpu)
model resnet34(num_classes5).to(device)
modeltorch.load(/home/lm/Resnet/resnet34.pth)
model.eval()
example torch.ones(1, 3, 244, 244)
example example.to(device)
torch.onnx.export(model, example, resnet34.onnx, verboseTrue, opset_version11)将生成的onnx文件导入这样的可视化清晰了许多 六.批量数据预测
现在新建一个dta文件夹里面放入五类带预测的样本编写代码完成对整个文件夹下所有样本的预测即批量预测。
batch_predict.py如下
import os
import jsonimport torch
from PIL import Image
from torchvision import transformsfrom model import resnet34def main():device torch.device(cuda if torch.cuda.is_available() else cpu)data_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])])# load image# 指向需要遍历预测的图像文件夹imgs_root ./data/imgs# 读取指定文件夹下所有jpg图像路径img_path_list [os.path.join(imgs_root, i) for i in os.listdir(imgs_root) if i.endswith(.jpg)]# read class_indictjson_file open(./class_indices.json, r)class_indict json.load(json_file)# create modelmodel resnet34(num_classes5).to(device)model torch.load(./resnet34.pth)# predictionmodel.eval()batch_size 8 # 每次预测时将多少张图片打包成一个batchwith torch.no_grad():for ids in range(0, len(img_path_list) // batch_size):img_list []for img_path in img_path_list[ids * batch_size: (ids 1) * batch_size]:img Image.open(img_path)img data_transform(img)img_list.append(img)# batch img# 将img_list列表中的所有图像打包成一个batchbatch_img torch.stack(img_list, dim0)# predict classoutput model(batch_img.to(device)).cpu()predict torch.softmax(output, dim1)probs, classes torch.max(predict, dim1)for idx, (pro, cla) in enumerate(zip(probs, classes)):print(fimage: {img_path_list[ids*batch_sizeidx]} class: {class_indict[str(cla.numpy())]} prob: {pro.numpy():.3})if __name__ __main__:main()运行之后输出
image: ./data/imgs/455728598_c5f3e7fc71_m.jpg class: dandelion prob: 0.989 image: ./data/imgs/3464015936_6845f46f64.jpg class: dandelion prob: 0.999 image: ./data/imgs/3461986955_29a1abc621.jpg class: dandelion prob: 0.996 image: ./data/imgs/8223949_2928d3f6f6_n.jpg class: dandelion prob: 0.991 image: ./data/imgs/10919961_0af657c4e8.jpg class: dandelion prob: 1.0 image: ./data/imgs/10443973_aeb97513fc_m.jpg class: dandelion prob: 0.906 image: ./data/imgs/8475758_4c861ab268_m.jpg class: dandelion prob: 0.805 image: ./data/imgs/3857059749_fe8ca621a9.jpg class: dandelion prob: 1.0 image: ./data/imgs/2457473644_5242844e52_m.jpg class: dandelion prob: 1.0 image: ./data/imgs/146023167_f905574d97_m.jpg class: dandelion prob: 0.998 image: ./data/imgs/2502627784_4486978bcf.jpg class: dandelion prob: 0.488 image: ./data/imgs/2481428401_bed64dd043.jpg class: dandelion prob: 1.0 image: ./data/imgs/13920113_f03e867ea7_m.jpg class: dandelion prob: 1.0 image: ./data/imgs/2535769822_513be6bbe9.jpg class: dandelion prob: 0.997 image: ./data/imgs/3954167682_128398bf79_m.jpg class: dandelion prob: 1.0 image: ./data/imgs/2516714633_87f28f0314.jpg class: dandelion prob: 0.998 image: ./data/imgs/2634665077_597910235f_m.jpg class: dandelion prob: 0.996 image: ./data/imgs/3502447188_ab4a5055ac_m.jpg class: dandelion prob: 0.999 image: ./data/imgs/425800274_27dba84fac_n.jpg class: dandelion prob: 0.422 image: ./data/imgs/3365850019_8158a161a8_n.jpg class: dandelion prob: 1.0 image: ./data/imgs/674407101_57676c40fb.jpg class: dandelion prob: 1.0 image: ./data/imgs/2628514700_b6d5325797_n.jpg class: dandelion prob: 0.999 image: ./data/imgs/3688128868_031e7b53e1_n.jpg class: dandelion prob: 0.962 image: ./data/imgs/2502613166_2c231b47cb_n.jpg class: dandelion prob: 1.0
完成预期功能这里我的样本都是dandelion,当然混合的也可以
七.模型改进
当不加载预训练模型而从头开始训练的话当epoch为50时经实际训练准确率为80%多但当加载预训练模型时完成第一次迭代准确率就已达到了90%这也正说明了迁移学习的好处。
同时这里采用的是Resnet34也可以尝试更深的50、101、152层网络。
还有其他方法会在之后进行补充。