深度学习中的模型并行

一、概念

模型并行是深度学习中用于训练大型神经网络的一种并行计算策略。它的目的是将一个大型模型的不同部分分布到多个GPU或计算设备上,从而实现模型的高效训练。与数据并行不同,模型并行关注的是模型的大小,特别是当模型太大而无法在单个GPU上容纳时。

模型并行的原理是人为地将模型的层或子网络分配到不同的GPU上,每个GPU负责模型的一部分计算,并通过通信接口(如PCIe或InfiniBand)交换中间结果。这样可以使得每个GPU只存储和处理模型的一部分参数,从而允许训练更大的模型。


二、基本流程

1、模型分割

将大型模型 分割成多个子模型或层,这些子模型或层可以并行处理。

2、设备分配

将分割后的模型部分分配到不同的GPU上。这通常基于模型的结构和计算需求。

3、前向传播

在前向传播过程中,数据在不同的GPU间传递,每个GPU计算其分配到的部分的输出。

4、梯度计算

在反向传播过程中,梯度需要在各个GPU间传递,以便每个部分的梯度可以被计算并用于更新模型参数。

5、梯度同步

为了确保模型参数的一致性,梯度需要在所有GPU上同步。

6、参数更新

根据聚合后的梯度更新模型参数。


三、python 实现

这里,我们构建一个非常简单的示例,用于帮助读者理解。我们首先构造一个6层的MLP,每一层隐藏层的参数量均为1024;接着,将这个MLP的前3层放入0号GPU,后三层放入1号GPU,并使用sklearn的wine数据集进行训练和验证。

1、导入必要的库

import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
 
# 检查是否有可用的GPU
device0 = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
device1 = torch.device("cuda:1" if torch.cuda.device_count() > 1 else "cpu")

2、数据集处理

加载wine数据集并进行训练集和测试集的划分,标准化数据之后转化为torch.tensor。

# 加载和预处理数据
wine = load_wine()
X = wine.data
y = wine.target
 
# 标准化特征
scaler = StandardScaler()
X = scaler.fit_transform(X)
 
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
 
# 转换为PyTorch张量
X_train = torch.tensor(X_train, dtype=torch.float32)
y_train = torch.tensor(y_train, dtype=torch.long)
X_test = torch.tensor(X_test, dtype=torch.float32)
y_test = torch.tensor(y_test, dtype=torch.long)

3、神经网络构建

这里要注意一个细节,我们在前向传播函数forward中定义数据的动向,从而使得同一份数据可以在多个GPU之间共享。

# 定义神经网络
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(13, 1024)
        self.fc2 = nn.Linear(1024, 1024)
        self.fc3 = nn.Linear(1024, 1024)
        self.fc4 = nn.Linear(1024, 1024)
        self.fc5 = nn.Linear(1024, 1024)
        self.fc6 = nn.Linear(1024, 3)
 
    def forward(self, x):
        # 首先将数据传入GPU0,在前三层网络上进行前向传播
        x = x.to(device0)
        x = torch.relu(self.fc1(x.to(device0)))
        x = torch.relu(self.fc2(x))
        x = torch.relu(self.fc3(x))
        # 再将数据传入GPU1,完成后三层网络的前向传播
        x = x.to(device1)
        x = torch.relu(self.fc4(x))
        x = torch.relu(self.fc5(x))
        x = self.fc6(x)
        return x

下面实例化模型并将各层加载到对应的目标设备中:

# 创建模型实例
model = Net()
 
# 将前3层放入GPU0,后3层放入GPU1
model.fc1.to(device0)
model.fc2.to(device0)
model.fc3.to(device0)
model.fc4.to(device1)
model.fc5.to(device1)
model.fc6.to(device1)

4、模型训练

下面,我们训练模型。需要注意的是,真实标签y_train应当传入最后一层所在的GPU,这样才能正确计算loss。在调用loss.backward()函数之后,模型会将损失按照前向传播的路径反过来自动进行反向传播,不需要我们额外进行操作。

# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
 
# 训练模型
num_epochs = 50
for epoch in range(num_epochs):
    model.train()
    optimizer.zero_grad()
    
    outputs = model(X_train)
    loss = criterion(outputs, y_train.to(device1))
    
    loss.backward()
    optimizer.step()
    
    if (epoch+1) % 10 == 0:
        print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

5、评估模型

完成训练之后,进行模型性能的评估,这个例子中所有指标的结果都是1,毕竟数据太简单,模型太复杂。

# 测试模型
model.eval()
with torch.no_grad():
    outputs = model(X_test)
    _, predicted = torch.max(outputs.data, 1)
    accuracy = accuracy_score(y_test.cpu(), predicted.cpu())
    precision = precision_score(y_test.cpu(), predicted.cpu(), average='weighted')
    recall = recall_score(y_test.cpu(), predicted.cpu(), average='weighted')
    f1 = f1_score(y_test.cpu(), predicted.cpu(), average='weighted')
    print(f'Accuracy: {accuracy:.4f}')
    print(f'Precision: {precision:.4f}')
    print(f'Recall: {recall:.4f}')
    print(f'F1 Score: {f1:.4f}')

四、总结

通过上面的实践,相信大家对于模型并行已经有了深刻的了解了。即便真的需要微调超大模型,使用类似的途径也能够易如反掌。当然,上面的实现方式可拓展性不强,且实现过于简单,通过使用torch.nn.parallel.DistributedDataParallel等工具可以实现更为高效的模型并行,且大大提升GPU之间的通信效率,这些内容我们后面再深入探究。


版权声明:本文为CSDN博主「CM莫问」的原创文章,
遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ChaneMo/article/details/144357450

最新文章