在 Pytorch 中使用 TensorBoard

  • 机器学习的训练过程中会产生各类数据,包括 “标量scalar”、“图像image”、“统计图diagram”、“视频video”、“音频audio”、“文本text”、“嵌入Embedding” 等等。为了更好地追踪和分析这些数据,许多可视化工具应运而生,比如之前介绍的 wandb
  • 本文介绍另一种更加常用的数据追踪工具 TensorBoard,参考见 Pytorch 官方文档

文章目录

  • 1. Tensorboard 简介
  • 2. 快速入门
    • 2.1 运行方法
    • 2.2 常用 API
  • 3. 使用 TensorBoard 记录 PPO 运行情况
  • 4. 其他

1. Tensorboard 简介

  • TensorBoard 是Google开发的一个机器学习可视化工具,它原本是TensorFlow中的模块,不过现在已经集成到了Pytorch中。它的功能主要包括
    1. 跟踪和可视化损失及准确率等指标
    2. 可视化模型图(操作和层)
    3. 查看权重、偏差或其他张量随时间变化的直方图
    4. 将嵌入投射到较低的维度空间
    5. 显示图片、文字和音频数据
    6. 剖析 TensorFlow 程序
  • TensorBoard 的工作原理和 Wandb 基本相同,本质也是一个网页服务,分成前端和后台两部分,两部分间是异步I/O的
    • 后台程序将数据写入到本地文件中
    • 前端程序读取本地文件中的数据来进行显示
  • 由于 TensorBoard 已经集成到 Pytorch,无需再单独安装,直接 torch.utils.tensorboard 即可找到

2. 快速入门

2.1 运行方法

  • 可以把 Tensorboard 的运行分成两步
    1. 记录数据:使用 SummaryWriter 类实例数据要追踪的数据。每次运行时,该类对象首先会在给定目录log_dir中创建 “事件文件”(本次运行的数据仓库),然后在训练过程中我们可以利用其提供的一系列高级 API 向事件文件中异步地添加数据,从而实时地追踪数据变化。该类的定义如下
      torch.utils.tensorboard.writer.SummaryWriter(log_dir=None, comment='', purge_step=None, max_queue=10, flush_secs=120, filename_suffix='')
      
      • log_dir (str) – 事件文件保存的目录位置。默认为runs / CURRENT_DATETIME_HOSTNAME,每次运行后都会更改。推挤使用分层文件夹结构,例如对于每个新实验,可以传递“runs / exp1”,“runs / exp2”等来进行比较。
      • comment (str) –在默认的log_dir后缀中的注释。如果已设置log_dir,则此参数无效。
      • purge_step (int) – 若在运行到第 T + X T + X T+X 个 step 的时候由于各种原因(内存溢出)发生崩溃,那么当服务重启之后,就回退 X X X 个 step,从 T T T 时刻重新开始将数据写入文件。purge_step 参数就是设置的 X X X,这一段数据将被重新写入。注意,崩溃和恢复的实验应该具有相同的log_dir
      • max_queue (int) – 记录事件和摘要时在内存中开的队列的长度,当队列慢了之后就会把数据写入磁盘(文件)中。
      • flush_secs (int) – 将待处理事件和摘要刷新到磁盘的频率,以秒为单位,默认为每两分钟一次。
      • filename_suffix (str) – 添加到log_dir目录中所有事件文件名的后缀。有关文件名构造的更多详细信息,请参阅tensorboard.summary.writer.event_file_writer.EventFileWriter。
    2. 启动网页服务显示数据:使用 tensorboard --logdir 数据文件夹 命令运行网页服务,其中 “数据文件夹” 应设置为之前实验时设置的 log_dir。若看到了如下输出 TensorBoard 2.8.0 at http://localhost:6006/ (Press CTRL+C to quit) 则说明启动成功,在浏览器打开相应 url 就能进入TensorBoard界面看到数据显示了。注意默认端口是 6006,如果想进一步指定网页服务端口,可以用 tensorboard --logdir=数据文件夹 --port=端口 命令

2.2 常用 API

  • 我们使用 SummaryWriter 类提供的一系列 API 记录数据,比较常用的包括

    1. add_hparams:以表格形式添加一组要比较的超参数
    2. add_scalar:将标量数据添加到摘要中,用来画一条折线
    3. add_scalars:将一组标量数据添加到摘要中,可以在同一张图内画多条折线
    4. add_histogram:绘制直方图
    5. add_image:将一张图片添加到摘要,需要 pillow
    6. add_images:将一组图片添加到摘要,需要 pillow
    7. add_figure:将matplotlib图形渲染到图像中,并将其添加到摘要中,需要 matplotlib
    8. add_video:将视频数据添加到摘要中,需要 moviepy
    9. add_audio:将音频数据添加到摘要中
    10. add_text:将文本数据添加到摘要中
    11. add_graph:将图表数据添加到摘要中,这个常用来显示模型结构
    12. add_embedding:将词嵌入向量数据添加到摘要中,这个可以交互式显示一组词向量在三维空间的投影
    13. add_pr_curve:添加精确召回曲线。绘制精确召回曲线可让您了解模型在不同阈值设置下的性能。此函数可以为每个目标提供真实标签(T/F)和预测置信度(通常是模型的输出)。利用 TensorBoard UI 可以交互式地选择阈值
    14. add_custom_scalars:通过在 “scalar” 中收集的图表 tag 来创建特殊图表。注意对于每个 SummaryWriter 对象该函数只能调用一次,因为它只向tensorboard提供元数据,所以可以在训练循环之前或之后调用该函数
    15. add_mesh:向TensorBoard添加网格或3D点云。可视化基于Three.js,因此它允许用户与呈现的对象进行交互。除了顶点、面等基本定义外,用户还可以提供相机参数、光照条件等
  • 综合测试代码参考这里,运行效果可以参考这里:

    # 引入SummaryWriter
    import numpy as np
    import torch
    from torch.utils.tensorboard import SummaryWriter
    import torchvision
    from PIL import Image
    import matplotlib
    import matplotlib.pyplot as plt
    matplotlib.use('Agg')##### 1、add_scalar实例
    def add_scalar_demo():# 将信息写入logs文件夹,可以供TensorBoard消费,来可视化writer = SummaryWriter("logs/add_scalar")# 绘制 y = 2x 实例x = range(100)for i in x:writer.add_scalar('add_scalar实例:y=2x', i * 2, i)# 关闭writer.close()
    add_scalar_demo()##### 2、add_scalars 实例
    def add_scalars_demo():# 将信息写入logs文件夹,可以供TensorBoard消费,来可视化writer = SummaryWriter("logs/add_scalars")r = 5for i in range(100):writer.add_scalars('add_scalars实例', {'xsinx':i*np.sin(i/r),'xcosx':i*np.cos(i/r),'tanx': np.tan(i/r)}, i)# 关闭writer.close()add_scalars_demo()##### 3、add_text 实例
    def add_text_demo():# 将信息写入logs文件夹,可以供TensorBoard消费,来可视化writer = SummaryWriter("logs/add_text")writer.add_text('lstm', 'This is an lstm', 0)writer.add_text('rnn', 'This is an rnn', 10)# 关闭writer.close()add_text_demo()##### 4、add_graph 实例def add_graph_demo():# 将信息写入logs文件夹,可以供TensorBoard消费,来可视化writer = SummaryWriter("logs/add_graph")img = torch.rand([1, 3, 64, 64], dtype=torch.float32)model = torchvision.models.AlexNet(num_classes=10)# print(model)writer.add_graph(model, input_to_model=img)# 关闭writer.close()add_graph_demo()##### 5、add_image 实例def add_image_demo():# 将信息写入logs文件夹,可以供TensorBoard消费,来可视化writer = SummaryWriter("logs/add_image")img1 = np.random.randn(1, 100, 100)writer.add_image('add_image 实例:/imag1', img1)img2 = np.random.randn(100, 100, 3)writer.add_image('add_image 实例:/imag2', img2, dataformats='HWC')img = Image.open('../imgs/1.png')img_array = np.array(img)writer.add_image('add_image 实例:/cartoon', img_array, dataformats='HWC')# 关闭writer.close()add_image_demo()##### 6、add_images 实例def add_images_demo():# 将信息写入logs文件夹,可以供TensorBoard消费,来可视化writer = SummaryWriter("logs/add_images")imgs1 = np.random.randn(8, 100, 100, 1)writer.add_images('add_images 实例/imgs1', imgs1, dataformats='NHWC')imgs2 = np.zeros((16, 3, 100, 100))for i in range(16):imgs2[i, 0] = np.arange(0, 10000).reshape(100, 100) / 10000 / 16 * iimgs2[i, 1] = (1 - np.arange(0, 10000).reshape(100, 100) / 10000) / 16 * iwriter.add_images('add_images 实例/imgs2', imgs2)  # Default is :math:`(N, 3, H, W)`img = Image.open('../imgs/1.jpg')img3 = np.array(img)imgs4= np.zeros((5, img3.shape[0], img3.shape[1], img3.shape[2]))for i in range(5):imgs4[i] = img3//(i+1)writer.add_images('add_images 实例/imgs4', imgs4, dataformats='NHWC')  # Default is :math:`(N, 3, H, W)`# 关闭writer.close()add_images_demo()##### 7、add_figure 实例def add_figure_demo():# 将信息写入logs文件夹,可以供TensorBoard消费,来可视化writer = SummaryWriter("logs/add_figure")# First create some toy data:x = np.linspace(0, 2 * np.pi, 400)y = np.sin(x ** 2)# Create just a figure and only one subplotfig, ax = plt.subplots()ax.plot(x, y)ax.set_title('Simple plot')writer.add_figure("add_figure 实例:figure", fig)# 关闭writer.close()add_figure_demo()##### 8、add_pr_curve 实例def add_pr_curve_demo():# 将信息写入logs文件夹,可以供TensorBoard消费,来可视化writer = SummaryWriter("logs/add_pr_curve")labels = np.random.randint(2, size=100)  # binary labelpredictions = np.random.rand(100)writer.add_pr_curve('add_pr_curve 实例:pr_curve', labels, predictions, 0)# 关闭writer.close()add_pr_curve_demo()##### 9、add_embedding 实例def add_embedding_demo():# 将信息写入logs文件夹,可以供TensorBoard消费,来可视化writer = SummaryWriter("logs/add_embedding")import tensorflow as tfimport tensorboard as tbtf.io.gfile = tb.compat.tensorflow_stub.io.gfileimport keywordimport torchmeta = []while len(meta) < 100:meta = meta + keyword.kwlist  # get some stringsmeta = meta[:100]for i, v in enumerate(meta):meta[i] = v + str(i)label_img = torch.rand(100, 3, 10, 32)for i in range(100):label_img[i] *= i / 100.0writer.add_embedding(torch.randn(100, 5), metadata=meta, label_img=label_img)# 关闭writer.close()add_embedding_demo()
    

3. 使用 TensorBoard 记录 PPO 运行情况

  • 将 TensorBoard 相关代码添加到前文 RL 实践(7)—— CartPole【TPRO & PPO】 的 PPO 代码中,观察 RL 的收敛过程
    import gym
    import torch
    import random
    import torch.nn.functional as F
    import numpy as np
    import matplotlib.pyplot as plt
    from tqdm import tqdm
    from gym.utils.env_checker import check_env
    from gym.wrappers import TimeLimit 
    from datetime import datetime
    from torch.utils.tensorboard import SummaryWriterclass PolicyNet(torch.nn.Module):''' 策略网络是一个两层 MLP '''def __init__(self, input_dim, hidden_dim, output_dim):super(PolicyNet, self).__init__()self.fc1 = torch.nn.Linear(input_dim, hidden_dim)self.fc2 = torch.nn.Linear(hidden_dim, output_dim)def forward(self, x):x = F.relu(self.fc1(x))             # (1, hidden_dim)x = F.softmax(self.fc2(x), dim=1)   # (1, output_dim)return xclass VNet(torch.nn.Module):''' 价值网络是一个两层 MLP '''def __init__(self, input_dim, hidden_dim):super(VNet, self).__init__()self.fc1 = torch.nn.Linear(input_dim, hidden_dim)self.fc2 = torch.nn.Linear(hidden_dim, 1)def forward(self, x):x = F.relu(self.fc1(x))x = self.fc2(x)return xclass PPO(torch.nn.Module):def __init__(self, state_dim, hidden_dim, action_range, actor_lr, critic_lr, lmbda, epochs, eps, gamma, device):super().__init__()self.actor = PolicyNet(state_dim, hidden_dim, action_range).to(device)self.critic = VNet(state_dim, hidden_dim).to(device) self.actor_optimizer = torch.optim.Adam(self.actor.parameters(), lr=actor_lr)self.critic_optimizer = torch.optim.Adam(self.critic.parameters(), lr=critic_lr)self.device = deviceself.gamma = gammaself.lmbda = lmbda      # GAE 参数self.epochs = epochs    # 一条轨迹数据用来训练的轮数self.eps = eps          # PPO 中截断范围的参数self.device = device        def take_action(self, state):state = torch.tensor(state, dtype=torch.float).to(self.device)state = state.unsqueeze(0)probs = self.actor(state)action_dist = torch.distributions.Categorical(probs)action = action_dist.sample()return action.item()def compute_advantage(self, gamma, lmbda, td_delta):''' 广义优势估计 GAE '''td_delta = td_delta.detach().numpy()advantage_list = []advantage = 0.0for delta in td_delta[::-1]:advantage = gamma * lmbda * advantage + deltaadvantage_list.append(advantage)advantage_list.reverse()return torch.tensor(np.array(advantage_list), dtype=torch.float)def update(self, transition_dict):states = torch.tensor(np.array(transition_dict['states']), dtype=torch.float).to(self.device)actions = torch.tensor(transition_dict['actions']).view(-1, 1).to(self.device)rewards = torch.tensor(transition_dict['rewards'], dtype=torch.float).view(-1, 1).to(self.device)next_states = torch.tensor(np.array(transition_dict['next_states']), dtype=torch.float).to(self.device)dones = torch.tensor(transition_dict['dones'], dtype=torch.float).view(-1, 1).to(self.device)td_target = rewards + self.gamma * self.critic(next_states) * (1-dones)td_delta = td_target - self.critic(states)advantage = self.compute_advantage(self.gamma, self.lmbda, td_delta.cpu()).to(self.device)old_log_probs = torch.log(self.actor(states).gather(1, actions)).detach()# 用刚采集的一条轨迹数据训练 epochs 轮for _ in range(self.epochs):log_probs = torch.log(self.actor(states).gather(1, actions))ratio = torch.exp(log_probs - old_log_probs)surr1 = ratio * advantagesurr2 = torch.clamp(ratio, 1 - self.eps, 1 + self.eps) * advantage  # 截断actor_loss = torch.mean(-torch.min(surr1, surr2))                   # PPO损失函数critic_loss = torch.mean(F.mse_loss(self.critic(states), td_target.detach()))# 更新网络参数self.actor_optimizer.zero_grad()self.critic_optimizer.zero_grad()actor_loss.backward()critic_loss.backward()self.actor_optimizer.step()self.critic_optimizer.step()if __name__ == "__main__":def moving_average(a, window_size):''' 生成序列 a 的滑动平均序列 '''cumulative_sum = np.cumsum(np.insert(a, 0, 0)) middle = (cumulative_sum[window_size:] - cumulative_sum[:-window_size]) / window_sizer = np.arange(1, window_size-1, 2)begin = np.cumsum(a[:window_size-1])[::2] / rend = (np.cumsum(a[:-window_size:-1])[::2] / r)[::-1]return np.concatenate((begin, middle, end))def set_seed(env, seed=42):''' 设置随机种子 '''env.action_space.seed(seed)env.reset(seed=seed)random.seed(seed)np.random.seed(seed)torch.manual_seed(seed)state_dim = 4               # 环境观测维度action_range = 2            # 环境动作空间大小actor_lr = 1e-3critic_lr = 1e-2num_episodes = 200hidden_dim = 64gamma = 0.98lmbda = 0.95epochs = 10eps = 0.2device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")# build environmentenv_name = 'CartPole-v0'env = gym.make(env_name, render_mode='rgb_array')check_env(env.unwrapped)    # 检查环境是否符合 gym 规范set_seed(env, 0)# build agentagent = PPO(state_dim, hidden_dim, action_range, actor_lr, critic_lr, lmbda, epochs, eps, gamma, device)# TensorBoard writerTIMESTAMP = "{0:%Y-%m-%dT%H-%M-%S/}".format(datetime.now())writer = SummaryWriter(f"logs/PPO")#writer = SummaryWriter(f"logs/PPO/{TIMESTAMP}")# start trainingreturn_list = []for i in range(10):with tqdm(total=int(num_episodes / 10), desc='Iteration %d' % i) as pbar:for i_episode in range(int(num_episodes / 10)):episode_return = 0transition_dict = {'states': [],'actions': [],'next_states': [],'next_actions': [],'rewards': [],'dones': []}state, _ = env.reset()# 以当前策略交互得到一条轨迹while True:action = agent.take_action(state)next_state, reward, terminated, truncated, _ = env.step(action)next_action = agent.take_action(next_state)transition_dict['states'].append(state)transition_dict['actions'].append(action)transition_dict['next_states'].append(next_state)transition_dict['next_actions'].append(next_action)transition_dict['rewards'].append(reward)transition_dict['dones'].append(terminated or truncated)state = next_stateepisode_return += rewardif terminated or truncated:break#env.render()# 用当前策略收集的数据进行 on-policy 更新agent.update(transition_dict)# 更新进度条return_list.append(episode_return)pbar.set_postfix({'episode':'%d' % (num_episodes / 10 * i + i_episode + 1),'return':'%.3f' % episode_return,'ave return':'%.3f' % np.mean(return_list[-10:])})pbar.update(1)writer.add_scalars(main_tag='return', tag_scalar_dict={f'hidden{hidden_dim}':episode_return}, global_step=i*int(num_episodes / 10) + i_episode)writer.add_hparams(hparam_dict={'actor_lr': actor_lr, 'critic_lr': critic_lr,'hidden_dim': hidden_dim,'gamma': gamma,'lmbda': lmbda,'eps': eps,'num_episodes': num_episodes},metric_dict={'hparam/ave return': np.mean(return_list), })writer.close()# show policy performencemv_return_list = moving_average(return_list, 29)episodes_list = list(range(len(return_list)))plt.figure(figsize=(12,8))plt.plot(episodes_list, return_list, label='raw', alpha=0.5)plt.plot(episodes_list, mv_return_list, label='moving ave')plt.xlabel('Episodes')plt.ylabel('Returns')plt.title(f'{agent._get_name()} on CartPole-V0')plt.legend()plt.savefig(f'./result/{agent._get_name()}.png')plt.show()                           
    这里使用了两个 API,add_hparams 用来记录实验的超参数和结果,add_scalars 用来记录收敛过程(用这个是为了方便把多条曲线绘制到一张图中),结果如下
    在这里插入图片描述
    这里测试了两种隐藏层尺寸,发现 hidden_size=64 时收敛比 128 快一点

4. 其他

  • 关于 TensorBoard UI 的说明可以参考:TensorBoard最全使用教程:看这篇就够了
  • 关于多次实验数据混合显示互相干扰的问题可以参考:tensorboard多个events文件显示紊乱的解决办法
  • 总之感觉不如 Wandb 好用,就简单记录一下

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/103896.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 ChatGPT 创建 PowerPoint 演示文稿

让 ChatGPT 成为您的助手来帮助您编写电子邮件很简单,因为众所周知,它非常能够生成文本。很明显,ChatGPT 无法帮助您做饭。但您可能想知道它是否可以生成文本以外的其他内容。在上一篇文章中,您了解到 ChatGPT 只能通过中间语言为您生成图形。在这篇文章中,您将了解使用中…

Ubuntu 配置国内源

配置国内源 因为众所周知的原因&#xff0c;国外的很多网站在国内是访问不了或者访问极慢的&#xff0c;这其中就包括了Ubuntu的官方源。 所以&#xff0c;想要流畅的使用apt安装应用&#xff0c;就需要配置国内源的镜像。 市面上Ubuntu的国内镜像源非常多&#xff0c;比较有…

PyTorch训练简单的生成对抗网络GAN

文章目录 原理代码结果参考 原理 同时训练两个网络&#xff1a;辨别器Discriminator 和 生成器Generator Generator是 造假者&#xff0c;用来生成假数据。 Discriminator 是警察&#xff0c;尽可能的分辨出来哪些是造假的&#xff0c;哪些是真实的数据。 目的&#xff1a;使…

小区新冠疫情管理系统的设计与实现/基于springboot的小区疫情管理系统

摘要 采用更加便于维护和使用的Java语言&#xff0c;其可拓展性高且更富于表现力&#xff0c;基于mysql数据库、Springboot框架开发的小区新冠疫情管理系统&#xff0c;方便用户查看物资信息、疫苗信息。通过Eclipse来进行网页编程&#xff0c;其方便易用、移植适用性广、更加安…

日志搞不定?手把手教你如何使用Log4j2

系列文章目录 从零开始&#xff0c;手把手教你搭建Spring Boot后台工程并说明 Spring框架与SpringBoot的关联与区别 SpringBean生成流程详解 —— 由浅入深(附超精细流程图) Spring监听器用法与原理详解 Spring事务畅谈 —— 由浅入深彻底弄懂 Transactional注解 面试热点详解…

装备制造企业如何执行精益管理?

导 读 ( 文/ 2358 ) 精益管理是一种以提高效率、降低成本和优化流程为目标的管理方法。装备制造行业具备人工参与度高&#xff0c;产成品价值高&#xff0c;质量要求高的特点。 在装备制造企业中实施精益管理可以帮助企业提高竞争力、提升生产效率并提供高质量的产品。本文将…

边缘计算节点BEC典型实践:如何快速上手PC-Farm服务器?

百度智能云边缘计算节点BEC&#xff08;Baidu Edge Computing&#xff09;基于运营商边缘节点和网络构建&#xff0c;一站式提供靠近终端用户的弹性计算资源。边缘计算节点在海外覆盖五大洲&#xff0c;在国内覆盖全国七大区、三大运营商。BEC通过就近计算和处理&#xff0c;大…

【算法日志】贪心算法刷题:单调递增数列,贪心算法总结(day32)

代码随想录刷题60Day 目录 前言 单调递增数列 贪心算法总结 前言 今天是贪心算法刷题的最后一天&#xff0c;今天本来是打算刷两道题&#xff0c;其中的一道hard题做了好久都没有做出来(主要思路错了)。然后再总结一下。 单调递增数列 int monotoneIncreasingDigits(int n…

Wlan——STA上线流程与802.11MAC帧讲解以及报文转发路径

目录 802.11MAC帧基本概念 802.11帧结构 802.11MAC帧的分类 管理帧 控制帧 数据帧 STA接入无线网络流程 信号扫描—管理帧 链路认证—管理帧 用户关联—管理帧 用户上线 不同802.11帧的转发路径 802.11MAC帧基本概念 802.11协议在802家族中的角色位置 其中802.3标…

Linux内核学习(八)—— 内存管理(基于Linux 2.6内核)

目录 一、页&#xff08;page&#xff09; 二、区&#xff08;zone&#xff09; 三、页操作 四、kmalloc() 五、vmalloc() 六、slab 分配器 七、在栈上的静态分配 一、页&#xff08;page&#xff09; 内核把物理页作为内存管理的基本单位。尽管处理器的最小可寻 …

网工内推 | 锐捷招云工程师,HCIE、CCIE、RHCE优先,25k*13薪

01 锐捷网络 招聘岗位&#xff1a;云方案工程师 职责描述&#xff1a; 1、负责云数据中心方案项目方案设计撰写、项目实施交付、故障处理、业务割接、客户培训、现场保障、网络优化、网络巡检等技术相关业务 2、负责云数据中心方案新技术文档沉淀、体系建设、工具开发等标准化…

使用element-plus组件,默认显示英文 转换为中文

最近在边写项目边学习vue3 所以这几天没有更新 找机会把vue3的知识也统计一下吧 先说今天遇到的问题 最近做项目的时候使用element-plus分页组件时发现&#xff0c;显示的不是中文的了&#xff0c;是英文的 解决方法 在app.vue里面配置 <template><el-config-provi…

Pyqt5打开电脑摄像头进行拍照

目录 1、设计UI界面 2、设计逻辑代码&#xff0c;建立连接显示窗口 3、结果 1、设计UI界面 将ui界面转为py文件后获得的逻辑代码为&#xff1a;&#xff08;文件名为 Camera.py&#xff09; # -*- coding: utf-8 -*-# Form implementation generated from reading ui file …

【算法专题突破】双指针 - 移动零(1)

目录 写在前面 1. 题目解析 2. 算法原理 3. 代码编写 写在最后&#xff1a; 写在前面 在进行了剑指Offer和LeetCode hot100的毒打之后&#xff0c; 我决心系统地学习一些经典算法&#xff0c;增强我的综合算法能力。 1. 题目解析 题目链接&#xff1a;283. 移动零 - 力…

centos7.9和redhat6.9 离线升级OpenSSH和openssl (2023年的版本)

升级注意事项&#xff01; 1、多开几个连接窗口&#xff08;xshell&#xff09;&#xff0c;避免升级openssh失败无法再次连接终端&#xff0c;否则要跑机房了。 2、可开启telnet服务、vnc服务、打快照。多几个“保命”的路数。一、centos7.9的信息 [rootnode2 ~]# openssl v…

学会Mybatis框架:一文掌握MyBatis与GitHub插件分页的完美结合【三.分页】

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Mybatis的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.Mybatis分页 1. Mybatis自带分页 2…

AIGC如何借AI Agent落地?TARS-RPA-Agent破解RPA与LLM融合难题

文/王吉伟 大语言模型&#xff08;LLM&#xff0c;Large Language Model&#xff09;的持续爆发&#xff0c;让AIGC一直处于这股AI风暴最中央&#xff0c;不停席卷各个领域。 在国内&#xff0c;仍在雨后春笋般上新的大语言模型&#xff0c;在持续累加“千模大战”大模型数量的…

Linux之基础IO文件系统讲解

基础IO文件系统讲解 回顾C语言读写文件读文件操作写文件操作输出信息到显示器的方法stdin & stdout & stderr总结 系统文件IOIO接口介绍文件描述符fd文件描述符的分配规则C标准库文件操作函数简易模拟实现重定向dup2 系统调用在minishell中添加重定向功能 FILE文件系统…

什么是Pytorch?

当谈及深度学习框架时&#xff0c;PyTorch 是当今备受欢迎的选择之一。作为一个开源的机器学习库&#xff0c;PyTorch 为研究人员和开发者们提供了一个强大的工具来构建、训练以及部署各种深度学习模型。你可能会问&#xff0c;PyTorch 是什么&#xff0c;它有什么特点&#xf…

为什么需要单元测试?

为什么需要单元测试&#xff1f; 从产品角度而言&#xff0c;常规的功能测试、系统测试都是站在产品局部或全局功能进行测试&#xff0c;能够很好地与用户的需要相结合&#xff0c;但是缺乏了对产品研发细节&#xff08;特别是代码细节的理解&#xff09;。 从测试人员角度而言…