0.说在前面1.准备工作1.1 transform1.2 ToTensor1.3 Normalize1.4 datasets1.5 DataLoader1.6 GPU与CPU2.Barebones PyTorch2.1 Flatten Function2.2 Two-Layer Network2.3 Three-Layer ConvNet2.4 Initialization2.5 Check Accuracy2.6 Training Loop2.7 Train a Two-Layer Network2.8 Training a ConvNet3.PyTorch Module API3.1 Three-Layer ConvNet3.2 Train a Three-Layer ConvNet4.PyTorch Sequential API4.1 Three-Layer ConvNet5. CIFAR-10 open-ended challenge
本节更新week8作业的PyTorch.ipynb,顺便一起来学习一下PyTorch的一些基本用法!
下面一起来实践吧!
在这一部分,需要注意几个函数,分别如下:
T.Compose
将多个transforms的list组合起来。
transform = T.Compose([
T.ToTensor(),
T.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])
transforms.ToTensor()
将 PIL.Image/numpy.ndarray 数据进转化为torch.FloadTensor,并归一化到[0, 1.0]。
取值范围为[0, 255]的PIL.Image,转换成形状为[C, H, W],取值范围是[0, 1.0]的torch.FloadTensor;
形状为[H, W, C]的numpy.ndarray,转换成形状为[C, H, W],取值范围是[0, 1.0]的torch.FloadTensor。
T.ToTensor()
归一化对神经网络是非常重要的,那么如何归一化到[-1.0,1.0]?
这里就用到了transforms.Normalize()
,channel=(channel-mean)/std。
上述可转化为:
T.Normalize(mean=(0.4914, 0.4822, 0.4465), std=(0.2023, 0.1994, 0.2010))
使用datasets下载数据集。
import torchvision.datasets as dset
cifar10_train = dset.CIFAR10('./cs231n/datasets', train=True, download=True,
transform=transform)
组合数据集和采样器,并在数据集上提供单进程或多进程迭代器。
可以把下面通俗的理解为:使用采样器随机在数据集中采样,并用DataLoader将采样器与数据集进行装载,然后返回的结果是个迭代器,取出数据得通过for循环取出!
from torch.utils.data import DataLoader
from torch.utils.data import sampler
loader_train = DataLoader(cifar10_train, batch_size=64,
sampler=sampler.SubsetRandomSampler(range(NUM_TRAIN)))
我觉得这一段代码写的很棒,是因为简洁明了的将cpu与gpu一同来进行判别,直接确定你最后使用的是gpu还是cpu的设备去运行!
USE_GPU = True
dtype = torch.float32 # we will be using float throughout this tutorial
if USE_GPU and torch.cuda.is_available():
device = torch.device('cuda')
else:
device = torch.device('cpu')
# Constant to control how frequently we print train loss
print_every = 100
print('using device:', device)
这一节来实现PyTorch中的Flatten函数!
这里做的工作在注释中给出了明确的说明,我们来看一下:
The flatten function below first reads in the N, C, H, and W values from a given batch of data, and then returns a "view" of that data. "View" is analogous to numpy's "reshape" method: it reshapes x's dimensions to be N x ??, where ?? is allowed to be anything (in this case, it will be C x H x W, but we don't need to specify that explicitly.
这里有几个地方非常重要,第一:view类似与numpy中reshape方法,主要是将维度变为Nx?? 而两个问号又该是什么值呢,这就引出了第二个关键点:两个问号不需要明确指定。
那么根据前面的两个关键点,就可以明白下面函数所要做的事情了,那就是将(N,C,H,W)维度变为(N,CxHxW)!不需要明确指定就是-1即可完成,而reshape用view函数,那么这样的话,下面函数就so easy了!
这一节来实现两层神经网络!注释中说明,这一节不用写任何代码,但你需要理解!
A fully-connected neural networks; the architecture is:NN is fully connected -> ReLU -> fully connected layer.
全连接网络架构为:NN->ReLU->NN
这里的x.mm解释一下:x是一个pytorch 张量,x.mm使用了pytorch里面的矩阵乘法函数,作用就是实现x与w1的矩阵相乘,是真正的矩阵相乘,而不是对应元素相乘!
这里我们来看注释维度!
注意点:d1 * ... * dM = D
Inputs:
- x: A PyTorch Tensor of shape (N, d1, ..., dM) giving a minibatch of
input data.
- params: A list [w1, w2] of PyTorch Tensors giving weights for the network;
w1 has shape (D, H) and w2 has shape (H, C).
具体实现如下:先转化x维度为(N,D),再通过矩阵乘法(N,D)x(D,H)得到(N,H),此时是x.mm(w1)的结果,也就完成了NN层这一步,接着就是ReLU,那么用F.relu()完成了ReLU层,最后再做一次矩阵乘法,(N,H)x(H,C),得到(N,C),也就是x.mm(w2)的结果,最后返回即可!
import torch.nn.functional as F
x = flatten(x)
w1, w2 = params
x = F.relu(x.mm(w1))
x = x.mm(w2)
后面则是两层神经网络的测试,可以自己走一遍,这里就不阐述了!
为什么前面不写代码呢,就是因为让你县看懂,看懂完了,来实现三层卷积。
卷积架构如下:
A convolutional layer (with bias) with channel_1
filters, each with shape KW1 x KH1
, and zero-padding of two
ReLU nonlinearity
A convolutional layer (with bias) with channel_2
filters, each with shape KW2 x KH2
, and zero-padding of one
ReLU nonlinearity
Fully-connected layer with bias, producing scores for C classes.
接着来看一下给定的要求:
输入:
- x: (N, 3, H, W)
- params: should contain the following:
-conv_w1 : (channel_1, 3, KH1, KW1) 第一层卷积权重
-conv_b1 : (channel_1,) 第一层卷积偏值
-conv_w2 : (channel_2, channel_1, KH2, KW2) 第二层卷积权重
-conv_b2 : (channel_2,) 第二层卷积偏值
- fc_w: 全连接层权重
- fc_b: 全连接层偏值
返回:
- scores: (N,C)
具体解释看注释!
# 完成卷积一层操作(实现通过F.conv2d)
# 填入输入数据,权重,偏值,步长及零填补(看上面卷积架构)
# 后面的按照上面架构来就行了
# # shape=(N,channel_1,H,W)
conv1 = F.conv2d(x, weight=conv_w1, bias=conv_b1,stride=1, padding=2)
# shape=(N,channel_1,H,W)
relu1 = F.relu(conv1)
# shape=(N,channel_2,H,W)
conv2 = F.conv2d(relu1, weight=conv_w2, bias=conv_b2, stride=1,padding=1)
# shape=(N,channel_2,H,W)
relu2 = F.relu(conv2)
# 利用上面的两层神经网络的实现,直接调用即可!
# shape=(N,CxHxW)
relu2_flat = flatten(relu2)
# shape=(N,C)
scores = relu2_flat.mm(fc_w) + fc_b
让我们编写几个实用程序方法来初始化我们模型的权重矩阵。
random_weight(shape)
用Kaiming归一化方法初始化权重张量。
zero_weight(shape)
用全零初始化权重张量。用于实例化偏差参数。
在训练模型时,我们将使用以下函数来检查我们的模型在训练或验证集上的准确性。
在检查精度时,我们不需要计算任何梯度;因此,当我们计算分数时,我们不需要PyTorch为我们构建计算图。
我们现在可以建立一个基本的训练循环来训练我们的网络。我们将使用没有动量的随机梯度下降来训练模型。我们将使用torch.functional.cross_entropy来计算损失; 训练循环将神经网络函数,初始化参数列表(在我们的示例中为[w1,w2])和学习速率作为输入。
现在我们准备好运行训练循环了。我们需要为完全连接的权重w1和w2明确地分配张量。
CIFAR的每个小批量都有64个例子,因此张量形状为[64,3,32,32]。
展平后,x形应为[64,3 * 32 * 32]。这将是w1的第一个维度的大小。 w1的第二个维度是隐藏层大小,它也是w2的第一个维度。
最后,网络的输出是一个10维向量,表示10个类的概率分布。您不需要调整任何超参数,但在训练一个纪元后,您应该看到精度超过40%!
这里是调用了上述初始化函数,初始化w与b,由于传递的是shape,那么我们可以根据在上面的卷积神经网络注释的提示里面的shape来进行编写,上面的注释如下:
-conv_w1 : (channel_1, 3, KH1, KW1) 第一层卷积权重
-conv_b1 : (channel_1,) 第一层卷积偏值
-conv_w2 : (channel_2, channel_1, KH2, KW2) 第二层卷积权重
-conv_b2 : (channel_2,) 第二层卷积偏值
KH1与KW1,KH2与KW2是多少呢?这个注释也给出了,看本节的注释如下:
Convolutional layer (with bias) with 32 5x5 filters, with zero-padding of 2
ReLU
Convolutional layer (with bias) with 16 3x3 filters, with zero-padding of 1
ReLU
Fully-connected layer (with bias) to compute scores for 10 classes
那么最后就直接传参就行了!
conv_w1 = random_weight((channel_1,3,5,5))
conv_b1 = zero_weight((channel_1,))
conv_w2 = random_weight((channel_2,channel_1,3,3))
conv_b2 = zero_weight((channel_2,))
fc_w = random_weight((channel_2*channel_1*channel_1,10))
fc_b = zero_weight((10,))
本节则简单,就是实现调用pytorch封装的api实现就行了!这里我们直接来看需要填写代码处!
初始化conv1,w1,b1,conv2,w2,b2,以及fc及fc的w与b。
self.conv1 = nn.Conv2d(in_channel,channel_1,kernel_size=5,padding=2,bias=True)
nn.init.kaiming_normal_(self.conv1.weight)
nn.init.constant_(self.conv1.bias,0)
self.conv2 = nn.Conv2d(channel_1,channel_2,kernel_size=3,padding=1,bias=True)
nn.init.kaiming_normal_(self.conv2.weight)
nn.init.constant_(self.conv1.bias,0)
self.fc = nn.Linear(channel_2*32*32,num_classes)
nn.init.kaiming_normal_(self.fc.weight)
nn.init.constant_(self.fc.bias,0)
前向传播
调用API实现即可!
relu1 = F.relu(self.conv1(x))
relu2 = F.relu(self.conv2(relu1))
scores = self.fc(flatten(relu2))
训练三层卷积神经网络!
model = ThreeLayerConvNet(3,channel_1,channel_2,10)
optimizer = optim.SGD(model.parameters(), lr=learning_rate)
Pytorch中提供了容器模块,可以将上述model封装起来。
就直接用nn.Sequential包装起来即可!其余的基本一样!
model = nn.Sequential(
nn.Conv2d(3,channel_1,kernel_size=5,padding=2),
nn.ReLU(),
nn.Conv2d(channel_1,channel_2,kernel_size=3,padding=1),
nn.ReLU(),
Flatten(),
nn.Linear(channel_2*32*32, 10),
)
optimizer = optim.SGD(model.parameters(), lr=learning_rate,
momentum=0.9, nesterov=True)
这一节则是随意的调用api去实现自己的网络架构,然后去训练模型,使得自己的模型要在测试集上分数高于70%!
模型架构:
[conv-bn-relu-pool]x3 -> [affine]x1 -> [softmax or SVM]
layer1 = nn.Sequential(
nn.Conv2d(3, 30, kernel_size=5, padding=2),
nn.BatchNorm2d(30),
nn.ReLU(),
nn.MaxPool2d(2)
)
layer2 = nn.Sequential(
nn.Conv2d(30, 50, kernel_size=3, padding=1),
nn.BatchNorm2d(50),
nn.ReLU(),
nn.MaxPool2d(2)
)
layer3 = nn.Sequential(
nn.Conv2d(50, 100, kernel_size=3, padding=1),
nn.BatchNorm2d(100),
nn.ReLU(),
nn.MaxPool2d(2)
)
fc = nn.Linear(100*4*4, 10)
model = nn.Sequential(
layer1,
layer2,
layer3,
Flatten(),
fc
)
learning_rate = 1e-3
optimizer = optim.RMSprop(model.parameters(), lr=learning_rate)
最后跑分:
best_model = model
check_accuracy_part34(loader_test, best_model)
结果为:
Checking accuracy on test set
Got 7712 / 10000 correct (77.12)