赞
踩
目录
使用resnet网络预测植物幼苗分类。
kaggle链接:weiliutao | Novice | Kaggle
给的数据分为三部门,第一个是test文件夹,存放所有要预测的图片。第二个是train文件夹,里面是各个已经分好类别的植物幼苗图片文件夹,用来进行训练模型。还有一个提交样例的csv文件。
由于我们在训练网络时要关注模型在每一轮的正确率,因此需要将train(在实现时防止混淆我将这个名称改为train1)下的数据划分为训练集和验证集,即将train1文件夹划分为train和val文件夹,一般以0.9:0.1进行划分。train和val文件夹下仍然是各个种类幼苗的文件夹,使用一个划分脚本来实现。
- import os
- from shutil import copy, rmtree
- import random
-
-
- def 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文件夹
- #这里的os.getcwd方法是获取当前代码所在路径
- #我的数据集在上上一级目录下的data_set的plant下的train_1中
- cwd = os.getcwd()
- data_root = os.path.join(cwd, "../data_set/plant")
- origin_flower_path = os.path.join(data_root, "train_1")
- 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))]
-
- # 建立保存训练集的文件夹,生成在data_root目录下
- 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, k=int(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, index+1, num), end="") # processing bar
- print()
-
- print("processing done!")
-
-
- if __name__ == '__main__':
- main()

在train模块中使用这个数据集时: 首先将路径指定到存放train和val的文件夹下,也就是plant,这里我存储的位置是上上级目录的data_set文件夹下的plant。
- data_root = os.path.abspath(os.path.join(os.getcwd(), "../..")) # get data root path
- image_path = os.path.join(data_root, "data_set", "plant") # flower data set path
- assert os.path.exists(image_path), "{} path does not exist.".format(image_path)
- train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),
- transform=data_transform["train"])
找到文件夹后使用torchvision下的datasets包的ImageFolder方法读取‘train’文件中的训练图片 ,这里的transform是对图片做出的处理,先不管。
- train_loader = torch.utils.data.DataLoader(train_dataset,
- batch_size=batch_size, shuffle=True,
- num_workers=nw)
- validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),
- transform=data_transform["val"])
- val_num = len(validate_dataset)
- validate_loader = torch.utils.data.DataLoader(validate_dataset,
- batch_size=batch_size, shuffle=False,
- num_workers=nw)
train_loader是使用DataLoader方法处理刚刚读取的train_dataset,batch_size是将图片按照给定的batch_size分组,shuffle是否打乱顺序,num_workers是使用cpu或GPU的个数。
有了数据集,便能使用神经网络进行训练。在训练时如何使用这些图片。
tqdm是为了记录时间。
通过enumerate遍历训练集(此时的训练集分为若干个batch_size大小的集合),每次step都会得到一个batch_size大小的集合,分别为images集合和labels集合,将images送到模型得到结果。再将结果与真实标签label作损失函数处理,再反向传播。
- for epoch in range(epochs):
- # train
- net.train()
- running_loss = 0.0
- train_bar = tqdm(train_loader, file=sys.stdout)
- for step, data in enumerate(train_bar):
- images, labels = data
- optimizer.zero_grad()
- logits = net(images.to(device))
- loss = loss_function(logits, labels.to(device))
- loss.backward()
- optimizer.step()
-
- # print statistics
- running_loss += loss.item()
-
- train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
- epochs,
- loss)
-
- # validate
- net.eval()
- acc = 0.0 # accumulate accurate number / epoch
- with torch.no_grad():
- val_bar = tqdm(validate_loader, file=sys.stdout)
- for val_data in val_bar:
- val_images, val_labels = val_data
- outputs = net(val_images.to(device))
- # loss = loss_function(outputs, test_labels)
- predict_y = torch.max(outputs, dim=1)[1]
- acc += torch.eq(predict_y, val_labels.to(device)).sum().item()
-
- val_bar.desc = "valid epoch[{}/{}]".format(epoch + 1,
- epochs)
-
- val_accurate = acc / val_num
- print('[epoch %d] train_loss: %.3f val_accuracy: %.3f' %
- (epoch + 1, running_loss / train_steps, val_accurate))
-
- if val_accurate > best_acc:
- best_acc = val_accurate
- torch.save(net.state_dict(), save_path)
-
- print('Finished Training')

因为不会将一个列表写入到一个csv文件中,所以搜了一下写了个测试代码:
- import pandas as pd
- import numpy as np
- # predict_kind = np.array([np.arange(794) , np.arange(2)] , dtype=str)
- # predict_kind[0][0]='12323'
- # print(predict_kind)
- # import numpy as np
- # m = np.array([np.arange(2), np.arange(5)], dtype=str) # 创建一个二维数组
- # m[0][1] = "love"
- # print(m)
- # print(m[0][1])
- # a = np.array(794*2,dtype=object)
- # a[1] = "12sdsds"
- # print(a)
- a = [[] for i in range(5)]
- a[0].append("asdasda")
- a[0].append("ppppp")
- a[1].append(("dddd"))
- print(a)
- data1 = pd.DataFrame(a)
- data1.to_csv('d.csv')

测试了很多发现最后没有注释的可以实现,因为我想要两列数据,一列是图片名称,一列是预测值,在此处定义一个二维列表,每个一维维度都代表一行数据,也就是一行中的两个数据,将列表转换为DataFrame格式才能写入csv文件中。
这里我稍微改了预测方法为带参方法,传入的是图片的路径,进行预处理后(和训练时方式一样)将这张图片拿给模型,模型给出概率最大的结果。
Image是PIL包中的类,可以通过给定图片路径拿到图片
data_transform是对图片做出的处理,这时候的图片是一个三维[C,H,W],在最前面加上一个维度。
- def result(image_name):
- device = torch.device("cuda:0" 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
- img_path = image_name
- assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)
- img = Image.open(img_path)
- #plt.imshow(img)
- # [N, C, H, W]
- img = data_transform(img)
- # expand batch dimension
- img = torch.unsqueeze(img, dim=0)
-
- # read class_indict
- json_path = './class_indices.json'
- assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)
-
- with open(json_path, "r") as f:
- class_indict = json.load(f)
-
- # create model
- model = resnet101(num_classes=12).to(device)
-
- # load model weights
- weights_path = "./resNet101.pth"
- assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)
- model.load_state_dict(torch.load(weights_path, map_location=device))
-
- # prediction
- model.eval()
- with torch.no_grad():
- # predict class
- output = torch.squeeze(model(img.to(device))).cpu()
- predict = torch.softmax(output, dim=0)
- predict_cla = torch.argmax(predict).numpy()
- return class_indict[str(predict_cla)]

model(img)将图片给到模型,再压缩batch方向维度。通过softmax处理得到概率分布。通过argmax找到最大值所对应的索引。在class_indict找到索引对应的类别。
通过os.listdir可以遍历文件夹下的所有文件名称,先将名称加入进去,将路径加上图片名称传给result,将每个预测结果存放到名称后面,最后转换为DataFrame存储到CSV文件中。
- data_root = os.path.abspath(os.path.join(os.getcwd(), "../..")) # get data root path
- image_path = os.path.join(data_root, "data_set", "plant","test") # flower data set path
- predict_kind = [[] for i in range(794)]
- i=0
- for filename in os.listdir(image_path):
- #image_name.append(filename)
- predict_kind[i].append(filename)
- path_image = os.path.join(image_path, filename)
- predict_kind[i].append(result(path_image))
- #print(path_image)
- #predict_kind.append(result(path_image))
- i+=1
- print(i)
- #print(predict_kind)
- data1 = pd.DataFrame(predict_kind)
- data1.to_csv('predict_2.csv')

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。