要完成一个模型的训练,包括一下几个步骤,(1)有数据,并进行批量处理;(2)模型;(3)初始化模型参数;(4)损失函数,优化算法;(5)训练,即优化参数;(6)模型评价

注:以下代码在Pycharm运行

一、前期了解

1.问题

输入是28*28的灰度图。输出属于10个类别哪一类。x∈R784⟶概率向量∈R10

2.模型采用什么

o=XW+b,即把X拉成2维向量,通过每个特征*权重+偏执,为每个类别“打分”,谁得分高认为属于哪一类,同时由于打分过程可能出现负数,以及为了让打分高的更高,进行归一化处理,将其转变成概率

二、框架

1.首先对包进行引用

import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l

2.获取数据集并拿到训练数据和测试数据

(1)下载数据

首先定义trans操作列表,如果需要resize操作,将其插到列表最前面,其次下载训练和测试数据集,并将其进行批量化处理并返回

需要注意的是,如果是wins,不建议多进程,可以对num_workers设置为0,教程是对其采用4线程

def load_data_fashion_mnist(batch_size, resize=None):
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0, transforms.Resize(resize))
    trans = transforms.Compose(trans)
    mnist_train = torchvision.datasets.FashionMNIST(
        root="../data", train=True, transform=trans, download=True)
    mnist_test = torchvision.datasets.FashionMNIST(
        root="../data", train=False, transform=trans, download=True)
    return (data.DataLoader(mnist_train, batch_size, shuffle=True,
                            num_workers=get_dataloader_workers()),
            data.DataLoader(mnist_test, batch_size, shuffle=False,
                            num_workers=get_dataloader_workers()))

batch_size = 256
train_iter, test_iter = load_data_fashion_mnist(batch_size)

(2)初始化模型

num_inputs=28*28
num_outputs=10
W=torch.normal(0,0.01,(num_inputs,num_outputs),requires_grad=True)
b=torch.zeros(num_outputs)
def softmax(x):
    x_exp=torch.exp(x)
    partition=x_exp.sum(dim=1,keepdim=True)
    return x_exp/partition
def net(X):
    return softmax(np.dot(X.reshape(-1,W.shape[0]),W)+b)
#定义损失函数和优化算法
def cross_entropy(yhat, y):#求正确预测数量
    return -np.log(yhat[range(len(yhat))],y)

def accuracy(yhat, y):
    yhat=yhat.argmax(dim=1)
    yhat=yhat.numpy() #运行李沐代码报错,显示astype操作并不能用于tensor,所以将其转为np
    y=y.numpy()
    cmp=yhat.astype(y.dtype)==y
    return float(cmp.astype(y.dtype).sum())
class Accumulator:
    def __init__(self,n):
        self.data=[0.0]*n
    def add(self,*args):
        self.data=[a+float(b) for a,b in zip(self.data,args)]
    def reset(self):
        self.data=[0.0]*len(self.data)
    def __getitem__(self,idx):
        return self.data[idx]
def evaluate_accuracy(net,data_iter):#完成数据的总精确度
    metrics = Accumulator(2)
    for X,y in data_iter:
        yhat = net(X)
        metrics.add(accuracy(yhat,y),d2l.size(y))
    return metrics[0]/metrics[1]

(3)训练

def train_epoch_ch3(net,train_iter,loss,updater):
    if isinstance(net,torch.nn.Module): #判断模型是否是内置模型
        net.train()#开始训练
    metrics = Accumulator(3)#对三个参数进行累加
    for X,y in train_iter:
        yhat = net(X)
        l = loss(yhat,y)
        if isinstance(updater,torch.optim.Optimizer):
            updater.zero_grad()
            l.mean().backward()
            updater.step()
        else:
            l.sum().backward()
            updater(X.shape[0])
        metrics.add(float(l.sum()),accuracy(yhat,y),y.numel())
        return metrics[0]/metrics[2],metrics[1]/metrics[2]

(4)动图显示

class Animator:
    def __init__(self,xlabel=None,ylabel=None,legend=None,xlim=None,ylim=None,fmts=('-','g-','r:'),xscale='linear',yscale='linear',nrows=1,ncols=1,figsize=(3.5,2.5)):
        if legend is None:
            legend=[]
        self.fig,self.axes=d2l.plt.subplots(nrows,ncols,figsize=figsize)
        if nrows*ncols==1:
            self.axes=[self.axes]
#初始化,对子图,坐标轴进行初始化设置
       self.config_axes=lambda:d2l.set_axes(self.axes[0],xlabel,ylabel,xlim,ylim,xscale,yscale,legend)
        self.x,self.y,self.fmts=None,None,fmts

        self.fig.show()

    def add(self,x,y):

x,

是要增加的点,将点在图像里显示出来
        if not hasattr(y,'__len__'):#判断y是否标量,并将其转换成标量
            y=[y]
        n=len(y)
        if not hasattr(x,'__len__'):#判断x是否是标量,并与y对齐
            X=[x]*n
        if not self.X:#判断是否是首次调用,即self.X是否为空,并将其转换成n维的n条曲线列表
            self.X=[[] for _in range(n)]
        if not self.y:
            self.y=[[] for _in in range(n)]
        for i,(a,b) in enumerate(zip(x,y)): #循环拿到x里的新横坐标,y里的新的纵坐标,配对,拿到标号
            if a is not None and b is not None:#将新点加入到曲线中
                self.X[i].append(a)
                self.y[i].append(b)
        self.axes[0].cla()#清空子图
        for x,y, fmts in zip(self.X,self.y,self.fmts):
            self.axes[0].plot(x,y,fmts)#绘制新曲线
        self.config_axes()#每次重画完线条,都能保持统一的坐标格式。

       self.fig.canvas.draw()
       self.fig.canvas.flush_events()
        plt.pause(0.001)

(5)训练

def train_cha3(num_epochs,net,train_iter,loss,updater,test_iter):
    animator=Animator(xlabel='epoch',legend=['train loss','train acc','test acc'],xlim=[1,num_epochs],ylim=[0.3,0.9])
    for epoch in range(num_epochs):
        train_metrics=train_epoch_ch3(net, train_iter, loss, updater)
        test_acc=evaluate_accuracy(net,test_iter)
        animator.add(epoch+1,train_metrics+(test_acc,))
        train_loss,train_acc=train_metrics
        # assert train_loss<=0.5,train_loss
        # assert train_acc>=0.7,train_acc
        # assert test_acc>=0.7,test_acc
lr=0.1
def updater(batch_size):
    return d2l.sgd([W,b],lr,batch_size)
num_epochs = 12
train_cha3(num_epochs,net,train_iter,cross_entropy,updater,test_iter)
plt.ioff()
plt.show()

三、其他文件

自定义了一个d2l文件,放了如下代码

import numpy as np
import torch
import torchvision
from IPython import display
from torch.utils import data
from torchvision import transforms#数据操作
import random
import matplotlib.pyplot as plt
import matplotlib
from matplotlib_inline import backend_inline
import d2l
from torch.utils import data

def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend):
    """设置matplotlib的轴"""
    axes.set_xlabel(xlabel)
    axes.set_ylabel(ylabel)
    axes.set_xscale(xscale)
    axes.set_yscale(yscale)
    axes.set_xlim(xlim)
    axes.set_ylim(ylim)
    if legend:
        axes.legend(legend)
    axes.grid()
    return axes
def sgd(params, lr, batch_size):
    with torch.no_grad():
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()

更多推荐