数据并行、模型并行与张量并行:深度学习中的并行计算策略(中英双语)
中文版
数据并行、模型并行与张量并行:深度学习中的并行计算策略
随着深度学习模型的不断增大,单个计算节点(例如单个 GPU)的计算和内存能力逐渐成为了限制训练效率和模型规模的瓶颈。为了应对这些挑战,深度学习社区提出了多种并行计算策略,其中包括数据并行(Data Parallelism)、模型并行(Model Parallelism)和张量并行(Tensor Parallelism)。
这些并行策略的核心思想是通过在多个计算设备之间分配计算负载,来加速模型训练,尤其是当模型规模超出单个设备的计算能力时,能够有效提升训练效率和扩展性。
在本篇博客中,我们将通俗易懂地解释这些并行策略的概念,并通过简单的示例代码来帮助大家理解。我们还会加入数学公式来帮助阐明这些概念。
1. 数据并行(Data Parallelism)
数据并行是最常见的并行训练策略之一,它通过将数据集拆分成多个小批次(mini-batch),并将这些小批次分配给不同的计算设备(如不同的 GPU),以此来加速训练。
数据并行的基本思路:
- 模型复制:每个计算设备都有一个完整的模型副本。
- 数据划分:训练数据被划分成多个小批次,每个设备处理不同的小批次。
- 梯度聚合:各个设备计算出的梯度会被合并(通常使用求平均或求和),并同步更新模型的参数。
假设我们有一个总的数据集 ( D = { x 1 , x 2 , . . . , x n } D = \{x_1, x_2, ..., x_n\} D={x1,x2,...,xn} ),将其拆分成 ( P P P ) 个小批次,分配给 ( P P P ) 个 GPU。每个 GPU 上运行相同的模型,计算出对应小批次的梯度,并最终将梯度合并更新模型参数。
数学公式:
对于每个设备 ( i i i ),计算损失函数 ( L ( θ i , x i ) L(\theta_i, x_i) L(θi,xi) ):
L ( θ i , x i ) = Loss ( f ( x i ; θ i ) ) L(\theta_i, x_i) = \text{Loss}(f(x_i; \theta_i)) L(θi,xi)=Loss(f(xi;θi))
其中 ( f ( x i ; θ i ) f(x_i; \theta_i) f(xi;θi) ) 是模型的输出,( θ i \theta_i θi ) 是设备 ( i i i ) 上的模型参数。
计算完成后,所有设备计算出的梯度 ( ∇ θ i L \nabla \theta_i L ∇θiL ) 会聚合:
1 P ∑ i = 1 P ∇ θ i L \frac{1}{P} \sum_{i=1}^{P} \nabla \theta_i L P1i=1∑P∇θiL
然后,通过这种方式同步更新所有设备上的模型参数。
示例代码:
import torch
import torch.nn as nn
import torch.optim as optim
from torch.nn.parallel import DataParallel# 创建一个简单的神经网络
class SimpleNN(nn.Module):def __init__(self):super(SimpleNN, self).__init__()self.fc1 = nn.Linear(100, 50)self.fc2 = nn.Linear(50, 10)def forward(self, x):x = torch.relu(self.fc1(x))x = self.fc2(x)return x# 假设我们有两个GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = SimpleNN()# 使用DataParallel包装模型,进行数据并行训练
model = DataParallel(model) # 自动分配到多个GPU
model.to(device)# 假设数据
input_data = torch.randn(64, 100).to(device) # 一个批次的数据
target = torch.randn(64, 10).to(device)# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)# 训练循环
for epoch in range(10):optimizer.zero_grad()output = model(input_data)loss = criterion(output, target)loss.backward()optimizer.step()
2. 模型并行(Model Parallelism)
当模型的规模非常大,以至于无法在单个设备的内存中存储时,可以采用模型并行策略。与数据并行不同,模型并行通过将模型的不同部分分配到多个设备上进行计算,而不是将数据拆分。
模型并行的基本思路:
- 模型拆分:将模型划分为多个部分,每个部分被放置到不同的设备上。
- 设备间通信:各部分之间通过设备间的通信进行数据传递。
假设我们的模型包含两个大层 ( L 1 L_1 L1 ) 和 ( L 2 L_2 L2 ),由于 ( L L L ) 的参数过大,无法全部存放在单个 GPU 上,我们可以将 ( L 1 L_1 L1 ) 放在 GPU 1 上,将 ( L 2 L_2 L2 ) 放在 GPU 2 上,进行计算。
数学公式:
对于模型的不同部分,假设我们有两个设备 ( G P U 1 GPU_1 GPU1 ) 和 ( G P U 2 GPU_2 GPU2 ),计算流程如下:
- 在 ( G P U 1 GPU_1 GPU1 ) 上计算第一部分的输出 ( h 1 = f 1 ( x ) h_1 = f_1(x) h1=f1(x) )。
- 将 ( h 1 h_1 h1 ) 传输到 ( G P U 2 GPU_2 GPU2 ),在 ( G P U 2 GPU_2 GPU2 ) 上计算第二部分 ( h 2 = f 2 ( h 1 ) h_2 = f_2(h_1) h2=f2(h1) )。
- 最终输出 ( y = h 2 y = h_2 y=h2 )。
示例代码:
import torch
import torch.nn as nnclass Part1(nn.Module):def __init__(self):super(Part1, self).__init__()self.fc1 = nn.Linear(100, 200)def forward(self, x):return torch.relu(self.fc1(x))class Part2(nn.Module):def __init__(self):super(Part2, self).__init__()self.fc2 = nn.Linear(200, 10)def forward(self, x):return self.fc2(x)device1 = torch.device("cuda:0")
device2 = torch.device("cuda:1")model_part1 = Part1().to(device1)
model_part2 = Part2().to(device2)# 假设输入数据
input_data = torch.randn(64, 100).to(device1)# 在GPU 1上计算第一部分
h1 = model_part1(input_data)# 将数据传输到GPU 2并计算第二部分
h1 = h1.to(device2)
output = model_part2(h1)
3. 张量并行(Tensor Parallelism)
张量并行是一种细粒度的并行策略,它通过将张量的计算切分成多个部分,并在多个设备上并行计算来加速训练过程。张量并行通常与数据并行和模型并行结合使用。
张量并行的基本思路:
- 张量切分:将模型中的大张量(例如权重矩阵)分割成多个小张量,并分配给不同的设备。
- 并行计算:不同设备并行计算各自的部分,然后将结果合并。
假设我们有一个大型矩阵 ( A A A ) 需要进行矩阵乘法。我们可以将 ( A A A ) 切分成几个小矩阵,每个小矩阵分配给一个设备,进行并行计算。
数学公式:
假设我们要计算矩阵乘法 ( C = A ⋅ B C = A \cdot B C=A⋅B ),其中 ( A A A ) 是一个 ( m × n m \times n m×n ) 的矩阵,( B B B ) 是一个 ( n × p n \times p n×p ) 的矩阵。
在张量并行中,我们将矩阵 ( A A A ) 切分成若干个子矩阵 ( A 1 , A 2 , . . . , A k A_1, A_2, ..., A_k A1,A2,...,Ak ),并将每个子矩阵 ( A i A_i Ai ) 分配到不同的 GPU 上进行计算。
C = ( A 1 ⋅ B ) + ( A 2 ⋅ B ) + ⋯ + ( A k ⋅ B ) C = \left( A_1 \cdot B \right) + \left( A_2 \cdot B \right) + \dots + \left( A_k \cdot B \right) C=(A1⋅B)+(A2⋅B)+⋯+(Ak⋅B)
这里的拆分和合并过程请参考文末。
示例代码:
import torch
import torch.nn as nnclass Model(nn.Module):def __init__(self):super(Model, self).__init__()self.fc1 = nn.Linear(100, 200) # 第一个线性层self.fc2 = nn.Linear(200, 10) # 第二个线性层def forward(self, x):# 模拟张量并行:拆分输入并在不同设备上计算# 将输入拆分成两部分,分别送到不同的GPU上x1, x2 = x.chunk(2, dim=1) # 将输入拆成两部分,假设输入是[64, 100]x1 = x1.to(device1) # 第一部分数据送到GPU1x2 = x2.to(device2) # 第二部分数据送到GPU2# 在GPU1上计算第一部分out1 = self.fc1(x1) # 在GPU1上计算out1 = out1.to(device2) # 将输出转移到GPU2# 在GPU2上计算第二部分out2 = self.fc2(x2) # 在GPU2上计算# 合并两个输出结果out = out1 + out2 # 假设这里是两个部分的合并(可以是加法、拼接等)return out# 假设我们有两个GPU
device1 = torch.device("cuda:0") # GPU1
device2 = torch.device("cuda:1") # GPU2model = Model().to(device1) # 将模型的第一部分(fc1)放到GPU1# 模拟输入数据
input_data = torch.randn(64, 100).to(device1) # 假设输入数据是64个样本,每个样本100维# 计算前向传播
output_data = model(input_data)# 最终输出在GPU2上
print(output_data)
代码解析
- 输入数据拆分:
x.chunk(2, dim=1)
将输入数据拆分成两部分(64个样本,每个样本100维)。dim=1
表示沿着列方向(特征维度)拆分数据。因此,我们将每个样本的100维特征拆成两部分,每部分50维。
x1 和 x2 是拆分后的两部分输入,分别送到不同的设备(GPU 1 和 GPU 2)进行计算。
- 模型计算:
我们将第一部分输入 x1 放到GPU1上,使用 fc1 进行计算,然后将其结果转移到GPU2。
第二部分输入 x2 直接在GPU2上使用 fc2 进行计算。
- 合并结果:
假设我们将两个输出结果加在一起 (out1 + out2
) 作为最终的结果。在实际应用中,合并的方式可以是加法、拼接(torch.cat()
)等,取决于具体的模型设计。
总结
- 数据并行:将数据分配到多个设备,每个设备计算相同模型的不同数据。
- 模型并行:将模型拆分成多个部分,分配到不同设备,每个设备计算模型的一部分。
- 张量并行:将大型张量分割成多个小张量,分配到不同设备上并行计算。
通过这些并行策略,我们可以有效地解决训练大规模模型时的计算和内存瓶颈,提高训练效率并支持更大的模型和数据集。希望这篇博客能帮助大家理解这些并行计算策略,并在实际应用中选择合适的策略来优化模型训练。
英文版
Data Parallelism, Model Parallelism, and Tensor Parallelism: Parallel Computing Strategies in Deep Learning
As deep learning models continue to grow in size, the computational and memory capacity of a single machine (e.g., a single GPU) becomes a bottleneck for training large models. To address these challenges, the deep learning community has introduced various parallel computing strategies, including Data Parallelism, Model Parallelism, and Tensor Parallelism.
These parallel strategies aim to distribute the computation across multiple devices, allowing for faster training, especially when the model size exceeds the memory capacity of a single device. In this blog post, we will explain these parallelism strategies in simple terms, provide sample code for better understanding, and introduce mathematical formulas to clarify the concepts.
1. Data Parallelism
Data Parallelism is one of the most common parallel training strategies. It works by splitting the dataset into multiple smaller batches and distributing these batches across different computational devices (such as different GPUs) to accelerate the training process.
Key Idea of Data Parallelism:
- Model Replication: Each device has a complete replica of the model.
- Data Division: The training data is divided into smaller batches, and each device processes a different mini-batch.
- Gradient Aggregation: The gradients computed by each device are averaged (or summed) and used to update the model parameters.
Given a dataset ( D = { x 1 , x 2 , . . . , x n } D = \{x_1, x_2, ..., x_n\} D={x1,x2,...,xn} ), we split it into ( P P P ) mini-batches and assign each mini-batch to a different GPU. Each GPU runs the same model, computes the gradients for its mini-batch, and the gradients are aggregated to update the model parameters.
Mathematical Formula:
For each device ( i i i ), the loss function ( L ( θ i , x i ) L(\theta_i, x_i) L(θi,xi) ) is computed:
L ( θ i , x i ) = Loss ( f ( x i ; θ i ) ) L(\theta_i, x_i) = \text{Loss}(f(x_i; \theta_i)) L(θi,xi)=Loss(f(xi;θi))
where ( f ( x i ; θ i ) f(x_i; \theta_i) f(xi;θi) ) is the model’s output and ( θ i \theta_i θi ) are the model parameters on device ( i i i ).
Afterward, the gradients ( ∇ θ i L \nabla \theta_i L ∇θiL ) from all devices are aggregated:
1 P ∑ i = 1 P ∇ θ i L \frac{1}{P} \sum_{i=1}^{P} \nabla \theta_i L P1i=1∑P∇θiL
This aggregated gradient is then used to update the model parameters across all devices.
Example Code:
import torch
import torch.nn as nn
import torch.optim as optim
from torch.nn.parallel import DataParallel# Simple Neural Network model
class SimpleNN(nn.Module):def __init__(self):super(SimpleNN, self).__init__()self.fc1 = nn.Linear(100, 50)self.fc2 = nn.Linear(50, 10)def forward(self, x):x = torch.relu(self.fc1(x))x = self.fc2(x)return x# Assume we have two GPUs
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = SimpleNN()# Wrap the model for data parallelism
model = DataParallel(model) # Automatically distribute across GPUs
model.to(device)# Example data
input_data = torch.randn(64, 100).to(device) # A batch of data
target = torch.randn(64, 10).to(device)# Define loss function and optimizer
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)# Training loop
for epoch in range(10):optimizer.zero_grad()output = model(input_data)loss = criterion(output, target)loss.backward()optimizer.step()
2. Model Parallelism
When the model is too large to fit into the memory of a single device, Model Parallelism can be used. Unlike data parallelism, model parallelism splits the model itself into parts and assigns each part to a different device for computation.
Key Idea of Model Parallelism:
- Model Splitting: The model is divided into several parts, and each part is placed on a different device.
- Communication Between Devices: The different parts of the model communicate with each other during the forward and backward passes.
Assume we have a model with two large layers ( L 1 L_1 L1 ) and ( L 2 L_2 L2 ). If ( L L L ) is too large to fit in a single GPU, we place ( L 1 L_1 L1) on GPU 1 and place ( L 2 L_2 L2 ) on GPU 2.
Mathematical Formula:
For different parts of the model, assume we have two devices ( G P U 1 GPU_1 GPU1 ) and ( G P U 2 GPU_2 GPU2 ). The computation flow is as follows:
- On ( G P U 1 GPU_1 GPU1 ), compute the output of the first part ( h 1 = f 1 ( x ) h_1 = f_1(x) h1=f1(x) ).
- Transfer ( h 1 h_1 h1 ) to ( G P U 2 GPU_2 GPU2 ), where the second part is computed ( h 2 = f 2 ( h 1 ) h_2 = f_2(h_1) h2=f2(h1) ).
- The final output is ( y = h 2 y = h_2 y=h2 ).
Example Code:
import torch
import torch.nn as nnclass Part1(nn.Module):def __init__(self):super(Part1, self).__init__()self.fc1 = nn.Linear(100, 200)def forward(self, x):return torch.relu(self.fc1(x))class Part2(nn.Module):def __init__(self):super(Part2, self).__init__()self.fc2 = nn.Linear(200, 10)def forward(self, x):return self.fc2(x)device1 = torch.device("cuda:0")
device2 = torch.device("cuda:1")model_part1 = Part1().to(device1)
model_part2 = Part2().to(device2)# Example input data
input_data = torch.randn(64, 100).to(device1)# Compute first part on GPU1
h1 = model_part1(input_data)# Transfer to GPU2 and compute second part
h1 = h1.to(device2)
output = model_part2(h1)
3. Tensor Parallelism
Tensor Parallelism is a finer-grained parallel strategy that splits a large tensor (such as a weight matrix) into smaller chunks and distributes these chunks across multiple devices for parallel computation. Tensor parallelism is often used in conjunction with data and model parallelism to further enhance performance.
Key Idea of Tensor Parallelism:
- Tensor Splitting: Large tensors (e.g., weight matrices) are split into smaller chunks and assigned to different devices.
- Parallel Computation: Each device computes the part of the tensor assigned to it, and the results are combined.
For example, assume we need to compute a matrix multiplication ( C = A ⋅ B C = A \cdot B C=A⋅B ), where ( A A A ) is a ( m × n m \times n m×n ) matrix and ( B B B ) is a ( n × p n \times p n×p ) matrix. We can split ( A A A ) into smaller matrices ( A 1 , A 2 , . . . , A k A_1, A_2, ..., A_k A1,A2,...,Ak ), and assign each part to a different GPU for parallel computation.
Mathematical Formula:
Let’s calculate matrix multiplication ( C = A ⋅ B C = A \cdot B C=A⋅B ), where ( A A A ) is split into submatrices ( A 1 , A 2 , . . . , A k A_1, A_2, ..., A_k A1,A2,...,Ak ), and each part is processed on different GPUs:
C = ( A 1 ⋅ B ) + ( A 2 ⋅ B ) + ⋯ + ( A k ⋅ B ) C = \left( A_1 \cdot B \right) + \left( A_2 \cdot B \right) + \dots + \left( A_k \cdot B \right) C=(A1⋅B)+(A2⋅B)+⋯+(Ak⋅B)
Example Code:
import torch
import torch.nn as nnclass Model(nn.Module):def __init__(self):super(Model, self).__init__()self.fc1 = nn.Linear(100, 200) # First fully connected layerself.fc2 = nn.Linear(200, 10) # Second fully connected layerdef forward(self, x):# Simulate tensor parallelism: split the input and compute on different devices# Split the input into two parts, and send each part to a different GPUx1, x2 = x.chunk(2, dim=1) # Split the input into two parts, assuming input is [64, 100]x1 = x1.to(device1) # Send the first part of the data to GPU1x2 = x2.to(device2) # Send the second part of the data to GPU2# Compute the first part on GPU1out1 = self.fc1(x1) # Compute on GPU1out1 = out1.to(device2) # Move the output to GPU2# Compute the second part on GPU2out2 = self.fc2(x2) # Compute on GPU2# Combine the two output resultsout = out1 + out2 # Here we assume the two parts are combined by addition (can also be concatenation, etc.)return out# Assume we have two GPUs
device1 = torch.device("cuda:0") # GPU1
device2 = torch.device("cuda:1") # GPU2model = Model().to(device1) # Place the first part of the model (fc1) on GPU1# Simulate input data
input_data = torch.randn(64, 100).to(device1) # Assume input data consists of 64 samples, each with 100 features# Perform forward pass
output_data = model(input_data)# The final output is on GPU2
print(output_data)
Summary
- Data Parallelism: Distribute data across devices, each device computes gradients for its batch, and gradients are aggregated to update the model.
- Model Parallelism: Split the model into parts and place each part on a different device for computation.
- Tensor Parallelism: Split large tensors into smaller parts, and distribute the computation of these parts across devices.
By using these parallel strategies, we can effectively address the memory and computation bottlenecks during training, enabling the training of larger models and datasets. We hope this blog helps you understand these parallel computing strategies and choose the right approach to optimize your model training.
补充张量并行矩阵拆分与合并
正确的张量并行中的矩阵切分与计算流程
假设我们有两个矩阵:
- 矩阵 ( A A A ) 的形状是 ( m × n m \times n m×n )
- 矩阵 ( B B B ) 的形状是 ( n × p n \times p n×p )
我们想要计算矩阵乘法 ( C = A ⋅ B C = A \cdot B C=A⋅B ),其中 ( C C C ) 的形状将是 ( m × p m \times p m×p )。
张量并行中的切分方式
在张量并行中,我们会将矩阵 ( A A A ) 切分成多个子矩阵,然后将每个子矩阵分配到不同的 GPU 上进行计算。
如何切分矩阵 ( A )?
如果我们将矩阵 ( A A A ) 切分成 ( k k k ) 个子矩阵 ( A 1 , A 2 , . . . , A k A_1, A_2, ..., A_k A1,A2,...,Ak ),并分配到 ( k k k ) 个不同的 GPU 上,通常的切分方式是将 ( A A A ) 沿着行方向进行切分。也就是说,每个子矩阵 ( A i A_i Ai ) 的形状将是 ( ( m / k ) × n (m/k) \times n (m/k)×n ).
矩阵乘法的计算步骤
- 切分后的 ( A i A_i Ai ) 形状: 假设将矩阵 ( A A A ) 切分成 ( k k k ) 个子矩阵,每个子矩阵 ( A i A_i Ai ) 的形状是 ( ( m / k ) × n (m/k) \times n (m/k)×n ),每个子矩阵都会和矩阵 ( B B B )(形状为 ( n × p n \times p n×p ))进行矩阵乘法。
- 矩阵乘法: 对每个 ( A i A_i Ai )(形状是 ( ( m / k ) × n (m/k) \times n (m/k)×n ))和矩阵 ( B B B )(形状是 ( n × p n \times p n×p ))进行矩阵乘法时,结果会是一个 ( ( m / k ) × p (m/k) \times p (m/k)×p ) 的矩阵 ( C i C_i Ci )。
因此,每个 GPU 上的计算结果 ( C i C_i Ci ) 的形状是 ( ( m / k ) × p (m/k) \times p (m/k)×p )。
合并子矩阵的结果
当所有子矩阵的计算完成后,我们将得到 ( k k k ) 个形状为 ( ( m / k ) × p (m/k) \times p (m/k)×p ) 的矩阵 ( C 1 , C 2 , . . . , C k C_1, C_2, ..., C_k C1,C2,...,Ck )。这些矩阵会沿着行方向拼接起来,最终得到一个 ( m × p m \times p m×p ) 的矩阵 ( C C C )。
举个例子
假设矩阵 ( A A A ) 的形状是 ( 6 × 4 6 \times 4 6×4 ),矩阵 ( B B B ) 的形状是 ( 4 × 3 4 \times 3 4×3 ),那么我们要计算 ( C = A ⋅ B C = A \cdot B C=A⋅B ),其中 ( C C C ) 的形状应该是 ( 6 × 3 6 \times 3 6×3 )。
步骤 1:切分矩阵 ( A )
将矩阵 ( A A A ) 沿行方向切分成 2 个子矩阵 ( A 1 A_1 A1 ) 和 ( A 2 A_2 A2 ):
- ( A 1 A_1 A1 ) 的形状是 ( 3 × 4 3 \times 4 3×4 )
- ( A 2 A_2 A2 ) 的形状是 ( 3 × 4 3 \times 4 3×4 )
步骤 2:计算每个子矩阵的乘积
每个子矩阵与 ( B B B ) 进行矩阵乘法,得到:
- ( C 1 = A 1 ⋅ B C_1 = A_1 \cdot B C1=A1⋅B ),形状是 ( 3 × 3 3 \times 3 3×3 )
- ( C 2 = A 2 ⋅ B C_2 = A_2 \cdot B C2=A2⋅B ),形状是 ( 3 × 3 3 \times 3 3×3 )
步骤 3:合并子矩阵
将 ( C 1 C_1 C1 ) 和 ( C 2 C_2 C2 ) 沿着行方向拼接,得到最终的矩阵 ( C C C ),形状是 ( 6 × 3 6 \times 3 6×3 ):
C = [ C 1 [ 1 , : ] C 1 [ 2 , : ] C 1 [ 3 , : ] C 2 [ 1 , : ] C 2 [ 2 , : ] C 2 [ 3 , : ] ] C = \left[ \begin{array}{ccc} C_1[1, :] \\ C_1[2, :] \\ C_1[3, :] \\ C_2[1, :] \\ C_2[2, :] \\ C_2[3, :] \\ \end{array} \right] C= C1[1,:]C1[2,:]C1[3,:]C2[1,:]C2[2,:]C2[3,:]
最终得到的矩阵 ( C C C ) 的形状是 ( 6 × 3 6 \times 3 6×3),与预期一致。
总结
- 切分矩阵 ( A ):在张量并行中,我们将矩阵 ( A A A ) 沿行方向切分成多个子矩阵,每个子矩阵的形状是 ( ( m / k ) × n (m/k) \times n (m/k)×n )。
- 每个子矩阵的计算:每个子矩阵 ( A i A_i Ai ) 会与矩阵 ( B B B )(形状为 ( n × p n \times p n×p ))进行矩阵乘法,得到一个形状为 ( ( m / k ) × p (m/k) \times p (m/k)×p ) 的矩阵 ( C i C_i Ci )。
- 合并结果:所有子矩阵的计算结果会沿行方向拼接,最终得到形状为 ( m × p m \times p m×p ) 的矩阵 ( C C C )。
这样,通过张量并行的方式,我们可以将一个大矩阵的计算任务分配到多个 GPU 上,从而提高计算效率,同时保持最终结果的正确形状。
后记
2024年11月29日15点33分于上海,在GPT4o大模型辅助下完成。
相关文章:
数据并行、模型并行与张量并行:深度学习中的并行计算策略(中英双语)
中文版 数据并行、模型并行与张量并行:深度学习中的并行计算策略 随着深度学习模型的不断增大,单个计算节点(例如单个 GPU)的计算和内存能力逐渐成为了限制训练效率和模型规模的瓶颈。为了应对这些挑战,深度学习社区…...
大数据-239 离线数仓 - 广告业务 测试 FlumeAgent 加载ODS、DWD层
点一下关注吧!!!非常感谢!!持续更新!!! Java篇开始了! 目前开始更新 MyBatis,一起深入浅出! 目前已经更新到了: Hadoop࿰…...
Python中的数据结构深入解析:从列表到字典的优化技巧
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! Python是一门以易用性和可读性著称的高级编程语言,其内置的数据结构为开发者提供了强大的工具,但了解其底层实现及性能优化策略却常被忽略。本文深入探讨Python中的核心数据结构,包括列表(list)、元组…...
【JS】JS判断数据类型
typeof // typeof 后面括号有没有都可以 console.log(typeof(a)) // string console.log(typeof(123)) // number console.log(typeof(undefined)) // undefined console.log(typeof(true)) // boolean console.log(typeof(Symbol(123))) // symbolconsole.log(typeof(null)) /…...
基于jmeter+perfmon的稳定性测试记录
软件测试资料领取:[内部资源] 想拿年薪40W的软件测试人员,这份资料必须领取~ 软件测试面试刷题工具领取:软件测试面试刷题【800道面试题答案免费刷】 1. 引子 最近承接了项目中一些性能测试的任务,因此决定记录一下,…...
【0351】Postgres内核 Open WAL segment(包含 WAL 位置 ‘RecPtr’)(2 - 4)
上一篇: 文章目录 1. 打开 WAL Segment2. Standby mode 由一个 状态机(state machine)实现2.1 何处获取 WAL 文件?2.1.1 XLogSource2.1.2 从所选源(XLogSource )读取 XLOG2.1.2.1 walreceiver 运行状态 ?2.1.3 readFile(XLOG 文件句柄)1. 打开 WAL Segment 在经过前…...
Mysql基础
什么是关系型数据库? 顾名思义,关系型数据库(RDB,Relational Database)就是一种建立在关系模型的基础上的数据库。关系模型表明了数据库中所存储的数据之间的联系(一对一、一对多、多对多)。 …...
Altium Designer学习笔记 24 PCB初始布局2
基于Altium Designer 23学习版,四层板智能小车PCB 更多AD学习笔记:Altium Designer学习笔记 1-5 工程创建_元件库创建Altium Designer学习笔记 6-10 异性元件库创建_原理图绘制Altium Designer学习笔记 11-15 原理图的封装 编译 检查 _PCB封装库的创建Al…...
【从0学英语】形容词性/名词性物主代词是什么?
在英语中,物主代词是非常重要的语法概念之一,特别是对于初学者来说。理解形容词性物主代词和名词性物主代词的不同,能够帮助我们在日常对话中准确地表达拥有关系。在这篇文章中,我们将深入探讨这两个概念,并通过详细的…...
hhdb数据库介绍(10-29)
管理 数据备份 从存储节点或灾备机房数据备份 选择灾备机房类型、从库(双主备库)存储节点类型进行备份,页面根据选择类型,对应给出提示信息。发起备份时,检测从存储节点状态是否符合备份条件。 主从数据一致性检测…...
springboot(20)(删除文章分类。获取、更新、删除文章详细)(Validation分组校验)
目录 一、删除文章分类功能。 (1)接口文档。 1、请求路径、请求参数。 2、请求参数。 3、响应数据。 (2)实现思路与代码书写。 1、controller层。 2、service接口业务层。 3、serviceImpl实现类。 4、mapper层。 5、后端接口测试。…...
实战指南:理解 ThreadLocal 原理并用于Java 多线程上下文管理
目录 一、ThreadLocal基本知识回顾分析 (一)ThreadLocal原理 (二)既然ThreadLocalMap的key是弱引用,GC之后key是否为null? (三)ThreadLocal中的内存泄漏问题及JDK处理方法 &…...
Spark 内存管理机制
Spark 内存管理 堆内内存和堆外内存 作为一个 JVM 进程,Executor 的内存管理建立在 JVM(最小为六十四分之一,最大为四分之一)的内存管理之上,此外spark还引入了堆外内存(不在JVM中的内存),在spark中是指不…...
【Maven】继承和聚合
5. Maven的继承和聚合 5.1 什么是继承 Maven 的依赖传递机制可以一定程度上简化 POM 的配置,但这仅限于存在依赖关系的项目或模块中。当一个项目的多个模块都依赖于相同 jar 包的相同版本,且这些模块之间不存在依赖关系,这就导致同一个依赖…...
NViST运行笔记
文章标题: NViST: In the Wild New View Synthesis from a Single Image with Transformers 1. 环境配置 创建环境 conda create -n nvist python3.9 进入环境 conda activate nvist 安装torch torchvision torchaudio pip install torch2.1.2 torchvision0…...
性能测试工具Grafana、InfluxDB和Collectd的搭建
一、性能监控组成简介 1、监控能力分工:这个系统组合能够覆盖从数据采集、存储到可视化的整个监控流程。Collectd可以收集各种系统和应用的性能指标,InfluxDB提供高效的时序数据存储,而 Grafana 则将这些数据以直观的方式呈现出来。2,实时性能监控:对于需要实时了解系统状…...
JS中的类与对象
面向对象是使用最广泛的一种编程范式,最具代表性的面向对象语言就是Java和C,在它们的理念中,面向对象的三大特性:封装,继承,多态。类,对象,公有/私有方法/属性,各种继承就…...
域名解析系统 DNS
1.域名系统概述 用户与互联网上某台主机通信时,必须要知道对方的IP地址。然而用户很难记住长达32 位的二进制主机地址。即使是点分十进制地址也并不太容易记忆。但在应用层为了便于用户记忆各种网络应用,连接在互联网上的主机不仅有P地址,而…...
Flutter 1.1:下载Flutter环境
1、在AS中下载Flutter插件 在setting的Plugins中下载Flutter,如图所示,可以直接进行搜索查找 2、下载flutter的sdk源代码 flutter中文文档学习 通过Git下载SDK源代码 git clone -b stable https://github.com/flutter/flutter.git3、配置系统变量 3…...
HTML5系列(6)-- 拖放 API 实战指南
前端技术探索系列:HTML5 拖放 API 实战指南 🎯 致读者:探索现代交互技术 👋 前端开发者们, 今天我们将深入探讨 HTML5 中一个强大而实用的特性 —— 拖放 API。这项技术能够让我们创建更加直观和交互性强的用户界面…...
windows下kafka初体验简易demo
这里提供了windows下的java1.8和kafka3.9.0版本汇总,可直接免费下载 【免费】java1.8kafka2.13版本汇总资源-CSDN文库 解压后可以得到一个文件夹 资料汇总内有一个kafka文件资料包.tgz,解压后可得到下述文件夹kafka_2.13-3.9.0,资料汇总内还…...
算法训练(leetcode)二刷第三十三天 | *322. 零钱兑换、*279. 完全平方数、*139. 单词拆分
刷题记录 *322. 零钱兑换*279. 完全平方数*139. 单词拆分 *322. 零钱兑换 leetcode题目地址 dp[j]存储amount为j时所需要的最少硬币数。当j为0时需要0个硬币,因此dp[0]赋值为0. 因为是取最少硬币数,因此初始化需要赋值一个最大值。 状态转移方程&…...
windows的pip镜像源配置
Windows 中 pip 镜像源配置 在 Windows 系统中,为了提高 pip 包的安装速度,我们可以配置 pip 的镜像源。以下是具体的配置步骤: 创建文件夹 在 C:\Users\Administrator\pip 路径下创建一个名为 pip.ini 的文件。 编辑 pip.ini 文件 使用文本…...
Django Rest Framework中嵌套关系的JSON序列化
在 Django Rest Framework (DRF) 中,处理嵌套关系的 JSON 序列化是一个常见需求。以下是如何实现嵌套关系序列化的详细说明,包括序列化器定义、模型关系以及常见用法。 1、问题背景 假设我们有以下两个模型: class Jobdtl(models.Model):jo…...
ONVIF协议网络摄像机客户端使用gsoap获取RTSP流地址GStreamer拉流播放
什么是ONVIF协议 ONVIF(开放式网络视频接口论坛)是一个全球性的开放式行业论坛,旨在促进开发和使用基于物理IP的安全产品接口的全球开放标准。 ONVIF规范的目标是建立一个网络视频框架协议,使不同厂商生产的网络视频产品完全互通。…...
40分钟学 Go 语言高并发:Go程序性能优化方法论
Go程序性能优化方法论 一、性能指标概述 指标类型关键指标重要程度优化目标CPU相关CPU使用率、线程数、上下文切换⭐⭐⭐⭐⭐降低CPU使用率,减少上下文切换内存相关内存使用量、GC频率、对象分配⭐⭐⭐⭐⭐减少内存分配,优化GC延迟指标响应时间、处理延…...
MySQL基础(语句)知识复习 (除索引和视图)
1.客户端和数据库操作 1.登录客户端界面:mysql -uroot -p 2.查看当前的数据库版本:select version(); 3.显示所有数据库:show databases;, 4.创建数据库:create [IF NOT EXISTS] database 库名 character set 字符…...
【sqlcipher】pc端sqflite使用过程中遇到的问题
在flutter中使用sqlcipher时 Mac上如果通过flutter带的文件管理api(即File的delete()方法)删除数据库文件,再创建同名的数据文件的话,必现readonly问题, 这里需要注意的一点是 DatabaseFactory 在Mac上直接使用全局的…...
Vue 实现无线滚动效果
目录 1.Element-plus官网中的Infinite Scroll组件说明 2.滚动条设置 3.滚动到底部的函数调用 1.Element-plus官网中的Infinite Scroll组件说明 官网链接如下所示: Infinite Scroll 无限滚动 | Element Plus 首先查看该代码,发现这个组件使用了一个…...
【CSS in Depth 2 精译_062】第 10 章 CSS 中的容器查询(@container)概述 + 10.1 容器查询的一个简单示例
当前内容所在位置(可进入专栏查看其他译好的章节内容) 【第十章 CSS 容器查询】 ✔️ 10.1 容器查询的一个简单示例 ✔️ 10.1.1 容器尺寸查询的用法 ✔️ 10.2 深入理解容器10.3 与容器相关的单位10.4 容器样式查询的用法10.5 本章小结 文章目录 第 10…...
conda手动初始化
问题:环境中存在conda但是conda无法使用 方法: 进入到anaconda目录下, 进入bin目录, 然后执行 source activate要想启动时自动进入conda环境, 需要在 ~/.bashrc中添加如下命令 # >>> conda initialize >>> # !! Contents within this block are managed by …...
hhdb数据库介绍(10-28)
管理 管理菜单主要囊括对业务数据进行管理的功能,例如对数据的备份恢复或执行业务表的DDL语句等操作。 数据对象 数据对象功能可以帮助用户通过列表实时查看当前已存在的数据对象,了解业务数据的整体情况。提供了对数据对象的筛选、统计、关联、详情等…...
Spring Boot自定义启动banner
在启动 Springboot 应用时,默认情况下会在控制台打印出 Springboot 相关的banner信息。 自定义banner 如果你想自定义一个独特的启动banner,该怎么做呢?Springboot 允许我们通过自定义启动banner来替换默认的banner。只需要在 resources 目…...
c语言——数组名该如何理解呢?
一般情况下,数组名表示首元素地址,以下2种除外: ①、sizeof(数组名) 表示整个数组 ※只有数组名的情况 sizeof(数组名i) 就不能表示整个数组 ②、&数组名 表示整个数组,取的是整个数…...
前端 如何用 div 标签实现 步骤审批
在前端实现一个步骤审批流程,通常是通过 div 标签和 CSS 来构建一个可视化的流程图,结合 JavaScript 控制审批的状态变化。你可以使用 div 标签创建每一个步骤节点,通过不同的样式(如颜色、边框等)表示审批的不同状态&…...
QT工程,它该怎么学?
在现代软件开发中,QT因其强大的跨平台能力和友好的用户界面设计工具,成为开发者学习和应用的热门选择。特别是在Linux系统下,如何安装、配置QT开发环境,以及创建和管理QT工程是入门QT开发的关键环节。本文将从安装QT开发环境开始&…...
第426场周赛:仅含置位位的最小整数、识别数组中的最大异常值、连接两棵树后最大目标节点数目 Ⅰ、连接两棵树后最大目标节点数目 Ⅱ
Q1、仅含置位位的最小整数 1、题目描述 给你一个正整数 n。 返回 大于等于 n 且二进制表示仅包含 置位 位的 最小 整数 x 。 置位 位指的是二进制表示中值为 1 的位。 2、解题思路 我们需要找到一个整数 x,使得: x ≥ nx 的二进制表示中仅包含置位…...
23种设计模式之外观模式
目录 1. 简介2. 代码2.1 SelectFoodService (选择食品)2.2 PayService (支付服务)2.3 TakeService (制作服务)2.4 OrderService (下单服务)2.5 Food (食品)2.6 TackingSystem (外观类)2.7 Test (测试类) 3. 优缺点3. 总结 1. 简介…...
【智商检测——DP】
题目 代码 #include <bits/stdc.h> using namespace std; const int N 1e510, M 110; int f[N][M]; int main() {int n, k;cin >> n >> k;for(int i 1; i < n; i){int x;cin >> x;f[i][0] __gcd(f[i-1][0], x);for(int j 1; j < min(i, k)…...
LeetCode-430. 扁平化多级双向链表-题解
题目链接 430. 扁平化多级双向链表 - 力扣(LeetCode) 题目介绍 你将得到一个双链表,节点包含一个“下一个”指针、一个“前一个”指针和一个额外的“子指针”。这个子指针可能指向一个单独的双向链表,并且这些链表也包含类似的特殊…...
【CSS】一篇掌握CSS
不是因为有了希望才去坚持,而是坚持了才有了希望 目录 一.导入方式 1.行内样式 2.内部样式 3.外部样式(常用) 二.选择器 1.基本选择器(常用) 1.1标签选择器 1.2类选择器 1.3id选择器 2.层次选择器 2.1后代选择器 2.2子选择器 2.3相邻兄弟选择器 2.4通用兄弟选择器…...
华为仓颉编程环境搭建
1、仓颉介绍 摘自华为官方:仓颉编程语言作为一款面向全场景应用开发的现代编程语言,通过现代语言特性的集成、全方位的编译优化和运行时实现、以及开箱即用的 IDE 工具链支持,为开发者打造友好开发体验和卓越程序性能。 其具体特性表现为&am…...
手机实时提取SIM卡打电话的信令声音-蓝牙电话如何适配eSIM卡的手机
手机实时提取SIM卡打电话的信令声音 --蓝牙电话如何适配eSIM卡的手机 一、前言 蓝牙电话的海外战略中,由于海外智能手机市场中政策的差异性,对内置eSIM卡的手机进行支持是非常合理的需求。Android系列手机中,无论是更换通信运营商…...
三种方式(oss、本地、minio)图片的上传下载
一、OSS 1、前期准备 1.1 注册阿里云账号,开启对象存储oss功能,创建一个bucket(百度教程多的是,跟着创建一个就行,创建时注意存储类型是标准存储,读写权限是公共读) 有的在创建桶时读写属性是…...
使用pyQT完成简单登录界面
import sysfrom PyQt6.QtGui import QMovie,QPixmap from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QPushButton,QLineEdit#封装我的窗口类 class MyWidget(QWidget):#构造函数def __init__(self):#初始化父类super().__init__()# 设置窗口大小self.resize(330,…...
Postgres数据库自动化分区
一.创建自动化分区配置表并插入数据 -- Table: managerdb.par_info-- DROP TABLE IF EXISTS managerdb.par_info;CREATE TABLE IF NOT EXISTS managerdb.par_info (table_schema character varying(255) COLLATE pg_catalog."default" NOT NULL,table_name characte…...
【技术介绍】C++编程语言中的瑰宝
C,这门源于C语言并在其基础上进行大幅增强的编程语言,自诞生以来便以其独特的魅力和强大的功能吸引了无数编程者的目光。它不仅是计算机科学领域的一颗璀璨明珠,更是现代软件开发中不可或缺的重要工具。 解析【前言】 C的命名,寓…...
nginx反向代理
目录 环境准备 启动HTTP服务 配置Nginx 访问 部署 1.配置nginx 2.自动化脚本 3.执行脚本 4.使用ansible 什么是反向代理呢,参考nginx反向代理,业务部署过长中,常遇到的场景如下,通过访问域名/ip地址,后面接入网…...
分层图最短路
常见情形: 对于边有k次操作的题。。 整体思想: 分层图最短路可以视作是dijkstra的一个扩展,通常用于处理N小于10000,或者是k不大的情形。整体有点类似于拆点。将一个点拆成k个点处理。层与层之间互不影响。 好了我就说这么多&…...
FRU文件
FRU(Field Replaceable Unit)源文件的格式通常遵循IPMI FRU Information Storage Definition标准。在实际应用中,FRU源文件可以是JSON格式的,这种格式允许用户指定所有的FRU信息字段。以下是FRU源文件的JSON格式的一些关键点&…...