В этой статье в основном фиксируютсяFlaskПроцесс, используемый в процессе развертывания, возникшие проблемы и соответствующие решения.
1. Введение проекта
В этом разделе кратко описывается работа, проделанная некоторое время назад:
Реализуйте простую задачу классификации изображений на основе глубокого обучения.
Разверните его в веб-приложении с помощью фреймворка flask.
Высокие требования к параллелизму
Это первый раз, когда развертывается веб-приложение модели глубокого обучения, Во всем процессе это еще больше отражает узость предыдущих знаний и реализует версию в постоянной записи и решении.
2. Процесс проекта
Эта часть начинается с процесса реализации проекта, записи проделанной работы и использованных инструментов.
2.1 Модель классификации изображений
1. Выбор модели
Требуется классификация изображений, и первая реакция — использовать более зрелую и классическую сетевую структуру классификации, такую как серия VGG (VGG16, VGG19), серия ResNet (например,ResNet50),InceptionV3Ждать.
Учитывая, что изображения неизвестных типов засекречены, а данных для обучения напрямую нет,ImagenetПредобученная модель, обученная вышеизложенному, в основном соответствует требованиям.
Если требования к производительности (затратам времени) являются строгими, рекомендуется использовать неглубокую сетевую структуру, такую какVGG16, MobileNetЖдать.
в,MobileNetСеть предназначена для мобильных и встроенных приложений глубокого обучения, поэтому она также может обеспечить идеальные требования к скорости процессора. Это легкая глубокая сетевая структура.
MobileNetЗависит отGoogle 团队представлены, опубликованы вCVPR-2017, название статьи:
«MobileNets: эффективные сверточные нейронные сети для приложений мобильного зрения»
2. Выбор кадра
обычное использованиеKerasСуществует множество фреймворков,Kerasиспользование базовой библиотекиTheanoилиTensorflow, также известный как серверная часть Keras.KerasвTensorflowВысокоуровневый API, построенный поверхTensorflowЛегче начать.
Классификационная сеть, упомянутая выше, вKerasВ основном это было реализовано в Keras, а структура сети, реализованная в Keras, выглядит следующим образом:
Он прост в использовании и может быть импортирован напрямую следующим образом:
Поэтому в качестве фреймворка для глубокого обучения выбран Keras.
3. Пример кода
отKerasРамка,VGG16Возьмите сеть в качестве примера для классификации изображений.
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)
# 构建model
base_model = VGG16(weights=‘imagenet’, include_top=True)
model = Model(inputs=base_model.input,
outputs=base_model.get_layer(layer).output) # 获取指定层的输出值,layer为层名
# 进行预测
img = load_image(img_name, target_size=(224, 224)) # 加载图片并resize成224x224
# 图像预处理
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
feature = model.predict(x) # 提取特征
2.2 Тест производительности модели
После запуска моделей классификации нам необходимо проверить их производительность, например потребление времени, использование ЦП, использование памяти и использование памяти графического процессора.
1. Отнимает много времени
Затраты времени — это время, затрачиваемое на извлечение признака классификации тестового изображения, включая сумму времени предварительной обработки изображения и времени прогнозирования модели.
Использование командной строки Nvidianvidia-smiВы можете просмотреть использование памяти.
3. Занятие процессора, памяти
использоватьtopкоманда илиhtopКоманда для проверки использования процессора и памяти.
Использование памяти также может быть использованоfreeкоманда для просмотра:
free -h: добавлять-hвариант, результат вывода более дружелюбен, и будет дана соответствующая единица измерения.
Когда вам нужно постоянно наблюдать за состоянием памяти, вы можете использовать-sОпция указывает интервал в секундах:free -h -s 3(обновлять каждые 3 секунды, нажать при остановке обновленияCtrl+c)
Ubuntu 16.04по умолчанию в версииfreeВ версии есть баги, пользуйтесь-sПри выборе опции будет сообщено об ошибке.
В соответствии с результатами трех вышеприведенных тестов своевременно настройте структуру сети и используемые параметры занятости видеопамяти.
Чтобы узнать конкретное значение команды, обратитесь к сообщению в блоге:
Redis=Remote DIctionary Server, является высокопроизводительным, написанным Сальваторе Санфилиппо.key-valueСистема хранения. Redis — это база данных с открытым исходным кодом, написанная на языке ANSI C, совместимая с протоколом BSD, поддерживающая сеть, может храниться в памяти и быть постоянной, а также предоставляет API-интерфейсы на нескольких языках.
RedisПоддерживаемые типы хранения:string, list, set, zsetиhash, который больше используется в сценарии обработки крупномасштабных операций чтения и записи данных.
1. Основное использование
установить редис
pip install redis
# 测试
import redis
основное введение
redis.pyПредусмотрено два класса:Redis,StrictRedisиспользуется для реализацииRedisКомандаStrictRedisИспользуется для реализации большинства официальных команд и использования официального синтаксиса и команд.RedisдаStrictRedisПодкласс , длявперед совместимыйredis.pyОбычно мы используемStrictRedis.
Redis не может напрямую хранить массивы.Если вы напрямую сохраните значение типа массива, тип значения после получения изменится следующим образом: сохраните тип массива numpy, а полученный типbytesтип.
Чтобы поддерживать один и тот же тип до и после сохранения данных, сериализуйте массив перед его сохранением и десериализуйте его при получении массива.
С помощью питонаpickleМодуль выполняет операции сериализации.
Таким образом, тип данных до сохранения и после извлечения может поддерживаться согласованным.
2.4 среда веб-разработки — Flask
До изучения языка python никогда не обращал вниманияWeb开发Эта глава, потому что содержание работы не включает эту часть. Теперь надо еще раз посмотреть.
Раннее программное обеспечение в основном работало на настольных компьютерах, а программное обеспечение, такое как базы данных, работало на стороне сервера.Client/ServerАббревиатура режимаCSАрхитектура. С появлением Интернета,CSАрхитектура не подходитWeb, главная причина в том, что модификация и обновление веб-приложений происходят очень часто,CS架构Каждому клиенту необходимо обновлять настольное приложение одно за другим, поэтомуBrowser/Serverшаблон стал популярным, сокращение отBS架构.
существуетBS架构В этом случае клиенту нужен только браузер, а логика и данные приложения хранятся на стороне сервера, браузеру нужно только запросить сервер, получить веб-страницу и отобразить веб-страницу пользователю. В настоящее время веб-страницы также чрезвычайно интерактивны.
История рождения Python раньше, чем у Интернета.Поскольку Python является интерпретируемым языком сценариев с высокой эффективностью разработки, он очень подходит для веб-разработки.
Python имеет сотни веб-фреймворков с открытым исходным кодом, наиболее знакомыми из которых являютсяFlask, Django. Далее сFlaskНапример, как использовать Flask для веб-развертывания.
СвязанныйFlaskКонкретное использование может относиться к другим сообщениям в блоге, информация в этой области относительно полная. Нижеследующее в основном использует конкретные примеры для иллюстрации:
использоватьroute()Decorator, чтобы сообщить Flask URL-адрес для запуска функции;
Имя функции используется для создания связанного URL-адреса. Наконец, функция возвращает информацию, которую необходимо отобразить в браузере пользователя.
Запустите файл, вам будет предложено* Running on http://127.0.0.1:5000/, откройте этот URL в браузере, он автоматически вызоветhomeфункция, возвратHello, Flask, вы увидите на странице браузераHello, Flaskшрифт.
hostустановить как0.0.0.0, вы можете сделать сервер общедоступным
port: укажите номер порта, по умолчанию5000
debug: следует ли включить модель отладки, если вы включите режим отладки, сервер автоматически перезапустится после изменения кода приложения, а также предоставит полезный отладчик при сбое приложения.
processes: количество потоков, по умолчанию1
threaded:boolТипа, включить ли многопоточность. Примечание. При запуске нескольких процессов несколько потоков не поддерживаются одновременно.
Уведомление: Никогда не используйте отладчик в рабочей среде.
2. Реакция колбы
Возвращаемое значение функции представления автоматически преобразуется в объект ответа. Если возвращаемое значение является строкой, оно будет преобразовано в строку, содержащую в качестве тела ответа200 OKкод ошибки иtext/htmlТип объекта ответа. Если возвращаемое значение является словарем, он вызоветjsonify()чтобы сгенерировать ответ. Ниже приведены правила конвертации:
Если представление возвращает объект ответа, верните его напрямую.
Если возвращается строка, объект ответа генерируется для возврата на основе строки и параметров по умолчанию.
Если возвращается словарь, вызовите jsonify, чтобы создать объект ответа.
Если возвращается кортеж, элементы в кортеже могут предоставить дополнительную информацию. Кортеж должен содержать хотя бы один элемент, и этот элемент должен состоять из (ответ, статус), (ответ, заголовки) или (ответ, статус, заголовки). Значение состояния переопределяет код состояния, а заголовки представляют собой список или словарь дополнительных значений заголовков.
Если ничего из вышеперечисленного, Flask предположит, что возвращаемое значение является действительным приложением WSGI, и преобразует его в объект ответа.
API в формате JSON
JSONФорматированные ответы распространены, и начать писать такой API на Flask несложно. при возврате из представленияdict, то он будет преобразован вJSON 响应.
еслиdictОн не соответствует требованиям, и необходимо создать другие типы ответов в формате JSON.Вы можете использоватьjsonify()функция. Эта функция сериализует все поддерживаемыеJSONтип данных.
@app.route("/users")
def users_api():
users = get_all_users()
return jsonify([user.to_json() for user in users])
3. Запустите сервер разработки
Использование сервера разработки из командной строки
Настоятельно рекомендуется использовать скрипт командной строки flask (интерфейс командной строки) во время разработки, потому что он имеет мощную функцию перегрузки и обеспечивает очень хороший опыт перегрузки. Основное использование заключается в следующем:
$ export FLASK_APP=my_application
$ export FLASK_ENV=development
$ flask run
При этом запускается среда разработки (включая интерактивный отладчик и перезагрузку) иhttp://localhost:5000/Предоставлять услуги.
с помощью различныхrunПараметры могут управлять отдельными функциями сервера. Например, чтобы отключить перезагрузку:
$ flask run --no-reload
Использование сервера разработки из кода
Другой способFlask.run()метод запуска приложения, который немедленно запускает локальный сервер, в отличие от использованияflaskСкрипт работает так же.
Пример:
if __name__ == '__main__':
app.run()
В целом это нормально, но не для развития.
2.5 Использование Гуникорна
Когда мы выполняем вышеуказанноеapp.pyкогда используешьflaskВстроенный сервер завершает запуск веб-службы. В производственной среде сервер, идущий в комплекте с flask, не может соответствовать требованиям по производительности.GunicornДелатьwsgiконтейнер, для развертыванияflaskпрограмма.
Gunicorn(зеленый единорог)Python WSGI UNIX HTTPсервер. Перенесено из проекта Ruby's Unicorn. ДолженGunicornсервер какwsgi appконтейнер, который можетСовместимость с различными веб-фреймворками, чтобы добиться очень простого и легкого потребления ресурсов. Гуникорн Директначать с команды, не нужно писать файл конфигурации, что намного проще, чем uWSGI.
В веб-разработке метод развертывания примерно такой же.
1. Установка и использование
pip install gunicorn
Если хотитеGunicornАсинхронная поддержкаworkersВам необходимо установить следующие три пакета:
Возьмем приведенный выше файл hello.py в качестве примера:
gunicorn -w 4 -b 127.0.0.1:5001 hello:app
параметр:-w: представляет процесс (рабочий).-b: указывает IP-адрес привязки и номер порта (привязка).
Просмотр конкретных параметров исполняемого файла gunicorngunicorn -hПараметры конфигурации обычно записываются в файл конфигурации, напримерgunicorn_conf.py
Важные параметры:
bind: адрес прослушивания и порт
workers: количество рабочих процессов. предлагаемое значение:2~4 x (NUM_CORES), значение по умолчанию равно 1.
worker_class: Как работает рабочий процесс. имеют:sync(дефолт),eventlet, gevent, gthread, tornado
threads: количество потоков в рабочем процессе. предлагаемое значение:2~4 x (SUM_CORES), значение по умолчанию равно 1.
reload: Когда код изменен,Автоматический перезапуск рабочих. Для сред разработки по умолчанию используетсяFalse
daemon: запускается ли приложение сdaemonрежим запуска, следует ли запускать процесс демона, по умолчаниюFalse
accesslog: путь к файлу журнала доступа
errorlog: путь журнала ошибок
loglevel: уровень журнала.debug, info, warning, error, critical.
#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):
# 图像预处理
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Запустите сервисную команду:
gunicorn -c gunicorn_conf.py flask_feature:app
4. Возникшие проблемы
Здесь записываются проблемы, возникающие в ходе всей работы по развертыванию, и соответствующие решения.
4.1 Многопоточность Flask и проблемы с несколькими процессами
Из-за высоких требований к производительности алгоритма попробуйте использовать параметры многопоточности и многопроцессорности, которые поставляются с Flask, чтобы проверить эффект.
существуетFlaskизapp.run()функция, как описано вышеprocessesпараметр для указания количества открытых мультипроцессов,threadedПараметр используется, чтобы указать, следует ли включать многопоточность.
Flask включает режим отладки, когда служба запускается, режим отладки открывает поток тензорного потока, что приводит к смещению графика при вызове тензорного потока.
4.1 Проблема Flask и Keras
При использовании Flask для запуска сервиса здесь записываются возникшие проблемы и справочные материалы.
Q1: Тензор не является элементом этого графа
Сообщение об ошибке:
"Tensor Tensor(\"pooling/Mean:0\", shape=(?, 1280), dtype=float32) is not an element of this graph.",
Описание: использоватьKerasКод для извлечения признаков классификации изображений в предварительно обученной модели может работать нормально.Flaskдля запуска службы при доступе к функции прогнозирования возникает вышеуказанная ошибка.
Причина: Используется динамический график, т.е. при построении прогнозов загружаетсяgraphНе тогда, когда модель инициализируется в первый разGraph, в модели нет такой информации, как параметры и узлы.
Кто-то дал следующее решение:
import tensorflow as tf
global graph, model
graph = tf.get_default_graph()
#当需要进行预测的时候
with graph.as_default():
y = model.predict(x)
Q2: Используйте Flask, чтобы запустить службу, дважды загрузить модель и занять две копии видеопамяти.
Проблема возникает из-за использованияFlaskПри запуске службы включается режим отладки, т.е.debug=True.dubugрежим откроетtensorflowЕсли вы проверите использование видеопамяти GPU, вы обнаружите, что два процесса занимают один и тот же объем видеопамяти.
Отключить модель отладки (debug=False) может быть использован.
4.2 Проблемы, связанные со службой запуска gunicorn
При использовании gunicorn для запуска сервиса возникают следующие проблемы:
Q1: Failed precondition
Конкретные проблемы:
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."
Решение:
Создавая ссылку на сеанс, используемый для загрузки модели, затем используя keras для установки сеанса для каждого запроса, который необходимо использовать. детали следующим образом:
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(...)
# 在每一个request中:
global sess
global graph
with graph.as_default():
set_session(sess)
model.predict(...)
Некоторые пользователи сети проанализировали причины:tensorflowизgraphиsessionНе потокобезопасный, по умолчанию каждый поток создает новыйsession(за исключением весов, моделей и т. д., которые были загружены ранее). Поэтому, сохранив глобальный сеанс, содержащий все модели, и настроив его для вызова в каждом потоке с помощьюkerasиспользовать, может решить проблему.
Некоторые пользователи сети извлекли улучшенный способ:
# 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)
Новшество здесь позволяет загружать несколько моделей (одновременно) и использовать их в нескольких потоках. По умолчанию при загрузке модели используется «по умолчанию».Sessionи "по умолчанию"graph. Но здесь создаются новые. Также обратите внимание,Graphсохранить вSessionобъект, это более удобно.
Проверено, похоже, не работает
Q2: Не удается запустить службу, КРИТИЧЕСКИЙ ТАЙМ-АУТ WORKER
При использовании gunicorn для запуска службы flask при просмотре состояния сервера и файлов журнала было обнаружено, что он пытался запуститься, но безуспешно.
CRITICAL WORKER TIMEOUT
Вот параметры конфигурации gunicorntimeoutвызванный. По умолчанию30s, то есть, если оно превысит 30 с, процесс будет убит, а затем перезапущенrestart.
Когда время запуска службы для инициализации превышает значение таймаута, она всегда будет запускаться, убивать, перезапускать.
Это значение может быть соответствующим образом увеличено в зависимости от конкретной ситуации.