Pull to refresh

Книга «Прагматичный ИИ. Машинное обучение и облачные технологии»

Reading time 15 min
Views 4.6K
image Привет, Хаброжители! Эта книга Ноя Гифта предназначена для всех, кого интересуют ИИ, машинное обучение, облачные вычисления, а также любое сочетание данных тем. Как программисты, так и просто неравнодушные технари найдут тут для себя полезную информацию. Примеры кода даны на Python. Здесь рассматривается множество столь продвинутых тем, как использование облачных платформ (например, AWS, GCP и Azure), а также приемы машинного обучения и реализация ИИ. Джедаи, свободно ориентирующиеся в Python, облачных вычислениях и ML, также найдут для себя много полезных идей, которые смогут сразу применить в своей текущей работе.

Предлагаем ознакомиться с отрывком из книги «Создание интеллектуального бота Slack в AWS»

Люди давно мечтают создать «искусственную жизнь». Чаще всего пока это возможно путем создания ботов. Боты становятся все более неотъемлемой частью нашей повседневной жизни, особенно после появления Siri от компании Apple и Alexa от Amazon. В этой главе мы раскроем все тайны создания ботов.

Создание бота


Для создания бота мы воспользуемся библиотекой Slack для языка Python (https://github.com/slackapi/python-slackclient). Для начала работы со Slack необходимо сгенерировать идентификационный маркер. В целом имеет смысл при работе с подобными маркерами экспортировать переменную среды. Я часто делаю это в virtualenv, получая, таким образом, автоматически доступ к ней при выполнении в текущей среде. Для этого необходимо немного «взломать» утилиту virtualenv, отредактировав сценарий activate.

При экспорте переменной Slack в сценарии ~/.env/bin/activate он будет иметь нижеприведенный вид.

И просто для информации, если вы хотите идти в ногу с последними новинками, рекомендуется использовать появившуюся на рынке новую, официальную утилиту Python для управления средой — pipenv (https://github.com/pypa/pipenv):

_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/bin:$PATH"
export PATH
SLACK_API_TOKEN=<Your Token Here>
export SLACK_API_TOKEN

Для проверки того, задано ли значение переменной среды, удобно использовать команду printenv операционных систем OS X и Linux. После этого для проверки отправки сообщения можно воспользоваться следующим коротким сценарием:

import os
from slackclient import SlackClient

slack_token = os.environ["SLACK_API_TOKEN"]
sc = SlackClient(slack_token)

sc.api_call(
   "chat.postMessage",
   channel="#general",
   text="Hello from my bot! :tada:"
)

Стоит также отметить, что утилита pipenv — рекомендуемое решение, объединяющее в одном компоненте возможности утилит pip и virtualenv. Она стала новым стандартом, так что имеет смысл взглянуть на нее с точки зрения управления пакетами.

Преобразование библиотеки в утилиту командной строки


Как и в других примерах из этой книги, удачной идеей будет преобразовать наш код в утилиту командной строки, чтобы облегчить проверку новых идей. Стоит отметить, что многие разработчики-новички часто отдают предпочтение не утилитам командной строки, а другим решениям, например, просто работают в блокнотах Jupiter. Сыграю ненадолго роль адвоката дьявола и задам вопрос, который вполне может возникнуть у читателей: «А зачем нам утилиты командной строки в проекте, основанном на блокнотах Jupiter? Разве смысл блокнотов Jupiter состоит не в том, чтобы сделать ненужными командную оболочку и командную строку?» Добавление утилиты командной строки в проект хорошо тем, что позволяет быстро пробовать различные варианты входных данных. Блоки кода блокнотов Jupiter не принимают входные данные, в некотором смысле это сценарии с жестко «зашитыми» данными.

Множество утилит командной строки на платформах как GCP, так и AWS существует не случайно: они обеспечивают гибкость и возможности, недоступные для графических интерфейсов. Замечательный сборник эссе на эту тему фантаста Нила Стивенсона (Neal Stephenson) называется «В начале… была командная строка». В нем Стивенсон говорит: «GUI приводят к значительным дополнительным накладным расходам на каждый, даже самый маленький компонент программного обеспечения, которые полностью меняют среду программирования». Он заканчивает сборник словами: «… жизнь — штука очень тяжелая и сложная; никакой интерфейс это не изменит; и всякий, кто считает иначе, — простофиля...» Достаточно жестко, но мой опыт подсказывает, что и достаточно правдиво. Жизнь с командной строкой становится лучше. Попробуйте ее — и вы не захотите возвращаться обратно к GUI.

Для этого мы воспользуемся пакетом click, как показано ниже. Отправка сообщений с помощью нового интерфейса оказывается очень простым делом.

./clibot.py send --message "from cli"
sending message from cli to #general

Рисунок 7.1 демонстрирует значения по умолчанию, а также настраиваемое сообщение от утилиты cli.

#!/usr/bin/env python
import os
import click
from slackclient import SlackClient

SLACK_TOKEN = os.environ["SLACK_API_TOKEN"]

def send_message(channel="#general",
                            message="Hello from my bot!"):
     """Отправить сообщение на канал"""

     slack_client = SlackClient(SLACK_TOKEN)
     res = slack_client.api_call(
     "chat.postMessage",
     channel=channel,
     text=message
  )
  return res

@click.group()
@click.version_option("0.1")
def cli():

  """
  Утилита командной строки для слабаков
  """

@cli.command("send")
@click.option("--message", default="Hello from my bot!",
                       help="text of message")
@click.option("--channel", default="#general",
                       help="general channel")
def send(channel, message):
     click.echo(f"sending message {message} to {channel}")
     send_message(channel, message=message)

if __name__ == '__main__':
     cli()

image

Выводим бот на новый уровень с помощью сервиса AWS Step Functions


После создания каналов связи для отправки сообщений в Slack можно усовершенствовать наш код, а именно: запускать его по расписанию и использовать для каких-либо полезных действий. Сервис пошаговых функций AWS (AWS Step Functions) замечательно подходит для этой цели. В следующем разделе наш бот Slack научится производить скрапинг спортивных страниц Yahoo! игроков НБА, извлекать их места рождения, а затем отправлять эти данные в Slack.

Рисунок 7.2 демонстрирует готовую пошаговую функцию в действии. Первый шаг состоит в извлечении URL профилей игроков НБА, а второй — в использовании библиотеки Beautiful Soup для поиска места рождения каждого из игроков. По завершении выполнения пошаговой функции результаты будут отправлены обратно в Slack.

image

Для координации отдельных частей работы внутри пошаговой функции можно применить AWS Lambda и Chalice. Lambda (https://aws.amazon.com/lambda/) позволяет пользователю выполнять функции в AWS, а фреймворк Chalice (http://chalice.readthedocs.io/en/latest/) дает возможность создания бессерверных приложений на языке Python. Вот некоторые предварительные требования:

  • у пользователя должна быть учетная запись AWS;
  • пользователю необходимы учетные данные для использования API;
  • у роли Lambda (создаваемой Chalice) должна быть политика с привилегиями, необходимыми для вызова соответствующих сервисов AWS, например S3.

Настройка учетных данных IAM


Подробные инструкции по настройке учетных данных AWS можно найти по адресу boto3.readthedocs.io/en/latest/guide/configuration.html. Информацию об экспорте переменных AWS в операционных системах Windows и Linux можно найти здесь. Существует множество способов настройки учетных данных, но пользователи virtualenv могут поместить учетные данные AWS в локальную виртуальную среду, в сценарий /bin/activate:

#Добавляем ключи AWS
AWS_DEFAULT_REGION=us-east-1
AWS_ACCESS_KEY_ID=xxxxxxxx
AWS_SESSION_TOKEN=xxxxxxxx


#Экспортируем ключи
export AWS_DEFAULT_REGION
export AWS_ACCESS_KEY_ID
export AWS_DEFAULT_REGION

Работа с Chalice. У Chalice есть утилита командной строки с множеством доступных команд:

Usage: chalice [OPTIONS] COMMAND [ARGS]...

Options:
    --version                        Show the version and exit.
    --project-dir                   TEXT The project directory. Defaults to CWD
    --debug / --no-debug      Print debug logs to stderr.
    --help                            Show this message and exit.

Commands:
    delete
    deploy
    gen-policy
    generate-pipeline Generate a cloudformation template for a...
    generate-sdk
    local
    logs
    new-project
    package
    url

Код внутри шаблона app.py можно заменить на функции сервиса Lambda. В AWS Chalice удобно то, что он дает возможность создавать, помимо веб-сервисов, «автономные» функции Lambda. Благодаря этой функциональности можно создать несколько функций Lambda, связать их с пошаговой функцией и свести воедино, как кубики «Лего».

Например, можно легко создать запускаемую по расписанию функцию Lambda, которая будет выполнять какие-либо действия:

@app.schedule(Rate(1, unit=Rate.MINUTES))
def every_minute(event):
      """Событие, запланированное для ежеминутного выполнения"""

      #Отправка сообщения боту Slack

Для налаживания взаимодействия с ботом для веб-скрапинга необходимо создать несколько функций. В начале файла находятся импорты и объявлено некоторое количество переменных:

import logging
import csv
from io import StringIO

import boto3
from bs4 import BeautifulSoup
import requests
from chalice import (Chalice, Rate)

APP_NAME = 'scrape-yahoo'
app = Chalice(app_name=APP_NAME)
app.log.setLevel(logging.DEBUG)

Боту может понадобиться хранить часть данных в S3. Следующая функция использует Boto для сохранения результатов в CSV-файле:

def create_s3_file(data, name="birthplaces.csv"):

      csv_buffer = StringIO()
      app.log.info(f"Creating file with {data} for name")
      writer = csv.writer(csv_buffer)
      for key, value in data.items():
           writer.writerow([key,value])
      s3 = boto3.resource('s3')
      res = s3.Bucket('aiwebscraping').\
            put_object(Key=name, Body=csv_buffer.getvalue())
      return res

Функция fetch_page использует библиотеку Beautiful Soup для синтаксического разбора HTML-страницы, расположенной в соответствии с URL статистики НБА, и возвращает объект soup:

def fetch_page(url="https://sports.yahoo.com/nba/stats/"):
      """Извлекает URL Yahoo"""

      #Скачивает страницу и преобразует ее в объект
      # библиотеки Beautiful Soup
      app.log.info(f"Fetching urls from {url}")
      res = requests.get(url)
      soup = BeautifulSoup(res.content, 'html.parser')
      return soup

Функции get_player_links и fetch_player_urls получают ссылки на URL профилей игроков:

def get_player_links(soup):
      """Получает ссылки из URL игроков

      Находит все URL на странице в тегах 'a' и фильтрует их в поисках
      строки 'nba/players'
      """

      nba_player_urls = []
      for link in soup.find_all('a'):
           link_url = link.get('href')
           #Отбрасываем неподходящие
           if link_url:
               if "nba/players" in link_url:
                   print(link_url)
                   nba_player_urls.append(link_url)
      return nba_player_urls


def fetch_player_urls():
      """Возвращает URL игроков"""
      soup = fetch_page()
      urls = get_player_links(soup)
      return urls

Далее в функции find_birthplaces мы извлекаем с расположенных по этим URL страниц места рождения игроков:

def find_birthplaces(urls):
      """Получаем места рождения со страниц профилей игроков NBA
          на Yahoo"""

      birthplaces = {}
      for url in urls:
           profile = requests.get(url)
           profile_url = BeautifulSoup(profile.content, 'html.parser')
           lines = profile_url.text
           res2 = lines.split(",")
           key_line = []
           for line in res2:
                if "Birth" in line:
                    #print(line)
                    key_line.append(line)
           try:
                birth_place = key_line[0].split(":")[-1].strip()
                app.log.info(f"birth_place: {birth_place}")
           except IndexError:
                app.log.info(f"skipping {url}")
                continue
           birthplaces[url] = birth_place
           app.log.info(birth_place)
      return birthplaces

Теперь мы перейдем к функциям Chalice. Обратите внимание: для фреймворка Chalice необходимо, чтобы был создан путь по умолчанию:

#Их можно вызвать с помощью HTTP-запросов
@app.route('/')
def index():
      """Корневой URL"""

      app.log.info(f"/ Route: for {APP_NAME}")
      return {'app_name': APP_NAME}

Следующая функция Lambda представляет собой маршрут, связывающий HTTP URL с написанной ранее функцией:

@app.route('/player_urls')
def player_urls():
      """Извлекает URL игроков"""

      app.log.info(f"/player_urls Route: for {APP_NAME}")
      urls = fetch_player_urls()
      return {"nba_player_urls": urls}

Следующие функции Lambda — автономные, их можно вызвать внутри пошаговой функции:

#Это автономная функция Lambda
@app.lambda_function()
def return_player_urls(event, context):
     """Автономная функция Lambda, возвращающая URL игроков"""

     app.log.info(f"standalone lambda 'return_players_urls'\
        {APP_NAME} with {event} and {context}")
     urls = fetch_player_urls()
     return {"urls": urls}

#Это автономная функция Lambda
@app.lambda_function()
def birthplace_from_urls(event, context):
      """Находит места рождения игроков"""

      app.log.info(f"standalone lambda 'birthplace_from_urls'\
         {APP_NAME} with {event} and {context}")
      payload = event["urls"]
      birthplaces = find_birthplaces(payload)
      return birthplaces

#Это автономная функция Lambda
@app.lambda_function()
def create_s3_file_from_json(event, context):
      """Создает файл S3 на основе данных в формате JSON"""

      app.log.info(f"Creating s3 file with event data {event}\
          and context {context}")
      print(type(event))
      res = create_s3_file(data=event)
      app.log.info(f"response of putting file: {res}")
      return True

Если запустить получившееся приложение Chalice локально, будут выведены следующие результаты:

→ scrape-yahoo git:(master)  chalice local
Serving on 127.0.0.1:8000
scrape-yahoo - INFO - / Route: for scrape-yahoo
127.0.0.1 - - [12/Dec/2017 03:25:42] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [12/Dec/2017 03:25:42] "GET /favicon.ico"
scrape-yahoo - INFO - / Route: for scrape-yahoo
127.0.0.1 - - [12/Dec/2017 03:25:45] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [12/Dec/2017 03:25:45] "GET /favicon.ico"
scrape-yahoo - INFO - /player_urls Route: for scrape-yahoo
scrape-yahoo - INFO - https://sports.yahoo.com/nba/stats/
https://sports.yahoo.com/nba/players/4563/
https://sports.yahoo.com/nba/players/5185/
https://sports.yahoo.com/nba/players/3704/
https://sports.yahoo.com/nba/players/5012/
https://sports.yahoo.com/nba/players/4612/
https://sports.yahoo.com/nba/players/5015/
https://sports.yahoo.com/nba/players/4497/
https://sports.yahoo.com/nba/players/4720/
https://sports.yahoo.com/nba/players/3818/
https://sports.yahoo.com/nba/players/5432/
https://sports.yahoo.com/nba/players/5471/
https://sports.yahoo.com/nba/players/4244/
https://sports.yahoo.com/nba/players/5464/
https://sports.yahoo.com/nba/players/5294/
https://sports.yahoo.com/nba/players/5336/
https://sports.yahoo.com/nba/players/4390/
https://sports.yahoo.com/nba/players/4563/
https://sports.yahoo.com/nba/players/3704/
https://sports.yahoo.com/nba/players/5600/
https://sports.yahoo.com/nba/players/4624/
127.0.0.1 - - [12/Dec/2017 03:25:53] "GET /player_urls"
127.0.0.1 - - [12/Dec/2017 03:25:53] "GET /favicon.ico"

Для развертывания приложения выполните команду chalice deploy:

→ scrape-yahoo git:(master)  chalice deploy
Creating role: scrape-yahoo-dev
Creating deployment package.
Creating lambda function: scrape-yahoo-dev
Initiating first time deployment.
Deploying to API Gateway stage: api
https://bt98uzs1cc.execute-api.us-east-1.amazonaws.com/api/

Благодаря интерфейсу командной строки для HTTP (https://github.com/jakubroztocil/httpie) мы вызываем маршрут HTTP из AWS и извлекаем доступные в /api/player_urls ссылки:

→ scrape-yahoo git:(master)  http \
https://<a lambda route>.amazonaws.com/api/player_urls
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 941
Content-Type: application/json
Date: Tue, 12 Dec 2017 11:48:41 GMT
Via: 1.1 ba90f9bd20de9ac04075a8309c165ab1.cloudfront.net (CloudFront)
X-Amz-Cf-Id: ViZswjo4UeHYwrc9e-5vMVTDhV_Ic0dhVIG0BrDdtYqd5KWcAuZKKQ==
X-Amzn-Trace-Id: sampled=0;root=1-5a2fc217-07cc12d50a4d38a59a688f5c
X-Cache: Miss from cloudfront
x-amzn-RequestId: 64f24fcd-df32-11e7-a81a-2b511652b4f6

{

       "nba_player_urls": [
              "https://sports.yahoo.com/nba/players/4563/",
              "https://sports.yahoo.com/nba/players/5185/",
              "https://sports.yahoo.com/nba/players/3704/",
              "https://sports.yahoo.com/nba/players/5012/",
              "https://sports.yahoo.com/nba/players/4612/",
              "https://sports.yahoo.com/nba/players/5015/",
              "https://sports.yahoo.com/nba/players/4497/",
              "https://sports.yahoo.com/nba/players/4720/",
              "https://sports.yahoo.com/nba/players/3818/",
              "https://sports.yahoo.com/nba/players/5432/",
              "https://sports.yahoo.com/nba/players/5471/",
              "https://sports.yahoo.com/nba/players/4244/",
              "https://sports.yahoo.com/nba/players/5464/",
              "https://sports.yahoo.com/nba/players/5294/",
              "https://sports.yahoo.com/nba/players/5336/",
              "https://sports.yahoo.com/nba/players/4390/",
              "https://sports.yahoo.com/nba/players/4563/",
              "https://sports.yahoo.com/nba/players/3704/",
              "https://sports.yahoo.com/nba/players/5600/",
              "https://sports.yahoo.com/nba/players/4624/"
       ]
}

Еще один удобный способ работы с функциями Lambda — непосредственный их вызов с помощью пакета click и библиотеки Boto языка Python.

Мы можем создать новую утилиту командной строки с названием wscli.py (сокращение от web-scraping command-line interface — «интерфейс командной строки для веб-скрапинга»). В первой части кода мы настраиваем журналирование и импортируем библиотеки:

#!/usr/bin/env python

import logging
import json

import boto3
import click
from pythonjsonlogger import jsonlogger

#Инициализация журналирования
log = logging.getLogger(__name__)
log.setLevel(logging.INFO)
LOGHANDLER = logging.StreamHandler()
FORMMATTER = jsonlogger.JsonFormatter()
LOGHANDLER.setFormatter(FORMMATTER)
log.addHandler(LOGHANDLER)

Следующие три функции предназначены для подключения к функции Lambda через invoke_lambda:

###Вызовы API Boto Lambda
def lambda_connection(region_name="us-east-1"):
      """Создаем подключение к Lambda"""

      lambda_conn = boto3.client("lambda", region_name=region_name)
      extra_msg = {"region_name": region_name, "aws_service": "lambda"}
      log.info("instantiate lambda client", extra=extra_msg)
      return lambda_conn

def parse_lambda_result(response):
      """Получаем результаты из ответа библиотеки Boto в формате JSON"""

            body = response['Payload']
      json_result = body.read()
      lambda_return_value = json.loads(json_result)
      return lambda_return_value

def invoke_lambda(func_name, lambda_conn, payload=None,
                             invocation_type="RequestResponse"):
      """Вызываем функцию Lambda"""


      extra_msg = {"function_name": func_name, "aws_service": "lambda",
                           "payload":payload}
      log.info("Calling lambda function", extra=extra_msg)
      if not payload:
           payload = json.dumps({"payload":"None"})

      response = lambda_conn.invoke(FunctionName=func_name,
                       InvocationType=invocation_type,
                       Payload=payload
      )
      log.info(response, extra=extra_msg)
      lambda_return_value = parse_lambda_result(response)
      return lambda_return_value

Обертываем функцию invoke_lambda с помощью пакета Python для создания утилит командной строки Click. Обратите внимание, что мы задали значение по умолчанию для опции --func, при котором используется развернутая нами ранее функция Lambda:

@click.group()
@click.version_option("1.0")
def cli():
      """Вспомогательная утилита командной строки для веб-скрапинга"""

@cli.command("lambda")
@click.option("--func",
            default="scrape-yahoo-dev-return_player_urls",
            help="name of execution")
@click.option("--payload", default='{"cli":"invoke"}',
            help="name of payload")
def call_lambda(func, payload):
       """Вызывает функцию Lambda

       ./wscli.py lambda
       """
       click.echo(click.style("Lambda Function invoked from cli:",
             bg='blue', fg='white'))
       conn = lambda_connection()
       lambda_return_value = invoke_lambda(func_name=func,
               lambda_conn=conn,
               payload=payload)
       formatted_json = json.dumps(lambda_return_value,
               sort_keys=True, indent=4)
       click.echo(click.style(
            "Lambda Return Value Below:", bg='blue', fg='white'))
       click.echo(click.style(formatted_json,fg="red"))

if __name__ == "__main__":
     cli()

Выводимые этой утилитой результаты аналогичны вызову HTTP-интерфейса:

→ X ./wscli.py lambda \
--func=scrape-yahoo-dev-birthplace_from_urls\
--payload '{"url":["https://sports.yahoo.com/nba/players/4624/",\
"https://sports.yahoo.com/nba/players/5185/"]}'
Lambda Function invoked from cli:
{"message": "instantiate lambda client",
"region_name": "us-east-1", "aws_service": "lambda"}
{"message": "Calling lambda function",
"function_name": "scrape-yahoo-dev-birthplace_from_urls",
"aws_service": "lambda", "payload":
"{\"url\":[\"https://sports.yahoo.com/nba/players/4624/\",
\"https://sports.yahoo.com/nba/players/5185/\"]}"}
{"message": null, "ResponseMetadata":
{"RequestId": "a6049115-df59-11e7-935d-bb1de9c0649d",
"HTTPStatusCode": 200, "HTTPHeaders":
{"date": "Tue, 12 Dec 2017 16:29:43 GMT", "content-type":
"application/json", "content-length": "118", "connection":
"keep-alive", "x-amzn-requestid":
"a6049115-df59-11e7-935d-bb1de9c0649d",
"x-amzn-remapped-content-length": "0", "x-amz-executed-version":
"$LATEST", "x-amzn-trace-id":
"root=1-5a3003f2-2583679b2456022568ed0682;sampled=0"},
"RetryAttempts": 0}, "StatusCode": 200,
"ExecutedVersion": "$LATEST", "Payload":
"<botocore.response.StreamingBody object at 0x10ee37dd8>",
"function_name": "scrape-yahoo-dev-birthplace_from_urls",
"aws_service": "lambda", "payload":
"{\"url\":[\"https://sports.yahoo.com/nba/players/4624/\",
\"https://sports.yahoo.com/nba/players/5185/\"]}"}
Lambda Return Value Below:
{
        "https://sports.yahoo.com/nba/players/4624/": "Indianapolis",
        "https://sports.yahoo.com/nba/players/5185/": "Athens"
}

Завершение создания пошаговой функции


Последний этап создания пошаговой функции, как описывается в документации от AWS (https://docs.aws.amazon.com/step-functions/latest/dg/tutorial-creating-activity-state-machine.html), — создание с помощью веб-интерфейса структуры конечного автомата в формате нотации объектов JavaScript (JavaScript Object Notation, JSON). Следующий код демонстрирует этот конвейер, начиная от исходных функций Lambda для скрапинга Yahoo!, сохранения данных в файле S3 и, наконец, отправки содержимого в Slack:

{
      "Comment": "Fetch Player Urls",
      "StartAt": "FetchUrls",
      "States": {
         "FetchUrls": {
             "Type": "Task",
             "Resource": \
             "arn:aws:lambda:us-east-1:561744971673:\
             function:scrape-yahoo-dev-return_player_urls",
             "Next": "FetchBirthplaces"
         },
         "FetchBirthplaces": {
             "Type": "Task",
             "Resource": \
             "arn:aws:lambda:us-east-1:561744971673:\
             function:scrape-yahoo-dev-birthplace_from_urls",
             "Next": "WriteToS3"
         },
          "WriteToS3": {
             "Type": "Task",
             "Resource": "arn:aws:lambda:us-east-1:\
             561744971673:function:scrape-yahoo-dev-create_s3_file_from_json",
             "Next": "SendToSlack"
         },
         "SendToSlack": {
             "Type": "Task",
             "Resource": "arn:aws:lambda:us-east-1:561744971673:\
             function:send_message",
             "Next": "Finish"
         },

             "Finish": {
             "Type": "Pass",
             "Result": "Finished",
             "End": true
          }
     }
}

На рис. 7.2 было показано выполнение первой части этого конвейера. Чрезвычайно полезна возможность видеть промежуточные результаты работы конечного автомата. Кроме того, возможность мониторинга в режиме реального времени каждой части конечного автомата очень удобна для отладки.

Рисунок 7.3 демонстрирует полный конвейер с добавлением шагов записи в S3-файл и отправки содержимого в Slack. Осталось только решить, как запускать эту утилиту скрапинга — через определенный интервал времени или в ответ на какое-либо событие.

image

Резюме


В этой главе вы познакомились с множеством потрясающих концепций построения приложений ИИ. В ней были созданы бот Slack и утилита веб-скрапинга, соединенные затем с помощью бессерверных сервисов от AWS. В такой начальный каркас можно добавить еще много всего — например, Lambda-функцию обработки написанных на естественных языках текстов для чтения веб-страниц и получения их краткого содержимого или алгоритм кластеризации без учителя, который бы кластеризовал новых игроков НБА по произвольным атрибутам.

» Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок

Для Хаброжителей скидка 20% по купону — Гифт

P.S.: 7% от стоимости книги пойдет на перевод новых компьютерных книг, список сданных в типографию книг здесь.
Tags:
Hubs:
+12
Comments 2
Comments Comments 2

Articles

Information

Website
piter.com
Registered
Founded
Employees
201–500 employees
Location
Россия