位置: IT常识 - 正文

多智能体强化学习—QMIX(多智能体概念)

编辑:rootadmin
多智能体强化学习—QMIX 多智能体强化学习—QMIX

推荐整理分享多智能体强化学习—QMIX(多智能体概念),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:多智能体的应用,多智能体slam,多智能体vdn,多智能体vdn,多智能体的应用,多智能体强化博弈,多智能体定义,多智能体强化博弈,内容如对您有帮助,希望把文章链接给更多的朋友!

论文地址:https://arxiv.org/pdf/1803.11485.pdf

1 介绍

  首先介绍一下VDN(value decomposition networks)顾名思义,VDN是一种价值分解的网络,采用对每个智能体的值函数进行整合,得到一个联合动作值函数。 为了简单阐述考虑两个智能体:(o-observations,a-actions,Q-action-value function)   当智能体观察他自己的目标时,但不一定是队友的目标,那么有:

  当(oi,aio^i,a^ioi,ai)不足以完全建模Qˉiπ(s,a)\bar{Q}_{i}^{\pi}(\mathbf{s}, \mathbf{a})Qˉ​iπ​(s,a),利用LSTM网络的历史观测获取额外信息(t时刻看到目标A,t+5时刻目标A被挡住了,利用t+5时刻的观测数据无法获得目标A的有效信息,只有利用历史观测数据从新定位目标A) Qπ(s,a)=:Qˉ1π(s,a)+Qˉ2π(s,a)≈Q~1π(h1,a1)+Q~2π(h2,a2)Q^{\pi}(\mathbf{s}, \mathbf{a})=: \bar{Q}_{1}^{\pi}(\mathbf{s}, \mathbf{a})+\bar{Q}_{2}^{\pi}(\mathbf{s}, \mathbf{a}) \approx \tilde{Q}_{1}^{\pi}\left(h^{1}, a^{1}\right)+\tilde{Q}_{2}^{\pi}\left(h^{2}, a^{2}\right)Qπ(s,a)=:Qˉ​1π​(s,a)+Qˉ​2π​(s,a)≈Q~​1π​(h1,a1)+Q~​2π​(h2,a2)   值分解网络旨在学习一个联合动作值函数 Qtot(τ,u)Q_{t o t}(\tau,u)Qtot​(τ,u) ,其中 τ∈T≡τn\tau \in T \equiv \tau^{n}τ∈T≡τn 是一个联合动作-观测的历史轨迹,uuu是一个联合动作。它是由每个智能体 iii独立计算其值函数 Qi(τi,ui;θi)Q_{i}\left(\tau^{i}, u^{i} ; \theta^{i}\right)Qi​(τi,ui;θi),之后累加求和得到的。其关系如下所示: Qtot=∑i=1nQi(τi,ai;θi)Q_{t o t}=\sum_{i=1}^{n} Q_{i}\left(\tau_{i}, a_{i} ; \theta_{i}\right)Qtot​=i=1∑n​Qi​(τi​,ai​;θi​) 具体请看原论文:https://arxiv.org/pdf/1706.05296.pdf

  QMIX,和VDN类似,也是一种基于价值的方法,可以以集中的端到端方式训练分散策略。QMIX采用了一个网络,将联合动作值估计为每个智能体值的复杂非线性组合(VDN是线性加和),且仅基于局部观测。并且在结构上施加约束,使联合动作值函数与每个智能体动作值函数之间是单调的,保证集中策略和分散策略之间的一致性。 IGM(Individual-Global-Max):

argmax⁡uQtot(τ,u)=(argmax⁡u1Q1(τ1,u1)⋮argmax⁡unQn(τn,un))\underset{\mathbf{u}}{\operatorname{argmax}} Q_{t o t}(\tau, \mathbf{u})=\left(\begin{array}{cc} \operatorname{argmax}_{u^{1}} & Q_{1}\left(\tau^{1}, u^{1}\right) \\ \vdots \\ {\operatorname{argmax}}_{u^{n}} & Q_{n}\left(\tau^{n}, u^{n}\right) \end{array}\right)uargmax​Qtot​(τ,u)=⎝⎜⎛​argmaxu1​⋮argmaxun​​Q1​(τ1,u1)Qn​(τn,un)​⎠⎟⎞​

  其中,QtotQ_{tot}Qtot​表示联合Q函数;QiQ_iQi​表示智能体 i的动作值函数。 IGM表示argmax(Qtot)argmax (Q_{tot})argmax(Qtot​) 与argmax(Qi)argmax (Q_i)argmax(Qi​)得到相同结果,这表示在无约束条件的情况下,个体最优就代表整体最优。为了保证这一条件,QMIX以单调条件进行限制:

2 QMIX 算法框架

框架主要分三两部分:

(a)混合网络结构(红色是超网络,为蓝色的混合网络层产生权重和偏差)(b)整体的QMIX架构(c)智能体网络结构

下面进行具体分析:

2.1 Agent network多智能体强化学习—QMIX(多智能体概念)

输入:ttt时刻智能体aaa的观测值otao_t^aota​、t−1t-1t−1时刻智能体aaa的动作ut−1au_{t-1}^aut−1a​ 输出:ttt时刻智能体aaa的值函数Qa(τa,uta)Q_{a}\left(\tau^{a}, u_t^{a}\right)Qa​(τa,uta​)

  Agent network由DRQN网络实现,根据不同的任务需求,不同智能体的网络可以进行单独训练,也可进行参数共享,DRQN是将DQN中的全连接层替换为GRU网络,其循环层由一个具有64维隐藏状态的GRU组成,循环网络在观测质量变化的情况下,具有更强的适应性。如图所示,其网络一共包含 3 层,输入层(MLP多层神经网络)→ 中间层(GRU门控循环神经网络)→ 输出层(MLP多层神经网络)   实现代码如下: 智能体网络参数配置:

# --- Agent parameters ---agent: "rnn" # Default rnn agentrnn_hidden_dim: 64 # Size of hidden state for default rnn agentobs_agent_id: True # Include the agent's one_hot id in the observationobs_last_action: True # Include the agent's last action (one_hot) in the observation

RNN网络:

class RNNAgent(nn.Module): def __init__(self, input_shape, args): super(RNNAgent, self).__init__() self.args = args #根据参数配置,智能体网络的输入 #input_shape = obs_shape + n_actions + one_hot_code(one_hot_code_o+one_hot_code_u) self.fc1 = nn.Linear(input_shape, args.rnn_hidden_dim) # 线性层 self.rnn = nn.GRUCell(args.rnn_hidden_dim, args.rnn_hidden_dim) # GRU层,需要输入隐藏状态 self.fc2 = nn.Linear(args.rnn_hidden_dim, args.n_actions) # 线性层 def init_hidden(self): # make hidden states on same device as model return self.fc1.weight.new(1, self.args.rnn_hidden_dim).zero_() def forward(self, inputs, hidden_state): x = F.relu(self.fc1(inputs)) # 输入经过线性层后relu激活,输出x h_in = hidden_state.reshape(-1, self.args.rnn_hidden_dim) # 对隐藏状态进行变形,列数为隐藏层维度大小 h = self.rnn(x, h_in) # 循环神经网络,输入x,与隐藏状态(上一时刻信息) q = self.fc2(h) # 输出Q值 return q, h2.2 Mixing network

输入:ttt时刻智能体aaa的值函数Qa(τa,uta)Q_{a}\left(\tau^{a}, u_t^{a}\right)Qa​(τa,uta​)、ttt时刻全局状态sss 输出:ttt时刻联合动作价值函数Qtot(τ,u)Q_{tot}\left(\tau, u\right)Qtot​(τ,u)

  Mixing network是一个前馈神经网络,它以智能体网络的输出作为输入,单调地混合,产生QtotQ_{tot}Qtot​的值,如图所示。为了保证的单调性约束,混合网络的权值weightsweightsweights被限制为非负值(偏差bias可以为负数)。这使得混合网络可以逼近任何单调函数。   混合网络的权值是由单独的超网络产生的。每个超网络以全局状态sss作为输入,生成一层混合网络的权值。每个超网络由一个单一的线性层组成,然后是一个绝对值激活函数,确保混合网络的权值是非负的。偏差也以同样的方式产生,但偏差的生成网络没有绝对值激活函数。最终的偏差是由一个具有ReLU非线性的2层超网络产生。   实现代码如下:

mixer: "qmix"mixing_embed_dim: 32hypernet_layers: 2hypernet_embed: 64class QMixer(nn.Module): def __init__(self, args): super(QMixer, self).__init__() self.args = args self.n_agents = args.n_agents # 智能体个数 self.state_dim = int(np.prod(args.state_shape)) # 状态维度 self.embed_dim = args.mixing_embed_dim # 网络内部嵌入维度 if getattr(args, "hypernet_layers", 1) == 1: # 超网络的层数是否为1 # 不包含隐层,只有一个线性层 self.hyper_w_1 = nn.Linear(self.state_dim, self.embed_dim * self.n_agents) self.hyper_w_final = nn.Linear(self.state_dim, self.embed_dim) elif getattr(args, "hypernet_layers", 1) == 2: # 超网络的层数是否为2 hypernet_embed = self.args.hypernet_embed # 超网络的嵌入层数 # 因为生成的hyper_w1需要是一个矩阵,而pytorch神经网络只能输出一个向量, # 所以就先输出长度为需要的 矩阵行*矩阵列 的向量,然后再转化成矩阵 # hyper_w1 网络用于输出推理网络中的第一层神经元所需的 weights self.hyper_w_1 = nn.Sequential(nn.Linear(self.state_dim, hypernet_embed), nn.ReLU(), nn.Linear(hypernet_embed, self.embed_dim * self.n_agents)) # hyper_w_final 生成推理网络需要的从隐层到输出 Q 值的所有 weights,共 embed_dim 个 self.hyper_w_final = nn.Sequential(nn.Linear(self.state_dim, hypernet_embed), nn.ReLU(), nn.Linear(hypernet_embed, self.embed_dim)) elif getattr(args, "hypernet_layers", 1) > 2: # 超网络层数进行判断 raise Exception("Sorry >2 hypernet layers is not implemented!") else: raise Exception("Error setting number of hypernet layers.") # hyper_b1 生成第一层网络对应维度的偏差 bias self.hyper_b_1 = nn.Linear(self.state_dim, self.embed_dim) # V 生成对应从隐层到输出 Q 值层的 bias self.V = nn.Sequential(nn.Linear(self.state_dim, self.embed_dim), nn.ReLU(), nn.Linear(self.embed_dim, 1)) def forward(self, agent_qs, states): # 输入状态单个智能体的q值,全局状态s # states的shape为(episode_num, max_episode_len, state_shape) bs = agent_qs.size(0) # 传入的agent_qs是三维的,shape为(episode_num, max_episode_len, n_agents) states = states.reshape(-1, self.state_dim) # (episode_num * max_episode_len, state_shape) agent_qs = agent_qs.view(-1, 1, self.n_agents) # (episode_num * max_episode_len, 1, n_agents) # First layer w1 = th.abs(self.hyper_w_1(states)) # 获得参数w1,加绝对值,保证单调 b1 = self.hyper_b_1(states) # 获得偏差b1 w1 = w1.view(-1, self.n_agents, self.embed_dim) # 变换(episode_num, n_agents, embed_dim) b1 = b1.view(-1, 1, self.embed_dim) # 变换(episode_num, 1, embed_dim) hidden = F.elu(th.bmm(agent_qs, w1) + b1) # th.bmm矩阵乘法,输出到隐藏 # Second layer w_final = th.abs(self.hyper_w_final(states)) # 获得参数w2,加绝对值,保证单调 w_final = w_final.view(-1, self.embed_dim, 1) # 变换(episode_num, embed_dim, 1) # State-dependent bias v = self.V(states).view(-1, 1, 1) # 获得偏差b2(episode_num, 1, 1) # Compute final output y = th.bmm(hidden, w_final) + v # th.bmm矩阵乘法,得到最终数值 # Reshape and return q_tot = y.view(bs, -1, 1) # 变换(episode_num,1,1) return q_tot # 得到Q_tot2.3 算法更新流程

损失函数:L(θ)=∑i=1b[(yitot−Qtot(τ,u,s;θ))2]\mathcal{L}(\theta)=\sum_{i=1}^{b}\left[\left(y_{i}^{t o t}-Q_{t o t}(\tau, \mathbf{u}, s ; \theta)\right)^{2}\right]L(θ)=∑i=1b​[(yitot​−Qtot​(τ,u,s;θ))2] 其中bbb表示从经验池中采样的样本数量,ytot=r+γmax⁡u′Qtot(τ′,u′,s′;θ−)y^{t o t}=r+\gamma \max _{\mathbf{u}^{\prime}} Q_{t o t}\left(\tau^{\prime}, \mathbf{u}^{\prime}, s^{\prime} ; \theta^{-}\right)ytot=r+γmaxu′​Qtot​(τ′,u′,s′;θ−),θ−\theta^{-}θ−是目标网络的参数, 所以时序差分的误差可表示为: TDerror=(r+γQtot( target ))−Qtot( evalutate )\begin{aligned} {TDerror}=(r+\gamma Q _{ tot }(\text { target })) -Q _{ tot }(\text { evalutate }) \end{aligned}TDerror=(r+γQtot​( target ))−Qtot​( evalutate )​ Qtot( target )Q _{ tot }(\text { target })Qtot​( target ):状态s′s^{'}s′的情况下,所有行为中,获取的最大价值QtotQ_{tot}Qtot​。根据IGM条件,输入为此状态下每个智能体的最大动作价值。 Qtot( evalutate )Q _{ tot }(\text { evalutate })Qtot​( evalutate ): 状态sss的情况下,根据当前网络策略所能获得QtotQ_{tot}Qtot​。   实现代码如下: 参数配置:

# use epsilon greedy action selectoraction_selector: "epsilon_greedy"epsilon_start: 1.0epsilon_finish: 0.05epsilon_anneal_time: 50000runner: "episode"buffer_size: 5000# update the target network every {} episodestarget_update_interval: 200

动作选择:(ε-greedy)

class EpsilonGreedyActionSelector(): def __init__(self, args): self.args = args self.schedule = DecayThenFlatSchedule(args.epsilon_start, args.epsilon_finish, args.epsilon_anneal_time, decay="linear") self.epsilon = self.schedule.eval(0) def select_action(self, agent_inputs, avail_actions, t_env, test_mode=False): # Assuming agent_inputs is a batch of Q-Values for each agent bav self.epsilon = self.schedule.eval(t_env) # 获取epsilon if test_mode: # Greedy action selection only self.epsilon = 0.0 # mask actions that are excluded from selection masked_q_values = agent_inputs.clone() # q值 q_value masked_q_values[avail_actions == 0.0] = -float("inf") # should never be selected! 不能选择的动作赋值为 负无穷 random_numbers = th.rand_like(agent_inputs[:, :, 0]) # 生成相同维度的随机矩阵 pick_random = (random_numbers < self.epsilon).long() # 如果小于epsilon random_actions = Categorical(avail_actions.float()).sample().long() # 把可选的动作进行类别分布 # pick_random==1 说明 random_numbers < self.epsilon 进行随机探索 # pick_random==0 说明 random_numbers > self.epsilon 选择动作价值最大的函数 picked_actions = pick_random * random_actions + (1 - pick_random) * masked_q_values.max(dim=2)[1] # 进行动作选择 return picked_actions # 选择的动作

计算单个智能体估计的Q值

# Calculate estimated Q-Values 得到每个agent对应的Q值mac_out = []self.mac.init_hidden(batch.batch_size)for t in range(batch.max_seq_length): agent_outs = self.mac.forward(batch, t=t) #计算智能体的Q值,获得Q表 mac_out.append(agent_outs) #添加到列表中mac_out = th.stack(mac_out, dim=1) # Concat over time 沿着维度1,连接张量 ([mac_out, 1])# Pick the Q-Values for the actions taken by each agent# 取每个agent动作对应的Q值,并且把最后不需要的一维去掉,因为最后一维只有一个值了chosen_action_qvals = th.gather(mac_out[:, :-1], dim=3, index=actions).squeeze(3) # Remove the last dimx_mac_out = mac_out.clone().detach() #提取数据不带梯度x_mac_out[avail_actions == 0] = -9999999 #不能执行的动作赋值为负无穷max_action_qvals, max_action_index = x_mac_out[:, :-1].max(dim=3) #最大的动作值及其索引max_action_index = max_action_index.detach().unsqueeze(3) #去掉梯度is_max_action = (max_action_index == actions).int().float() #是最大动作

计算单个智能体目标Q值

# Calculate the Q-Values necessary for the target 计算目标Q值target_mac_out = []self.target_mac.init_hidden(batch.batch_size) #初始化隐层 RNNAgentfor t in range(batch.max_seq_length): target_agent_outs = self.target_mac.forward(batch, t=t) #计算Q值,获得Q表 target_mac_out.append(target_agent_outs)#添加到列表中# We don't need the first timesteps Q-Value estimate for calculating targetstarget_mac_out = th.stack(target_mac_out[1:], dim=1) # Concat across time# Max over target Q-Values 找到最大的动作价值if self.args.double_q: #是否使用double q # Get actions that maximise live Q (for double q-learning) mac_out_detach = mac_out.clone().detach() #去掉梯度 mac_out_detach[avail_actions == 0] = -9999999 #不能执行的动作赋值为负无穷 cur_max_actions = mac_out_detach[:, 1:].max(dim=3, keepdim=True)[1] #找到最大价值的动作 # 利用最优动作求取最大动作价值,并且把最后不需要的一维去掉 target_max_qvals = th.gather(target_mac_out, 3, cur_max_actions).squeeze(3)else: target_max_qvals = target_mac_out.max(dim=3)[0] #找到最大价值函数

根据损失函数,进行反向传播

# Mix 混合网络,求total值# qmix更新过程,evaluate网络输入的是每个agent选出来的行为的q值,target网络输入的是每个agent最大的q值,和DQN更新方式一样if self.mixer is not None: chosen_action_qvals = self.mixer(chosen_action_qvals, batch["state"][:, :-1]) # 计算Q _{ tot }(evalutate) target_max_qvals = self.target_mixer(target_max_qvals, batch["state"][:, 1:]) # 计算Q _{ tot }(target )# Calculate 1-step Q-Learning targets 以Q-Learning的方法计算目标值r+gamma*Q _{ tot }({ target }targets = rewards + self.args.gamma * (1 - terminated) * target_max_qvals# Td-errortd_error = (chosen_action_qvals - targets.detach())mask = mask.expand_as(td_error) # 将mask扩展为td_error相同的size# 0-out the targets that came from padded datamasked_td_error = td_error * mask # 抹掉填充的经验的td_error# Normal L2 loss, take mean over actual data# L2的损失函数,不能直接用mean,因为还有许多经验是没用的,所以要求和再比真实的经验数,才是真正的均值loss = (masked_td_error ** 2).sum() / mask.sum()# Optimise# 优化self.optimiser.zero_grad() # 梯度清零loss.backward() # 反向传播grad_norm = th.nn.utils.clip_grad_norm_(self.params, self.args.grad_norm_clip) # 梯度剪裁self.optimiser.step() # 执行# 在指定周期更新 target network 的参数if (episode_num - self.last_target_update_episode) / self.args.target_update_interval >= 1.0: self._update_targets() self.last_target_update_episode = episode_num3 实验效果:

IQL、VDN和QMIX在StarCraft II六种不同的战斗地图上的获胜率。基于启发式的算法的性能用虚线表示。

参考:

博客:【QMIX】一种基于Value-Based多智能体算法    多智能体强化学习入门(五)——QMIX算法分析    多智能体强化学习入门Qmix 代码:https://github.com/wjh720/QPLEX

本文链接地址:https://www.jiuchutong.com/zhishi/300706.html 转载请保留说明!

上一篇:vue最易理解且详细的调用swiper插件(vue常用)

下一篇:卷积神经网络学习—Resnet50(论文精读+pytorch代码复现)(卷积神经网络课程)

  • 小红书怎么绑手机账号(小红书上怎样绑定手机)

    小红书怎么绑手机账号(小红书上怎样绑定手机)

  • 青年大学习怎么打开

    青年大学习怎么打开

  • 支付宝芭芭农场怎么换商品(支付宝芭芭农场怎么换果树)

    支付宝芭芭农场怎么换商品(支付宝芭芭农场怎么换果树)

  • iqoo8pro怎么关闭应用推荐(iqoo8pro怎么关闭5g)

    iqoo8pro怎么关闭应用推荐(iqoo8pro怎么关闭5g)

  • vivoy70s如何设置微信视频美颜(vivoy70s如何设置下面按键图标)

    vivoy70s如何设置微信视频美颜(vivoy70s如何设置下面按键图标)

  • 淘宝虚假发货,申请退款后可投诉卖家吗(淘宝虚假发货怎么投诉,买家得到什么赔偿)

    淘宝虚假发货,申请退款后可投诉卖家吗(淘宝虚假发货怎么投诉,买家得到什么赔偿)

  • 腾讯会议能用电脑吗(腾讯会议能用电视投屏吗)

    腾讯会议能用电脑吗(腾讯会议能用电视投屏吗)

  • 苹果贴膜用力按压屏幕会坏吗(苹果手机贴膜时用劲摁伤屏吗)

    苹果贴膜用力按压屏幕会坏吗(苹果手机贴膜时用劲摁伤屏吗)

  • 坚果pro2可以插内存卡吗(坚果pro2接口)

    坚果pro2可以插内存卡吗(坚果pro2接口)

  • 华为nova6怎么取卡(华为nova6怎么取出手机卡)

    华为nova6怎么取卡(华为nova6怎么取出手机卡)

  • 华为ai人工智能是什么(华为ai智能)

    华为ai人工智能是什么(华为ai智能)

  • 爱奇艺和奇异果的区别(爱奇艺和奇异果的会员能通用吗)

    爱奇艺和奇异果的区别(爱奇艺和奇异果的会员能通用吗)

  • oppoa11x支不支持无线充电(oppoa11x支持nfc功能)

    oppoa11x支不支持无线充电(oppoa11x支持nfc功能)

  • 手机安装的软件在哪找(手机安装的软件删除了怎么找回)

    手机安装的软件在哪找(手机安装的软件删除了怎么找回)

  • lndal40是什么型号(lndtl40)

    lndal40是什么型号(lndtl40)

  • 微型计算机的工作过程是指(微型计算机的工作环境)

    微型计算机的工作过程是指(微型计算机的工作环境)

  • mate30是5g吗(华为mate30属于5g手机吗)

    mate30是5g吗(华为mate30属于5g手机吗)

  • oppoa9有呼吸灯么(oppoa9x手机的呼吸灯在哪里设置)

    oppoa9有呼吸灯么(oppoa9x手机的呼吸灯在哪里设置)

  • word落款在哪里(word落款是什么意思)

    word落款在哪里(word落款是什么意思)

  • 剪映怎么把视频和音乐对齐(剪映怎么把视频放慢)

    剪映怎么把视频和音乐对齐(剪映怎么把视频放慢)

  • 淘宝个人尺码设置在哪(淘宝个人尺码设置怎么弄)

    淘宝个人尺码设置在哪(淘宝个人尺码设置怎么弄)

  • steam必须安装在c盘吗(steam必须安装在英文目录)

    steam必须安装在c盘吗(steam必须安装在英文目录)

  • 港版iphonex怎么用电信(港版iphoneX怎么用电信卡)

    港版iphonex怎么用电信(港版iphoneX怎么用电信卡)

  • 一加总部在哪里(一加手机总部大楼)

    一加总部在哪里(一加手机总部大楼)

  • messenger.exe是什么进程 有什么用 messenger进程查询(messenger 是什么意思)

    messenger.exe是什么进程 有什么用 messenger进程查询(messenger 是什么意思)

  • 存储器详解(存储器的示意图)

    存储器详解(存储器的示意图)

  • 关于在使用Mock模拟服务端数据时获取不到请求参数的坑(关于在使用手机)

    关于在使用Mock模拟服务端数据时获取不到请求参数的坑(关于在使用手机)

  • 工资超出5000怎么样纳税
  • 会计凭证辅助项
  • 买金税盘怎么做账
  • 建行网银转账复核流程
  • 其他应收款资产负债表是负数怎么办
  • 美团代金券是抵用券吗
  • 所得税季报利润总额
  • 会计工作的基本认识
  • 稽查查补税款能缓缴
  • 车辆报废补贴多久到账
  • 公司并购股权转让协议
  • 企业固定资产没提折旧怎么办
  • 佣金可以直接转到个人账户吗
  • 工会筹备金交给谁
  • 个税申报表在哪下载打印
  • 增值税税负率是按年算吗
  • 税法关于印花税的规定
  • 金税三期得死多少企业
  • 偶然所得是什么科目
  • 企业自建自用房的规定
  • 实收资本大于注册资本是什么意思
  • 个体户不建账怎样处罚
  • 海关专用缴款书认证的步骤和说明
  • 如何用php操作mysql
  • laravel创建项目
  • 投资性房地产的主要构成内容为
  • 手把手教你使用opc
  • php源码 数据库
  • 个税系统怎么查询已申报个人明细
  • 人防车库成本能抵扣吗
  • 首涂第二十一套模板
  • 在什么情况下要切除子宫
  • aspcms标签
  • 织梦如何使用
  • 2021新旧会计准则的比较分析
  • mysql 执行动态语句
  • row number函数的使用场景
  • 专票时限是多长时间的
  • 加计抵减与加计抵消区别
  • 业务招待费可以进项抵扣吗
  • 固定资产计提完折旧残值怎么处理
  • 单位内部食堂怎么举报
  • 设备升级是什么意思
  • 企业研发费用包括工资支出吗
  • 一般纳税人只交社保无发票往来需要做账吗
  • 现金支付的范围包括
  • 劳动关系从什么时候确立
  • 从农民手里租用土地用于科研要开发票吗
  • 会计借贷帐
  • 会计计量属性是什么意思
  • mysql连接是什么协议
  • sqlserver 查看表
  • Linux下mysql 5.6.17安装图文教程详细版
  • 无法启动diagnostic policy service服务
  • w10预览版新功能
  • 电脑爆音卡死
  • linux中df命令详解
  • win7安装sqlserver2005失败
  • win8.1 升级
  • macbook os x
  • sointgr.exe - sointgr是什么进程 有什么用
  • spybuddy.exe - spybuddy是什么进程
  • win8显示屏亮度调节
  • 订书针的原理
  • 消除类策略游戏
  • perl 批量注释
  • cocoscreator lua
  • lua教程书籍
  • unity de
  • android系统架构由几部分组成
  • js拖拽元素到另一个元素
  • 开发流程和步骤
  • Javascript Boolean、Nnumber、String 强制类型转换的区别详细介绍
  • Android屏幕适配分屏
  • 用yum安装samba
  • javascript产生随机整数随机性强
  • flask框架图
  • 运输专票怎么开发票
  • 汽车车船税怎么交
  • 天津市网签查询
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设