[深度学习] 回归、损失函数、分类
1、 回归模型
比如一共有4种特征和3种输出动物类别(狗、猫、鸡),则权重包含12个标量(带下标的
最后,在对这些输出值进行
2、 函数
它们通过下式将输出值变成值为正数且和为
其中:
容易看出
尽管
因此,如果你在未经
3.
线性函数:
设高和宽分别为2个像素样本
则输出层的输出为:
预测狗、猫、鸡的概率分布为:
那么总结一下
则
4、参数估计
4.1 交叉熵损失函数(用于分类问题)
在使用
对于单个样本和多分类问题,交叉熵损失函数定义如下:
其中:
是类别的总数 是一个 编码的向量。表示真实的标签。在这个向量中,正确类别的索引处的值为1,其余为0。这意味着,交叉熵损失只计算真实类别对应的预测概率的负对数。如果模型对真实类别非常有信心,即其预测概率 接近于1,那么损失将接近于零。相反,如果模型对真实类别的预测概率很低,损失将会很高。 是模型的输出,即在 函数后得到的预测概率分布,也是一个长度为 的向量,每个元素 代表模型预测样本属于第 个类别的概率。
这个损失函数将真实标签的概率乘以预测概率的负对数。由于是one-hot编码的,所以除了实际类别对应的项,其他项都会是零,不会对损失函数的求和有贡献。这意味着损失函数实质上简化为:
这里的
对于所有训练样本的平均交叉熵损失计算如下:
假设你有一个由
这里:
表示关于模型权重的代价函数, 包含了权重和偏置 是样本的总数。 是真实标签的第 个元素,针对第 个样本。如果第个 样本的真实类别是 ,就是1,否则是0。 是模型预测出的第 个样本属于类别的概率。 是类别的总数。- 外层的求和
是对所有训练样本进行遍历。 - 内层的求和
是对所有类别进行遍历。 代表的是正确类别的索引
注意:
对于单个样本,交叉熵损失函数定义为:
而对于所有训练样本的平均损失定义为 :
这个平均损失
4.2 均方误差损失函数(用于回归问题)
对于单个样本
假设我们有一个线性回归模型,损失函数
为什么是均方误差的一半?这只是为了在求导时可以抵消掉系数2,简化计算过程。
应用链式法则,我们首先对内部进行求导(关于预测
接下来,由于
利用链式法则,结合上面的两个偏导数,我们可以得到梯度如下:
考虑到梯度下降的方向是损失函数减少的方向,我们取上式的负值:
这就是参数
5. 回归的从零开始实现
我们将使用Fashion-MNIST数据集【简介】,进行图像多分类问题。
:导包
import torch from IPython import display from d2l import torch as d2l
:读取数据集
batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
① 将展平每个图像,将它们视为长度784的向量。向量的每个元素与w相乘,所以w也需要784行。
② 因为数据集有10个类别,所以网络输出维度为10.
初始化模型参数
将展平每个图像,把它们看作长度为
因此softmax回归的权重和偏差参数分别为
num_inputs = 784 num_outputs = 10 # 随机初始化参数,均值为0,方差为0.01,形状,并为模型参数附上梯度 W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True) b = torch.zeros(num_outputs, requires_grad=True)
定义 函数
def softmax(X): X_exp = torch.exp(X) # 每个都进行指数运算 partition = X_exp.sum(1, keepdim=True)# 按维度1(行)求和,结果为列向量 return X_exp / partition # 这里应用了广播机制
我们将每个元素变成一个非负数。 此外,依据概率原理,每行总和为1
:实现 回归模型
def net(X): return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)#-1为默认的批量大小,表示有多少个图片,每个图片用一维的784列个元素表示,即X被reshape为256*784形状的矩阵 ,并且行和为1
实现交叉熵损失函数
① 创建一个数据y_hat,其中包含2个样本在3个类别的预测概率,使用y作为y_hat中概率的索引。
y = torch.tensor([0,2]) # 标号索引 y_hat = torch.tensor([[0.1,0.3,0.6],[0.3,0.2,0.5]]) # 两个样本在3个类别的预测概率 y_hat[[0,1],y] # 把第0个样本对应标号"0"的预测值拿出来、第1个样本对应标号"2"的预测值拿出来,比如这里就是拿出y_hat[0][y[0]] = 0.1 和 y_hat[1][y[1]] = 0.5
⑧ 实现交叉熵损失函数。
def cross_entropy(y_hat, y): return -torch.log(y_hat[range(len(y_hat)), y])
计算分类准确率
给定一个类别的预测概率分布y_hat,我们把预测概率最大的类别作为输出类别。如果它与真实类别y一致,说明这次预测是正确的。分类准确率即正确预测数量与总预测数量之比。
为了演示准确率的计算,下面定义准确率accuracy函数。其中y_hat.argmax(axis=1)返回矩阵y_hat每行中最大元素的索引,且返回结果与变量y形状相同。相等条件判别式(y_hat.argmax(axis=1) == y)是一个类型为ByteTensor的Tensor,我们用float()将其转换为值为0(相等为假)或1(相等为真)的浮点型Tensor。
将预测类别与真实y
元素进行比较
def accuracy(y_hat,y): """计算预测正确的数量""" if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: # y_hat.shape[1]>1表示不止一个类别,每个类别有各自的概率 y_hat = y_hat.argmax(axis=1) # y_hat.argmax(axis=1)为求行最大值的索引(最大的作为预测) cmp = y_hat.type(y.dtype) == y # 先判断逻辑运算符==,再赋值给cmp,cmp为布尔类型的数据 return float(cmp.type(y.dtype).sum()) # 获得y.dtype的类型作为传入参数,将cmp的类型转为y的类型(int型),然后再求和
评价任意模型net在数据集data_iter上的准确率。
# 可以评估在任意模型net的准确率 def evaluate_accuracy(net,data_iter): """计算在指定数据集上模型的精度""" if isinstance(net,torch.nn.Module): # 如果net模型是torch.nn.Module实现的神经网络的话,将它变成评估模式 net.eval() # 将模型设置为评估模式 metric = Accumulator(2) # 正确预测数、预测总数,metric为累加器的实例化对象,里面存了两个数 for X, y in data_iter: metric.add(accuracy(net(X),y),y.numel()) # net(X)将X输入模型,获得预测值。y.numel()为样本总数 return metric[0] / metric[1] # 分类正确的样本数 / 总样本数
# Accumulator实例中创建了2个变量,用于分别存储正确预测的数量和预测的总数量 class Accumulator: """在n个变量上累加""" 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)] # zip函数把两个列表第一个位置元素打包、第二个位置元素打包.... def reset(self): self.data = [0.0] * len(self.data) def __getitem__(self,idx): return self.data[idx]
训练模型
回归的训练
# 训练函数 def train_epoch_ch3(net, train_iter, loss, updater): if isinstance(net, torch.nn.Module): net.train() # 开启训练模式 metric = Accumulator(3) for X, y in train_iter: y_hat = net(X) l = loss(y_hat,y) # 计算损失 if isinstance(updater, torch.optim.Optimizer): # 如果updater是pytorch的优化器的话 updater.zero_grad() # 梯度置0 l.backward() # 计算梯度 updater.step() # 参数自更新 metric.add(float(l)*len(y),accuracy(y_hat,y),y.size().numel()) # 总的训练损失、样本正确数、样本总数 else: l.sum().backward() updater(X.shape[0]) metric.add(float(l.sum()),accuracy(y_hat,y),y.numel()) return metric[0] / metric[2], metric[1] / metric[2] # 所有loss累加除以样本总数,总的正确个数除以样本总数
动画绘制(辅助函数)
class Animator: def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None, ylim=None, xscale='linear',yscale='linear', fmts=('-','m--','g-.','r:'),nrows=1,ncols=1, figsize=(3.5,2.5)): if legend is None: legend = [] d2l.use_svg_display() 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 def add(self, x, y): if not hasattr(y, "__len__"): y = [y] n = len(y) if not hasattr(x, "__len__"): x = [x] * n if not self.X: self.X = [[] for _ in range(n)] if not self.Y: self.Y = [[] for _ in range(n)] for i, (a,b) in enumerate(zip(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, fmt in zip(self.X, self.Y, self.fmts): self.axes[0].plot(x, y, fmt) self.config_axes() display.display(self.fig) display.clear_output(wait=True)
总训练函数
def train_ch3(net,train_iter,test_iter,loss,num_epochs,updater): animator = Animator(xlabel='epoch',xlim=[1,num_epochs],ylim=[0.3,0.9], legend=['train loss','train acc','test acc'])# 可视化 for epoch in range(num_epochs): # 变量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_metrics+(test_acc,) 仅将两个值的正确率相加, train_loss, train_acc = train_metrics lr = 0.1 def updater(batch_size): return d2l.sgd([W,b], lr, batch_size)
预测
def predict_ch3(net,test_iter,n=6): for X, y in test_iter: break # 仅拿出一批六个数据 trues = d2l.get_fashion_mnist_labels(y) preds = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1)) titles = [true + '\n' + pred for true, pred in zip(trues,preds)] d2l.show_images(X[0:n].reshape((n,28,28)),1,n,titles=titles[0:n]) predict_ch3(net,test_iter)
5. 回归的简洁实现(使用框架)
import torch from torch import nn from d2l import torch as d2l batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) # Softmax回归的输出是一个全连接层 # PyTorch不会隐式地调整输入的形状 # 因此,我们定义了展平层(flatten)在线性层前调整网络输入的形状 net = nn.Sequential(nn.Flatten(),nn.Linear(784,10)) def init_weights(m): if type(m) == nn.Linear: nn.init.normal_(m.weight, std=0.01) # 均值为0(默认),方差为0.01的随机值 net.apply(init_weights) print(net.apply(init_weights)) # net网络的参数用的是init_weights初始化参数 # 在交叉熵损失函数中传递未归一化的预测,并同时计算softmax及其对数 loss = nn.CrossEntropyLoss() # 使用学习率为0.1的小批量随即梯度下降作为优化算法 trainer = torch.optim.SGD(net.parameters(),lr=0.1) num_epochs = 10 train_ch3(net,train_iter,test_iter,loss,num_epochs,trainer)
参考:
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 分享4款.NET开源、免费、实用的商城系统
· 解决跨域问题的这6种方案,真香!
· 一套基于 Material Design 规范实现的 Blazor 和 Razor 通用组件库
· 5. Nginx 负载均衡配置案例(附有详细截图说明++)