Path: blob/master/site/pt-br/agents/tutorials/bandits_tutorial.ipynb
25118 views
Copyright 2023 The TF-Agents Authors.
Tutorial sobre Multi-Armed Bandits no TF-Agents
Configuração
Se você ainda não instalou as seguintes dependências, execute:
Importação
Introdução
O problema Multi-Armed Bandit (MAB) é um caso especial de aprendizado por reforço: um agente coleta recompensas em um ambiente ao pegar algumas ações após observar algum estado do ambiente. A principal diferença entre o RL geral e o MAB é que, no MAB, presumimos que a ação tomada pelo agente não influencia o próximo estado do ambiente. Por isso, os agentes não modelam transições de estado, creditam recompensas a ações anteriores ou "planejam antes" para chegar a estados ricos de recompensas.
Como em outros domínios de RL, o objetivo de um agente de MAB é encontrar uma política que colete o máximo de recompensas possível. Seria um erro, no entanto, sempre tentar explorar a ação que promete a recompensa mais alta, porque então há uma chance de perder ações melhores se não explorarmos o suficiente. Esse é o principal problema a ser resolvido em (MAB), geralmente chamado de dilema exploration-exploitation.
Os ambientes bandit, políticas e agentes para MAB podem ser encontrados em subdiretórios de tf_agents/bandits.
Ambientes
No TF-Agents, a classe de ambiente serve a função de dar informações sobre o estado atual (isso é chamado de observação ou contexto), recebendo uma ação como entrada, realizando uma transição de estado e gerando uma recompensa. Essa classe também lida com a redefinição quando um episódio termina, para um novo episódio começar. Isso é realizado com a chamada de uma função reset
quando um estado é rotulado como o "último" do episódio.
Para mais detalhes, confira o tutorial de ambientes do TF-Agents.
Conforme mencionado acima, o MAB difere do RL geral porque as ações não influenciam a próxima observação. Outra diferença é que, nos Bandits, não há "episódios": todo timestep começa com uma nova observação, independentemente dos timesteps anteriores.
Para conferir se as observações são independentes e abstrair o conceito de episódios do RL, apresentamos subclasses de PyEnvironment
e TFEnvironment
: BanditPyEnvironment e BanditTFEnvironment. Essas classes expõem duas funções de membros privadas que permanecem para serem implementadas pelo usuário:
e
A função _observe
retorna uma observação. Em seguida, a política escolhe uma ação com base nessa observação. A _apply_action
recebe essa ação como entrada e retorna a recompensa correspondente. Essas funções de membros privadas são chamadas pelas funções reset
e step
, respectivamente.
A classe abstrata ínterim acima implementa as funções _reset
e _step
de PyEnvironment
e expõe as funções abstratas _observe
e _apply_action
a serem implementadas pelas subclasses.
Um exemplo simples de classe de ambiente
A seguinte classe fornece um ambiente bastante simples para o qual a observação é um número inteiro aleatório entre -2 e 2, há 3 ações possíveis (0, 1, 2) e a recompensa é o produto da ação e da observação.
Agora podemos usar esse ambiente para obter observações e receber recompensas para nossas ações.
Ambientes TF
É possível definir um ambiente de bandit ao criar subclasses de BanditTFEnvironment
ou, de maneira semelhante a ambientes de RL, é possível definir um BanditPyEnvironment
e o envolver com TFPyEnvironment
. Para fins de simplicidade, vamos escolher a última opção neste tutorial.
Políticas
Uma política em um problema de bandit funciona da mesma maneira que um problema de RL: fornece uma ação (ou distribuição de ações), a partir de uma observação como entrada.
Para mais detalhes, confira o tutorial de política do TF-Agents.
Assim como ambientes, há duas maneiras de construir uma política: é possível criar uma PyPolicy
e a envolver com uma TFPyPolicy
ou criar diretamente uma TFPolicy
. Aqui decidimos usar o método direto.
Como esse exemplo é bastante simples, podemos definir a política ideal manualmente. A ação só depende do sinal da observação, 0 quando é negativo e 2 quando é positivo.
Agora podemos solicitar uma observação do ambiente, chamar a política para escolher uma ação, e o ambiente gerará a recompensa:
A maneira que os ambientes de bandit são implementados garante que, a cada passo realizado, seja recebida a recompensa para a ação tomada e também a próxima observação.
Agentes
Agora que temos os ambientes e as políticas de bandit, é hora de definir também os agentes de bandit, que lidam com a mudança da política com base nas amostras de treinamento.
A API para agentes de bandit não são diferentes dos agentes de RL: o agente só precisa implementar os métodos _initialize
e _train
e definir uma policy
e collect_policy
.
Um ambiente mais complicado
Antes de escrever nosso agente de bandit, precisamos ter um ambiente um pouco mais difícil de entender. Para deixar as coisas mais animadas, o próximo ambiente sempre fornecerá reward = observation * action
ou reward = -observation * action
. Isso será decidido quando o ambiente for inicializado.
Uma política mais complicada
Um ambiente mais complicado pede uma política mais complicada. Precisamos de uma política que detecte o comportamento do ambiente subjacente. Há três situações com que a política precisa lidar:
O agente ainda não detectou a versão em execução do ambiente.
O agente detectou que a versão original do ambiente está em execução.
O agente detectou que a versão invertida do ambiente está em execução.
Definimos uma tf_variable
chamada _situation
para armazenar essa informações codificadas como valores em [0, 2]
e fazer a política se comportar de maneira adequada.
Agente
Agora é hora de definir o agente que detecta o sinal do ambiente e define a política de maneira apropriada.
No código acima, o agente define a política, e a variável situation
é compartilhada pelo agente e pela política.
Além disso, o parâmetro experience
da função _train
é uma trajetória:
Trajetórias
No TF-Agents, trajectories
são tuplas nomeadas que contêm amostras dos passos anteriores realizados. Essas amostras são usadas pelo agente para treinar e atualizar a política. No RL, as trajetórias precisam conter informações sobre o estado atual, o estado seguinte e se o episódio atual já terminou. Como no mundo do Bandit não precisamos disso, configuramos uma função helper para criar uma trajetória:
Treinamento de um agente
Agora todas as partes já estão prontas para treinar nosso agente de bandit.
A partir da saída, é possível ver que, após o segundo passo (a menos que a observação tenha sido 0 no primeiro passo), a política escolhe a ação corretamente e, então, a recompensa coletada é sempre não negativa.
Um exemplo real de Bandit contextual
Ambiente estocástico estacionário com funções de payoff lineares
O ambiente usado neste exemplo é o StationaryStochasticPyEnvironment. Esse ambiente aceita como parâmetro uma função (geralmente ruidosa) para dar observações (contexto) e, para cada braço (arm), aceita uma função (também ruidosa) que computa a recompensa com base na observação específica. Em nosso exemplo, usamos o contexto como amostra de maneira uniforme a partir de um cubo d-dimensional, e as funções de recompensa são funções lineares do contexto, além de um pouco de ruído gaussiano.
Agente LinUCB
O agente abaixo implementa o algoritmo LinUCB.
Métrica de arrependimento
A métrica mais importante do Bandit é o arrependimento, calculado como a diferença entre a recompensa coletada pelo agente e a recompensa esperada de uma política de oráculo que tem acesso às funções de recompensa do ambiente. A RegretMetric precisa de uma função baseline_reward_fn que calcule a melhor recompensa esperada possível a partir de uma observação. Para nosso exemplo, precisamos pegar o máximo dos equivalentes sem ruído das funções de recompensa que já definidos para o ambiente.
Treinamento
Agora unimos todos os componentes apresentados acima: o ambiente, a política e o agente. Executamos a política no ambiente, geramos dados de treinamento com a ajuda de um driver e treinamos o agente com os dados.
Observe que há dois parâmetros que, juntos, especificam o número de passos realizados. num_iterations
especifica quantas vezes executamos o loop de treinamento, enquanto o driver faz steps_per_loop
passos por iteração. O principal motivo para manter esses dois parâmetros é que algumas operações são realizadas por iteração e outras são realizadas pelo driver em todos os passos. Por exemplo, a função train
do agente só é chamada uma vez por iteração. O trade-off aqui é que, se treinarmos com mais frequência, nossa política será mais "recente", mas o treinamento com lotes maiores pode economizar mais tempo.
Depois de executar o último fragmento de código, o plot resultante (com sorte) mostra que o arrependimento médio está caindo conforme o agente é treinado, e a política fica melhor em descobrir a ação correta considerando a observação.
Próximos passos
Para ver mais exemplos práticos, consulte bandits/agents/examples, com exemplos prontos para execução em diferentes agentes e ambientes.
A biblioteca do TF-Agents também é capaz de lidar com Multi-Armed Bandits com características por braço. Para isso, consulte o tutorial de bandits por braço.