<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Python實(shí)戰(zhàn) | 基于 Flask 部署 Keras 深度學(xué)習(xí)模型

          共 12751字,需瀏覽 26分鐘

           ·

          2021-06-03 23:49

          文 | 風(fēng)玲兒  出處 | 掘金

          本文主要記錄在進(jìn)行Flask部署過(guò)程中所使用的流程,遇到的問(wèn)題以及相應(yīng)的解決方案。

          1、項(xiàng)目簡(jiǎn)介

          該部分簡(jiǎn)要介紹一下前一段時(shí)間所做的工作:

          • 基于深度學(xué)習(xí)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的圖像分類問(wèn)題
          • 借助 flask 框架將其部署到 web 應(yīng)用中
          • 并發(fā)要求較高

          這是第一次進(jìn)行深度學(xué)習(xí)模型的 web 應(yīng)用部署,在整個(gè)過(guò)程中,進(jìn)一步折射出以前知識(shí)面之窄,在不斷的入坑、解坑中實(shí)現(xiàn)一版。

          2、項(xiàng)目流程

          這部分從項(xiàng)目實(shí)施的流程入手,記錄所做的工作及用到的工具。

          2.1 圖像分類模型

          1. 模型的選擇

          需要進(jìn)行圖像分類,第一反應(yīng)是利用較為成熟與經(jīng)典的分類網(wǎng)絡(luò)結(jié)構(gòu),如 VGG 系列(VGG16, VGG19),ResNet 系列(如ResNet50),InceptionV3等。

          考慮到是對(duì)未知類型的圖像進(jìn)行分類,且沒(méi)有直接可用的訓(xùn)練數(shù)據(jù),因此使用在Imagenet上訓(xùn)練好的預(yù)訓(xùn)練模型,基本滿足要求。

          如果對(duì)性能(耗時(shí))要求較為嚴(yán)格,則建議使用深度較淺的網(wǎng)絡(luò)結(jié)構(gòu),如VGG16, MobileNet等。

          其中,MobileNet網(wǎng)絡(luò)是為移動(dòng)端和嵌入式端深度學(xué)習(xí)應(yīng)用設(shè)計(jì)的網(wǎng)絡(luò),使得在 cpu 上也能達(dá)到理想的速度要求。是一種輕量級(jí)的深度網(wǎng)絡(luò)結(jié)構(gòu)。

          MobileNetGoogle 團(tuán)隊(duì)提出,發(fā)表于 CVPR-2017,論文標(biāo)題:《MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications》

          2. 框架選擇

          • 平時(shí)使用Keras框架比較多,Keras底層庫(kù)使用TheanoTensorflow,也稱為 Keras 的后端。Keras是在Tensorflow基礎(chǔ)上構(gòu)建的高層 API,比Tensorflow更容易上手。

          • 上述提到的分類網(wǎng)絡(luò),在Keras中基本已經(jīng)實(shí)現(xiàn),Keras 中已經(jīng)實(shí)現(xiàn)的網(wǎng)絡(luò)結(jié)構(gòu)如下所示:

          • 使用方便,直接導(dǎo)入即可,如下:

          因此,選擇 Keras 作為深度學(xué)習(xí)框架。

          3. 代碼示例

          Keras框架,VGG16網(wǎng)絡(luò)為例,進(jìn)行圖像分類。

          from keras.models import Model
          from keras.applications.vgg16 import VGG16, preprocess_input
          import keras.backend.tensorflow_backend as KTF
          import tensorflow as tf
          os.environ["CUDA_VISIBLE_DEVICES"] = "0,1" #使用GPU
          # 按需占用GPU顯存
          gpu_options = tf.GPUOptions(allow_growth=True)
          sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options))
          KTF.set_session(sess)

          # 構(gòu)建model
          base_model = VGG16(weights=‘imagenet’, include_top=True)
          model = Model(inputs=base_model.input,
          outputs=base_model.get_layer(layer).output) # 獲取指定層的輸出值,layer為層名

          # 進(jìn)行預(yù)測(cè)
          img = load_image(img_name, target_size=(224, 224)) # 加載圖片并resize成224x224

          # 圖像預(yù)處理
          x = image.img_to_array(img)
          x = np.expand_dims(x, axis=0)
          x = preprocess_input(x)

          feature = model.predict(x) # 提取特征

          2.2 模型性能測(cè)試

          將分類模型跑通后,我們需要測(cè)試他們的性能,如耗時(shí)、CPU 占用率、內(nèi)存占用以及 GPU 顯存占用率等。

          1. 耗時(shí)

          耗時(shí)是為了測(cè)試圖像進(jìn)行分類特征提取時(shí)所用的時(shí)間,包括圖像預(yù)處理時(shí)間和模型預(yù)測(cè)時(shí)間的總和。

          # 使用python中的time模塊
          import time
          t0 = time.time()
          ....
          圖像處理和特征提取
          ....

          print(time.time()-t0) #耗時(shí),以秒為單位

          2. GPU 顯存占用

          使用英偉達(dá)命令行nvidia-smi可以查看顯存占用。

          3. CPU, MEM 占用

          使用top命令或htop命令查看 CPU 占用率以及內(nèi)存占用率。

          內(nèi)存占用還可以使用free命令來(lái)查看:

          • free -h : 加上-h選項(xiàng),輸出結(jié)果較為友好,會(huì)給出合適單位

          • 需要持續(xù)觀察內(nèi)存狀況時(shí),可以使用-s選項(xiàng)指定間隔的秒數(shù):free -h -s 3(每隔 3 秒更新一次,停止更新時(shí)按下Ctrl+c

          Ubuntu 16.04版本中默認(rèn)的free版本有 bug,使用-s選項(xiàng)時(shí)會(huì)報(bào)錯(cuò)。

          根據(jù)以上三個(gè)測(cè)試結(jié)果適時(shí)調(diào)整所采用的網(wǎng)絡(luò)結(jié)構(gòu)及顯存占用選項(xiàng)。

          命令具體含義可參考博文:

          Linux 查看 CPU 和內(nèi)存使用情況[1]

          2.3 Redis 使用

          Redis=Remote DIctionary Server,是一個(gè)由 Salvatore Sanfilippo 寫的高性能的key-value存儲(chǔ)系統(tǒng)。Redis 是一個(gè)開(kāi)源的使用 ANSI C 語(yǔ)言編寫、遵守 BSD 協(xié)議、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日?qǐng)?zhí)行、key-value 數(shù)據(jù)庫(kù),并提供多種語(yǔ)言的 API。

          Redis支持存儲(chǔ)的類型有string, list, set, zsethash,在處理大規(guī)模數(shù)據(jù)讀寫的場(chǎng)景下運(yùn)用比較多。

          1. 基本使用

          安裝 redis

          pip install redis

          # 測(cè)試
          import redis

          基本介紹

          redis.py提供了兩個(gè)類:RedisStrictRedis用于實(shí)現(xiàn)Redis的命令 StrictRedis用于實(shí)現(xiàn)大部分官方命令,并使用官方的語(yǔ)法和命令 RedisStrictRedis的子類,用于向前兼容redis.py 一般情況下我們就是用StrictRedis

          使用示例

          # 1. 導(dǎo)入redis
          from redis import StrictRedis

          # 2. 連接數(shù)據(jù)庫(kù),指定host,端口號(hào),數(shù)據(jù)庫(kù)
          r = StrictRedis(host=‘localhost’, port=6379, db=2)

          # 3. 存儲(chǔ)到redis中
          r.set('test1', 'value1') # 單個(gè)數(shù)據(jù)存儲(chǔ)
          r.set('test2', 'value2')

          # 4. 從redis中獲取值
          r.get('test1')

          # 5. 批量操作
          r.mset(k1='v1', k2='v2')
          r.mset({'k1':'v1', 'k2':'v2'})
          r.mget('k1', 'k2')
          r.mget(['k1', 'k2'])

          2. Redis 存儲(chǔ)數(shù)組

          Redis 是不可以直接存儲(chǔ)數(shù)組的,如果直接存儲(chǔ)數(shù)組類型的數(shù)值,則獲取后的數(shù)值類型發(fā)生變化,如下,存入 numpy 數(shù)組類型,獲取后的類型是bytes類型。

          import numpy as np
          from redis import StrictRedis

          r = StrictRedis(host=‘localhost’, port=6379, db=2)
          x1 = np.array(([0.2,0.1,0.6],[10.2,4.2,0.9]))
          r.set('test1', x1)
          >>> True
          r.get('test1')
          >>> b'[[ 0.2 0.1 0.6]\n [10.2 4.2 0.9]]'
          type(r.get('test1')) #獲取后的數(shù)據(jù)類型
          >>> <class 'bytes'>

          為了保持?jǐn)?shù)據(jù)存儲(chǔ)前后類型一致,在存儲(chǔ)數(shù)組之前將其序列化,獲取數(shù)組的時(shí)候?qū)⑵浞葱蛄谢纯伞?/p>

          借助于 python 的pickle模塊進(jìn)行序列化操作。

          import pickle
          r.set('test2', pickle.dumps(x1))
          >>> True
          pickle.loads(r.get('test2'))
          >>> array([[ 0.2, 0.1, 0.6],
          [10.2, 4.2, 0.9]])

          這樣,就可以保持?jǐn)?shù)據(jù)存入前和取出后的類型一致。

          2.4 web 開(kāi)發(fā)框架——Flask

          之前學(xué)習(xí) python 語(yǔ)言,從來(lái)沒(méi)有關(guān)注過(guò)Web開(kāi)發(fā)這一章節(jié),因?yàn)楣ぷ鲀?nèi)容并沒(méi)有涉及這一部分。如今需要重新看一下。

          早期軟件主要運(yùn)行在桌面上,數(shù)據(jù)庫(kù)這樣的軟件運(yùn)行在服務(wù)器端,這種Client/Server模式簡(jiǎn)稱CS架構(gòu)。隨著互聯(lián)網(wǎng)的興起,CS架構(gòu)不適合Web,最大原因是 Web 應(yīng)用程序的修改和升級(jí)非常頻繁,CS架構(gòu)需要每個(gè)客戶端逐個(gè)升級(jí)桌面 App,因此,Browser/Server模式開(kāi)始流行,簡(jiǎn)稱BS架構(gòu)

          BS架構(gòu)下,客戶端只需要瀏覽器,應(yīng)用程序的邏輯和數(shù)據(jù)存儲(chǔ)在服務(wù)器端,瀏覽器只需要請(qǐng)求服務(wù)器,獲取 Web 頁(yè)面,并把 Web 頁(yè)面展示給用戶即可。當(dāng)前,Web 頁(yè)面也具有極強(qiáng)的交互性。

          Python 的誕生歷史比 Web 還要早,由于 Python 是一種解釋型的腳本語(yǔ)言,開(kāi)發(fā)效率高,所以非常適合用來(lái)做 Web 開(kāi)發(fā)。

          Python 有上百個(gè)開(kāi)源的 Web 框架,比較熟知的有Flask, Django。接下來(lái)以Flask為例,介紹如何利用 Flask 進(jìn)行 web 部署。

          關(guān)于 web 開(kāi)發(fā)框架的介紹,可以參考下面這篇博文:三個(gè)目前最火的 Python Web 開(kāi)發(fā)框架,你值得擁有![2]

          有關(guān)Flask的具體用法可參考其他博文,這方面的資料比較全。下面主要以具體使用示例來(lái)說(shuō)明:

          1. 安裝使用

          1. 安裝 Flask

            pip install flask

            import flask # 導(dǎo)入
            flask.__version__ # 版本

            >>> '1.1.1' #當(dāng)前版本
          2. 一個(gè)簡(jiǎn)單的 Flask 示例

            Flask 使用 Python 的裝飾器在內(nèi)部自動(dòng)的把URL和函數(shù)給關(guān)聯(lián)起來(lái)。

            # hello.py
            from flask import Flask, request

            app = Flask(__name__) #創(chuàng)建Flask類的實(shí)例,第一個(gè)參數(shù)是模塊或者包的名稱
            app.config['JSON_AS_ASCII']=False # 支持中文顯示

            @app.route('/', methods=['GET', 'POST']) # 使用methods參數(shù)處理不同HTTP方法
            def home():
            return 'Hello, Flask'

            if __name__ == '__main__':
            app.run()

            運(yùn)行該文件,會(huì)提示* Running on http://127.0.0.1:5000/,在瀏覽器中打開(kāi)此網(wǎng)址,會(huì)自動(dòng)調(diào)用home函數(shù),返回Hello, Flask,則在瀏覽器頁(yè)面上就會(huì)看到Hello, Flask字樣。

            app.run 的參數(shù)

            app.run(host="0.0.0.0", port="5000", debug=True, processes=2, threaded=False)

            注意:絕對(duì)不能在生產(chǎn)環(huán)境中使用調(diào)試器

            • host設(shè)定為0.0.0.0,則可以讓服務(wù)器被公開(kāi)訪問(wèn)
            • port:指定端口號(hào),默認(rèn)為5000
            • debug:是否開(kāi)啟 debug 模型,如果你打開(kāi) 調(diào)試模式,那么服務(wù)器會(huì)在修改應(yīng)用代碼之后自動(dòng)重啟,并且當(dāng)應(yīng)用出錯(cuò)時(shí)還會(huì)提供一個(gè) 有用的調(diào)試器。
            • processes:線程數(shù)量,默認(rèn)是1
            • threadedbool類型,是否開(kāi)啟多線程。注:當(dāng)開(kāi)啟多個(gè)進(jìn)程時(shí),不支持同時(shí)開(kāi)啟多線程。
            • 使用 route() 裝飾器來(lái)告訴 Flask 觸發(fā)函數(shù)的 URL;
            • 函數(shù)名稱被用于生成相關(guān)聯(lián)的 URL。函數(shù)最后返回需要在用戶瀏覽器中顯示的信息。

          2. Flask 響應(yīng)

          視圖函數(shù)的返回值會(huì)自動(dòng)轉(zhuǎn)換為一個(gè)響應(yīng)對(duì)象。如果返回值是一個(gè)字符串,那么會(huì)被 轉(zhuǎn)換為一個(gè)包含作為響應(yīng)體的字符串、一個(gè) 200 OK 出錯(cuò)代碼 和一個(gè) text/html 類型的響應(yīng)對(duì)象。如果返回值是一個(gè)字典,那么會(huì)調(diào)用 jsonify() 來(lái)產(chǎn)生一個(gè)響應(yīng)。以下是轉(zhuǎn)換的規(guī)則:

          • 如果視圖返回的是一個(gè)響應(yīng)對(duì)象,那么就直接返回它。
          • 如果返回的是一個(gè)字符串,那么根據(jù)這個(gè)字符串和缺省參數(shù)生成一個(gè)用于返回的 響應(yīng)對(duì)象。
          • 如果返回的是一個(gè)字典,那么調(diào)用 jsonify 創(chuàng)建一個(gè)響應(yīng)對(duì)象。
          • 如果返回的是一個(gè)元組,那么元組中的項(xiàng)目可以提供額外的信息。元組中必須至少 包含一個(gè)項(xiàng)目,且項(xiàng)目應(yīng)當(dāng)由 (response, status) 、 (response, headers) 或者 (response, status, headers) 組成。status 的值會(huì)重載狀態(tài)代碼, headers 是一個(gè)由額外頭部值組成的列表 或字典。
          • 如果以上都不是,那么 Flask 會(huì)假定返回值是一個(gè)有效的 WSGI 應(yīng)用并把它轉(zhuǎn)換為一個(gè)響應(yīng)對(duì)象。

          JSON 格式的 API

          JSON格式的響應(yīng)是常見(jiàn)的,用 Flask 寫這樣的 API 是很容易上手的。如果從視圖 返回一個(gè) dict ,那么它會(huì)被轉(zhuǎn)換為一個(gè) JSON 響應(yīng)

          @app.route("/me")
          def me_api():
          user = get_current_user()
          return {
          "username": user.username,
          "theme": user.theme,
          "image": url_for("user_image", filename=user.image),
          }

          如果 dict 還不能滿足需求,還需要?jiǎng)?chuàng)建其他類型的 JSON 格式響應(yīng),可以使用 jsonify() 函數(shù)。該函數(shù)會(huì)序列化任何支持的 JSON 數(shù)據(jù)類型。

          @app.route("/users")
          def users_api():
          users = get_all_users()
          return jsonify([user.to_json() for user in users])

          3. 運(yùn)行開(kāi)發(fā)服務(wù)器

          1. 通過(guò)命令行使用開(kāi)發(fā)服務(wù)器

            強(qiáng)烈推薦開(kāi)發(fā)時(shí)使用 flask 命令行腳本( 命令行接口 ),因?yàn)橛袕?qiáng)大的重載功能,提供了超好的重載體驗(yàn)?;居梅ㄈ缦?

            $ export FLASK_APP=my_application
            $ export FLASK_ENV=development
            $ flask run

            這樣做開(kāi)始了開(kāi)發(fā)環(huán)境(包括交互調(diào)試器和重載器),并在 http://localhost:5000/提供服務(wù)。

            通過(guò)使用不同 run 參數(shù)可以控制服務(wù)器的單獨(dú)功能。例如禁用重載器:

            $ flask run --no-reload

          2. 通過(guò)代碼使用開(kāi)發(fā)服務(wù)器

            另一種方法是通過(guò) Flask.run() 方法啟動(dòng)應(yīng)用,這樣立即運(yùn)行一個(gè)本地服務(wù)器,與使用 flask 腳本效果相同。

            示例:

            if __name__ == '__main__':
            app.run()

            通常情況下這樣做不錯(cuò),但是對(duì)于開(kāi)發(fā)就不行了。

          2.5 使用 Gunicorn

          當(dāng)我們執(zhí)行上面的app.py時(shí),使用的flask自帶的服務(wù)器,完成了 web 服務(wù)的啟動(dòng)。在生產(chǎn)環(huán)境中,flask 自帶的服務(wù)器,無(wú)法滿足性能要求,我們這里采用Gunicornwsgi容器,來(lái)部署flask程序。

          Gunicorn(綠色獨(dú)角獸)是一個(gè)Python WSGI UNIX HTTP服務(wù)器。從 Ruby 的獨(dú)角獸(Unicorn )項(xiàng)目移植。該Gunicorn服務(wù)器作為wsgi app的容器,能夠與各種 Web 框架兼容,實(shí)現(xiàn)非常簡(jiǎn)單,輕量級(jí)的資源消耗。Gunicorn 直接用命令啟動(dòng),不需要編寫配置文件,相對(duì) uWSGI 要容易很多。

          web 開(kāi)發(fā)中,部署方式大致類似。

          1. 安裝及使用

          pip install gunicorn

          如果想讓Gunicorn支持異步workers的話需要安裝以下三個(gè)包:

          pip install gevent
          pip install eventlet
          pip install greenlet

          指定進(jìn)程和端口號(hào),啟動(dòng)服務(wù)器:

          gunicorn -w 4 -b 127.0.0.1:5001 運(yùn)行文件名稱:Flask程序?qū)嵗?/code>

          以上述 hello.py 文件為例:

          gunicorn -w 4 -b 127.0.0.1:5001 hello:app

          參數(shù):-w: 表示進(jìn)程(worker)。-b:表示綁定 ip 地址和端口號(hào)(bind)

          查看 gunicorn 的具體參數(shù),可執(zhí)行gunicorn -h 通常將配置參數(shù)寫入到配置文件中,如gunicorn_conf.py

          重要參數(shù):

          • bind: 監(jiān)聽(tīng)地址和端口
          • workers: worker 進(jìn)程的數(shù)量。建議值:2~4 x (NUM_CORES),缺省值是 1.
          • worker_class:worker 進(jìn)程的工作方式。有:sync (缺省值),eventlet, gevent, gthread, tornado
          • threads:工作進(jìn)程中線程的數(shù)量。建議值:2~4 x (SUM_CORES),缺省值是 1.
          • reload: 當(dāng)代碼有修改時(shí),自動(dòng)重啟 workers。適用于開(kāi)發(fā)環(huán)境,默認(rèn)為False
          • daemon:應(yīng)用是否以daemon方式運(yùn)行,是否以守護(hù)進(jìn)程啟動(dòng),默認(rèn)False
          • accesslog:訪問(wèn)日志文件路徑
          • errorlog:錯(cuò)誤日志路徑
          • loglevel:日志級(jí)別。debug, info, warning, error, critical.

          一個(gè)參數(shù)配置示例:

          # gunicorn_conf.py
          bind: '0.0.0.0:5000' # 監(jiān)聽(tīng)地址和端口號(hào)
          workers = 2 # 進(jìn)程數(shù)
          worker_class = 'sync' #工作模式,可選sync, gevent, eventlet, gthread, tornado等
          threads = 1 # 指定每個(gè)進(jìn)程的線程數(shù),默認(rèn)為1
          worker_connections = 2000 # 最大客戶并發(fā)量
          timeout = 30 # 超時(shí)時(shí)間,默認(rèn)30s
          reload = True # 開(kāi)發(fā)模式,代碼更新時(shí)自動(dòng)重啟
          daemon = False # 守護(hù)Gunicorn進(jìn)程,默認(rèn)False

          accesslog = './logs/access.log' # 訪問(wèn)日志文件
          errorlog = './logs/error.log'
          loglevel = 'debug' # 日志輸出等級(jí),debug, info, warning, error, critical

          調(diào)用命令:

          gunicorn -c gunicorn_conf.py hello:app

          參數(shù)配置文件示例可見(jiàn):gunicorn/example_config.py at master · benoitc/gunicorn[3]

          3、代碼示例

          #flask_feature.app
          import numpy as np
          from flask import Flask, jsonify
          from keras.models import Model
          from keras.applications.vgg16 import VGG16
          from keras.backend.tensorflow_backend import set_session

          app = Flask(__name__)
          app.config['JSON_AS_ASCII']=False

          @app.route("/", methods=["GET", "POST"])
          def feature():
          img_feature = extract()
          return jsonify({'result':'true', 'msg':'成功'})

          def extract(img_name):
          # 圖像預(yù)處理
          img = load_image(img_name, target_size=(feature_params["size"], feature_params["size"]))

          x = image.img_to_array(img)
          x = np.expand_dims(x, axis=0)
          x = preprocess_input(x)

          with graph.as_default():
          set_session(sess)
          res = model.predict(x)

          return res


          if __name__ == '__main__':
          tf_config = some_custom_config
          sess = tf.Session(config=tf_config)
          set_session(sess)
          base_model = VGG16(weights=model_weights, include_top=True)
          model = Model(inputs=base_model.input,
          outputs=base_model.get_layer(layer).output)
          graph = tf.get_default_graph()

          app.run()

          使用gunicorn啟動(dòng)服務(wù)命令:

          gunicorn -c gunicorn_conf.py flask_feature:app

          4、遇到的問(wèn)題

          在此記錄整個(gè)部署工作中遇到的問(wèn)題及對(duì)應(yīng)解決方法。

          4.1 Flask 多線程與多進(jìn)程問(wèn)題

          由于對(duì)算法的時(shí)間性能要求較高,因此嘗試使用 Flask 自帶的多線程與多進(jìn)程選項(xiàng)測(cè)試效果。在Flaskapp.run()函數(shù)中,上面有介紹到processes參數(shù),用于指定開(kāi)啟的多進(jìn)程數(shù)量,threaded參數(shù)用于指定是否開(kāi)啟多線程。

          flask 開(kāi)啟 debug 模式,啟動(dòng)服務(wù)時(shí),dubug 模式會(huì)開(kāi)啟一個(gè) tensorflow 的線程,導(dǎo)致調(diào)用 tensorflow 的時(shí)候,graph 產(chǎn)生了錯(cuò)位。

          4.1 Flask 與 Keras 問(wèn)題

          使用 Flask 啟動(dòng)服務(wù)的時(shí)候,將遇到的問(wèn)題及參考的資料記錄在此。

          Q1:Tensor is not an element of this graph

          錯(cuò)誤信息:

          "Tensor Tensor(\"pooling/Mean:0\", shape=(?, 1280), dtype=float32) is not an element of this graph.",

          描述:使用Keras中預(yù)訓(xùn)練模型進(jìn)行圖像分類特征提取的代碼可以正常跑通,當(dāng)通過(guò)Flask來(lái)啟動(dòng)服務(wù),訪問(wèn)預(yù)測(cè)函數(shù)時(shí),出現(xiàn)上述錯(cuò)誤。

          原因:使用了動(dòng)態(tài)圖,即在做預(yù)測(cè)的時(shí)候,加載的graph并不是第一次初始化模型時(shí)候的Graph,所有里面并沒(méi)有模型里的參數(shù)和節(jié)點(diǎn)等信息。

          有人給出如下解決方案:

          import tensorflow as tf
          global graph, model
          graph = tf.get_default_graph()

          #當(dāng)需要進(jìn)行預(yù)測(cè)的時(shí)候
          with graph.as_default():
          y = model.predict(x)

          Q2:使用 Flask 啟動(dòng)服務(wù),加載兩次模型,占用兩份顯存

          出現(xiàn)該問(wèn)題的原因是使用Flask啟動(dòng)服務(wù)的時(shí)候,開(kāi)啟了 debug 模式,即debug=True。dubug模式會(huì)開(kāi)啟一個(gè)tensorflow的線程,此時(shí)查看 GPU 顯存占用情況,會(huì)發(fā)現(xiàn)有兩個(gè)進(jìn)程都占用相同份的顯存。

          關(guān)閉 debug 模型(debug=False)即可。

          參考資料:

          [1]:Keras + Flask 提供接口服務(wù)的坑~~~[4]

          4.2 gunicorn 啟動(dòng)服務(wù)相關(guān)問(wèn)題

          當(dāng)使用 gunicorn 啟動(dòng)服務(wù)的時(shí)候,遇到以下問(wèn)題:

          Q1: Failed precondition

          具體問(wèn)題:

          2 root error(s) found.\n
          (0) Failed precondition: Error while reading resource variable block5_conv2/kernel from Container: localhost. This could mean that the variable was uninitialized. Not found: Container localhost does not exist. (Could not find resource: localhost/block5_conv2/kernel)\n\t [[{{node block5_conv2/convolution/ReadVariableOp}}]]\n\t [[fc2/Relu/_7]]\n
          (1) Failed precondition: Error while reading resource variable block5_conv2/kernel from Container: localhost. This could mean that the variable was uninitialized. Not found: Container localhost does not exist. (Could not find resource: localhost/block5_conv2/kernel)\n\t [[{{node block5_conv2/convolution/ReadVariableOp}}]]\n0 successful operations.\n0 derived errors ignored."

          解決方法:

          通過(guò)創(chuàng)建用于加載模型的會(huì)話的引用,然后在每個(gè)需要使用的請(qǐng)求中使用 keras 設(shè)置 session。具體如下:

          from tensorflow.python.keras.backend import set_session
          from tensorflow.python.keras.models import load_model

          tf_config = some_custom_config
          sess = tf.Session(config=tf_config)
          graph = tf.get_default_graph()

          # IMPORTANT: models have to be loaded AFTER SETTING THE SESSION for keras!
          # Otherwise, their weights will be unavailable in the threads after the session there has been set
          set_session(sess)
          model = load_model(...)

          # 在每一個(gè)request中:
          global sess
          global graph
          with graph.as_default():
          set_session(sess)
          model.predict(...)

          有網(wǎng)友分析原因:tensorflowgraphsession不是線程安全的,默認(rèn)每個(gè)線程創(chuàng)建一個(gè)新的session(不包含之前已經(jīng)加載的 weights, models 等)。因此,通過(guò)保存包含所有模型的全局會(huì)話并將其設(shè)置為在每個(gè)線程中由keras使用,可以解決問(wèn)題。

          有網(wǎng)友提取一種改進(jìn)方式:

          # on thread 1
          session = tf.Session(graph=tf.Graph())
          with session.graph.as_default():
          k.backend.set_session(session)
          model = k.models.load_model(filepath)

          # on thread 2
          with session.graph.as_default():
          k.backend.set_session(session)
          model.predict(x, **kwargs)

          這里的新穎性允許(一次)加載多個(gè)模型并在多個(gè)線程中使用。默認(rèn)情況下,加載模型時(shí)使用“默認(rèn)”Session和“默認(rèn)”graph。但是在這里是創(chuàng)建新的。還要注意,Graph存儲(chǔ)在Session對(duì)象中,這樣更加方便。

          測(cè)試了一下好像不行

          Q2:無(wú)法啟動(dòng)服務(wù),CRITICAL WORKER TIMEOUT

          當(dāng)使用 gunicorn 啟動(dòng) flask 服務(wù)時(shí),查看服務(wù)器狀態(tài)和日志文件發(fā)現(xiàn)一直在嘗試啟動(dòng),但是一直沒(méi)有成功。

          CRITICAL WORKER TIMEOUT

          這是 gunicorn 配置參數(shù)timeout導(dǎo)致的。默認(rèn)值為30s,即超過(guò) 30s,就會(huì) kill 掉進(jìn)程,然后重新啟動(dòng)restart

          當(dāng)啟動(dòng)服務(wù)進(jìn)行初始化的時(shí)間超過(guò) timeout 值時(shí),就會(huì)一直啟動(dòng),kill, restart。

          可根據(jù)具體情況,適當(dāng)增加該值。

          參考資料

          [1]

          Linux查看CPU和內(nèi)存使用情況: https://www.cnblogs.com/mengchunchen/p/9669704.html

          [2]

          三個(gè)目前最火的Python Web開(kāi)發(fā)框架,你值得擁有!: https://yq.aliyun.com/articles/700673

          [3]

          gunicorn/example_config.py at master · benoitc/gunicorn: https://github.com/benoitc/gunicorn/blob/master/examples/example_config.py

          [4]

          1]:[Keras + Flask 提供接口服務(wù)的坑~~~: https://www.cnblogs.com/svenwu/p/10189557.html

          [5]

          tensorflow - GCP ML-engine FailedPreconditionError (code: 2) - Stack Overflow: https://stackoverflow.com/questions/55632876/gcp-ml-engine-failedpreconditionerror-code-2



          好文和朋友一起看~
          瀏覽 69
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  大香蕉色性在线视频 | 国产无码激情后入 | 国产拍拍视频 | 欧美成人网站网址 | 少女爱操B |