Вот история о том, как однажды я решил создать какой-нибудь причудливый интернет-сервис. Я подумал, машинное обучение — это своего рода тренд, не так ли? Посмотрим, что я могу сделать…

Существует множество интернет-сервисов на основе машинного обучения, таких как sonix.ai и, очевидно, ChatGPT, а также другие, такие как synthesia.io, deepl.com/translator, photoai.com и многие другие. Люди заходят на эти сайты, чтобы извлечь текст из аудио, чрезвычайно интеллектуальный текст из подсказок, видео, созданное из текста, текст на другом языке, созданный из текста, и изображения, созданные из изображений.

Итак, как я могу сделать такой сайт? — подумал я. Сайт, который создает изображение с граничными рамками из изображения, может быть хорошим началом. Итак, вот я: создал детектор объектов. [Кстати, ссылка, вероятно, останется рабочей навсегда, но базовый детектор объектов находится на несвободном экземпляре AWS, поэтому я, вероятно, в какой-то момент отключу его.]

Итак, как же это работает, спросите вы? Просто зайдите сюда: https://object-detector-2000.netlify.app/

Затем загрузите образ из Интернета на свой жесткий диск или просто выберите существующий образ на жестком диске. Затем нажмите Выбрать файл и укажите локальный путь к этому изображению.

Затем нажмите Открыть и немного подождите, пока детектор объектов сделает свое волшебство, и вы просто сможете увидеть что-то вроде этого:

Вот так, уже. Не стесняйтесь загрузить еще пару изображений и посмотреть, какие объекты обнаружит сайт и как он их назовет!

Давайте рассмотрим еще один пример:

Почему не все машины распознаются?

Это здорово, но как я это сделал?

История начинается на этой веб-странице: https://ultralytics.com/

На этой веб-странице представлена ​​модель YOLOv8. Вы можете получить код Python оттуда, и он будет запускать модель YOLOv8 на любом изображении, которое вы загружаете в свой Python. Я подумал, что это круто, но я хотел иметь веб-сервис. Итак, я вспомнил о пакете Python flask. Затем я варил, варил и варил, пока не получилось вот это:

import flask
import glob
import os
import ssl
import ultralytics

app = flask.Flask(__name__)

@app.route('/yolo', methods=['GET', 'POST'])
def get_traces_handle():
  try:

    with open('received_image.jpg', 'wb') as binary_image:
      binary_image.write(flask.request.data)

    model = ultralytics.YOLO("yolov8n.pt")
    results = model.predict(
      source='received_image.jpg',
      save=True,
    )

    # Get all files in the current directory
    files = glob.glob('./runs/**/*', recursive=True)

    # Find the most recently modified file
    latest_file = max(files, key=os.path.getmtime)

    # Open the file in binary mode and read it
    with open(latest_file, 'rb') as f:
        file_bytes = f.read()

    # Create a response with the file bytes
    flask_response = flask.make_response(file_bytes)

    # Set the appropriate content type
    flask_response.headers.set('Content-Type', 'image/jpeg')

    # Optionally, set the content disposition to allow for file download
    flask_response.headers.set('Content-Disposition', 'attachment', filename='filename.jpg')

    return flask_response
  except Exception as exception:
    error_text = str(exception)
    flask_response = flask.make_response(error_text)
    flask_response.headers['Content-Type'] = 'text/plain; charset=utf-8'
    return flask_response

@app.after_request
def add_cors_headers(response):
  response.headers.add('Access-Control-Allow-Origin', '*')
  response.headers.add('Access-Control-Allow-Headers', 'Content-Type')
  return response

if __name__ == '__main__':
  context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
  context.load_cert_chain('certificate.pem', 'key.pem')

  app.run(
    host='0.0.0.0',
    port=8080,
    debug=True,
    use_reloader=False,
    ssl_context=context,
  )

На самом деле не такая уж и сложность. Весь код на самом деле делает три вещи:

  1. Настройте веб-сервер для получения изображений от таких клиентов, как приложения ReactJS, клиенты Postman или даже другие программы Python.
  2. В веб-сервисе получите изображение в виде строки байтов, запишите его на жесткий диск и укажите путь к изображению для модели YOLOv8. Затем подождите, пока модель сделает свое дело, и прочитайте последний измененный файл в каталоге runs/, специфичном для YOLOv8. Этот файл будет нашим выходным изображением с граничными рамками. Отправьте файл запрашивающему.
  3. На самом деле мы используем SSL-сертификаты, поэтому веб-сервер будет иметь определенную подпись в виде пары файлов: SSL-сертификат + SSL-ключ. Из-за этого мы можем обращаться к нашему серверу с URL-адресом, начинающимся с https://, что будет важно, поскольку мы также будем размещать наш пользовательский интерфейс на https://, и вы не можете адресовать URL-адрес http:// из https:// :// URL (мне пришлось выучить его на собственном горьком опыте во время этого проекта).

Назовем этот файл Python api.py.

Итак, давайте запустим api.py локально! Ах, подождите, сначала requirements.txt:

flask
jupyter
ultralytics
watchdog

И вот небольшой сценарий оболочки для установки этих требований и запуска приведенного выше кода Python:

# Delete the priorly created Anaconda3 environment
conda activate base
rm -rf $(conda info --base)/envs/object_detection/
conda env remove --name object_detection

# Create a new Anaconda3 environment
conda create --yes --name object_detection python
conda activate object_detection
pip install --upgrade --requirement requirements.txt

# Run the actual Python file
python api.py

На данный момент мы будем работать без поддержки HTTPS, поэтому перепишите вызов как

if __name__ == '__main__':
  # context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
  # context.load_cert_chain('certificate.pem', 'key.pem')

  app.run(
    host='0.0.0.0',
    port=8080,
    debug=True,
    use_reloader=False,
    # ssl_context=context,
  )

в приведенном выше файле Python.

Теперь зайдите в Почтальон и установите программу на свой локальный компьютер. Мы хотим отправить изображение в этот API и посмотреть, вернется ли что-нибудь.

Если бы вы были внимательны, то заметили бы, что мы обслуживаем HTTP-запросы по пути /yolo. Итак, мы должны иметь возможность обратиться к этому URL: http://127.0.0.1:8080/yolo. Давайте откроем Postman и подготовим HTTP-запрос следующим образом:

Если вы отправите его, вы получите:

Изображение не найдено […]/received_image.jpg

Этот файл received_image.jpg — это файл, который сервер Python создает при получении запроса HTTP/HTTPS. Но на этот раз мы не отправляли изображения на сервер, поэтому получаем эту ошибку.

Давайте настроим изображение в HTTP-POST-Body с помощью Postman:

Совет: вам нужно прокрутить раскрывающееся меню, чтобы найти «двоичный» тип тела. Затем просто выберите локальный путь к вашему изображению.

Хорошо: это сработало хорошо. Теперь давайте фактически настроим HTTPS: для этого нам нужно использовать программное обеспечение под названием mkcert.

Сначала установите программное обеспечение, а затем установите в своей системе локальный центр сертификации SSL. Я не точно понимаю, как это работает, но полагаю, что программа mkcert связывается с вашим браузером Chromium или Firefox, сообщая им, что определенный SSL-сертификат, если он представлен через HTTPS-сервер, безопасен. Это взаимодействие происходит в то самое время, когда mkcert создает этот SSL-сертификат и соответствующий ему ключ. Затем мы перенесем сертификат и ключ на сервер HTTPS, чтобы сервер мог использовать эти вещи для аутентификации в отношении любопытных веб-браузеров.

Хватит говорить! Вот так:

# Download the mkcert binary
curl --insecure -JLO "https://dl.filippo.io/mkcert/v1.4.4?for=linux/amd64"
chmod +x mkcert-v1.4.4-linux-amd64

# Make up a pair of SSL certificate + key, as detailed above.
./mkcert-v1.4.4-linux-amd64 -install
./mkcert-v1.4.4-linux-amd64 localhost

# Rename the files for convenience
mv localhost.pem certificate.pem
mv localhost-key.pem key.pem

Теперь верните эти строки в файл Python:

if __name__ == '__main__':
  context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
  context.load_cert_chain('certificate.pem', 'key.pem')

  app.run(
    host='0.0.0.0',
    port=8080,
    debug=True,
    use_reloader=False,
    ssl_context=context,
  )

Бегать:

python api.py

Мы проверим службу HTTPS, введя URL-адрес: https://127.0.0.1:8080/yolo.

Но теперь мы получаем эту ошибку:

Ошибка SSL: невозможно проверить первый сертификат

Это означает, что Postman не знает SSL-сертификат mkcert. На самом деле это должно быть решаемо, но я не понял. Поэтому просто нажмите Отключить проверку SSL:

Красивый!

Теперь у нас есть HTTPS-сервер, работающий в нашей локальной системе, который рисует ограничивающие рамки в изображениях, которые мы ему передаем. Очень круто, привет!

А как же Терраформ?

Это действительно здорово, но никто не может видеть наш сервис, кроме нас, нашего кота и, возможно, некоторых соседей, которые используют наш WiFi против нашей воли. Как мы можем сознательно выйти на всеобщее обозрение?

Конечно же, обратимся к Amazon и его платформе AWS.

Когда вы находитесь на платформе AWS, выполните следующие действия:

  1. Перейдите в раздел Управление идентификацией и доступом (IAM).
  2. Нажмите Управление доступом > Пользователи. Нажмите Добавить пользователей.
  3. Введите имя пользователя, затем нажмите Прикрепить политики напрямую > Создать политику.
  4. Переключитесь с Visual на JSON.
  5. Затем вставьте этот код в поле:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ec2:*",
            "Resource": "*"
        }
    ]
}

Мы позволим вашей учетной записи пользователя AWS создавать, изменять и уничтожать произвольные вычислительные ресурсы и экземпляры AWS.

6. Нажмите Далее.

7. Введите имя для этой политики, например EC2FullAccessObjectDetector. Нажмите Создать политику.

8. Перейдите на вкладку с кнопкой Создать политику.

9. Обновите политики с помощью кнопки с кружком слева. Теперь найдите политику EC2FullAccessObjectDetector. Выберите его с помощью флажка слева. Затем нажмите Далее и создайте пользователя.

10. Перейдите на страницу пользователя. Перейдите в раздел Учетные данные безопасности > Ключи доступа.

11. Нажмите Создать ключ доступа. Выберите Сторонний сервис. Установите флажок и перейдите к Далее.

12. Загрузить CSV-файл.

13. Откройте файл .csv и найдите там две важные части информации: идентификатор вашего ключа доступа (AWS_ACCESS_KEY_ID) и ваш секретный ключ доступа (AWS_SECRET_ACCESS_KEY). сильный>).

Эти ключи принадлежат созданному пользователю платформы AWS. Платформу AWS можно настроить с помощью Terraform, который представляет собой не что иное, как формальный язык для настройки платформы AWS. Возможно, у вас был опыт посещения AWS, и вы часами щелкали, щелкали и щелкали, только чтобы обнаружить, что небольшая ошибка, которую вы сделали при нажатии, заставляет вас щелкать еще больше. Вместо этого, используя Terraform, вы просто пишете, какие экземпляры AWS вам нужны с точки зрения объема памяти или хранилища, какого типа ЦП и какой открытый ключ SSH они должны иметь, а Terraform переходит к AWS и щелкает там. от вашего имени.

Давайте проверим это. Вот немного холодной воды для вас, чтобы прыгнуть:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

provider "aws" {
  region = "us-west-2"
}

resource "aws_security_group" "app_sg" {
  name        = "app_sg"
  description = "Allow SSH and TCP traffic on port 8080"

  ingress {
    description = "SSH"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "TCP"
    from_port   = 8080
    to_port     = 8080
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_instance" "app_server" {
  ami           = "ami-08d70e59c07c61a3a"
  instance_type = "t2.small"
  associate_public_ip_address = true
  key_name         = "id_ed25519"
  vpc_security_group_ids = [aws_security_group.app_sg.id]

  tags = {
    Name = "ObjectDetector"
  }

  root_block_device {
    volume_size = 32
  }
}

resource "aws_key_pair" "ssh-key" {
  key_name   = "id_ed25519"
  public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAVlCPR6Uvbz5XxdgfQTat2jZc3gM9mi9FSj7sRDagFQ dimitri@tokyo"
}

В дополнение к тому, что описано выше, мы также открываем два порта на нашем экземпляре: 22 (который принадлежит SSH) и 8080 (который будет портом нашего детектора объектов). Конечно, нам нужен SSH, чтобы войти в экземпляр AWS и запустить настоящий веб-сервис. Для этого вы должны заменить свойство public_key в ssh-key своим открытым ключом, соответствующий закрытый ключ которого у вас есть.

Назовем указанный выше файл main.tf. Также у нас есть еще один файл outputs.tf:

output "public_ip" {
  description = "The public IP for ssh access"
  value       = aws_instance.app_server.public_ip
}

Теперь давайте сделаем что-нибудь с этой конфигурацией Terraform. Давайте снова откроем нашу оболочку bash:

export AWS_ACCESS_KEY_ID=1234567890
export AWS_SECRET_ACCESS_KEY=1234567890
env | grep AWS

Это установит наши переменные среды. Конечно, вставьте свои реальные значения, которые вам нужно будет получить из AWS, вручную щелкая по платформе.

Далее мы заставим Terraform выполнять клики:

terraform init
terraform apply

Вам нужно будет ввести да где-нибудь во время этого процесса.

Так или иначе, на этот раз мы запоминаем IP-адрес инстанса AWS, созданного для нас Terraform:

export DEPLOYED_IP=$(terraform output -raw public_ip)

Теперь просто создайте mkcert SSL-сертификат + ключ, скопируйте их в экземпляр AWS и перейдите туда самостоятельно:

rm -f *.pem mkcert*
curl --insecure -JLO "https://dl.filippo.io/mkcert/v1.4.4?for=linux/amd64"
chmod u+x mkcert-v1.4.4-linux-amd64
./mkcert-v1.4.4-linux-amd64 -install
./mkcert-v1.4.4-linux-amd64 $DEPLOYED_IP

scp $DEPLOYED_IP.pem ubuntu@$DEPLOYED_IP:/home/ubuntu/certificate.pem
scp $DEPLOYED_IP-key.pem ubuntu@$DEPLOYED_IP:/home/ubuntu/key.pem

ssh ubuntu@$DEPLOYED_IP

Предупреждение. Это последний момент, когда вы заметите, что не предоставили Terraform свой собственный открытый ключ. Если у вас возникла ошибка аутентификации SSH, вернитесь к настройке Terraform и настройте ssh-key с помощью собственного ключа SSH.

Что теперь? Конечно, нам нужны эти файлы Python api.py и requirements.txt прямо сейчас. Их вы также можете скопировать с помощью scp, но я предпочитаю использовать GitHub.

Просто поместите эти два файла в отдельный каталог (например, object_detection/) и запустите репозиторий git с удаленным URL-адресом GitHub. Вот как я это сделал: https://github.com/Habimm/object_detection

Теперь клонируйте репозиторий и выполните следующие действия, чтобы установить веб-службу на экземпляре AWS:

#!/usr/bin/env bash

# Stop if one of the below line commands results in an error.
set -e

sudo apt update
sudo apt install -y libgl1-mesa-glx

# Install Anaconda3
wget -O Anaconda3.sh https://repo.anaconda.com/archive/Anaconda3-2023.03-1-Linux-x86_64.sh
chmod +x Anaconda3.sh
rm -rf ~/anaconda3
bash Anaconda3.sh -b
echo 'export PATH="$HOME/anaconda3/bin:$PATH"' >> ~/.bashrc
. ~/.bashrc
. ~/anaconda3/etc/profile.d/conda.sh

# Delete a priorly setup Anaconda3 environment
conda activate base
rm -rf $(conda info --base)/envs/object_detection/
conda env remove --name object_detection

# Create a fresh Anaconda3 environment
conda create --yes --name object_detection python
conda activate object_detection
pip install --upgrade --requirement requirements.txt

# Copy the SSL certificate + key from the home directory,
# (to which we copied them using scp
# after creating them with mkcert)
cp ../{certificate,key}.pem .

# Run the web service,
# in such a way that it continues running,
# even after the SSH connection terminates.
pkill -f "python api.py"
nohup python api.py > output.log 2>&1 &
ps aux | grep "python api.py"
cat output.log

Это горсть!

Все, что мы на самом деле делаем, это следующие пять вещей:

  1. В начале мы говорим set -e, что заставит остановить оболочку, если одна из команд выдаст ошибку.
  2. Мы устанавливаем apt-пакет, который может потребоваться для веб-службы.
  3. Мы устанавливаем Anaconda3, менеджер зависимостей для Python. Например, мы используем Anaconda для установки пакета Python ultralytics, который содержит код для оценки нашей типичной модели YOLOv8.
  4. Мы копируем SSL сертификат и ключ в репозиторий git.
  5. Запускаем веб-сервис. Здесь мы используем трюк nohup, который предотвращает закрытие службы, когда мы сами Ctrl+D выходим из SSH-терминала.

Вы можете поместить этот шелл-код в файл с именем INSTALL и также закоммитить его в репозиторий git. Затем вы можете запустить этот файл bash сразу после клонирования репозитория git на экземпляре AWS, в который вы SSH с полученным IP-адресом. Терраформ.

Это очень приятно! После запуска скрипта установки мы можем увидеть что-то вроде следующего:

Теперь мы хотим добавить эти ограничивающие рамки (!) в наше изображение. Но какой IP-адрес мы вводим в Postman? Бегать:

curl ifconfig.me && echo

Это выведет IP-адрес, который мы можем поместить в Postman. Давайте посмотрим:

Это сработало!

Что мы наделали? Мы использовали Terraform для развертывания экземпляра AWS где-то в западной части США. Затем мы загрузили наш код веб-службы YOLOv8 в этот экземпляр и запустили его. Затем мы использовали нашего локального клиента под названием Postman, чтобы отправить изображение 4 красивых женщин в веб-службу для обнаружения объектов на изображении и обрамления вокруг них. И тогда мы фактически получили изображение с граничными прямоугольниками!

Создание пользовательского интерфейса для веб-сервиса

В качестве последнего шага в нашем маленьком путешествии по творчеству давайте создадим пользовательский интерфейс, в котором человек может фактически загрузить изображение и получить эти ограничивающие рамки. Я имею в виду: мы хотим избавиться от этого интерфейса Postman. !

Для этого шага мы будем использовать ReactJS. Давайте перейдем в родительский каталог нашего object_detection/ и запустим:

npx create-react-app detection_interface

Теперь у нас уже есть довольно обширный скелет для написания кода для нашего UI-приложения. Просто откройте detection_interface/src/App.css и замените содержимое на:

img {
  position: relative;
}

/*https://stackoverflow.com/questions/22051573/how-to-hide-image-broken-icon-using-only-css-html*/
img:after {
  display: block;
  position: absolute;
  top: 0;
  width: 100%;
  height: 100%;
  background-color: #fff;
  content: attr(alt);
}

.App {
  text-align: center;
}

.App-header {
  background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

Это окружит изображения желтой рамкой и добавит пару других стилей.

Затем откройте detection_interface/src/App.js:

import './App.css';
import React, { useState } from 'react';

function App() {
  const [selectedImage, setSelectedImage] = useState(null);
  const [outlinedImage, setOutlinedImage] = useState(null);

  const handleImageUpload = (event) => {
    if (event.target.files.length === 0) {
      return;
    }

    var selectedFileObject = event.target.files[0];

    // We use a temporary variable, because
    // React batches state updates for performance reasons,
    // so we cannot call setSelectedImage() and expect
    // selectedImage to hold the correct value in the next line.
    var tempSelectedImage = URL.createObjectURL(selectedFileObject);

    fetch(tempSelectedImage)
      .then(response => response.blob())
      .then(blob => {
        var reader = new FileReader();
        reader.onloadend = function() {
          // could be a: ArrayBuffer(497559)
          var imageArrayBuffer = reader.result;

          var myHeaders = {
            "Content-Type": "image/jpeg"
          };

          var requestOptions = {
            method: 'POST',
            headers: myHeaders,
            body: imageArrayBuffer,
            redirect: 'follow',
          };

        fetch(process.env.REACT_APP_API_URL, requestOptions)
          .then(response => response.blob())
          .then(blob => {
            const detectionsBlobUrl = URL.createObjectURL(blob);
            setOutlinedImage(detectionsBlobUrl);
          })
          .catch(error => console.log('error', error));
        }
        reader.readAsArrayBuffer(blob);
      });

    setSelectedImage(tempSelectedImage);
  };

  return (
    <div className="App">
    <h3>
      Upload an image to detect objects!
    </h3>
      <header className="App-header">
        <input
          style={{border: '4px solid red'}}
          type="file"
          accept="image/*"
          onChange={handleImageUpload}
        />
        {(
          <div style={{display: 'flex', justifyContent: 'center'}}>
            <img
              src={selectedImage}
              alt="Selected"
              style={{width: '100%', height: 'auto', border: '10px solid yellow', maxWidth: '45%'}}
            />
            <img
              src={outlinedImage}
              alt="Outlined"
              style={{width: '100%', height: 'auto', border: '10px solid yellow', maxWidth: '45%'}}
            />
          </div>
        )}
      </header>
    </div>
  );
}

export default App;

Это фактический код пользовательского интерфейса:

  1. Мы заставляем браузер запрашивать у пользователя файл в виде изображения, если он нажимает кнопку Выбрать файл.
  2. Затем мы читаем изображение с жесткого диска строкой var imageArrayBuffer = reader.result;
  3. Затем мы проверяем переменную process.env.REACT_APP_API_URL, в которую помещаем общедоступный IP-адрес экземпляра AWS.
  4. И мы сделаем запрос к веб-службе экземпляра AWS. Получим результат и используем хук React setOutlinedImage(detectionsBlobUrl);, чтобы запомнить изображение с граничными прямоугольниками.
  5. Как только экземпляр AWS отправляет ответ HTTPS с изображением с нужными нам граничными рамками, React воссоздает этот HTML-код, чтобы изображение с граничными рамками, которое будет сохранено в оперативная память браузера, будет отображаться пользователю.

Теперь создайте файл detection_interface/.env:

REACT_APP_API_URL=https://34.221.234.90:8080/yolo

Замените цифры общедоступным IP-адресом вашего экземпляра AWS. Затем перейдите в корневой каталог интерфейсного приложения, которым является detection_interface/, и запустите:

npm start

Надеюсь, вы можете получить такой результат, как:

Здесь модель YOLOv8 ведет себя очень забавно, потому что очень многие люди на самом деле вообще не обнаруживаются.

Разверните пользовательский интерфейс

Это очень, очень хорошо! Но на самом деле нам еще предстоит развернуть пользовательский интерфейс для широкой публики. Если только наш единственный пользователь не является соседом, крадущим WiFi, который ранее обнаруживал объекты на изображениях.

Переходим к Netlify!

Единственная причина, по которой я упоминаю Netlify, заключается в том, что с мая 2023 года эта платформа совершенно бесплатна, если вы просто хотите разместить пару строк кода пользовательского интерфейса. Я не совсем уверен в AWS в этом вопросе.

Но, помимо затрат, платформа Netlify, на мой взгляд, намного удобнее для пользователя, чем платформа AWS. Это очень чистый и простой дизайн, который действительно оживляет.

Итак, давайте сначала отправим этот код пользовательского интерфейса на GitHub: создайте новый репозиторий GitHub, инициализируйте репозиторий git в своем каталоге detection_interface/, зафиксируйте и загрузите все на GitHub.

Одно маленькое исключение: не отправляйте файл .env. Такие файлы никогда не должны возвращаться в репозитории git.

Вот мой GitHub: https://github.com/Habimm/detection-interface

Теперь перейдите в Netlify и нажмите

Add new site -> Import an existing project -> GitHub

Затем выберите репозиторий GitHub. Это довольно упрощенный процесс публикации кода из вашего репозитория GitHub.

Как только Netlify развернет ваш пользовательский интерфейс, у вас может возникнуть соблазн загрузить изображение:

Тем не менее, никакой реакции…

Почему это? Помните тот файл .env, о котором я предупреждал вас не включать. Вот и причина: файл содержал IP-адрес экземпляра AWS. И теперь нам нужно найти другой способ сообщить Netlify IP-адрес экземпляра AWS.

Для этого перейдите на

Site overview
  -> Site settings
  -> Environment variables
  -> Add a variable
  -> Import from a .env file

Откройте этот ваш файл .env (который будет содержать только одну строку). Скопируйте все из него в окно Netlify. Импортировать переменные.

Теперь, чтобы Netlify заменил

process.env.REACT_APP_API_URL

часть вашего кода с

https://35.92.22.120:8080/yolo

нам нужно повторно развернуть пользовательский интерфейс. В левой части панели инструментов Netlify нажмите

Deploys -> Trigger deploy -> Deploy site

Затем подождите:

Роботы Netlify заняты созданием и развертыванием вашего сайта в нашей CDN.

Затем снова откройте Обзор сайта и подождите, пока текст Последняя публикация в 21:45 не покажет текущее время. Заходим внутрь и вуаля:

Вот оно, народ!

Что мы наделали?

Сегодня мы развернули компьютерную программу 4 раза! Мы развернули в нашей локальной системе веб-сервис для обнаружения объектов на изображениях. Мы развернули тот же веб-сервис на экземпляре AWS, который мы настроили с помощью Terraform. Мы развернули пользовательский интерфейс, созданный с помощью ReactJS, в нашей локальной системе. И, наконец, мы развернули пользовательский интерфейс в Netlify, изменив переменную среды с помощью удобной панели инструментов Netlify!

Ух ты! Я действительно горжусь тобой. Не могу поверить, ты все еще со мной! Если вы сделали это здесь, я совершенно потерял дар речи.

Вот и все!

Теперь идите и разверните свой собственный проект машинного обучения!

И расскажите об этом :)