首先下载读取Mnist数据集
%matplotlib inline
from pathlib import Path
import requests
%matplotlib inline
DATA_PATH = Path("data")
PATH = DATA_PATH / "mnist"
PATH.mkdir(parents=True, exist_ok=True)
URL = "http://deeplearning.net/data/mnist/"
FILENAME = "mnist.pkl.gz"
if not (PATH / FILENAME).exists():
content = requests.get(URL + FILENAME).content
(PATH / FILENAME).open("wb").write(content)
import pickle
import gzip
with gzip.open((PATH / FILENAME).as_posix(), "rb") as f:
((x_train, y_train), (x_valid, y_valid), _) = pickle.load(f, encoding="latin-1")
随机查看数据
#看一个数据
from matplotlib import pyplot as plt
import numpy as np
plt.imshow(x_train[1125].reshape(28,28),cmap="gray")
print(x_train.shape)
将数据转换为tensor张量形式
import torch
#数据转换为tensor的格式
x_train,y_train,x_valid,y_valid=map(
torch.tensor,(x_train,y_train,x_valid,y_valid)
)
n,c=x_train.shape
使用nn.moduel构建网络,torch.nn.functional中有很多功能,也会很常用。那什么时候使用nn.Module,什么时候使用nn.functional呢?一般情况下,如果模型有可学习的参数,最好用nn.Module,其他情况nn.functional相对更简单一些
import torch.nn.functional as F
loss_func=F.cross_entropy
#构建网络
from torch import nn
import torch.nn.functional as F
class Mnist_NN(nn.Module):
def __init__(self):
super().__init__()
self.hidden1=nn.Linear(784,128)
self.hidden2=nn.Linear(128,256)
self.out=nn.Linear(256,10)
def forward(self,x):
x=F.relu(self.hidden1(x))
x=F.relu(self.hidden2(x))
x=self.out(x)
return x
net=Mnist_NN()
print(net)
打印构建的网络net,可以看到有两个隐藏层,一个输出层。输出层的输出特征是10而不是1,因为这是一个十分类的网络,会对每一个类都输出一个概率,因此输出的是一个由10个概率组成的一维矩阵。例如[0,0.1,0.05,0,0,0,0,0,0.85,0],此输出就代表此输入图像为8的概率为0.85,为1的概率为0.1,为3的概率为0.05,其余数字的概率均为0。
构建网络时,已经自动进行了权重以及偏置的初始化,可以用下面的代码进行打印。nn.moduel构建网络有如下特点。
for name, parameter in net.named_parameters():
print(name, parameter, parameter.size())
print('---------------------------------------------')
打印定义好名字里的权重和偏置项
使用tenordataset和dataloader来简化batch_size需要编写的代码,调用这两个工具包即可完成batch_size的数据拆分。具体代码如下:
#使用tenordataset和dataloader来简化
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader
def get_data(x_train, y_train,x_valid,y_valid, bs):
train_ds=TensorDataset(x_train,y_train)
valid_ds=TensorDataset(x_valid,y_valid)
return (
DataLoader(train_ds, batch_size=bs, shuffle=True),
DataLoader(valid_ds, batch_size=bs * 2),
)
接下来写训练函数fit,其中loss_batch用于每一个batch的损失值计算。除此之外,一般在训练模型时加上model.train(),这样会正常使用Batch Normalization和 Dropout;测试的时候一般选择model.eval(),这样就不会使用Batch Normalization和 Dropout。
import numpy as np
def loss_batch(model,loss_func,xb,yb,opt=None):
loss=loss_func(model(xb),yb)
if opt is not None:
loss.backward()
opt.step()
opt.zero_grad()
return loss.item(),len(xb)
def fit(steps,model,loss_func,opt,train_dl,valid_dl):
for step in range(steps):
model.train()
for xb,yb in train_dl:
loss_batch(model,loss_func,xb,yb,opt)
model.eval()
with torch.no_grad():
losses,nums=zip(
*[loss_batch(model,loss_func,xb,yb) for xb,yb in valid_dl]
)
val_loss = np.sum(np.multiply(losses, nums)) / np.sum(nums)
print('当前step:'+str(step), '验证集损失:'+str(val_loss))
最后的准备工作是写get_model,导入网络模型。
from torch import optim
def get_model():
model=Mnist_NN()
return model,optim.SGD(model.parameters(),lr=0.001)
然后三行代码完成手写数字的识别
train_dl, valid_dl = get_data(x_train, y_train, x_valid, y_valid, bs)
model,opt=get_model()
fit(25,model,loss_func,opt,train_dl,valid_dl)
心得:通过这个简单的网络熟悉PyTorch的神经网络编写过程,这个代码其实更注重调用,并不是完全按照前向传播后向传播的顺序一步一步构建一个网络,而是写了很多函数,最后主要的三行代码就完成了网络的训练。