如何在一天內構建和部署機器學習web應用程序 — 榴蓮分類

本文我將帶領大家構建一個Web應用程序以對榴蓮進行分類,在這裏(https://durian-classifier.herokuapp.com/) 可以查看相關信息。
如何在一天內構建和部署機器學習web應用程序 — 榴蓮分類
如果你不知道榴蓮是什麼,那我向你說明一下。
它是一種質地乳脂狀、氣味刺鼻(見下圖)和外表刺眼的水果(在新加坡我們稱之爲水果之王),這意味着刺鼻的氣味讓人要麼討厭它,要麼絕對喜歡它(很明顯,我屬於後者)。如果你覺得它聞起來很香,那麼它的味道可能會更好。
如何在一天內構建和部署機器學習web應用程序 — 榴蓮分類
問題陳述
是的,這個項目的動力源於我對榴蓮的熱愛。你一定想知道,我們到底在分類什麼?
你會發現,榴蓮有很多種,它們的味道、質地和顏色各不相同。對於此項目,我們將對四種不同類型的榴蓮進行分類,即:






  • 貓山王
  • 金鳳凰
  • D24
  • 紅蝦
    下表總結了這些榴蓮的不同之處:
    如何在一天內構建和部署機器學習web應用程序 — 榴蓮分類
    榴蓮的品種還有很多,但我認爲這些榴蓮的細微差別可能會讓我們的模型難以學習。
    數據收集
    每個項目都從數據收集開始。由於我們將部署的模型用於個人和教育目的,因此我們將從google獲取圖像,如果你將圖片用於其他用途,請檢查版權。
    我們將使用此API(https://github.com/ultralytics/google-images-download) 來獲取圖像。
    只需按照repo上的說明安裝軟件包。在說明的第3步中,我們將針對特定的用例運行此命令(將路徑替換爲chromedriver):







python3 bing_scraper.py --url 'https://www.bing.com/images/search?q=mao+shan+wang' --limit 100 --download --chromedriver <path_to_chromedriver>


在這裏,我們將下載的圖片數量限制在100張,因爲沒有多少具體的“貓山王”圖片。我們重複以上步驟三次,用其他品種的榴蓮進行搜索。請注意,由於我們在API中修改了搜索URL,查詢中的空格將替換爲“+”(即mao+shan+wang,red+prawn+durian等)。
當然,你可以對任何要分類的圖像執行此步驟。
**數據清理**
在我們的用例中,由於沒有公開的榴蓮圖像,因此下載的許多圖像可能與正確的榴蓮品種不符(例如,在搜索“ mao shan wang”時可能會找到通用的“未標記”榴蓮) )。
因此,我需要手動檢查所有下載的圖像,以確保圖片的質量,畢竟擁有高質量(即正確標記)的數據勝過大數量的數據,對吧?
此步驟確實需要一些領域知識,並且可能會花費一些時間。(但是,數據清理是機器學習管道中的基本步驟,反映了數據科學家和AI工程師的實際情況。)
清除數據後,剩下55張 D24、39張金鳳,59張貓山王和68張紅蝦圖像。
**訓練榴蓮分類器**
我選擇使用TensorFlow框架,我相信大多數實踐者都已經熟練使用了(當然,可以隨意使用Pytorch)。由於我們只有很少的圖像,我們無疑必須使用一個預先訓練好的模型,並在我們的數據集上對其進行微調。
首先,確保你有下面的文件夾結構,這是之後使用 flow_from_directory 所必需的。

train
|-- d24
|-- golden-phoenix
|-- mao-shan-wang
|-- red-prawn
valid
|-- d24
|-- golden-phoenix
|-- mao-shan-wang
|-- red-prawn









讓我們開始構建分類器!

Import relevant libraries we will be using

import numpy as np

from tensorflow.keras.initializers import glorot_uniform
from tensorflow.keras.regularizers import l2
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.applications import Xception
from tensorflow.keras.layers import (
Flatten,
Dense,
AveragePooling2D,
Dropout
)
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.preprocessing import image
from tensorflow.keras import Model
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.callbacks import (
EarlyStopping,
ModelCheckpoint,
LearningRateScheduler
)


















如上所示,我們將使用的基本模型是Xception,讓我們實例化它並添加一些全連接層。因爲我們有很多圖像,所以我們將使用較小的批處理大小8。我們還需要警惕我們的小型數據集過度擬合。

SHAPE = 224
BATCH_SIZE = 8

model = Xception(
input_shape=(SHAPE, SHAPE, 3),
include_top=False,
weights='imagenet'
)



x = model.output
x = AveragePooling2D(pool_size=(2, 2))(x)
x = Dense(32, activation='relu')(x)
x = Dropout(0.1)(x)
x = Flatten()(x)
x = Dense(4, activation='softmax',
kernel_regularizer=l2(.0005))(x)





model = Model(inputs=model.inputs, outputs=x)

opt = SGD(lr=0.0001, momentum=.9)
model.compile(loss='categorical_crossentropy',
optimizer=opt,
metrics=['accuracy'])



在此之後,讓我們使用TensorFlow的ImageDataGenerator及其flow_from_directory創建圖像生成器對象。由於我們沒有足夠的訓練圖像,圖像增強比以往任何時候都更重要。

train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=15,
width_shift_range=0.1,
height_shift_range=0.1,
horizontal_flip=True
)





valid_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=0,
width_shift_range=0.0,
height_shift_range=0.0,
horizontal_flip=False
)





train_generator = train_datagen.flow_from_directory(
'train/',
target_size=(SHAPE, SHAPE),
shuffle=True,
batch_size=BATCH_SIZE,
class_mode='categorical',
)





valid_generator = valid_datagen.flow_from_directory(
'valid/',
target_size=(SHAPE, SHAPE),
shuffle=True,
batch_size=BATCH_SIZE,
class_mode='categorical',
)





Found 178 images belonging to 4 classes.
Found 42 images belonging to 4 classes.

讓我們在.fit()即我們的模型之前定義一些回調函數。


earlystop = EarlyStopping(monitor='val_loss',
                          patience=4,
                          verbose=1)

checkpoint = ModelCheckpoint(
    "model-weights/xception_checkpoint.h5",
    monitor="val_loss",
    mode="min",
    save_best_only=True,
    verbose=1
)

我們的模型終於開始訓練了!


history = model.fit_generator(
    train_generator,
    epochs=30,
    callbacks=[earlystop, checkpoint],
    validation_data=valid_generator
)

# Save our model for inference
model.save("model-weights/xception.h5")

不幸的是,由於我們擁有的圖像數量有限,我們的模型在驗證集上無法獲得非常好的準確性,但是,模型微調並不是本文的重點,因此我們不會對此進行過多介紹。
選擇我們的Web框架
在這個項目中,我選擇使用streamlit(https://www.streamlit.io/) ,因爲它可以實現機器學習應用程序的超快速可視化,並且科可以方便地用Python編寫。建立好這些之後,剩下要做的就是部署它。
首先,導入所需的庫並指定模型權重的路徑,同樣由於我們使用了flow_from_directory ,TensorFlow按字母順序分配類編號,因此,D24將爲0類,依此類推。



import numpy as np

from PIL import Image
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.preprocessing import image
import streamlit as st

PATH = "model-weights/"
WEIGHTS = "xception.h5"
CLASS_DICT = {
    0: 'D24',
    1: 'JIN FENG',
    2: 'MAO SHAN WANG',
    3: 'RED PRAWN'
}

接下來,我們創建一個函數,將上傳的圖像轉換爲模型要使用的格式。我們使用PIL中的Image類,因爲上傳的圖像是BytesIO格式的。


def load_img(input_image, shape):
    img = Image.open(input_image).convert('RGB')
    img = img.resize((shape, shape))
    img = image.img_to_array(img)
    return np.reshape(img, [1, shape, shape, 3])/255

Streamlit的工作方式是,用戶指定參數的每一次更改,腳本都會從上到下重新運行(因此它是交互式的UI),因此它以st.cache形式提供了一個緩存裝飾器來緩存加載的對象。
緩存通常用於數據加載步驟或任何需要長時間計算/處理的步驟。請記住,我們使用allow_output_variation=True參數,因爲默認情況下這是False,如果輸出對象以任何方式發生了變化,則應用程序將被重新加載。
在我們的例子中,模型對象將在每次預測中發生變化,因此我們將 allow_output_variation的參數設置爲True。我們之所以要緩存我們的模型是因爲我們不希望每次用戶選擇不同的圖像時都加載它(即只加載一次模型)。


@st.cache(allow_output_mutation=True)
def load_own_model(weights):
    return load_model(weights)

最後,我們只需要向UI添加一些代碼即可:


if __name__ == "__main__":
    result = st.empty()
    uploaded_img = st.file_uploader(label='upload your image:')
    if uploaded_img:
        st.image(uploaded_img, caption="your sexy durian pic",
                 width=350)
        result.info("please wait for your results")
        model = load_own_model(PATH + WEIGHTS)
        pred_img = load_img(uploaded_img, 224)
        pred = CLASS_DICT[np.argmax(model.predict(pred_img))]
        result.success("The breed of durian is " + pred)

我們用Python創建的web應用程序不需要太多的代碼行。你可以確保它(假設它會被調用應用程序副本)可以通過在命令行中輸入以下命令在本地運行:


streamlit run app.py

將我們的模型部署到Heroku
就我個人而言,部署不是我最喜歡的部分,但是,如果web應用程序不在web上,那它還有什麼意義呢?我們開始吧。
你可以通過多種方式爲web應用程序提供服務,也可以使用許多雲服務提供商來託管它。在這種情況下,我選擇使用Heroku主要是因爲我以前沒有嘗試過。
什麼是Heroku?
Heroku是一個雲平臺即服務(PaaS),支持多種編程語言,允許開發人員完全在雲中構建、運行和操作應用程序。下面這篇文章解釋得很清楚。



  • 文章鏈接:https://devcenter.heroku.com/articles/how-heroku-works
    在Heroku部署
    爲了部署應用程序,我們總是需要某種版本控制,以確保我們的應用程序運行在一個不同的服務器上,而不是在本地計算機上。爲此,許多人使用Docker容器,指定所需的可運行應用程序和包。
    使用Heroku進行部署類似於同時使用Docker容器和web託管服務,但是,它使用Git作爲部署應用程序的主要手段。我們不需要將所有必需的文件打包到Docker容器中,而是創建一個用於版本控制的git存儲庫,然後我們可以使用熟悉的git push,但是要用到heroku遠程。
    Heroku隨後使用了相同的容器技術,以dyno的形式進行。每個應用程序都放在一個dyno(或容器)中,每個應用程序都消耗“dyno hours”。
    每個Heroku帳戶都有一些可用的空閒小時數,消耗的小時數取決於應用程序的活動/流量。如果你的應用程序不需要大量流量,那麼免費套餐應已足夠了。
    另外值得注意的是,當Heroku接收到應用程序源時,它會啓動應用程序的構建(例如在requirements.txt創建必要的資產等),被組裝成一個slug。
    術語解釋:slug是源代碼、獲取的依賴項、語言運行時和編譯生成的系統輸出的捆綁包—爲執行做準備。
    要在Heroku上部署,我們需要以下文件:
    (1)setup.sh








  • 創建必要的目錄並將一些信息(例如端口號)寫入.toml文件
mkdir -p ~/.streamlit/

echo "\
[server]\n\
headless = true\n\
port = $PORT\n\
enableCORS = false\n\
\n\
" > ~/.streamlit/config.toml

(2) Procfile

  • 類似於Dockerfile,包含我們要執行的指令。我們將首先在setup.sh中執行一些bash命令,然後執行streamlit run app.py命令。

web: sh setup.sh && streamlit run app.py


(3) requirements.txt
* 包含應用程序所需的所有包依賴項。請注意,這些是我正在使用的版本。你可以通過終端中的conda list或使用pip freeze > requirements.txt獲取環境當前使用的軟件包的詳盡列表。

numpy==1.18.1
spacy==2.2.4
pandas==1.0.1
Pillow==7.1.2
streamlit==0.61.0
tensorflow-cpu==2.2.0





我們的文件夾目錄應如下所示:

app.py
Procfile
README.md
requirements.txt
setup.sh
model-weights
|-- xception.h5






如果你以前從未創建過Github存儲庫,請按照以下一些簡單步驟進行操作:
創建一個新的存儲庫<repo_name>
![](https://s4.51cto.com/images/blog/202101/06/a230ab50e7e1e4160899f5f5ae45edac.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)
複製紅色框中的URL
![](https://s4.51cto.com/images/blog/202101/06/5b94c2a58ff6765d60da401acf569732.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)
在終端上,運行以下命令:

Clone the repository into our local machine

git clone <repo URL in step 2>

Enter the directory we just cloned

cd <repo_name>


將之前創建的文件複製到此文件夾中,然後在終端中運行以下命令:

Add all the files we just copied over to be committed

git add .

Commit the files, along with a commit message

git commit -m "deploy app"

Push to master branch on our github repo

git push origin master



我們就快到了!這是最後的步驟。
(1)創建一個Heroku帳戶並進行驗證
(2)在此處(https://devcenter.heroku.com/articles/heroku-cli) 安裝Heroku CLI
(3)通過終端登錄到你的Heroku帳戶。將打開一個瀏覽器窗口,供你進行身份驗證。
heroku login
(4)創建一個Heroku應用
heroku create <project-name>
完成此步驟後,你將能夠在終端中看到指向你的項目的鏈接。
(5)將git repo推送到Heroku遙控器。在我們的github存儲庫的同一目錄中,運行以下命令:
git push heroku master
我們完成了!構建完成後,你應該能夠在上面的鏈接中看到部署的應用程序!
參考鏈接:https://towardsdatascience.com/how-to-build-and-deploy-a-machine-learning-web-application-in-a-day-f194fdbd4a5f
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章