数据并行、模型并行与张量并行:深度学习中的并行计算策略(中英双语)

中文版

数据并行、模型并行与张量并行:深度学习中的并行计算策略

随着深度学习模型的不断增大,单个计算节点(例如单个 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=1Pθ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 ),计算流程如下:

  1. 在 ( G P U 1 GPU_1 GPU1 ) 上计算第一部分的输出 ( h 1 = f 1 ( x ) h_1 = f_1(x) h1=f1(x) )。
  2. 将 ( 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) )。
  3. 最终输出 ( y = h 2 y = h_2 y=h2 )。
示例代码:
import torch
import torch.nn as nn

class 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=AB ),其中 ( 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=(A1B)+(A2B)++(A

上一篇:【C++笔记】继承


下一篇:ONVIF协议网络摄像机客户端使用gsoap获取RTSP流地址GStreamer拉流播放