import os
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import torch.optim as optimizer
有以下几种方式:
# 方案一:使用os.environ,这种情况如果使用GPU不需要设置
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
# 方案二:使用“device”,后续对要使用GPU的变量用.to(device)即可
device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")
#方案三:对于想用gpu的macOS用户,需要搭配预览版的pytorch(Nightly)
device = torch.device("mps")
Dataset + Dataloader
常见有两种方式。
import torch
from torchvision import datasets
train_data = datasets.ImageFolder(train_path, transform=data_transform)
val_data = datasets.ImageFolder(val_path, transform=data_transform)
我们可以定义自己的Dataset类来实现灵活的数据读取,定义的类需要继承PyTorch自身的Dataset类。主要包含三个函数:
from torch.utils.data import DataLoader
它的参数:
Module 类是 nn 模块里提供的一个模型构造类,是所有神经⽹网络模块的基类,我们可以继承它来定义我们想要的模型。下面继承 Module 类构造多层感知机。这里定义的 MLP 类重载了 Module 类的 init 函数和 forward 函数。它们分别用于创建模型参数和定义前向计算。前向计算也即正向传播。
import torch
from torch import nn
class MLP(nn.Module):
# 声明带有模型参数的层,这里声明了两个全连接层
def __init__(self, **kwargs):
# 调用MLP父类Block的构造函数来进行必要的初始化。这样在构造实例时还可以指定其他函数
super(MLP, self).__init__(**kwargs)
self.hidden = nn.Linear(784, 256)
self.act = nn.ReLU()
self.output = nn.Linear(256,10)
# 定义模型的前向计算,即如何根据输入x计算返回所需要的模型输出
def forward(self, x):
o = self.act(self.hidden(x))
return self.output(o)
torch.nn.init提供了常用的初始化方法,如:
通常使用isinstance()来进行判断模块属于什么类型,对于不同的类型层,我们就可以设置不同的权值初始化的方法。
常常将各种初始化方法定义为一个initialize_weights()的函数并在模型初始后进行使用。
首先设置模型的状态:
在PyTorch中,模型的状态设置非常简便,如下的两个操作二选一即可:
model.train() # 训练状态
model.eval() # 验证/测试状态
训练过程:
用for循环读取DataLoader中的全部数据:
for data, label in train_loader:
之后将数据放到GPU上用于后续计算,此处以.cuda()为例
data, label = data.cuda(), label.cuda()
开始用当前批次数据做训练时,应当先将优化器的梯度置零:
optimizer.zero_grad()
之后将data送入模型中训练:
output = model(data)
根据预先定义的criterion计算损失函数:
loss = criterion(output, label)
将loss反向传播回网络:
loss.backward()
使用优化器更新模型参数:
optimizer.step()
验证/测试的流程基本与训练过程一致,不同点在于:
优化器是根据网络反向传播的梯度信息来更新网络的参数,以起到降低loss函数计算值,使得模型输出更加接近真实标签。
Pytorch优化器库torch.optim
,提供了十种优化器:
Optimizer
通过FashionMNIST时装分类来学习PyTorch进行深度学习的流程。FashionMNIST数据集中包含已经预先划分好的训练集和测试集,其中训练集共60,000张图像,测试集共10,000张图像。每张图像均为单通道黑白图像,大小为32*32pixel,分属10个类别。
import os
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
# 配置GPU,这里有两种方式
## 方案一:使用os.environ
#os.environ['CUDA_VISIBLE_DEVICES'] = '0'
# 方案二:使用“device”,后续对要使用GPU的变量用.to(device)即可
#device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")
device = torch.device('mps') #这里使用的是M1 Pro的GPU
## 配置其他超参数,如batch_size, num_workers, learning rate, 以及总的epochs
batch_size = 256
num_workers = 4 # 对于Windows用户,这里应设置为0,否则会出现多线程错误
lr = 1e-4
epochs = 20
数据读入和加载,这里同时展示两种方式:
这些变换可以很方便地借助torchvision
包来完成,这是PyTorch官方用于图像处理的工具库,上面提到的使用内置数据集的方式也要用到。PyTorch的一大方便之处就在于它是一整套“生态”,有着官方和第三方各个领域的支持。这些内容我们会在后续课程中详细介绍。
# 首先设置数据变换
from torchvision import transforms
image_size = 28 #可以随便设置,但是要与后面的模型一致
data_transform = transforms.Compose([
transforms.ToPILImage(), # 这一步取决于后续的数据读取方式,如果使用内置数据集则不需要
transforms.Resize(image_size), #改变大小
transforms.ToTensor() #转成tensor
])
## 读取方式一:使用torchvision自带数据集,下载可能需要一段时间
from torchvision import datasets
train_data = datasets.FashionMNIST(root='./', train=True, download=True, transform=data_transform)
test_data = datasets.FashionMNIST(root='./', train=False, download=True, transform=data_transform)
## 读取方式二:读入csv格式的数据,自行构建Dataset类
# csv数据下载链接:https://www.kaggle.com/zalando-research/fashionmnist
class FMDataset(Dataset):
def __init__(self, df, transform=None):
self.df = df
self.transform = transform
self.images = df.iloc[:,1:].values.astype(np.uint8) #提取图像
self.labels = df.iloc[:, 0].values #提取标签
def __len__(self):
return len(self.images) #图片数量
def __getitem__(self, idx): #决定dataset是如何构建的,通过idx索引图片
image = self.images[idx].reshape(28,28,1) #变成28*28的图像,1用于加一个通道
label = int(self.labels[idx])
if self.transform is not None:
image = self.transform(image)
else:
image = torch.tensor(image/255., dtype=torch.float)#变成0-1的tensor
label = torch.tensor(label, dtype=torch.long)
return image, label
train_df = pd.read_csv("./fashion-mnist_train.csv")
test_df = pd.read_csv("./fashion-mnist_test.csv")
train_data = FMDataset(train_df, data_transform)
test_data = FMDataset(test_df, data_transform)
train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True, num_workers=num_workers, drop_last=True)
test_loader = DataLoader(test_data, batch_size=batch_size, shuffle=False, num_workers=num_workers)
读入后,我们可以做一些数据可视化操作,主要是验证我们读入的数据是否正确:
import matplotlib.pyplot as plt
image, label = next(iter(train_loader))
print(image.shape, label.shape)
plt.imshow(image[0][0], cmap="gray")
运行到这里,我的jupyter notebook就开始内核崩溃挂掉,上网查了很多资料,其实主要还是依赖的冲突问题,解决方案查看这里。
next(iterable[, default])
- 函数要和生成迭代器的 iter() 函数一起使用。
- iterable – 可迭代对象
- default – 可选,用于设置在没有下一个元素时返回该默认值,如果不设置,又没有下一个元素则会触发 StopIteration 异常。
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__() # 自己继承
self.conv = nn.Sequential( # 序贯模型
nn.Conv2d(1, 32, 5),
nn.ReLU(),
nn.MaxPool2d(2, stride=2),
nn.Dropout(0.3),
nn.Conv2d(32, 64, 5),
nn.ReLU(),
nn.MaxPool2d(2, stride=2),
nn.Dropout(0.3)
)
self.fc = nn.Sequential( # 全连接
nn.Linear(64*4*4, 512),
nn.ReLU(),
nn.Linear(512, 10) # 10的设定比较关键,10类
)
def forward(self, x): # 前馈
x = self.conv(x)
x = x.view(-1, 64*4*4) # 改变维度,便于全连接层操作
x = self.fc(x)
# x = nn.functional.normalize(x)
return x
model = Net() # 实例化
model = model.to(device)
# model = nn.DataParallel(model).cuda() # 多卡训练时的写法,之后的课程中会进一步讲解
cirterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(),lr=0.001)
def train(epoch):
model.train()
train_loss = 0
for data, label in train_loader:
data, label = data.to(device), label.to(device) # 把data和label放在gpu上
optimizer.zero_grad() # 避免梯度累加
output = model(data)
loss = criterion(output, label)
loss.backward() # 前向传播
optimizer.step() #
train_loss += loss.item()*data.size(0)
train_loss = train_loss/len(train_loader.dataset)
print('Epoch: {} \tTraining Loss: {:.6f}'.format(epoch, train_loss))
如果需要对每一个batch进行一些操作,可以使用 for i,(data,label) in enumerate(train_loader)
def val(epoch):
model.eval()
val_loss = 0
gt_labels = []
pred_labels = []
with torch.no_grad(): #不做梯度计算
for data, label in test_loader:
data, label = data.to(device), label.to(device)
output = model(data)
preds = torch.argmax(output, 1)
gt_labels.append(label.cpu().data.numpy())
pred_labels.append(preds.cpu().data.numpy())
loss = criterion(output, label)
val_loss += loss.item()*data.size(0)
val_loss = val_loss/len(test_loader.dataset)
gt_labels, pred_labels = np.concatenate(gt_labels), np.concatenate(pred_labels)
acc = np.sum(gt_labels==pred_labels)/len(pred_labels) #准确率
print('Epoch: {} \tValidation Loss: {:.6f}, Accuracy: {:6f}'.format(epoch, val_loss, acc))
模型至此搭建好,接下来进行训练!
for epoch in range(1, epochs+1):
train(epoch)
val(epoch)