首页 > 其他分享 >CUDA:out of memory的解决方法(实测有效)

CUDA:out of memory的解决方法(实测有效)

时间:2024-11-27 10:01:42浏览次数:9  
标签:显存 self torch train CUDA memory import model out

一、问题概述       

1.问题分析         

CUDA out of memory问题通常发生在深度学习训练过程中,当GPU的显存不足以容纳模型、输入数据以及中间计算结果时就会触发。这个问题可能由几个因素引起:

  1. 模型和数据规模:深度学习模型尤其是大型模型,如Transformer或大型CNN,拥有大量的参数,这些参数在训练时需要被加载到GPU显存中。同时,如果批量大小(batch size)设置得过大,一次性处理的数据量也会增加,进一步加大显存的负担。

  2. 内存管理:深度学习框架在处理数据和模型时,可能会因为不当的内存管理而导致显存没有被及时释放,或者由于频繁的内存分配和释放造成内存碎片化,这会减少可用的连续内存块,即使总显存使用量没有达到100%,也可能出现内存不足的情况。

  3. 数据加载和预处理:如果数据加载和预处理步骤没有优化,比如加载了大量未被立即使用的数据,或者数据没有被适当地压缩或降维,也可能导致显存使用量激增。

2.报错代码

        

3.示例代码       

 这里提供一个简单的深度学习示例代码,使用PyTorch框架来实现一个简单的多层感知器(MLP)模型,用于分类MNIST数据集中的手写数字。MNIST是一个包含60,000个训练样本和10,000个测试样本的数据库,每个样本都是一个28x28的灰度图像,代表一个手写数字(0到9)。

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# 定义一个简单的多层感知器模型
class SimpleMLP(nn.Module):
    def __init__(self):
        super(SimpleMLP, self).__init__()
        self.fc1 = nn.Linear(28 * 28, 512)  # 输入层到隐藏层
        self.fc2 = nn.Linear(512, 256)      # 隐藏层到隐藏层
        self.fc3 = nn.Linear(256, 10)       # 隐藏层到输出层

    def forward(self, x):
        x = x.view(-1, 28 * 28)  # 展平图像
        x = F.relu(self.fc1(x))  # 第一个隐藏层
        x = F.relu(self.fc2(x))  # 第二个隐藏层
        x = self.fc3(x)          # 输出层
        return x

# 设置数据加载器
transform = transforms.Compose([
    transforms.ToTensor(),  # 将图片转换为Tensor
    transforms.Normalize((0.5,), (0.5,))  # 归一化
])

train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
test_dataset = datasets.MNIST(root='./data', train=False, transform=transform)

train_loader = DataLoader(dataset=train_dataset, batch_size=32, shuffle=True)
test_loader = DataLoader(

二、修改策略        

为了解决这些问题,我们可以采取多种策略。首先,调整批量大小是一种简单有效的方法。通过减少每次迭代中处理的数据量,可以减轻显存压力,但可能会影响模型训练的效率和最终性能。其次,梯度累积技术可以在不改变批量大小时,通过累积多个小批量的梯度来模拟大批量的效果,这样可以在保持较大有效批量的同时减少单次迭代的显存需求。

        模型优化也是一个重要的方向。通过剪枝、量化或知识蒸馏等技术减少模型大小,或者重新设计模型结构以减少参数数量,可以有效地减少显存的使用。混合精度训练,即在训练过程中使用半精度浮点数(FP16)代替全精度浮点数(FP32),不仅可以减少显存使用,还可以加速训练过程。在内存管理方面,我们可以在PyTorch中通过删除不再需要的变量并调用torch.cuda.empty_cache()来释放未使用的显存。此外,通过设置环境变量来调整CUDA的内存分配策略,减少内存碎片化,也是一个有效的手段。

        最后,分布式训练可以将模型和数据分布到多个GPU上,从而分散显存压力。这种方法虽然需要更多的硬件资源,但在处理特别大的模型或数据集时,可以显著提高训练的可行性和效率。

1. 减小批量大小(Batch Size)

减小批量大小是解决CUDA内存不足的简单有效方法。以下是代码示例:

import torch
import torchvision.models as models

# 尝试较大的批量大小
model = models.resnet50().cuda()  # 将模型放到GPU上
input = torch.randn(32, 3, 224, 224).cuda()  # 大批量的输入数据
try:
    output = model(input)  # 尝试运行模型
except RuntimeError as e:
    if 'out of memory' in str(e):
        print("CUDA内存不足,尝试减少批量大小...")
        torch.cuda.empty_cache()  # 清理缓存
        input = torch.randn(16, 3, 224, 224).cuda()  # 减小批量大小后重试
        output = model(input)

2. 使用梯度累积(Gradient Accumulation)

通过累积梯度,可以在不增加显存压力的情况下训练更大批量的数据。以下是代码示例:

optimizer.zero_grad()
for i in range(gradient_accumulation_steps):
    loss = model(input[i]).backward()
optimizer.step()

3. 清理显存

在不需要变量时手动删除,并调用 torch.cuda.empty_cache() 来释放GPU内存。

import torch, gc

# 清理Python垃圾回收
gc.collect()
# 清理PyTorch缓存
torch.cuda.empty_cache()

4. 使用混合精度训练(Mixed Precision Training)

通过使用混合精度训练,可以减少内存的使用。以下是代码示例:

from torch.cuda.amp import GradScaler, autocast

scaler = GradScaler()

for data, target in train_loader:
    optimizer.zero_grad()
    with autocast():
        output = model(data)
        loss = criterion(output, target)
    scaler.scale(loss).backward()
    scaler.step(optimizer)
    scaler.update()

5. 设置环境变量减少内存碎片化

通过设置 PYTORCH_CUDA_ALLOC_CONF 环境变量,调整内存分配策略,减少内存碎片化。

import os
os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'max_split_size_mb:128'

这些代码示例提供了几种不同的方法来解决CUDA内存不足的问题,可以根据具体情况选择合适的策略。

三、示例修改

        这个代码将包括一些改进,比如使用更小的批量大小来减少显存使用,以及使用梯度累积来模拟更大的批量大小。此外,我还会添加一些注释来帮助理解代码的每个部分。

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# 定义一个简单的多层感知器模型
class SimpleMLP(nn.Module):
    def __init__(self):
        super(SimpleMLP, self).__init__()
        self.fc1 = nn.Linear(28 * 28, 512)  # 输入层到隐藏层
        self.fc2 = nn.Linear(512, 256)      # 隐藏层到隐藏层
        self.fc3 = nn.Linear(256, 10)       # 隐藏层到输出层

    def forward(self, x):
        x = x.view(-1, 28 * 28)  # 展平图像
        x = F.relu(self.fc1(x))  # 第一个隐藏层
        x = F.relu(self.fc2(x))  # 第二个隐藏层
        x = self.fc3(x)          # 输出层
        return x

# 设置数据加载器
transform = transforms.Compose([
    transforms.ToTensor(),  # 将图片转换为Tensor
    transforms.Normalize((0.5,), (0.5,))  # 归一化
])

train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
test_dataset = datasets.MNIST(root='./data', train=False, transform=transform)

# 使用更小的批量大小以减少显存使用
batch_size = 16
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)

# 初始化模型、损失函数和优化器
model = SimpleMLP().cuda()  # 将模型移到GPU上
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 梯度累积步骤
gradient_accumulation_steps = 4

# 训练模型
def train_model(model, criterion, optimizer, train_loader, test_loader, epochs=5):
    for epoch in range(epochs):
        model.train()  # 设置模型为训练模式
        running_loss = 0.0
        for i, (images, labels) in enumerate(train_loader):
            images, labels = images.cuda(), labels.cuda()  # 将数据移到GPU上
            optimizer.zero_grad()  # 清空梯度

            # 梯度累积
            for _ in range(gradient_accumulation_steps):
                outputs = model(images)  # 前向传播
                loss = criterion(outputs, labels)  # 计算损失
                loss = loss / gradient_accumulation_steps  # 将损失除以累积步数
                loss.backward()  # 反向传播

            optimizer.step()  # 更新权重
            running_loss += loss.item() * gradient_accumulation_steps

        print(f'Epoch {epoch+1}, Loss: {running_loss/len(train_loader)}')

        # 测试模型
        model.eval()  # 设置模型为评估模式
        correct = 0
        total = 0
        with torch.no_grad():  # 在测试阶段不计算梯度
            for images, labels in test_loader:
                images, labels = images.cuda(), labels.cuda()  # 将数据移到GPU上
                outputs = model(images)
                _, predicted = torch.max(outputs.data, 1)
                total += labels.size(0)
                correct += (predicted == labels).sum().item()

        print(f'Accuracy of the network on the test images: {100 * correct / total}%')

# 运行训练
train_model(model, criterion, optimizer, train_loader, test_loader, epochs=5)

在这个修改后的代码中,做了以下几点改进:

  1. 减小批量大小:将批量大小从32减少到16,以减少每次迭代中GPU需要处理的数据量。
  2. 梯度累积:通过设置gradient_accumulation_steps为4,我们可以在不增加显存压力的情况下,通过累积梯度来模拟更大的批量大小。
  3. 数据和模型移到GPU:在训练和测试阶段,将数据和模型移到GPU上,以利用GPU的计算能力。

这些改进可以帮助减少显存的使用,同时保持模型训练的效率和性能。

标签:显存,self,torch,train,CUDA,memory,import,model,out
From: https://blog.csdn.net/qq_63129682/article/details/144073511

相关文章

  • LFS导致GitLab: Push operation timed out
    问题pushaosp(单独一个git):$gitpushoriginXXXX:XXXXCountingobjects:1468867,done.Deltacompressionusingupto24threads.Compressingobjects:100%(768892/768892),done.Writingobjects:100%(1468867/1468867),29.98GiB|24.27MiB/s,done.Total14......
  • WebRTC 作者加入 OpenAI 主导实时 AI 项目;TTS 小模型 OuteTTS v0.2 发布:声音克隆+多语
       开发者朋友们大家好: 这里是「RTE开发者日报」,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享RTE(Real-TimeEngagement)领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「有看点的会议」,但内容仅代表编......
  • 2025 财务 AI 浪潮来袭,这些工具你还不知道就 OUT 了!
        随着2025年财务AI浪潮的到来,一批实用的AI工具在财务领域崭露头角。Trullion是人工智能驱动的会计监督平台,其主要功能为简化收入确认、租赁会计和审计流程。它能自动化复杂手动工作流程,提高准确性并确保持续遵守最新会计准则,自动执行计算且确保准确报告,节省......
  • Sigrity OptimizePI Post-Layout Analysis模式如何对PDS系统进行电容统计分析操作指导
    SigrityOptimizePIPost-LayoutAnalysis模式如何对PDS系统进行电容统计分析操作指导-StatisticalAnalysis下面介绍使用SigrityOptimizePIPost-LayoutAnalysis中的Statistical模式进行电容公差的统计分析,统计分析就是分析电容的Upper和LowerTol公差对于阻抗的影响.分别......
  • [Javascript] About private access (Object.getOwnPropertySymbols)
    Wehaveamodule:constkey=Symbol('key')exportclassA{[key]=1value(){console.log(this[key])}}Itseemsthat keyisnotexposetooutsideofmodule,butstillweareabletogetit.import{A}from'./mod......
  • ISSCC2025 Computing-In-Memory Session 趋势整理
    今天下午ISSCC2025发布会开完,CIMSession花落谁家终于清楚了。今年CIM被放到了Session14,共录取七篇,投稿数如果和去年差不多的话,那么录取率应该是进一步下降了(去年录取了九篇)。只能说体感上来说就明显越来越卷。还是先来看一下录取的Paper:7篇都来自远东,两篇台湾,五篇大陆,东南......
  • 2024-2025 ICPC, NERC, Southern and Volga Russian Regional Contest(ABCGJLN)
    文章目录N.FixingtheExpression思路codeJ.Waitingfor...思路codeC.DIY思路codeL.BridgeRenovation思路codeA.BonusProject思路codeG.GuessOneCharacter思路codeB.MakeItEqual思路codeN.FixingtheExpression思路签到题,只改变中间的字符即......
  • The 3rd Universal Cup. Stage 18: Southeastern Europe
    A.All-Star每次操作至多可以把一个点插在根上,因此选择度数最多的点插在根上,然后根据深度标记边的方向。#include<bits/stdc++.h>usingnamespacestd;usingi32=int32_t;usingvi=vector<int>;usingpii=pair<int,int>;i32main(){ ios::sync_with_stdio(fals......
  • 说说你对hasLayout的理解,触发hasLayout的方式有哪些?
    hasLayout是InternetExplorer(IE)浏览器(版本6到8)中的一个专有概念,它描述了一个元素是否控制其自身及其子元素的尺寸和定位。当一个元素拥有hasLayout时,它会建立一个新的布局上下文,这意味着IE会为该元素及其子元素创建一个独立的布局容器。这会影响元素的尺寸计算、定......
  • [Vue] Routes auto generator
    Needtofollowsomerules:Eachfoldershouldhasapage.jsconfigurationtosavethemetadata.//page.jsexportdefault{title:"About",menuOrder:1,}; Generator.js:constpages=import.meta.glob('../views/**/page.js'......