Então o que é A3C? O cálculo do A3C foi feito recentemente pelo grupo do DeepMind do Google, e fez um sprinkle pelo… basicamente obsoleto DQN. Foi mais rápido, mais fácil, cada vez mais vigoroso e pronto para atingir notas muito melhores na bateria padrão dos empreendimentos Profound RL. No geral, ele poderia funcionar em espaços de atividade consistentes e discretos. Diante disso, tornou-se o mergulho para o cálculo de RL Profundo para novas questões de teste com estados complexos e espaços de atividade. Verdade seja dita, o OpenAI acaba de descarregar uma versão do A3C como seu “especialista geral de iniciação” por trabalhar com seu novo (e extremamente diferente) conjunto de situações do Universo.

Os 3 A partir do A3C

O Pundit de Entretenimento não-corrente Bit of Leeway é uma peça séria. Que tal começarmos por descarregar o nome, e a partir daí, começarmos a descarregar a mecânica do próprio cálculo.

Não-corrente: Não como a DQN, onde um operador solitário falou por um sistema neural solitário colabora com uma situação solitária, a A3C utiliza diferentes manifestações do acima exposto de forma a se adaptar de forma ainda mais produtiva. No A3C há um sistema mundial e diferentes operadores especializados, cada um com sua própria disposição de parâmetros do sistema. Cada um destes operadores se conecta com sua própria duplicata da terra simultaneamente, já que diferentes especialistas estão se comunicando com seu entorno. A explicação deste trabalho é superior a ter um especialista solitário (além da velocidade de realização de mais trabalho), é que a experiência de cada operador está livre da experiência dos outros. Nesta linha, a experiência geral acessível para a preparação acaba por ser progressivamente variada.

Animador Pundit: Até agora este arranjo tem se concentrado em estratégias de ênfase de estima, por exemplo, Q-learning, ou técnicas de ciclo de abordagem, por exemplo, Strategy Slope. O personagem na tela Pundit consolida as vantagens das duas metodologias. Por conta do A3C, nosso sistema avaliará tanto uma capacidade V(s) de valor (quão grande é um estado específico em que se deve estar) quanto uma estratégia π(s) (muitas probabilidades de rendimento de atividades). Cada um destes será independente, com camadas completamente associadas, no ponto mais alto do sistema. Basicamente, o operador utiliza o medidor de valor (o pundit) para atualizar a estratégia (o animador) de forma mais inteligente do que as estratégias convencionais de inclinação de arranjo.

Implementando o Algoritmo

Durante o tempo gasto estruturando esta utilização do cálculo do A3C, utilizei como referência as execuções de qualidade pela DennyBritz e OpenAI. As duas das quais prescrevo profundamente no caso de você querer ver opções em contraste com meu código aqui. Cada segmento inserido aqui é tomado fora de qualquer conexão relevante ao assunto em questão para fins instrucionais, e não será executado sozinho. Para ver e executar a execução completa e útil do A3C, veja meu cofre do Github.

O diagrama geral da engenharia do código é:

AC_Network – Esta classe contém todas as operações de Tensorflow para fazer os próprios sistemas.

Especialista – Esta classe contém um duplicado da AC_Network, uma classe de domínio, assim como todas as razões para cooperar com a natureza, e refrescar o sistema mundial.

Código de nível elevado para construir as ocorrências dos Especialistas e executá-las em paralelo.

O cálculo do A3C começa com o desenvolvimento do sistema mundial. Este sistema será composto por camadas convolutivas para processar condições espaciais, seguidas por uma camada LSTM para processar condições fugazes, por fim, camadas de valor e rendimento estratégico. A seguir, o código modelo para a configuração do próprio gráfico do sistema.

classe AC_Network():

    def __init__(self,s_size,a_size,a_size,scope,trainer):

        com tf.variable_scope(escopo):

            #Níveis de entrada e codificação visual

            self.inputs = tf.placeholder(shape=[None,s_size],dtype=tf.float32)

            self.imageIn = tf.reshape(self.inputs,shape=[-1,84,84,1])

            self.conv1 = slim.conv2d(activation_fn=tf.nn.elu,

                inputs=self.imageIn,num_outputs=16,

                kernel_size=[8,8],stride=[4,4],padding=’VALID’)

            self.conv2 = slim.conv2d(activation_fn=tf.nn.elu,

                inputs=self.conv1,num_outputs=32,

                kernel_size=[4,4],stride=[2,2],padding=’VALID’)

            hidden = slim.fully_connected(slim.flatten(self.conv2),256,activation_fn=tf.nn.elu)

            #Rede atual para dependências temporais

            lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(256,state_is_tuple=True)

            c_init = np.zeros((1, lstm_cell.state_size.c), np.float32)

            h_init = np.zeros((1, lstm_cell.state_size.h), np.float32)

            self.state_init = [c_init, h_init]

            c_in = tf.placeholder(tf.float32, [1, lstm_cell.state_size.c])

            h_in = tf.placeholder(tf.float32, [1, lstm_cell.state_size.h])

            self.state_in = (c_in, h_in)

            rnn_in = tf.expand_dims(hidden, [0])

            step_size = tf.shape(self.imageIn)[:1]

            state_in = tf.nn.rnn_cell.LSTMStateTuple(c_in, h_in)

            lstm_outputs, lstm_state = tf.nn.dynamic_rnn(

                lstm_cell, rnn_in, initial_state=state_in, sequence_length=step_size,

                time_major=False)

            lstm_c, lstm_h = lstm_state

            self.state_out = (lstm_c[:1, :], lstm_h[:1, :])

            rnn_out = tf.reshape(lstm_outputs, [-1, 256])

            #Camadas de saída para política e estimativas de valor

            self.policy = slim.fully_connected(rnn_out,a_size,

                activation_fn=tf.nn.softmax,

                weights_initializer=normalized_columns_initializer(0.01),

                bias_inicializer=Nenhum)

            self.value = slim.fully_connected(rnn_out,1,

                activation_fn=None,

                weights_initializer=normalized_columns_initializer(1.0),

                bias_inicializer=Nenhum)

Em seguida, são feitas muitas operadoras, cada uma com seu próprio sistema e condição. Cada um desses operários é executado em uma string de processador diferente, portanto não deve haver um número maior de operários do que as strings em sua CPU.

com tf.device(“/cpu:0”):

    master_network = AC_Network(s_size,a_size,’global’,None) # Gerar rede global

    num_workers = multiprocessing.cpu_count() # Definir trabalhadores ou número de threads de CPU disponíveis

    trabalhadores = []

    # Criar classes de trabalhadores

    para i no range(num_workers):

        workers.append(Trabalhador(DoomGame(),i,s_size,a_size,trainer,saver,model_path))

com tf.Session() como sess:

    coord = tf.train.train.Coordinator()

    if load_model == Verdadeiro:

        imprimir ‘Carregando Modelo…

        ckpt = tf.train.get_checkpoint_state(model_path)

        saver.restore(sess,ckpt.model_checkpoint_path)

    senão:

        sess.run(tf.global_variables_initializer())

    # Aqui é onde a magia assíncrona acontece.

    # Iniciar o processo de “trabalho” para cada trabalhador em uma ameaça separada.

    worker_threads = []

    para trabalhador em trabalhador:

        worker_work = lambda: worker.work(max_episode_length,gamma,master_network,sess,coord)

        t = threading.Thread(target=(worker_work))

        t.start()

        worker_threads.append(t)

    coord.join(worker_threads)

Todo trabalhador começa ajustando seus parâmetros do sistema para os do sistema mundial. Podemos fazer isso desenvolvendo uma operação de Tensorflow que define todos os fatores do sistema especializado do bairro para o mesmo incentivo variável no sistema mundial.

Cada especialista, nesse ponto, associa-se com sua própria duplicata da natureza e reúne o entendimento. Cada um guarda um conjunto de tufos de experiência (percepção, atividade, remuneração, feito, estima) que se somam continuamente à comunicação com a Terra.

trabalhadora():

      ….

      ….

      ….

      def work(auto,max_episode_length,gamma,global_AC,sess,coord):

        episode_count = 0

        total_passo_conta = 0

        imprimir “Trabalhador iniciante” + str(self.number)

        com sess.as_default(), sess.graph.as_default():                

            enquanto não coord.should_stop():

                sess.run(self.update_local_ops)

                episode_buffer = []

                episode_values = []

                episode_frames = []

                episode_reward = 0

                episode_step_count = 0

                d = Falso

                self.env.new_episode()

                s = self.env.get_state().screen_buffer

                episode_frames.append(s)

                s = process_frame(s)

                rnn_state = auto.local_AC.state_init

                enquanto self.env.is_episode_finished() == Falso:

                    #Tomar uma ação usando as probabilidades de saída da rede de políticas.

                    a_dist,v,rnn_state = sess.run([auto.local_AC.policy,auto.local_AC.value,auto.local_AC.state_out],

                        feed_dict={self.local_AC.inputs:[s],

                        self.local_AC.state_in[0]:rnn_state[0],

                        self.local_AC.state_in[1]:rnn_state[1]})

                    a = np.random.choice(a_dist[0],p=a_dist[0])

                    a = np.argmax(a_dist == a)

                    r = self.env.make_action(self.actions[a]) / 100.0

                    d = self.env.is_episode_finished()

                    se d === Falso:

                        s1 = self.env.get_state().screen_buffer

                        episode_frames.append(s1)

                        s1 = process_frame(s1)

                    senão:

                        s1 = s

                    episode_buffer.append([s,a,r,s1,d,v[0,0]])

                    episode_values.append(v[0,0])

                    episódio_recompensativo += r

                    s = s1 

                    total_etapas += 1

                    episode_step_count += 1

                    #Específico para a VizDoom. Nós dormimos o jogo por um tempo específico.

                    if self.sleep_time>0:

                        sleep(self.sleep_time)

                    # Se o episódio ainda não terminou, mas o buffer de experiência está cheio, então nós

                    # fazer um passo de atualização usando essa experiência de implementação.

                    if len(episode_buffer) == 30 and d != True and episode_step_count != max_episode_length – 1:

                        # Como não sabemos qual é o verdadeiro retorno final, nós “bootstrap” do nosso atual

                        # estimativa de valor.

                        v1 = sess.run(self.local_AC.value,

                            feed_dict={self.local_AC.inputs:[s],

                            self.local_AC.state_in[0]:rnn_state[0],

                            self.local_AC.state_in[1]:rnn_state[1]})[0,0])

                        v_l,p_l,e_l,g_n,v_n = self.train(global_AC,episode_buffer,sess,gamma,v1)

                        episode_buffer = []

                        sess.run(self.update_local_ops)

                    se d === Verdadeiro:

                        intervalo

                self.episode_rewards.append(episode_reward)

                self.episode_lengths.append(episode_step_count)

                self.episode_mean_values.append(np.mean(episode_values))

                # Atualizar a rede usando o buffer de experiência no final do episódio.

                v_l,p_l,e_l,g_n,v_n = self.train(global_AC,episode_buffer,sess,gamma,0.0)

Quando o histórico de experiência do trabalhador é enorme o suficiente, nós o usamos para decidir o retorno limitado e um pouco de margem de manobra, e utilizamos esses para computar o valor e abordar os infortúnios. Além disso, verificamos uma entropia (H) da abordagem. Isto se compara à propagação das probabilidades de atividade. Na hipótese remota de que a estratégia produza atividades com probabilidades moderadamente comparativas, nesse ponto a entropia será alta, mas na hipótese remota de que o arranjo recomende uma atividade solitária com uma probabilidade enorme, nesse ponto a entropia será baixa. Utilizamos a entropia como um método para melhorar a investigação, ao instarmos o modelo a ser tradicionalista no que diz respeito à sua certeza da atividade correta.

Perda de Valor: L = Σ(R – V(s))²

Perda de apólice: L = -log(π(s)) * A(s) – β*H(π)

Um operário, nesse ponto, utiliza esses infortúnios para adquirir ângulos relativos aos parâmetros de seu sistema. Cada um desses ângulos é comumente cortado de forma a antecipar-se a atualizações de parâmetros excessivamente enormes que podem desestabilizar a abordagem.

Um especialista nesse ponto utiliza as inclinações para refrescar os parâmetros do sistema mundial. Nesta linha, o sistema mundial está sendo continuamente atualizado por cada um dos especialistas, pois eles colaboram com a sua condição.

classe AC_Network():

    def __init__(self,s_size,a_size,a_size,scope,trainer):

        ….

        ….

        ….

        if scope != ‘global’:

            self.actions = tf.placeholder(shape=[None],dtype=tf.int32)

            self.actions_onehot = tf.one_hot(self.actions,a_size,dtype=tf.float32)

            self.target_v = tf.placeholder(shape=[None],dtype=tf.float32)

            self.advantages = tf.placeholder(shape=[None],dtype=tf.float32)

            self.responsible_outputs = tf.reduce_sum(self.policy * self.actions_onehot, [1])

            #Perda de funções

            self.value_loss = 0.5 * tf.reduce_sum(tf.square(self.target_v – tf.reshape(self.value,[-1])))

            self.entropy = – tf.reduce_sum(self.policy * tf.log(self.policy))

            self.policy_loss = -tf.reduce_sum(tf.log(self.responsible_outputs)*self.advantages)

            self.loss = 0.5 * self.value_loss + self.policy_loss – self.entropy * 0.01

            #Obter gradientes da rede local usando perdas locais

            local_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope)

            self.gradients = tf.gradients(self.loss,local_vars)

            self.var_norms = tf.global_norm(local_vars)

            grads,self.grad_norms = tf.clip_by_global_norm(self.gradients,40.0)

            #Aplicar inclinações locais à rede global

            global_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, ‘global’)

            self.apply_grads = trainer.apply_gradients(zip(grads,global_vars))

trabalhador de classe():

      ….

      ….

      ….

      def train(self,global_AC,rollout,sess,gamma,bootstrap_value):

        rollout = np.array(rollout)

        observações = rollout[:,0]

        ações = rollout[:,1]

        recompensas = rollout[:,2]

        next_observations = rollout[:,3]

        valores = rollout[:,5]

        # Aqui nós pegamos as recompensas e valores do rollout, e os usamos para

        # geram a vantagem e os retornos descontados.

        # A função vantagem usa “Generalized Advantage Estimation” (Estimativa de Vantagem Generalizada)

        self.rewards_plus = np.asarray(rewards.tolist() + [bootstrap_value])

        discount_rewards = discount(self.rewards_plus,gamma)[:-1]

        self.value_plus = np.asarray(values.tolist() + [bootstrap_value])

        vantagens = recompensas + gama * self.value_plus[1:] – self.value_plus[:-1]

        vantagens = desconto(vantagens,gamma)

        # Atualizar a rede global usando gradientes de perda

        # Gerar estatísticas de rede para salvar periodicamente

        rnn_state = auto.local_AC.state_init

        feed_dict = {self.local_AC.target_v:discounted_rewards,

            self.local_AC.inputs:np.vstack(observações),

            auto.local_AC.actions:actions,

            auto.local_AC.vantagens:vantagens,

            self.local_AC.state_in[0]:rnn_state[0],

            self.local_AC.state_in[1]:rnn_state[1]}

        v_l,p_l,e_l,g_n,v_n,_ = sess.run([auto.local_AC.value_loss,

            self.local_AC.policy_loss,

            auto.local_AC.entropia,

            auto-.local_AC.grad_norms,

            auto.local_AC.var_norms,

            self.local_AC.apply_grads],

            feed_dict=feed_dict)

        retornar v_l / len(rollout),p_l / len(rollout),e_l / len(rollout), g_n,v_n

Quando uma atualização efetiva é feita para o sistema mundial, todo o procedimento é reiniciado! O trabalhador nesse momento reajusta seus próprios parâmetros do sistema para os do sistema mundial, e o procedimento começa mais uma vez.