13

EDIT (1/3/16): corresponding github issueКак использовать оптимизатор Tensorflow без повторной активации активации в программе обучения арматуре, которая возвращает управление после каждой итерации?

Я использую Tensorflow (интерфейс Python) для реализации ватного обучения агента с функцией приближения обучен с использованием стохастического градиентного спуска. На каждой итерации эксперимента вызывается функция шага в агенте, которая обновляет параметры аппроксиматора на основе новой награды и активации, а затем выбирает новое действие для выполнения.

Вот проблема (с обучением армирования жаргоне):

  • Агент вычисляет свои предсказания значений состояния действия, чтобы выбрать действие.
  • Затем дает управление другой программой, которая имитирует шаг в окружающей среде.
  • Теперь функция шага агента вызывается для следующей итерации. Я хочу использовать класс Optimizer Tensorflow для вычисления градиентов для меня. Однако для этого требуются как предсказания значения состояния, которые я вычислял на последнем шаге, так и их график. Итак:
    • Если я запустил оптимизатор на всем графике, он должен будет пересчитать предсказания значения состояния.
    • Но если я сохраняю предсказание (для выбранного действия) в качестве переменной, а затем передаю его оптимизатору в качестве заполнителя, у него больше нет графика, необходимого для вычисления градиентов.
    • Я не могу просто запустить все это в одном выражении sess.run(), потому что я должен отказаться от контроля и вернуть выбранное действие, чтобы получить следующее наблюдение и вознаграждение (использовать в цели для функция потерь).

Итак, есть ли способ, что я могу (без армирующего обучения жаргона):

  1. Compute часть моего графика, возвращающийся значение1.
  2. Возвращаемое значение1 в вызывающую программу для вычисления значения2
  3. В следующей итерации используйте значение2 как часть функции потерь для градиентного спуска БЕЗ пересчета части графика, вычисляющего значение1.

Конечно, я рассмотрел очевидные решения:

  1. Просто жёстко градиенты: Это было бы легко для очень простых аппроксиматоров я использую сейчас, но было бы очень неудобно, если Я экспериментировал с различными фильтрами и функциями активации в большой сверточной сети. Я бы очень хотел использовать класс Optimizer, если это возможно.

  2. Вызов моделирования окружающей среды изнутри агента: This system делает это, но это сделает мой сложнее и удалит много модульности и структуры. Поэтому я не хочу этого делать.

Я читал API и технический документ несколько раз, но, похоже, не придумал решение. Я пытался каким-то образом накормить цель в графе, чтобы вычислить градиенты, но не смог создать способ построить этот график автоматически.

Если окажется, что в TensorFlow это невозможно, вы считаете, что было бы очень сложно реализовать это как новый оператор? (Я не использовал C++ через пару лет, поэтому источник TensorFlow выглядит немного пугающим.) Или мне лучше переключиться на что-то вроде Torch, которое имеет императивное дифференцирование Autograd вместо символической дифференциации?

Спасибо, что нашли время, чтобы помочь мне в этом. Я пытался сделать это как можно более сжатым.

EDIT: После дальнейших поисков я наткнулся на this previously asked question. Это немного отличается от моего (они стараются не обновлять сеть LSTM дважды на каждой итерации в Torch), и пока не имеют ответов.

Вот код, если это помогает:

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

ответ

14

Прямо сейчас, что вы хотите сделать, это очень сложно в Tensorflow (0.6). Лучше всего укусить пулю и запустить вызов несколько раз за счет перерасчета активаций. Тем не менее, мы очень хорошо знаем эту проблему внутри страны. Прототип решения «частичного запуска» находится в работе, но сейчас нет времени для его завершения. Поскольку действительно удовлетворительный ответ может потребовать изменения самого тензорного потока, вы также можете сделать для него проблему github и посмотреть, есть ли у кого-нибудь еще что-нибудь сказать по этому поводу.

Edit: Экспериментальная поддержка partial_run теперь в https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/client/session.py#L317

+0

Сделано [GitHub выпуск] (https://github.com/tensorflow/tensorflow/issues/672). –