Coding Corner

Last updated on November 21, 2024 pm

Linux-Ubuntu系统安装gcc和g++

GCC:GNU Compiler Collection(GNU编译器集合),它可以编译C、C++、JAV、Fortran、Pascal、Object-C、Ada等语言。

gcc是GCC中的GUN C Compiler(C 编译器);g++是GCC中的GUN C++ Compiler(C++编译器)。

1.查看当前版本

gcc -v (gcc --version)
g++ -v (g++ --version)

2.查看目录下已有的gcc(g++)版本

ls /usr/bin/ | grep gcc (g++)

3.安装对应版本的gcc和g++

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 20
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-7 20

最后的数值(20)表示优先级,数值越大,优先级越高,可以自行设定。

4.管理gcc(g++)的版本

sudo update-alternatives --config gcc (g++)

上述指令执行后,用户可以自行选择需要的版本,只需要填入相应版本前面的号码再按回车即可。如果目录中已经有需要的版本,便不需要第3步的下载安装了。最后再键入步骤1的指令查看当前版本是否更改成功。

VSCode搭建C++运行环境

参考链接:https://zhuanlan.zhihu.com/p/643934671

VSCode(全称:Visual Studio Code)是一款由微软开发且跨平台的免费源代码编辑器,轻量可扩展。因为它完全免费,启动速度超级快(没有对比就没有伤害),再加上默认的dark主题很舒服(比Pycharm自带的好很多),所以我相当喜欢这款软件。

下载安装VSCode

这一步我就不多说了,网上教程很多,跟着操作就好。然后安装C/C++扩展。

下载编译器mingw

下载链接:MinGW-W64 GCC/G++

在"Files"栏往下翻页,在"Totals"之下有很多版本的mingw,可以选择MinGW-W64 GCC-8.1.0的x86_64-win32-seh版本,下载并解压至自己想放的文件夹里,记住路径。

编辑系统环境变量

桌面右键"此电脑",点击"属性",然后选择"高级系统设置",依次点击"高级" - "环境变量",在系统变量中点击"Path" - "编辑" - "新建",把mingw64文件夹下的bin文件夹的路径拷贝进去,一路"确定"保存即可。

接下来进入cmd(command的缩写,即Windows命令提示符,是命令行操作工具,也叫DOS窗口),输入指令gcc -v或者g++ -v(上篇中也介绍过gcc/g++编译器),显示了版本就说明环境已经配置成功啦!

配置json文件

写一段C++代码(后缀名为.cpp),如test.cpp,通过VSCode打开,按快捷键 Ctrl + Shift + P 调出面板,输入:C/C++,选择"编辑配置(UI)",在VSCode目录里会出现.vscode文件夹,里面有一个"c_cpp_properties.json"文件。

在这个配置界面里,编辑器路径根据自己的需求设置:C语言选gcc,C++选g++。编辑器路径是mingw的路径。

IntelliSense模式设置成:gcc-x64(legacy),将C标准设置为c11,C++标准设置为c++17。当然,这里也可以将C/C++标准设置成更高的版本。

此时,c_cpp_properties.json中的文件内容应该是这样的:

{
    "configurations": [
        {
            "name": "Win32",
            "includePath": [
                "${workspaceFolder}/**"
            ],
            "defines": [
                "_DEBUG",
                "UNICODE",
                "_UNICODE"
            ],
            "compilerPath": "/自己的MinGW路径/mingw64/bin/g++.exe",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "gcc-x64"
        }
    ],
    "version": 4
}

配置构建任务

回到test.cpp界面,按下 Ctrl + Shift + P 调出面板,输入:tasks,选择"任务:配置默认生成任务",然后选择"C/C++:g++.exe生成活动文件",此时.vscode文件夹内会多出一个"tasks.json"文件。文件内容如下:

{
	"version": "2.0.0",
	"tasks": [
		{
			"type": "cppbuild",
			"label": "C/C++: g++.exe 生成活动文件",
			"command": "/自己的MinGW路径/mingw64/bin/g++.exe",
			"args": [
				"-fdiagnostics-color=always",
				"-g",
				"${file}",
				"-o",
				"${fileDirname}\\${fileBasenameNoExtension}.exe"
			],
			"options": {
				"cwd": "/自己的MinGW路径/mingw64/bin"
			},
			"problemMatcher": [
				"$gcc"
			],
			"group": {
				"kind": "build",
				"isDefault": true
			},
			"detail": "编译器: \"/自己的MinGW路径/mingw64/bin/g++.exe\""
		}
	]
}

配置调试设置

回到test.cpp界面,按下 Ctrl + Shift + P 调出面板,输入:debug,选择"调试:开始调试",再选择"C++ (GDB/LLDB)",然后点击VSCode左上角运行和调试下面的蓝色按钮:"创建launch.json文件",再选择"C++ (GDB/LLDB)"。

此时,.vscode文件夹内会生成一个"launch.json"文件。文件内容中"configurations"属性为空。把launch.json文件中的"configurations"属性内容修改为:

{
    "name": "g++.exe - 生成和调试活动文件",
    "type": "cppdbg",
    "request": "launch",
    "program": "${fileDirname}\\${fileBasenameNoExtension}.exe",
    "args": [],
    "stopAtEntry": false,
    "cwd": "${workspaceFolder}",
    "environment": [],
    "externalConsole": true,
    "MIMode": "gdb",
    "miDebuggerPath": "/自己的MinGW路径/mingw64/bin/gdb.exe",
    "setupCommands": [
        {
            "description": "为 gdb 启用整齐打印",
            "text": "-enable-pretty-printing",
            "ignoreFailures": true
        }
    ],
    "preLaunchTask": "C/C++: g++.exe 生成活动文件"
}

验证可行性

回到test.cpp界面,在某条语句前打个断点,按F5进行调试(或者按菜单栏右边的相关按钮),如果调试成功,则说明所有的配置都已经成功了,可以正常使用C++进行编辑、运行和调试了!

PS:Debug是调试工具,目的是检查程序中可能存在的bug,然后供程序员进行修复。

补充:搭建C语言的运行环境

如果想用VSCode搭建 C 语言编译环境,步骤同上,但是 json 文件中的某些指令需要稍作修改。

tasks.json文件只需修改 "tasks": [ ... ] 中的属性 "label" 和 "detail" 为下列语句即可:

"label": "C/C++: gcc.exe 生成活动文件",
"detail": "编译器: \"/自己的MinGW路径/mingw64/bin/gcc.exe\""

launch.json文件中的"configurations"属性内容中,只需修改属性 "name" 和 "preLaunchTask" 为下列语句即可:

"name": "gcc.exe - 生成和调试活动文件",
"preLaunchTask": "C/C++: gcc.exe 生成活动文件"

Linux命令行调试

代码的调试(Debug)是软件开发过程中一个至关重要的环节,它指的是在程序开发过程中发现并修正错误(bug)。调试是确保软件按照预期运行的关键步骤。在调试过程中,开发人员通常会使用调试工具(Debugger)来辅助进行。

调试工具允许开发人员执行以下操作:

  • 设置断点:在代码的特定位置设置断点,使得程序在到达该位置时暂停执行。这样开发人员可以查看并控制此时程序的状态。
  • 单步执行:使用Step Over、Step Into、Step Out等命令逐行或逐过程地执行代码,以便观察程序的执行流程。
  • 查看变量值:在程序暂停时,开发人员可以查看和修改程序中的变量值,以便理解程序当前的状态。
  • 调用栈跟踪:当程序出现错误时,调试工具可以显示调用栈(Call Stack),即函数调用的层次结构,帮助开发人员定位错误发生的具体位置。
  • 条件断点:设置当满足特定条件时才触发的断点,以便更精确地控制程序的执行流程。
  • 异常捕获:调试工具可以捕获程序运行时的异常,并显示异常信息和调用栈,帮助开发人员定位和解决问题。

几种不同的调试方式

在程序调试中,Step Over、Step Into、Step Into my code 和 Step Out 是四种不同的调试步骤,它们各自有特定的用途和行为。

  • Step Over(逐过程执行):当你执行到一行代码,并且这行代码是一个函数调用时,Step Over 会执行整个函数(不会进入函数内部),也就是把整个函数作为一步,然后在函数调用的下一行代码处停止。这在你想要忽略某些函数细节,只想查看程序的主要流程时很有用。

  • Step Into(单步执行/逐语句执行):当你执行到一行代码,并且这行代码是一个函数调用时,Step Into 会进入函数内部,并逐行执行函数内的代码。这对于你想要深入了解函数内部的执行细节时很有用。

  • Step Into my code(仅进入我的代码):这是某些集成开发环境(IDE)中的一个选项,特别是当你调试包含第三方库或框架的代码时。当你执行到一行代码,并且这行代码是一个函数调用,但该函数调用属于第三方库或框架时,Step Into my code 会忽略这个函数调用,并在该函数调用后的下一行代码处停止。这可以帮助你专注于自己的代码,而不用进入第三方库的细节中。

  • Step Out(步出):当你已经单步进入了一个函数,并想要快速返回到调用这个函数的代码行时,你可以使用 Step Out。Step Out 后会执行完当前函数内的剩余代码(但不会进入任何子函数),然后返回到调用这个函数的代码行处停止。

python交互环境中启用调试

在Linux命令行模式下,有两种常用的代码调试方式,它们都是基于 pdb(Python Debugger)的。一种是在文件内使用 pdb 模块在需要的地方进行调试,另一种是在命令行以 pdb 指令的形式从代码第一行开始调试。

例如我要调试一个文件名提取函数(FileNameExtraction.py),

(1)在文件中可以在指定位置加入调试代码:

import pdb
<-- code snippet I -->
pdb.set_trace()
<-- code snippet II -->

运行上面的代码后,程序会在 pdb.set_trace() 处暂停,此时可以使用 pdb 提供的命令对代码进行调试。

(2)在命令行启动目标程序:

python -m pdb FileNameExtraction.py

然后会进入下图所示的 pdb 调试模式:

进入pdb调试模式

这样程序会自动停在第一行,等待你进行调试。接下来可以使用调试命令进行调试。常用的调试命令有:

(Pdb) b 8  # 设置断点:断点设在该文件的第8行(b: break point)
(Pdb) b  # 显示所有断点:b命令,不加参数
(Pdb) cl 2  # 删除断点:删除第2个断点(cl: clear)
(Pdb) cl  # 删除所有断点

(Pdb) n  # Step Over:向下执行一行(n: next)
(Pdb) s  # Step Into:进入一个函数(s: step)
(Pdb) r  # Setp Return:快速执行到函数的最后一行(r: return)
(Pdb) c  # Resume:继续执行代码(c: continue)
(Pdb) j 10  # Run to Line:运行到指定行,即第10行(j: jump)

(Pdb) p a,b  # 查看变量 (a, b) 的值(p: parameter)
(Pdb) l  # 查看当前代码,即运行到的代码(l: list)
(Pdb) a  # 查看全部栈内变量,打印所有形参语句(a: args)

(Pdb) h  # 帮助(h: help)
(Pdb) q  # 退出调试(q: quit)

Anaconda/pip常用命令

Anaconda是可以便捷获取包且能对包和环境进行统一管理的发行版本。Anaconda包含了conda、Python在内的超过180个科学包及其依赖项。

conda是包及其依赖项和虚拟环境的管理工具,pip是用于安装和管理软件包的包管理器。conda结合了pip和virtualenv(用于创建独立的Python环境的工具)的功能,安装包时自动安装其依赖项,可以便捷地在包的不同版本中自由切换,且适配很多编程语言,因此推荐使用conda。

environment

虚拟环境是隔离不同Python项目(或不同的Python版本、不同的包)的机制。

新建虚拟环境:

conda create -n env_name python=x.x
conda create --name env_name python=3.8

查看已存在的虚拟环境:

conda info -e
conda env list
conda info --envs

进入(激活)虚拟环境:

conda activate env_name

退出当前虚拟环境:

conda deactivate

(完整)删除虚拟环境:

conda remove -n env_name --all

如果要创建一个轻量化的虚拟环境,也可以使用Python标准库自带的 venv 模块,下面的命令创建了python3.8版本的虚拟环境(python3.8要事先安装):

python3.8 -m venv env_name

此时虚拟环境会保存在当前目录中,若需要删除该环境,手动删除文件夹即可。

启动虚拟环境:

. env_name/bin/activate
source env_name/bin/activate

packages

查看所有已安装的包:

conda list
pip list

查看某个包是否在当前环境中:

conda list pkg_name
pip show pkg_name

# pip show package_name 会显示package的安装路径
# 一般默认的安装路径: (Environment position).../lib/site_packages/

安装某个包:

conda install pkg_name[=version]
pip install some_package[==x.x]

# -r表示从指定的文件中读取每行的内容(待安装的包列表)
# 指定文件的每行须是一个包名,可以用==或>=指定版本号
pip install -r requirements.txt
pip install --requirement FILE_name

更新包:

conda update pkg_name
conda update --all

删除包:

conda remove pkg_name
pip uninstall some_package

导出/安装环境中的包:

pip freeze > ./requirements.txt  # 导出到文件
pip install -r requirements.txt  # 在线安装

检验torch是否安装成功:

python  # 进入Python开发环境
>>> import torch  # 未报错
>>> torch.cuda.is_available()  # 输出: True
>>> exit()  # 大功告成!退出即可

建议首选conda进行管理。若conda没有相应的包,再使用pip指令。不过pip下载安装的响应很快,且基本不会报错,而conda容易报错,尤其是用conda安装torch的时候。

非root用户安装CUDA和cudnn

CUDA和cudnn介绍

CUDA 是 NVIDIA 推出的一个并行计算平台和编程模型,允许开发者使用 GPU 进行高性能计算。CUDA 本质上是一个软件架构(框架),包含一些开发工具包(如编译器、库和驱动程序),使得开发者可以使用类似于 C 语言的 CUDA C/C++ 代码直接控制 GPU 的计算资源。

CUDA 是许多高层库(例如 cuDNN、TensorRT、Thrust)构建的基础,所有基于 NVIDIA GPU 的计算都依赖于 CUDA 支持。CUDA 主要用于加速各类计算密集型任务,比如科学计算、图像处理、机器学习和人工智能等。

有人说:CUDA是一门编程语言,像 C,C++,python 一样,也有人说CUDA是API。

官方说:CUDA是一个并行计算平台和编程模型,能够使得使用GPU进行通用计算变得简单和优雅。

运行CUDA应用程序要求系统至少具有一个具有CUDA功能的GPU和与CUDA Toolkit兼容的驱动程序。

查看CUDA版本命令:nvcc -V 或 nvcc --version 或 cat /usr/local/cuda/version.txt

cuDNN 是一个深度学习专用的 GPU 加速库,基于 CUDA 构建。它提供高效的底层实现,支持卷积神经网络(CNN)所需的关键操作,比如卷积运算、池化、归一化等,是为深度学习框架量身定制的库。

cuDNN 针对深度学习任务进行了大量优化,特别是卷积运算和反向传播的实现,使得训练和推理的速度大大提升。cuDNN 为高层深度学习框架(如 TensorFlow、PyTorch、Caffe 等)提供 API,这些框架通过 cuDNN 来调用 GPU 加速深度学习的操作。

CUDA 不仅用于深度学习,也适用于广泛的并行计算领域,如数值分析、物理仿真、金融建模等;而 cuDNN 主要用于深度学习,特别是 CNN 相关的模型训练和推理加速,但必须依赖 CUDA 才能正常运行。

查看电脑支持的最高CUDA版本

下载的CUDA版本不能超过系统最高的支持版本,可以用命令查看,右上角的CUDA Version就是系统能支持的最高版本(Driver API):

nvidia-smi

下载:CUDA 10.1

首先,提前建好cuda的自定义目录,我拟安装在/mnt/sda的cuda-10.1目录下,故需要创建两个空文件夹:

mkdir /mnt/sda/cuda-10.1
mkdir /mnt/sda/cuda-10.1/mylib

进入下面的网站下载CUDA-10.1(下载update2的原因是:其他版本无法自定义目录):

https://developer.nvidia.com/cuda-10.1-download-archive-update2

选择对应的配置后,格式选"runfile(local)",根据弹出的命令进行安装。一般弹出两条命令,上面的wget命令是下载到本地,第二条sh命令是安装到指定目录,此时可由cd命令进入创建好的cuda-10.1文件夹,在其中安装CUDA。命令如下:

wget https://developer.download.nvidia.com/compute/cuda/10.1/Prod/local_installers/cuda_10.1.243_418.87.00_linux.run

sudo sh cuda_10.1.243_418.87.00_linux.run

降低gcc和g++版本

运行.run命令后,弹出新的对话框,选择"Continue",若弹出了gcc相关的错误,说明gcc版本过高,需要重新安装低版本的gcc。因为Ubuntu20.04自带的gcc版本为9.x,而cuda10.1不支持gcc-9,因此要手动安装gcc-7,命令如下:

sudo apt-get install gcc-7 g++-7

安装完gcc-7,系统中就存在两个版本的gcc,因此要设置默认的gcc,通过update-alternatives设置gcc各版本的优先级:

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 9
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 1

设置默认的g++也是如此:

sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-7 9
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 1

上面是独立配置gcc和g++的版本,优点是可以自由组合各个版本。还可以使用 "--slave" 参数将gcc和g++绑定在一起,使得选择gcc版本的同时,也会自动选择对应的g++版本,gcc和g++的替换会同步进行:

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 9 --slave /usr/bin/g++ g++ /usr/bin/g++-7
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 1 --slave /usr/bin/g++ g++ /usr/bin/g++-9

用下述命令显示所有gcc/g++的优先级,优先级最高的为系统默认版本:

sudo update-alternatives --display gcc
sudo update-alternatives --display g++

在已经安装了多个gcc/g++版本的系统中切换当前版本:

sudo update-alternatives --config gcc

查看当前gcc/g++版本:

gcc --version

安装:CUDA 10.1

(继续)运行.run命令,选择"Continue",此时可以正常进入下一个窗口,填写"accept",回车。

由于系统中已经有了NVIDIA显卡驱动,如果不想安装CUDA 10.1中附带的驱动,就移动上下箭头到Driver选项上,按空格键将该项取消。前面方括号内有"X"则表示选中状态,为空则是取消状态。

只安装CUDA Toolkit即可。一般不搞CUDA编程的话,单跑深度学习够了。

选中"Options",回车进入,更改"Toolkit Options"(/usr这种非用户目录的全都取消选择,因为需要权限,可以将该页面所有选项全取消,之后进入"Change Toolkit Install Path"设置cuda安装到有写入权限的路径(提前建好的),我这里是"/mnt/sda/cuda-10.1/")。

做完"Done",回到Options菜单,更改"Library install path (Blank for system default)"防止写入/var/lib:"/mnt/sda/cuda-10.1/mylib/"(不用加双引号)。

一路"Done",回到最初页面,点击"Install",开始安装。安装好后,会有个Summary的提示。

下一步,修改环境变量:

vim ~/.profile

在末尾添加:

# CUDA
export PATH="/mnt/sda/cuda-10.1/bin:$PATH"
export LD_LIBRARY_PATH="/mnt/sda/cuda-10.1/lib64:/mnt/sda/cuda-10.1/mylib/lib64:$LD_LIBRARY_PATH"

重新加载环境变量使变动生效(用下面两种命令都可以):

source ~/.profile
. ~/.profile

测试CUDA(下面两种都可),显示的是Runtime API:

nvcc -V
nvcc --version

如果想恢复原来的CUDA版本,将配置文件(~/.profile)中加在末尾的那几行命令注释即可。

下载安装:cudnn 7.6.5

然后下载cudnn,选择CUDA10.1对应的版本:v7.6.5(从"Archive of Previous Releases"中进入寻找):

https://developer.nvidia.com/cudnn-downloads

选择"cuDNN Library for Linux"下载,下载下来是一个.tgz的压缩包。

解压,然后进入解压缩的目录:

tar -zxvf ${your PATH of cudnn-xxx.tgz}
cd cuda  # 此处进入cudnn解压的目录

复制文件cudnn.h到CUDA目录的include子目录中:

cp ./include/cudnn.h /mnt/sda/cuda-10.1/include

复制所有文件到CUDA(也即libcudnn开头的文件):

cp ./lib64/libcudnn* /mnt/sda/cuda-10.1/lib64

为上述文件添加读取和执行权限:

sudo chmod 755 /mnt/sda/cuda-10.1/include/cudnn.h /mnt/sda/cuda-10.1/lib64/libcudnn*

Git下载代码

Git是一个软件(需要下载安装),可以帮助你在Github网站进行代码操作。GitHub是一个面向开源及私有软件项目的托管平台,即代码仓库。

在Github上找到了你所需要的项目,只需要在“code”按钮上点击,进入Clone界面,复制代码地址,然后在本地打开Git Bash命令行窗口,键入代码:

git clone copied_address

代码就会从网站下载到你的电脑里。

在使用git clone时,若是遇到了类似fatal: unable to access 'https://github.com/...': Failed to connect to 127.0.0.1 port 7890 after 2021 ms: Couldn't connect to server的报错,一般是配置出了问题。因为git在拉取(clone)或者提交项目时,中间会有git的HTTP和HTTPS代理,但是我们的本地环境中本来就有SSL协议,所以要么重新设置git代理,要么取消git的https代理(不行再把http代理也取消了)。此外,网络原因可能也会导致连接失败,合理科学上网(VPN)可以提高服务器的连接速度,从而解决“time out 443”问题。

# 检查当前全局的http/https代理
git config --global http.proxy
git config --global https.proxy

# 取消git代理
git config --global --unset http.proxy
git config --global --unset https.proxy

# 设置git代理
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy https://127.0.0.1:7890

# Git的默认编辑器会打开 ~/.gitconfig 文件
# 该文件中包括了代理的配置,可以进行编辑
git config --global --edit

# 若使用以上方法后仍然报错,可尝试关闭SSL证书验证
git config --global http.sslVerify false

监控GPU使用情况

工具1:nvidia-smi

直接在终端输入以下代码即可查看显卡的基本情况:

nvidia-smi

每过1s自动刷新:

watch -n 1 nvidia-smi

系统指令,无需安装。

工具2:gpustat

高亮显示GPU的信息:

gpustat

每秒刷新:

gpustat -i

需要安装。安装方式:

sudo apt install gpustat  作为系统包进行安装
pip install gpustat  作为Python库进行安装

工具3:nvitop

高亮展示完整详细的GPU信息:

nvitop  (default)
nvitop -m full

指定GPU运行

命令行模式指定显卡:

CUDA_VISIBLE_DEVICES=gpu_id python xxx.py

源代码模式指定显卡,通过配置环境变量的形式使PyTorch只能看到指定编号的GPU:

import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'  # gpu_id: 0
os.environ['CUDA_VISIBLE_DEVICES'] = '1'  # gpu_id: 1
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'  # gpu_id: 0 and 1

用 .to(device) 移动到显卡上计算:

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')  # 推荐
device = 'cuda' if torch.cuda.is_available() else 'cpu'  # 不推荐
device = 'cuda:0' if torch.cuda.is_available() else 'cpu'  # 指定GPU

关于 .cuda() 的几点注意:

“.cuda()”默认将数据或模型移动到编号为0的GPU上,“.cuda(1)”是移动到编号为1的GPU上。

建议使用 .to(device) 的方式,与环境变量指定的GPU和device变量结合使用,逻辑清晰易懂。

设置环境变量,使用第一块GPU:export CUDA_VISIBLE_DEVICES=0

export命令的主要作用是将变量导出到当前shell的环境中,使其在当前shell及其子进程中都可用。

输出可用的GPU个数:python -c "import torch;print(torch.cuda.device_count())"

显存和BatchSize的关系

显存用于存储图形数据和计算中间结果,深度学习中的“batch_size”指的是每次训练时输入到模型中的样本数(批次)。显存在深度学习训练过程中主要用于存储这些数据:输入数据(例如图像)、中间计算结果(例如激活值)、模型参数和梯度信息、优化过程中额外需要的内存(如动量或自适应学习率的缓存)。

batch_size 越大,显存的占用也越多,因为需要同时存储更多的数据和中间结果。因此,显存的大小直接限制了 batch_size 的取值。如果显存不足,过大的 batch_size 会导致显存溢出,从而导致程序崩溃或性能下降。

如果显存足够大,batch_size 取值也可以很大,但这并不意味着训练速度更快。训练速度受多种因素影响,包括显存大小、计算资源、模型架构以及数据集特性等。显存越大,确实可以允许更大的 batch_size,因为更大的 batch_size 需要同时处理更多的数据,占用更多的显存空间。更大的 batch_size 可以减少梯度更新的次数(每个 epoch 的迭代次数减少),GPU 可以更好地利用其并行计算能力,通常会提高单次迭代的速度,从而提高每个 epoch 的计算效率。同时,大 batch_size 会产生更平滑的梯度估计,也可能要调整学习率。

即使显存足够大,batch_size 增大也可能受到内存带宽、数据传输速率或其他硬件瓶颈的限制,这些因素可能会减慢训练速度。当 batch_size 增加到一定程度后,训练速度的提升可能会减弱,甚至由于硬件瓶颈反而变慢。此外,过大的 batch_size 可能导致模型难以从噪声中学习,从而降低模型的泛化能力。反之,如果 batch_size 太小,虽然显存占用较少,但每个 batch 的梯度估计噪声较大,且需要更多的迭代次数才能收敛,从而增加总的训练时间。

在实际应用中,需要在 batch_size 和训练速度之间找到一个平衡点。一般来说,找到一个能够充分利用显存而不过度增加计算负担的 batch_size 是最佳选择。故为了充分利用显存资源,同时确保训练稳定性,在 batch_size 和显存使用之间进行权衡是必不可少的。

RuntimeError: CUDA out of memory

这个Bug的出现往往是直接采用了作者源代码中的指令,因为自己电脑/服务器的算力不一定能满足其所需。有以下解决方法:

调小batch_size(首选)

一般把batch_size设置为4基本上能解决问题,再不然设置成2呢(不建议这么做,因为太慢了)。

设置参数梯度(推荐)

在测试阶段和验证阶段前插入代码 with torch.no_grad() ,目的是该段程序不计算参数梯度。不过一般在测试阶段都会这么做吧~

关闭锁页内存

查看代码中是否存在以下代码(通常出现在main.py或者数据加载的py文件中):

kwargs = {'num_workers': 6, 'pin_memory': True} if torch.cuda.is_available() else {}

将"pin_memory": True改为:False,具体原因如下:

pin_memory就是锁页内存,创建DataLoader时,设置pin_memory=True,则意味着生成的Tensor数据最开始是属于内存中的锁页内存,这样将内存的Tensor转义到GPU的显存就会更快一些。
主机中的内存,有两种存在方式,一是锁页,二是不锁页。锁页内存存放的内容在任何情况下都不会与主机的虚拟内存进行交换(注:虚拟内存就是硬盘),而不锁页内存在主机内存不足时,数据会存放在虚拟内存中。显卡中的显存全部是锁页内存,当计算机内存充足的时候,可以设置pin_memory=True。当系统卡住,或者交换内存使用过多的时候,设置pin_memory=False。因为pin_memory与电脑硬件性能有关,pytorch开发者不能确保每一个炼丹玩家都有高端设备,因此pin_memory默认设置为False。

降低精度

如果你用的是Pytorch-Lightning,你也可以尝试将精度更改为“float16”。这可能会带来诸如预期的Double和Float张量之间的不匹配等问题,但它可以节省很多内存,并且在性能上有一个非常轻微的权衡,使其成为一个可行的选择。

选择空闲显卡

在命令行执行指令前加上一行选择GPU的代码,选择空闲状态的GPU运行程序:

CUDA_VISIBLE_DEVICES = gpu_id[, gpu_id2]

或者,在python源代码中设置:

import os
os.environ['CUDA_VISIBLE_DEVICES'] = 'gpu_id[, gpu_id2]'

清理内存

要弄清楚你的模型在cuda上占用了多少内存,你可以尝试:

import torch
def report_gpu():
print(torch.cuda.list_gpu_processes())

在关键的代码节点或报错处(一个epoch跑完)插入以下代码定时清理内存:

import gc
gc.collect()
torch.cuda.empty_cache()

Coding Corner
https://afly36-swordsman.github.io/2024/03/16/Coding/
Author
Zenitsu
Posted on
March 16, 2024
Updated on
November 21, 2024