强化学习已经成为大家关注的点,至少据我了解世界顶级名校CV的实验室都在做这方面的工作。最近也在做相关的Research,正好遇到了在Torch中的RL实现,发现没有什么可以参考的中文资料,只能试着来解释一下Torch中的RL实现。
在Torch中的RL【2】实现参考的是论文【1】的方法实现。实现的也是比较简单的immediate RL。在【1】中提出了immediate RL,也是类似于associative reward-inaction AR-I的方法。
假设输出是一个向量,那么第i和输出的计算如下:
首先是为了计算出 si :
再用mass function对 si 进行处理得到 pi :
这里的 fi 可以使用logistic function计算:
假设输出符合伯努利分布,那么对于输出为0或者1的概率分别为:
首先是 αij 可以是一个常数,也可以是:
对于 eij 的计算首先是表示mass probability function:
然后可以进行求导表示为如下方式:(有点儿cross-entropy的感觉)
然后 pi 对 si 进行求导:
然后 si 对 wij 进行求导:
把reward, α 加上就是最终的梯度:(baseline reward为0的时候)
和一个叫做associative reward-inaction对比:(取 λ 为0即可)
然后在推出来的导数中加入baseline reward,就可以得到:
这里有一个estimate reward:通过以下公式得到。
Torch的代码实现在这里:【2】,是集合了目前Torch有实现的RL方法。是根据【1】实现的。这次要讲的是ReinforceCategorical【3】这部分的代码。
module = nn.ReinforceCategorical([stochastic])
这个类是继承了nn.Reinforce,实现的是针对一个sample的Multinomial distribution的分类。输入时multinomial distribution,比如从Softmax的输出获取的就是一个multinomial distribution(概率和为1),得到的输出就是一个one-hot coding,也就是输出的vector只有一个为1,其余为0,这样的结构用于做分类再合适不过了。在训练的时候,输入可以是batch size,也就是可以进行batch gradient training。
在进行evaluation的时候,若stochastic=false,那么输入等于输出。其实nn.ReinforceCategorical继承于nn.Reinforce,主要定义了updateGradInput和updateOutput这两个函数。
函数中有如下变量:
f:对于输入的概率进行另外一种变化的函数,比如sigmoid。
y:对某个sample的index表示,采用one-hot code表示。
p:输入的概率分布,(p[1],p[2],p[3]...)
计算对weight的e(ij)进行更新的公式的时候采用如下:
d ln(f(y,p)) 1/p[i] if i = y
------------ =
d p 0 otherwise
ReinforceCategorical.lua代码解释:
local ReinforceCategorical, parent = torch.class("nn.ReinforceCategorical", "nn.Reinforce")
-- 根据输入input的概率分布,进行一次采样,把采样的index的输出设置为1,其余为0
function ReinforceCategorical:updateOutput(input)
self.output:resizeAs(input)
self._index = self._index or ((torch.type(input) == 'torch.CudaTensor') and torch.CudaTensor() or torch.LongTensor())
if self.stochastic or self.train ~= false then
-- sample from categorical with p = input
self._input = self._input or input.new()
-- prevent division by zero error (see updateGradInput)
self._input:resizeAs(input):copy(input):add(0.00000001)
input.multinomial(self._index, input, 1)
-- one hot encoding
self.output:zero()
self.output:scatter(2, self._index, 1)
else
-- use p for evaluation
self.output:copy(input)
end
return self.output
end
-- 计算需要进行更新的weight的梯度,self.gradInput
-- 先是初始化为上面函数的one-hot uotput,然后除以input,为了防止除0错误,加了一个小数。然后乘以reward,乘以-1。
function ReinforceCategorical:updateGradInput(input, gradOutput)
-- Note that gradOutput is ignored
-- f : categorical probability mass function
-- x : the sampled indices (one per sample) (self.output)
-- p : probability vector (p[1], p[2], ..., p[k])
-- derivative of log categorical w.r.t. p
-- d ln(f(x,p)) 1/p[i] if i = x
-- ------------ =
-- d p 0 otherwise
self.gradInput:resizeAs(input):zero()
self.gradInput:copy(self.output)
self._input = self._input or input.new()
-- prevent division by zero error
self._input:resizeAs(input):copy(input):add(0.00000001)
self.gradInput:cdiv(self._input)
-- multiply by reward
self.gradInput:cmul(self:rewardAs(input))
-- multiply by -1 ( gradient descent on input )
self.gradInput:mul(-1)
return self.gradInput
end
-- 设置parent类型并且把self._index置为nil
function ReinforceCategorical:type(type, tc)
self._index = nil
return parent.type(self, type, tc)
end
所以可以看出ReinforceCategorical计算的是用于训练时的output以及具体的gradient,另外的函数也就是在mass function上以及表示输出output的方式不同。还有类似的函数有: ReinforceBernoulli,ReinforceNormal,ReinforceGamma。
在代码里面有scatter【5】和multinomial【6】这两个函数,其实一眼看去这个意思是很明确,scatter是把一个数组的值或者是value填到一个Tensor里面。multinomial是根据给出的概率分布,进行采样。比如给出[0.1,0.2,0.7],然后就会进行采用,返回的是index,比如采集5次,可能会得到3,3,3,1,3这样的序列。使用multinomial的时候要主要,不要让分布为0,否则报错。为0的时候无法进行采样。
在scatter中,接口如下:
[Tensor] scatter(dim, index, src|val)
例子如下:
x = torch.rand(2, 5)
> x
0.3227 0.4294 0.8476 0.9414 0.1159
0.7338 0.5185 0.2947 0.0578 0.1273
[torch.DoubleTensor of size 2x5]
y = torch.zeros(3, 5):scatter(1, torch.LongTensor{{1, 2, 3, 1, 1}, {3, 1, 1, 2, 3}}, x)
> y
0.3227 0.5185 0.2947 0.9414 0.1159
0.0000 0.4294 0.0000 0.0578 0.0000
0.7338 0.0000 0.8476 0.0000 0.1273
[torch.DoubleTensor of size 3x5]
填充的时候,首先看第一个参数为1,那么说明是以第一维(列)为单位的。那么第一列,填充的位置分别是{1,3},那么x[1,1]和x[2,1]分别放在y[1,1],y[3,1]。到了第二列就是{2,1},那么x[1,2],x[2,2]分别放在y[2,2],y[1,2]。依次类推。
z = torch.zeros(2, 4):scatter(2, torch.LongTensor{{3}, {4}}, 1.23)
> z
0.0000 0.0000 1.2300 0.0000
0.0000 0.0000 0.0000 1.2300
[torch.DoubleTensor of size 2x4]
填充的时候,因为是根据第二维(行)的进行填充,那么填的第一元素就是z[1,3],另外一个元素就在z[2,4]
这是简单的RL实现,由论文【10】通过一些改变得到,基本上是一样的,除了多了别的mass function以外。
module = nn.Reinforce([stochastic])
stochastic=flase的时候只在训练的时候进行stochastic(不确定的,概率),evaluation的时候不需要。默认为false。计算reward的方式和VRClassReward【7】类似。
reward = a*(R - b)
a在论文中是一个常数或者是用某个计算式计算(看上面的论文介绍)。R就是(通常 0 或者 1), b是基准reward(baseline reward), 是对R的预测,用到了上一个时刻的reward以及上一个时刻的预测,看上面的论文解释。
------------------------------------------------------------------------
--[[ Reinforce ]]--
-- Ref A. http://incompleteideas.net/sutton/williams-92.pdf
-- Abstract class for modules that use the REINFORCE algorithm (ref A).
-- The reinforce(reward) method is called by a special Reward Criterion.
-- After which, when backward is called, the reward will be used to
-- generate gradInputs. The gradOutput is usually ignored.
------------------------------------------------------------------------
local Reinforce, parent = torch.class("nn.Reinforce", "nn.Module")
function Reinforce:__init(stochastic)
parent.__init(self)
-- true makes it stochastic during evaluation and training
-- false makes it stochastic only during training
self.stochastic = stochastic
end
-- 这个reward来自于parent的reinforce计算reward,具体计算在VRClassReward里面【11】。
-- a Reward Criterion will call this
function Reinforce:reinforce(reward)
parent.reinforce(self, reward)
self.reward = reward
end
-- 在子类中实现
function Reinforce:updateOutput(input)
self.output:set(input)
end
-- 在子类中实现,被子类调用用来计算梯度
function Reinforce:updateGradInput(input, gradOutput)
local reward = self:rewardAs(input)
self.gradInput:resizeAs(reward):copy(reward)
end
-- 计算的是input的reward,在这里需要注意的是训练的时候可能是batch traning,所以要考虑input size的问题(这一部分不能确定,还需要在研究一下)
-- this can be called by updateGradInput
function Reinforce:rewardAs(input)
assert(self.reward:dim() == 1)
if input:isSameSizeAs(self.reward) then
return self.reward
else
if self.reward:size(1) ~= input:size(1) then
-- assume input is in online-mode
input = self:toBatch(input, input:dim())
assert(self.reward:size(1) == input:size(1), self.reward:size(1).." ~= "..input:size(1))
end
self._reward = self._reward or self.reward.new()
self.__reward = self.__reward or self.reward.new()
local size = input:size():fill(1):totable()
size[1] = self.reward:size(1)
self._reward:view(self.reward, table.unpack(size))
self.__reward:expandAs(self._reward, input)
return self.__reward
end
end
另外,对于reward函数,nn中也有更加专业的模块:【7】。如果想要了解完整的强化学习的内容,可以看这个example【8】,来源于Google Attention的论文复现。我目前看到整个网络远比这个大得多,等有空再把【8】介绍一下。
在RL里面,经常用到ArgMax.lua,用来计算每一维的最大输出【12】。nn.Collapse对Tensor大小进行改变【13】。
【1】RL 论文: http://www-anw.cs.umass.edu/~barto/courses/cs687/williams92simple.pdf
【2】Torch中的RL代码: https://github.com/nicholas-leonard/dpnn#nn.ReinforceCategorical
【3】Torch中的RL ReinforceCategorical代码: https://github.com/Element-Research/dpnn/blob/master/ReinforceCategorical.lua
【4】Torch中别的RL ReinforceCategorical实现: https://github.com/shubhtuls/volumetricPrimitives/blob/master/modules/ReinforceCategorical.lua
【5】Torch scatter: https://github.com/torch/torch7/blob/master/doc/tensor.md
【6】Torch Multinomial: https://github.com/torch/torch7/blob/master/doc/maths.md
【7】VRClassReward: https://github.com/nicholas-leonard/dpnn#nn.Module.reinforce
【8】RL Example: https://github.com/nicholas-leonard/dpnn/blob/master/Reinforce.lua
【9】Visual Attention: http://papers.nips.cc/paper/5542-recurrent-models-of-visual-attention.pdf
【10】RL class code: https://github.com/nicholas-leonard/dpnn/blob/master/Reinforce.lua
【11】VRClass Reward: https://github.com/nicholas-leonard/dpnn/blob/master/VRClassReward.lua
【12】nn.ArgMax: https://github.com/Element-Research/dpnn/blob/master/ArgMax.lua
【13】nn.Collapse: https://github.com/torch/nn/blob/master/Collapse.lua
转载请注明出处:
http://blog.csdn.net/c602273091/article/details/78966962