强化学习pytorch

时间: 2023-08-01 08:13:51 浏览: 30
回答: 强化学习中使用Pytorch作为框架是一个不错的选择。Pytorch是一个基于Python的深度学习框架,它提供了丰富的工具和库来支持强化学习算法的实现。Pytorch的优势在于其简洁的API和易于使用的动态计算图,使得模型的构建和调试更加方便。此外,Pytorch还提供了丰富的预训练模型和数据处理工具,可以帮助快速搭建强化学习模型。因此,如果你对Pytorch已经有一定的了解并且喜欢它的特点,那么选择Pytorch作为强化学习框架是一个不错的选择。\[1\]\[3\] #### 引用[.reference_title] - *1* *2* [深度强化学习-Pytorch环境配置](https://blog.csdn.net/weixin_46133643/article/details/122657023)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [使用Pytorch实现强化学习——DQN算法](https://blog.csdn.net/Er_Studying_Bai/article/details/128462002)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

相关推荐

PyTorch是一个流行的深度学习框架,它提供了丰富的工具和库来支持强化学习任务。在PyTorch中进行强化学习,首先需要定义一个环境,然后使用神经网络模型作为智能体来与环境进行交互,并根据交互结果更新模型的参数。 以下是一个简单的PyTorch强化学习的示例代码: python import torch import torch.optim as optim import torch.nn as nn import gym # 定义神经网络模型 class QNetwork(nn.Module): def __init__(self, state_size, action_size): super(QNetwork, self).__init__() self.fc1 = nn.Linear(state_size, 64) self.fc2 = nn.Linear(64, 64) self.fc3 = nn.Linear(64, action_size) def forward(self, x): x = torch.relu(self.fc1(x)) x = torch.relu(self.fc2(x)) x = self.fc3(x) return x # 定义强化学习算法 def reinforce(env, model, optimizer, gamma): state = env.reset() done = False while not done: state_tensor = torch.tensor(state, dtype=torch.float32) q_values = model(state_tensor) action = q_values.argmax().item() # 根据最大Q值选择动作 next_state, reward, done, _ = env.step(action) next_state_tensor = torch.tensor(next_state, dtype=torch.float32) next_q_values = model(next_state_tensor) max_next_q_value = next_q_values.max().item() target_q = reward + gamma * max_next_q_value # 奖励与下一个状态的最大Q值的乘积 q = q_values[action] loss = nn.MSELoss()(q, target_q) # 使用均方误差作为损失函数 optimizer.zero_grad() loss.backward() optimizer.step() state = next_state # 创建环境和模型 env = gym.make('CartPole-v1') state_size = env.observation_space.shape[0] action_size = env.action_space.n model = QNetwork(state_size, action_size) # 定义优化器和训练参数 optimizer = optim.Adam(model.parameters(), lr=0.001) gamma = 0.99 # 训练模型 for episode in range(100): reinforce(env, model, optimizer, gamma) # 在环境中测试模型 state = env.reset() done = False while not done: state_tensor = torch.tensor(state, dtype=torch.float32) q_values = model(state_tensor) action = q_values.argmax().item() next_state, _, done, _ = env.step(action) state = next_state env.close() 请注意,这只是一个简单的示例代码,实际的强化学习应用可能需要更复杂的算法和技术。强化学习是一个非常广阔且活跃的研究领域,如果你有特定的问题或任务,请提供更多的细节,我将尽力提供更准确和详细的指导。
PyTorch是一个开源的深度学习框架,也可以用于强化学习任务的实现。以下是关于PyTorch强化学习的教程: PyTorch提供了一些用于强化学习的工具和库,例如PyTorch的神经网络模块nn和优化器optim。在开始之前,先要了解强化学习的基本概念,特别是强化学习中的环境、状态、动作和奖励。 首先,我们需要定义强化学习任务的环境。这可以是一个简单的游戏,如迷宫,也可以是一个复杂的环境,如自动驾驶汽车的模拟器。接下来,我们需要定义状态空间和动作空间。状态空间表示环境可能的状态,动作空间表示智能体可以采取的动作。然后,我们需要定义奖励函数,即智能体在每个动作后获得的奖励。 接下来,可以使用PyTorch的神经网络模块nn来定义强化学习的智能体。可以选择不同的神经网络架构,如深度Q网络(DQN)或策略梯度方法。网络的输入是状态,输出是每个动作的Q值或概率。在这个教程中,我们将以DQN为例。 在训练过程中,智能体与环境进行交互。它从当前状态观察到环境,根据当前策略选择一个动作,并将其应用于环境。然后,智能体观察到下一个状态和对应的奖励。通过这种方式,我们可以逐步收集经验和样本。使用这些样本,我们可以计算损失函数,并使用优化器optim来更新神经网络的参数。 接下来,我们使用PyTorch的强化学习库RLlib执行训练过程。RLlib提供了一种方便的方式来管理整个强化学习训练过程的迭代和评估。通过调整训练过程中的参数和神经网络架构,我们可以改进智能体的性能。 总结来说,PyTorch提供了一个强大的深度学习框架,可以用于强化学习任务的实现。通过定义环境、状态空间、动作空间和奖励函数,以及使用PyTorch的nn模块和optim优化器来构建和训练强化学习的神经网络模型,我们可以实现一个高效的强化学习系统。同时,RLlib库提供了方便的工具来迭代和评估训练过程。希望这个教程能够帮助您更好地理解和应用PyTorch强化学习。
### 回答1: 强化学习是一种通过试错方法来学习最佳决策策略的机器学习方法。在路径规划问题中,强化学习可以用于学习如何在具有不同障碍物和地形的环境中找到最短路径,而不是依赖于预定路径或手动规划。 Pytorch是一种深度学习框架,其简便性和灵活性使其成为各种应用程序的首选。它提供了构建神经网络和处理数据的高级工具,使得强化学习算法可以快速构建和训练。Pytorch的自动梯度计算和动态计算图特性也使其成为一个非常适合路径规划问题的框架。 在强化学习路径规划中,Pytorch可以使用深度学习网络来表示路径规划问题中的策略函数。这个策略函数将环境状态映射到动作选择,使智能体能够通过最小化走过的路径和尽可能避免碰撞来完成任务。此外,强化学习中的价值函数或效用函数也可以使用Pytorch快速构建和训练。该函数可以评估不同策略效果,指导策略学习。 实际上,Pytorch提供的交互式调试工具和数据可视化工具可以有效地帮助开发人员调试和评估基于强化学习的路径规划系统。通过使用Pytorch,可以快速开发高效、精确的路径规划算法,使智能体在虚拟或现实环境中快速且准确地实现任务。 ### 回答2: 强化学习是一种机器学习方法,它通过观察和与环境互动来寻找最优解决方案的过程。例如,在路径规划问题中,强化学习能够学习如何在未知环境中找到最优路径,使得规划的策略能够在错误的情况下自我修正。 而Pytorch是一个Python开源的深度学习框架。它提供了高效的Tensor操作,以及自动求导和动态图功能。Pytorch为强化学习提供了一个灵活的框架,用于构建和测试强化学习算法。 在路径规划问题中,我们可以使用Pytorch构建深度强化学习模型。该模型将观察到的环境状态输入到网络中,网络输出行动的概率。然后,使用强化学习的方法(例如Q-learning),学习网络参数,以便网络能够产生最佳的行动策略。 在这种方法中,运用了神经网络的高级表达能力和PyTorch的高效计算能力,所以该模型可以具有较高的准确性和泛化能力。但是,由于深度强化学习需要大量的数据和计算资源,需要注意模型的训练和测试过程可能会很耗时。 因此,要进行有效的路径规划,需要仔细选择模型,并配置合适的参数和超参数。同时,还需要注意数据集的选择和预处理,以及对模型进行评估和调优。 ### 回答3: 强化学习是一种机器学习方法,它让智能体通过在环境中进行试错来学习怎样做出最优决策。在路径规划中,强化学习可以让机器人或其他自主系统学会在环境中有效地寻找到目标位置。这个方法可以用来解决很多实际问题,如机器人运动控制和自动驾驶。 PyTorch是一个流行的机器学习框架,它可以用来构建神经网络和实现强化学习算法。它的优点在于它的动态图机制,该机制能够让用户进行快速迭代和实验。对于路径规划,PyTorch可以用来训练神经网络以预测最佳动作,从而找到最短的路径。 在实践中,强化学习路径规划需要解决一个关键问题,即“回报函数”的定义。这个函数描述了智能体所获得的奖励,以及在每个时间步长给智能体的特定动作。智能体通过将优化回报函数来学习最佳行动。 总之,强化学习和PyTorch的结合使得路径规划变得更加高效和准确,这样人工智能的应用将更加广泛。
这里是一个简单的强化学习预测的PyTorch代码,使用的是Q-learning算法: python import torch import torch.nn as nn import torch.optim as optim import numpy as np # 定义一个简单的神经网络,输入状态,输出每个动作的Q值 class Net(nn.Module): def __init__(self, state_dim, action_dim): super(Net, self).__init__() self.fc1 = nn.Linear(state_dim, 64) self.fc2 = nn.Linear(64, 64) self.fc3 = nn.Linear(64, action_dim) def forward(self, x): x = torch.relu(self.fc1(x)) x = torch.relu(self.fc2(x)) x = self.fc3(x) return x class QLearning: def __init__(self, state_dim, action_dim, gamma=0.99, lr=0.001, epsilon=0.1): self.state_dim = state_dim self.action_dim = action_dim self.gamma = gamma self.epsilon = epsilon self.net = Net(state_dim, action_dim) self.optimizer = optim.Adam(self.net.parameters(), lr=lr) def get_action(self, state): # 以epsilon的概率随机选择一个动作,否则选择Q值最大的动作 if np.random.random() < self.epsilon: return np.random.randint(self.action_dim) else: with torch.no_grad(): q_values = self.net(torch.tensor(state).float()) return q_values.argmax().item() def update(self, state, action, reward, next_state, done): # 计算Q值 q_values = self.net(torch.tensor(state).float()) q_value = q_values[action] with torch.no_grad(): next_q_values = self.net(torch.tensor(next_state).float()) next_q_value = next_q_values.max().item() target_q_value = reward + (1 - done) * self.gamma * next_q_value # 计算损失并更新网络 loss = (q_value - target_q_value) ** 2 self.optimizer.zero_grad() loss.backward() self.optimizer.step() 其中,Net是一个简单的神经网络,QLearning是一个使用Q-learning算法的强化学习类,包括了网络的定义、动作选择和网络更新等方法。在使用时,可以根据具体情况设置状态维度、动作维度、学习率、折扣率等参数,并调用get_action方法选择动作,调用update方法更新网络。
DQN(Deep Q-Network)是一种使用深度神经网络实现的强化学习算法,用于解决离散动作空间的问题。在PyTorch中实现DQN可以分为以下几个步骤: 1. 定义神经网络:使用PyTorch定义一个包含多个全连接层的神经网络,输入为状态空间的维度,输出为动作空间的维度。 python import torch.nn as nn import torch.nn.functional as F class QNet(nn.Module): def __init__(self, state_dim, action_dim): super(QNet, self).__init__() self.fc1 = nn.Linear(state_dim, 64) self.fc2 = nn.Linear(64, 64) self.fc3 = nn.Linear(64, action_dim) def forward(self, x): x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x 2. 定义经验回放缓存:包含多条经验,每条经验包含一个状态、一个动作、一个奖励和下一个状态。 python import random class ReplayBuffer(object): def __init__(self, max_size): self.buffer = [] self.max_size = max_size def push(self, state, action, reward, next_state): if len(self.buffer) < self.max_size: self.buffer.append((state, action, reward, next_state)) else: self.buffer.pop(0) self.buffer.append((state, action, reward, next_state)) def sample(self, batch_size): state, action, reward, next_state = zip(*random.sample(self.buffer, batch_size)) return torch.stack(state), torch.tensor(action), torch.tensor(reward), torch.stack(next_state) 3. 定义DQN算法:使用PyTorch定义DQN算法,包含训练和预测两个方法。 python class DQN(object): def __init__(self, state_dim, action_dim, gamma, epsilon, lr): self.qnet = QNet(state_dim, action_dim) self.target_qnet = QNet(state_dim, action_dim) self.gamma = gamma self.epsilon = epsilon self.lr = lr self.optimizer = torch.optim.Adam(self.qnet.parameters(), lr=self.lr) self.buffer = ReplayBuffer(100000) self.loss_fn = nn.MSELoss() def act(self, state): if random.random() < self.epsilon: return random.randint(0, action_dim - 1) else: with torch.no_grad(): q_values = self.qnet(state) return q_values.argmax().item() def train(self, batch_size): state, action, reward, next_state = self.buffer.sample(batch_size) q_values = self.qnet(state).gather(1, action.unsqueeze(1)).squeeze(1) target_q_values = self.target_qnet(next_state).max(1)[0].detach() expected_q_values = reward + self.gamma * target_q_values loss = self.loss_fn(q_values, expected_q_values) self.optimizer.zero_grad() loss.backward() self.optimizer.step() def update_target_qnet(self): self.target_qnet.load_state_dict(self.qnet.state_dict()) 4. 训练模型:使用DQN算法进行训练,并更新目标Q网络。 python dqn = DQN(state_dim, action_dim, gamma=0.99, epsilon=1.0, lr=0.001) for episode in range(num_episodes): state = env.reset() total_reward = 0 for step in range(max_steps): action = dqn.act(torch.tensor(state, dtype=torch.float32)) next_state, reward, done, _ = env.step(action) dqn.buffer.push(torch.tensor(state, dtype=torch.float32), action, reward, torch.tensor(next_state, dtype=torch.float32)) state = next_state total_reward += reward if len(dqn.buffer.buffer) > batch_size: dqn.train(batch_size) if step % target_update == 0: dqn.update_target_qnet() if done: break dqn.epsilon = max(0.01, dqn.epsilon * 0.995) 5. 测试模型:使用训练好的模型进行测试。 python total_reward = 0 state = env.reset() while True: action = dqn.act(torch.tensor(state, dtype=torch.float32)) next_state, reward, done, _ = env.step(action) state = next_state total_reward += reward if done: break print("Total reward: {}".format(total_reward)) 以上就是在PyTorch中实现DQN强化学习的基本步骤。需要注意的是,DQN算法中还有很多细节和超参数需要调整,具体实现过程需要根据具体问题进行调整。
在PyTorch中使用强化学习通常需要以下步骤: 1. 安装PyTorch:访问PyTorch官网,选择适合你的操作系统的PyTorch版本,并按照说明进行安装。 2. 定义环境:定义你的强化学习环境,包括状态空间、动作空间、奖励函数等。 3. 定义智能体:定义你的强化学习智能体,包括神经网络模型、策略、价值函数等。 4. 定义训练过程:定义你的强化学习训练过程,包括采样策略、优化器、损失函数等。 5. 进行训练:在定义好环境、智能体和训练过程后,使用强化学习算法进行模型训练。 以下是一个简单的强化学习示例: python import torch import torch.nn as nn import torch.optim as optim import gym # 定义环境 env = gym.make('CartPole-v0') state_space = env.observation_space.shape[0] action_space = env.action_space.n # 定义智能体 class Agent(nn.Module): def __init__(self): super(Agent, self).__init__() self.fc1 = nn.Linear(state_space, 128) self.fc2 = nn.Linear(128, action_space) def forward(self, x): x = torch.relu(self.fc1(x)) x = self.fc2(x) return x # 定义训练过程 agent = Agent() optimizer = optim.Adam(agent.parameters(), lr=0.01) def train(): state = env.reset() done = False while not done: state_tensor = torch.FloatTensor(state) action_probs = agent(state_tensor) action = torch.argmax(action_probs).item() next_state, reward, done, _ = env.step(action) next_state_tensor = torch.FloatTensor(next_state) if done: target = torch.FloatTensor([reward]) else: target = reward + gamma * torch.max(agent(next_state_tensor)) loss = nn.MSELoss()(target, agent(state_tensor)[action]) optimizer.zero_grad() loss.backward() optimizer.step() state = next_state # 进行训练 gamma = 0.99 for i in range(100): train() 在这个示例中,我们使用PyTorch定义了一个智能体,它是一个具有两个全连接层的神经网络。我们使用Adam优化器和均方误差损失函数对模型进行训练,并使用CartPole-v0环境进行测试。您可以根据需要进行修改,例如使用其他环境或强化学习算法。
PyTorch是一个非常流行的深度学习框架,可以用于实现强化学习算法,其中包括Mountain Car Continuous问题。 Mountain Car Continuous是一个经典的强化学习环境,目标是让小车从低谷处驶上山顶。与离散版的Mountain Car不同,连续版本的Mountain Car允许小车进行连续动作,即小车可以在[-1, 1]的范围内选择连续的加速度值。 为了使用PyTorch实现Mountain Car Continuous,首先需要定义一个神经网络模型来近似值函数。可以使用多层感知器(MLP)作为模型结构,其中包括输入层、多个隐藏层和输出层。输入可以是小车的位置和速度,输出是预测的动作加速度。 接下来,可以使用PyTorch提供的优化器(如Adam)来最小化动作的损失函数。损失函数可以使用均方误差(MSE)或其他合适的损失函数来计算预测动作与真实动作之间的差异。 在训练过程中,可以使用强化学习算法中的策略梯度方法(如Proximal Policy Optimization,PPO)来更新模型的参数。PPO算法可以通过最大化累积奖励函数的期望值来优化策略。 为了收集经验数据,可以使用环境模拟器进行交互,通过采样不同的动作和观察奖励反馈来构建经验回放缓冲区。然后,可以从缓冲区中随机采样一批经验数据,用于训练神经网络模型。 最后,在训练过程中,可以使用一些技巧来提高算法的性能,例如引入逐步增加动作幅度的探索策略,或者使用进化策略来进一步优化策略。 总而言之,通过使用PyTorch来实现Mountain Car Continuous可以利用PyTorch的灵活性和高效性,以及其提供的优化器和神经网络模型来构建和训练一个强化学习代理,使其能够成功驾驶小车从低谷处上山顶。

最新推荐

安全文明监理实施细则_工程施工土建监理资料建筑监理工作规划方案报告_监理实施细则.ppt

安全文明监理实施细则_工程施工土建监理资料建筑监理工作规划方案报告_监理实施细则.ppt

"REGISTOR:SSD内部非结构化数据处理平台"

REGISTOR:SSD存储裴舒怡,杨静,杨青,罗德岛大学,深圳市大普微电子有限公司。公司本文介绍了一个用于在存储器内部进行规则表达的平台REGISTOR。Registor的主要思想是在存储大型数据集的存储中加速正则表达式(regex)搜索,消除I/O瓶颈问题。在闪存SSD内部设计并增强了一个用于regex搜索的特殊硬件引擎,该引擎在从NAND闪存到主机的数据传输期间动态处理数据为了使regex搜索的速度与现代SSD的内部总线速度相匹配,在Registor硬件中设计了一种深度流水线结构,该结构由文件语义提取器、匹配候选查找器、regex匹配单元(REMU)和结果组织器组成。此外,流水线的每个阶段使得可能使用最大等位性。为了使Registor易于被高级应用程序使用,我们在Linux中开发了一组API和库,允许Registor通过有效地将单独的数据块重组为文件来处理SSD中的文件Registor的工作原

typeerror: invalid argument(s) 'encoding' sent to create_engine(), using con

这个错误通常是由于使用了错误的参数或参数格式引起的。create_engine() 方法需要连接数据库时使用的参数,例如数据库类型、用户名、密码、主机等。 请检查你的代码,确保传递给 create_engine() 方法的参数是正确的,并且符合参数的格式要求。例如,如果你正在使用 MySQL 数据库,你需要传递正确的数据库类型、主机名、端口号、用户名、密码和数据库名称。以下是一个示例: ``` from sqlalchemy import create_engine engine = create_engine('mysql+pymysql://username:password@hos

数据库课程设计食品销售统计系统.doc

数据库课程设计食品销售统计系统.doc

海量3D模型的自适应传输

为了获得的目的图卢兹大学博士学位发布人:图卢兹国立理工学院(图卢兹INP)学科或专业:计算机与电信提交人和支持人:M. 托马斯·福吉奥尼2019年11月29日星期五标题:海量3D模型的自适应传输博士学校:图卢兹数学、计算机科学、电信(MITT)研究单位:图卢兹计算机科学研究所(IRIT)论文主任:M. 文森特·查维拉特M.阿克塞尔·卡里尔报告员:M. GWendal Simon,大西洋IMTSIDONIE CHRISTOPHE女士,国家地理研究所评审团成员:M. MAARTEN WIJNANTS,哈塞尔大学,校长M. AXEL CARLIER,图卢兹INP,成员M. GILLES GESQUIERE,里昂第二大学,成员Géraldine Morin女士,图卢兹INP,成员M. VINCENT CHARVILLAT,图卢兹INP,成员M. Wei Tsang Ooi,新加坡国立大学,研究员基于HTTP的动态自适应3D流媒体2019年11月29日星期五,图卢兹INP授予图卢兹大学博士学位,由ThomasForgione发表并答辩Gilles Gesquière�

1.创建以自己姓名拼音缩写为名的数据库,创建n+自己班级序号(如n10)为名的数据表。2.表结构为3列:第1列列名为id,设为主键、自增;第2列列名为name;第3列自拟。 3.为数据表创建模型,编写相应的路由、控制器和视图,视图中用无序列表(ul 标签)呈现数据表name列所有数据。 4.创建视图,在表单中提供两个文本框,第一个文本框用于输入以上数据表id列相应数值,以post方式提交表单。 5.控制器方法根据表单提交的id值,将相应行的name列修改为第二个文本框中输入的数据。

步骤如下: 1. 创建数据库和数据表 创建名为xny_n10的数据表,其中xny为姓名拼音缩写,n10为班级序号。 ``` CREATE DATABASE IF NOT EXISTS xny_n10; USE xny_n10; CREATE TABLE IF NOT EXISTS xny_n10 ( id INT(11) PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50), column3 VARCHAR(50) ); ``` 2. 创建模型 在app/Models目录下创建XnyN10.php文件,定义XnyN10模型类,继承自I

液压推板式隧道电阻炉计算机监控设计毕业设计.doc

液压推板式隧道电阻炉计算机监控设计毕业设计.doc

HAL多学科开放获取档案库的作用及代理重加密和认证委托的研究

0HAL编号:tel-038172580https://theses.hal.science/tel-038172580提交日期:2022年10月17日0HAL是一个多学科开放获取档案库,用于存储和传播科学研究文档,无论其是否发表。这些文档可以来自法国或国外的教育和研究机构,也可以来自公共或私人研究中心。0HAL多学科开放获取档案库旨在存储和传播法国或国外的教育和研究机构、公共或私人实验室发表或未发表的研究文档。0代理重加密和认证委托的贡献0Anass Sbai0引用此版本:0Anass Sbai. 代理重加密和认证委托的贡献. 离散数学[cs.DM]. 皮卡第朱尔大学, 2021. 法语. �NNT:2021AMIE0032�. �tel-03817258�0博士学位论文0专业“计算机科学”0提交给科技与健康学院博士学位学校0皮卡第朱尔大学0由0Anass SBAI0获得皮卡第朱尔大学博士学位0代理重加密和认证委托的贡献0于2021年7月5日,在评审人的意见后,面向考试委员会进行

c++输入若干不同的整数构成的升序序列,请编写一个程序,找出序列中有多少个数是序列中其他两个数的平均值要求使用算法,使计算量最小,计算时间最短

算法思路: 1. 先将输入的升序序列存储到vector<int>中 2. 枚举每个数,以该数为平均数,查找序列中是否存在两个数的平均值等于该数 3. 可以采用双指针法,从序列的两端开始查找,如果两个指针所指的数的平均值大于目标数,则右指针左移;如果平均值小于目标数,则左指针右移;如果平均值等于目标数,则找到一个符合条件的数 4. 计数器加1,继续枚举下一个数 代码实现如下: ```c++ #include<iostream> #include<vector> using namespace std; int main() { int n; cin >> n;

基于jsp的景区票务系统源码数据库论文.doc

基于jsp的景区票务系统源码数据库论文.doc