Files
2018-11-12 18:02:31 +08:00

816 lines
49 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# coding:utf-8
import sys,numpy,os,scipy.signal,threading,time,random,copy
import tensorflow as tf
sys.path.append('../../pythonModules')
import wgenv,common,wgfeature,wgsdata
from resnet_model import ResNet
## HELPER FUNCTIONS
# Copies one set of variables to another.
# Used to set worker network parameters to those of global network.
def update_target_graph(from_scope,to_scope):
from_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, from_scope)
to_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, to_scope)
op_holder = []
for from_var,to_var in zip(from_vars,to_vars):
op_holder.append(to_var.assign(from_var))
return op_holder
def filterMetadata(env,BattleMode):
'''sgr修改,去除部分加载算子,在简单情况下验证算法
battlemode = 66
'''
try:
l_rbops,l_bbops, l_pbops= env.obj_main.dic_metadata['l_rbops'],env.obj_main.dic_metadata['l_bbops'],env.obj_main.dic_metadata['l_pbops']
if BattleMode == '6v6':
return l_rbops, l_bbops, l_pbops
elif BattleMode == '1v0':
l_f_rbops = []
l_f_bbops = []
for bop in l_bbops:
if bop.ObjTypeX == 0:
l_f_bbops.append(bop)
break
l_f_pbops = []
return l_f_rbops,l_f_bbops,l_f_pbops
elif BattleMode == '1v1':
l_f_rbops = []
l_f_bbops = []
for bop in l_bbops:
if bop.ObjTypeX == 0:
l_f_bbops.append(bop)
break
for bop in l_rbops:
if bop.ObjTypeX == 0:
l_f_rbops.append(bop)
break
l_f_pbops = []
l_rbops, l_bbops, l_pbops = l_f_rbops, l_f_bbops, l_f_pbops
except Exception as e:
print('error in wgsdata.py -> filterMetadata() ' + str(e))
raise
def process_observation(obj_main):
wgfeature.calUpdateFeature(obj_main)
np_mapFeature = obj_main.dic_metadata['np_mapFeature']
np_mapFeature = numpy.swapaxes(np_mapFeature,0, 1)
np_mapFeature = numpy.swapaxes(np_mapFeature, 1, 2)
np_bopFeature = obj_main.dic_metadata['np_bopFeature']
return np_mapFeature[numpy.newaxis,:,:,:],np_bopFeature[numpy.newaxis,:]
def normalized_columns_initializer(std=1.0):
def _initializer(shape, dtype=None, partition_info=None):
out = numpy.random.randn(*shape).astype(numpy.float32)
out *= std / numpy.sqrt(numpy.square(out).sum(axis=0, keepdims=True))
return tf.constant(out)
return _initializer
# Discounting function used to calculate discounted returns.
def discount(x, gamma):
return scipy.signal.lfilter([1], [1, -gamma], x[::-1], axis=0)[::-1]
#
def calScore(obj_main,flag_rlaicolor):
try:
l_values = wgsdata.getSdataValue(obj_main.dic_metadata)
l_scores = [l_values[0],120-l_values[2],l_values[1]] if flag_rlaicolor == 0 else [l_values[2],120-l_values[0],l_values[3]]
return l_scores
except Exception as e:
print('error in test_rl.py -> calScore() : ' + str(e))
raise
class AC_Network():
def __init__(self, scope, trainer, trainer_critic, nonspatial_size,map_size,dict_actionsepc):
'''modify0 2018年11月07日 sgr 修改网络节点的name属性,共享节点.name =scope/public...
policy = scope/policy... value = scope/value...为了反向传播时只更新特定层的梯度
modify1 2018年11月07日 sgr 增加dic_hparas['UpdateGradsMethod'] in ['Integration','Divide'] 指示整体训练policy和value网,
还是分开训。分训的时候value 网只更新全连接层参数
'''
try:
with tf.variable_scope(scope):
# get size of features from action_spec and observation_spec
assert dic_hparas['NetType'] in ['AtariNet','ResNet']
with tf.variable_scope('public'): # name 'scope/public/...'
if dic_hparas['NetType'] == 'AtariNet':
self.inputs_nonspatial = tf.placeholder(shape=[None, nonspatial_size], dtype=tf.float32)
self.inputs_spatial_screen = tf.placeholder(shape=[None, map_size[0], map_size[1], map_size[2]],dtype=tf.float32)
# Architecture here follows Atari-net Agent described in [1] Section 4.3
self.nonspatial_dense = tf.layers.dense(inputs=self.inputs_nonspatial,units=32,activation=tf.tanh)
self.screen_conv1 = tf.layers.conv2d(inputs=self.inputs_spatial_screen,filters=16,kernel_size=[8,8],strides=[4,4],padding='valid',activation=tf.nn.relu)
self.screen_conv2 = tf.layers.conv2d(inputs=self.screen_conv1,filters=32,kernel_size=[4,4],strides=[2,2],padding='valid',activation=tf.nn.relu)
# According to [1]: "The results are concatenated and sent through a linear layer with a ReLU activation."
screen_output_length = 1
for dim in self.screen_conv2.get_shape().as_list()[1:]:
screen_output_length *= dim
self.latent_vector = tf.layers.dense(
inputs=tf.concat([self.nonspatial_dense, tf.reshape(self.screen_conv2,shape=[-1,screen_output_length])], axis=1),units=256,activation=tf.nn.relu)
elif dic_hparas['NetType'] == 'ResNet':
self.inputs_nonspatial = tf.placeholder(shape=[None, nonspatial_size], dtype=tf.float32)
self.inputs_spatial_screen = tf.placeholder(shape=[None, map_size[0], map_size[1], map_size[2]],dtype=tf.float32)
# Architecture here follows ResNet
batch_size = self.inputs_spatial_screen.get_shape().as_list()[0]
self.resnet = ResNet(self.inputs_spatial_screen,batch_size)
self.latent_vector = self.resnet._build_model()
### 下面是HQ加的网络
self.policy_dense_1 = {}
self.policy_dense_2 = {}
self.value_dense_1 = None
self.value_dense_2 = None
self.policy_arg = {}
self.value = None
else:
common.echosentence_color('dic_hparas.NetType error.')
# Output layers for policy and value estimations
# 1 policy network for base actions
# 16 policy networks for arguments
# - All modeled independently
# - Spatial arguments have the x and y values modeled independently as well
# 1 value network
with tf.variable_scope('policy'): # name = 'scope/policy/...'
for key,value in dict_actionsepc.items():
self.policy_dense_1[key] = tf.layers.dense(inputs=self.latent_vector,units=128,activation=tf.nn.tanh)
self.policy_dense_2[key] = tf.layers.dense(inputs=self.policy_dense_1[key], units=64,
activation=tf.nn.tanh)
self.policy_arg[key] = tf.layers.dense(inputs=self.policy_dense_2[key], units=value,
activation=tf.nn.softmax,
kernel_initializer=normalized_columns_initializer(0.01))
with tf.variable_scope('value'): # name = 'scope/value/...'
self.value_dense_1 = tf.layers.dense(inputs=self.latent_vector, units=128,
activation=tf.nn.tanh)
self.value_dense_2 = tf.layers.dense(inputs=self.value_dense_1, units=64,
activation=tf.nn.tanh)
self.value = tf.layers.dense(inputs=self.value_dense_2,units=1,kernel_initializer=normalized_columns_initializer(1.0))
# Only the worker network need ops for loss functions and gradient updating.
if scope != 'global':
self.actions_arg = dict()
self.actions_onehot_arg = dict()
for key, value in dict_actionsepc.items():
self.actions_arg[key] = tf.placeholder(shape=[None],dtype=tf.int32)
self.actions_onehot_arg[key] = tf.one_hot(self.actions_arg[key],value,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_arg = dict()
for key, value in dict_actionsepc.items():
self.responsible_outputs_arg[key] = tf.reduce_sum(self.policy_arg[key] * self.actions_onehot_arg[key], [1])
# tf_c1 = tf.assign(self.responsible_outputs_arg[key],1)
# #shape = tf.shape(self.responsible_outputs_arg[key])
# shape = self.screen_conv2.get_shape()
# tf_c1= tf.constant(1,shape=self.screen_conv2.get_shape())
# self.responsible_outputs_arg[key] = tf.where(tf.equal(self.responsible_outputs_arg[key],0),tf_c1,self.responsible_outputs_arg[key])
# Loss functions
self.value_loss = 0.5 * tf.reduce_sum(tf.square(self.target_v - tf.reshape(self.value,[-1])))
self.entropy_arg = dict()
for key, value in dict_actionsepc.items():
self.entropy_arg[key] = -tf.reduce_sum(self.policy_arg[key] * tf.log(tf.clip_by_value(self.policy_arg[key],1e-20,1.)))
self.entropy = 0
for key, value in dict_actionsepc.items():
self.entropy += self.entropy_arg[key]
self.policy_loss_arg = dict()
for key, value in dict_actionsepc.items():
self.policy_loss_arg[key] = - tf.reduce_sum(tf.log(tf.clip_by_value(self.responsible_outputs_arg[key]+0.05, 1e-20, 1.05)) * self.advantages)
self.policy_loss = 0
for key, value in dict_actionsepc.items():
self.policy_loss += self.policy_loss_arg[key]
# Get gradients from local network using local losses
self.local_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope)
self.var_norms = tf.global_norm(self.local_vars)
if 'UpdateGradsMethod' not in dic_hparas.keys() or dic_hparas['UpdateGradsMethod'] not in ['Integration','Divide']:
print('dic_hparas key error : UpdateGradsMethod')
if dic_hparas['UpdateGradsMethod'] == 'Integration':
self.loss = 0.5 * self.value_loss + self.policy_loss - self.entropy * 0.01
# self.gradients - gradients of loss wrt local_vars
self.gradients = tf.gradients(self.loss,self.local_vars)
self.grads,self.grad_norms = tf.clip_by_global_norm(self.gradients,0.5) # old=40 2018年09月10日修改,梯度太大
# Apply local gradients to global network
self.global_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, 'global')
self.apply_grads = trainer.apply_gradients(zip(self.grads,self.global_vars))
elif dic_hparas['UpdateGradsMethod'] == 'Divide':
# policy
self.loss_policy = self.policy_loss - self.entropy * 0.01
self.local_policy_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,scope+'/public') + tf.get_collection(
tf.GraphKeys.TRAINABLE_VARIABLES,scope+'/policy')
self.gradients_policy = tf.gradients(self.loss_policy,self.local_policy_vars)
self.grads_policy,self.grad_norms_policy = tf.clip_by_global_norm(self.gradients_policy,0.5)
self.global_vars_policy = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,'global'+'/public') + tf.get_collection(
tf.GraphKeys.TRAINABLE_VARIABLES,'global'+'/policy')
self.apply_grads_policy = trainer.apply_gradients(zip(self.grads_policy,self.global_vars_policy))
#value
self.loss_value = self.value_loss
self.local_value_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,scope+'/value')
self.gradients_value = tf.gradients(self.loss_value,self.local_value_vars)
self.grads_value,self.grad_norms_value = tf.clip_by_global_norm(self.gradients_value,0.5)
self.global_vars_value = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,'global'+'/value')
self.apply_grads_value = trainer_critic.apply_gradients(zip(self.grads_value,self.global_vars_value))
except Exception as e:
common.echosentence_color('error in AC_NetWork -> init: {}'.format(str(e)) )
raise
## WORKER AGENT
class Worker():
def __init__(self, name, trainer, trainer_critic, model_path,log_path,roomrd_path, global_episodes,flag_rlaicolor,dic_mainparas,
flag_testai = False,testai_freq = 500,testai_runnums = 20,step_length = 100,history_policy_len = 5):
self.name = "worker_" + str(name)
self.number = name
self.model_path = model_path
self.roomrd_path = roomrd_path
self.flag_rlaicolor = flag_rlaicolor
self.trainer = trainer
self.trainer_critic = trainer_critic
self.global_episodes = global_episodes
self.increment = self.global_episodes.assign_add(1)
self.episode_rewards = []
self.episode_lengths = []
self.episode_mean_values = []
self.episode_winnums = []
self.l_actkeys = ['remain_scores','defeat_scores','city_scores']
self.dict_episode_scores = {'remain_scores':[],'defeat_scores':[],'city_scores':[]} #分数
self.dict_episode_actnums = {} #动作数
self.summary_writer = tf.summary.FileWriter(log_path) # 修改,由外部传入
self.flag_testai = flag_testai # 是否测试ai
self.testai_freq = testai_freq # 测试ai的频率
self.testai_runnums = testai_runnums # 测试50场的平均胜率
self.history_policy_len = history_policy_len #指训练价值网时所使用的历史策略网长度
self.step_length = step_length
self.episode_test_winrates = [] #测试的平均胜率
self.episode_test_winscores = [] #测试的平均净胜分
self.prob_dec_fn = lambda x : 1.0/(0.0009*x+1.1)
print('Initializing environment #{}...'.format(self.number))
self.env = wgenv.ENV(dic_mainparas = dic_mainparas,flag_rlaicolor=flag_rlaicolor)
filterMetadata(self.env,dic_hparas['BattleMode'])
# 初始化保存动作执行变量
self.num_whats = self.env.getWhatNums()
for i in range(self.num_whats):
self.dict_episode_actnums[i] = []
print('initlize static map feature...')
wgfeature.cfunRegister(self.env.obj_main)
wgfeature.calOneTimeFeature(self.env.obj_main)
wgfeature.calUpdateFeature(self.env.obj_main)
print('initlize static map feature done...')
# Create the local copy of the network and the tensorflow op to copy global paramters to local network
self.nonspatial_size = self.env.obj_main.dic_metadata['np_bopFeature'].shape[0]
tmp_map_size = self.env.obj_main.dic_metadata['np_mapFeature'].shape
self.map_size = (tmp_map_size[1],tmp_map_size[2],tmp_map_size[0])
self.l_action_keys = ['who','what','how']
dict_actionsepc ={self.l_action_keys[0]:self.env.getWhoNums(),self.l_action_keys[1]:self.env.getWhatNums(),self.l_action_keys[2]:self.env.getHowNums()}
self.local_AC = AC_Network(self.name, trainer, trainer_critic, self.nonspatial_size,self.map_size,dict_actionsepc)
self.update_local_ops = update_target_graph('global', self.name)
def train(self, list_rollout, sess, gamma, bootstrap_value,updateParas = None):
'''modify: 2018年11月09日 宋国瑞 添加updateParas 取值:['Policy','Value','None','Both'] None或Both表示两者都更新'''
try:
common.echosentence_color('--' * 20, 'red')
common.echosentence_color('Start Train {}'.format(updateParas))
for index in range(len(list_rollout)):
rollout = numpy.array(list_rollout[index])
s_np_mapFeature,s_np_bopFeature = rollout[:, 0],rollout[:,1]
np_l_chooseArg, rewards = rollout[:,2],rollout[:,3]
s1_np_mapFeature, s1_np_bopFeature = rollout[:,4],rollout[:,5]
episode_end, values = rollout[:,6],rollout[:,7]
actions_arg_stack = dict()
for l_chooseArg in np_l_chooseArg:
for i in range(len(self.l_action_keys)):
key = self.l_action_keys[i]
if key not in actions_arg_stack:
actions_arg_stack[key] = []
actions_arg_stack[key].append(l_chooseArg[i])
# Here we take the rewards and values from the rollout, and use them to calculate the advantage and discounted returns
# The advantage function uses generalized advantage estimation from [2]
self.rewards_plus = numpy.asarray(rewards.tolist() + [bootstrap_value])
discounted_rewards = discount(self.rewards_plus, gamma)[:-1]
self.value_plus = numpy.asarray(values.tolist() + [bootstrap_value])
advantages = rewards + gamma * self.value_plus[1:] - self.value_plus[:-1]
advantages = discount(advantages, gamma)
# Update the global network using gradients from loss
# Generate network statistics to periodically save
if index == 0:
feed_dict = {self.local_AC.target_v: discounted_rewards,
self.local_AC.inputs_spatial_screen: numpy.stack(s_np_mapFeature).reshape(-1, self.map_size[0],self.map_size[1],self.map_size[2]),
self.local_AC.inputs_nonspatial: numpy.stack(s_np_bopFeature).reshape(-1, self.nonspatial_size),
self.local_AC.advantages: advantages}
for key,value in actions_arg_stack.items():
feed_dict[self.local_AC.actions_arg[key]] = value
else:
feed_dict[self.local_AC.target_v] = numpy.concatenate((feed_dict[self.local_AC.target_v],discounted_rewards),axis = 0)
np_mapFeature = numpy.stack(s_np_mapFeature).reshape(-1, self.map_size[0],self.map_size[1],self.map_size[2])
np_bopFeature = numpy.stack(s_np_bopFeature).reshape(-1, self.nonspatial_size)
feed_dict[self.local_AC.inputs_spatial_screen] = numpy.concatenate((feed_dict[self.local_AC.inputs_spatial_screen], np_mapFeature), axis=0)
feed_dict[self.local_AC.inputs_nonspatial] = numpy.concatenate((feed_dict[self.local_AC.inputs_nonspatial], np_bopFeature), axis=0)
feed_dict[self.local_AC.advantages] = numpy.concatenate((feed_dict[self.local_AC.advantages], advantages), axis=0)
for key,value in actions_arg_stack.items():
feed_dict[self.local_AC.actions_arg[key]] += value
if dic_hparas['UpdateGradsMethod'] == 'Integration':
v_l, p_l, e_l, g_n, v_n, _ = sess.run([self.local_AC.value_loss,
self.local_AC.policy_loss,
self.local_AC.entropy,
self.local_AC.grad_norms,
self.local_AC.var_norms,
self.local_AC.apply_grads],
feed_dict=feed_dict)
elif dic_hparas['UpdateGradsMethod'] == 'Divide':
if updateParas == 'Policy':
v_l, p_l, e_l, g_n, v_n, _, = sess.run([self.local_AC.value_loss,
self.local_AC.policy_loss,
self.local_AC.entropy,
self.local_AC.grad_norms_policy,
self.local_AC.var_norms,
self.local_AC.apply_grads_policy,
],feed_dict=feed_dict)
elif updateParas == 'Value':
v_l, p_l, e_l, g_n, v_n, _, = sess.run([self.local_AC.value_loss,
self.local_AC.policy_loss,
self.local_AC.entropy,
self.local_AC.grad_norms_policy,
self.local_AC.var_norms,
self.local_AC.apply_grads_value,
],feed_dict=feed_dict)
else:
v_l, p_l, e_l, g_n, v_n, _, _, = sess.run([self.local_AC.value_loss,
self.local_AC.policy_loss,
self.local_AC.entropy,
self.local_AC.grad_norms_policy,
self.local_AC.var_norms,
self.local_AC.apply_grads_policy,
self.local_AC.apply_grads_value,
], feed_dict=feed_dict)
common.echosentence_color('--' * 20, 'red')
return v_l / rollout.shape[0] / rollout.shape[1], p_l / rollout.shape[0] / rollout.shape[1], \
e_l / rollout.shape[0] / rollout.shape[1], g_n, v_n
except Exception as e:
common.echosentence_color('error in test_rl.py->train():{}'.format(str(e)))
self.env.__del__()
raise
except KeyboardInterrupt as k:
common.echosentence_color('error in test_rl.py->train():{}'.format(str(k)))
self.env.__del__()
raise
def getwincolor(self,dic_metadata):
'''打印态势信息'''
try:
list_values = wgsdata.getSdataValue(dic_metadata)
# cout_left = [0] * 2
# for cur_bop in self.dic_metadata['l_rbops'] + self.dic_metadata['l_bbops'] + self.dic_metadata['l_pbops']:
# if cur_bop.ObjBlood > 0:
# cout_left[int(cur_bop.GameColor)] += 1
# print '\t 红方剩余算子数量:%d. 剩余兵力分值:%d, 夺控得分:%d' % (cout_left[0], list_values[0], list_values[1])
# print '\t 蓝方剩余算子数量:%d. 剩余兵力分值:%d, 夺控得分:%d' % (cout_left[1], list_values[2], list_values[3])
return 0 if list_values[0] + list_values[1] - list_values[2] > \
list_values[2] + list_values[3] - list_values[0] \
else 1
except Exception as e:
common.echosentence_color('wgsdata > showSDDat():{}'.format(str(e)))
raise
def work(self, max_episode_length, gamma, sess, coord, saver):
try:
episode_count = sess.run(self.global_episodes)
total_steps = 0
print ("Starting worker " + str(self.number))
with sess.as_default(), sess.graph.as_default():
t_start = time.time()
winnums = 0
list3_episode_buffer = []
while True: #not coord.should_stop():
# Download copy of parameters from global network
# prob = self.prob_dec_fn(episode_count) #用规则ai的概率
prob = 0
sess.run(self.update_local_ops)
episode_buffer = []
episode_values = []
# episode_frames = []
episode_reward = 0
episode_step_count = 0
episode_end = False
# 测试ai性能
if self.flag_testai and episode_count % self.testai_freq == 0 and episode_count >= 0:
winrate,winscore = self.test_ai(sess,self.testai_runnums)
self.episode_test_winrates.append(winrate)
self.episode_test_winscores.append(winscore)
common.echosentence_color('====================','blue')
common.echosentence_color('测试{}场的胜率为:{}% ;净胜分为 {}'.format(self.testai_runnums,winrate*100,winscore))
common.echosentence_color('====================', 'yellow')
summary = tf.Summary()
summary.value.add(tag='test/WinRate', simple_value=float(winrate))
summary.value.add(tag='test/WinScore', simple_value=float(winscore))
self.summary_writer.add_summary(summary, episode_count)
self.summary_writer.flush()
flag_wincolor = 0
# Start new episode
flag_goon = self.env.reset()
episode_end = not flag_goon
np_mapFeature,np_bopFeature = process_observation(self.env.obj_main) #resize np_mapFeature to [1,2,0]
s_np_mapFeature,s_np_bopFeature = np_mapFeature,np_bopFeature
for i in range(self.num_whats):
self.dict_episode_actnums[i].append(0)
while not episode_end:
flag_baseai = False
if(random.random() < prob):
flag_thisvalid,flag_goon,reward,list2_chooseArg,list_npMapFea,list_npBopFea = self.env.stepFollowBaseAI() # 依概率选择按照规则AI走,不参与训练
if flag_thisvalid: #生成有效动作
flag_baseai = True
else: #否则按照rl-ai选择动作
flag_baseai = False
policy_args, v = sess.run([self.local_AC.policy_arg, self.local_AC.value],
feed_dict={self.local_AC.inputs_spatial_screen: np_mapFeature,
self.local_AC.inputs_nonspatial: np_bopFeature})
l_nppolicy = []
for policy_key in self.l_action_keys:
l_nppolicy.append(policy_args[policy_key][0, :])
flag_goon, reward, l_chooseArg = self.env.step(l_nppolicy[0], l_nppolicy[1],l_nppolicy[2])
else:
policy_args, v = sess.run([self.local_AC.policy_arg, self.local_AC.value],
feed_dict={self.local_AC.inputs_spatial_screen: np_mapFeature,
self.local_AC.inputs_nonspatial: np_bopFeature})
# nonspatial_dense,screen_conv1,screen_conv2,latent_vector= sess.run([self.local_AC.nonspatial_dense, self.local_AC.screen_conv1,self.local_AC.screen_conv2,self.local_AC.latent_vector],
# feed_dict={self.local_AC.inputs_spatial_screen: np_mapFeature,
# self.local_AC.inputs_nonspatial: np_bopFeature})
#
# local_vars,global_vars = sess.run([self.local_AC.local_vars,self.local_AC.global_vars])
l_nppolicy = []
for policy_key in self.l_action_keys:
l_nppolicy.append(policy_args[policy_key][0, :])
flag_goon, reward, l_chooseArg = self.env.step(l_nppolicy[0], l_nppolicy[1], l_nppolicy[2])
episode_end = not flag_goon
np_mapFeature, np_bopFeature = process_observation(self.env.obj_main) # resize np_mapFeature to [1,2,0]
if episode_end: #游戏结束,根据游戏胜负修正最后的reward
flag_wincolor = self.getwincolor(self.env.obj_main.dic_metadata)
if flag_rlaicolor == flag_wincolor:
reward += 20 # [0,200]
s1_np_mapFeature, s1_np_bopFeature = np_mapFeature, np_bopFeature
# if not episode_end:
# s1_np_mapFeature, s1_np_bopFeature = np_mapFeature, np_bopFeature
# else:
# s1_np_mapFeature, s1_np_bopFeature = s_np_mapFeature,s_np_bopFeature
# Append latest state to buffer
if not flag_baseai:
if l_chooseArg != None:
self.dict_episode_actnums[l_chooseArg[1]][-1] += 1
episode_buffer.append([s_np_mapFeature,s_np_bopFeature, l_chooseArg, reward, s1_np_mapFeature,s1_np_bopFeature, episode_end, v[0, 0]])
episode_values.append(v[0, 0])
else:
assert (len(list_npBopFea) == len(list2_chooseArg))
list_npMapFea = [s_np_mapFeature] + list_npMapFea # 修复bug 动作执行之前的状态应该是s_np_mapFeature
list_npBopFea = [s_np_bopFeature] + list_npBopFea
for l_i in range(len(list2_chooseArg)):
l_chooseArg,s_np_mapFeature,s_np_bopFeature = list2_chooseArg[l_i],list_npMapFea[l_i],list_npBopFea[l_i]
s1_np_mapFeature,s1_np_bopFeature = list_npMapFea[l_i+1],list_npBopFea[l_i+1]
v = sess.run(self.local_AC.value,
feed_dict={self.local_AC.inputs_spatial_screen: s_np_mapFeature,
self.local_AC.inputs_nonspatial: s_np_bopFeature})
self.dict_episode_actnums[l_chooseArg[1]][-1] += 1
episode_buffer.append([s_np_mapFeature, s_np_bopFeature, l_chooseArg, reward, s1_np_mapFeature,s1_np_bopFeature, episode_end, v[0, 0]])
episode_values.append(v[0, 0])
episode_reward = reward
s_np_mapFeature, s_np_bopFeature = s1_np_mapFeature, s1_np_bopFeature
total_steps += 1
episode_step_count += 1
# If the episode hasn't ended, but the experience buffer is full, then we make an update step using that experience rollout
if len(episode_buffer) == self.step_length and not episode_end and episode_step_count != max_episode_length - 1:
# Since we don't know what the true final return is, we "bootstrap" from our current value estimation
v1 = sess.run(self.local_AC.value,feed_dict={self.local_AC.inputs_spatial_screen: np_mapFeature,
self.local_AC.inputs_nonspatial: np_bopFeature})
v_l, p_l, e_l, g_n, v_n = self.train(episode_buffer, sess, gamma, v1)
episode_buffer = []
sess.run(self.update_local_ops)
if episode_end:
break
if flag_wincolor == flag_rlaicolor:
winnums += 1
self.episode_rewards.append(episode_reward)
self.episode_lengths.append(episode_step_count)
self.episode_mean_values.append(numpy.mean(episode_values))
self.episode_winnums.append(winnums)
#计算分数
l_scores = calScore(self.env.obj_main,flag_rlaicolor)
for i in range(len(self.l_actkeys)):
self.dict_episode_scores[self.l_actkeys[i]].append(l_scores[i])
episode_count += 1
global _max_score, _running_avg_score, _episodes, _steps
if _max_score < episode_reward:
_max_score = episode_reward
_running_avg_score = (2.0 / 101) * (episode_reward - _running_avg_score) + _running_avg_score
_episodes[self.number] = episode_count
_steps[self.number] = total_steps
t_now = time.time()
print("{} Step #{} Episode #{} Reward: {}".format(self.name, total_steps, episode_count,
episode_reward))
print("Total Steps: {}\tTotal Episodes: {}\tTotal time: {}\tMax Score: {}\tAvg Score: {}".format(numpy.sum(_steps),
numpy.sum(_episodes),
t_now-t_start,
_max_score,
_running_avg_score))
print(self.l_actkeys[0] + ' : ' + str(l_scores[0]) + ' '+self.l_actkeys[1] + ' : ' + str(l_scores[1]) + ' '
+self.l_actkeys[2] + ' : ' + str(l_scores[2]) + ' ')
common.echosentence_color('=='*10,'blue')
# Update the network using the episode buffer at the end of the episode
if len(episode_buffer) != 0:
if len(list3_episode_buffer) == self.history_policy_len: # 保证buffer中最多有history_policy_len个策略生成的数据
list3_episode_buffer = list3_episode_buffer[1:]
list3_episode_buffer.append(episode_buffer)
# v1 = sess.run(self.local_AC.value,feed_dict={self.local_AC.inputs_spatial_screen: np_mapFeature,
# self.local_AC.inputs_nonspatial: np_bopFeature})
if dic_hparas['UpdateGradsMethod'] == 'Integration':
v_l, p_l, e_l, g_n, v_n = self.train(episode_buffer, sess, gamma, 0,
updateParas='Both') # 用v1替换0; 2018年09月11日重新替换为0
else:
v_l, p_l, e_l, g_n, v_n = self.train([episode_buffer], sess, gamma, 0,updateParas = 'Policy') # 用v1替换0; 2018年09月11日重新替换为0
v_l, p_l, e_l, g_n, v_n = self.train(list3_episode_buffer, sess, gamma, 0,
updateParas='Value') # 用v1替换0; 2018年09月11日重新替换为0
if episode_count % 5 == 0 and episode_count != 0:
if episode_count % 250 == 0 and self.name == 'worker_0':
saver.save(sess, self.model_path + '/model-' + str(0) + '.cptk')
print ("Saved Model")
mean_reward = numpy.mean(self.episode_rewards[-5:])
mean_length = numpy.mean(self.episode_lengths[-5:])
mean_value = numpy.mean(self.episode_mean_values[-5:])
mean_winnums = numpy.mean(self.episode_winnums[-5:])
summary = tf.Summary()
summary.value.add(tag='Perf/Reward', simple_value=float(mean_reward))
summary.value.add(tag='Perf/Length', simple_value=float(mean_length))
summary.value.add(tag='Perf/Value', simple_value=float(mean_value))
summary.value.add(tag='Perf/winums', simple_value=float(mean_winnums))
summary.value.add(tag='Losses/Value Loss', simple_value=float(v_l))
summary.value.add(tag='Losses/Policy Loss', simple_value=float(p_l))
summary.value.add(tag='Losses/Entropy', simple_value=float(e_l))
summary.value.add(tag='Losses/Grad Norm', simple_value=float(g_n))
summary.value.add(tag='Losses/Var Norm', simple_value=float(v_n))
for i in range(self.num_whats):
summary.value.add(tag='ActNums/'+str(i),simple_value=float(self.dict_episode_actnums[i][-1]))
for actkey in self.l_actkeys:
summary.value.add(tag='Scores/' + actkey, simple_value=float(self.dict_episode_scores[actkey][-1]))
self.summary_writer.add_summary(summary, episode_count)
self.summary_writer.flush()
if self.name == 'worker_0':
sess.run(self.increment)
except Exception as e:
common.echosentence_color('error in test_rl.py->work():{}'.format(str(e)))
self.env.__del__()
raise
except KeyboardInterrupt as k:
common.echosentence_color('error in test_rl.py->work():{}'.format(str(k)))
self.env.__del__()
raise
def test_ai(self,sess,test_nums):
try:
winrate = 0
winscore = 0
for gamenum in range(test_nums):
flag_goon = self.env.reset()
episode_end = not flag_goon
np_mapFeature, np_bopFeature = process_observation(self.env.obj_main) # resize np_mapFeature to [1,2,0]
while not episode_end:
policy_args, v = sess.run([self.local_AC.policy_arg, self.local_AC.value],
feed_dict={self.local_AC.inputs_spatial_screen: np_mapFeature,
self.local_AC.inputs_nonspatial: np_bopFeature})
l_nppolicy = []
for policy_key in self.l_action_keys:
l_nppolicy.append(policy_args[policy_key][0, :])
flag_goon, reward, l_chooseArg = self.env.step(l_nppolicy[0], l_nppolicy[1], l_nppolicy[2])
episode_end = not flag_goon
np_mapFeature, np_bopFeature = process_observation(self.env.obj_main) # resize np_mapFeature to [1,2,0]
list_values = wgsdata.getSdataValue(self.env.obj_main.dic_metadata)
red_score = 2*list_values[0] + list_values[1] - 2* list_values[2] - list_values[3]
myscore = red_score if self.flag_rlaicolor == 0 else -red_score
winrate = winrate + 1 if myscore > 0 else winrate
winscore += myscore
wincolor = 'RED' if red_score > 0 else 'BLUE'
if self.env.obj_main.obj_pk.dic_paras['flag_qd_rm'] and random.random() < 0.1:
str_savefile = self.roomrd_path + 'BAK_Trainnums-{}_Testnums-{}-WinColor={}.xls'.format(int(_episodes[self.number])
,gamenum,wincolor)
wgsdata.record(self.env.obj_main,str_savefile)
winrate /= test_nums
winscore /= test_nums
return winrate,winscore
except Exception as e:
common.echosentence_color('error in test_rl.py->Worker->test_ai():{}'.format(str(e)))
self.env.__del__()
raise
except KeyboardInterrupt as k:
common.echosentence_color('error in test_rl.py->Worker->test_ai():{}'.format(str(k)))
self.env.__del__()
raise
def defmainparas():
try:
num_xd, num_plays, num_objcutility = 0, 1, 1
strategy_id_r, strategy_id_b = 0, 1
import AI_QD, wgdensestranet
dic2_rolloutaiparas = {
'red': {'type_ai': AI_QD.AI_QD_BASE,
'type_stra': 'rule-base',
'type_stranet': wgdensestranet.StraDenseNet,
},
'blue': {'type_ai': AI_QD.AI_QD_STRA,
'type_stra': 'random',
'type_stranet': wgdensestranet.StraDenseNet,
},
}
dic_mainparas = {'str_wgrootdir': '../../',
'str_global_flag': 'QD',
'flag_show': True,
'num_plays': num_plays,
'num_objcutility': num_objcutility,
'num_xd': num_xd,
'strategy_ids': (strategy_id_r,strategy_id_b),
'flag_action_cache': False,
'flag_qd_rm': True,
'flag_cache': False,
'flag_gpu': False,
'flag_afm': True, # 两个AI都为BASE时,flag_afm=False; 否则为True
'flag_dllnum': 0,
'cuda_id': 0,
'flag_savestate': False,
'dic2_aiparas': {
'flag_color4acai': 0,
'red': {'type_ai': AI_QD.AI_QD_DEMO,
'type_stra': 'rule-base',
# type of stratree of nodes, how to select next path, [rule-base, random, net]
'type_stranet': wgdensestranet.StraDenseNet,
'dic2_rolloutaiparas': dic2_rolloutaiparas,
'flag_candidateactions': 'rule-base'
# [rule-base, stra] how to get candidate actions
},
'blue': {'type_ai': AI_QD.AI_QD_HA,
'type_stra': 'net',
'type_stranet': wgdensestranet.StraDenseNet,
'dic2_rolloutaiparas': dic2_rolloutaiparas,
'flag_candidateactions': 'stra'
},
},
'rewrad_type': 'nowscore-base', # ['nowsocoe-base','action-base']
}
return dic_mainparas
except Exception as e:
print('error in defmainparas() : '+str(e))
raise
dic_hparas = {
'BattleMode' : '6v6',
'NetType': 'ResNet', #['AtariNet','ResNet']
'UpdateGradsMethod':'Divide' #['Integration','Divide']
}
global flag_ignoreEnemyReward
flag_ignoreEnemyReward = True
if __name__ == '__main__':
try:
'''注: 需要修改参数说明
修改rl颜色: flag_rlaicolor
defmainparas()中的dic_mainparas中dic2_paras的'red'和'blue'互换
修改算子数: BattleMode (1v0,1v1,6v6)
是否加载已训练模型: load_model
注意:每次更换实验条件之后,记得修改exp_version,免得覆盖之前的游戏版本
'''
load_model = False
flag_rlaicolor = 1
exp_version = 'v10'
num_workers = 1 # 多线程数 multiprocessing.cpu_count()
flag_testai = True
testai_freq = 500 # 测试ai的频率
testai_runnums = 50 # 测试50场的平均胜率
step_length = 200 # 推演多少步进行一次训练
max_episode_length = 300
gamma = 0.99 # Discount rate for advantage estimation and reward discounting
history_policy_len = 5 # 指训练价值网时所使用的历史策略网长度
str_ai = 'red' if flag_rlaicolor == 0 else 'blue'
save_path = '../../../a3c-explog/'+ dic_hparas['BattleMode']+'_'+str_ai+'_'+exp_version+'/'
model_path = save_path + 'model/'
log_path = save_path + 'train_0/'
roomrd_path = save_path + 'roomrecord/'
dic_mainparas = defmainparas()
tf.reset_default_graph()
if not os.path.exists(model_path):
load_model = False
os.makedirs(model_path)
os.makedirs(roomrd_path)
common.echosentence_color('BattleMode = ' + dic_hparas['BattleMode'] ,'yellow')
print('Initializing temporary environment to retrive action_spec...')
env = wgenv.ENV(dic_mainparas = dic_mainparas,flag_rlaicolor=flag_rlaicolor)
filterMetadata(env,dic_hparas['BattleMode'])
wgfeature.cfunRegister(env.obj_main)
wgfeature.calOneTimeFeature(env.obj_main)
wgfeature.calUpdateFeature(env.obj_main)
nonspatial_size = env.obj_main.dic_metadata['np_bopFeature'].shape[0]
tmp_map_size = env.obj_main.dic_metadata['np_mapFeature'].shape
map_size = (tmp_map_size[1], tmp_map_size[2], tmp_map_size[0])
# if dic_hparas['NetType'] == 'ResNet':
# map_size = (tmp_map_size[1], tmp_map_size[1], tmp_map_size[0])
l_action_keys = ['who', 'what', 'how']
dict_actionsepc = {l_action_keys[0]: env.getWhoNums(), l_action_keys[1]: env.getWhatNums(),l_action_keys[2]: env.getHowNums()}
print('Initializing temporary environment to retrive action_spec done...')
with tf.device("/cpu:0"):
global_episodes = tf.Variable(0, dtype=tf.int32, name='global_episodes', trainable=False)
trainer = tf.train.AdamOptimizer(learning_rate=1e-4)
trainer_critic = tf.train.AdamOptimizer(learning_rate=3e-4)
master_network = AC_Network('global', None, None, nonspatial_size,map_size,dict_actionsepc) # Generate global network
global _max_score, _running_avg_score, _steps, _episodes
_max_score = -200
_running_avg_score = 0
_steps = numpy.zeros(num_workers)
_episodes = numpy.zeros(num_workers)
workers = []
# Create worker classes
for i in range(num_workers):
tmp_dic_mainparas = copy.deepcopy(dic_mainparas)
dic_mainparas['flag_dllnum'] = i
workers.append(Worker(i, trainer, trainer_critic, model_path, log_path, roomrd_path,global_episodes, flag_rlaicolor,dic_mainparas,
flag_testai=flag_testai,testai_freq=testai_freq,testai_runnums= testai_runnums,step_length=step_length
,history_policy_len = history_policy_len))
saver = tf.train.Saver(max_to_keep=5)
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
with tf.Session(config = config) as sess:
coord = tf.train.Coordinator()
if load_model == True:
print ('Loading Model...')
ckpt = tf.train.get_checkpoint_state(model_path)
saver.restore(sess, ckpt.model_checkpoint_path)
else:
sess.run(tf.global_variables_initializer())
# This is where the asynchronous magic happens
# Start the "work" process for each worker in a separate thread
# while(True):
# for worker in workers:
# worker.work(max_episode_length, gamma, sess, None, saver)
worker_threads = []
for worker in workers:
worker_work = lambda: worker.work(max_episode_length, gamma, sess, coord, saver)
t = threading.Thread(target=(worker_work))
t.start()
time.sleep(0.001)
worker_threads.append(t)
coord.join(worker_threads)
except Exception as e:
common.echosentence_color('error in test_rl.py-> main() : {}'.format(str(e)))
env.__del__()
raise
except KeyboardInterrupt as k:
common.echosentence_color('error in test_rl.py-> main() : {}'.format(str(k)))
env.__del__()
raise
#