您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)怎么用python搭建一個(gè)花卉識(shí)別系統(tǒng),小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
使用步驟如下:
* (1)在data_set文件夾下創(chuàng)建新文件夾"flower_data"
* (2)點(diǎn)擊鏈接下載花分類數(shù)據(jù)集download.tensorflow.org/example\_im…
* (3)解壓數(shù)據(jù)集到flower_data文件夾下
* (4)執(zhí)行"split_data.py"腳本自動(dòng)將數(shù)據(jù)集劃分成訓(xùn)練集train和驗(yàn)證集val
split_data.py
import os from shutil import copy, rmtree import random def mk_file(file_path: str): if os.path.exists(file_path): # 如果文件夾存在,則先刪除原文件夾在重新創(chuàng)建 rmtree(file_path) os.makedirs(file_path) def main(): # 保證隨機(jī)可復(fù)現(xiàn) random.seed(0) # 將數(shù)據(jù)集中10%的數(shù)據(jù)劃分到驗(yàn)證集中 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) flower_class = [cla for cla in os.listdir(origin_flower_path) if os.path.isdir(os.path.join(origin_flower_path, cla))] # 建立保存訓(xùn)練集的文件夾 train_root = os.path.join(data_root, "train") mk_file(train_root) for cla in flower_class: # 建立每個(gè)類別對應(yīng)的文件夾 mk_file(os.path.join(train_root, cla)) # 建立保存驗(yàn)證集的文件夾 val_root = os.path.join(data_root, "val") mk_file(val_root) for cla in flower_class: # 建立每個(gè)類別對應(yīng)的文件夾 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) # 隨機(jī)采樣驗(yàn)證集的索引 eval_index = random.sample(images, k=int(num*split_rate)) for index, image in enumerate(images): if image in eval_index: # 將分配至驗(yàn)證集中的文件復(fù)制到相應(yīng)目錄 image_path = os.path.join(cla_path, image) new_path = os.path.join(val_root, cla) copy(image_path, new_path) else: # 將分配至訓(xùn)練集中的文件復(fù)制到相應(yīng)目錄 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()
model.py
import torch.nn as nn import torch class AlexNet(nn.Module): def __init__(self, num_classes=1000, init_weights=False): super(AlexNet, self).__init__() # 用nn.Sequential()將網(wǎng)絡(luò)打包成一個(gè)模塊,精簡代碼 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), # 直接修改覆蓋原值,節(jié)省運(yùn)算內(nèi)存 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] 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 隨機(jī)失活神經(jīng)元,默認(rèn)比例為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 # 網(wǎng)絡(luò)權(quán)重初始化,實(shí)際上 pytorch 在構(gòu)建網(wǎng)絡(luò)時(shí)會(huì)自動(dòng)初始化權(quán)重 def _initialize_weights(self): for m in self.modules(): if isinstance(m, nn.Conv2d): # 若是卷積層 nn.init.kaiming_normal_(m.weight, mode='fan_out', # 用(何)kaiming_normal_法初始化權(quán)重 nonlinearity='relu') if m.bias is not None: nn.init.constant_(m.bias, 0) # 初始化偏重為0 elif isinstance(m, nn.Linear): # 若是全連接層 nn.init.normal_(m.weight, 0, 0.01) # 正態(tài)分布初始化 nn.init.constant_(m.bias, 0) # 初始化偏重為0
train.py
# 導(dǎo)入包 import torch import torch.nn 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訓(xùn)練 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") #數(shù)據(jù)預(yù)處理 data_transform = { "train": transforms.Compose([transforms.RandomResizedCrop(224), # 隨機(jī)裁剪,再縮放成 224×224 transforms.RandomHorizontalFlip(p=0.5), # 水平方向隨機(jī)翻轉(zhuǎn),概率為 0.5, 即一半的概率翻轉(zhuǎn), 一半的概率不翻轉(zhuǎn) 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))])} #導(dǎo)入、加載 訓(xùn)練集 # 導(dǎo)入訓(xùn)練集 #train_set = torchvision.datasets.CIFAR10(root='./data', # 數(shù)據(jù)集存放目錄 # train=True, # 表示是數(shù)據(jù)集中的訓(xùn)練集 # download=True, # 第一次運(yùn)行時(shí)為True,下載數(shù)據(jù)集,下載完成后改為False # transform=transform) # 預(yù)處理過程 # 加載訓(xùn)練集 #train_loader = torch.utils.data.DataLoader(train_set, # 導(dǎo)入的訓(xùn)練集 # batch_size=50, # 每批訓(xùn)練的樣本數(shù) # shuffle=False, # 是否打亂訓(xùn)練集 # num_workers=0) # num_workers在windows下設(shè)置為0 # 獲取圖像數(shù)據(jù)集的路徑 data_root = os.path.abspath(os.path.join(os.getcwd(), "../..")) # get data root path 返回上上層目錄 image_path = data_root + "/jqsj/data_set/flower_data/" # flower data_set path # 導(dǎo)入訓(xùn)練集并進(jìn)行預(yù)處理 train_dataset = datasets.ImageFolder(root=image_path + "/train", transform=data_transform["train"]) train_num = len(train_dataset) # 按batch_size分批次加載訓(xùn)練集 train_loader = torch.utils.data.DataLoader(train_dataset, # 導(dǎo)入的訓(xùn)練集 batch_size=32, # 每批訓(xùn)練的樣本數(shù) shuffle=True, # 是否打亂訓(xùn)練集 num_workers=0) # 使用線程數(shù),在windows下設(shè)置為0 #導(dǎo)入、加載 驗(yàn)證集 # 導(dǎo)入驗(yàn)證集并進(jìn)行預(yù)處理 validate_dataset = datasets.ImageFolder(root=image_path + "/val", transform=data_transform["val"]) val_num = len(validate_dataset) # 加載驗(yàn)證集 validate_loader = torch.utils.data.DataLoader(validate_dataset, # 導(dǎo)入的驗(yàn)證集 batch_size=32, shuffle=True, num_workers=0) # 存儲(chǔ) 索引:標(biāo)簽 的字典 # 字典,類別:索引 {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4} flower_list = train_dataset.class_to_idx # 將 flower_list 中的 key 和 val 調(diào)換位置 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) #訓(xùn)練過程 net = AlexNet(num_classes=5, init_weights=True) # 實(shí)例化網(wǎng)絡(luò)(輸出類型為5,初始化權(quán)重) net.to(device) # 分配網(wǎng)絡(luò)到指定的設(shè)備(GPU/CPU)訓(xùn)練 loss_function = nn.CrossEntropyLoss() # 交叉熵?fù)p失 optimizer = optim.Adam(net.parameters(), lr=0.0002) # 優(yōu)化器(訓(xùn)練參數(shù),學(xué)習(xí)率) save_path = './AlexNet.pth' best_acc = 0.0 for epoch in range(150): ########################################## train ############################################### net.train() # 訓(xùn)練過程中開啟 Dropout running_loss = 0.0 # 每個(gè) epoch 都會(huì)對 running_loss 清零 time_start = time.perf_counter() # 對訓(xùn)練一個(gè) epoch 計(jì)時(shí) for step, data in enumerate(train_loader, start=0): # 遍歷訓(xùn)練集,step從0開始計(jì)算 images, labels = data # 獲取訓(xùn)練集的圖像和標(biāo)簽 optimizer.zero_grad() # 清除歷史梯度 outputs = net(images.to(device)) # 正向傳播 loss = loss_function(outputs, labels.to(device)) # 計(jì)算損失 loss.backward() # 反向傳播 optimizer.step() # 優(yōu)化器更新參數(shù) running_loss += loss.item() # 打印訓(xùn)練進(jìn)度(使訓(xùn)練過程可視化) rate = (step + 1) / len(train_loader) # 當(dāng)前進(jìn)度 = 當(dāng)前step / 訓(xùn)練一輪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() # 驗(yàn)證過程中關(guān)閉 Dropout acc = 0.0 with torch.no_grad(): for val_data in validate_loader: val_images, val_labels = val_data outputs = net(val_images.to(device)) predict_y = torch.max(outputs, dim=1)[1] # 以output中值最大位置對應(yīng)的索引(標(biāo)簽)作為預(yù)測輸出 acc += (predict_y == val_labels.to(device)).sum().item() val_accurate = acc / val_num # 保存準(zhǔn)確率最高的那次網(wǎng)絡(luò)參數(shù) 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')
訓(xùn)練結(jié)果后,準(zhǔn)確率是94%
訓(xùn)練日志如下:
predict.py
import torch
接著對其中一個(gè)花卉圖片進(jìn)行識(shí)別,其結(jié)果如下:
可以看到只有一個(gè)識(shí)別結(jié)果(daisy雛菊)和準(zhǔn)確率1.0是100%(范圍是0~1,所以1對應(yīng)100%)
為了方便使用這個(gè)神經(jīng)網(wǎng)絡(luò),接著我們將其開發(fā)成一個(gè)可視化的界面操作
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')) # 關(guān)閉 Dropout model.eval() ################### from datetime import timedelta # 設(shè)置允許的文件格式 ALLOWED_EXTENSIONS = set(['png', 'jpg', 'JPG', 'PNG', 'bmp']) def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS app = Flask(__name__) # 設(shè)置靜態(tài)文件緩存過期時(shí)間 app.send_file_max_age_default = timedelta(seconds=1) #圖片裝換操作 def tran(img_path): # 預(yù)處理 data_transform = transforms.Compose( [transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) # load image img = Image.open("pgy2.jpg") #plt.imshow(img) # [N, C, H, W] img = data_transform(img) # expand batch dimension img = torch.unsqueeze(img, dim=0) return img @app.route('/upload', methods=['POST', 'GET']) # 添加路由 def upload(): path="" if request.method == 'POST': f = request.files['file'] if not (f and allowed_file(f.filename)): return jsonify({"error": 1001, "msg": "請檢查上傳的圖片類型,僅限于png、PNG、jpg、JPG、bmp"}) basepath = os.path.dirname(__file__) # 當(dāng)前文件所在路徑 path = secure_filename(f.filename) upload_path = os.path.join(basepath, 'static/images', secure_filename(f.filename)) # 注意:沒有的文件夾一定要先創(chuàng)建,不然會(huì)提示沒有該路徑 # upload_path = os.path.join(basepath, 'static/images','test.jpg') #注意:沒有的文件夾一定要先創(chuàng)建,不然會(huì)提示沒有該路徑 print(path) img = tran('static/images'+path) ########################## #預(yù)測圖片 with torch.no_grad(): # predict class output = torch.squeeze(model(img)) # 將輸出壓縮,即壓縮掉 batch 這個(gè)維度 predict = torch.softmax(output, dim=0) predict_cla = torch.argmax(predict).numpy() res = class_indict[str(predict_cla)] pred = predict[predict_cla].item() #print(class_indict[str(predict_cla)], predict[predict_cla].item()) res_chinese = "" if res=="daisy": res_chinese="雛菊" if res=="dandelion": res_chinese="蒲公英" if res=="roses": res_chinese="玫瑰" if res=="sunflower": res_chinese="向日葵" if res=="tulips": res_chinese="郁金香" #print('result:', class_indict[str(predict_class)], 'accuracy:', prediction[predict_class]) ########################## f.save(upload_path) pred = pred*100 return render_template('upload_ok.html', path=path, res_chinese=res_chinese,pred = pred, val1=time.time()) return render_template('upload.html') if __name__ == '__main__': # app.debug = True app.run(host='127.0.0.1', port=80,debug = True)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>李運(yùn)辰-花卉識(shí)別系統(tǒng)v1.0</title> <link rel="stylesheet" type="text/css" href="../static/css/bootstrap.min.css" rel="external nofollow" > <link rel="stylesheet" type="text/css" href="../static/css/fileinput.css" rel="external nofollow" > <script src="../static/js/jquery-2.1.4.min.js"></script> <script src="../static/js/bootstrap.min.js"></script> <script src="../static/js/fileinput.js"></script> <script src="../static/js/locales/zh.js"></script> </head> <body> <h2 align="center">李運(yùn)辰-花卉識(shí)別系統(tǒng)v1.0</h2> <div align="center"> <form action="" enctype='multipart/form-data' method='POST'> <input type="file" name="file" class="file" data-show-preview="false" /> <br> <input type="submit" value="上傳" class="button-new btn btn-primary" /> </form> <p >識(shí)別結(jié)果:{{res_chinese}}</p> </br> <p >準(zhǔn)確率:{{pred}}%</p> <img src="{{ './static/images/'+path }}" width="400" height="400" /> </div> </body> </html>
python main.py
接著在瀏覽器在瀏覽器里面訪問
http://127.0.0.1/upload
出現(xiàn)如下界面:
最后來一個(gè)識(shí)別過程的動(dòng)圖
關(guān)于“怎么用python搭建一個(gè)花卉識(shí)別系統(tǒng)”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),請把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。