Здравствуйте, любопытный человек! Добро пожаловать во вторую часть нашей серии «Развертывание глубокого обучения». В этой части, как я и обещал, мы напишем несколько HTML-страниц и Dockerfile для нашего первого сервиса, т. е. веб-сайта. Итак, положите пальцы на клавиатуру и начнем.
Сначала мы создадим папку с именем «static» внутри папки «flask». В этой папке мы храним статические файлы, которые будут использоваться нашим веб-приложением. Я храню в нем файл «index.png», который будет отображаться при входе пользователя в наше веб-приложение.
Теперь мы создадим папку с именем «Шаблон» внутри папки «фласк». Здесь мы будем хранить наши HTML-страницы, а именно «base.html», «index.html», «login.html», «result.html», «signup.html» и «start.html».
Итак, давайте создадим нашу первую страницу «base.html». Каждая вторая страница будет расширять эту страницу «base.html». Эта страница предоставит макет для расширения всех остальных страниц. Он предоставляет кнопки в правом верхнем углу веб-страницы для входа, регистрации и дома.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Flask Auth Example</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css" /> </head> <body> <section class="hero is-primary is-fullheight"> <div class="hero-head"> <nav class="navbar"> <div class="container"> <div id="navbarMenuHeroA" class="navbar-menu"> <div class="navbar-end"> <a href="{{ url_for('index') }}" class="navbar-item"> Home </a> {% if current_user.is_authenticated %} <a href="{{ url_for('predict') }}" class="navbar-item"> Application </a> {% endif %} {% if not current_user.is_authenticated %} <a href="{{ url_for('login') }}" class="navbar-item"> Login </a> <a href="{{ url_for('signup') }}" class="navbar-item"> Sign Up </a> {% endif %} {% if current_user.is_authenticated %} <a href="{{ url_for('logout') }}" class="navbar-item"> Logout </a> {% endif %} </div> </div> </div> </nav> </div> <div class="hero-body"> <div class="container has-text-centered"> {% block content %} {% endblock %} </div> </div> </section> </body> </html>
Теперь напишем «index.html». Эта страница появится, когда пользователь войдет в наше веб-приложение. Эта страница предоставит пользователю макет для загрузки изображения в наше веб-приложение.
{% extends "base.html" %} {% block content %} <head><style>.button { background-color: #4CAF50; border: none; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; }</style></head> <div class="container"> <h1>Upload File Here</h1> <hr> <img src="{{ result.image_path }}" class="img-rounded" width="400" height="200"> <form class="form-inline" action="/app" method="post" enctype="multipart/form-data"> <div class="form-group"> <input type="file" name="file" class="btn btn-default"> </div> <div class="form-group"> <input type="submit" value="Upload" class="button"> </div> </form> </div> {% endblock %}
Далее мы создадим «login.html». Эта страница предоставит пользователю макет для входа в наше веб-приложение, т. е. введите свое имя пользователя и пароль и кнопку отправки для входа.
{% extends "base.html" %} {% block content %} <div class="column is-4 is-offset-4"> <h3 class="title">Login</h3> <div class="box"> {% with messages = get_flashed_messages() %} {% if messages %} <div class="notification is-danger"> {{ messages[0] }} </div> {% endif %} {% endwith %} <form method="POST" action="/login"> <div class="field"> <div class="control"> <input class="input is-large" type="email" name="email" placeholder="Your Email" autofocus=""> </div> </div> <div class="field"> <div class="control"> <input class="input is-large" type="password" name="password" placeholder="Your Password"> </div> </div> <div class="field"> <label class="checkbox"> <input type="checkbox"> Remember me </label> </div> <button class="button is-block is-info is-large is-fullwidth">Login</button> </form> </div> </div> {% endblock %}
Теперь напишем «result.html». На этой странице будет представлен макет, когда наше веб-приложение будет отображать результат классификации для загруженного изображения, то есть, является ли загруженное изображение кошкой или собакой.
{% extends "base.html" %} {% block content %} <div class="container"> <h1>Predicted Class: {{ result.class_name }}</h1> <hr> <div> <img src="{{ result.image_path }}" class="img-rounded" width="400" height="200"> </div> <a href="{{ url_for('predict') }}" class="btn btn-default">Back</a> </div> {% endblock %}
Далее мы напишем «signup.html». Эта страница предоставит пользователю макет для регистрации в нашем веб-приложении. Пользователи могут загружать изображения в наше веб-приложение, только если они вошли в систему.
{% extends "base.html" %} {% block content %} <div class="column is-4 is-offset-4"> <h3 class="title">Sign Up</h3> <div class="box"> {% with messages = get_flashed_messages() %} {% if messages %} <div class="notification is-danger"> {{ messages[0] }}. Go to <a href="{{ url_for('login') }}">login page</a>. </div> {% endif %} {% endwith %} <form method="POST" action="/signup"> <div class="field"> <div class="control"> <input class="input is-large" type="email" name="email" placeholder="Email" autofocus=""> </div> </div> <div class="field"> <div class="control"> <input class="input is-large" type="text" name="name" placeholder="Name" autofocus=""> </div> </div> <div class="field"> <div class="control"> <input class="input is-large" type="password" name="password" placeholder="Password"> </div> </div> <button class="button is-block is-info is-large is-fullwidth">Sign Up</button> </form> </div> </div> {% endblock %}
И в конце напишем «start.html». Когда наш пользователь впервые открывает наше веб-приложение через веб-браузер. Это первая страница, которую увидит пользователь.
{% extends "base.html" %} {% block content %} <h1 class="title"> Flask Web App </h1> <h2 class="subtitle"> Flask Web App </h2> {% endblock %}
Мы написали так много HTML-страниц, что, наконец, мы закончили со страницами. Чтобы развернуть наше веб-приложение в контейнере докеров, нам нужно написать файл «requirements.txt». По сути, этот файл содержит все пакеты Python, которые требуются для запуска наших кодов Python.
Flask>=1.1.2 Flask-Bootstrap>=3.3.7.1 waitress>=1.4.3 tensorflow-cpu==2.1.0 numpy>=1.18.3 requests>=2.23.0 Pillow==7.1.0 gunicorn==20.0.4 Flask-SQLAlchemy==2.4.3 Flask-Login==0.5.0 pandas==1.0.5 grpcio==1.31.0 tensorflow-serving-api==2.1.0
Теперь мы можем написать более крутой файл, который будет нашим Dockerfile. Для этого создайте файл с именем «Dockerfile.flask» внутри папки «flask».
FROM python:3.7-slim-buster COPY . /app WORKDIR /app RUN pip install -r requirements.txt
Наш файл Dockerfile довольно маленький, но он сделал все, что требуется для нашего первого сервиса, то есть Web. Обратите внимание, что я запускаю контейнер Docker с пользователем root, поскольку я запускаю свой контейнер Docker в Windows, который имеет некоторые проблемы, когда я использую команду «chown» в файле dockerfile. В целях безопасности рекомендуется запускать контейнер Docker от пользователя без полномочий root.
Давайте посмотрим на нашу структуру папок для нашего первого сервиса, то есть Web.
services |---web |---flask |---database |---static | |---index.png |---Template | |---base.html | |---index.html | |---login.html | |---result.html | |---signup.html | |---start.html |---app.py |---database.py |---dbmodels.py |---Dockerfile.flask |---manage.py |---model.py |---requirements.txt
Примечание. В приведенной выше структуре папок мы видим, что папка базы данных пуста. Файл data2.db будет создан внутри него при первом запуске нашего веб-приложения. В этом файле будут храниться имя пользователя и хешированные пароли.
Если структура вашей папки выглядит так, как показано выше, поздравляем, вы создали свой первый сервис. то есть Интернет. Теперь вы можете расслабиться и убрать пальцы с клавиатуры.
В следующей части этой серии. Мы создадим другие службы для нашего веб-приложения. то есть Tensorflow-Serving и Nginx.
А пока наслаждайтесь вашим днем :)
Если вы готовы идти дальше, вот ссылка на Часть 3
Использованная литература-