См. Также: https://medium.com/@sunilmallya/serverless-inference-for-keras-with-aws-lambda-79f51a91c497
Обновление: с новыми слоями лямбда это стало намного проще. Посетите https://github.com/antonpaquin/Tensorflow-Lambda-Layer, чтобы узнать, как это сделать.
В прошлом году я обучил небольшой двоичный классификатор (находится на https://isitanime.website), чтобы научиться основам машинного обучения. Но когда дело дошло до размещения его в сети, Raspberry Pi, который был моим сервером последние несколько лет, не собирался его сокращать. Мне нужен был более мощный сервер для быстрого выполнения выводов для нескольких коротких пакетов, но я не хотел раскошелиться на более крупный экземпляр EC2, который большую часть времени простаивал бы. Разве не для этого нужна лямбда?
Однако есть проблема: Amazon устанавливает ограничение в 250 МБ для пакетов развертывания, а Tensorflow сам по себе составляет чуть менее 300 МБ. К счастью, другие уже проходили этот путь раньше. Мне не удалось запустить ни один их код из коробки, но я смог использовать те же идеи для создания своего собственного пакета развертывания.
Теперь моя модель (файл .h5 размером 23 МБ) работает как лямбда с максимальным объемом выделенной памяти. На самом деле он использует не так много памяти, но дополнительные вычисления делают его немного быстрее - примерно 0,7 секунды на запрос. У меня производительность примерно такая же, как у t2.medium, но я значительно ниже пределов бесплатного уровня.
Если у вас есть небольшая модель машинного обучения, которую вы хотите развернуть в Интернете, и вы не думаете, что сможете использовать ее в достаточной степени, чтобы оправдать постоянную работу экземпляра, это лучший способ Сделай это.
Код ниже
Вот как это работает.
Весь задействованный код, а также tar-архив пакета развертывания находятся на моем github по адресу https://github.com/antonpaquin/Tensorflow-Lambda
Сначала я создаю экземпляр t2.small с базовым AMI amazon-linux.
aws ec2 run-instances \ --image-id "ami-f973ab84" \ --key-name "my_ssh_key" \ --security-groups "GlobalSSH" \ --instance-type "t2.small" \ --placement "AvailabilityZone=us-east-1b" \ --count 1
Затем я отправляю свой код и модель (src.zip) и SSH. Сначала я устанавливаю некоторые базовые библиотеки, openssl и python 3.6.
mkdir build mv src.zip build pushd build unzip src.zip popd sudo yum groupinstall -y \ development sudo yum install -y \ zlib-devel \ openssl-devel wget https://github.com/openssl/openssl/archive/OpenSSL_1_0_2l.tar.gz tar -zxvf OpenSSL_1_0_2l.tar.gz pushd openssl-OpenSSL_1_0_2l/ ./config shared make sudo make install export LD_LIBRARY_PATH=/usr/local/ssl/lib/ popd rm -rf OpenSSL_1_0_2l.tar.gz openssl-OpenSSL_1_0_2l/ wget https://www.python.org/ftp/python/3.6.0/Python-3.6.0.tar.xz tar xJf Python-3.6.0.tar.xz pushd Python-3.6.0 ./configure make sudo make install popd sudo rm -rf Python-3.6.0.tar.xz Python-3.6.0
Я запускаю пакет развертывания virtualenv и устанавливаю нужные пакеты python (keras, tensorflow и подушка).
sudo env PATH=$PATH pip3 install --upgrade virtualenv virtualenv -p python3 env source env/bin/activate pip3 install \ keras \ tensorflow \ pillow
А вот и самое интересное. Я использую «inotifytools» для создания списка всех файлов, к которым обращается Python при запуске моей модели. Все файлы, к которым осуществляется доступ, являются истинными зависимостями и будут скопированы. Остальные можно проигнорировать.
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm sudo yum install -y \ epel-release-latest-7.noarch.rpm rm epel-release-latest-7.noarch.rpm sudo yum install -y \ inotify-tools inotifywait \ -m \ -e access \ -o inotifywait.list \ --format "%w%f" \ -r \ $VIRTUAL_ENV/lib/python3.6/site-packages/ & # Make sure to save the PID so it can be killed later INOTIFY="$!"
На этом этапе я запускаю тест своей модели, который запускает все обращения к файлам. (Код был разархивирован в ~ / build ранее, не показан)
pushd build python3 test.py kill $INOTIFY popd
Теперь я могу скопировать все затронутые файлы, а также исходные файлы .py.
pushd build for f in $(cat /home/ec2-user/inotifywait.list); do if [ -f $f ]; then REL=$(dirname $f | cut -c 48-) mkdir -p $REL cp $f $REL fi done pushd $VIRTUAL_ENV/lib/python3.6/site-packages/ find . -name "*.py" | cut -c 3- > $HOME/pydep.list popd for f in $(cat $HOME/pydep.list); do cp "$VIRTUAL_ENV/lib/python3.6/site-packages/$f" "/home/ec2-user/build/$f" 2>/dev/null done popd
На этом этапе в Tensorflow есть строка, которая прерывает сборку. Кажется, что что-то неправильно передается в строку и ломается только при развертывании лямбда. Это поправимо:
cat <<EOF | patch build/tensorflow/python/util/all_util.py 55c55 < for m in _reference_pattern.finditer(doc_module.__doc__) --- > for m in _reference_pattern.finditer(str(doc_module.__doc__)) EOF
Теперь я могу уменьшить общие объекты, что является самой большой операцией по экономии места:
pushd build find . -name "*.so" | xargs strip popd
Однако есть один .so, который жалуется на это удаление, так что давайте исправим это, просто используя оригинал.
cp \ $VIRTUAL_ENV/lib/python3.6/site-packages/PIL/_imaging.cpython-36m-x86_64-linux-gnu.so \ $HOME/build/PIL/_imaging.cpython-36m-x86_64-linux-gnu.so
И пакет развертывания готов! Последний тест, чтобы убедиться, что все работает правильно:
pushd build python3 test.py rm test.py popd
А потом застегиваю и отправляю.
pushd build zip -r9 lambda.zip * mv lambda.zip .. popd aws s3 cp lambda.zip s3://my-s3-bucket/lambda.zip
Вот и все! Окончательный размер: 220 МБ. Этот zip-файл можно загрузить в lambda.