softmax分类(内置和自定义)+动手学深度学习+(网络训练流程+累加器设置+交叉熵的定义+常用损失函数)

softmax的定义和代码

softmax可用于求解多分类问题的概率大小,通过对比最大概率的位置argmax来得到类别
softmax的特点:相加等于1,其交叉熵损失函数的导数为softmax输出值与真实值之间的差距

def softmax_(input):#@save
	input_exp=torch.exp(input)
	return input_exp/torch.sum(input_exp,dim=1,keepdim=True)

常见损失函数

交叉熵

针对分类问题,交叉熵越大,说明不确定性越高,信息量越大。
根据公式,对于二分类问题(01),只需要考虑1的概率y_hat即可,对1的y_hat对数相加,得到其交叉熵。
对交叉熵进行修改,将其变为所有目标类概率的对数综合,即认为所有的yj都是1.
交叉熵

def cross_entropy(y_true,y_hat):
    return -torch.log(y_hat[range(y_hat.shape[0]),y_true])#修改后的交叉熵

内置函数,使用内置交叉熵函数需要将softmax对输出的处理作用去除,因为该函数没有将softmax概率传递到损失函数中, 而是在交叉熵损失函数中传递未规范化的预测,并同时计算softmax及其对数

loss=torch.nn.CrossEntropyLoss(reduction='none')#保留每一个step的损失

内置的计算

lossNum=loss(y_pred,y)

分类问题的精度求解,即正确分类数/总样本数

def accuracy(y_true,y_hat):
    y_label=torch.argmax(y_hat,dim=1)
    y_diff= y_label.type(y_true.dtype)==y_true
    return float(y_diff.type(y_true.dtype).sum())#输出的是正确分类的样本数,需要除以y.numel()才能得到精度

Lp范数损失函数

L1范数在损失函数的所有位置都有一样的导数值,但是在0点处存在拐点,不可导
L2范数在损失函数的中心0处拥有最小导数值,且越靠近0越小,会导致误差越小收敛越慢
Huber‘s Robust Loss在中心处拥有L1的导数特性,在远中心区域有L2的特性
Huber‘s Robust Loss

累加器设置

为了方便在训练过程中,对多个batch的训练误差和测试误差累积计算,引入累加器

#累加器是一个1维的
class Accumulator():#@save
    def __init__(self,shape):
        self.receptor=[0.0]*shape

    def add(self,*args):
        self.receptor=[a+float(b) for a,b in zip(self.receptor,args)]#对各个元素进行加法操作

    def reset(self):
        self.receptor=[0.0]*len(self.receptor)

    def __getitem__(self, item):#实现索引
        return self.receptor[item]

网络训练流程

该过程包括了对自定义模型和内置模型的训练
内置总体流程是:模型参数梯度清零–》预测–》损失值计算–》反向传播–》梯度更新
其中train和eval模式的区别是:将一些在train中需要修改的参数确定下来,发生改变或不能被改变,如在BN层中和Dropout层中的计算,测试模式和训练模式的功能不同

def train_epoch_ch3(epoch,model,train_iter,updater,loss):
    metric=Accumulator(3)#累加器,第一个存储损失值,第二个存储正确分类数,第三个存储参与训练batches的样本数
    for x,y in train_iter:
        if isinstance(model,torch.nn.Module):
            model.train()#确定维train模式
        if isinstance(updater,torch.optim.Optimizer):#判断类别是否匹配
            model.zero_grad()
            y_pred=model(x)
            lossNum=loss(y,y_pred)
            lossNum.mean().backward()
            updater.step()
        else:
            y_pred = model(x)
            lossNum=loss(y,y_pred)
            lossNum.sum().backward()
            sgd([w,b],lr,x.shape[0])#内部自定义了梯度清零
        with torch.no_grad():
            metric.add(float(lossNum.sum()),accuracy(y,y_pred),y.numel())
    print(f'loss_mean:{metric[0]/metric[2]},accuracy_mean:{metric[1]/metric[2]}')
    return metric[0]/metric[2],metric[1]/metric[2]

测试集的训练流程

def estimate(model,test_iter):
    if isinstance(model,torch.nn.Module):
        model.eval()#将内置模型改为测试模式
    metrics=Accumulator(2)
    for x,y in test_iter:
    	with torch.no_grad():#加快计算速度
        	metrics.add(accuracy(y,model(x)),y.numel())#添加测试正确样本数和总样本数
    return metrics[0]/metrics[1]

epoch的循环

def train_ch3(model,updater,loss,trainIter,testIter):
    for epoch_num in range(epoch):
        train_metrics=train_epoch_ch3(epoch,model,trainIter,updater,loss)
        test_acc=estimate(model,testIter)#测试集的精度和损失计算
    train_loss, train_acc = train_metrics
    assert train_loss < 0.5, train_loss#如果没有达到条件,则输出train_loss(作为错误信息,也可以输出字符串如'error for calculation')
    assert train_acc <= 1 and train_acc > 0.7, train_acc
    assert test_acc <= 1 and test_acc > 0.7, test_acc

内置模型的建立

需要先将图片数据打平,再建立线性模型

model=torch.nn.Sequential(torch.nn.Flatten(),torch.nn.Linear(num_input,num_output))

模型参数的初始化

def initialize(model):
    if type(model)==torch.nn.Linear:
        torch.nn.init.normal_(model.weight,std=0.01)
model.apply(initialize)
loss=torch.nn.CrossEntropyLoss(reduction='none')
updater=torch.optim.SGD(model.parameters(),lr=lr)
train_ch3(model, updater, loss, trainIter, testIter)

总代码

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

def softmax_(input):#@save
    input_exp=torch.exp(input)
    return input_exp/torch.sum(input_exp,dim=1,keepdim=True)

def sgd(params,lr,batchsize):#@save
    with torch.no_grad():
        for param in params:
            param-=lr*param.grad/batchsize
            param.grad.zero_()

class Accumulator():#@save
    def __init__(self,shape):
        self.receptor=[0.0]*shape
    def add(self,*args):
        self.receptor=[a+float(b) for a,b in zip(self.receptor,args)]
    def reset(self):
        self.receptor=[0.0]*len(self.receptor)
    def __getitem__(self, item):
        return self.receptor[item]

def LinearNet(x):
    return softmax_(torch.matmul(x.reshape((-1,w.shape[0])),w)+b)

def cross_entropy(y_true,y_hat):
    return -torch.log(y_hat[range(y_hat.shape[0]),y_true])

def accuracy(y_true,y_hat):
    y_label=torch.argmax(y_hat,dim=1)
    y_diff= y_label.type(y_true.dtype)==y_true
    return float(y_diff.type(y_true.dtype).sum())

def train_epoch_ch3(epoch,model,train_iter,updater,loss):
    metric=Accumulator(3)
    for x,y in train_iter:
        if isinstance(model,torch.nn.Module):
            model.train()
        if isinstance(updater,torch.optim.Optimizer):
            model.zero_grad()
            y_pred=model(x)
            lossNum=loss(y_pred,y)
            lossNum.mean().backward()
            updater.step()
        else:
            y_pred = model(x)
            lossNum=loss(y,y_pred)
            lossNum.sum().backward()
            sgd([w,b],lr,x.shape[0])
        with torch.no_grad():
            metric.add(float(lossNum.sum()),accuracy(y,y_pred),y.numel())
    print(f'loss_mean:{metric[0]/metric[2]},accuracy_mean:{metric[1]/metric[2]}')
    return metric[0]/metric[2],metric[1]/metric[2]

def estimate(model,test_iter):
    if isinstance(model,torch.nn.Module):
        model.eval()
    metrics=Accumulator(2)
    for x,y in test_iter:
    	with torch.no_grad():
        	metrics.add(accuracy(y,model(x)),y.numel())
    return metrics[0]/metrics[1]

def train_ch3(model,updater,loss,trainIter,testIter):
    for epoch_num in range(epoch):
        train_metrics=train_epoch_ch3(epoch,model,trainIter,updater,loss)
        test_acc=estimate(model,testIter)
    train_loss, train_acc = train_metrics
    assert train_loss < 0.5, train_loss
    assert train_acc <= 1 and train_acc > 0.7, train_acc
    assert test_acc <= 1 and test_acc > 0.7, test_acc


num_input=3600
num_output=10
epoch=10
lr=0.01
w=torch.normal(0,0.01,[num_input,num_output],dtype=torch.float32,requires_grad=True)
b=torch.zeros(num_output,dtype=torch.float32,requires_grad=True)
if __name__=='__main__':
    trans=[transforms.ToTensor()]
    trans.insert(0,transforms.Resize(60))
    trans=transforms.Compose(trans)
    trainData = torchvision.datasets.FashionMNIST('../data', train=True, transform=trans, download=True)
    testData = torchvision.datasets.FashionMNIST('../data', train=False, transform=trans, download=True)
    trainIter = data.DataLoader(trainData, batch_size=20, shuffle=True, num_workers=4)
    testIter=data.DataLoader(testData, batch_size=20, shuffle=True, num_workers=4)
    train_ch3(LinearNet, sgd, cross_entropy, trainIter, testIter)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值