使用Flask部署影象分類模型
作者|LAKSHAY ARORA
編譯|VK
來源|Analytics Vidhya
概述
-
瞭解PyTorch和Flask的概況
-
學習在PyTorch中建立影象分類模型
-
瞭解如何使用Flask部署模型。
介紹
當涉及到社交媒體的健康執行時,影象分類是一個關鍵點。根據特定標籤對內容進行分類可以代替各種法律法規。它變得很重要,以便對特定的受眾群體隱藏內容。
當我在Instagram上瀏覽時,我經常會遇到一些圖片上有“敏感內容”的帖子。我肯定你也有。
任何有關人道主義危機、恐怖主義或暴力的圖片通常被歸類為“敏感內容”。Instagram如何對圖片進行分類一直讓我很感興趣。這種不斷的好奇心促使我去理解影象分類的過程。
大部分影象是由Instagram部署的影象分類模型檢測出來的。此外,還有一個基於社群的反饋迴圈。這是影象分類最重要的用例之一。
在本文中,我們將部署一個影象分類模型來檢測影象的類別。
目錄
-
什麼是模型部署?
-
PyTorch簡介
-
什麼是Flask?
-
在機器上安裝Flask和PyTorch
-
理解問題陳述
-
建立預訓練的影象分類模型
-
建立一個影象Scraper
-
建立網頁
-
設定Flask專案
-
部署模型的工作
什麼是模型部署
在典型的機器學習和深度學習專案中,我們通常從定義問題陳述開始,然後是資料收集和準備,然後是模型構建,對嗎?
一旦我們成功地構建和訓練了模型,我們希望它能為終端使用者所用。
因此,我們必須“部署”模型,以便終端使用者可以使用它。模型部署是任何機器學習或深度學習專案的後期階段之一。
在本文中,我們將在PyTorch中構建一個分類模型,然後學習如何使用Flask部署相同的模型。在我們進入細節之前,讓我們先簡單介紹一下PyTorch。
PyTorch簡介
PyTorch是一個基於python的庫,它提供了作為深度學習開發平臺的靈活性。PyTorch的工作流程與python的科學計算庫NumPy非常接近。
PyTorch被廣泛用於構建深度學習模型。以下是PyTorch的一些重要優勢
- 易於使用的API–PyTorch API與python一樣簡單。
- Python支援—PyTorch與Python完美整合。
- 動態計算圖——PyTorch為我們提供了一個框架來構建計算圖,甚至在執行時改變它們。這對於我們不知道建立一個神經網路需要多少記憶體的情況很有價值。
在接下來的章節中,我們將使用一個預訓練的模型來使用PyTorch來檢測影象的類別。接下來,我們將使用Flask進行模型部署。在下一節中,我們將簡要討論Flask。
什麼是Flask?
Flask是一個用Python編寫的web應用程式框架。它有多個模組,使web開發人員更容易編寫應用程式,而不必擔心協議管理、執行緒管理等細節。
Flask為開發web應用程式提供了多種選擇,併為我們提供了構建web應用程式所需的工具和庫。
在機器上安裝Flask和PyTorch
安裝Flask簡單明瞭。這裡,我假設你已經安裝了python3和pip。要安裝Flask,需要執行以下命令:
sudo apt-get install python3-flask
接下來,我們需要安裝PyTorch。執行本文中提供的程式碼不需要有GPU。
!pip install torch torchvision
就這樣!現在讓我們開始一個問題陳述並建立一個模型。
理解問題陳述
讓我們討論一下問題陳述,我們想要建立一個包含如下文字框的網頁(如下所示)。使用者在這裡輸入網址。
這裡的任務是從URL中抓取所有影象。對於每個影象,我們將使用影象分類模型預測影象的類別或類別,並在網頁上按類別呈現影象。
下面是端到端模型的工作流-
設定專案工作流
- 模型構建:我們將使用預訓練的模型Densenet 121來預測影象類。它可以在PyTorch的torchvision庫中找到。這裡,我們的重點不是從頭開始構建一個高度精確的分類模型,而是看看如何部署該模型並在web介面中使用它。
- 建立一個影象Scraper:我們將使用請求和BeautifulSoup庫建立一個web scraper。它將從一個URL下載所有的影象並將其儲存,這樣我們就可以對其進行預測。
- 設計網頁模板:我們還將設計一個使用者介面,使用者可以提交一個網址,也可以得到結果,一旦計算。
- 對影象進行分類併發送結果:一旦我們從使用者那裡得到查詢,我們將使用該模型預測影象的類別並將結果傳送給使用者。
下面是我們剛剛看到的步驟的一個表示:
讓我們討論一下專案所需的所有組成部分:
建立預訓練的影象分類模型
我們將使用預訓練的模型Densenet 121對影象進行分類。
你可以在這裡下載完整的程式碼和資料集。
連結:https://github.com/lakshay-arora/Densenet121-Image-Classification-Deployed-using-Flask
讓我們從匯入一些必需的庫開始,並從torchvision庫獲取densenet121模型。確保將引數“pretrained”新增為True。
# 匯入所需的庫
import json
import io
import glob
from PIL import Image
from torchvision import models
import torchvision.transforms as transforms
# 將引數“pretraining”傳遞為“True”,使用預訓練的權重:
model = models.densenet121(pretrained=True)
# 切換到模型到“eval”模式:
model.eval()
現在,我們將定義一個函式來轉換影象。它將建立一個轉換管道並根據需要轉換影象。此方法以位元組為單位獲取影象資料,並對其應用一系列“轉換”函式並返回張量。這段程式碼取自pytorch文件。
# 定義預處理的函式
def transform_image(image_bytes):
my_transforms = transforms.Compose([transforms.Resize(255),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(
[0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])])
image = Image.open(io.BytesIO(image_bytes))
return my_transforms(image).unsqueeze(0)
現在,預訓練的模型返回預測類id的索引。PyTorch已經為它提供了對映,以便我們可以看到預測類的名稱。你可以在這裡下載地圖。它有1000個不同的類別。
# 載入由pytorch提供的對映
imagenet_class_mapping = json.load(open('imagenet_class_index.json'))
下面是一個示例:
接下來,我們將定義一個函式來獲取影象的類別。為此,我們將影象的路徑作為唯一的引數傳遞。
首先,它將開啟並讀取二進位制格式的影象,然後對其進行轉換。然後將變換後的影象傳遞給模型,得到預測類。它將使用對映並返回類名。
# 定義函式來獲得圖片的預測
# 它接受引數:圖片路徑並提供預測作為輸出
def get_category(image_path):
#以二進位制形式讀取影象
with open(image_path, 'rb') as file:
image_bytes = file.read()
# 變換影象
transformed_image = transform_image(image_bytes=image_bytes)
# 使用模型來預測類
outputs = model.forward(transformed_image)
_, category = outputs.max(1)
# 返回
predicted_idx = str(category.item())
return imagenet_class_mapping[predicted_idx]
讓我們在一些影象上嘗試此函式:
get_category(image_path='static/sample_1.jpeg')
## ['n02089973', 'English_foxhound']
get_category(image_path='static/sample_2.jpeg')
## ['n11939491', 'daisy']
現在,我們的模型可以預測影象的類。讓我們從構建影象Scraper開始。
建立一個影象Scraper
在本節中,我們將構建一個web scraper,它將從提供的URL下載影象。我們將使用BeautifulSoup庫下載影象。你可以自由使用任何其他庫或API來提供影象。
我們將從匯入一些必需的庫開始。對於我們將抓取的每個url,將建立一個新目錄來儲存影象。我們將建立一個函式get_path,它將返回為該URL建立的資料夾的路徑。
# 匯入所需的庫
import requests
from bs4 import BeautifulSoup
import os
import time
def get_path(url):
return "static/URL_" + str(url.replace("/","_"))
headers = {
'User-Agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"
}
現在,我們將定義一個函式get_images。它將首先使用get_path函式建立目錄,然後傳送對原始碼的請求。從原始碼中,我們將使用“img”標籤提取原始碼。
在此之後,我們將只選擇jpeg格式的影象。也可以新增png格式的影象。我已經過濾掉了,因為大多數png格式的圖片都是logo。
最後,啟動計數器並將帶有計數器名稱的影象儲存到指定的目錄中。
# 定義爬取影象並將其儲存在目錄中的函式
def get_images(url):
# get the directory path
path = get_path(url)
try:
os.mkdir(path)
except:
pass
# 從URL請求原始碼
response = requests.request("GET", url, headers=headers)
# 通過Beautiful Soup解析資料
data = BeautifulSoup(response.text, 'html.parser')
# 在原始碼中找到影象標記
images = data.find_all('img', src=True)
# 從所有的影象標籤中提取src
image_src = [x['src'] for x in images]
# 只選擇jpeg
image_src = [x for x in image_src if x.endswith('.jpeg') ]
image_count = 1
# 在指定目錄儲存影象
for image in image_src:
print(image)
image_file_name = path+'/'+str(image_count)+'.jpeg'
print(image_file_name)
# 以寫入二進位制形式開啟檔案並新增影象內容來儲存它
with open(image_file_name, 'wb') as f:
res = requests.get(image)
f.write(res.content)
image_count = image_count+1
讓我們試試我們剛剛創造的scraper!
get_images('https://medium.com/@allanishac/9-wild-animals-that-would-make-a-much-better-president-than-donald-trump-b41f960bb171')
現在,建立了一個新目錄,並檢視它的外觀。我們在一個地方下載了所有的圖片。
注意:建議僅根據學習目的使用此影象Scraper。始終遵循目標網站的robots.txt檔案,也稱為機器人排除協議。這會告訴網路機器人哪些頁面不能爬。
建立網頁
我們將建立兩個網頁一個是“home.html另一個是“image_class.html”.
-
“home.html“是預設的,它將有一個文字框,使用者可以在其中鍵入URL。
-
“image_class.html“將幫助我們按類別渲染影象。
1.home.html
我們需要在home.html檔案以收集搜尋容器中的資料。在form標籤中,我們將使用post方法,並且資料通過名為“search”的輸入欄傳遞。
通過這樣做,我們的後端程式碼將能夠知道我們收到了一些名為“search”的資料。在後端,我們需要處理併發送資料。
2.image_class.html
在計算結果時,另一個頁面將呈現如下結果。本頁“image_class.html“將在每次查詢時更新。你可以看到我們在網頁上顯示了以下資訊:
-
影象類別
-
影象
-
所有可用影象類別的頻率計數
下面是執行此操作的程式碼:
def get_picture_html(path, tag):
image_html = """<p> {tag_name} </p> <picture> <img src= "../{path_name}" height="300" width="400"> </picture>"""
return image_html.format(tag_name=tag, path_name=path)
# 定義在html檔案中新增列表元素的函式
def get_count_html(category, count):
count_html = """<li> {category_name} : {count_} </li>"""
return count_html.format(category_name = category, count_ = count)
# 計數
def get_value_count(image_class_dict):
count_dic = {}
for category in image_class_dict.values():
if category in count_dic.keys():
count_dic[category] = count_dic[category]+1
else:
count_dic[category] = 1
return count_dic
# 函式從image_class字典生成html檔案
# 鍵將是影象的路徑,而值將是與之關聯的類。
def generate_html(image_class_dict):
picture_html = ""
count_html = ""
# 迴圈這些鍵並將影象新增到html檔案中
for image in image_class_dict.keys():
picture_html += get_picture_html(path=image, tag= image_class_dict[image])
value_counts = get_value_count(image_class_dict)
# 迴圈value_counts並向html檔案中新增類的計數
for value in value_counts.keys():
count_html += get_count_html(value, value_counts[value])
下一步是建立Flask專案,將這些單獨的部分組合起來解決這個挑戰。
設定Flask專案
我們在專案中完成了以下任務:
-
影象分類模型工作良好,能夠對影象進行分類。
-
我們已經建立了影象Scraper,將下載影象並存儲它們。
-
我們已經建立了網頁來獲取並返回結果。
現在我們需要將所有這些檔案連線在一起,這樣我們就可以有一個工作專案了。
讓我們看看目錄結構。
注意:請確保將影象儲存在static資料夾和html 檔案放在templates資料夾中。Flask只會查詢這些名字。如果你改變這些,你會得到一個錯誤。
執行Flask應用程式
Flask應用程式首先將home.html當有人傳送影象分類請求時,Flask將檢測一個post方法並呼叫get_image_class函式。
此函式將按以下步驟工作:
-
首先,它將傳送一個請求來下載並存儲這些影象。
-
接下來,它將把目錄路徑傳送到get_prediction.py將計算並以字典形式返回結果的檔案。
-
最後,它將把這個字典傳送給generate_html.py,使用者將返回生成該檔案的輸出。
# 匯入庫
from flask import Flask, render_template, request, redirect, url_for
from get_images import get_images, get_path, get_directory
from get_prediction import get_prediction
from generate_html import generate_html
from torchvision import models
import json
app = Flask(__name__)
# 對映
imagenet_class_mapping = json.load(open('imagenet_class_index.json'))
# 使用預訓練模型
model = models.densenet121(pretrained=True)
model.eval()
# 定義從url獲取影象並預測類的函式
def get_image_class(path):
# 從URL獲取影象並將其儲存在給定的路徑中
get_images(path)
# 根據所提供的目錄預測影象的影象類別
path = get_path(path)
images_with_tags = get_prediction(model, imagenet_class_mapping, path)
# 生成html檔案以在我們預測類之後呈現
generate_html(images_with_tags)
一旦以上步驟完成,我們就可以為使用者提供結果。我們將呼叫success函式,該函式將渲染image_class.html檔案。
# 根頁面為"home.html"
@app.route('/')
def home():
return render_template('home.html')
@app.route('/', methods=['POST', 'GET'])
def get_data():
if request.method == 'POST':
user = request.form['search']
# 如果搜尋按鈕被點選,呼叫函式get_image_class
get_image_class(user)
#返回image_class.html
return redirect(url_for('success', name=get_directory(user)))
@app.route('/success/<name>')
def success(name):
return render_template('image_class.html')
if __name__ == '__main__' :
app.run(debug=True)
獲取源URL的所有影象的預測
到目前為止,我們已經分別對每幅影象進行了預測。現在,我們將用新引數修改get_category函式來解決這個問題。我們將傳遞包含多個影象檔案的目錄路徑。
現在,我們將定義另一個函式get_prediction,它將使用get_category函式並返回字典,其中鍵將是影象路徑,值將是影象類。
稍後,我們將把這個字典傳送給generate_html.py將為我們建立HTML檔案的檔案。
# 獲取目錄中出現的所有影象的類
def get_category(model, imagenet_class_mapping, image_path):
with open(image_path, 'rb') as file:
image_bytes = file.read()
transformed_image = transform_image(image_bytes=image_bytes)
outputs = model.forward(transformed_image)
_, category = outputs.max(1)
predicted_idx = str(category.item())
return imagenet_class_mapping[predicted_idx]
# 它將建立一個影象路徑和預測類的字典
# 我們將使用該字典生成html檔案。
def get_prediction(model, imagenet_class_mapping, path_to_directory):
files = glob.glob(path_to_directory+'/*')
image_with_tags = {}
for image_file in files:
image_with_tags[image_file] = get_category(model, imagenet_class_mapping, image_path=image_file)[1]
return image_with_tags
現在,所有的程式碼檔案都準備好了,我們只需要將它們與主檔案連線起來。
首先,建立一個Flask類的物件,該物件將以當前模組的名稱作為引數。route函式將告訴Flask應用程式下一步在網頁上呈現哪個URL。
部署模型的工作
你可以在這裡下載完整的程式碼和資料集。
連結:https://github.com/lakshay-arora/Densenet121-Image-Classification-Deployed-using-Flask
現在,我們執行get_class.py,Flask伺服器就可以在 localhost:5000啟動
開啟web瀏覽器並轉到localhost:5000,你將看到預設主頁在那裡呈現。現在,在文字框中輸入任何URL並按search按鈕。這可能需要20-30秒,這取決於網址中的圖片數量和網速。
讓我們看看部署模型的工作情況。
視訊:https://cdn.analyticsvidhya.com/wp-content/uploads/2020/07/pytorch-deployment-2020-07-04-11.mp4
結尾
在本文中,我簡要地解釋了模型部署、Pytorch和Flask的概念。
然後我們深入瞭解了使用PyTorch建立影象分類模型並將其與Flask一起部署的過程中涉及的各個步驟。我希望這有助於你構建和部署影象分類模型。
另外,模型被部署在本地主機上。我們也可以把它部署在雲服務上,比如Google Cloud,Amazon,github.io等等,我們也將在下一篇文章中討論這一點。
原文連結:https://www.analyticsvidhya.com/blog/2020/07/deploy-an-image-classification-model-using-flask/
歡迎關注磐創AI部落格站:
http://panchuang.net/
sklearn機器學習中文官方文件:
http://sklearn123.com/
歡迎關注磐創部落格資源彙總站:
http://docs.panchuang.net/