13

EDIT (03.01.16): corresponding github issueJak korzystać z Optymalizatora Tensorflow bez ponownej obliczania aktywacji w programie uczenia zbrojenia, który zwraca kontrolę po każdej iteracji?

Używam Tensorflow (interfejs Python) wdrożenie Q-learning agenta z funkcji zbliżenia wyszkolonych za pomocą gradientu stochastycznego zejście. Przy każdej iteracji eksperymentu wywoływana jest funkcja krokowa w agencie, która aktualizuje parametry aproksymatora na podstawie nowej nagrody i aktywacji, a następnie wybiera nową akcję do wykonania.

Tutaj jest problem (z żargonu uczenia zbrojenie):

  • Środek oblicza swoje prognozy wartość stanu działania, aby wybrać akcję.
  • Następnie daje kontrolę nad innym programem symulującym krok w środowisku.
  • Teraz funkcja kroku agenta jest wywoływana dla następnej iteracji. Chcę użyć klasy Optimizer firmy Tensorflow do obliczenia gradientów dla mnie. Wymaga to jednak zarówno prognoz wartości stanu działania, które obliczałem w ostatnim kroku, jak i ich wykresu. A więc:
    • Po uruchomieniu optymalizatora na całym wykresie, należy ponownie przeliczyć prognozy wartości stanu.
    • Ale jeśli przechowuję predykcję (dla wybranej akcji) jako zmienną, a następnie wprowadzam ją do optymalizatora jako symbolu zastępczego, nie ma on już wykresu niezbędnego do obliczenia gradientów.
    • Nie mogę uruchomić tego wszystkiego w tym samym słowie sess.run(), ponieważ muszę zrezygnować z kontroli i zwrócić wybraną akcję, aby uzyskać następną obserwację i nagrodę (do wykorzystania w celu dla funkcja straty).

Więc, czy jest jakiś sposób, że mogę (bez zbrojenia uczenia żargonie):

  1. Compute część mojego wykresu, powracającego Value1.
  2. Zwróć wartość1 programowi wywołującemu, aby obliczyć wartość2
  3. W następnej iteracji użyj wartości 2 jako części funkcji utraty dla spadku gradientu BEZ przeliczania części wykresu, która oblicza wartość1.

Oczywiście, mam uznać oczywistych rozwiązań:

  1. Wystarczy zakodować na gradienty: To byłoby łatwe dla bardzo prostych approximators używam teraz, ale byłoby bardzo niewygodne jeśli Eksperymentowałem z różnymi filtrami i funkcjami aktywacyjnymi w dużej splotowej sieci. Bardzo chciałbym użyć klasy Optimizer, jeśli to możliwe.

  2. Zadzwoń do symulacji środowiska z poziomu agenta: This system robi to, ale to sprawiłoby, że moja praca byłaby bardziej skomplikowana, a także usunięto by modułowość i strukturę. Więc nie chcę tego robić.

Przeczytałem kilka razy API i dokumentację, ale nie mogę wymyślić rozwiązania. Próbowałem wymyślić jakiś sposób, aby nakierować cel na wykres, aby obliczyć gradienty, ale nie mógł wymyślić sposobu na automatyczne zbudowanie tego wykresu.

Jeśli okaże się, że w TensorFlow nie jest to jeszcze możliwe, czy uważa Pan/Pani, że wdrożenie tego jako nowego operatora byłoby bardzo skomplikowane? (Nie używałem C++ za parę lat, więc źródło TensorFlow wygląda trochę onieśmielająco.) A może lepiej byłoby przejść na coś takiego jak Latarka, które ma imperatywne zróżnicowanie Autogradu zamiast symbolicznego różnicowania?

Dzięki za poświęcenie czasu, aby mi pomóc w tej sprawie. Próbowałem uczynić to tak zwięzłym, jak tylko mogłem.

EDYCJA: Po wykonaniu dalszych poszukiwań natrafiłem na this previously asked question. Jest trochę inny niż mój (próbują uniknąć aktualizacji sieci LSTM dwa razy w każdej iteracji w Pochodni) i nie ma jeszcze żadnych odpowiedzi.

Oto niektóre kodu, jeśli pomaga:

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

Odpowiedz

14

Teraz to, co chcesz zrobić, to bardzo trudne w Tensorflow (0,6). Najlepiej jest ugryźć pocisk i zadzwonić kilka razy, kosztem ponownego obliczenia aktywacji. Jednak jesteśmy bardzo świadomi tego problemu wewnętrznie. Prototypowe rozwiązanie "częściowego uruchomienia" jest już w toku, ale nie ma w tej chwili harmonogramu jego ukończenia. Ponieważ prawdziwie satysfakcjonująca odpowiedź może wymagać zmodyfikowania samego tensorflow, możesz również zrobić dla tego github kwestię i sprawdzić, czy ktoś ma coś do powiedzenia na ten temat.

Edit: Eksperymentalne wsparcie dla partial_run jest teraz w https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/client/session.py#L317

+0

Made [github problem] (https://github.com/tensorflow/tensorflow/issues/672). –