⽤python搭建⼀个花卉识别系统⽬录
⼀.开源神经⽹络(AlexNet)
1.获取数据集
2.神经⽹络模型
3.训练神经⽹络
4.对模型进⾏预测
⼆、花卉识别系统搭建(flask)
1.构建页⾯:
2.调⽤神经⽹络模型
3.系统识别结果
4.启动系统:
三、总结
⼀.开源神经⽹络(AlexNet)
1.获取数据集
使⽤步骤如下:
* (1)在data_set⽂件夹下创建新⽂件夹"flower_data"
* (2)点击链接下载花分类数据集/example\_im…
* (3)解压数据集到flower_data⽂件夹下
* (4)执⾏"split_data.py"脚本⾃动将数据集划分成训练集train和验证集val
split_data.py
import os
from shutil import copy, rmtree
import random
def mk_file(file_path: str):
if ists(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 ists(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, 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()
2.神经⽹络模型
model.py
as nn
import torch
class AlexNet(nn.Module):
def __init__(self, num_classes=1000, init_weights=False):
super(AlexNet, self).__init__()
# ⽤nn.Sequential()将⽹络打包成⼀个模块,精简代码
self.features = nn.Sequential(  # 卷积层提取图像特征
nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2),  # input[3, 224, 224]  output[48, 55, 55]            nn.ReLU(inplace=True),          # 直接修改覆盖原值,节省运算内存
nn.MaxPool2d(kernel_size=3, stride=2),                  # output[48, 27, 27]
nn.Conv2d(48, 128, kernel_size=5, padding=2),          # output[128, 27, 27]
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),                  # output[128, 13, 13]
nn.Conv2d(128, 192, kernel_size=3, padding=1),          # output[192, 13, 13]resized
nn.ReLU(inplace=True),
nn.Conv2d(192, 192, kernel_size=3, padding=1),          # output[192, 13, 13]
nn.ReLU(inplace=True),
nn.Conv2d(192, 128, kernel_size=3, padding=1),          # output[128, 13, 13]
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),                  # output[128, 6, 6]
)
self.classifier = nn.Sequential(  # 全连接层对图像分类
nn.Dropout(p=0.5),      # Dropout 随机失活神经元,默认⽐例为0.5
nn.Linear(128 * 6 * 6, 2048),
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(2048, 2048),
nn.ReLU(inplace=True),
nn.Linear(2048, num_classes),
)
if init_weights:
self._initialize_weights()
# 前向传播过程
def forward(self, x):
x = self.features(x)
x = torch.flatten(x, start_dim=1) # 展平后再传⼊全连接层
x = self.classifier(x)
return x
# ⽹络权重初始化,实际上 pytorch 在构建⽹络时会⾃动初始化权重
def _initialize_weights(self):
for m dules():
if isinstance(m, nn.Conv2d):                            # 若是卷积层
nn.init.kaiming_normal_(m.weight, mode='fan_out',  # ⽤(何)kaiming_normal_法初始化权重
nonlinearity='relu')
if m.bias is not None:
stant_(m.bias, 0)                    # 初始化偏重为0
elif isinstance(m, nn.Linear):            # 若是全连接层
al_(m.weight, 0, 0.01)    # 正态分布初始化
stant_(m.bias, 0)          # 初始化偏重为0
3.训练神经⽹络
train.py
# 导⼊包
import torch
as nn
from torchvision import transforms, datasets, utils
import matplotlib.pyplot as plt
import numpy as np
import torch.optim as optim
from model import AlexNet
import os
import json
import time
# 使⽤GPU训练
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
with open(os.path.join("train.log"), "a") as log:
log.write(str(device)+"\n")
#数据预处理
data_transform = {
"train": transforms.Compose([transforms.RandomResizedCrop(224),      # 随机裁剪,再缩放成 224×224
transforms.RandomHorizontalFlip(p=0.5),  # ⽔平⽅向随机翻转,概率为 0.5, 即⼀半的概率翻转, ⼀半的概率不翻转                                transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),
"val": transforms.Compose([transforms.Resize((224, 224)),  # cannot 224, must (224, 224)
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}
#导⼊、加载训练集
# 导⼊训练集
#train_set = torchvision.datasets.CIFAR10(root='./data',  # 数据集存放⽬录
#          train=True,  # 表⽰是数据集中的训练集
#                                        download=True,    # 第⼀次运⾏时为True,下载数据集,下载完成后改为False
#                                        transform=transform) # 预处理过程
# 加载训练集
#train_loader = torch.utils.data.DataLoader(train_set,    # 导⼊的训练集
#            batch_size=50, # 每批训练的样本数
#                                          shuffle=False,  # 是否打乱训练集
#                                          num_workers=0)  # num_workers在windows下设置为0
# 获取图像数据集的路径
data_root = os.path.abspath(os.path.wd(), "../.."))    # get data root path 返回上上层⽬录
image_path = data_root + "/jqsj/data_set/flower_data/"        # flower data_set path
# 导⼊训练集并进⾏预处理
train_dataset = datasets.ImageFolder(root=image_path + "/train",
transform=data_transform["train"])
train_num = len(train_dataset)
# 按batch_size分批次加载训练集
train_loader = torch.utils.data.DataLoader(train_dataset, # 导⼊的训练集
batch_size=32,  # 每批训练的样本数
shuffle=True, # 是否打乱训练集
num_workers=0) # 使⽤线程数,在windows下设置为0
#导⼊、加载验证集
# 导⼊验证集并进⾏预处理
validate_dataset = datasets.ImageFolder(root=image_path + "/val",
transform=data_transform["val"])
val_num = len(validate_dataset)
# 加载验证集
validate_loader = torch.utils.data.DataLoader(validate_dataset, # 导⼊的验证集
batch_size=32,
shuffle=True,
num_workers=0)
# 存储索引:标签的字典
# 字典,类别:索引 {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
flower_list = train_dataset.class_to_idx
# 将 flower_list 中的 key 和 val 调换位置
cla_dict = dict((val, key) for key, val in flower_list.items())
# 将 cla_dict 写⼊ json ⽂件中
json_str = json.dumps(cla_dict, indent=4)
with open('class_indices.json', 'w') as json_file:
json_file.write(json_str)
#训练过程
net = AlexNet(num_classes=5, init_weights=True)    # 实例化⽹络(输出类型为5,初始化权重)
<(device)            # 分配⽹络到指定的设备(GPU/CPU)训练
loss_function = nn.CrossEntropyLoss()      # 交叉熵损失
optimizer = optim.Adam(net.parameters(), lr=0.0002)  # 优化器(训练参数,学习率)
save_path = './AlexNet.pth'
best_acc = 0.0
for epoch in range(150):
>>>>>>>>## train >>>>>>>>>##    ain()          # 训练过程中开启 Dropout
running_loss = 0.0    # 每个 epoch 都会对 running_loss  清零
time_start = time.perf_counter() # 对训练⼀个 epoch 计时
for step, data in enumerate(train_loader, start=0):  # 遍历训练集,step从0开始计算
images, labels = data  # 获取训练集的图像和标签
<_grad() # 清除历史梯度
outputs = (device))    # 正向传播
loss = loss_function(outputs, (device)) # 计算损失
loss.backward()            # 反向传播
optimizer.step()        # 优化器更新参数
running_loss += loss.item()
# 打印训练进度(使训练过程可视化)
rate = (step + 1) / len(train_loader)          # 当前进度 = 当前step / 训练⼀轮epoch所需总step
a = "*" * int(rate * 50)
b = "." * int((1 - rate) * 50)
with open(os.path.join("train.log"), "a") as log:
log.write(str("\rtrain loss: {:^3.0f}%[{}->{}]{:.3f}".format(int(rate * 100), a, b, loss))+"\n")
print("\rtrain loss: {:^3.0f}%[{}->{}]{:.3f}".format(int(rate * 100), a, b, loss), end="")
print()
with open(os.path.join("train.log"), "a") as log:
log.write(str('%f s' % (time.perf_counter()-time_start))+"\n")
print('%f s' % (time.perf_counter()-time_start))
>>>>>>>>### validate >>>>>>>>###    net.eval()    # 验证过程中关闭 Dropout
acc = 0.0
_grad():
for val_data in validate_loader:
val_images, val_labels = val_data
outputs = net((device))
predict_y = torch.max(outputs, dim=1)[1]  # 以output中值最⼤位置对应的索引(标签)作为预测输出
acc += (predict_y == (device)).sum().item()
val_accurate = acc / val_num
# 保存准确率最⾼的那次⽹络参数
if val_accurate > best_acc:
best_acc = val_accurate
torch.save(net.state_dict(), save_path)
with open(os.path.join("train.log"), "a") as log:
log.write(str('[epoch %d] train_loss: %.3f  test_accuracy: %.3f \n' %
(epoch + 1, running_loss / step, val_accurate))+"\n")
print('[epoch %d] train_loss: %.3f  test_accuracy: %.3f \n' %
(epoch + 1, running_loss / step, val_accurate))
with open(os.path.join("train.log"), "a") as log:
log.write(str('Finished Training')+"\n")
print('Finished Training')
训练结果后,准确率是94%
训练⽇志如下:
4.对模型进⾏预测
predict.py
import torch
接着对其中⼀个花卉图⽚进⾏识别,其结果如下:
可以看到只有⼀个识别结果(daisy雏菊)和准确率1.0是100%(范围是0~1,所以1对应100%)为了⽅便使⽤这个神经⽹络,接着我们将其开发成⼀个可视化的界⾯操作
⼆、花卉识别系统搭建(flask)
1.构建页⾯:
2.调⽤神经⽹络模型
main.py
# coding:utf-8
from flask import Flask, render_template, request, redirect, url_for, make_response, jsonify
from werkzeug.utils import secure_filename
import os
import time
>>>####
#模型所需库包
import torch
from model import AlexNet
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as plt
import json
# read class_indict
try:
json_file = open('./class_indices.json', 'r')
class_indict = json.load(json_file)
except Exception as e:
print(e)
exit(-1)
# create model
model = AlexNet(num_classes=5)
# load model weights
model_weight_path = "./AlexNet.pth"
#, map_location='cpu'
model.load_state_dict(torch.load(model_weight_path, map_location='cpu'))
# 关闭 Dropout
model.eval()
>>>####
from datetime import timedelta
# 设置允许的⽂件格式
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'JPG', 'PNG', 'bmp'])
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS

发表评论