学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)

总结

以_结尾的方法,好像是原位替换(即 原地修改,就地修改变量)如 fill_()

感恩的心:(沐神的直播环境)
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第1张图片

08-线性回归+基础优化算法

引言(如何在美国买房)

根据现在行情预测房价
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第2张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第3张图片

线性回归(简化模型)、线性模型、神经网络

b为偏差
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第4张图片
扩展到一般化
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第5张图片

线性模型
每个箭头代表一个权重
当层单层神经网络原因:不看输出层,将权重层和input放一起 带权重的层只有一层
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第6张图片

【书中】
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第7张图片

衡量预估质量

1/2是为了求导时把2消去
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第8张图片

线性回归(求最优解)

一般的模型都没有显示解,因为有显示解的模型都过于简单(显示解:个人感觉就是 能直接用数学方法手动解出来的解)
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第9张图片

训练数据

当每个xi是一个列向量样本时, X即一个大矩阵,该矩阵每一行对应一个样本
y是一个列向量,每个yi是一个实数数值,对应预测的房价
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第10张图片

参数学习、显示解

损失函数l:模型在每一个数据上的损失 求均值就得到损失函数
目标是求 最小的损失函数

这里X是一个矩阵,x_{i}的意思是第i条数据,每一条数据都包含了(房间个数,居住面积)等决策信息
argmin是指让后面公式取得最小值的参数大小, min是指公式本身的最小值.
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第11张图片

因为线性模型,因此有显示解。下面求:

为了写起来方便,将偏差b也写进来,
在这里插入图片描述
此时为了能进行矩阵计算X·W,为 矩阵X加入一列全一的特征(增广矩阵?)

最优解是 导数为0的,即
在这里插入图片描述
此处是唯一一个有最优解的模型,以后学的都不会有了
具体计算过程:

优化算法(初步体验)

后面有专门的章节将优化方法

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第12张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第13张图片

梯度下降法(含超参数:学习率)

【实际场景】很少直接使用梯度下降,一般都用小批量随机梯度下降
当一个模型没有显示解时,我们会:

下图画的是等高线
梯度是使其增加最快的方向,负梯度就是下降最快的方向

学习率:此处的yita,是超参数,是需要人为指定的(超参数不需要数据来驱动,而是在训练前或者训练中人为的进行调整的参数)
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第14张图片
以w0到w1为例:yita相当于每一步走多远(即w0到w1),偏导数(即梯度)相当于 确定向哪个方向走(沿负梯度方向就是沿下降最快的方向)
上图中橙框乘红框就相当于 w0到w1、w1到w2的每段向量, 则上述等式相当于 起点+ yita乘梯度(即 要运动的向量)=最终的向量
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第15张图片
选太小:每次走的步长有限,需要计算很多次梯度,那么 时间和空间成本都太大
后续有教程教选学习率

【常用】小批量随机梯度下降法(Stochastic Gradient Descent)(含超参数:批量大小batch)

每次走一步都计算所有的梯度成本太大。

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第16张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第17张图片

【补充】吴恩达反向传播

前向传播

缓存cache的意思,就是在正向传播的时候保存对应Z, 应用到计算反向传播中
g为激活函数

则对于输入一个样本时,输入即为 a[0];输入为一整个训练集时,输入即为A[0]

前向传播即下图中绿框,从左到右不断计算
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第18张图片

反向传播

d即为偏导

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第19张图片

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第20张图片

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第21张图片

代码实现

线性回归(从0实现,不用框架)

从零实现整个方法,包括 数据流水线、模型、损失函数、小批量随机梯度下降优化器

生成数据集

标准差:standard deviation
torch.normal() 返回一个张量,包含从给定参数means,std的离散正态分布中抽取随机数。 均值means是一个张量,包含每个输出元素相关的正态分布的均值
torch.matmul:两个张量矩阵相乘
reshape中-1表示自动计算,1表示固定,即列向量为1

弹幕说:
这个函数返回的特征与标签,相当于分别把真实的房屋‘关键因素’和对应的‘房价’列出来。这一页的信息,相当于去市场调研收集真实的房屋数据。
features中每一行都包含一个二维数据样本,labels中的每一行都包含一维标签值(一个标签)。
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第22张图片

绘制数据集

此处打印看一下 第0个样本的样子 以及 可视化一下所有样本

使用.detach()方法可以将一个张量从当前的计算图中分离出来,使其变成一个不再需要梯度追踪的普通张量,即使它是由需要梯度的操作创建的

从图中也看出 横纵轴变量是有线性相关性的
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第23张图片

读取小批量

定义一个data_iter函数,接收批量大小、特征矩阵、标签向量作为输入,生成大小为batch_size的小批量

yield就是 return 返回一个值,并且记住这个返回的位置,下次迭代就从这个位置后开始。

%matplotlib inline
import random
import torch
from d2l import torch as d2l

def synthetic_data(w,b,num_exaples):
    """生成 y = Xw + b + 噪声"""
    X = torch.normal(0,1,(num_exaples,len(w)))
    y = torch.matmul(X,w) + b
    y += torch.normal(0,0.01,y.shape)
    return X, y.reshape((-1,1))

true_w = torch.tensor([2,-3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
print('features:',features[0],'\nlabel:',labels[0])

d2l.set_figsize()
d2l.plt.scatter(features[:,(1)].detach().numpy(),labels.detach().numpy(),1)   

# data_iter:把所有样本分成很多个batch,每个batch中的样本数量为batch_size(函数接收批量大小、特征矩阵和标签向量作为输入,生成大小为batch_size的小批量)
def data_iter(batch_size,features,labels):
    num_examples = len(features)  # 样本个数
    indices = list(range(num_examples)) # 样本索引(从0- num_examples-1)
    # 这些样本是随即读取的,没有特定的顺序
    random.shuffle(indices) # 把索引随机打乱
    for i in range(0, num_examples, batch_size): # 遍历所有样本,每次步长为batch_size
        # 每batch_size个样本为一组,拿到该组样本的索引。注:当i+batch_size超出时,取num_examples         
        # batch_indices = torch.tensor(indices[i:min(i+batch_size,num_examples)]) 
        batch_indices = indices[i:min(i+batch_size,num_examples)] # 这么写也行,只是为了拿到索引嘛, 索引用不用tensor感觉均可
        yield features[batch_indices], labels[batch_indices] # 基于样本索引获得对应的特征,及对应的标签
        
batch_size = 10
for X,y in data_iter(batch_size, features, labels):
    print(X, '\n', y) # 取一个批次后,就break跳出了
    break

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第24张图片

定义模型、损失函数、优化算法

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第25张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第26张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第27张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第28张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第29张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第30张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第31张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第32张图片

说明:
1.关于 l.sum().backward():
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第33张图片

%matplotlib inline
import random
import torch
from d2l import torch as d2l

def synthetic_data(w,b,num_exaples):
    """生成 y = Xw + b + 噪声"""
    X = torch.normal(0,1,(num_exaples,len(w)))
    y = torch.matmul(X,w) + b
    y += torch.normal(0,0.01,y.shape)
    return X, y.reshape((-1,1))

true_w = torch.tensor([2,-3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
print('features:',features[0],'\nlabel:',labels[0])

d2l.set_figsize()
d2l.plt.scatter(features[:,(1)].detach().numpy(),labels.detach().numpy(),1)   

def data_iter(batch_size,features,labels):
    num_examples = len(features)  # 样本个数
    indices = list(range(num_examples)) # 样本索引
    # 这些样本是随即读取的,没有特定的顺序
    random.shuffle(indices) # 把索引随即打乱
    for i in range(0, num_examples, batch_size):
        batch_indices = torch.tensor(indices[i:min(i+batch_size,num_examples)]) # 当i+batch_size超出时,取num_examples         
        yield features[batch_indices], labels[batch_indices] # 获得随即顺序的特征,及对应的标签
        
batch_size = 10
for X,y in data_iter(batch_size, features, labels):
    print(X, '\n', y) # 取一个批次后,就break跳出了
    break

# 定义初始化模型参数
w = torch.normal(0,0.01,size=(2,1),requires_grad=True)  # 初始化w:w是长为2的向量,均值为0,标准差为0.01
b = torch.zeros(1,requires_grad=True)

# 定义模型
def linreg(X,w,b):
    """线性回归模型"""
    return torch.matmul(X,w)+b

# 定义损失函数
def squared_loss(y_hat,y):
    """均方损失"""
    return (y_hat - y.reshape(y_hat.shape))**2/2 # 将y统一成与y_hat一样的尺寸   

# 定义优化算法
def sgd(params,lr,batch_size):   # params:参数list,如[w、b]
    """小批量随即梯度下降"""
    with torch.no_grad(): # 设置不更新梯度
        for param in params: # 每个参数进行遍历
            # 每个参数进行更新:因为之前的损失函数没有求均值,所以这里除以 batch_size 求了均值。由于乘法的线性关系,这里除以放在loss的除以是等价的。 
            param -= lr * param.grad / batch_size                
            param.grad.zero_() # 每个参数的梯度清零(以避免影响下次梯度计算)

# 训练过程
lr = 0.03
num_epochs = 3
net = linreg # 这里用线性模型,这样写是很方便net赋予其他模型,只需要改一处,不需要下面所有网络模型名称都改
loss = squared_loss

# 训练过程(都是套路,大同小异,两次for loop)
for epoch in range(num_epochs):  # 第一层for:对数据扫一遍
    for X,y in data_iter(batch_size,features,labels): # 第二层for:每次拿出batch_size大小的一个batch
        l = loss(net(X,w,b),y) # x和y的小批量损失: 输入参数给模型net得到预测值;用预测值和真实值y做损失。此时损失l就是长度为batch_size的向量
        # 因为l是形状是(batch_size,1),而不是一个标量。l中所有元素被加到一起,并以此计算关于[w,b]的梯度
        l.sum().backward() # 求和算梯度不懂什么意思的可以回看上一节自动求导,求和后算梯度是求每一个y对每一个x的梯度,不求和直接算梯度结果是一个矩阵!不是我们要的结果
        sgd([w,b],lr,batch_size) #使用参数的梯度更新参数(此处可能存在细节问题,但只是为了说明整体上的训练过程,因为 最后一个batch可能样本数量不足batch_size个)
    # 扫完一个epoch后,评价一下(这部分无需更新梯度)
    with torch.no_grad():
        train_l = loss(net(features,w,b),labels)
        print(f'epoch{epoch+1},loss{float(train_l.mean()):f}')   

# 比较真实参数和通过训练学到的参数来评估训练的成功程度
print(f'w的估计误差:{true_w-w.reshape(true_w.shape)}')
print(f'b的估计误差:{true_b-b}')

打印结果:
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第34张图片

体验lr太大或太小时的效果:(注意 改变lr时要 重新初始化一下w和b)
当lr很小时
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第35张图片
此时可以考虑增加epoch,但loss还不是特别小
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第36张图片
lr太大时,loss为nan,因为可能 求导时会除以0,超出傅里叶的计算范围了
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第37张图片

线性回归(使用框架)

python中的* (拆包、解包):在Python中,号表示解包操作,它可以将一个包含多个元素的元组、列表、集合等数据结构解压为多个独立的元素。 表示接受任意多个参数并将其放在一个元组中
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第38张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第39张图片
下图中 左侧为本节图片,等价于右图(即上节手动实现的方式)
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第40张图片

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第41张图片
在这里插入图片描述
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第42张图片

import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
from torch import nn    
# 0.基本数据准备    
true_w = torch.tensor([2,-3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w,true_b,1000) # 库函数生成人工数据集    

# 1.【与不用框架实现的区别之一】调用框架现有的API来读取数据(把已有的训练数据(features,labels)传入TensorDataset)
def load_array(data_arrays,batch_size,is_train=True):
    """构造一个Pytorch数据迭代器"""
    dataset = data.TensorDataset(*data_arrays) # dataset相当于Pytorch的Dataset。一个星号*,表示对list解开入参(是python中的拆包符号)。      
    return data.DataLoader(dataset,batch_size,shuffle=is_train) # 返回的是从dataset中随机挑选出batch_size个样本出来     

batch_size = 10
data_iter = load_array((features,labels),batch_size) # 返回的数据的迭代器
print(next(iter(data_iter))) # iter(data_iter) 是一个迭代器对象,next是取迭代器里面的元素  

# 2.使用框架的预定义好的层
# nn是神经网络的缩写
net = nn.Sequential(nn.Linear(2,1))

# 3.初始化模型参数
# 以单下划线_结尾的方法都是原位替换
net[0].weight.data.normal_(0,0.01) # 使用正态分布替换掉weight变量里面的数据值。 其中net[0]就是 神经网络net的第一层(即 nn.Linear(2,1))。
net[0].bias.data.fill_(0)  # 偏差bias变量里面的值设置为0。normal_ :以单下划线_结尾的方法都是原位替换
print(net[0])

# 4.计算均方误差使用的是MSELoss类,也称为平方L2范数
loss = nn.MSELoss()  #L1是算术差,L2是平方差

# 4.实例化SGD实例
trainer = torch.optim.SGD(net.parameters(),lr=0.03)  # 传入网络中所有的参数(此处指w和b)

# 5.训练过程代码与从零开始时所做的非常相似
num_epochs = 3
for epoch in range(num_epochs):
    for X, y in data_iter:  # 从DataLoader里面一次一次把所有数据拿出来
#         print("X:",X)
#         print("y:",y)
        l = loss(net(X),y) # net(X) 为计算出来的线性回归的预测值
        trainer.zero_grad() # 梯度清零(避免影响其他次计算)
        l.backward()  # pytorch已经帮我们做了 向量.sum()  (即转成了标量),无需手动写了(如上节的l.sum().backward())
        trainer.step()  # SGD优化器优化模型:调用step()进行模型更新
    l = loss(net(features),labels)  # 当前epoch的loss
    print(f'epoch{epoch+1},loss{l:f}')   # 这行都是l 而不是1

打印结果:
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第43张图片

QA

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第44张图片

1.可以,直接 上面pip就行

2.后面会讲一下二者区别,但是区别不大。
绝对差值在0点时可能不能求导。

3.也可以不求。求不求本质上没区别,不求平均 梯度数值会比较大。如果损失不除以n,那么学习率 除以n也行, 但是 只不过除了之后 更好调(除n主要就是用来忽略样本规模的)

  1. 是的

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第45张图片

6.除以谁都没关系,不影响 本质, 最后求的都是min Loss

7.经验。后续也会讲

8.其实batch_size越小越好,对收敛越好但是大了不行。
采样样本越小,噪音越大,但是噪音对于神经网络是好事情, 更利用训练出 泛化性强的网络

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第46张图片

9.batch_size只要不是特别特别大都行

10.是的。 梯度是线性的, 和的均值等于均值的和

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第47张图片

11.不是。批量大小是一样的即batch_size,随机只每次在样本中 随机采集batch_size大小个元素

12.需要。 但是一般损失函数和正则会分开, 而且正则有很多方法。

13.首先二阶导数不一定能计算,很难算。 有时一阶导是向量,二阶导就变成了矩阵
为什么不用牛顿法:
【二阶导不一定能计算,或者方便计算】此处有两个问题,一个是统计模型(损失函数长什么样),一个是优化模型(用什么算法求解),但其实这俩都是错的, 因为 统计模型是错的,优化模型肯定也求不对, 应该是指我们很难找到 完全正确、十分精准的损失函数,一般机器学习都求不到最优解(面对实际的问题我们不可能拿到精确的模型,真实的损失函数是非常复杂的)。
因此求出一个统计模型的最优解的意义并不大, 因为它是一个错误的模型,因此收敛快不快并不care,我们关心损失函数收敛到那个地方,用牛顿法虽然快,但 求的结果可能并不平坦。 不一定 很快得到的结果就是好的结果(可能泛化性差)
【牛顿法求出的结果不一定比随机梯度求得的结果好,收敛不一定快, 即便收敛快、结果也不一定好】
弹幕说:收敛更准确比更快更重要

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第48张图片

14.如果损失函数L中没除以n,那么 学习率yita除以n
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第49张图片

15.从计算图分离:要转换成numpy数据类型,就需要先将其从求梯度的计算图中分离出来(但是具体也看pytorch版本)
16.detach就是pytorch中的
17.是的,如果数据量特别大会爆掉。 但是整本书中dataset都不大, 如果真实情况下 GPU足够大,load全部也没问题。
实际场景下,数据都存储在硬盘上的

18.弹幕说也可以
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第50张图片

19.相当于洗牌之后再把所有的牌抽起来看一遍
20.不用return时,每次需要数据时run一遍即可,现用现生成,节省内存;
用iter就是python的写法,python的习惯

21、22.三种做法:(代码中只是刚好整除了)
(1)【常用】代码中:每次取的范围是: [ i , min(i+batch_size,num_examples)]
(2)丢掉最后不足的样本
(3)从下一个epoch中补一些过来 使最后一个batch也满足 batch_size个样本数

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第51张图片
24.lr不做衰减也问题不大,此处先不讲这个
24-2.
(1)可以判断相邻两次epoch的loss的变化范围不大(如1%时)
(2)用 交叉验证集,当其精度不增加时
(3)算力允许的情况下,epoch多点也没关系

25.是的。除了线性回归模型外,都没有显示解。能求出显示解的模型都太简单了

NPC(np-complete)问题即NP完全问题, 任何一个NP问题都可以在多项式时间内归约为某个NP问题,那么这个问题就成为NPC问题
【NP问题】
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第52张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第53张图片

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第54张图片

26.初始时也可以用同样的值,偷懒就用了随机的
27.求导时会涉及除法,后面也会讲数据稳定性问题
28.不一定, 只是此处手动设了,后续就不会了
29.是的
30.就是为了print,同时我们只run了forward,没run backward,即没计算梯度,无需清0
31.因为pytorch不自动帮你清零,如果 不人为清零,则 后面梯度计算时会在前面的梯度计算的结果上 累加
在这里插入图片描述
32.后面会单独讲

09-softmax回归+损失函数+图片分类数据集

Softmax回归(具体细节也可看吴恩达)

【softmax算子(操作子)的作用】将所有的输入都 拉到[0,1]区间内,且令这些输入 经由softmax的输出 之和为1(刚好符合概率的感觉)
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第55张图片

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第56张图片

回归vs分类

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第57张图片
下图补充:
回归:
输出的区间是 单个自然区间

分类:
多输出,输出的个数等于类别的个数
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第58张图片

【分类问题举例】Kaggle上的分类问题

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第59张图片

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第60张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第61张图片

从回归到多类分类

如何从回归问题过渡到分类问题:

【编码和初步计算】均方损失

1.编码
如果有n个类别,则 构造一个 长度为n的向量y。(one-hot编码)
如果该东西真实类别是i,则yi=1,其他yi=0

下图中yhat 是 使得oi取最大值对应的那个类别标号i。
oi:中o为置信度,o是神经网络的输出
y是真实值,yhat是预测值
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第62张图片

【分析】无校验比例

【我们关心的问题1】对于分类问题,我们不关心 物体实际的值,而是 关心 分类正确物体的置信度是不是特别大

下图中yhat 是 使得oi取最大值对应的那个类别标号i
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第63张图片
我们目的是 要使得对正确类别y的置信度 oy能远远大于 其他类别的置信度oi, 即 二者的差大于等于一个阈值(如 derta)
这样能保证我们的模型能够将 物体真正的类别和其他类别拉开距离

【softmax定义】【基于指数运算进行映射】校验比例(看书效果更好)

【我们关心的问题2】
同时接问题1,我们还希望 分类的预测输出是一个概率,暂时现在我们的输出是一个向量( o1,…,on)
此时我们引入一个操作子 softmax,作用于o,得到yhat,它是一个长度为n的向量(y1hat,…,ynhat),该向量的每个元素yi都非负、且这些元素之和为1(每个元素就是 该物体被预测为当前索引类别的概率)
其中yhati
【个人总结softmax作用】【使用exp(即指数)、exp(oi)/xigema exp(ok) 的目的】
(1)通俗版本:将所有的输入都 拉到[0,1]区间内,且令这些输入 经由softmax的输出 之和为1(刚好符合概率的感觉)
(2)将 每个元素都映射到非负区间上(这才符合概率)且 最后的yhat i更符合概率的特点, 值属于[0,1]且 之和为1
(因为真实的向量y也是 所有元素之和为1,即只有一个1, 也可以将其视为概率)

【书上soffmax作用】softmax函数能够将未规范化的预测变换为⾮负数(范围在 0-1)并且总和为1,同时让模型保持可导的性质
【为什么不能直接将oi作为输出】
我们能否将未规范化的预测o直接视作我们感兴趣的输出呢?答案是否定的。因为将线性层的输出直接
视为概率时存在⼀些问题:⼀⽅⾯,我们没有限制这些输出数字的总和为1。另⼀⽅⾯,根据输⼊的不同,它
们可以为负值。
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第64张图片
【o与y的关系(这也就是softmax的 主要计算原理)】
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第65张图片

下图中的softmax的输入O是一个(行)向量
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第66张图片
下图中softmax的输入是一个矩阵(可见softmax从0实现一节)
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第67张图片

Softmax和交叉熵(Cross Entropy)损失

p和q是两个概率,H表示交叉熵,i为第i个元素,共n个元素
向量y(即加粗的黑体y)是真实值的向量(共n个元素, 只有一个元素为1,其余为0),向量yhat是预测得到的向量,
标量y是 物体真实类别,标量yhati是物体被预测为 i类别的概率

解释下图第一个公式的推导(如绿框):由于y是⼀个⻓度为n的独热编码向量,所以除了⼀个项以外的所有项j都消失了。
yhaty:对真实类别y的预测的概率 为yhat。 即 对于分类问题,我们只关心对正确类别的预测值有多大,而不关心对于非正确类别的预测情况
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第68张图片

上图中第三个公式的推导
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第69张图片

损失函数

【作用】用于衡量真实值和预测值之间的区别
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第70张图片
下面:
y:真实值
y’:预测值
除以2:求导后 2和1/2抵消

L2 loss

横轴是y’
在这里插入图片描述

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第71张图片
④ 【变化规律】当预测值y’跟真实值y隔的比较远的时候,(真实值y为0,预测值就是下面的曲线里的x轴),梯度比较大,所以参数更新比较多。

⑤ 随着预测值靠近真实值是,梯度越来越小,参数的更新越来越小。
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第72张图片
下图中的更新指参数权重的更新
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第73张图片

L1 loss

L2也不一定很好,因为 当y‘离原点很远时(此例中取真实值y=0),也不一定希望更新的很快,因此看下L1
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第74张图片
① 相对L2 loss,L1 loss的梯度就是距离原点时,梯度也不是特别大,权重的更新也不是特别大。会带来很多稳定性的好处。

② 他的缺点是在零点处不可导,并在零点处左右有±1的变化,这个不平滑性导致预测值与真实值靠的比较近的时候,优化到末期的时候,可能会不那么稳定。
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第75张图片

Huber’s Robust Loss(鲁棒损失)

Huber’s Robust Loss 结合了L1和L2 loss的优点:
当预测值和真实值的差距不同时,Loss计算方式不同:即 预测值和真实值差的比较大时,梯度通过比较均匀的力度调整参数;预测值和真实值差的比较小时(即优化末期),梯度绝对值越来越小,以保证 整个参数调整过程是比较平滑的
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第76张图片

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第77张图片

图像分类数据集(Fashion-MNIST)

0.概述和课件

此处是先初步介绍一下后面要常用到的一个数据集Fashion-MNIST

① MINIST数据集是图像分类中广泛使用的数据集之一,但作为基准数据集过于简单。
② 下面将使用类似但更复杂的Fashion-MNIST数据集。

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第78张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第79张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第80张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第81张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第82张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第83张图片

【代码】

1.导入包

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第84张图片

2.准备数据

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第85张图片

3.单进程读取batch并可视化的函数

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第86张图片

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第87张图片

4.多进程读取一个小batch,大小为batch_size

一般要保证读取数据的速度大于训练的速度

%matplotlib inline
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l

d2l.use_svg_display()

# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式
# 并除以255使得所有像素的数值均在0到1之间
trans = transforms.ToTensor()
mnist_train = torchvision.datasets.FashionMNIST(root="01_data/01_DataSet_FashionMNIST",train=True,transform=trans,download=True)
mnist_test = torchvision.datasets.FashionMNIST(root="01_data/01_DataSet_FashionMNIST",train=False,transform=trans,download=True)            

def get_fashion_mnist_labels(labels):
    """返回Fashion-MNIST数据集的文本标签"""
    text_labels = ['t-shirt','trouser','pullover','dress','coat',
                   'sandal','shirt','sneaker','bag','ankle boot']
    return [text_labels[int(i)] for i in labels]

def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):
    """Plot a list of images."""
    figsize = (num_cols * scale, num_rows * scale) # 传进来的图像尺寸,scale 为放缩比例因子
    _, axes = d2l.plt.subplots(num_rows,num_cols,figsize=figsize)
    print(_)
    print(axes) # axes 为构建的两行九列的画布
    axes = axes.flatten()
    print(axes) # axes 变成一维数据
    for i,(ax,img) in enumerate(zip(axes,imgs)):
        if torch.is_tensor(img):
            # 图片张量
            ax.imshow(img.numpy())
            ax.set_title(titles[i])
        else:
            # PIL图片
            ax.imshow(img)
            
X, y = next(iter(data.DataLoader(mnist_train,batch_size=18))) # X,y 为仅抽取一次的18个样本的图片、以及对应的标签值
show_images(X.reshape(18,28,28),2,9,titles=get_fashion_mnist_labels(y))

batch_size = 256
def get_dataloader_workers(): # 一般数据都在硬盘上,不能一次读取完,此时使用4个进程来读
    """使用4个进程来读取的数据"""
    return 4

train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True,
                            num_workers=get_dataloader_workers())

timer = d2l.Timer() # 计时器对象实例化,开始计时
for X,y in train_iter:  # 遍历一个batch_size数据的时间
    continue
f'{timer.stop():.2f}sec' # 计时器停止时,停止与开始的时间间隔事件

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第88张图片

5.【完整代码】整合-上述内容到一个函数中(load_data_fashion_mnist)

本节整合代码并实现函数load_data_fashion_mnist 以供后面使用 Fashion-MNIST数据集
额外声明resize参数,以便后续 如果模型需要不同size的图片时可以 基于此调整
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第89张图片

%matplotlib inline
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l

d2l.use_svg_display()

# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式
# 并除以255使得所有像素的数值均在0到1之间
trans = transforms.ToTensor()
mnist_train = torchvision.datasets.FashionMNIST(root="01_data/01_DataSet_FashionMNIST",train=True,transform=trans,download=True)
mnist_test = torchvision.datasets.FashionMNIST(root="01_data/01_DataSet_FashionMNIST",train=False,transform=trans,download=True)            

def get_fashion_mnist_labels(labels):
    """返回Fashion-MNIST数据集的文本标签"""
    text_labels = ['t-shirt','trouser','pullover','dress','coat',
                   'sandal','shirt','sneaker','bag','ankle boot']
    return [text_labels[int(i)] for i in labels]

def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):
    """Plot a list of images."""
    figsize = (num_cols * scale, num_rows * scale) # 传进来的图像尺寸,scale 为放缩比例因子
    _, axes = d2l.plt.subplots(num_rows,num_cols,figsize=figsize)
    print(_)
    print(axes) # axes 为构建的两行九列的画布
    axes = axes.flatten()
    print(axes) # axes 变成一维数据
    for i,(ax,img) in enumerate(zip(axes,imgs)):
        if torch.is_tensor(img):
            # 图片张量
            ax.imshow(img.numpy())
            ax.set_title(titles[i])
        else:
            # PIL图片
            ax.imshow(img)
            
X, y = next(iter(data.DataLoader(mnist_train,batch_size=18))) # X,y 为仅抽取一次的18个样本的图片、以及对应的标签值
show_images(X.reshape(18,28,28),2,9,titles=get_fashion_mnist_labels(y))

batch_size = 256
def get_dataloader_workers():
    """使用4个进程来读取的数据"""
    return 4

train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True,
                            num_workers=get_dataloader_workers())

timer = d2l.Timer()
for X,y in train_iter:
    continue
f'{timer.stop():.2f}sec'  # 扫一边数据集的事件

def load_data_fashion_mnist(batch_size, resize=None): # 额外声明resize参数,以便后续 如果模型需要不同size的图片时可以 基于此调整
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0,transforms.Resize(resize)) # 如果有Resize参数传进来,就进行resize操作
    trans = transforms.Compose(trans)
    mnist_train = torchvision.datasets.FashionMNIST(root="01_data/01_DataSet_FashionMNIST",train=True,transform=trans,download=True)
    mnist_test = torchvision.datasets.FashionMNIST(root="01_data/01_DataSet_FashionMNIST",train=False,transform=trans,download=True)            
    return (data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_workers()),
           data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_workers()))               

Softmax-从零实现

1.训练集、测试集抽取

基于上节实现的函数读取数据集的数据
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第90张图片

2.初始化参数w、b

因为对于softmax回归来说,input需要是一个向量,因此 784=28*28,将图片展平(但这样会丢失图片的空间信息,后续用卷积神经网络来实现)

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第91张图片
size是关键:个人理解
X是的shape是(1,784),为了XW能计算,且输出为10类,则 W.shape为(784,10)
在这里插入图片描述

3.定义Softmax

回顾一下:矩阵可以多不同axis上求和
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第92张图片
正文:
此处softmax的对象是一个矩阵,对矩阵做softmax就相当于对其每一行做softmax

分别计算公式中的分子和分母
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第93张图片

验证一下上面写的是否正确:符合softmax的预期:每个元素都为非负且 在0,1之间,每一行所有元素之和也为1
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第94张图片
W.shape为(784,10) (第一步就分析了)
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第95张图片

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第96张图片

4.交叉熵损失

先补充个细节:如何 从所有预测值中根据 标号(即类别label)拿到其对应的预测值(即 最终预测的概率)
(弹幕说)此处内容涉及python的高级索引
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第97张图片
最终拿到的是yhat[0,0], [1,2]
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第98张图片
正文:
上面说明的内容就是为了 实现 获取下图中的 yhaty:即 预测结果中 某东西的真实类别(即老师口中的标号)的预测值
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第99张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第100张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第101张图片

5.计算预测正确的数量

因为是分类问题,将预测类别与真实y元素进行比较。

【过程总结】
1.找出yhat中每行(每行代表 对一个样本类别的 属于各个类别的概率预测值)中 预测值最大的元素对应的索引,即 预测该样本的类别;
2.将yhat与y中每个元素逐个比较并 将 每个元素的比较结果保存到cmp中;其中cmp中每个元素都是布尔类型
3.将 cmp转换为y中元素的数据类型(即 int)并求和,得到了 所有预测类别正确的样本的数量
4. 预测正确样本数/总样本数 =准确率
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第102张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第103张图片

【补充】变量1.type(变量2):将 变量1的类型转换为变量2的类型
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第104张图片
【补充2】QA中的问题:为什么不在accuracy中 直接除以len(y)

因为我们读取batch时 最后一个batch中样本数量可能不足 batch_size,
那么此时 accuracy/len(y) 是不对的
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第105张图片

6.任意模型的准确率(或称精度)

evaluate_accuracy(net,data_iter)作用:基于给定模型net和数据迭代器data_iter,可以计算该模型在该数据迭代器上的精度
Accumulator类的作用:该类可生成一个累加迭代器
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第106张图片
弹幕说:如报Runtime的错误,把自己的Batchsize和进程数改小就可以了,毕竟cpu太拉

%matplotlib inline
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l

def get_dataloader_workers():
    """使用4个进程来读取的数据"""
    return 4

def load_data_fashion_mnist(batch_size, resize=None):
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0,transforms.Resize(resize)) # 如果有Resize参数传进来,就进行resize操作
    trans = transforms.Compose(trans)
    mnist_train = torchvision.datasets.FashionMNIST(root="01_data/01_DataSet_FashionMNIST",train=True,transform=trans,download=True)
    mnist_test = torchvision.datasets.FashionMNIST(root="01_data/01_DataSet_FashionMNIST",train=False,transform=trans,download=True)            
    return (data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_workers()),
           data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_workers()))               


batch_size = 256
train_iter, test_iter = load_data_fashion_mnist(batch_size) # 返回训练集、测试集的迭代器     

num_inputs = 784
num_outputs = 10
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) 
    return X_exp / partition # 这里应用了广播机制

# 实现softmax回归模型
def net(X):
    return softmax(torch.matmul(X.reshape((-1,w.shape[0])),w)+b) # -1为默认的批量大小,表示有多少个图片,每个图片用一维的784列个元素表示      

def cross_entropy(y_hat, y):
    return -torch.log(y_hat[range(len(y_hat)),y]) # y_hat[range(len(y_hat)),y]为把y的标号列表对应的值拿出来。传入的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):  # 当传入一个模型net和数据迭代器data_iter
    """计算在指定数据集上模型的精度"""
    #如果net模型是torch.nn.Module实现的神经网络的话,将它变成评估模式(评估模式下不计算梯度,即只做forward不做backward)
    if isinstance(net,torch.nn.Module):      
        net.eval()  # 将模型设置为评估模式
    metric = Accumulator(2) # 正确预测数、预测总数,metric为累加器的实例化对象,里面存了两个数
    for X, y in data_iter:  # 对于迭代器中每次拿到的一个batch中的X和y
        metric.add(accuracy(net(X),y),y.numel()) # net(X)将X输入模型,获得预测值。y.numel()为y中元素总个数
    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]

print(evaluate_accuracy(net, test_iter))   # 看一下随机出来的模型和测试迭代器 的效果(因为共10个类别,因此正确率应在10%左右)

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第107张图片

【补充】pytorch中的tensor.numel()方法
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第108张图片

7.训练函数

train_epoch_ch3:对整个数据集数据迭代一次(即一个epoch)的实现方式
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第109张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第110张图片

8.动画绘制

自定义一个小动画,可视化训练过程
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第111张图片

9.多epoch总训练函数

ch3指在第三章的训练函数,后面会不断完善这个训练函数
metrics:指标
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第112张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第113张图片

我们一般关注测试的精度test acc

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第114张图片
可视化效果:
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第115张图片

10.预测数据

从测试集中拿出6个样本预测一下
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第116张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第117张图片

【完整代码】

个别局部部分的解释可以看具体对应章节的注释

##### %matplotlib inline
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l
from IPython import display

def get_dataloader_workers():
    """使用4个进程来读取的数据"""
    return 0

def load_data_fashion_mnist(batch_size, resize=None):
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0,transforms.Resize(resize)) # 如果有Resize参数传进来,就进行resize操作
    trans = transforms.Compose(trans)
    mnist_train = torchvision.datasets.FashionMNIST(root="01_data/01_DataSet_FashionMNIST",train=True,transform=trans,download=True)
    mnist_test = torchvision.datasets.FashionMNIST(root="01_data/01_DataSet_FashionMNIST",train=False,transform=trans,download=True)            
    return (data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_workers()),
           data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_workers()))               


batch_size = 256
train_iter, test_iter = load_data_fashion_mnist(batch_size) # 返回训练集、测试集的迭代器     
# 2.
num_inputs = 784
num_outputs = 10
w = torch.normal(0,0.01,size=(num_inputs,num_outputs),requires_grad=True)  # 重点是size
b = torch.zeros(num_outputs,requires_grad=True)

def softmax(X): # 此处softmax的对象是一个矩阵。对矩阵做softmax就相当于对其每一行做softmax
    X_exp = torch.exp(X) # 每个元素都进行指数运算(即softmax公式中的分子)
    partition = X_exp.sum(1,keepdim=True)  # 按axis=1进行求和,并仍保持原维度(即二维矩阵)(即softmax公式中的分母)
    return X_exp / partition # 这里应用了广播机制(即softmax公式中的分子/分母)

# 实现softmax回归模型
def net(X):
    return softmax(torch.matmul(X.reshape((-1,w.shape[0])),w)+b) # -1表示自动计算一下(为默认的批量大小),表示有多少个图片,每个图片用一维的784列个元素表示      

def cross_entropy(y_hat, y):
    return -torch.log(y_hat[range(len(y_hat)),y]) # y_hat[range(len(y_hat)),y]为把y的标号列表对应的值拿出来。传入的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的准确率
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()
            l.mean().backward()  # 这里对loss取了平均值出来
            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
    
# 【实现SGD】小批量随即梯度下降来优化模型的损失函数
lr = 0.1
def updater(batch_size):
    return d2l.sgd([w,b],lr,batch_size)

num_epochs = 10
train_ch3(net,train_iter,test_iter,cross_entropy,num_epochs,updater)

预测数据:

def predict_ch3(net,test_iter,n=6):
    for X, y in test_iter: 
        break # 仅拿出一批六个数据
    trues = d2l.get_fashion_mnist_labels(y)  # 真实label
    preds = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1))  # 预测label
    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)

Softmax-基于框架实现(nn.Module)

1.拿到数据和迭代器
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第118张图片

2.构造softmax回归模型:

nn.Flatten():将任何维度的tensor转换为2Dtensor(即保留其第0维度,其他维度均展平为一个向量)

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第119张图片
3.损失函数与优化算法
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第120张图片

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第121张图片

后面的课程会不断在该数据集上应用更深、更复杂的模型

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): # 该函数会对网络的每一层都 call一次,其中m就是当前的layer
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01) # 如果当前层是线性层,就将其权重初始化为 均值为0、方差为0.01的随机值

net.apply(init_weights)  # net网络权重初始化:应用init_weights函数到net网络的每一层
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
d2l.train_ch3(net,train_iter,test_iter,loss,num_epochs,trainer)

QA

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第122张图片
1.softlabel是图像分类问题的实际场景中的一个常用技巧,大概意思就是 softmax理论上的效果在实际中可能不能完全实现?
刚刚讲的是 softmax+one-hot编码:将 n类 变成长度为n的向量,只有类别正确的那一类为1,其余均为0 ,然后用softmax去逼近这个 纯0、1的分布。
但其存在的问题是 (回想softmax公式)很难用 指数去逼近1, 即 要求输出几乎接近无穷大(分子)、分母的其他项都很小,这样 最后结果才是1,很难实现
在这里插入图片描述
因此提出了改进方案softlabel,将正确类记为0.9,错误类记为0.1(而不是像one-hot这样纯0、1),除以n? 这样就使得 用softmax去拟合 那些很小的数 是有可能实现的

2、3.逻辑回归就是 当类别n=2是的softmax回归

4.互信息不好算, 交叉熵就够用、好用了

5.也不是说我们不关注不正确的类,只是one-hot编码 将不正确的类的概率变为0了,所以计算时可以忽略掉不正确的类 。
如果是用softlabel,即类比one-hot用 0.9和0.1,那么计算时就不能忽略掉不正确的类了

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第123张图片

6.会。但其实只要每个类都有足够多的样本时, 还是比较平衡的

7.每次走的步长取决于两点:梯度大小和lr,假设lr固定,那么步长只受梯度(如下图中橙色线)影响,因此对于下图,不管梯度是+1or-1,梯度的绝对值是一样的,因此每步移动的距离都是平均的
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第124张图片
而下图则是,当 离原点较远时,梯度较大,因此每步步长就较大
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第125张图片
8、9.似然函数属于统计学中的概念,因为深度学习后面的模型和统计没有太大关系,因此没有细讲这里
【最小化损失就等价于最大化似然函数】
似然函数简单理解:当给定一个模型(即 有了确定的权重)和数据,这个模型权重出现的概率有多大。因此我们要找到最大似然, 使得 这个权重更合理

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第126张图片

10.前面讲过了(回想 权重更新公式即可 w1 = w0 - lr * 梯度)
11.可以用

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第127张图片

13、14.是的。开多个python进程
课程中单独定义一个设置num_workers的原因是 老版本 中 windows不支持设置 多进程,因此需要在函数中判断一下是不是win系统,如果是就不用多进程
现在支持了,直接写死num也行,不用函数定义

15.有讲究,此处只是为了当前教学,后面会展开讲方差

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第128张图片

17.不是。无论batch_size设为几,计算量不变。
只是说能不能想办法增加计算的并行度,以提高效率。
如果增大减小batch_size的没区别,可能说明 你在CPU上或模型很小

18.python

19.因为我们读取batch时 最后一个batch中样本数量可能不足 batch_size,
那么此时 accuracy/len(y) 是不对的,而是应该像evaluate_accuracy中这样将所有正、负例 累加起来再除
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第129张图片
学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第130张图片

20.后面基于GPU会再讲

  1. 设一下是好习惯,即 设置后不用计算并更新梯度了,性能会好
  2. 就是传参穿进去的, 如手动实现时 手动放进去和 基于框架实现时用 net.parameters放进去

23.可能是。 后面会通过 微调学习率或加入正则项来解决过拟合

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第131张图片

24.后面

25.后面
26.自己比一下。

学习笔记-李沐动手学深度学习(二)(08-09、线性回归、优化算法、梯度下降、Softmax回归、损失函数、图片分类)_第132张图片

27.可以看下pytorch文档,如将每个类别单独放一个文件夹

29.建议看下统计学习
弹幕说:softmax往前回溯的话应该是模式识别中的最大后验分类

你可能感兴趣的:(李沐动手学深度学习,算法,回归,学习)