13

EDIT (1/3/16): corresponding github issueCome utilizzare Tensorflow Optimizer senza ricalcolare le attivazioni nel programma di apprendimento di rinforzo che restituisce il controllo dopo ogni iterazione?

Sto utilizzando tensorflow (interfaccia Python) per implementare un agente q-apprendimento con funzione di approssimazione addestrata con gradiente stocastico-discesa. Ad ogni iterazione dell'esperimento viene chiamata una funzione di passo nell'agente che aggiorna i parametri dell'approssimatore in base alla nuova ricompensa e attivazione, quindi sceglie una nuova azione da eseguire.

Qui è il problema (con l'apprendimento di rinforzo gergo):

  • L'agente calcola le sue previsioni valore dello stato-azione per scegliere un'azione.
  • Quindi restituisce il controllo di un altro programma che simula un passaggio nell'ambiente.
  • Ora la funzione di passo dell'agente viene chiamata per l'iterazione successiva. Voglio usare la classe Optimizer di Tensorflow per calcolare le sfumature per me. Tuttavia, ciò richiede sia le previsioni del valore di azione dello stato che ho calcolato l'ultimo passaggio, sia il loro grafico. Quindi:
    • Se eseguo l'ottimizzatore sull'intero grafico, deve ricalcolare le previsioni del valore di azione dello stato.
    • Tuttavia, se si memorizza la previsione (per l'azione scelta) come variabile, quindi la si invia all'ottimizzatore come segnaposto, non ha più il grafico necessario per calcolare i gradienti.
    • Non riesco a eseguire tutto nella stessa istruzione sess.run(), perché devo rinunciare al controllo e restituire l'azione scelta per ottenere l'osservazione e la ricompensa successive (da utilizzare nell'obiettivo per funzione di perdita).

Quindi, c'è un modo che io possa (senza rinforzo apprendimento gergo):

  1. Compute parte del mio grafico, tornando valore1.
  2. Valore restituito1 al programma chiamante per calcolare il valore2
  3. Nella successiva iterazione, utilizzare il valore 2 come parte della funzione di perdita per la discesa del gradiente SENZA ricalibrare la parte del grafico che calcola il valore1.

Naturalmente, ho considerato le soluzioni ovvie:

  1. Basta hardcode le pendenze: Questo sarebbe facile per i davvero semplici approssimatori sto usando ora, ma sarebbe davvero scomodo se Stavo sperimentando diversi filtri e funzioni di attivazione in una grande rete convoluzionale. Mi piacerebbe davvero usare la classe Optimizer se possibile.

  2. Chiama la simulazione ambientale dall'interno dell'agente: This system esegue questa operazione, ma renderà il mio più complicato e rimuoverà gran parte della modularità e della struttura. Quindi, non voglio farlo.

Ho letto più volte l'API e il whitepaper, ma non riesco a trovare una soluzione. Stavo cercando di trovare un modo per alimentare il bersaglio in un grafico per calcolare i gradienti, ma non ho potuto trovare un modo per costruire quel grafico automaticamente.

Se risulta che non è ancora possibile in TensorFlow, pensi che sarebbe molto complicato implementarlo come un nuovo operatore? (Non ho usato C++ in un paio d'anni, quindi la sorgente TensorFlow sembra un po 'intimidatoria.) O sarebbe meglio passare a qualcosa come Torch, che ha la differenziazione imperativa di Autograd, invece della differenziazione simbolica?

Grazie per aver trovato il tempo di darmi una mano su questo. Stavo cercando di renderlo il più conciso possibile.

EDIT: Dopo aver fatto qualche ulteriore ricerca mi sono imbattuto in this previously asked question. È un po 'diverso dal mio (stanno cercando di evitare di aggiornare una rete LSTM due volte ogni iterazione in Torch), e non ha ancora risposte.

Ecco po 'di codice se questo aiuta:

''' 
-Q-Learning agent for a grid-world environment. 
-Receives input as raw rbg pixel representation of screen. 
-Uses an artificial neural network function approximator with one hidden layer 

2015 Jonathon Byrd 
''' 

import random 
import sys 
#import copy 
from rlglue.agent.Agent import Agent 
from rlglue.agent import AgentLoader as AgentLoader 
from rlglue.types import Action 
from rlglue.types import Observation 

import tensorflow as tf 
import numpy as np 

world_size = (3,3) 
total_spaces = world_size[0] * world_size[1] 

class simple_agent(Agent): 

    #Contants 
    discount_factor = tf.constant(0.5, name="discount_factor") 
    learning_rate = tf.constant(0.01, name="learning_rate") 
    exploration_rate = tf.Variable(0.2, name="exploration_rate") # used to be a constant :P 
    hidden_layer_size = 12 

    #Network Parameters - weights and biases 
    W = [tf.Variable(tf.truncated_normal([total_spaces * 3, hidden_layer_size], stddev=0.1), name="layer_1_weights"), 
    tf.Variable(tf.truncated_normal([hidden_layer_size,4], stddev=0.1), name="layer_2_weights")] 
    b = [tf.Variable(tf.zeros([hidden_layer_size]), name="layer_1_biases"), tf.Variable(tf.zeros([4]), name="layer_2_biases")] 

    #Input placeholders - observation and reward 
    screen = tf.placeholder(tf.float32, shape=[1, total_spaces * 3], name="observation") #input pixel rgb values 
    reward = tf.placeholder(tf.float32, shape=[], name="reward") 

    #last step data 
    last_obs = np.array([1, 2, 3], ndmin=4) 
    last_act = -1 

    #Last step placeholders 
    last_screen = tf.placeholder(tf.float32, shape=[1, total_spaces * 3], name="previous_observation") 
    last_move = tf.placeholder(tf.int32, shape = [], name="previous_action") 

    next_prediction = tf.placeholder(tf.float32, shape = [], name="next_prediction") 

    step_count = 0 

    def __init__(self): 
     #Initialize computational graphs 
     self.q_preds = self.Q(self.screen) 
     self.last_q_preds = self.Q(self.last_screen) 
     self.action = self.choose_action(self.q_preds) 
     self.next_pred = self.max_q(self.q_preds) 
     self.last_pred = self.act_to_pred(self.last_move, self.last_q_preds) # inefficient recomputation 
     self.loss = self.error(self.last_pred, self.reward, self.next_prediction) 
     self.train = self.learn(self.loss) 
     #Summaries and Statistics 
     tf.scalar_summary(['loss'], self.loss) 
     tf.scalar_summary('reward', self.reward) 
     #w_hist = tf.histogram_summary("weights", self.W[0]) 
     self.summary_op = tf.merge_all_summaries() 
     self.sess = tf.Session() 
     self.summary_writer = tf.train.SummaryWriter('tensorlogs', graph_def=self.sess.graph_def) 


    def agent_init(self,taskSpec): 
     print("agent_init called") 
     self.sess.run(tf.initialize_all_variables()) 

    def agent_start(self,observation): 
     #print("agent_start called, observation = {0}".format(observation.intArray)) 
     o = np.divide(np.reshape(np.asarray(observation.intArray), (1,total_spaces * 3)), 255) 
     return self.control(o) 

    def agent_step(self,reward, observation): 
     #print("agent_step called, observation = {0}".format(observation.intArray)) 
     print("step, reward: {0}".format(reward)) 
     o = np.divide(np.reshape(np.asarray(observation.intArray), (1,total_spaces * 3)), 255) 

     next_prediction = self.sess.run([self.next_pred], feed_dict={self.screen:o})[0] 

     if self.step_count % 10 == 0: 
      summary_str = self.sess.run([self.summary_op, self.train], 
       feed_dict={self.reward:reward, self.last_screen:self.last_obs, 
       self.last_move:self.last_act, self.next_prediction:next_prediction})[0] 

      self.summary_writer.add_summary(summary_str, global_step=self.step_count) 
     else: 
      self.sess.run([self.train], 
       feed_dict={self.screen:o, self.reward:reward, self.last_screen:self.last_obs, 
       self.last_move:self.last_act, self.next_prediction:next_prediction}) 

     return self.control(o) 

    def control(self, observation): 
     results = self.sess.run([self.action], feed_dict={self.screen:observation}) 
     action = results[0] 

     self.last_act = action 
     self.last_obs = observation 

     if (action==0): # convert action integer to direction character 
      action = 'u' 
     elif (action==1): 
      action = 'l' 
     elif (action==2): 
      action = 'r' 
     elif (action==3): 
      action = 'd' 
     returnAction=Action() 
     returnAction.charArray=[action] 
     #print("return action returned {0}".format(action)) 
     self.step_count += 1 
     return returnAction 

    def Q(self, obs): #calculates state-action value prediction with feed-forward neural net 
     with tf.name_scope('network_inference') as scope: 
      h1 = tf.nn.relu(tf.matmul(obs, self.W[0]) + self.b[0]) 
      q_preds = tf.matmul(h1, self.W[1]) + self.b[1] #linear activation 
      return tf.reshape(q_preds, shape=[4]) 

    def choose_action(self, q_preds): #chooses action epsilon-greedily 
     with tf.name_scope('action_choice') as scope: 
      exploration_roll = tf.random_uniform([]) 
      #greedy_action = tf.argmax(q_preds, 0) # gets the action with the highest predicted Q-value 
      #random_action = tf.cast(tf.floor(tf.random_uniform([], maxval=4.0)), tf.int64) 

      #exploration rate updates 
      #if self.step_count % 10000 == 0: 
       #self.exploration_rate.assign(tf.div(self.exploration_rate, 2)) 

      return tf.select(tf.greater_equal(exploration_roll, self.exploration_rate), 
       tf.argmax(q_preds, 0), #greedy_action 
       tf.cast(tf.floor(tf.random_uniform([], maxval=4.0)), tf.int64)) #random_action 

     ''' 
     Why does this return NoneType?: 

     flag = tf.select(tf.greater_equal(exploration_roll, self.exploration_rate), 'g', 'r') 
     if flag == 'g': #greedy 
      return tf.argmax(q_preds, 0) # gets the action with the highest predicted Q-value 
     elif flag == 'r': #random 
      return tf.cast(tf.floor(tf.random_uniform([], maxval=4.0)), tf.int64) 
     ''' 

    def error(self, last_pred, r, next_pred): 
     with tf.name_scope('loss_function') as scope: 
      y = tf.add(r, tf.mul(self.discount_factor, next_pred)) #target 
      return tf.square(tf.sub(y, last_pred)) #squared difference error 


    def learn(self, loss): #Update parameters using stochastic gradient descent 
     #TODO: Either figure out how to avoid computing the q-prediction twice or just hardcode the gradients. 
     with tf.name_scope('train') as scope: 
      return tf.train.GradientDescentOptimizer(self.learning_rate).minimize(loss, var_list=[self.W[0], self.W[1], self.b[0], self.b[1]]) 


    def max_q(self, q_preds): 
     with tf.name_scope('greedy_estimate') as scope: 
      return tf.reduce_max(q_preds) #best predicted action from current state 

    def act_to_pred(self, a, preds): #get the value prediction for action a 
     with tf.name_scope('get_prediction') as scope: 
      return tf.slice(preds, tf.reshape(a, shape=[1]), [1]) 


    def agent_end(self,reward): 
     pass 

    def agent_cleanup(self): 
     self.sess.close() 
     pass 

    def agent_message(self,inMessage): 
     if inMessage=="what is your name?": 
      return "my name is simple_agent"; 
     else: 
      return "I don't know how to respond to your message"; 

if __name__=="__main__": 
    AgentLoader.loadAgent(simple_agent()) 

risposta

14

In questo momento ciò che si vuole fare è molto difficile in tensorflow (0,6). La soluzione migliore è mordere il proiettile e chiamare eseguire più volte al costo di ricalcolare le attivazioni. Tuttavia, siamo molto consapevoli di questo problema internamente. Una soluzione prototipo "a esecuzione parziale" è in lavorazione, ma al momento non è prevista la sua scadenza. Dal momento che una risposta veramente soddisfacente potrebbe richiedere la modifica di tensorflow, potresti anche creare un problema di github per questo e vedere se qualcun altro ha qualcosa da dire su questo.

Edit: il supporto sperimentale per partial_run è ora in https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/client/session.py#L317

+0

Fatto un [problema github] (https://github.com/tensorflow/tensorflow/issues/672). –

Problemi correlati