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

    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% от стоимости книги пойдет на перевод новых компьютерных книг, список сданных в типографию книг здесь.
    Издательский дом «Питер»
    212,00
    Компания
    Поделиться публикацией

    Комментарии 2

      0

      В принципе интересно было бы купить эту книгу у вас и я постоянно у вас закупался раньше, но с каждым днем ваш сервис все хуже и хуже.


      В прошлый раз я заказал у вас 5 книг из которых два экземпляра относительно недавно изданной вами книги "Чистый код: создание, анализ и рефакторинг". Мне пришли три вторичные книги без 2 книг ради которого и оформлял заказ в принципе. Доставили заранее никак не уведомив что книг не будет или как то еще.
      Когда я позвонил по телефону сказали что потом доставят, а я попросил как то уведомить меня о том что я все таки получу эти книги.
      Сейчас в личном кабинете у меня светится что я все получил (нет, не получил). Ни одного письма или сообщения до сих пор не было, хотя прошло чуть больше месяца.


      Хотелось бы узнать какие шансы что я снова куплю у вас книги и опять ничего не получу. (про извинияния и уведомления о том когда исправите свою оплошность я уже и не мечтаю).

        +2
        Спасибо за фидбек, написали в личку для уточнения.

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое