前言
之前的一个暑假跟着师兄做了一些任务,开学了发现自己成了一名代码搬运工。就比如dataloader封装环节,我自己还是写不出,所以借着pytorch综合实践上机课的机会,想重新学一学pytorch。
文章是参照pytorch官网的教程WHAT IS TORCH.NN REALLY?,将最后的mnist分类任务过程做了整合。
正文
第一步:下载mnist数据集
from pathlib import Path
import requests
import pickle
import gzip
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)
with gzip.open((PATH / FILENAME).as_posix(),"rb" ) as f:
((x_train,y_train),(x_valid,y_valid),_) = pickle.load(f, encoding = "latin-1")
第二步:封装成dataloader
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import TensorDataset, DataLoader
from torch import optim
###############
#numpy.array->torch.tensor->tensordataset->dataloader
###############
x_train, y_train, x_valid, y_valid = map(torch.tensor, (x_train, y_train, x_valid, y_valid))
train_ds = TensorDataset(x_train, y_train)
valid_ds = TensorDataset(x_valid, y_valid)
train_dl = DataLoader(train_ds, bs,shuffle = True)
valid_dl = DataLoader(valid_ds, bs*2)
###############
#在dataloader中加入预处理
###############
class WrappedDataLoader:
def __init__(self,dl):
self.dl = dl
# self.func = func
def preprocess(self,x,y):
return x.view(-1,1,28,28),y
def __len__(self):
return len(self.dl)
def __iter__(self):
batches = iter(self.dl)
for b in batches:
yield (self.preprocess(*b))
train_dl, valid_dl = get_data(train_ds, valid_ds, bs = 64)
train_dl = WrappedDataLoader(train_dl)
valid_dl = WrappedDataLoader(valid_dl)
第三步:定义模型与损失函数
###############
# 模型类
###############
class Cnn_model(nn.Module):
def __init__(self):
super(Cnn_model,self).__init__()
# self.sequence = nn.Sequential(
self.sequence = nn.Sequential(
nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1),
nn.ReLU(),
nn.Conv2d(16, 16, kernel_size=3, stride=2, padding=1),
nn.ReLU(),
nn.Conv2d(16, 10, kernel_size=3, stride=2, padding=1),
nn.ReLU(),
nn.AdaptiveAvgPool2d(1),
)
def forward(self,xb):
xb = self.sequence(xb)
xb = xb.view(xb.size(0),-1)
return xb
###############
# 返回模型,和optim
###############
def get_model():
model = Cnn_model()
optimizer = optim.SGD(model.parameters(),lr = 1e-2)
return model, optimizer
model, optimizer = get_model()
loss_func = F.cross_entropy # loss_func
第四步:定义fit过程
###############
# 结果的计算/loss的计算/是否要反向传播
###############
def loss_batch(model,loss_func,xb,yb, optimizer= None):
y_pred = model(xb)
loss = loss_func(y_pred, yb)
if optimizer != None:
loss.backward()
optimizer.step()
optimizer.zero_grad()
return loss, len(xb) # len(xb)用于计算valid中loss平均值
###############
# fit
###############
def fit(epochs, model, loss_func, opt, train_dl, valid_dl):
for epoch in range(epochs):
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(epoch, val_loss)
最后只需要:
fit(epochs=10, model=model, loss_func=loss_func, opt=optimizer, train_dl=train_dl, valid_dl=valid_dl)
就可以跑出结果:
总结
大致用到的技巧:
- pathlib\requests\pickle\gzip库
- Tensordataset\dataloader,以及自定义dataloader类(需要定义:init\len\iter)
- nn.Sequential与nn.AdaptiveAvgPool2d(参数取决于你需要输出的维度)
- 定义get_model 函数,返回model和optim,loss_fn额外定义
- 定义fit函数,简化训练过程
- 将结果计算、loss计算、优化写入loss_batch类中,并通过opt参数使得train与valid过程都可以使用
下一篇:
Text Segmentation as a Supervised Learning Task模型复现