Pytorch分布式訓練,數據并行,單機多卡,多機多卡

分布式訓練

所有代碼可以見我github 倉庫:https://github.com/xiejialong/ddp_learning.git

數據并行(Data Parallelism,DP)

跨多個gpu訓練模型的最簡單方法是使用 torch.nn.DataParallel. 在這種方法中,模型被復制到所有可用的GPU上,并且所有進程都由第一個GPU(也稱為主進程)管理。該方法將輸入拆分到gpu上,并行計算梯度,并在主進程上更新模型參數之前對它們進行平均。更新后,主進程將更新后的參數廣播給所有其他gpu。

DataParallel并不推薦,有以下原因:

  • 額外開支較大:雖然它很容易使用,但它有一些通信開銷,因為要等待所有gpu完成反向傳播、收集梯度并廣播更新的參數。為了獲得更好的性能,特別是在擴展到多個節點時,請使用分布式數據并行DistributedDataParallel(DDP)
  • 顯存占用大:主GPU的內存使用率比其他GPU高,因為它收集了其他GPU的所有梯度。因此,如果您在單個GPU上已經存在內存問題,那么dataparlil將使其變得更糟。

注意,dataparllel在反向傳播后平均gpu之間的梯度。確保相應地縮放學習率(乘以gpu的數量)以保持相同的有效學習率。這同樣適用于批處理大小,提供給數據加載器的批處理大小在gpu上進行劃分<

例子:

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset
import osclass MyModel(nn.Module): # 模型定義def __init__(self):super().__init__()self.net = nn.Sequential(nn.Linear(10, 10000), nn.Linear(10000, 5000),nn.Linear(5000, 2))def forward(self, x):return self.net(x)class MyData(Dataset): # 數據集定義def __init__(self):super().__init__()self.data_x = torch.concat([torch.rand(size=(10000, 10)) + torch.zeros(size=(10000, 10)), torch.rand(size=(10000, 10)) + torch.ones(size=(10000, 10))], dim=0)self.data_y = torch.concat([torch.zeros(size=(10000, ), dtype=torch.long), torch.ones(size=(10000, ), dtype=torch.long)], dim=0)def __getitem__(self, index):x = self.data_x[index]y = self.data_y[index]return x, ydef __len__(self):return len(self.data_x)train_data = MyData()  # 實例化數據集
train_loader = DataLoader(dataset=train_data, batch_size=64, shuffle=True)
model = MyModel() # 實例化模型
if torch.cuda.device_count() > 1:model = nn.DataParallel(model) 
model = model.cuda()optimizer = optim.Adam(model.parameters(), lr=0.0001) # 定義優化器
criterion = nn.CrossEntropyLoss() # 定義評價器
print(len(train_loader))
for data, target in train_loader:data, target = data.cuda(), target.cuda() # 數據放入顯卡optimizer.zero_grad() # 梯度歸零output = model(data) # 模型推理loss = criterion(output, target) # 計算lossloss.backward() # 反向傳播梯度optimizer.step() # 模型參數更新print(loss.item())
分布式數據并行(Distributed Data Parallelism, DDP)

為了獲得更好的性能,PyTorch提供了torch.nn.parallel.distributedDataParallel(DDP),它對于多gpu訓練更有效,特別是對于多節點設置。事實上,當使用DDP時,訓練代碼分別在每個GPU上執行,每個GPU直接與其他GPU通信,并且僅在必要時進行通信,從而減少了通信開銷。在DDP方法中,主進程的作用大大減少,每個GPU負責自己的向前和向后傳遞,以及參數更新。向前傳遞后,開始向后傳遞,每個GPU開始將自己的梯度發送給所有其他GPU,每個GPU接收所有其他GPU的梯度之和。這個過程被稱為all-reduce操作。之后,每個GPU都有完全相同的梯度,并更新其自己的模型副本的參數。Reduce:分布式計算中的一種常見操作,其中計算結果跨多個進程聚合。All -reduce意味著所有進程都調用Reduce操作來接收來自所有其他進程的結果。

基于torch.multiprocessing的啟動方式

啟動程序時不需要在命令行輸入額外的參數,寫起來也比較容易,但是調試較麻煩

import os
import torch
import torch.distributed as dist  # 分布式庫
import torch.multiprocessing as mp  # 多線程
from torch.utils.data import Dataset, DataLoader, DistributedSampler  # 數據集庫
import torch.nn as nn  # 網絡結構庫
import torch.optim as optim  # 優化器庫
from torch.amp import autocast, GradScaler  # 混合精度庫os.environ["CUDA_VISIBLE_DEVICES"]='2,3'scaler = GradScaler() # 自動縮放梯度class MyModel(nn.Module): # 模型定義def __init__(self):super().__init__()self.net = nn.Sequential(nn.Linear(10, 10000), nn.Linear(10000, 5000),nn.Linear(5000, 2))def forward(self, x):return self.net(x)class MyData(Dataset): # 數據集定義def __init__(self):super().__init__()self.data_x = torch.concat([torch.rand(size=(10000, 10)) + torch.zeros(size=(10000, 10)), torch.rand(size=(10000, 10)) + torch.ones(size=(10000, 10))], dim=0)self.data_y = torch.concat([torch.zeros(size=(10000, ), dtype=torch.long), torch.ones(size=(10000, ), dtype=torch.long)], dim=0)def __getitem__(self, index):x = self.data_x[index]y = self.data_y[index]return x, ydef __len__(self):return len(self.data_x)def worker(rank, world_size):dist.init_process_group("nccl", rank=rank, world_size=world_size) # 定義通信方式torch.cuda.set_device(rank) # 設置當前線程控制的GPUprint("init model")model = MyModel().cuda()print(f"init ddp rank {rank}")ddp_model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[rank]) traindata = MyData()train_sampler = DistributedSampler(dataset=traindata, shuffle=True) # 定義分布式數據采集器train_loader = DataLoader(traindata, batch_size=64,sampler=train_sampler, num_workers=4, pin_memory=True) # 定義數據加載器optimizer = optim.Adam(ddp_model.parameters(), lr=0.0001) # 定義優化器criterion = nn.CrossEntropyLoss() # 定義評價函數print("train")accumulation_steps = 4 # 設置梯度累計次數optimizer.zero_grad(set_to_none=True) # 重設梯度for batch_idx, (inp, target) in enumerate(train_loader):inp, target = inp.cuda(), target.cuda()with autocast(device_type="cuda"): # 開啟混合精度訓練output = ddp_model(inp)loss = criterion(output, target)loss = loss / accumulation_steps  # 歸一化損失scaler.scale(loss).backward() # 混合精度訓練下進行損失縮放并執行后向傳播if (batch_idx + 1) % accumulation_steps == 0:# optimizer.step()  # 更新權重scaler.step(optimizer) # 混合精度下的權重更新scaler.update()optimizer.zero_grad(set_to_none=True)  # 每次更新完進行梯度清零print(loss)dist.barrier()dist.destroy_process_group()if __name__ == "__main__":world_size = torch.cuda.device_count()mp.spawn(worker, nprocs=world_size, args=(world_size,))

啟動多GPU訓練的命令(高版本被移除rank):

python -m torch.distributed.launch --nproc_per_node=4 train.py

或使用torchrun

torchrun --nproc_per_node=4 train.py

基于torch.distributed的啟動方式

一個完整的訓練架構

參考:

https://blog.csdn.net/wxc971231/article/details/132827787

import os
import torch
import torch.nn as nn
import torch.optim as optim
import torch.distributed as dist
from torch.utils.data import Dataset, DataLoader, DistributedSampler
import argparse
import numpy as np
from tqdm import tqdm
os.environ["CUDA_VISIBLE_DEVICES"]="2,3"class MyModel(nn.Module): # 模型定義def __init__(self):super().__init__()self.net = nn.Sequential(nn.Linear(10, 10000), nn.Linear(10000, 5000),nn.Linear(5000, 2))def forward(self, x):return self.net(x)class MyData(Dataset): # 數據集定義def __init__(self):super().__init__()self.data_x = torch.concat([torch.rand(size=(10000, 10)) + torch.zeros(size=(10000, 10)), torch.rand(size=(10000, 10)) + torch.ones(size=(10000, 10))], dim=0)self.data_y = torch.concat([torch.zeros(size=(10000, ), dtype=torch.long), torch.ones(size=(10000, ), dtype=torch.long)], dim=0)def __getitem__(self, index):x = self.data_x[index]y = self.data_y[index]return x, ydef __len__(self):return len(self.data_x)def load_train_objs(ags):train_dataset = MyData() # 定義數據集train_sampler = DistributedSampler(train_dataset, num_replicas=ags.world_size, rank=ags.rank, shuffle=True)  # 將數據集進行均分train_loader = DataLoader(train_dataset, batch_size=args.batch_size, sampler=train_sampler, pin_memory=True) # 定義數據加載器model = MyModel() # 定義模型model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model)model.to(ags.device)ddp_model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[ags.local_rank]) # 把模型放入不同的gpureturn train_loader, ddp_modeldef init_ddp_env(args):# 分布式同行環境初始化dist.init_process_group(backend='nccl', init_method="env://")# 獲取全局/本地 rank、world_sizeargs.rank = int(os.environ.get("RANK", -1))args.local_rank = int(os.environ.get("LOCAL_RANK", -1))args.world_size = int(os.environ.get("WORLD_SIZE", -1))# 設置GPU顯卡綁定torch.cuda.set_device(args.local_rank)args.device = torch.device("cuda")# 打印綁定信息print(f"[RANK {args.rank} | LOCAL_RANK {args.local_rank}] Using CUDA device {torch.cuda.current_device()}: {torch.cuda.get_device_name(torch.cuda.current_device())} | World size: {args.world_size}")dist.barrier() # 等待所有進程都初始化完畢,即所有GPU都要運行到這一步以后再繼續class Trainer:def __init__(self, args, model: torch.nn.Module, train_loader: DataLoader, optimizer: torch.optim.Optimizer, criterion):self.model = modelself.train_loader = train_loaderself.optimizer = optimizerself.criterion = criterionself.device = args.deviceself.snapshot_path = args.snapshot_pathself.gpu_id = args.local_rankself.max_epochs = args.max_epochsself.save_every = args.save_everyself.epochs_run = 0if os.path.exists(args.resume_path):print('loading snapshot')self._load_snapshot(args.resume_path)def _load_snapshot(self, resume_path):''' 加載 snapshot 并重啟訓練 '''loc = f"cuda:{self.gpu_id}"snapshot = torch.load(resume_path, map_location=loc)self.model.load_state_dict(snapshot["MODEL_STATE"])self.epochs_run = snapshot["EPOCHS_RUN"]print(f"Resuming training from snapshot at Epoch {self.epochs_run}")def _run_batch(self, inp, targets):self.optimizer.zero_grad()output = self.model(inp)loss = self.criterion(output, targets)loss.backward()self.optimizer.step()return loss.item()def _run_epoch(self, epoch):epoch_losses = []self.train_loader.sampler.set_epoch(epoch)            # 設置 epoch 保證多 GPU 上數據不重疊for inp, targets in self.train_loader:inp = inp.to(self.device)targets = targets.to(self.device)loss = self._run_batch(inp, targets)epoch_losses.append(loss)return np.mean(epoch_losses)def _save_snapshot(self, epoch):# 在 snapshot 中保存恢復訓練所必須的參數snapshot = {"MODEL_STATE": self.model.state_dict(),  # 由于多了一層 DDP 包裝,通過 .module 獲取原始參數 "EPOCHS_RUN": epoch,}save_path = os.path.join(self.snapshot_path, f"epoch_{epoch}.pt")torch.save(snapshot, save_path)# print(f"Epoch {epoch} | Training snapshot saved at {save_path}")def train(self):# 現在從 self.epochs_run 開始訓練,統一重啟的情況with tqdm(total=self.max_epochs, desc=f"[GPU{self.gpu_id}] Training", position=self.gpu_id, initial=self.epochs_run) as pbar:for epoch in range(self.epochs_run, self.max_epochs + self.epochs_run):epoch_loss = self._run_epoch(epoch)                         # 各個 GPU 上都在跑一樣的訓練進程,這里指定 rank0 進程保存 snapshot 以免重復保存if self.gpu_id == 0 and epoch % self.save_every == 0:self._save_snapshot(epoch)pbar.set_postfix({'epoch': epoch, 'loss':'{:.2f}'.format(epoch_loss)})pbar.update()def worker(args):init_ddp_env(args)  # 初始化分布式環境train_loader, ddp_model = load_train_objs(args)  # 導入分布式數據導入器和模型optimizer = optim.Adam(ddp_model.parameters(), lr=args.lr)criterion = nn.CrossEntropyLoss()trainer = Trainer(args, ddp_model, train_loader, optimizer, criterion)trainer.train()if __name__=="__main__":import argparseparser = argparse.ArgumentParser(description='simple distributed training job')parser.add_argument('--rank', default=-1, type=int, help='Rank (default: -1)')parser.add_argument('--world_size', default=1, type=int, help='world_size (default: -1)')parser.add_argument('--local_rank', default=-1, type=int, help='local_rank (default: 1)')parser.add_argument('--device', default="cuda", type=str, help='local_rank (default: 1)')parser.add_argument('--batch_size', default=32, type=int, help='Input batch size on each device (default: 32)')parser.add_argument('--lr', default=0.001, type=float, help='Learing rate (default: 0.001)')parser.add_argument('--snapshot_path', default="checkpoints/", type=str, help='Path of checkpoints (default: checkpoints/)')parser.add_argument('--save_every', default=1, type=int, help='Frequence of checkpoint save')parser.add_argument('--max_epochs', default=5, type=int, help='Total epoch')parser.add_argument('--resume_path', default="checkpoints/epoch_2.pt", type=str, help='Path of resume file')args = parser.parse_args()worker(args)#  torchrun --nnodes=2 --nproc_per_node=2 --node_rank=0 --master_addr=xxx --master_port=xx xxx.py
# --nnodes: 表示參與訓練的總機器數
# --nproc_per_node:表示每臺機器上要啟動幾個訓練進程,一個進程對應一個 GPU,因通常設置為你機器上要用到的GPU數。整個分布式環境下,總訓練進程數 = nnodes * nproc_per_node
# --node_rank:表示當前機器是第幾臺機器,從 0 開始編號,必須每臺機器都不同
# --master_addr 和 --master_port:指定主節點的 IP 和端口,用于 rendezvous(進程對齊)和通信初始化,所有機器必須填寫相同的值!
多機多卡的啟動和bash

參考:https://cloud.tencent.com/developer/article/2514642

下面是一個簡單的分布式訓練代碼

import os
from time import sleep
import torch
import torch.distributed as dist
import torch.nn as nn
import torch.optim as optim
import datetimefrom torch.nn.parallel import DistributedDataParallel as DDPclass ToyModel(nn.Module):def __init__(self):super(ToyModel, self).__init__()self.net1 = nn.Linear(10, 10)self.relu = nn.ReLU()self.net2 = nn.Linear(10, 5)def forward(self, x):return self.net2(self.relu(self.net1(x)))def train():local_rank = int(os.environ["LOCAL_RANK"])rank = int(os.environ["RANK"])while True:print(f"[{os.getpid()}] (rank = {rank}, local_rank = {local_rank}) training...")model = ToyModel().cuda(local_rank)ddp_model = DDP(model, [local_rank])loss_fn = nn.MSELoss()optimizer = optim.SGD(ddp_model.parameters(), lr=0.001)optimizer.zero_grad()outputs = ddp_model(torch.randn(20, 10).to(local_rank))labels = torch.randn(20, 5).to(local_rank)loss = loss_fn(outputs, labels)loss.backward()print(f"[{os.getpid()}] (rank = {rank}, local_rank = {local_rank}) loss = {loss.item()}\n")optimizer.step()sleep(1)def run():env_dict = {key: os.environ[key]for key in ("MASTER_ADDR", "MASTER_PORT", "WORLD_SIZE", "LOCAL_WORLD_SIZE")}print(f"[{os.getpid()}] Initializing process group with: {env_dict}")dist.init_process_group(backend="nccl", timeout=datetime.timedelta(seconds=30))train()dist.destroy_process_group()if __name__ == "__main__":run()

在多個主機上執行

torchrun --nproc_per_node=M --nnode=N --node_rank=0 --master_addr='xxx.xxx.xxx.xxx' --master_port=1234 ddp_multi_master.py

注意這里參數M表示你單個機器上的顯卡數,N是你有幾臺機器,--node_rank,這里是不同機器上的區別,主機上設置0,其他機器上設置1,2,…,N-1.

也可以寫bash文件執行更方便

#!/bin/bash
# 設置基本參數
MASTER_ADDR=xxx.xxx.xxx.xxx           # 主機IP
MASTER_PORT=29400                   # 主機端口
NNODES=2                            # 參與訓練的總機器數
NPROC_PER_NODE=2                    # 每臺機器上的進程數# 所有網卡的IP地址,用于篩選
ALL_LOCAL_IPS=$(hostname -I)
# 根據本機 IP 配置通信接口
if [[ "$ALL_LOCAL_IPS" == *"xxx.xxx.xxx.xxx"* ]]; thenNODE_RANK=0                       # 表示當前機器是第0臺機器IFNAME=eno1   # 機器0的網卡名稱mytorchrun=~/anaconda3/envs/lora/bin/torchrun  # 虛擬環境下torchrun的位置
elif [[ "$ALL_LOCAL_IPS" == *"xxx.xxx.xxx.xxx"* ]]; thenNODE_RANK=1                       # 表示當前機器是第1臺機器IFNAME=enp6s0 # 機器1的網卡名稱mytorchrun=/home/users1/xjl/miniconda3/envs/lora/bin/torchrun
elseexit 1
fi# 設置 RDMA 接口
export NCCL_IB_DISABLE=0            # 是否禁用InfiniBand
export NCCL_IB_HCA=mlx5_1           # 使用哪個RDMA接口進行通信
export NCCL_SOCKET_IFNAME=$IFNAME   # 使用哪個網卡進行通信
export NCCL_DEBUG=INFO              # 可選:調試用export GLOO_IB_DISABLE=0            # 是否禁用InfiniBand
export GLOO_SOCKET_IFNAME=$IFNAME   # 使用哪個網卡進行通信
export PYTHONUNBUFFERED=1           # 實時輸出日志# 啟動分布式任務
$mytorchrun \--nnodes=$NNODES \--nproc_per_node=$NPROC_PER_NODE \--node_rank=$NODE_RANK \--master_addr=$MASTER_ADDR \--master_port=$MASTER_PORT \ddp_multi_master.py

主機輸出信息:
在這里插入圖片描述
從機輸出信息:
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/82160.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/82160.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/82160.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【論文閱讀】——D^3-Human: Dynamic Disentangled Digital Human from Monocular Vi

文章目錄 摘要1 引言2 相關工作3 方法3.1 HmSDF 表示3.2 區域聚合3.3. 變形場3.4. 遮擋感知可微分渲染3.5 訓練3.5.1 訓練策略3.5.2 重建損失3.5.3 正則化限制 4. 實驗4.1 定量評估4.2 定性評價4.3 消融研究4.4 應用程序 5 結論 摘要 我們介紹 D 3 D^{3} D3人&#xff0c;一種…

docker commit除了提交容器成鏡像,還能搞什么之修改cmd命令

要讓新鏡像默認啟動時執行 /usr/sbin/sshd -D&#xff0c;需在提交鏡像時 ??顯式指定新的啟動命令??。 方法一&#xff1a;提交時通過 --change 覆蓋 CMD docker commit --changeCMD ["/usr/sbin/sshd", "-D"] v2 project:v2 方法二&#xff1a;重…

為什么我輸入對了密碼,還是不能用 su 切換到 root?

“為什么我輸入對了密碼&#xff0c;還是不能用 su 切換到 root&#xff1f;” 其實這背后可能不是“密碼錯了”&#xff0c;而是系統不允許你用 su 切 root&#xff0c;即使密碼對了。 &#x1f447; 以下是最常見的幾個真正原因&#xff1a; ? 1. Root 用戶沒有設置密碼&…

轉移dp簡單數學數論

1.轉移dp問題 昨天的練習賽上有一個很好玩的起終點問題&#xff0c;第一時間給出bfs的寫法。 但是寫到后面發現不行&#xff0c;還得是的dp轉移的寫法才能完美的解決這道題目。 每個格子可以經過可以不經過&#xff0c;因此它的狀態空間是2^&#xff08;n*m&#xff09;&…

IP查詢基礎介紹

IP 查詢原理 IP 地址是網絡設備唯一標識&#xff0c;IP 查詢通過解析 IP 地址獲取地理位置、運營商等信息。目前主流的 IPv4&#xff08;32 位&#xff09;與 IPv6&#xff08;128 位&#xff09;協議&#xff0c;前者理論提供約 43 億地址&#xff0c;后者地址空間近乎無限。…

Linux命令簡介

1 Linux系統的命令概述 在 Linux 操作系統中&#xff0c;凡是在字符操作界面中輸入能夠完成特定操作和任務的字符串都可以稱為命令。嚴格來說&#xff0c;命令通常只代表實現某一類功能的指令或程序的名稱。 1.1 Shell Linux 命令的執行必須依賴于 Shell 命令解釋器。Shell …

WebRTC與RTSP|RTMP的技術對比:低延遲與穩定性如何決定音視頻直播的未來

引言 音視頻直播技術已經深刻影響了我們的生活方式&#xff0c;尤其是在教育、醫療、安防、娛樂等行業中&#xff0c;音視頻技術成為了行業發展的重要推動力。近年來&#xff0c;WebRTC作為一種開源的實時通信技術&#xff0c;成為了音視頻領域的重要選擇&#xff0c;它使得瀏覽…

多通道振弦式數據采集儀MCU安裝指南

設備介紹 數據采集儀 MCU集傳統數據采集器與5G/4G,LoRa/RS485兩種通信功能與一體的智能數據采集儀。該產品提供振弦、RS-485等的物理接口&#xff0c;能自動采集并存儲多種自然資源、建筑、橋梁、城市管廊、大壩、隧道、水利、氣象傳感器的實時數據&#xff0c;利用現場采集的數…

Vue3 + Element Plus表格篩選樣式設置

如果彈出框掛載在 body 下&#xff08;而非組件內部&#xff09;&#xff0c;scoped 樣式無法生效&#xff0c;這時就需要使用全局樣式。 強制全局樣式 1、添加全局樣式文件&#xff08;或在原有的文件中添加以下內容&#xff09; src/assets/global.scss /* 全局強制樣式覆…

vue--ofd/pdf預覽實現

背景 實現預覽ofd/pdf超鏈接功能 業務實現 pdf的預覽 實現方式&#xff1a; 直接使用 <iframe :src"${url}#navpanes0&toolbar0" /> 實現pdf的預覽。 navpanes0 隱藏側邊欄toolbar0 隱藏頂部工具欄 使用pdf.js&#xff0c;代碼先行&#xff1a; <tem…

【C++20新特性】ranges::sort()使用方法,優勢,注意點

以下是關于 ranges::sort() 的詳細說明&#xff1a; 1. ranges::sort() 的使用方法 ranges::sort() 是 C20 引入的基于范圍&#xff08;Ranges&#xff09;的排序函數&#xff0c;其語法更簡潔&#xff0c;支持直接操作容器或范圍對象。 (1)基本用法 #include <vector&g…

深入理解設計模式之適配器模式

深入理解設計模式之適配器模式 1. 適配器模式概述 適配器模式(Adapter Pattern)是一種結構型設計模式&#xff0c;它允許將一個類的接口轉換為客戶端所期望的另一個接口。適配器模式使得原本由于接口不兼容而不能一起工作的類能夠協同工作&#xff0c;扮演了"轉換器&quo…

【數據結構 · 初階】- 快速排序

目錄 一. Hoare 版本 1. 單趟 2. 整體 3. 時間復雜度 4. 優化&#xff08;搶救一下&#xff09; 4.1 隨機選 key 4.2 三數取中 二. 挖坑法 格式優化 三. 前后指針&#xff08;最好&#xff09; 四. 小區間優化 五. 改非遞歸 快速排序是 Hoare 提出的一種基于二叉樹…

第2周 PINN核心技術揭秘: 如何用神經網絡求解偏微分方程

1. PDEs與傳統數值方法回顧 (Review of PDEs & Traditional Numerical Methods) 1.1 什么是偏微分方程 (Partial Differential Equations, PDEs)? 偏微分方程是描述自然界和工程領域中各種物理現象(如熱量傳播、流體流動、波的振動、電磁場分布等)的基本數學語言。 1.…

Neo4j(二) - 使用Cypher操作Neo4j

文章目錄 前言一、Cypher簡介二、數據庫操作1. 創建數據庫2. 查看數據庫3. 刪除數據庫4. 切換數據庫 三、節點、關系及屬性操作1. 創建節點與關系1.1 語法1.2 示例 2. 查詢數據2.1 語法2.2 示例 3. 更新數據3.1 語法3.2 示例 4. 刪除節點與關系4.1 語法4.2 示例 5. 合并數據5.1…

RabbitMQ的Web管理頁面給我看懵了,這都什么意思啊

文章目錄 OverviewTotalsMessage RatesQueued Messages NodesChurn StatisticsPorts and ContextsExport DefinitionsImport Definitions ConnectionsChannelsExchangesQueuesAdmin他們之間的關聯 在上一篇文章中我們講到了如何在Windows中安裝Rabbitmq&#xff0c; 小白也能搞…

安全基礎與協議分析

5.1 Web安全基礎 5.1.1 Web安全基礎概覽&#xff08;一、二&#xff09; Web安全的核心目標是保護Web應用及其數據免受攻擊&#xff0c;涵蓋以下關鍵領域&#xff1a; 攻擊面&#xff1a; 前端漏洞&#xff08;XSS、CSRF&#xff09;。 后端漏洞&#xff08;SQL注入、RCE&a…

STM32項目實戰:ADC采集

STM32F103C8T6的ADC配置。PB0對應的是ADC1的通道8。在標準庫中&#xff0c;需要初始化ADC&#xff0c;設置通道&#xff0c;時鐘&#xff0c;轉換模式等。需要配置GPIOB的第0腳為模擬輸入模式&#xff0c;然后配置ADC1的通道8&#xff0c;設置轉換周期和觸發方式。 接下來是I2C…

第十四章:數據治理之數據源:數據源的數據接入、業務屬性梳理及監控

本章開始&#xff0c;將進入9大模塊的介紹。第一個模塊我們先介紹&#xff1a;數據源。數據源是整個數據中臺數據的來源&#xff0c;是一個起點。更好的管理好數據源這個起點&#xff0c;是數據治理的一個好的開始。 在【數據&#xff1a;業務生數據&#xff0c;數據生“萬物”…

【C/C++】多線程開發:wait、sleep、yield全解析

文章目錄 多線程開發&#xff1a;wait、sleep、yield全解析1 What簡要介紹詳細介紹wait() — 條件等待&#xff08;用于線程同步&#xff09;sleep() — 睡覺&#xff0c;定時掛起yield() — 自愿讓出 CPU 2 區別以及建議區別應用場景建議 3 三者協作使用示例 多線程開發&#…