当前位置: 首页 > news >正文

Sat- nerf深度损失

首先损失函数定义在metrics.py,代码如下:

class DepthLoss(torch.nn.Module):def __init__(self, lambda_ds=1.0):super().__init__()# 初始化lambda_ds参数,用于调节深度损失的权重,并且将其缩小为原来的1/3self.lambda_ds = lambda_ds / 3.# 初始化均方误差损失函数(MSELoss),并设置reduce=False表示不对损失值进行平均self.loss = torch.nn.MSELoss(reduce=False)def forward(self, inputs, targets, weights=1.):# 创建一个字典,用来存储不同类型的深度损失loss_dict = {}# 默认使用'coarse'(粗略)类型typ = 'coarse'# 计算输入深度与目标深度之间的损失,并将结果存入字典loss_dict[f'{typ}_ds'] = self.loss(inputs['depth_coarse'], targets)# 如果输入中包含'fine'(精细)类型的深度数据if 'depth_fine' in inputs:typ = 'fine'# 计算精细深度的损失,并存入字典loss_dict[f'{typ}_ds'] = self.loss(inputs['depth_fine'], targets)# 对每个损失项应用权重for k in loss_dict.keys():# 计算加权的平均损失,并乘以lambda_ds来调整损失的权重loss_dict[k] = self.lambda_ds * torch.mean(weights * loss_dict[k])# 计算所有损失项的总和loss = sum(l for l in loss_dict.values())# 返回总损失以及包含各个深度损失的字典return loss, loss_dict

需要三个输入inputs, targets, weights=1(inputs为输入深度,target为gt,weight为权重),得到两个输出loss, loss_dict(loss为总和,loss_dict为记录单个损失的字典)。

当main函数运行到

system = NeRF_pl(args)  # 初始化 NeRF 模型系统,传入配置参数,为模型训练做好准备工作,确保所有需要的配置和资源都已经到位。

开始调用Nerf_pl 的_init_,会在其中实例化 DepthLoss 类:

self.depth_loss = DepthLoss(lambda_ds=args.ds_lambda)  # 初始化深度损失对象,传入深度监督系数

当运行trainer.fit(system)时,训练启动:

当执行trainer.fit(system)时,Lightning接管了训练过程,
Lightning首先调用prepare_data()准备数据集
然后调用configure_optimizers()设置优化器和学习率调度器

训练循环:

Lightning自动开始训练循环,每个epoch包含:

训练步骤: Lightning自动从train_dataloader()加载数据:

    def train_dataloader(self):"""创建并返回训练数据加载器字典根据配置参数创建不同模态(颜色/深度)的训练数据加载器。当self.depth为True时,会同时创建颜色数据和深度数据的加载器。数据加载器使用4个工作进程进行数据加载,启用内存锁页(pin_memory)以加速GPU数据传输,并自动进行批次数据打乱。Returns:dict: 包含数据加载器的字典,键为模态名称("color"/"depth"),值为对应的torch.utils.data.DataLoader实例"""# 创建颜色数据的训练集加载器(第一个数据集)a = DataLoader(self.train_dataset[0],shuffle=True,num_workers=4,batch_size=self.args.batch_size,pin_memory=True)loaders = {"color": a}# 当需要加载深度数据时,创建第二个数据加载器if self.depth:b = DataLoader(self.train_dataset[1],#数据从上面dataloade 的self.train_dataset[1],这是一个SatelliteDataset_depth类的实例,在prepare_data()方法中创建shuffle=True,num_workers=4,batch_size=self.args.batch_size,pin_memory=True)loaders["depth"] = b#通过batch["depth"]访问return loaders

可以看到深度数据从上面prepare_data()方法中创建的self.train_dataset[1],这是一个SatelliteDataset_depth类的实例。
接下来,调用training_step()处理每个批次:

    def training_step(self, batch, batch_nb):self.log("lr", train_utils.get_learning_rate(self.optimizer))self.train_steps += 1rays = batch["color"]["rays"] # (B, 11)rgbs = batch["color"]["rgbs"] # (B, 3)ts = None if not self.use_ts else batch["color"]["ts"].squeeze() # (B, 1)results = self(rays, ts)if 'beta_coarse' in results and self.get_current_epoch(self.train_steps) < 2:loss, loss_dict = self.loss_without_beta(results, rgbs)else:loss, loss_dict = self.loss(results, rgbs)self.args.noise_std *= 0.9if self.depth:tmp = self(batch["depth"]["rays"], batch["depth"]["ts"].squeeze())kp_depths = torch.flatten(batch["depth"]["depths"][:, 0])kp_weights = 1. if self.args.ds_noweights else torch.flatten(batch["depth"]["depths"][:, 1])loss_depth, tmp = self.depth_loss(tmp, kp_depths, kp_weights)#tmp是作为imput输入进去了,kp_depths是target,kp_weights是权重if self.train_steps < self.ds_drop :loss += loss_depthfor k in tmp.keys():loss_dict[k] = tmp[k]self.log("train/loss", loss)typ = "fine" if "rgb_fine" in results else "coarse"with torch.no_grad():psnr_ = metrics.psnr(results[f"rgb_{typ}"], rgbs)self.log("train/psnr", psnr_)for k in loss_dict.keys():self.log("train/{}".format(k), loss_dict[k])self.log('train_psnr', psnr_, on_step=True, on_epoch=True, prog_bar=True)return {'loss': loss}

而在这中间 results = self(rays, ts)相当于就是隐式调用了Nerf_pl的forward算法,另外我们可以看到我们的目标self.depth_loss被调用了,tmp是作为imput输入进去了,kp_depths是target,kp_weights是权重。
那接下来我们一个一个分析这三个输入都是从哪来的。

预测深度(tmp)的完整产生过程

1. 初始数据:深度射线信息

# 在training_step中获取的初始数据,来自于train_dataloader(),
rays = batch["depth"]["rays"]  # 形状为[B, 11]的张量
ts = batch["depth"]["ts"].squeeze()  # 时间戳信息

这里的rays包含11个通道的信息:

  • rays[:, 0:3]: 射线原点坐标
  • rays[:, 3:6]: 射线方向向量
  • rays[:, 6:7]: 近平面距离
  • rays[:, 7:8]: 远平面距离
  • rays[:, 8:11]: 太阳方向向量

2. 模型前向传递:self方法调用

# training_step中的调用
tmp = self(batch["depth"]["rays"], batch["depth"]["ts"].squeeze())

这调用了NeRF_plforward方法(在main.py中):

def forward(self, rays, ts):chunk_size = self.args.chunkbatch_size = rays.shape[0]results = defaultdict(list)for i in range(0, batch_size, chunk_size):rendered_ray_chunks = render_rays(self.models, self.args, rays[i:i + chunk_size],ts[i:i + chunk_size] if ts is not None else None)for k, v in rendered_ray_chunks.items():results[k] += [v]for k, v in results.items():results[k] = torch.cat(v, 0)return results

该方法将射线分成小块,处理后合并结果。

3. 在forward中调用了render_rays函数:渲染逻辑

进入rendering.py中的render_rays函数:

def render_rays(models, args, rays, ts):# 获取配置参数N_samples = args.n_samples  N_importance = args.n_importancevariant = args.model  # "sat-nerf"# 分解射线信息rays_o, rays_d = rays[:, 0:3], rays[:, 3:6]near, far = rays[:, 6:7], rays[:, 7:8]# 采样深度点z_steps = torch.linspace(0, 1, N_samples, device=rays.device)z_vals = near * (1-z_steps) + far * z_steps  # 线性采样# 添加随机扰动if perturb > 0:# 采样点添加扰动代码...# 计算3D采样点坐标xyz_coarse = rays_o.unsqueeze(1) + rays_d.unsqueeze(1) * z_vals.unsqueeze(2)# 根据模型类型调用相应的inference函数if variant == "sat-nerf":from models.satnerf import inferencesun_d = rays[:, 8:11]rays_t = models['t'](ts) if ts is not None else Noneresult = inference(models["coarse"], args, xyz_coarse, z_vals, rays_d=None, sun_d=sun_d, rays_t=rays_t)# 太阳光校正相关代码...# 组织结果result_ = {}for k in result.keys():result_[f"{k}_coarse"] = result[k]# 如果需要精细采样if N_importance > 0:# 精细采样相关代码...return result_

这个函数处理射线,生成采样点,并调用合适的模型inference函数。

4. 又在render_rays中调用satnerf的inference函数

进入models/satnerf.pyinference函数:

def inference(model, args, rays_xyz, z_vals, rays_d=None, sun_d=None, rays_t=None):N_rays = rays_xyz.shape[0]N_samples = rays_xyz.shape[1]xyz_ = rays_xyz.view(-1, 3)  # 展平为[N_rays*N_samples, 3]# 处理额外输入rays_d_ = None if rays_d is None else torch.repeat_interleave(rays_d, repeats=N_samples, dim=0)sun_d_ = None if sun_d is None else torch.repeat_interleave(sun_d, repeats=N_samples, dim=0)rays_t_ = None if rays_t is None else torch.repeat_interleave(rays_t, repeats=N_samples, dim=0)# 分块运行NeRF模型chunk = args.chunkbatch_size = xyz_.shape[0]out_chunks = []for i in range(0, batch_size, chunk):out_chunks += [model(xyz_[i:i+chunk],input_dir=None if rays_d_ is None else rays_d_[i:i + chunk],input_sun_dir=None if sun_d_ is None else sun_d_[i:i + chunk],input_t=None if rays_t_ is None else rays_t_[i:i + chunk])]out = torch.cat(out_chunks, 0)# 处理输出out = out.view(N_rays, N_samples, model.number_of_outputs)rgbs = out[..., :3]       # 颜色sigmas = out[..., 3]      # 体密度# 其他输出处理...# 计算alpha合成权重deltas = z_vals[:, 1:] - z_vals[:, :-1]delta_inf = 1e10 * torch.ones_like(deltas[:, :1])deltas = torch.cat([deltas, delta_inf], -1)noise_std = args.noise_stdnoise = torch.randn(sigmas.shape, device=sigmas.device) * noise_stdalphas = 1 - torch.exp(-deltas * torch.relu(sigmas + noise))alphas_shifted = torch.cat([torch.ones_like(alphas[:, :1]), 1 - alphas + 1e-10], -1)transparency = torch.cumprod(alphas_shifted, -1)[:, :-1]weights = alphas * transparency# 计算深度depth_final = torch.sum(weights * z_vals, -1)  # 关键:计算加权平均深度# 组织返回结果result = {'rgb': rgb_final,'depth': depth_final,  # 这是最终的预测深度'weights': weights,# 其他输出...}return result

这个函数调用实际的NeRF模型,处理采样点,并生成体密度和颜色,最后计算深度。

5.inference调用SatNeRF模型

进入satnerf.py中的SatNeRF类的forward方法:

def forward(self, input_xyz, input_dir=None, input_sun_dir=None, input_t=None, sigma_only=False):# 将输入坐标送入映射层input_xyz = self.mapping[0](input_xyz)# 通过全连接网络处理xyz_ = input_xyzfor i in range(self.layers):if i in self.skips:xyz_ = torch.cat([input_xyz, xyz_], -1)xyz_ = self.fc_net[2*i](xyz_)xyz_ = self.fc_net[2*i + 1](xyz_)# 获取共享特征shared_features = xyz_# 预测体密度(sigma)sigma = self.sigma_from_xyz(shared_features)if sigma_only:return sigma# 预测颜色和其他属性xyz_features = self.feats_from_xyz(shared_features)# RGB预测...# 太阳可见度和天空颜色预测...# 预测不确定度参数betainput_for_beta = torch.cat([xyz_features, input_t], -1)beta = self.beta_from_xyz(input_for_beta)# 组合所有输出out = torch.cat([rgb, sigma, sun_v, sky_color, beta], 1)return out

这个神经网络预测每个点的体密度、颜色和其他属性。

6. 从体密度到深度的转换

回到inference函数中,关键的深度计算步骤:

# 计算alpha合成权重
alphas = 1 - torch.exp(-deltas * torch.relu(sigmas + noise))
# ...处理alphas...
weights = alphas * transparency  # 体密度权重# 预测深度:沿射线的加权平均深度
depth_final = torch.sum(weights * z_vals, -1)  # [N_rays]

体密度通过alpha合成转换为权重,然后用这些权重计算加权平均深度。

7. 结果整合与返回

最终在render_rays函数中:

result_ = {}
for k in result.keys():result_[f"{k}_coarse"] = result[k]# 深度结果作为'depth_coarse'返回

这就形成了tmp中的'depth_coarse'键,即粗略深度预测。

完整数据流向总结

  1. 初始数据: batch["depth"]["rays"]batch["depth"]["ts"] (来自数据加载器)
  2. NeRF_pl.forward: 将射线分块并调用render_rays
  3. render_rays: 处理射线,生成采样点,调用satnerf.inference
  4. satnerf.inference:
    • 处理采样点
    • 调用SatNeRF模型预测体密度
    • 将体密度转换为权重
    • 计算加权平均深度
  5. SatNeRF.forward: 神经网络计算每个点的体密度
  6. 结果整合: 深度值被组织为'depth_coarse'键返回

这就是tmp中预测深度的完整产生过程,从最初的射线输入到最终的深度预测值。

SatNeRF中真实深度(kp_depths)的完整产生过程

下面我详细梳理从原始数据到最终在training_step中使用的kp_depths的完整生成过程,按照数据流向逐步说明:

1. 原始数据:卫星图像和元数据

最初的原始数据包括:

  • 卫星图像(RGB图像)
  • JSON元数据文件,包含相机参数(RPC)和关键点信息

这些数据存储在args.root_dirargs.img_dir指定的目录中。

2. 数据集创建:load_dataset函数

prepare_data方法中通过调用load_dataset函数加载数据:

# main.py中的prepare_data方法
def prepare_data(self):self.train_dataset = [] + load_dataset(self.args, split="train")self.val_dataset = [] + load_dataset(self.args, split="val")

load_dataset函数(在datasets/init.py中)会根据数据类型创建不同的数据集:

def load_dataset(args, split):if args.data == 'sat':ds_list = []from .satellite import SatelliteDatasetds_list.append(SatelliteDataset(args.root_dir, args.img_dir, split, img_downscale=args.img_downscale, cache_dir=args.cache_dir))if split == 'train' and args.ds_lambda > 0:from .satellite_depth import SatelliteDataset_depthds_list.append(SatelliteDataset_depth(args.root_dir, args.img_dir, split, img_downscale=args.img_downscale, cache_dir=args.cache_dir))return ds_list# ...

args.ds_lambda > 0时,会创建SatelliteDataset_depth类的实例作为深度数据集。

3. 深度数据集初始化:SatelliteDataset_depth

SatelliteDataset_depth__init__方法中处理关键点数据:

# datasets/satellite_depth.py
def __init__(self, root_dir, img_dir, split='train', img_downscale=1.0, cache_dir=None):super().__init__(root_dir, img_dir, split, img_downscale, cache_dir)

4. 如果split == “train”,调用load_train_split()方法

tie_points是通过多视图几何(MVG)从多张卫星图像中三角测量得到的3D点坐标。

def load_train_split(self):with open(os.path.join(self.json_dir, "train.txt"), "r") as f:json_files = f.read().split("\n")self.json_files = [os.path.join(self.json_dir, json_p) for json_p in json_files]if os.path.exists(self.json_dir + "/pts3d.npy"):self.tie_points = np.load(self.json_dir + "/pts3d.npy")self.all_rays, self.all_depths, self.all_ids = self.load_depth_data(self.json_files, self.tie_points, verbose=True)else:raise FileNotFoundError("Could not find {}".format(self.json_dir + "/pts3d.npy"))

读取训练图像列表
加载3D关键点(tie_points)
调用load_depth_data方法处理深度数据

5.从4中调用load_depth_data处理深度数据

load_depth_data方法是生成真实深度数据的核心:

def load_depth_data(self, json_files, tie_points, verbose=False):all_rays, all_depths, all_sun_dirs, all_weights = [], [], [], []all_ids = []kp_weights = self.load_keypoint_weights_for_depth_supervision(json_files, tie_points)for t, json_p in enumerate(json_files):# 读取JSON数据d = sat_utils.read_dict_from_json(json_p)img_id = sat_utils.get_file_id(d["img"])# 获取关键点信息pts2d = np.array(d["keypoints"]["2d_coordinates"])/ self.img_downscalepts3d = np.array(tie_points[d["keypoints"]["pts3d_indices"], :])rpc = sat_utils.rescale_rpc(rpcm.RPCModel(d["rpc"], dict_format="rpcm"), 1.0 / self.img_downscale)# 生成射线cols, rows = pts2d.Tmin_alt, max_alt = float(d["min_alt"]), float(d["max_alt"])rays = get_rays(cols, rows, rpc, min_alt, max_alt)rays = self.normalize_rays(rays)all_rays += [rays]# 获取太阳方向sun_dirs = self.get_sun_dirs(float(d["sun_elevation"]), float(d["sun_azimuth"]), rays.shape[0])all_sun_dirs += [sun_dirs]# 标准化3D坐标pts3d = torch.from_numpy(pts3d).type(torch.FloatTensor)pts3d[:, 0] -= self.center[0]pts3d[:, 1] -= self.center[1]pts3d[:, 2] -= self.center[2]pts3d[:, 0] /= self.rangepts3d[:, 1] /= self.rangepts3d[:, 2] /= self.range# 计算深度值depths = torch.linalg.norm(pts3d - rays[:, :3], axis=1)all_depths += [depths[:, np.newaxis]]# 获取权重current_weights = torch.from_numpy(kp_weights[d["keypoints"]["pts3d_indices"]]).type(torch.FloatTensor)all_weights += [current_weights[:, np.newaxis]]all_ids += [t * torch.ones(rays.shape[0], 1)]# 组合所有数据all_ids = torch.cat(all_ids, 0)all_rays = torch.cat(all_rays, 0)  # (len(json_files)*h*w, 8)all_depths = torch.cat(all_depths, 0)  # (len(json_files)*h*w, 1)all_weights = torch.cat(all_weights, 0)all_depths = torch.hstack([all_depths, all_weights])  # 深度和权重合并all_sun_dirs = torch.cat(all_sun_dirs, 0)  # (len(json_files)*h*w, 3)all_rays = torch.hstack([all_rays, all_sun_dirs])  # (len(json_files)*h*w, 11)return all_rays, all_depths, all_ids

该方法处理每个JSON文件的关键点,计算射线和深度值,并整合成训练数据。

6. 在5中调用了计算关键点权重:load_keypoint_weights_for_depth_supervision方法

def load_keypoint_weights_for_depth_supervision(self, json_files, tie_points):# 初始化权重数组kp_weights = np.ones(len(tie_points))# 收集所有关键点的2D-3D对应关系all_obs = {}for json_p in json_files:with open(json_p) as f:d = json.load(f)if "keypoints" in d.keys():# 获取RPC模型rpc = rpcm.RPCModel(d["rpc"], dict_format="rpcm")# 获取2D坐标和对应的3D索引pts2d = np.array(d["keypoints"]["2d_coordinates"]) / self.img_downscalepts3d_indices = d["keypoints"]["pts3d_indices"]# 收集观察for i, idx in enumerate(pts3d_indices):if idx not in all_obs:all_obs[idx] = []all_obs[idx].append((json_p, pts2d[i], rpc))# 计算每个3D点的重投影误差for idx, obs_list in all_obs.items():if len(obs_list) >= 2:  # 至少需要2个观察# 计算重投影误差reproj_err = compute_reprojection_error(tie_points[idx], obs_list)# 根据重投影误差设置权重kp_weights[idx] = compute_keypoint_weight(reproj_err)return kp_weights

这个方法计算每个3D关键点的权重,基于其重投影误差。重投影误差越小,权重越大。

7. 数据集的__getitem__方法:准备批次数据

__getitem__方法定义了如何访问数据集中的一个样本:

def __getitem__(self, idx):# 获取训练样本if self.train:sample = {"rays": self.all_rays[idx], "depths": self.all_depths[idx], "ts": self.all_ids[idx].long()}else:# 验证集处理...return sample

对于训练集,直接返回预先计算好的射线、深度和时间戳信息。
如果把数据集想象成一本书:

__init__相当于准备整本书和目录
__len__告诉你书有多少页
__getitem__允许你翻到任意一页并读取内容

DataLoader就像是一个阅读助手,它会按照你指定的顺序(随机或顺序)一次翻几页(批次大小)给你看。

8. 数据加载器:train_dataloader方法

main.pytrain_dataloader方法中创建数据加载器:

def train_dataloader(self):a = DataLoader(self.train_dataset[0],shuffle=True,num_workers=4,batch_size=self.args.batch_size,pin_memory=True)loaders = {"color": a}if self.depth:b = DataLoader(self.train_dataset[1],shuffle=True,num_workers=4,batch_size=self.args.batch_size,pin_memory=True)loaders["depth"] = breturn loaders

这个方法创建数据加载器,将SatelliteDataset_depth的数据作为loaders["depth"]返回。

9. 训练步骤:training_step方法

最后,在training_step方法中获取真实深度:

def training_step(self, batch, batch_nb):# ...if self.depth:tmp = self(batch["depth"]["rays"], batch["depth"]["ts"].squeeze())# 获取真实深度数据kp_depths = torch.flatten(batch["depth"]["depths"][:, 0])  # 第一列是深度值kp_weights = 1. if self.args.ds_noweights else torch.flatten(batch["depth"]["depths"][:, 1])  # 第二列是权重# 计算深度损失loss_depth, tmp = self.depth_loss(tmp, kp_depths, kp_weights)# ...

batch["depth"]["depths"][:, 0]就是我们要找的真实深度值kp_depths

完整数据流向总结

初始数据:3D关键点坐标,存储在/pts3d.npy文件中
数据加载:load_train_split方法加载关键点并调用load_depth_data
深度计算:

从JSON文件读取2D对应关系和相机参数
计算相机射线
标准化3D点坐标
计算3D点到射线原点的距离作为深度值

权重计算:基于重投影误差计算每个点的可靠性权重
数据组织:将深度值和权重组合成all_depths张量
批次访问:通过__getitem__方法访问预计算的深度数据
训练使用:在training_step中,kp_depths从batch[“depth”][“depths”][:, 0]提取

回到training_step()

经过

        if self.depth:tmp = self(batch["depth"]["rays"], batch["depth"]["ts"].squeeze())kp_depths = torch.flatten(batch["depth"]["depths"][:, 0])kp_weights = 1. if self.args.ds_noweights else torch.flatten(batch["depth"]["depths"][:, 1])loss_depth, tmp = self.depth_loss(tmp, kp_depths, kp_weights)

就已经拿到了最后的损失了。

验证步骤: 定期调用val_dataloader()加载验证数据,并执行validation_step()

前向传播:

在training_step()和validation_step()中,代码调用了self(rays, ts)
这实际上是隐式调用了forward()方法,因为在Python中,当一个类实例被当作函数调用时,会自动调用其__call__方法,而Lightning模型的__call__会调用forward()

钩子函数:

Lightning通过一系列"钩子函数"(如training_step, validation_step等)自动组织训练流程
只需实现这些钩子函数,而不需要手动调用它们

相关文章:

Sat- nerf深度损失

首先损失函数定义在metrics.py,代码如下: class DepthLoss(torch.nn.Module):def __init__(self, lambda_ds1.0):super().__init__()# 初始化lambda_ds参数&#xff0c;用于调节深度损失的权重&#xff0c;并且将其缩小为原来的1/3self.lambda_ds lambda_ds / 3.# 初始化均方…...

c++的多态

1.多态的概念 多态&#xff0c;通俗来说&#xff0c;就是多种形态 多态分为编译时多态(静态多态)和运⾏时多 态(动态多态) 静态多态主要是函数重载和函数模板&#xff0c;它们传不同类型的参数就可以调⽤不同的函数&#xff0c;通过参数不同达到多种形态&#xff0c;之所以叫…...

基于 Rust 与 GBT32960 规范构建高并发、高可用、高扩展服务端程序

一、需求背景 如今&#xff0c;数字化发展特别快&#xff0c;各种设备和系统之间要频繁地交换数据&#xff0c;而且这个过程变得越来越复杂。很多行业都有难题&#xff0c;既要处理大量的数据&#xff0c;又得快速响应各种命令。比如说在智能交通这一块&#xff0c;路上跑的车…...

《宝塔 Nginx SSL 端口管理实战指南:域名解析、端口冲突与后端代理解析》

&#x1f4e2; Nginx & SSL 端口管理分析 1️⃣ 域名解析与 SSL 申请失败分析 在使用宝塔申请 www.mywebsite.test 的 SSL 证书时&#xff0c;遇到了解析失败的问题。最初&#xff0c;我认为 www 只是一个附加的前缀&#xff0c;不属于域名的关键部分&#xff0c;因此只为…...

iOS 实现UIButton自动化点击埋点

思路&#xff1a;我们HOOK UIControl的 addtarget:action:forControlEvents方法&#xff0c;交换UIControl的 addtarget:action:forControlEvents 方法的实现&#xff0c; 在交换的方法中添加原来响应的同时&#xff0c;再添加一个埋点响应&#xff0c;该响应方法实现了点击埋点…...

Java 并行流(Parallel Stream)详解

并行流是Java 8引入的高效处理集合数据的工具&#xff0c;通过多线程加速计算。以下是其核心概念、使用方法及注意事项的详细指南&#xff1a; 1. 核心概念与原理 并行处理机制&#xff1a;将数据分割为多个块&#xff0c;利用Fork/Join框架在多个线程上并行处理&#xff0c;…...

开源软件的版权保护措施

开源软件的版权保护措施主要有以下几方面&#xff1a; 著作权保护 明确版权归属与许可使用&#xff1a;开源软件的源代码是著作权法保护的对象&#xff0c;作者享有复制权、发行权、改编权等专有权益。通过开源协议&#xff0c;作者明确授权用户使用、复制和修改软件&#xf…...

11.24 SpringMVC(1)@RequestMapping、@RestController、@RequestParam

一.RequestMapping("/user")//HTTP 请求方法既支持get也支持post&#xff0c;可表示为类路径与方法路径 二.RequestMapping(value "/m7", method {RequestMethod.POST, RequestMethod.GET}) value这个参数指定了请求的 URL 路径。method 参数指定了允许…...

杰和科技GDSM-C数字化信息发布管理系统,信息触达无死角,更全面

在数字化时代&#xff0c;信息的高效传递与精准管理成为商业、教育、公共服务等领域的核心需求。传统信息发布模式常面临设备分散难管控、内容更新滞后、多屏协同效率低等问题。 杰和科技为此开发了一套数字化信息发布管理系统GDSM-C&#xff08;简称 GDSM-C&#xff09;系统&a…...

如何停止Oracle expdp/impdp job

一、停止 expdp job举例 1.执行 expdp 命令 $ expdp rui/rui DIRECTORYdmp_dir dumpfilestudyfull_expdp.dmp FULLy logfilestudyfullexpdp.log job_nameexpdp_job2.查看在运行的作业名称 SQL> select job_name,state from dba_datapump_jobs; JOB_NAME …...

Java 8 中,可以使用 Stream API 和 Comparator 对 List 按照元素对象的时间字段进行倒序排序

文章目录 引言I 示例对象II List 按时间字段倒序排序: 使用 `Stream` 和 `Comparator` 排序方法 1:使用 `Comparator.comparing`方法 2:使用 `Comparator.reversed`方法 3:自定义 `Comparator`输出结果III 注意事项**时间字段类型**:**空值处理**:IV 总结引言 案例:在线用…...

MySQL零基础教程14—子查询

子查询比较简单&#xff0c;我们还是通过案例引入。 有时候我们查询的时候&#xff0c;需要用到的不止一个表的数据&#xff0c;比如下面的场景&#xff1a; 查询名字叫李晓红同学的班主任姓名 我们提供三个表的基础信息如下&#xff1a; 从三张表的结构&#xff0c;我们不难…...

考研408数据结构线性表核心知识点与易错点详解(附真题示例与避坑指南)

一、线性表基础概念 1.1 定义与分类 定义&#xff1a;线性表是由n&#xff08;n≥0&#xff09;个相同类型数据元素构成的有限序列&#xff0c;元素间呈线性关系。 分类&#xff1a; 顺序表&#xff1a;元素按逻辑顺序存储在一段连续的物理空间中&#xff08;数组实现&…...

Microk8s Ingress实现七层负载均衡

Microk8s Ingress是什么 Ingress是k8s的一种资源对象&#xff0c;用于管理外部对集群内服务的访问, 它通过提供一个统一的入口点&#xff0c;将外部流量路由到集群内部的不同服务。 Microk8s Ingress用于解决什么问题 k8s集群中服务默认只能在集群内访问。 如果需要从外部访…...

部署Windows Server自带“工作文件夹”实现企业网盘功能完整步骤

前文已经讲解过Windows Server自带的“工作文件夹”功能&#xff0c;现以Windows Server 2025为例介绍部署工作文件夹的完整步骤&#xff1a; 为了确保您能够顺利部署和充分利用工作文件夹的功能&#xff0c;我将按照以下步骤进行讲解。 请注意&#xff0c;在域环境中部署工作…...

前缀和算法 算法4

算法题中帮助复习的知识 vector<int > dp( n ,k); n为数组大小 ,k为初始化 哈希表unordered_map<int ,int > hash; hash.find(k)返回值是迭代器 ,找到k返回其迭代器 没找到返回hash.end() hash.count(k)返回值是数字 ,找到k返回1 ,没找到返回0. C和java中 负数…...

Excel 豆知识 - XLOOKUP 为啥会出 #N/A 错误

XLOOKUP有的时候会出 #VALUE! 这个错误。 因为这个XLOOUP有个参数叫 找不到时的返回值&#xff0c;那么为啥还会返回 #VALUE! 呢&#xff1f; 可能还有别的原因&#xff0c;但是主要原因应该就是 检索范围 和 返回范围 不同。 比如这里检索范围在 B列&#xff0c;是 4-21&…...

ZK Rollup

ZK Rollup 通过生成零知识证明来确保所有提交的交易都是有效的。生成零知识证明的过程涉及复杂的密码学运算&#xff0c;通常使用的是 zk-SNARK&#xff08;零知识简洁非互动知识论证&#xff09;或 zk-STARK&#xff08;零知识可扩展透明知识论证&#xff09;。以下是 ZK Roll…...

UI设计——新拟态手机主题锁屏设计分享

新拟态手机主题锁屏设计分享 给大家展示一款新式手机主题锁屏设计作品。 整体设计采用简洁的灰白主色调&#xff0c;搭配亮眼的橙色元素&#xff0c;形成鲜明对比&#xff0c;视觉效果清爽又不失活力。 上方显示大数字时钟 “20:36”&#xff0c;日期 “04/11 星期一” 以及天…...

Kafka面试题及原理

1. 消息可靠性&#xff08;不丢失&#xff09; 使用Kafka在消息的收发过程都会出现消息丢失&#xff0c;Kafka分别给出了解决方案 生产者发送消息到Brocker丢失消息在Brocker中存储丢失消费者从Brocker 幂等方案&#xff1a;【分布式锁、数据库锁&#xff08;悲观锁、乐观锁…...

leetcode 238. 除自身以外数组的乘积

题目如下 数据范围 使用两个辅助数组分别存从前乘到后面和从后到前后面再计算就行。 &#xff08;f数组没处理好还包含了本不能乘于的数所以要向后移动一位&#xff09;。通过代码 class Solution { public:vector<int> productExceptSelf(vector<int>& n…...

DeepSeek 与 ChatGPT 终极对决:谁才是 AI 语言之王?

我的个人主页 我的专栏&#xff1a;人工智能领域、java-数据结构、Javase、C语言&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞&#x1f44d;收藏❤ 引言 在当今科技飞速发展的时代&#xff0c;人工智能已然成为推动各领域变革的核心力量&#xff…...

python爬虫:pyspider的详细使用

文章目录 一、pyspider介绍1.1 核心概念1.2 与其他爬虫框架的比较二、 安装 pyspider三、编写爬虫脚本四、运行和监控爬虫4.1 启动爬虫4.2 监控任务状态4.3 任务管理五、高级功能5.1 分布式爬取5.2 JavaScript 渲染5.3 数据存储5.4 定时任务5.5 错误处理和重试机制六、示例:采…...

CSS—text文本、font字体、列表list、表格table、表单input、下拉菜单select

目录 1.文本 2.字体 3.列表list a.无序列表 b.有序列表 c.定义列表 4.表格table a.内容 b.合并单元格 3.表单input a.input标签 b.单选框 c.上传文件 4.下拉菜单 1.文本 属性描述color设置文本颜色。direction指定文本的方向 / 书写方向。letter-spacing设置字符…...

宝塔webhooks与码云实现自动部署

1. 宝塔面板配置Webhook 登录宝塔面板&#xff0c;进入「软件商店」→ 搜索「Webhook」并安装。添加Webhook&#xff1a; 名称&#xff1a;自定义&#xff08;如 Gitee自动部署&#xff09;脚本&#xff1a;编写部署脚本&#xff0c;示例如下&#xff1a;#!/bin/bash# 项目路径…...

迷你世界脚本聊天接口:Chat

聊天接口&#xff1a;Chat 彼得兔 更新时间: 2023-04-26 10:18:43 具体函数名及描述如下: 序号 函数名 函数描述 1 sendChat(...) 发送聊天消息(默认全部玩家) 2 sendSystemMsg(...) 发送系统消息(默认全部玩家) sendChat 参数及类型&#xff1a; content:s…...

Yocto + 树莓派摄像头驱动完整指南

—— 从驱动配置、Yocto 构建&#xff0c;到 OpenCV 实战 在树莓派上运行摄像头&#xff0c;在官方的 Raspberry Pi OS 可能很简单&#xff0c;但在 Yocto 项目中&#xff0c;需要手动配置驱动、设备树、软件依赖 才能确保摄像头正常工作。本篇文章从 BSP 驱动配置、Yocto 关键…...

多镜头视频生成、机器人抓取、扩散模型个性化 | Big Model weekly第58期

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 01 GLM-4-Voice: Towards Intelligent and Human-Like End-to-End Spoken Chatbot 本文介绍了一种名为GLM-4-Voice的智能且类人化的端到端语音聊天机器人。它支持中文和英文&#xff0c;能够进行实时语音对话&a…...

Llama 2中的Margin Loss:为何更高的Margin导致更大的Loss和梯度?

Llama 2中的Margin Loss&#xff1a;为何更高的Margin导致更大的Loss和梯度&#xff1f; 在《Llama 2: Open Foundation and Fine-Tuned Chat Models》论文中&#xff0c;作者在强化学习与人类反馈&#xff08;RLHF&#xff09;的Reward Model训练中引入了Margin Loss的概念&a…...

边缘计算收益低的三大指标

边缘计算收益低的三大指标主要包括以下方面&#xff1a; 1. 资源贡献不足&#xff1a; 边缘计算的收益通常基于所提供的带宽、存储和计算资源来计算。如果设备的网络带宽有限、在线时间短或提供的存储容量较小&#xff0c;可能无法满足平台设定的最低贡献标准&#xff0c;从而导…...

基于单片机的智能宿舍管理系统(论文+源码)

2.1总体方案设计 本课题为智能宿舍的设计&#xff0c;整个系统架构如图2.1所示&#xff0c;整个系统在器件上包括了主控制器STM32单片机&#xff0c;LD3320语音识别模块&#xff0c;按键模块&#xff0c;串口通信模块&#xff0c;照明模块&#xff0c;窗帘控制模块家电控制模块…...

(下:补充——五个模型的理论基础)深度学习——图像分类篇章

目录 1.1 卷积神经网络基础 3.1 AlexNet网络结构详解与花分类数据集下载 4.1 VGG网络详解及感受野的计算 5.1 GoogLeNet网络详解 6.1 ResNet网络结构&#xff0c;BN以及迁移学习详解 总结&#xff08;可以直接看总结&#xff09; 1.1 卷积神经网络基础 视频讲解&#xf…...

SVN 简介

SVN 简介 引言 版本控制系统(Version Control System,VCS)是软件开发过程中不可或缺的工具之一。它能够帮助开发者管理代码的版本,追踪代码变更,协同工作,以及确保代码的稳定性和安全性。Subversion(简称SVN)是一种流行的版本控制系统,本文将为您详细介绍SVN的基本概…...

【前端场景题】如何应对页面请求接口的大规模并发问题

如何应对页面请求接口的大规模并发问题&#xff0c;尤其是前端方面的解决方案&#xff0c;并且需要给出详细的代码解释。首先&#xff0c;我需要仔细阅读我搜索到的资料&#xff0c;找出相关的信息&#xff0c;然后综合这些信息来形成答案。 首先看&#xff0c;它提到前端优化策…...

Kafka 为什么会消息堆积?

Kafka 定期清理 Partition&#xff0c;但消息堆积&#xff08;backlog&#xff09; 依然可能发生&#xff0c;主要是因为 Kafka 的清理机制和消息消费进度是两回事。我们可以用一个 快递仓库 的类比来解释。 类比&#xff1a;Kafka 就像一个快递仓库 生产者&#xff08;Produc…...

毕业项目推荐:基于yolov8/yolo11的苹果叶片病害检测识别系统(python+卷积神经网络)

文章目录 概要一、整体资源介绍技术要点功能展示&#xff1a;功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出&#xff08;xls格式&#xff09;功能6 支持切换检测到的目标查看 二、数据集三、算法介绍1. YO…...

十四届蓝桥杯JAVA-b组-合并石子

点我写题 思路&#xff1a;区间dp和缝合dp板子题&#xff0c;先用个dp[i][j][k]表示考虑区间[i,j]合并成颜色k的最小代价&#xff0c;然后用min[i][j]存一下[i,j]区间合并的最小代价&#xff0c;即min(dp[i][j][0-2])&#xff0c;has[i][j]表示区间[i,j]是否能合并&#xff0c…...

【Maven】入门介绍 与 安装、配置

文章目录 一、Maven简介1. Maven介绍2. Maven软件工作原理模型图 二、Maven安装和配置1. Maven安装2. Maven环境配置3. Maven功能配置4. IDEA配置本地Maven软件 一、Maven简介 1. Maven介绍 https://maven.apache.org/what-is-maven.html Maven 是一款为 Java 项目管理构建、…...

物联网小范围高精度GPS使用

在园区内实现小范围高精度GPS&#xff08;全球定位系统&#xff09;定位&#xff0c;通常需要结合多种技术来弥补传统GPS在精度和覆盖范围上的不足。以下是实现小范围高精度GPS定位的解决方案&#xff0c;包括技术选择、系统设计和应用场景。 一、技术选择 在园区内实现高精度…...

突破Ajax跨域困境,解锁前端通信新姿势

一、引言 在当今的 Web 开发领域&#xff0c;前后端分离的架构模式已经成为主流&#xff0c;它极大地提升了开发效率和项目的可维护性。在这种开发模式下&#xff0c;前端通过 Ajax 技术与后端进行数据交互&#xff0c;然而&#xff0c;跨域问题却如影随形&#xff0c;成为了开…...

Docker 学习(一)

一、Docker 核心概念 Docker 是一个开源的容器化平台&#xff0c;允许开发者将应用及其所有依赖&#xff08;代码、运行时、系统工具、库等&#xff09;打包成一个轻量级、可移植的“容器”&#xff0c;实现 “一次构建&#xff0c;随处运行”。 1、容器&#xff08;Container…...

【漫话机器学习系列】111.指数之和的对数(Log-Sum-Exp)

在计算机科学和机器学习中&#xff0c;经常会遇到计算指数和的对数的情况&#xff0c;例如&#xff1a; 然而&#xff0c;由于指数函数 的值增长极快&#xff0c;直接计算可能会导致数值上溢&#xff08;overflow&#xff09;或下溢&#xff08;underflow&#xff09;&#xf…...

算法004——盛最多水的容器

力扣——盛最多水的容器点击即可跳转 当我们选择1号线和8号线时&#xff0c;下标为 1 和 8 形成容器的容积的高度是由 较矮的决定的&#xff0c;即下标为 8 的位置&#xff1b; 而宽度则是 1到8 之间的距离&#xff0c;为 8-17&#xff0c;此时容器的容积为 7 * 7 49。 当我…...

前端内存泄漏的几种情况及方案

前端内存泄漏是常见但容易被忽视的问题&#xff0c;可能导致页面卡顿、崩溃或性能下降。以下是几种典型场景及解决方案&#xff1a; 1. 未清理的全局变量 场景&#xff1a; 意外创建全局变量&#xff08;未使用 var/let/const&#xff09;。主动挂载到 window 的大对象未释放…...

14. LangChain项目实战1——基于公司制度RAG回答机器人

教学视频&#xff1a; 12. 基于Gradio搭建基于公司制度RAG_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV11VXRYTErZ/ 环境配置&#xff1a; python版本&#xff1a;3.10.8 服务器&#xff1a;Ubuntu 依赖包requirements.txt文件内容&#xff1a; aiofiles23.2.1 …...

解锁 indexOf、substring 和 JSON.stringify:从小程序图片上传看字符串魔法 ✨

&#x1f31f; 解锁 indexOf、substring 和 JSON.stringify&#xff1a;从小程序图片上传看字符串魔法 ✨ 在 JavaScript 中&#xff0c;字符串操作和数据序列化是开发中不可或缺的技能。indexOf、substring 和 JSON.stringify 是三个简单却强大的工具&#xff0c;分别用于定位…...

Git快速入门

文章目录 Git简介准备工作常用的Linux命令git配置 git工作原理git项目创建和克隆git基本操作命令git忽略文件配置ssh远程连接 IDEA集成Gitgit分支&#xff08;多人开发&#xff09;公司中用到的&#xff08;很清楚&#xff09; Git 简介 Git就是版本控制的工具 下面这个叫手动…...

老牌工具,16年依然抗打!

在电脑还没普及、操作系统为Windows XP/7的时代&#xff0c;多媒体文件的转换操作常常面临格式不兼容的问题。这时一款名为格式工厂的软件成为了众多用户的首选工具。格式工厂以其简洁易用的界面和强大的功能&#xff0c;轻松地进行各种文件格式的转换。成为很多修小伙伴的喜爱…...

JavaScript 进阶A(作用域、闭包、变量和函数提升、函数相关只是、数组解构、对象解构、构造函数

1.作用域 作用域主要分为&#xff1a;局部作用域和全局作用域。 局部作用域又分为&#xff1a;函数作用域和块作用域 函数作用域&#xff1a;在函数中定义的变量只能在函数内部使用&#xff0c;外部无法访问块作用域&#xff1a;被大括号{}包起来的代码块&#xff0c;在这个…...

《深度剖析:特征工程—机器学习的隐秘基石》

在机器学习的宏大版图中&#xff0c;特征工程宛如一座隐藏在幕后却又至关重要的基石。它默默发挥着作用&#xff0c;将原始数据雕琢成模型能够有效学习和理解的形态&#xff0c;深刻影响着机器学习模型的性能与表现。 特征工程&#xff1a;机器学习的关键前奏 特征工程是运用…...