更好的理解TensorFlow 2.0的新特性

自tensorflow官方发布其2.0版本新性能以来,不少人可能对此会有些许困惑。因此博主roman ring写了一篇概述性的文章,通过实现深度强化学习算法来具体的展示了tensorflow 2.0的特性。
正所谓实践出真知。
tensorflow 2.0的特性公布已经有一段时间了,但很多人对此应当还是一头雾水。
在本教程中,作者通过深度强化学习(drl)来展示即将到来的tensorflow 2.0的特性,具体来讲就是通过实现优势actor-critic(演员-评判家,a2c)智能体来解决经典的cartpole-v0环境。
虽然作者本文的目标是展示tensorflow 2.0,但他先介绍了drl方面的内容,包括对该领域的简要概述。
事实上,由于2.0版本的主要关注点是简化开发人员的工作,即易用性,所以现在正是使用tensorflow进入drl的好时机。
本文完整代码资源链接:github:https://github.com/inoryy/tensorflow2-deep-reinforcement-learning
google colab:https://colab.research.google.com/drive/12qvw7vzszoaf-org-u-n6aitdbn5ohna
安装
由于tensorflow 2.0仍处于试验阶段,建议将其安装在一个独立的(虚拟)环境中。我比较倾向于使用anaconda,所以以此来做说明:
>condacreate-ntf2python=3.6>sourceactivatetf2>pipinstalltf-nightly-2.0-preview#tf-nightly-gpu-2.0-previewforgpuversion
让我们来快速验证一下,一切是否按着预测正常工作:
>>>importtensorflowastf>>>print(tf.__version__)1.13.0-dev20190117>>>print(tf.executing_eagerly())true
不必担心1.13.x版本,这只是一个早期预览。此处需要注意的是,默认情况下我们是处于eager模式的!
>>>print(tf.reduce_sum([1,2,3,4,5]))tf.tensor(15,shape=(),dtype=int32)
如果读者对eager模式并不熟悉,那么简单来讲,从本质上它意味着计算是在运行时(runtime)被执行的,而不是通过预编译的图(graph)来执行。读者也可以在tensorflow文档中对此做深入了解:
https://www.tensorflow.org/tutorials/eager/eager_basics
深度强化学习
一般来说,强化学习是解决顺序决策问题的高级框架。rl智能体通过基于某些观察采取行动来导航环境,并因此获得奖励。大多数rl算法的工作原理是最大化智能体在一个轨迹中所收集的奖励的总和。
基于rl的算法的输出通常是一个策略—一个将状态映射到操作的函数。有效的策略可以像硬编码的no-op操作一样简单。随机策略表示为给定状态下行为的条件概率分布。
actor-critic方法
rl算法通常根据优化的目标函数进行分组。基于值的方法(如dqn)通过减少预期状态-动作值(state-action value)的误差来工作。
策略梯度(policy gradient)方法通过调整其参数直接优化策略本身,通常是通过梯度下降。完全计算梯度通常是很困难的,所以通常用蒙特卡洛(monte-carlo)方法来估计梯度。
最流行的方法是二者的混合:actor- critical方法,其中智能体策略通过“策略梯度”进行优化,而基于值的方法则用作期望值估计的引导。
深度actor- critical方法
虽然很多基础的rl理论是在表格案例中开发的,但现代rl几乎完全是用函数逼近器完成的,例如人工神经网络。具体来说,如果策略和值函数用深度神经网络近似,则rl算法被认为是“深度的”。
异步优势(asynchronous advantage) actor- critical
多年来,为了解决样本效率和学习过程的稳定性问题,已经为此做出了一些改进。
首先,梯度用回报(return)来进行加权:折现的未来奖励,这在一定程度上缓解了信用(credit)分配问题,并以无限的时间步长解决了理论问题。
其次,使用优势函数代替原始回报。收益与基线(如状态行动估计)之间的差异形成了优势,可以将其视为与某一平均值相比某一给定操作有多好的衡量标准。
第三,在目标函数中使用额外的熵最大化项,以确保智能体充分探索各种策略。本质上,熵以均匀分布最大化,来测量概率分布的随机性。
最后,并行使用多个worker来加速样品采集,同时在训练期间帮助将它们去相关(decorrelate)。
将所有这些变化与深度神经网络结合起来,我们得到了两种最流行的现代算法:异步优势actor- critical算法,或简称a3c/a2c。两者之间的区别更多的是技术上的而不是理论上的:顾名思义,它归结为并行worker如何估计其梯度并将其传播到模型中。
有了这些,我将结束我们的drl方法之旅,因为这篇博客文章的重点是tensorflow 2.0特性。如果您仍然不确定主题,不要担心,通过代码示例,一切都会变得更加清晰明了。
使用tensorflow 2.0实现advantage actor-critic
让我们看看实现各种现代drl算法的基础是什么:是actor-critic agent,如前一节所述。为了简单起见,我们不会实现并行worker,尽管大多数代码都支持它。感兴趣的读者可以将这作为一个练习机会。
作为一个测试平台,我们将使用cartpole-v0环境。虽然有点简单,但它仍然是一个很好的选择。
通过keras模型api实现的策略和价值
首先,让我们在单个模型类下创建策略和价值预估神经网络:
importnumpyasnpimporttensorflowastfimporttensorflow.keras.layersasklclassprobabilitydistribution(tf.keras.model):defcall(self,logits):#samplearandomcategoricalactionfromgivenlogitsreturntf.squeeze(tf.random.categorical(logits,1),axis=-1)classmodel(tf.keras.model):def__init__(self,num_actions):super().__init__('mlp_policy')#notf.get_variable(),justsimplekerasapiself.hidden1=kl.dense(128,activation='relu')self.hidden2=kl.dense(128,activation='relu')self.value=kl.dense(1,name='value')#logitsareunnormalizedlogprobabilitiesself.logits=kl.dense(num_actions,name='policy_logits')self.dist=probabilitydistribution()defcall(self,inputs):#inputsisanumpyarray,converttotensorx=tf.convert_to_tensor(inputs,dtype=tf.float32)#separatehiddenlayersfromthesameinputtensorhidden_logs=self.hidden1(x)hidden_vals=self.hidden2(x)returnself.logits(hidden_logs),self.value(hidden_vals)defaction_value(self,obs):#executescall()underthehoodlogits,value=self.predict(obs)action=self.dist.predict(logits)#asimpleroption,willbecomeclearlaterwhywedon'tuseit#action=tf.random.categorical(logits,1)returnnp.squeeze(action,axis=-1),np.squeeze(value,axis=-1)
然后验证模型是否如预期工作:
importgymenv=gym.make('cartpole-v0')model=model(num_actions=env.action_space.n)obs=env.reset()#nofeed_dictortf.session()neededatallaction,value=model.action_value(obs[none,:])print(action,value)#[1][-0.00145713]
这里需要注意的是:
模型层和执行路径是分别定义的
没有“输入”层,模型将接受原始numpy数组
通过函数api可以在一个模型中定义两个计算路径
模型可以包含一些辅助方法,比如动作采样
在eager模式下,一切都可以从原始numpy数组中运行
random agent
现在让我们转到 a2cagent 类。首先,让我们添加一个 test 方法,该方法运行完整的episode并返回奖励的总和。
classa2cagent:def__init__(self,model):self.model=modeldeftest(self,env,render=true):obs,done,ep_reward=env.reset(),false,0whilenotdone:action,_=self.model.action_value(obs[none,:])obs,reward,done,_=env.step(action)ep_reward+=rewardifrender:env.render()returnep_reward
让我们看看模型在随机初始化权重下的得分:
agent=a2cagent(model)rewards_sum=agent.test(env)print(%doutof200%rewards_sum)#18outof200
离最佳状态还很远,接下来是训练部分!
损失/目标函数
正如我在drl概述部分中所描述的,agent通过基于某些损失(目标)函数的梯度下降来改进其策略。在 actor-critic 中,我们针对三个目标进行训练:利用优势加权梯度加上熵最大化来改进策略,以及最小化价值估计误差。
importtensorflow.keras.lossesasklsimporttensorflow.keras.optimizersaskoclassa2cagent:def__init__(self,model):#hyperparametersforlosstermsself.params={'value':0.5,'entropy':0.0001}self.model=modelself.model.compile(optimizer=ko.rmsprop(lr=0.0007),#defineseparatelossesforpolicylogitsandvalueestimateloss=[self._logits_loss,self._value_loss])deftest(self,env,render=true):#unchangedfromprevioussection...def_value_loss(self,returns,value):#valuelossistypicallymsebetweenvalueestimatesandreturnsreturnself.params['value']*kls.mean_squared_error(returns,value)def_logits_loss(self,acts_and_advs,logits):#atricktoinputactionsandadvantagesthroughsameapiactions,advantages=tf.split(acts_and_advs,2,axis=-1)#polymorphiccelossfunctionthatsupportssparseandweightedoptions#from_logitsargumentensurestransformationintonormalizedprobabilitiescross_entropy=kls.categoricalcrossentropy(from_logits=true)#policylossisdefinedbypolicygradients,weightedbyadvantages#note:weonlycalculatethelossontheactionswe'veactuallytaken#thusunderthehoodasparseversionofcelosswillbeexecutedactions=tf.cast(actions,tf.int32)policy_loss=cross_entropy(actions,logits,sample_weight=advantages)#entropylosscanbecalculatedviaceoveritselfentropy_loss=cross_entropy(logits,logits)#heresignsareflippedbecauseoptimizerminimizesreturnpolicy_loss-self.params['entropy']*entropy_loss
我们完成了目标函数!注意代码非常紧凑:注释行几乎比代码本身还多。
agent training loop
最后,还有训练环路。它有点长,但相当简单:收集样本,计算回报和优势,并在其上训练模型。
classa2cagent:def__init__(self,model):#hyperparametersforlosstermsself.params={'value':0.5,'entropy':0.0001,'gamma':0.99}#unchangedfromprevioussection...deftrain(self,env,batch_sz=32,updates=1000):#storagehelpersforasinglebatchofdataactions=np.empty((batch_sz,),dtype=np.int32)rewards,dones,values=np.empty((3,batch_sz))observations=np.empty((batch_sz,)+env.observation_space.shape)#trainingloop:collectsamples,sendtooptimizer,repeatupdatestimesep_rews=[0.0]next_obs=env.reset()forupdateinrange(updates):forstepinrange(batch_sz):observations[step]=next_obs.copy()actions[step],values[step]=self.model.action_value(next_obs[none,:])next_obs,rewards[step],dones[step],_=env.step(actions[step])ep_rews[-1]+=rewards[step]ifdones[step]:ep_rews.append(0.0)next_obs=env.reset()_,next_value=self.model.action_value(next_obs[none,:])returns,advs=self._returns_advantages(rewards,dones,values,next_value)#atricktoinputactionsandadvantagesthroughsameapiacts_and_advs=np.concatenate([actions[:,none],advs[:,none]],axis=-1)#performsafulltrainingsteponthecollectedbatch#note:noneedtomessaroundwithgradients,kerasapihandlesitlosses=self.model.train_on_batch(observations,[acts_and_advs,returns])returnep_rewsdef_returns_advantages(self,rewards,dones,values,next_value):#next_valueisthebootstrapvalueestimateofafuturestate(thecritic)returns=np.append(np.zeros_like(rewards),next_value,axis=-1)#returnsarecalculatedasdiscountedsumoffuturerewardsfortinreversed(range(rewards.shape[0])):returns[t]=rewards[t]+self.params['gamma']*returns[t+1]*(1-dones[t])returns=returns[:-1]#advantagesarereturns-baseline,valueestimatesinourcaseadvantages=returns-valuesreturnreturns,advantagesdeftest(self,env,render=true):#unchangedfromprevioussection...def_value_loss(self,returns,value):#unchangedfromprevioussection...def_logits_loss(self,acts_and_advs,logits):#unchangedfromprevioussection...
训练&结果
我们现在已经准备好在cartpole-v0上训练这个single-worker a2c agent!训练过程应该只用几分钟。训练结束后,你应该看到一个智能体成功地实现了200分的目标。
rewards_history=agent.train(env)print(finishedtraining,testing...)print(%doutof200%agent.test(env))#200outof200
在源代码中,我包含了一些额外的帮助程序,可以打印出正在运行的episode的奖励和损失,以及rewards_history。
静态计算图
eager mode效果这么好,你可能会想知道静态图执行是否也可以。当然是可以!而且,只需要多加一行代码就可以启用静态图执行。
withtf.graph().as_default():print(tf.executing_eagerly())#falsemodel=model(num_actions=env.action_space.n)agent=a2cagent(model)rewards_history=agent.train(env)print(finishedtraining,testing...)print(%doutof200%agent.test(env))#200outof200
有一点需要注意的是,在静态图执行期间,我们不能只使用 tensors,这就是为什么我们需要在模型定义期间使用categoricaldistribution的技巧。
one more thing…
还记得我说过tensorflow在默认情况下以eager 模式运行,甚至用一个代码片段来证明它吗?好吧,我骗了你。
如果你使用keras api来构建和管理模型,那么它将尝试在底层将它们编译为静态图。所以你最终得到的是静态计算图的性能,它具有eager execution的灵活性。
你可以通过model.run_eager标志检查模型的状态,还可以通过将此标志设置为true来强制使用eager mode,尽管大多数情况下可能不需要这样做——如果keras检测到没有办法绕过eager mode,它将自动退出。
为了说明它确实是作为静态图运行的,这里有一个简单的基准测试:
#createa100000samplesbatchenv=gym.make('cartpole-v0')obs=np.repeat(env.reset()[none,:],100000,axis=0)
eager benchmark
%%timemodel=model(env.action_space.n)model.run_eagerly=trueprint(eagerexecution:,tf.executing_eagerly())print(eagerkerasmodel:,model.run_eagerly)_=model(obs)########results#######eagerexecution:trueeagerkerasmodel:truecputimes:user639ms,sys:736ms,total:1.38s
static benchmark
%%timewithtf.graph().as_default():model=model(env.action_space.n)print(eagerexecution:,tf.executing_eagerly())print(eagerkerasmodel:,model.run_eagerly)_=model.predict(obs)########results#######eagerexecution:falseeagerkerasmodel:falsecputimes:user793ms,sys:79.7ms,total:873ms
default benchmark
%%timemodel=model(env.action_space.n)print(eagerexecution:,tf.executing_eagerly())print(eagerkerasmodel:,model.run_eagerly)_=model.predict(obs)########results#######eagerexecution:trueeagerkerasmodel:falsecputimes:user994ms,sys:23.1ms,total:1.02s
正如你所看到的,eager模式位于静态模式之后,默认情况下,模型确实是静态执行的。
结论
希望本文对理解drl和即将到来的tensorflow 2.0有所帮助。请注意,tensorflow 2.0仍然只是预览版的,一切都有可能发生变化,如果你对tensorflow有什么特别不喜欢(或喜欢:))的地方,请反馈给开发者。
一个总被提起的问题是,tensorflow是否比pytorch更好?也许是,也许不是。两者都是很好的库,所以很难说是哪一个更好。如果你熟悉pytorch,你可能会注意到tensorflow 2.0不仅赶上了它,而且还避免了pytorch api的一些缺陷。
无论最后谁胜出,对于开发者来说,这场竞争给双方都带来了净积极的结果,我很期待看到这些框架未来会变成什么样子。

存储空间不够大?试试华为云OBS对象存储服务
虹膜识别的优势有哪些?指纹识别会被虹膜识别取代吗?
安捷伦全新公司“是德科技”,开启测量新视野
ZXUN uMAC-AMF 5G到4G切换成功率优化
如何正确进行射频连接
更好的理解TensorFlow 2.0的新特性
第十代小金刚Redmi Note10官宣 全系列全球销量已突破2亿台
荣耀Magic2游戏性能实测 到底怎么样
晶圆尺寸从300毫米过渡到450毫米的技术挑战
PCB的层叠设计指南
《物联网技术》杂志跻身2020年RCCSE计算机科学技术B+行列
不需要GPS的无人机,你相信吗?
学会嵌入式Linux下I2C的接口调试
3D打印PETG需要注意的几点问题
基于区块链去中心化的应用程序R8网络介绍
zpwsmile Steag系统执行CMP后清洁至0.12微米及以下
2019款iPhone最新消息汇总 到底香不香
直流隔离电源电路图大全(DC/DC/变压销隔离/精密开关电源电路图)
Q4季度阿里云首次实现盈亏平衡,营收同比增长50%
水产品药物残留快速检测仪的用途及性能