在 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中。它的功能主要包括
- 跟踪和可视化损失及准确率等指标
- 可视化模型图(操作和层)
- 查看权重、偏差或其他张量随时间变化的直方图
- 将嵌入投射到较低的维度空间
- 显示图片、文字和音频数据
- 剖析 TensorFlow 程序
- TensorBoard 的工作原理和 Wandb 基本相同,本质也是一个网页服务,分成前端和后台两部分,两部分间是异步I/O的
- 后台程序将数据写入到本地文件中
- 前端程序读取本地文件中的数据来进行显示
- 由于 TensorBoard 已经集成到 Pytorch,无需再单独安装,直接
torch.utils.tensorboard即可找到
2. 快速入门
2.1 运行方法
- 可以把 Tensorboard 的运行分成两步
- 记录数据:使用
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_dirmax_queue(int) – 记录事件和摘要时在内存中开的队列的长度,当队列慢了之后就会把数据写入磁盘(文件)中。flush_secs(int) – 将待处理事件和摘要刷新到磁盘的频率,以秒为单位,默认为每两分钟一次。filename_suffix(str) – 添加到log_dir目录中所有事件文件名的后缀。有关文件名构造的更多详细信息,请参阅tensorboard.summary.writer.event_file_writer.EventFileWriter。
- 启动网页服务显示数据:使用
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 记录数据,比较常用的包括- add_hparams:以表格形式添加一组要比较的超参数
- add_scalar:将标量数据添加到摘要中,用来画一条折线
- add_scalars:将一组标量数据添加到摘要中,可以在同一张图内画多条折线
- add_histogram:绘制直方图
- add_image:将一张图片添加到摘要,需要
pillow包 - add_images:将一组图片添加到摘要,需要
pillow包 - add_figure:将matplotlib图形渲染到图像中,并将其添加到摘要中,需要
matplotlib包 - add_video:将视频数据添加到摘要中,需要
moviepy包 - add_audio:将音频数据添加到摘要中
- add_text:将文本数据添加到摘要中
- add_graph:将图表数据添加到摘要中,这个常用来显示模型结构
- add_embedding:将词嵌入向量数据添加到摘要中,这个可以交互式显示一组词向量在三维空间的投影
- add_pr_curve:添加精确召回曲线。绘制精确召回曲线可让您了解模型在不同阈值设置下的性能。此函数可以为每个目标提供真实标签(T/F)和预测置信度(通常是模型的输出)。利用 TensorBoard UI 可以交互式地选择阈值
- add_custom_scalars:通过在 “scalar” 中收集的图表
tag来创建特殊图表。注意对于每个SummaryWriter对象该函数只能调用一次,因为它只向tensorboard提供元数据,所以可以在训练循环之前或之后调用该函数 - 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 的收敛过程
这里使用了两个 API,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()add_hparams用来记录实验的超参数和结果,add_scalars用来记录收敛过程(用这个是为了方便把多条曲线绘制到一张图中),结果如下

这里测试了两种隐藏层尺寸,发现 hidden_size=64 时收敛比 128 快一点
4. 其他
- 关于 TensorBoard UI 的说明可以参考:TensorBoard最全使用教程:看这篇就够了
- 关于多次实验数据混合显示互相干扰的问题可以参考:tensorboard多个events文件显示紊乱的解决办法
- 总之感觉不如 Wandb 好用,就简单记录一下
相关文章:
在 Pytorch 中使用 TensorBoard
机器学习的训练过程中会产生各类数据,包括 “标量scalar”、“图像image”、“统计图diagram”、“视频video”、“音频audio”、“文本text”、“嵌入Embedding” 等等。为了更好地追踪和分析这些数据,许多可视化工具应运而生,比如之前介绍的…...
Grafana Dashboard 备份方案
文章目录 Grafana Dashboard 备份方案引言工具简介支持的组件要求配置备份安装使用 pypi 安装grafana备份工具配置环境变量使用Grafana Backup Tool 进行备份恢复备份 Grafana Dashboard恢复 Grafana Dashboard结论Grafana Dashboard 备份方案 引言 每个使用 Grafana 的同学都…...
opencv-疲劳检测-眨眼检测
#导入工具包 from scipy.spatial import distance as dist from collections import OrderedDict import numpy as np import argparse import time import dlib import cv2FACIAL_LANDMARKS_68_IDXS OrderedDict([("mouth", (48, 68)),("right_eyebrow",…...
2023-08-24力扣每日一题
链接: 1267. 统计参与通信的服务器 题意: 同行同列可以发生通信,求能发生通信的机器数量 解: 标记每行/每列的机器个数即可 实际代码: #include<bits/stdc.h> using namespace std; class Solution { pub…...
蚂蚁数科持续发力PaaS领域,SOFAStack布局全栈软件供应链安全产品
8月18日,记者了解到,蚂蚁数科再度加码云原生PaaS领域,SOFAStack率先完成全栈软件供应链安全产品及解决方案的布局,包括静态代码扫描Pinpoint、软件成分分析SCA、交互式安全测试IAST、运行时防护RASP、安全洞察Appinsight等&#x…...
Java后端开发面试题——消息中间篇
RabbitMQ-如何保证消息不丢失 交换机持久化: Bean public DirectExchange simpleExchange(){// 三个参数:交换机名称、是否持久化、当没有queue与其绑定时是否自动删除 return new DirectExchange("simple.direct", true, false); }队列持久化…...
C++ Windows API IsDebuggerPresent的作用
IsDebuggerPresent 是 Windows API 中的一个函数,它用于检测当前运行的程序是否正在被调试。当程序被如 Visual Studio 这样的调试器附加时,此函数会返回 TRUE;否则,它会返回 FALSE。 这个函数经常被用在一些安全相关的场景或是防…...
【JVM 内存结构 | 程序计数器】
内存结构 前言简介程序计数器定义作用特点示例应用场景 主页传送门:📀 传送 前言 Java 虚拟机的内存空间由 堆、栈、方法区、程序计数器和本地方法栈五部分组成。 简介 JVM(Java Virtual Machine)内存结构包括以下几个部分&#…...
华为云Stack的学习(一)
一、华为云Stack架构 1.HCS 物理分散、逻辑统一、业务驱动、运管协同、业务感知 2.华为云Stack的特点 可靠性 包括整体可靠性、数据可靠性和单一设备可靠性。通过云平台的分布式架构,从整体系统上提高可靠性,降低系统对单设备可靠性的要求。 可用性…...
人类反馈强化学习RLHF;微软应用商店推出AI摘要功能
🦉 AI新闻 🚀 微软应用商店推出AI摘要功能,快速总结用户对App的评价 摘要:微软应用商店正式推出了AI摘要功能,该功能能够将数千条在线评论总结成一段精练的文字,为用户选择和下载新应用和游戏提供参考。该…...
day1:前端缓存问题
❝ 「目标」: 持续输出!每日分享关于web前端常见知识、面试题、性能优化、新技术等方面的内容。篇幅不会过长,方便理解和记忆。 ❞ ❝ 「主要面向群体:」前端开发工程师(初、中、高级)、应届、转行、培训等同学 ❞ Day…...
学习网络编程No.4【socket编程实战】
引言 北京时间:2023/8/19/23:01,耍了好几天,主要归咎于《我欲封天》这本小说,听了几个晚上之后逐渐入门,在闲暇时间又看了一下,小高潮直接来临,最终在三个昼夜下追完了,哈哈哈&…...
HarmonyOS学习路之方舟开发框架—学习ArkTS语言(状态管理 四)
Observed装饰器和ObjectLink装饰器:嵌套类对象属性变化 上文所述的装饰器仅能观察到第一层的变化,但是在实际应用开发中,应用会根据开发需要,封装自己的数据模型。对于多层嵌套的情况,比如二维数组,或者数…...
arcgis--坐标系
1、arcgis中,投影坐标系的y坐标一定是7位数,X坐标有两种:6位和8位。 6位:省略带号,这是中央经线形式的投影坐标,一般投影坐标中会带CM字样;8位:包括带号,一般投影坐标中…...
LFS学习系列 第5章. 编译交叉工具链(1)
5.1 介绍 本章介绍如何构建交叉编译器及其相关工具。尽管这里的交叉编译是“伪造”、“假装”的,但其原理与真正的交叉工具链相同。 本章中编译的程序将安装在$LFS/tools目录下,以使它们与以下章节中安装的文件分离。而另一方面,库被安装到…...
网络互联与互联网 - TCP 协议详解
文章目录 1 概述2 TCP 传输控制协议2.1 报文格式2.2 三次握手,建立连接2.3 四次挥手,释放连接 3 扩展3.1 实验演示3.2 网工软考 1 概述 在 TCP/IP 协议簇 中有两个传输协议 TCP:Transmission Control Protocol,传输控制协议&…...
开源在线图片设计器,支持PSD解析、AI抠图等,基于Puppeteer生成图片
Github 开源地址: palxiao/poster-design 项目速览 git clone https://github.com/palxiao/poster-design.git cd poster-design npm run prepared # 快捷安装依赖指令 npm run serve # 本地运行将同时运行前端界面与图片生成服务(3000与7001端口),合成图片时…...
在Linux系统上安装和配置Redis数据库,无需公网IP即可实现远程连接的详细解析
文章目录 1. Linux(centos8)安装redis数据库2. 配置redis数据库3. 内网穿透3.1 安装cpolar内网穿透3.2 创建隧道映射本地端口 4. 配置固定TCP端口地址4.1 保留一个固定tcp地址4.2 配置固定TCP地址4.3 使用固定的tcp地址连接 Redis作为一款高速缓存的key value键值对的数据库,在…...
跨平台图表:ChartDirector for .NET 7.1 Crack
什么是新的 ChartDirector for .NET 7.0 支持跨平台使用,但仅限于 .NET 6。这是因为在 .NET 7 中,Microsoft 停止了用于非 Windows 使用的 .NET 图形库 System.Drawing.Common。由于 ChartDirector for .NET 7.0 依赖于该库,因此它不再支持 .…...
【unity数据持久化】XML数据管理器知识点
👨💻个人主页:元宇宙-秩沅 👨💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨💻 本文由 秩沅 原创 👨💻 收录于专栏:Uni…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
