首页 > 其他分享 >limu|P8-9|线性回归、softmax回归

limu|P8-9|线性回归、softmax回归

时间:2024-08-09 23:28:47浏览次数:11  
标签:loss self batch train softmax P8 net 回归 size

线性回归模型:\(y = Xw + b + \epsilon\)
1、如何衡量模型质量?loss function损失函数——量化实际值和预测值之间的差距
可证:在高斯噪声的假设下,线性模型的最大似然估计 等价于 最小化均方误差(MSE)。证明在另一篇里写过:https://www.cnblogs.com/xjl-ultrasound/p/18305000
平方误差:
\(l^{(i)}(w,b) = \frac{1}{2}(\hat{y}^{(i)} - y^{(i)})^2\)
均方误差(在n个样本上的损失均值):
\(L(w,b) = \frac{1}{n}\sum_{i=1}^{n}l^{(i)}(w,b)\)
2、如何更新参数以提升模型质量?一步步去更新参数以降低loss,最后,找到一组参数,使loss最小化
常用的为“梯度下降”:
(1)即在loss function递减的方向上更新参数,以降低loss。已知梯度是函数值增加最快的方向,那么负梯度方向则是函数值下降最快的方向。所以,可沿着loss function的负梯度方向更新参数,使loss降低
(2)这里面每次更新参数时都涉及求loss function的梯度,如果每次都要遍历数据集,会很慢,常用小批量随机梯度下降(minibatch SGD),每次更新参数,随机抽取一个小批量B,去计算小批量的损失均值关于模型参数的导数,用原参数减去(学习率*导数)
(3)公式:\((w,b) \leftarrow (w,b) - \lambda\frac{\sum_{i\in{B}}\partial_{(w,b)}l^{(i)}(w,b)}{|B|}\)
(4)学习率lr和批量大小batch_size两个超参数:
lr太小——算太多次梯度,贵!
lr太大——容易走过头,不断震荡但loss未有效下降
这里小批量随机梯度下降,相当于每次用小批量的损失均值去近似整体的损失均值,以更新参数。但是,反直觉的是,batch_size小点反而利于收敛
batch_size小:1-noise大,对神经网络是好事,不易走偏,使得模型容忍度↑,更鲁棒,防过拟合;2-但若batch_size过小,每次计算量太小,不适合并行以最大化利用资源
batch_size大:1-太大的话可能模型不那么鲁棒;2-内存消耗大;3-一个batch内可能包含太多相似的样本,浪费计算

线性模型实现
不管是复杂还是简单的神经网络,实现过程都是模板化的:
1、数据集获取&读取
2、定义模型
3、初始化模型参数
4、定义损失函数
5、定义优化算法
6、定义评估指标
7、训练
8、预测、评估

第一步:数据集获取和读取
这一步是模板化的,以下给出的不是线性回归所用的数据集,线性回归用的数据是自己生成的
公共数据集+DataLoader读取,如下:

def load_data_fashion_mnist(batch_size, resize=None):  #@save
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    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()))

私有数据集(Dataset类)+DataLoader读取,如下:

from torch.utils.data import Dataset, DataLoader
from torchvision import transforms
import os
from PIL import Image

batch_size = 128
train_trans = transforms.Compose([
    transforms.ToPILImage(), # 如果用Image.open打开(如下),这行不需要
    transforms.RandomHorizontalFlip(),
    transforms.RandomRotation(15),
    transforms.ToTensor(),])
test_trans = transforms.Compose([
    transforms.ToPILImage(),  # 如果用Image.open打开(如下),这行不需要
    transforms.ToTensor(),]) 
# 测试集不需要翻转或旋转图片
# 注意ToTensor会把channel提到第一维
# 还可按需加入Resize

class Mydata(Dataset):
    def __init__(self, root_dir, label_dir, trans = None):
        self.root_dir = root_dir
        self.label_dir = label_dir
        self.path = os.path.join(self.root_dir, self.label_dir)
        self.img_path = os.listdir(self.path) # 获取path路径下所有子文件(图像)的名称
        self.trans = trans
    def __getitem__(self, idx):
        img_name = self.img_path[idx] 
        img_item_path = os.path.join(self.root_dir, self.label_dir, img_name)
        img = Image.open(img_item_path) # Image.open返回PIL类型,还可以用cv2.imread(),返回ndarray类型
        if self.transform is not None:
          img = self.transform(img)
        label = self.label_dir
        return img, label
    def __len__(self):
        return len(self.img_path)

root_tr = r'D:\ai-learning\pytorch\hymenoptera_data\train' # train文件夹
benign_label_dir = "benign"
malignant_label_dir = "malignant"
benign_tr = Mydata(root_tr, benign_label_dir, trans=train_trans)
malignant_tr = Mydata(root_tr, malignant_label_dir, trans=train_trans)
tr_dataset = benign_tr + malignant_tr
# 同理可得te_dataset

tr_iter = DataLoader(tr_dataset, batch_size=batch_size, shuffle=True, num_workers=0)
# te_iter = DataLoader(te_dataset, batch_size=batch_size, shuffle=False, num_workers=0)

Image.open和cv2.imread区别:https://www.cnblogs.com/ElaineTiger/p/18138084

第二步:定义线性模型

from torch import nn
net = nn.Sequential(nn.Linear(2, 1))
# nn.Sequential理解为list of layers

第三步:初始化模型参数

net[0].weight.data.normal_(0, 0.01) # 访问net中第一层的参数weight,进行初始化
net[0].bias.data.fill_(0)

第四步:定义损失函数
loss = nn.MSELoss()

第五步:定义优化算法
trainer = torch.optim.SGD(net.parameters(), lr=0.03)

训练

num_epochs = 3
for epoch in range(num_epochs):
    for X, y in data_iter: # 每个batch的数据拿出来
        l = loss(net(X) ,y) # net(X)算的预测值y'
        trainer.zero_grad() # 先把trainer(SGD)的梯度清零
        l.backward() # 算loss(损失均值)关于参数的梯度
        trainer.step() # 更新模型参数
    l = loss(net(features), labels) # X=features, y=labels
    print(f'epoch {epoch + 1}, loss {l:f}')

李沐老师的线性回归部分QA总结(QA绝对是精华)
一、求梯度时为什么是求损失均值关于模型参数的导数,即为什么损失要求均值?
使得梯度大小不受batch_size影响,这样调学习率的时候也不受batch_size影响。此外,因为可能出现最后一个batch内数据量不足batch_size的情况,SGD里是会自动帮你除以当前batch的size,而不是预设好的batch_size
二、调学习率的办法:
1、用对学习率没那么敏感的方法,如Adam,比较smooth
2、合理的参数初始化,使得学习率简单调调就行,,,
3、gridsearch
三、二阶导(牛顿法)收敛更快,为什么不用二阶导而用一阶导?
1、未必都能求二阶导
2、二阶导,计算量大,贵;易陷入局部最优
*真正的牛顿法做不了,但有的方法可以近似
3、我们不太关心收敛得快不快,更关心收敛得准不准
还有另一个思想:模型都是错的,但是有用。意思是,我们不可能得到精确的模型,这时候,什么最优解意义不大,快速准确的求解模型可能还不如大方向是正确的随机计算(粗糙拟合)好。。。比如用梯度解析解(只有线性模型有解析解)的泛化性不如SGD。。。
四、load数据时,如果数据很大,全部load进来,内存爆炸?
可以把数据放在硬盘里,每次取一个batch左右的数据
五:随机取batch,其实是把数据下标随机打乱,按顺序抽,能够保证一个epoch下来,取完全部的数据
六、数据总量不是batch_size大小的整数倍,怎么办?1-不咋办,就这样,最后一个batch小一点;2-最后一个小一点的batch直接扔掉;3-从下一个epoch采点数据,补全最后一个小一点的batch
七、怎么判断收敛?关系到epoch的大小
1、两个目标之间变化不大时
2、用一个验证集,其预测精度变化不大时
3、一般更常用的是,先凭直觉设置epoch大小,比如epoch=100,画学习曲线,再定epoch。从而对不同数据集形成经验

iter和for循环:
for循环的本质就是先通过__iter__这个方法获取可迭代对象的迭代器,然后对获取的迭代器进行不断调用__next_方法,来获取下一个值,并将其赋值给临时变量i(in前面的那个临时变量),当遇到StopIteration异常对象则终止循环。参考:https://blog.csdn.net/abraham_ly/article/details/107874466
——————————————————————————————————————
Softmax回归模型:即Softmax输出层
用于:多类别分类问题——只有1个正确答案——互斥输出
真实值(标签):one-hot encoding,一个向量,分量数=类别数,仅正确类别的分量为1,其余为0
预测值(预测概率和预测类别):全连接层(input_dim=上一层输出的feature数,output_dim=类别数)+ softmax(保证1-输出为概率,范围0-1;2-输出之和为1;3-模型保持可导),这样我们取概率最大的分量对应的类别为预测类别
softmax公式:\(\hat{y} = softmax(o)\),其中,\(\hat{y_i} = \frac{exp(o_j)}{\sum_{k}\exp(o_k)}\)
损失函数:真实值和预测值均为概率模型,损失函数衡量两个概率模型之间的差异——交叉熵损失\(l(y,\hat{y}) = -\sum_{j=1}^{q}y_{j}log\hat{y_j}\)
——————————————————————————————————————
信息量、熵、KL散度、交叉熵:这部分参考了b站up主王木头学科学https://space.bilibili.com/504715181
1、信息量

可以看出信息量公式需要满足:
(1)概率相乘变相加——需要log运算
(2)发生概率越小,获得信息量越大——加负号
(3)以几为底?无所谓,先以2为底(这样单位是比特)。如果以e为底,单位是纳特
即:
\(f(x) = -log_{2}x\)
信息量:一件事从不确定变为确定的难度。原来的概率x越小,难度越大,包含的信息量f(x)就越大
2、熵
熵:一个系统(中的所有事件)从不确定变为确定的难度。即所有事件信息量的加权和(期望),权重即为本事件发生的概率
因此,熵可以衡量一个系统(一个概率模型)的混乱程度=不确定程度
概率模型P的熵:\(H(P) = \sum_{i=1}^{m}p_{i}f(p_{i}) = \sum_{i=1}^{m}p_{i}(-log_{2}p_i)\)
3、KL散度、交叉熵
KL散度衡量两个概率模型之间的差距。比如\(D_{KL}(P\|{Q})\)以概率模型P为基准(谁写在前,以谁为基准),衡量概率模型P和Q之间的差距
\(D_{KL}(P\|{Q}) = \sum_{i=1}^{m}p_{i}(f_{Q}(q_i)-f_{P}(p_i)) = \sum_{i=1}^{m}p_{i}(-logq_i) - \sum_{i=1}^{m}p_{i}(-logp_i)\)
计算内容为 事件在Q中的信息量与在P中的信息量的差值的加权和(期望),权重为对应事件在P中的概率。最后得到,以概率模型P为基准时,概率模型P和Q之间的KL散度=交叉熵-在P系统的熵
4、把交叉熵作为损失函数的合理性
吉布斯不等式证明了KL散度一定≥0。当P和Q一样时,为0;不一样时,大于0
让模型P(真实模型)和模型Q(预测模型)尽可能接近 —— 让\(D_{KL}(P\|{Q})\)尽可能小(KL散度=交叉熵-在P系统的熵,而后一项已知不变)—— 让交叉熵尽可能小
因此,交叉熵可作为损失函数,衡量两个概率模型的接近程度。最小化交叉熵,可使真实模型和预测模型尽可能接近
5、任何指数族分布模型均满足:交叉熵的梯度 = 预测概率 - 真实概率
一方面,我们可以看到,梯度为0,交叉熵取最小值时,确实让预测概率和真实概率尽可能接近(相等)
另一方面,这个性质使得求解梯度变得简单
——————————————————————————————————————
代码实现:

import torch
from torch import nn
from d2l import torch as d2l

class Accumulator:  #@save
    """在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)]

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

    def __getitem__(self, idx):
        return self.data[idx]

class Animator:  #@save
    """在动画中绘制数据"""
    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, ]
        # 使用lambda函数捕获参数
        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 accuracy(y_hat, y):  #@save
    """计算预测正确的数量"""
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: # 检查 y_hat 是否是一个二维张量,即有多个类的概率分布
        y_hat = y_hat.argmax(axis=1) # 找到每个样本预测概率最大的类别索引,y_hat 将变成一个一维张量,表示每个样本的预测类别
    cmp = y_hat.type(y.dtype) == y # y_hat和y相同位置元素比较,若一样,在cmp对应位置的元素为True,否则为False
    return float(cmp.type(y.dtype).sum())

def evaluate_accuracy(net, data_iter):  #@save
    """计算在指定数据集上模型的精度"""
    if isinstance(net, torch.nn.Module):
        net.eval()  # 将模型设置为评估模式,只做前向传递,不做梯度(反向传播)
    metric = Accumulator(2)  # 正确预测数、预测总数
    with torch.no_grad():
        for X, y in data_iter: # 累加器用于累加一个epoch的结果
            metric.add(accuracy(net(X), y), y.numel()) # 分类正确的样本数、总样本数;在下一句两者相除,得到精度
    return metric[0] / metric[1]

def train_epoch_ch3(net, train_iter, loss, updater):  #@save
    """训练模型一个迭代周期"""
    if isinstance(net, torch.nn.Module):
        net.train() # 将模型设置为训练模式,要计算梯度
    # 训练损失总和、训练准确度总和、样本数;因此用长度为3的迭代器累计信息
    metric = Accumulator(3)
    for X, y in train_iter:
        # 计算梯度并更新参数
        y_hat = net(X)
        l = loss(y_hat, y)
        if isinstance(updater, torch.optim.Optimizer):
            # 如果使用PyTorch内置的优化器和损失函数
            updater.zero_grad()
            l.mean().backward()
            updater.step()
        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]

def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):  #@save
    """训练模型(定义见第3章)"""
    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):
        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 <= 1 and train_acc > 0.7, train_acc
    assert test_acc <= 1 and test_acc > 0.7, test_acc

def predict_ch3(net, test_iter, n=6):  #@save
    """预测标签(定义见第3章)"""
    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])

# 读取数据
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) 

# 定义模型
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10)) 
# nn.Flatten用于将多维张量展平为二维张量(即,保持批量维度不变,将其余维度展平为一个维度)。常用于在卷积层和全连接层之间的转换

#初始化权重
def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)
net.apply(init_weights);

# 在交叉熵损失函数中传递未规范化的预测,并同时计算softmax及其对数,这样做可以防止溢出
loss = nn.CrossEntropyLoss(reduction='none') 
# nn.CrossEntropyLoss的reduction参数:1-默认为mean,对n样本的loss求均值;2-sum,对n样本的loss求和;3-none,直接返回n样本的loss
# 如果这里不使用默认的mean,则在d2l.train_ch3的设置里求梯度之前,要先求loss的mean,即l.mean().backward()

#优化算法
trainer = torch.optim.SGD(net.parameters(), lr=0.1)

#训练
num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

#预测
predict_ch3(net, test_iter)

标签:loss,self,batch,train,softmax,P8,net,回归,size
From: https://www.cnblogs.com/xjl-ultrasound/p/18345729

相关文章

  • Java学习回归
    先从第一个代码开始!点击查看代码//创建一个类,类的名字叫做HelloWorldpublicclassHelloWorld{//这是java公认的程序入口方法publicstaticvoidmain(String[]args){//调用系统的输出功能,将内容输出在控制台中System.out.println("你好......
  • 回归预测|一种多输入多输出的粒子群优化支持向量机数据回归预测Matlab程序PSO-MSVR非f
    回归预测|一种多输入多输出的粒子群优化支持向量机数据回归预测Matlab程序PSO-MSVR非for循环实现原理上进行修改多输出文章目录前言回归预测|一种多输入多输出的粒子群优化支持向量机数据回归预测Matlab程序PSO-MSVR非for循环实现原理上进行修改多输出一、PSO-MSVR......
  • P8026 [ONTAK2015] Bajtocja & 杭电多校2 L.图计算
    题目传送门1题目传送门2题意洛谷那题比较简明,就是多张图(\(d\leq200\)),每次给某张图加一条边,询问加完后有多少点对在所有图都联通。题解翻了很多题解都是用的hash做法,具体而言就是如果两个点在某张图联通,那么他们在该图并查集有相同根节点。将每个点在所有图的根节点构成一......
  • P8819题解
    题目大意现在有个有向图图,共有n个点,m条边总共有四种操作:操作1:将一条边打上标记操作2:将一个点出发的所有边打上标记操作3:将一条边移除标记操作4:将一个点出发的所有边移除标记打上标记的边视为被移除每次操作进行一次询问,如果每个点出度都是1,整张图是个强连通图,那么输出"YES......
  • 机器学习算法之一 线性回归
    1.线性预测函数定义左侧为真实值,右侧为预测值与误差的和,其中为权重矩阵。2.目标函数的推导2.1高斯分布函数误差符合独立同分布假设,服从均值为0的高斯分布:将线性函数带入,得:......
  • AP8854 输入10-120V 外置MOS 10A 降压恒压型 DC-DC电源管理芯片 控制器方案
    产品描述AP8854一款宽电压范围降压型DC-DC电源管理芯片,内部集成使能开关控制、基准电源、误差放大器、过热保护、限流保护、短路保护等功能,非常适合宽电压输入降压使用。AP8854带使能控制,可以大大节省外围器件,更加适合电池场合使用,具有很高的方案性价比。特点◆电压......
  • 基于WOA优化的CNN-GRU的时间序列回归预测matlab仿真
    1.算法运行效果图预览(完整程序运行后无水印)   2.算法运行软件版本matlab2022a 3.部分核心程序(完整版代码包含详细中文注释和操作步骤视频) %调整参数c1=2-t*((1)/300);c2=-1+t*((-1)/300);%位置更新fori=1:Numr1......
  • 1.12 - 动手搓KNN近邻-分类和回归
    1.通过sklearn调用机器学习api处理问题通用流程 #-*-coding:utf-8-*-importtimeimportjoblibimportnumpyasnpimportpandasaspdfromcollectionsimportCounterfromsklearn.model_selectionimporttrain_test_splitfromsklearn.preprocessingimportL......
  • ESP8266串口
    Serial.print(val)–串口输出数据并。Serial.println(val)–串口输出数据并换行。Serial.available()–判断串口缓冲区的状态,返回从串口缓冲区读取的字节数。Serial.read()–读取串口数据,一次读一个字符,读完后删除已读数据。当没有可读数据时返回-1,整数类型。Serial.readB......
  • 洛谷P8869 莲子的软件工程学之警钟长鸣
    洛谷P8869题解传送锚点摸鱼环节[传智杯#5初赛]A-莲子的软件工程学题目背景在宇宙射线的轰击下,莲子电脑里的一些她自己预定义的函数被损坏了。对于一名理科生来说,各种软件在学习和研究中是非常重要的。为了尽快恢复她电脑上的软件的正常使用,她需要尽快地重新编写这么一......