Entonces, ¿qué es el A3C? El cálculo del A3C fue descargado por el grupo DeepMind de Google hace poco, e hizo un salto por… básicamente el obsoleto DQN. Fue más rápido, más fácil, cada vez más vigoroso, y listo para lograr mejores resultados en la batería estándar de las empresas de RL Profundo. En general, que podía funcionar en espacios de actividad consistentes y discretos. Dado esto, se ha convertido en el cálculo de inmersión a Profundo RL para nuevos problemas de pruebas con espacios de estado y actividad complejos. La verdad es que OpenAI acaba de descargar una versión de A3C como su “especialista en arranques generales” para trabajar con su nuevo (y extremadamente diferente) conjunto de situaciones del Universo.
Los 3 A partir de A3C
El artista Pundit es una pieza seria. Qué tal si empezamos descargando el nombre, y desde ese punto, empezamos a descargar la mecánica del propio cálculo.
No corriente: No del todo como DQN, donde un operador solitario al que le habla un sistema neural solitario colabora con una situación de soledad, A3C utiliza diferentes manifestaciones de lo anterior para adaptarse de manera más productiva. En A3C hay un sistema mundial y diferentes operadores especializados que tienen cada uno su propia disposición de los parámetros del sistema. Cada uno de estos operadores se conecta con su propio duplicado de la tierra simultáneamente, ya que diferentes especialistas se comunican con su entorno. La explicación de que esto funciona mejor que tener un especialista solitario (más allá de la velocidad de realizar más trabajo), es que la experiencia de cada operador está libre de la experiencia de los demás. En este sentido, la experiencia general accesible para la preparación resulta ser progresivamente variada.
El artista Pundit: Hasta ahora este arreglo se ha concentrado en las estrategias de énfasis en la estima, por ejemplo, Q-learning, o en las técnicas de ciclo de aproximación, por ejemplo, Estrategia Pendiente. El personaje en pantalla Pundit consolida las ventajas de las dos metodologías. Debido a A3C, nuestro sistema evaluará tanto una capacidad de valor V(s) (cuán grande debe ser un estado específico) como una estrategia π(s) (mucha probabilidad de actividad rinde). Cada una de ellas será independiente, con capas completamente asociadas que se ubicarán en el punto más alto del sistema. Básicamente, el operador utiliza el medidor de valor (el experto) para refrescar la estrategia (el animador) de manera más inteligente que las estrategias convencionales de inclinación de arreglos.
Aplicación del algoritmo

Durante el tiempo que pasé estructurando este uso del cálculo del A3C, utilicé como referencia las ejecuciones de calidad de DennyBritz y OpenAI. Los dos de los cuales prescribo profundamente en caso de que quieras ver opciones en contraste con mi código aquí. Cada segmento insertado aquí es tomado fuera de cualquier conexión relevante al tema en cuestión para propósitos de instrucción, y no correrá solo. Para ver y ejecutar la completa y útil ejecución del A3C, vea mi bóveda de Github.
El diagrama general del código de ingeniería es:
AC_Network – Esta clase contiene todas las operaciones de Tensorflow para hacer los sistemas mismos.
Especialista – Esta clase contiene un duplicado de AC_Network, una clase de dominio, al igual que todos los fundamentos para cooperar con la naturaleza, y refrescar el sistema mundial.
Código de nivel elevado para construir las ocurrencias de los Especialistas y ejecutarlas en paralelo.
El cálculo del A3C comienza por desarrollar el sistema mundial. Este sistema comprenderá capas convolucionales para procesar las condiciones espaciales, seguidas por una capa de LSTM para procesar las condiciones fugaces, y por último, capas de valor y rendimiento estratégico. El siguiente es el código modelo para establecer el gráfico del sistema en sí.
clase AC_Network():
def __init__(self,s_size,a_size,scope,trainer):
con tf.variable_scope(scope):
#Capas de entrada y codificación 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,
tamaño_del_núcleo=[8,8],zancada=[4,4],acolchado=’VÁLIDO’)
self.conv2 = slim.conv2d(activation_fn=tf.nn.elu,
inputs=self.conv1,num_outputs=32,
tamaño_del_núcleo=[4,4],zancada=[2,2],acolchado=’VÁLIDO’)
hidden = slim.fully_connected(slim.flatten(self.conv2),256,activation_fn=tf.nn.elu)
# Red recurrente de dependencias temporales
lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(256,state_is_tuple=True)
c_init = np.ceros((1, lstm_cell.state_size.c), np.float32)
h_init = np.ceros((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])
tamaño_paso = 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=Falso)
lstm_c, lstm_h = lstm_state
self.state_out = (lstm_c[:1, :], lstm_h[:1, :])
rnn_out = tf.reshape(lstm_outputs, [-1, 256])
#Capas de salida para estimaciones de políticas y valores
self.policy = slim.fully_connected(rnn_out,a_size,
activation_fn=tf.nn.softmax,
weights_initializer=normalized_columns_initializer(0.01),
biases_inicializador=Ninguno)
self.value = slim.fully_connected(rnn_out,1,
activation_fn=Ninguno,
weights_initializer=normalized_columns_initializer(1.0),
biases_inicializador=Ninguno)
A continuación, un montón de operarios, cada uno con su propio sistema y condición se hacen. Cada uno de estos trabajadores se ejecuta en una cadena de procesador diferente, por lo que no debería haber un número mayor de trabajadores que el de las cadenas de su CPU.
con tf.device(“/cpu:0”):
master_network = AC_Network(s_size,a_size,’global’,None) # Generar red global
num_workers = multiprocessing.cpu_count() # Set workers ot number of available CPU threads
trabajadores = []
# Crear clases de trabajadores
para i en rango(num_trabajadores):
workers.append(Trabajador(DoomGame(),i,s_tamaño,a_tamaño,entrenador,ahorrador,ruta_modelo))
con tf.Session() como sess:
coord = tf.train.Coordinator()
si load_model == Verdadero:
imprimir ‘Modelo de carga…’
ckpt = tf.train.get_checkpoint_state(ruta_modelo)
saver.restore(sess,ckpt.model_checkpoint_path)
…sino..:
sess.run(tf.global_variables_inicializador())
# Aquí es donde ocurre la magia asincrónica.
# Comenzar el proceso de “trabajo” para cada trabajador en una amenaza separada.
hilos_de_trabajo = []
para los trabajadores en los trabajadores:
trabajo_de_trabajo = lambda: trabajo.trabajo(max_episodio_longitud,gamma,red_maestra,sess,coord)
t = rosca.Rosca(objetivo=(trabajo_de_trabajo))
t.start()
worker_threads.append(t)
coord.join(hilos_de_trabajo)
Cada trabajador empieza por ajustar sus parámetros de sistema a los del sistema mundial. Podemos hacer esto desarrollando una operación Tensorflow que establece cada factor en el sistema de especialistas de barrio al incentivo variable igual en el sistema mundial.
Cada especialista en ese punto se asocia con su propio duplicado de la naturaleza y recoge la comprensión. Cada uno mantiene una lista de tuplas de experiencia (percepción, actividad, remuneración, hecho, estima) que se agrega continuamente a partir de las comunicaciones con la tierra.
Lass Worker():
….
….
….
Def work(self,max_episode_length,gamma,global_AC,sess,coord):
episode_count = 0
total_step_count = 0
Imprimir “Trabajador inicial” + str(auto.número)
con sess.as_default(), sess.graph.as_default():
mientras que no coord.should_stop():
sess.run(self.update_local_ops)
episode_buffer = []
valores_de_expedición = []
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 = cuadro_de_proceso(s)
rnn_state = self.local_AC.state_init
mientras que self.env.is_episode_finished() == Falso:
#Tomar una acción usando probabilidades de la salida de la red de políticas.
a_dist,v,rnn_state = sess.run([self.local_AC.policy,self.local_AC.value,self.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()
si d == Falso:
s1 = self.env.get_state().screen_buffer
episode_frames.append(s1)
s1 = marco_de_proceso(s1)
…sino..:
s1 = s
episode_buffer.append([s,a,r,s1,d,v[0,0]])
episode_values.append(v[0,0])
episode_reward += r
s = s1
total_pasos += 1
episode_step_count += 1
# Específico de VizDoom. Dormimos el juego por un tiempo específico.
si self.sleep_time>0:
sleep(self.sleep_time)
# Si el episodio no ha terminado, pero el buffer de experiencia está lleno, entonces nosotros
# Hacer un paso de actualización usando ese despliegue de experiencia.
if len(episode_buffer) == 30 y d != True y episode_step_count != max_episode_length – 1:
# Ya que no sabemos cuál es el verdadero retorno final, nos “bootstrap” de nuestra actual
# Estimación del 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)
si d == Verdadero:
break
self.episode_rewards.append(episode_reward)
self.episode_lengths.append(episode_step_count)
self.episode_mean_values.append(np.mean(episode_values))
# Actualizar la red usando el buffer de experiencia al final del episodio.
v_l,p_l,e_l,g_n,v_n = self.train(global_AC,episode_buffer,sess,gamma,0.0)
Cuando la historia de la experiencia del trabajador es lo suficientemente enorme, la usamos para decidir el retorno limitado y el poco margen de maniobra, y los utilizamos para calcular el valor y acercarnos a las desgracias. Además, determinamos una entropía (H) de la aproximación. Esto se compara con la propagación de las probabilidades de actividad. En la remota posibilidad de que la estrategia produzca actividades con probabilidades comparativas moderadas, en ese momento la entropía será alta, pero en la remota posibilidad de que el arreglo recomiende una actividad solitaria con una enorme probabilidad, en ese momento la entropía será baja. Utilizamos la entropía como método para mejorar la investigación, instando al modelo a ser tradicionalista con respecto a la seguridad de la actividad correcta.
Pérdida de valor: L = Σ(R – V(s))²
Pérdida de la póliza: L = -log(π(s)) * A(s) – β*H(π)
Un trabajador en ese punto utiliza estas desgracias para adquirir ángulos relativos a los parámetros de su sistema. Cada uno de estos ángulos es comúnmente cortado para anticipar refrescos de parámetros excesivamente enormes que pueden desestabilizar el enfoque.
Un especialista en ese punto utiliza las inclinaciones para refrescar los parámetros del sistema mundial. En esta línea, el sistema mundial está siendo continuamente refrescado por cada uno de los especialistas, ya que colaboran con su condición.
clase AC_Network():
def __init__(self,s_size,a_size,scope,trainer):
….
….
….
si el alcance != ‘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])
#Pérdida de funciones
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
#Conseguir gradientes de la red local utilizando las pérdidas locales
local_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope)
auto.gradientes = tf.gradientes(auto.pérdida,local_vars)
self.var_norms = tf.global_norm(local_vars)
grads,self.grad_norms = tf.clip_by_global_norm(self.gradients,40.0)
# Aplicar gradientes locales a la red global
global_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, ‘global’)
self.apply_grads = trainer.apply_gradients(zip(grads,global_vars))
clase Worker():
….
….
….
def train(self,global_AC,rollout,sess,gamma,bootstrap_value):
rollout = np.array(rollout)
observaciones = despliegue[:,0]
acciones = despliegue[:,1]
recompensas = despliegue[:,2]
next_observations = rollout[:,3]
valores = despliegue[:,5]
# Aquí tomamos las recompensas y los valores del despliegue, y los usamos para
# Generar la ventaja y los retornos descontados.
# La función de ventaja utiliza “Estimación de ventaja 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])
ventajas = recompensas + gamma * self.value_plus[1:] – self.value_plus[:-1]
ventajas = descuento(ventajas,gamma)
# Actualizar la red global usando gradientes de pérdida
# Generar estadísticas de la red para guardar periódicamente
rnn_state = self.local_AC.state_init
feed_dict = {self.local_AC.target_v:discounted_rewards,
self.local_AC.inputs:np.vstack(observaciones),
acciones.locales.de.la.A.C.:acciones,
Ventajas de la auto-localización: ventajas,
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([self.local_AC.value_loss,
…pérdida de la política de autogestión local,
auto.local_AC.entropía,
auto.local_AC.grad_normas,
auto.local_AC.var_normas,
self.local_AC.apply_grads],
feed_dict=feed_dict)
return v_l / len(rollout),p_l / len(rollout),e_l / len(rollout), g_n,v_n
Cuando se hace una actualización efectiva del sistema mundial, todo el procedimiento se repite. El trabajador en ese momento reajusta sus propios parámetros del sistema a los del sistema mundial, y el procedimiento comienza una vez más.