Pytorch模型中的GPU运算详解与实践

前言

什么是GPU
GPU(Graphic Process Units,图形处理器)。是一种单芯片处理器,主要用于管理和提高视频和图形的性能。GPU 加速计算是指同时利用图形处理器 (GPU) 和 CPU,加快应用程序的运行速度。
为什么要用GPU
深度学习涉及很多向量或多矩阵运算,如矩阵相乘、矩阵相加、矩阵-向量乘法等。深层模型的算法,如BP,Auto-Encoder,CNN等,都可以写成矩阵运算的形式,无须写成循环运算。然而,在单核CPU上执行时,矩阵运算会被展开成循环的形式,本质上还是串行执行。GPU的众核体系结构包含几千个流处理器,可将矩阵运算并行化执行,大幅缩短计算时间
如何使用GPU
现在很多深度学习工具都支持GPU运算,使用时只要简单配置即可。Pytorch支持GPU,可以通过to(device)函数来将数据从内存中转移到GPU显存,如果有多个GPU还可以定位到哪个或哪些GPU。Pytorch一般把GPU作用于张量(Tensor)或模型(包括torch.nn下面的一些网络模型以及自己创建的模型)等数据结构上。

一、关于CUDA的函数接口

1.1 torch.cuda

如何查看平台GPU的配置信息?在cmd命令行输入命令nvidia-smi即可 (适合于Linux或Windows环境)。

第一次使用这个命令可能会显示
‘nvidia-smi’ 不是内部或外部命令,也不是可运行的程序或批处理文件。
我们只需要配置一下环境变量即可,配置环境的路径在下面,

C:\Program Files\NVIDIA Corporation\NVSMI

然后添加环境变量后,即可显示本机的GPU配置信息。示例如下:
Pytorch模型中的GPU运算详解与实践

import torch

print(torch.cuda.is_available()) # 查看系统GPU是否可以使用,经常用来判断是否装好gpu版的pytorch
print(torch.cuda.current_device())# 返回当前设备序号
print(torch.cuda.get_device_name(0))# 返回第0号设备的name
print(torch.cuda.device_count())# 返回可使用的gpu的数量
print(torch.cuda.memory_allocated(device="cuda:0"))#返回0号设备的当前GPU显存使用量(以字节为单位)

1.2 torch.device

作为Tensor 的一种属性,其包含了两种设备类型,cpu和gpu,通常通过两种方式来进行创建:

#1.通过字符串创建
eg: 
torch.device('cuda:0')
torch.device('cpu')
torch.device('cuda')  # 当前cuda设备

#2.通过字符串加设备编号创建
eg:
torch.device('cuda', 0)
torch.device('cpu', 0)


#常见的几种创建gpu设备上的Tensor对象:
torch.randn((2,3),device = torch.device('cuda:0'))
torch.randn((2,3),device = 'cuda:0')
torch.randn((2,3),device = 0)  #历史遗留做法,仅支持gpu

# 把数据从内存转移到GPU,一般针对张量(我们需要的数据)和模型。 对张量(类型为FloatTensor或者是LongTensor等),一律直接使用方法.to(device)或.cuda()即可。
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#或device = torch.device("cuda:0")
device1 = torch.device("cuda:1")  
for batch_idx, (img, label) in enumerate(train_loader):
    img=img.to(device)
    label=label.to(device)
# 对于模型来说,也是同样的方式,使用.to(device)或.cuda来将网络放到GPU显存。
#实例化网络
model = Net()
model.to(device)   #使用序号为0的GPU
#或model.to(device1) #使用序号为1的GPU

1.3 .to()

进行设备转化,也是常用的设置gpu的方式。
我个人比较喜欢用的一种方式就是:
device=torch.device(“cuda” if torch.cuda.is_available() else “cpu”)


通常可以这样调用:

to(device=None, dtype=None, non_blocking=False)
#第一个可以设置当前的设备,比如device=torch.device('cuda:0')
#或者device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
#第二个就是数据类型了,如torch.float,torch.int,torch.double
#第三个参数,若设置为True, 并且此对象的资源存储在固定内存上(pinned memory),那么此cuda()函数产生的复制将与host端的原storage对象保持同步。否则此参数不起作用

1.4 使用指定的GPU

PyTorch默认使用从0开始的GPU,常有以下两种方式来指定特定的GPU

1.CUDA_VISIBLE_DEVICES
终端设置:  CUDA_VISIBLE_DEVICES=1,2  python train.py    (举个例子)
代码里设置:
			import os
			os.environ["CUDA_VISIBLE_DEVICES"] = '1,2'
2.torch.cuda.set_device()
代码里设置:
			import torch
			torch.cuda.set_device(id)

不过官方建议使用CUDA_VISIBLE_DEVICES,不建议使用 set_device 函数。

1.5 多GPU训练

为了提高训练速度,常常一个机器有多张gpu,这时候便可以进行并行训练,提高效率。而并行训练又可分为数据并行处理模型并行处理
数据并行处理指的是使用同一个模型,将数据均匀分到多种gpu上,进行训练;
模型并行处理指的是多张gpu训练模型的不同部分,使用同一批数据。

二、训练实例代码展示

2.1 数据并行处理

本代码以波士顿房价数据为例,共506个样本,13个特征。数据划分成训练集和测试集,然后用data.DataLoader转换为可批加载的方式。采用nn.DataParallel并发机制,环境有2个GPU。当然,数据量很小,按理不宜用nn.DataParallel,这里只是为了说明使用方法。

from sklearn import datasets
from sklearn.model_selection import train_test_split
import torch
import torch.nn as nn
import torch.nn.functional as F

# 加载数据
boston = datasets.load_boston()
X, y = (boston.data, boston.target)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
# 组合训练数据及标签
myset = list(zip(X_train, y_train))

# 把数据转换为批处理加载方式批次大小为128,打乱数据
from torch.utils import data
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
dtype = torch.FloatTensor
train_loader = data.DataLoader(myset,batch_size=128,shuffle=True)

# 定义网络
class Net1(nn.Module):
    """
    使用sequential构建网络,Sequential()函数的功能是将网络的层组合到一起
    """

    def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
        super(Net1, self).__init__()
        self.layer1 = torch.nn.Sequential(nn.Linear(in_dim, n_hidden_1))
        self.layer2 = torch.nn.Sequential(nn.Linear(n_hidden_1, n_hidden_2))
        self.layer3 = torch.nn.Sequential(nn.Linear(n_hidden_2, out_dim))

    def forward(self, x):
        x1 = F.relu(self.layer1(x))
        x1 = F.relu(self.layer2(x1))
        x2 = self.layer3(x1)
        # 显示每个GPU分配的数据大小
        print("\tIn Model: input size", x.size(), "output size", x2.size())
        return x2

if __name__ == '__main__':
	# 把模型转换为多GPU并发处理格式
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    # 实例化网络
    model = Net1(13, 16, 32, 1)
    if torch.cuda.device_count() > 1:
        print("Let's use", torch.cuda.device_count(), "GPUs")
        # dim = 0 [64, xxx] -> [32, ...], [32, ...] on 2GPUs
        model = nn.DataParallel(model)
    model.to(device)
    # 选择优化器及损失函数
    optimizer_orig = torch.optim.Adam(model.parameters(), lr=0.01)
    loss_func = torch.nn.MSELoss()
    # 模型训练,并可视化损失值
    # from torch.utils.tensorboard import SummaryWriter
    # writer = SummaryWriter(log_dir='logs')
    for epoch in range(100):
        model.train()
        for data, label in train_loader:
            input = data.type(dtype).to(device)
            label = label.type(dtype).to(device)
            output = model(input)
            loss = loss_func(output, label)
            # 反向传播
            optimizer_orig.zero_grad()
            loss.backward()
            optimizer_orig.step()
            print("Outside: input size", input.size(), "output_size", output.size())
        # writer.add_scalar('train_loss_paral', loss, epoch)

运行结果

Let's use 2 GPUs
DataParallel(
(module): Net1(
(layer1): Sequential(
(0): Linear(in_features=13, out_features=16, bias=True)
)
(layer2): Sequential(
(0): Linear(in_features=16, out_features=32, bias=True)
)
(layer3): Sequential(
(0): Linear(in_features=32, out_features=1, bias=True)
)
)
)

从运行结果可以看出,一个批次数据(batch-size=128)拆分成两份,每份大小为64,分别放在不同的GPU上。

In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
Outside: input size torch.Size([128, 13]) output_size torch.Size([128, 1])
In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
Outside: input size torch.Size([128, 13]) output_size torch.Size([128, 1])

损失情况:
Pytorch模型中的GPU运算详解与实践
图形中出现较大振幅,是由于采用批次处理,而且数据没有做任何预处理,对数据进行规范化应该更平滑一些,大家可以尝试一下。
单机多GPU也可使用DistributedParallel,它多用于分布式训练,但也可以用在单机多GPU的训练,配置比使用nn.DataParallel稍微麻烦一点,但是训练速度和效果更好一点。具体配置为:

#初始化使用nccl后端
torch.distributed.init_process_group(backend="nccl")
#模型并行化
model=torch.nn.parallel.DistributedDataParallel(model)

单机运行时使用下面方法启动

蒼狼性腎炎 - 醫生在線交流

現已被診斷為患有蒼狼性腎炎,通過治療可以把病情得到控制嗎?一般患者的生命可以維持多久?目前一般情況:會掉頭發,天氣變冷時手指會發紫,有時會感覺身體無力。病史:無以往的診斷和治療經過及效果:現在才被診斷換病開始接受治療

不吃早餐會得什麼病嗎? - 醫生在線交流

不吃早餐會得什麼病嗎?<BR

稍一動就出汗是啥緣故 - 醫生在線交流

患者年齡:18孩子稍微一運動就渾身是汗,從學校到傢也就是十分鐘的路程,(騎自行車)回來後衣服都能濕透瞭.(內穿秋衣)本次發病及持續的時間:今年夏天至今(夏天以為天熱出汗)

我現在懷孕40幾天瞭,10天前我眼睛有點癢 - 醫生在線交流

我現在懷孕40幾天瞭,10天前我眼睛 有點癢,就自己用瞭點眼藥水,還擦瞭眼膏,那個時候還不知道懷孕,現在懷瞭的關稅嗎?

未婚,宮頸糜爛三度,如何治療? - 醫生在線交流

您好! 我今年26,未婚,曾有過性生活,但很少。去年在做人流的時候,檢查有宮頸糜爛三度。一直用藥物治療,可是今年檢查,還是三度。而且有雜菌感染。做活檢和陰道鏡檢查,有增生。醫生建議我用電熨的方法治療。但是有人說如果達不到深度的話不可能一次性治愈還可能影響到以後生育。我曾咨詢過別的醫生,她建議我用微波或波姆。我主要想問一下:哪種治療方法對宮頸傷害比較小,能一次性治愈的,不影響到以後生育,期待你的幫助,謝謝第一次問題補充:先謝謝你們的回答,真的好煩,今天又跑瞭幾傢醫院,說法不一,好困擾啊,我到底該怎麼辦?今天去的醫院有利普刀,醫生說能一次性治愈,手術費600元。我聽著也聽好的,可是有的醫生說那中方法就是進行錐形切除,對於我傷害太大。還有的說用聚焦超聲的,波姆的,還有一傢說用冷凍的260包治。而且他們都是有名的醫院。有的醫生說無論采用什麼方法治療,都對以後分娩有影響,以後都應該拋腹產。我想問一下是這樣麼?如果不是,那哪中對以後自然生產沒有影響???利普刀是錐形切除術麼??急急急!!!

近半年手腳經常流虛汗腰疼大便難解 - 醫生在線交流

近半年手腳經常流虛汗腰疼大便難解治

請問怎樣對肝臟有好處?例如食療或生活中應該註意什麼 - 醫生在線交流

BR

我愛人今年四月份生的小孩,最近體檢發現甲狀腺有些腫大彩超診斷 - 醫生在線交流

大夫您好,我愛人今年四月份生的小孩,最近體檢發現甲狀腺有些腫大彩超診斷為:甲狀腺內部回聲不均勻,增粗,未見明顯結節。CDFI:甲狀腺腺體內可探豐富血流信號,呈火海征。右甲狀腺上動脈:Vmax: 72.6, RI:0.59左甲狀腺上動脈:Vmax: 71.4cms:0.56超聲提示:甲狀腺彌漫性病變伴血供豐富甲功三項和TG檢查結果為:參考值范圍FT3 1.26 ↓ pmolL 2.2-5.3FT4 7.55 ↓ pmolL 9.1-23.8TSH 100.0↑ uIUmL 0.490-4.670anti-tg 99.3↑ IUmL 0-34anti-tpo 201.7↑ IUmL 0-12請問醫生以上數據反應瞭什麼病癥,謝謝。我愛人28歲,身高158cm體重47公斤,體重一直在這個范圍左右。

我04年診斷為慢性糜爛性胃竇炎經治療05年為慢性淺 - 醫生在線交流

BR

我確定肚裡有蟲子,清風醫師,你有什麼好辦法嗎? - 醫生在線交流

我確定肚裡有蟲子,平時總感覺有東西在肚裡蠕動,吃完藥後,感覺好點,但是一吃飯就又有感覺瞭,有時候感覺肛門有蠕動,但是到醫院檢查,醫生都說不清.謝謝你!

我36歲,放環8年瞭,請問專傢我是否需更換。 - 醫生在線交流

我36歲,放環8年瞭,請問專傢我是否需更換,一般放多長時間為易。

不滿三個月的寶寶便秘有什麼解決方法 - 醫生在線交流

不滿三個月的寶寶便秘有什麼解決方法

快來幫我確診,謝瞭! - 醫生在線交流

來一次,有時要隔三四個月甚至半年,上初中時還多些,而且每次量挺多持續至少一個禮拜,現在尤其是結婚(05臘月)後越少瞭,從05年農歷9月、10月均來過一次(當然量也不少時間也有一星期)後,到結婚直到今年6月底來瞭一次,後來在8月份流產瞭,流產的再前一天晚上我和我老公那啥(因不知道已懷孕)後來去醫院做B超並化驗弱陽性後又做瞭刮宮,一直到現在近3個月瞭還沒來,而且我小腿的汗毛較長且黑粗,身體其他部位均正常,上大學體檢時還有平時身體都正常也不很胖中等身材。請幫幫我,我好困惑!第一次問題補充:就是小腿部位的汗毛始終就較長濃且黑夏天都不好意思不過我父親汗毛也較重和遺傳有關嗎?第二次問題補充:我想問這種情況如何治療:汗毛能不能去掉以及如何調理月經幫我判斷一下這是否屬於多囊卵巢綜合征?我很困惑也很無奈希望您能幫幫我!!!

下午可以做早孕檢查嗎 - 醫生在線交流

下午可以做早孕檢查嗎

我傢孩子四個月.為什麼一吃奶就哭啊.而且還沒吃就的 - 醫生在線交流

我傢孩子四個月.為什麼一吃奶就哭啊.而且還沒吃就的哭一會.哭一會就吃瞭.而且吃不瞭一會.你不給他吃他也沒吃奶的意識

2歲小孩靜滴阿樂欣後的不良反應通常發生在註射後的多 - 醫生在線交流

一個兩歲小孩,沒有做過青黴素皮試,隻做過西力欣的皮試(皮試陰性),那在靜脈註射阿樂欣1克後,不良反應會在註射後多長時間出現???如果4小時內沒有發生不良泛音,是不是可以說不會再發生不良反應瞭?第一次問題補充:三天後所發生的是過敏反應是過敏性皮疹還是過敏性休克?

牙酸痛什麼回事?怎麼治療? - 醫生在線交流

患者年齡:52牙酸痛治療本次發病及持續時間:30多天目前一般情況:酸病史:無以往診斷治療經過及效果:刷冷酸靈牙膏無明顯效果輔助檢查:無其它:無

胃病與貧血 - 醫生在線交流

你好!我由於小時長期挨餓,現在留下瞭胃病,容易犯病。且由於自己是嚴重貧血,身體不是很好營養不良。有病的時候由於胃不好吃藥還容易嘔吐不能下咽。我想問以下象我患有胃病與貧血這種情況在平時的飲食中應該怎麼搭配飲食,該註意什麼,搭配什麼,多補充什麼。還有 由於傢在農村,傢境不好,有什麼偏方之內的可以治療我的胃並與貧血嗎?有什麼對胃病與貧血 好的便宜的藥嗎?

我侄子今年19歲得的水豆,我昨天以下午都跟他在一塊 - 醫生在線交流

上一篇:YOLOX改进之损失函数(下)


下一篇:pytorch 11 mae、mse、BCELoss、BCEWithLogitsLoss和CrossEntropyLoss等loss的使用