O livro “Pragmatic AI. Aprendizado de máquina e tecnologia em nuvem

imagem Oi, habrozhiteli! Este livro Noah Gift é para qualquer pessoa interessada em IA, aprendizado de máquina, computação em nuvem e qualquer combinação de tópicos de dados. Tanto os programadores quanto os técnicos que se preocupam com isso encontrarão aqui informações úteis. Exemplos de código são dados em Python. Ele abrange muitos tópicos avançados, como o uso de plataformas em nuvem (por exemplo, AWS, GCP e Azure), além de técnicas de aprendizado de máquina e a implementação da IA. Os Jedi que são fluentes em Python, computação em nuvem e ML também encontrarão muitas idéias úteis para si mesmos que podem aplicar imediatamente em seu trabalho atual.

Convidamos você a ler um trecho do livro "Criando um Slack Bot Inteligente na AWS"

Há muito que as pessoas sonham em criar uma "vida artificial". Na maioria das vezes, embora isso seja possível criando bots. Os robôs estão se tornando uma parte cada vez mais integrante de nossas vidas diárias, especialmente desde o advento da Siri da Apple e Alexa da Amazon. Neste capítulo, revelaremos todos os segredos da criação de bots.

Criação de bot


Para criar o bot, usaremos a biblioteca Slack para a linguagem Python (https://github.com/slackapi/python-slackclient). Para começar com o Slack, você precisa gerar um token de identificação. Em geral, faz sentido ao trabalhar com esses marcadores exportar uma variável de ambiente. Costumo fazer isso no virtualenv, obtendo acesso automaticamente a ele quando executado no ambiente atual. Para fazer isso, você precisa “quebrar” o utilitário virtualenv um pouco editando o script de ativação.

Ao exportar a variável Slack no script ~ / .env / bin / enable, ela se parecerá com a abaixo.

E apenas para obter informações, se você deseja acompanhar as inovações mais recentes, é recomendável usar o novo utilitário oficial Python para gerenciar o ambiente - 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 

É conveniente usar o comando printenv dos sistemas operacionais OS X e Linux para verificar se a variável de ambiente está configurada. Depois disso, você pode usar o seguinte script curto para verificar o envio de uma mensagem:

 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:" ) 

Também é importante notar que o utilitário pipenv é a solução recomendada, combinando os recursos dos utilitários pip e virtualenv em um componente. Tornou-se um novo padrão, por isso faz sentido olhar para ele do ponto de vista do gerenciamento de pacotes.

Converter biblioteca em utilitário de linha de comando


Como nos outros exemplos deste livro, é uma boa ideia converter nosso código em um utilitário de linha de comando para facilitar o teste de novas idéias. Vale a pena notar que muitos desenvolvedores iniciantes geralmente preferem não ferramentas de linha de comando, mas outras soluções, por exemplo, elas simplesmente funcionam nos notebooks Jupiter. Desempenharei por um curto período o papel de advogado do diabo e fiz uma pergunta que os leitores podem ter: “Por que precisamos de utilitários de linha de comando em um projeto baseado nos blocos de anotações de Júpiter? Não é o objetivo dos blocos de notas de Júpiter tornar desnecessário o shell e a linha de comando? " Adicionar um utilitário de linha de comando a um projeto é bom porque permite que você experimente rapidamente diferentes opções de entrada. Os blocos de código do notebook Jupiter não aceitam entrada; em certo sentido, esses são scripts com dados conectados.

Muitos utilitários de linha de comando nas plataformas GCP e AWS não existem por acaso: eles fornecem flexibilidade e recursos que não estão disponíveis nas interfaces gráficas. Uma maravilhosa coleção de ensaios sobre esse assunto, do escritor de ficção científica Neal Stephenson, é chamada "No começo ... havia uma linha de comando". Nele, Stevenson diz: "As GUIs acarretam sobrecarga adicional significativa para cada componente de software, mesmo o menor, que altera completamente o ambiente de programação". Ele termina a coleção com as palavras: “... a vida é uma coisa muito difícil e difícil; nenhuma interface mudará isso; e qualquer um que pense de outra maneira é um simplório ... "Bastante difícil, mas minha experiência sugere que isso é verdade. A vida com a linha de comando está melhorando. Experimente e você não vai querer voltar para a GUI.

Para fazer isso, usaremos o pacote de clique, como mostrado abaixo. O envio de mensagens usando a nova interface é muito simples.

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

A Figura 7.1 mostra os valores padrão, bem como a mensagem customizada do utilitário 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() 

imagem

Levamos o bot a um novo nível usando o serviço AWS Step Functions


Após criar canais de comunicação para o envio de mensagens ao Slack, você pode aprimorar nosso código, a saber: executá-lo de acordo com uma programação e usá-lo para quaisquer ações úteis. O serviço AWS Step Functions é ótimo para isso. Na próxima seção, nosso bot do Slack aprenderá a descartar as páginas de esportes do Yahoo! Jogadores da NBA, recuperem seu local de nascimento e depois enviem esses dados para o Slack.

A Figura 7.2 mostra uma função passo a passo pronta em ação. O primeiro passo é extrair os URLs do perfil do jogador da NBA e o segundo é usar a biblioteca Beautiful Soup para encontrar o local de nascimento de cada jogador. Após a conclusão da função passo a passo, os resultados serão enviados de volta ao Slack.

imagem

O AWS Lambda e o Chalice podem ser usados ​​para coordenar as partes individuais do trabalho em uma função de etapa. O Lambda (https://aws.amazon.com/lambda/) permite que o usuário execute funções na AWS, e a estrutura do Chalice (http://chalice.readthedocs.io/en/latest/) permite criar aplicativos sem servidor em Python. Aqui estão alguns pré-requisitos:

  • O usuário deve ter uma conta da AWS.
  • o usuário precisa de credenciais para usar a API;
  • a função Lambda (criada pelo Chalice) deve ter uma política com os privilégios necessários para chamar os serviços da AWS correspondentes, como o S3.

Configurar credenciais do IAM


Instruções detalhadas para configurar credenciais da AWS podem ser encontradas em boto3.readthedocs.io/en/latest/guide/configuration.html . Informações sobre a exportação de variáveis ​​da AWS nos sistemas operacionais Windows e Linux podem ser encontradas aqui . Existem várias maneiras de configurar credenciais, mas os usuários do virtualenv podem colocar credenciais da AWS em um ambiente virtual local no script / bin / enable:

 #  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 

Trabalhar com cálice. O Chalice possui um utilitário de linha de comando com muitos comandos disponíveis:

 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 

O código dentro do modelo app.py pode ser substituído pelas funções do serviço Lambda. O AWS Chalice é conveniente, pois possibilita criar, além de serviços da Web, funções Lambda “independentes”. Graças a essa funcionalidade, você pode criar várias funções Lambda, associá-las a uma função passo a passo e reuni-las como cubos de Lego.

Por exemplo, você pode criar facilmente uma função Lambda agendada que executará qualquer ação:

 @app.schedule(Rate(1, unit=Rate.MINUTES)) def every_minute(event): """,    """ #   Slack 

Para estabelecer interação com o bot para raspagem na web, você precisa criar várias funções. No início do arquivo, há importações e várias variáveis ​​são declaradas:

 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) 

O bot pode precisar armazenar alguns dados no S3. A função a seguir usa Boto para salvar os resultados em um arquivo 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 

A função fetch_page usa a biblioteca Beautiful Soup para analisar uma página HTML localizada de acordo com a URL de estatísticas da NBA e retorna um objeto de sopa:

 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 

As funções get_player_links e fetch_player_urls obtêm links para os URLs do perfil do jogador:

 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 

Em seguida, na função find_birthplaces, extraímos os locais de nascimento dos jogadores dos URLs localizados nestas páginas:

 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 

Agora vamos passar para as funções do cálice. Observe: para a estrutura do Chalice, o caminho padrão deve ser criado:

 #     HTTP- @app.route('/') def index(): """ URL""" app.log.info(f"/ Route: for {APP_NAME}") return {'app_name': APP_NAME} 

A seguinte função Lambda é uma rota que conecta uma URL HTTP a uma função escrita anteriormente:

 @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} 

As seguintes funções do Lambda são independentes, elas podem ser chamadas dentro de uma função passo a passo:

 #   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 

Se você executar o aplicativo Chalice resultante localmente, os seguintes resultados serão exibidos:

 → 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" 

Para implantar o aplicativo, execute o comando 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/ 

Graças à interface da linha de comandos para HTTP (https://github.com/jakubroztocil/httpie), chamamos a rota HTTP da AWS e extraímos os links disponíveis em / 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/" ] } 

Outra maneira conveniente de trabalhar com as funções do Lambda é chamá-las diretamente, usando o pacote click e a biblioteca Python Boto.

Podemos criar um novo utilitário de linha de comando chamado wscli.py (abreviação de interface de linha de comando para raspagem na web - “interface de linha de comando para raspagem na web”). Na primeira parte do código, configuramos o log e importamos as bibliotecas:

 #!/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) 

As três funções a seguir foram projetadas para se conectar à função Lambda por meio de 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 

Envolvemos a função invoke_lambda com o pacote Python para criar utilitários de linha de comando Click. Observe que definimos o valor padrão para a opção --func, que usa a função Lambda que implantamos anteriormente:

 @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() 

Os resultados exibidos por este utilitário são semelhantes aos da interface 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" } 

Concluir a criação de uma função passo a passo


A etapa final na criação de uma função passo a passo, conforme descrito na documentação da AWS (https://docs.aws.amazon.com/step-functions/latest/dg/tutorial-creating-activity-state-machine.html), é criar usando interface da web da estrutura da máquina de estado no formato JavaScript Object Notation (JSON). O código a seguir demonstra esse pipeline, iniciando pelas funções originais do Lambda para raspar o Yahoo !, armazenar dados em um arquivo S3 e finalmente enviar conteúdo para o 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 } } } 

Na fig. 7.2 a execução da primeira parte deste pipeline foi mostrada. É extremamente útil ver os resultados intermediários da máquina de estado. Além disso, o monitoramento em tempo real de cada parte da máquina de estado é muito conveniente para depuração.

A Figura 7.3 mostra um pipeline completo com a adição das etapas de gravação em um arquivo S3 e envio de conteúdo para o Slack. Resta apenas decidir como executar esse utilitário de raspagem - em um determinado intervalo de tempo ou em resposta a um evento.

imagem

Sumário


Neste capítulo, você foi apresentado a muitos conceitos surpreendentes para a criação de aplicativos de IA. Ele criou o Slack bot e o utilitário de raspagem da Web, que foram conectados usando serviços sem servidor da AWS. Você pode adicionar muito mais a essa estrutura inicial - por exemplo, a função Lambda para processar textos escritos em linguagens naturais para ler páginas da Web e obter seu breve conteúdo ou o algoritmo de agrupamento sem um professor, que agruparia novos jogadores da NBA por atributos arbitrários.

»Mais informações sobre o livro podem ser encontradas no site do editor
» Conteúdo
» Trecho

20% de desconto no cupom para Couriers - Gift

PS: 7% do custo do livro será destinado à tradução de novos livros de informática, a lista de livros entregues à gráfica aqui .

Source: https://habr.com/ru/post/pt439990/


All Articles