为了深入学习各种深度学习网络和强化学习的结合,实现了一下下列文章:
状态、动作、奖励函数及实验的简单介绍可参考:
基于深度强化学习的自适应作业车间调度问题研究_松间沙路的博客-CSDN博客_强化学习调度
整体代码复现可见个人Github:Aihong-Sun/DQN-DDQN-Dueling_networ-D3QN-_for_JSP: pytorch implementation of DQN/DDQN/Dueling_networ/D3QN for job shop scheudling problem (github.com)
1 状态特征提取
首先从特征提取开始,原文的状态特征为3个网格矩阵,如下:
于是搭建CNN进行特征提取:
不太了解CNN的可以参考:卷积神经网络(CNN)详解 - 知乎 (zhihu.com)
self.conv1=nn.Sequential(
nn.Conv2d(
in_channels=3,
out_channels=6,
kernel_size=3,
stride=1,
padding=1,
), # output shape (3,J_num,O_max_len)
nn.ReLU(),
nn.MaxPool2d(kernel_size=2,ceil_mode=False)
)
1.1 卷积层
上诉状态可看作是长、宽为工件数、工件最大工序数(若不相等时取最大,但原文涉及的算例都为工序数相等),深度为3的一个图像,于是in_channels=3,out_channels可按自己的需求进行设计,这里我设为6,即有6个卷积核,输出的图像的深度就为6,为保证图片的长宽不变(便于后面针对不同工件数、工序数的全连接层的计算),用0在图像边缘处进行填充,计算方法如下:
假设Jnum=6,O_max_len=6,于是,令卷积核kernel_size=3,stride=1,padding=1,此时,W2=(6-3+2)/1+1=6,H2=(6-3+2)/1+1=6,于是图片大小没变。
1.2 池化层
它的作用是用来逐渐降低数据体的空间尺寸,这样的话就能减少网络中参数的数量,使得计算资源耗费变少,也能有效控制过拟合。
池化层使用 MAX 操作,对输入数据体的每一个深度切片独立进行操作,改变它的空间尺寸。最常见的形式是汇聚层使用尺寸2x2的滤波器,以步长为2来对每个深度切片进行降采样。
这里设置kernel_size=2,即对图像缩小一般,ceil_mode=False,即针对工件为奇数的情况,比如Jnum=7,Omax_len=7,图像缩小边缘的数则不取,于是生成新的图像大小为(3,3),若ceil_mode=False,生成新的图像大小则为(4,4).
2 动作
动作为17条规则,具体可见上诉给出的个人Github
3DQN/DDQN/Dueling Network/D3QN
3.1 DQN与DDQN
下面第一个式子为DQN的目标函数,第二个式子为DDQN的目标函数:
DDQN与DQN大部分都相同,只有一步不同,那就是在选择Q(s_{t+1},a_{t+1})的过程中,DQN总是选择Target Q网络的最大输出值。而DDQN不同,DDQN首先从Q网络中找到最大输出值的那个动作,然后再找到这个动作对应的Target Q网络的输出值。这么做的原因是传统的DQN通常会高估Q值得大小,两者代码差别如下:
q_eval = self.eval_net(batch_state).gather(1, batch_action)
q_next = self.target_net(batch_next_state).detach()
if self.double: #ddqn
q_next_eval=self.eval_net( batch_next_state).detach()
q_a=q_next_eval.argmax(dim=1)
q_a=torch.reshape(q_a,(-1,len(q_a)))
q_target = batch_reward + self.GAMMA * q_next.gather(1, q_a)
else: #dqn
q_target = batch_reward + self.GAMMA * q_next.max(1)[0]
3.2 DQN 与Dueling Network
Dueling network 是一篇来自2015年的论文,这篇论文提出了一个新的网络架构,这个架构不但提高了最终效果,而且还可以和其他的算法相结合以获取更加优异的表现。
之前的DQN网络在将图片卷积获取特征之后会输入几个全连接层,经过训练直接输出在该state下各个action的价值也就是Q(s,a)。而Dueling network则不同,它在卷积网络之后引出了两个不同的分支,一个分支用于预测state的价值,另一个用于预测每个action的优势。最后将这两个分支的结果合并输出Q(s,a),两者的网络结构如下(上为DQN,下为Dueling network)
代码上的区别:
DQN:
class DQN(nn.Module):
"""docstring for Net"""
def __init__(self,J_num,O_max_len):
super(CNN_FNN, self).__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(
in_channels=3, # input shape (3,J_num,O_max_len)
out_channels=6,
kernel_size=3,
stride=1,
padding=1, # 使得出来的图片大小不变P=(3-1)/2,
), # output shape (3,J_num,O_max_len)
nn.ReLU(),
# nn.MaxPool2d(kernel_size=2, ceil_mode=True) # output shape: (6,int(J_num/2),int(O_max_len/2))
)
# summary(self.conv1,(3,6,6))
self.fc1 &