目录
1. GPU设置
(1)默认用全部GPU并且内存全部占满(why GPU或者内存会全部占满?)
(2)如何不浪费内存和计算资源?
内存自增长:即弹性内存机制,内存自增长。需要多少内存,就用多少内存。
虚拟设备机制:类似于windows中的磁盘(系统中一般只有一个磁盘,单windows会将其分成好几个盘,比如C盘,D盘,E盘,每个盘放不同的东西)虽然只有一个GPU,但可以将其切分成多个逻辑上的GPU
(3)多GPU使用
虚拟GPU & 实际GPU
手工设置 & 分布式机制
手工设备,指定某一层,在哪个GPU上去计算,或者使用tensorflow框架所支持的分布式机制。
2. GPU设置的API列表
(1)tf.debugging.set_log_device_placement
用来打印一些信息:某个变量,分配在哪个设备上
(2)tf.config.experimental.set_visible_devices
用于设置对于本进程可见的设备。比如一个机器上有4个GPU,只设置1个对本进程可见,故本进程就不能使用其他设备。
(3)tf.config.experimental.list_logical_devices
获取所有的逻辑设备。物理设备即一整块儿物理磁盘,逻辑设备即物理磁盘上的分区。
(4)tf.config.experimental.list_physical_devices
获取所有物理设备的列表。
(5)tf.config.experimental.set_memory_devices
设置内存自增长,内存用多少就占多少,而不是一下子把内存都占满。
(6)tf.config.experimental.VirtualDeviceConfiguration
用来在物理设备上建立逻辑分区
(7)tf.config.set_soft_device_placement
手动指定某一个计算在哪一个设备上执行,这个设备可以是CPU,GPU或者是其他的设备,但是手动指定的方式很容易出错。故可用Set_soft_device_placement自动地将某个计算分配到某个设备上去,这种分配是可以保证正确性的。某个设备支持某个计算,才会将某个计算分配到某个设备上。
3.GPU设置实战
以cnn对fashion_minist数据集分类为例
载入要使用的模块
import os
import sys
import time
import pandas as pd
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow import keras
加载数据
fashion_mnist = keras.datasets.fashion_mnist
(x_train_all, y_train_all), (x_test, y_test) = fashion_mnist.load_data()
x_valid, x_train = x_train_all[:5000], x_train_all[5000:]
y_valid, y_train = y_train_all[:5000], y_train_all[5000:]
对数据进行归一化处理
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
# x_train [None, 28, 28] --> [None, 784]
x_train_scaler = scaler.fit_transform(x_train.reshape(-1, 784)).reshape(-1, 28, 28, 1) # 最后一维 1,表示1个通道
x_valid_scaler = scaler.transform(x_valid.reshape(-1, 784)).reshape(-1, 28, 28, 1)
x_test_scaler = scaler.transform(x_test.reshape(-1, 784)).reshape(-1, 28, 28, 1)
构造dataset
def make_dataset(images, labels, epochs, batch_size, shuffle=True):
dataset = tf.data.Dataset.from_tensor_slices((images, labels))
if shuffle:
dataset = dataset.shuffle(10000)
# prefetch:表示从数据中预先取出来多少个,来给生成数据作准备。为什么说是用来加速的一个函数?
dataset = dataset.repeat(epochs).batch(batch_size).prefetch(50)
return dataset
batch_size = 128
epochs = 100
train_dataset = make_dataset(x_train_scaler, y_train, epochs, batch_size)
构造一个20层的模型
model = keras.models.Sequential()
model.add(keras.layers.Conv2D(filters=32, kernel_size=3,
padding='same',
activation='selu',
input_shape=(28, 28, 1)))
model.add(keras.layers.SeparableConv2D(filters=32, kernel_size=3,
padding='same',
activation='selu'))
model.add(keras.layers.MaxPool2D(pool_size=2))
# 一般每进行一次pooling层,图像的大小就会缩小,中间的数据就会大大减少,为减少这种信息的损失,故将filters翻倍。
model.add(keras.layers.SeparableConv2D(filters=64, kernel_size=3,
padding='same',
activation='selu'))
model.add(keras.layers.SeparableConv2D(filters=64, kernel_size=3,
padding='same',
activation='selu'))
model.add(keras.layers.MaxPool2D(pool_size=2))
model.add(keras.layers.SeparableConv2D(filters=128, kernel_size=3,
padding='same',
activation='selu'))
model.add(keras.layers.SeparableConv2D(filters=128, kernel_size=3,
padding='same',
activation='selu'))
model.add(keras.layers.MaxPool2D(pool_size=2))
# 展平
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(128, activation='selu')) # 全链接层
model.add(keras.layers.Dense(10, activation="softmax")) # 全链接层
model.compile(loss=keras.losses.SparseCategoricalCrossentropy(),
optimizer=keras.optimizers.SGD(),
metrics=["accuracy"])
model.summary()
训练
history = model.fit(train_dataset,
steps_per_epoch = x_train_scaler.shape[0] // batch_size,
epochs=10)
训练过程中查看内存及gpu使用情况
命令行输入:nvidia-smi
可以看到,不做任何处理,基本将第一个gpu的内存全部占满
命令行:watch -n 0.1 -x nvidia-smi (-n 表示刷新的时间间隔,-x表示监控的是哪条命令)
3.1 对每个GPU设置内存自增长
tf.debugging.set_log_device_placement(True)
gpus = tf.config.experimental.list_physical_devices('GPU')
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu, True)
logical_gpus = tf.config.experimental.list_logical_devices('GPU')
print("the num of gpu", len(gpus))
print("the num of logical gpu", len(logical_gpus))
note:memory growth must be set at program startup
再训练模型
可以看到,设置内存自增长之后,占用的内存明显的变少了呢
3.2 设置可见GPU
设置第4个gpu可见,即可使用第4个gpu
tf.debugging.set_log_device_placement(True)
gpus = tf.config.experimental.list_physical_devices('GPU')
# 设置GPU可见
tf.config.experimental.set_visible_devices(gpus[3], 'GPU')
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu, True)
logical_gpus = tf.config.experimental.list_logical_devices('GPU')
print("the num of gpu", len(gpus))
print("the num of logical gpu", len(logical_gpus))
训练模型
发现是在使用第4个gpu哦
3.3给GPU做逻辑切分
在第2个gpu上,划分两个逻辑磁盘,每个磁盘的内存上限为3G
tf.debugging.set_log_device_placement(True)
gpus = tf.config.experimental.list_physical_devices('GPU')
# 设置GPU可见
tf.config.experimental.set_visible_devices(gpus[0], 'GPU')
# 对GPU进行切分
tf.config.experimental.set_virtual_device_configuration(
gpus[0],
[tf.config.LogicalDeviceConfiguration(memory_limit=3072),
tf.config.LogicalDeviceConfiguration(memory_limit=3072)]
)
logical_gpus = tf.config.experimental.list_logical_devices('GPU')
print("the num of gpu", len(gpus))
print("the num of logical gpu", len(logical_gpus))
训练模型
可以看到有两个1号gpu
3.4 手动指定的方式使用多GPU环境
tf.debugging.set_log_device_placement(True)
gpus = tf.config.experimental.list_physical_devices('GPU')
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu, True)
logical_gpus = tf.config.experimental.list_logical_devices('GPU')
print("the num of gpu", len(gpus))
print("the num of logical gpu", len(logical_gpus))
测试代码
c = []
for gpu in logical_gpus:
print(gpu.name)
with tf.device(gpu.name):
a = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
b = tf.constant([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
c.append(tf.matmul(a, b))
with tf.device('/cpu:0'):
matmul_sum = tf.add_n(c)
print(matmul_sum)
将tf.device() 用在训练模型上,不同的层在不同的gpu上运行
model = keras.models.Sequential()
with tf.device(logical_gpus[0].name):
model.add(keras.layers.Conv2D(filters=32, kernel_size=3,
padding='same',
activation='selu',
input_shape=(28, 28, 1)))
model.add(keras.layers.SeparableConv2D(filters=32, kernel_size=3,
padding='same',
activation='selu'))
model.add(keras.layers.MaxPool2D(pool_size=2))
# 一般每进行一次pooling层,图像的大小就会缩小,中间的数据就会大大减少,为减少这种信息的损失,故将filters翻倍。
model.add(keras.layers.SeparableConv2D(filters=64, kernel_size=3,
padding='same',
activation='selu'))
model.add(keras.layers.SeparableConv2D(filters=64, kernel_size=3,
padding='same',
activation='selu'))
model.add(keras.layers.MaxPool2D(pool_size=2))
with tf.device(logical_gpus[1].name):
model.add(keras.layers.SeparableConv2D(filters=128, kernel_size=3,
padding='same',
activation='selu'))
model.add(keras.layers.SeparableConv2D(filters=128, kernel_size=3,
padding='same',
activation='selu'))
model.add(keras.layers.MaxPool2D(pool_size=2))
# 展平
model.add(keras.layers.Flatten())
with tf.device(logical_gpus[2].name):
model.add(keras.layers.Dense(128, activation='selu')) # 全链接层
model.add(keras.layers.Dense(10, activation="softmax")) # 全链接层
model.compile(loss=keras.losses.SparseCategoricalCrossentropy(),
optimizer=keras.optimizers.SGD(),
metrics=["accuracy"])
model.summary()
把不同的测层次放在不同的GPU上:这样做的好处——当模型比较大时,1个GPU放不下的时候,可通过此种方式让模型能够被放下,能够被训练。但其并没有达到并行化的效果哦,仍然是在串行。