pytorch(六)一些训练技巧
1.dataloader训练时的num_worker设置(推荐设置为1)
num_workers = 0 只用主进程main process (训练程序的进程)来加载数据。主进程完成一个batch的前向后向传播,再去disk搬运下一个batch到cpu,然后再转移到GPU。
num_workers = 1 除了主进程main process外,再开一个额外的进程去disk搬运数据到内存,主进程完成一个batch的前向后向传播后,就可以直接从内存get数据而不是从disk。
因此num_workers = 1 (共有2个进程,main process + 1 additional process),效果提升明显。20%加速
2.关于训练数据的归一化(在ET阶段完成,即装入dataloader之前完成。)
原因:神经网络对于输入在0-1之间时拟合效果最好
train_set = torchvision.datasets.FashionMNIST(
root='./data'
,train=True
,download=True
transform = transforms.Compose( #将多个transpose的操作组合成一个使用,这里是转化tensor和标准化。
[transforms.ToTensor(), #必须先将pil.image或ndarray转化成tensor,才能进行下面的归一化操作。
#这里如果传进来的是torch.uint8类型即torch.ByteTensor,则transforms.ToTensor底层会自动给其/255.0归一化
, transforms.Normalize(mean, std)) #这里是标准化输入的是(mean,std)均值和方差,因为是RGB因此mean和std都是(1,3)的vector.Normalized_image = (image - mean)/std
)
输入归一化的方法:
- 方法一:pixel value直接/255 (tarnsforms.Totensor()对torch.uint8类型的自动处理,不能再显示搞一遍,会导致输入及参数很小,无法收敛
batch = batch.float()
batch /= 255.0
- 方法二:通过计算各通道的均值和标准差来进行各通道的归一化
n_channels = batch.shape[1] #通道数
for c in range(n_channels):
mean = torch.mean(batch[:,c]) #只取c通道
std = torch.std(batch[:,c])
batch[:,c] = (batch[:,c]-mean)/std
#结果显示,正规化的数据相同epoch后准率更高,意味着收敛的更快
#但是不总是成立,可能有些数据不归一化更好,需要试验。
params = OrderedDict(
lr = [.01]
, batch_size = [1000]
, num_workers = [1]
, device = ['cuda']
, trainset = ['not_normal', 'normal']
)
m = RunManager()
for run in RunBuilder.get_runs(params):
device = torch.device(run.device)
network = Network().to(device)
loader = DataLoader(
trainsets[run.trainset]
, batch_size=run.batch_size
, num_workers=run.num_workers
)
optimizer = optim.Adam(network.parameters(), lr=run.lr)
m.begin_run(run, network, loader)
for epoch in range(20):
m.begin_epoch()
for batch in loader:
images = batch[0].to(device)
labels = batch[1].to(device)
preds = network(images) # Pass Batch
loss = F.cross_entropy(preds, labels) # Calculate Loss
optimizer.zero_grad() # Zero Gradients
loss.backward() # Calculate Gradients
optimizer.step() # Update Weights
m.track_loss(loss, batch)
m.track_num_correct(preds, labels)
m.end_epoch()
m.end_run()
m.save('results')
3.关于数据的底层存放方式
- tensor的底层是一维的长向量,张量的索引实现是在长向量基于不同轴的步长实现。
- tensor和ndaaray都是连续存储,区别是tensor可直接运行在GPU上。
- 一般的python的序列化的存储容器如list不是连续的内存空间,因此索引等操作效率不高