一、简介
1、机器学习
机器学习研究如何使用经验改善计算机系统的性能。
2、表征学习
表征学习是机器学习的一类,研究的是,如何自动学习出数据合适的表示方式,更好地由输入得到正确的输出。
3、深度学习
深度学习是具有多级表示的表征学习,逐级表示越来越抽象的概念或模式。
二、预备知识
1、张量基本操作
- 手动设置一个torch种子:使用不同模型进行预测,手动设置种子可以保证每次随机初始化参数和批次顺序是相同的,保证了实验的可重复性。
- 创建张量:若干种。
- 张量加法:逐个元素相加。
- 张量形状:shape和size()。
- 张量塑形:view。
- 张量内存
- 张量广播机制:如果两个张量在某一维度的大小相同,或其中一个的该维度大小为 1,则这两个维度是兼容的。
2、自动求梯度
如果将Tensor的属性.requires_grad
设置为True
,它将开始追踪(track)在其上的所有操作。完成计算后,可以调用.backward()
来完成所有梯度计算。此Tensor
的梯度将累积到.grad
属性中。
注意在调用
.backward()
时,如果Tensor
是标量,则不需要为backward()
指定任何参数;否则,需要指定一个求导变量。
如果不想要被继续追踪,可以调用.detach()
将其从追踪记录中分离出来,这样就可以防止将来的计算被追踪。此外,还可以用with torch.no_grad()
将不想被追踪的操作代码块包裹起来,这种方法在评估模型的时候很常用,因为在评估模型时,我们并不需要计算可训练参数(requires_grad=True
)的梯度。
Function
是另外一个很重要的类。Tensor
和Function
互相结合就可以构建一个记录有整个计算过程的非循环图。每个Tensor
都有一个.grad_fn
属性,该属性即创建该Tensor
的Function
(除非用户创建的Tensor
s时设置了grad_fn=None
)。
三、基础
1、线性回归
①线性回归vs分类问题
线性回归的输出是连续值,分类问题的输出是离散值。
②线性回归几要素
(1)模型
线性回归假设输出与各个输入之间是线性关系。
都是标量,分别是权重和偏置。
(2)线性回归模型训练
a.训练集
在机器学习术语里,用于模型训练的数据集被称为训练数据集(training data set)或训练集(training set),一栋房屋被称为一个样本(sample),其真实售出价格叫作标签(label),用来预测标签的两个因素叫作特征(feature)。特征用来表征样本的特点。
b.损失函数
在模型训练中,我们需要衡量价格预测值与真实值之间的误差。通常我们会选取一个非负数作为误差,且数值越小表示误差越小。
在机器学习里,将衡量误差的函数称为损失函数(loss function)。这里使用的平方误差函数也称为平方损失(square loss)。
通常,我们用训练数据集中所有样本误差的平均来衡量模型预测的质量,即:
在模型训练中,我们希望找出一组模型参数来使训练样本平均损失最小。
c.优化算法
- 解析解和数值解:解析解是通过公式可求得的解,数值解适用于解析解难以或者无法解决的问题,是一个近似结果。
- 小批量随机梯度下降:也被叫做SGD,随机采样一个小批次数据,前代获得损失,回代获得损失对模型参数的梯度,用梯度乘上一个步长(学习率)来更新当前参数。
- 超参数:深度学习里面,模型训练之前人工设置的参数叫做超参数,比如说学习率和batch大小,调参主要调的是超参数。
③线性回归模型预测
用学习出来的线性回归模型进行预测。
④线性回归的表示方式
(1)神经网络表示
线性回归模型是一个单层神经网络,每个输出层的神经元与每个输入层的神经元都有连接,这样的输出层被称为全连接层(fully-connected layer)或稠密层(dense layer)。
(2)矢量表示
线性回归模型:
损失函数:
梯度更新参数:
梯度:
2、线性回归从0开始实现
在训练中,我们将多次迭代模型参数。在每次迭代中,我们根据当前读取的小批量数据样本(特征X
和标签y
),通过调用反向函数backward
计算小批量随机梯度,并调用优化算法sgd
迭代模型参数。由于我们之前设批量大小batch_size
为10,每个小批量的损失l
的形状为(10, 1)。回忆一下“自动求梯度”一节。由于变量l
并不是一个标量,运行l.backward()
将对l
中元素求和得到新的变量(求和得到总损失),再求该变量有关模型参数的梯度。
如果样本个数不能被批量大小整除,data_iter
函数的行为会有什么变化:一般会默认dropout。
3、线性回归的简洁实现
①导入数据
DataLoader:封装数据,批量加载数据,可以打散和多线程读入。
dataset = Data.TensorDataset(features, labels)
# 把 dataset 放入 DataLoader
data_iter = Data.DataLoader(
dataset=dataset, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=True, # 要不要打乱数据 (打乱比较好)
num_workers=2, # 多线程来读数据
)
②定义模型
nn
模块:“nn”是neural networks(神经网络)的缩写。顾名思义,该模块定义了大量神经网络的层。
Sequential
实例:可以看作是一个串联各个层的容器。在构造模型时,我们在该容器中依次添加层。当给定输入数据时,容器中的每一层将依次计算并将输出作为下一层的输入。
net = nn.Sequential()
三种串联方法:
- 直接在nn.Sequential()内定义:
net = nn.Sequential(
nn.Linear(num_inputs, 1)
# 此处还可以传入其他层
)
- 创建实例后add_module:
net = nn.Sequential()
net.add_module('linear', nn.Linear(num_inputs, 1))
- OrderDict创建多个:每层有名字
from collections import OrderedDict
net = nn.Sequential(OrderedDict([
('linear', nn.Linear(num_inputs, 1))
# ......
]))
③初始化模型参数
init
模块:该模块提供了模型参数初始化的各种方法。这里的init
是initializer
的缩写形式。
from torch.nn import init
init.normal_(net[0].weight, mean=0.0, std=0.01)
init.constant_(net[0].bias, val=0.0) # 也可以直接修改bias的data: net[0].bias.data.fill_(0)
④定义损失函数
nn里面有很多Loss。
loss = nn.MSELoss()
⑤定义优化算法
torch.optim模块:
PyTorch的优化器模块,允许你使用不同的优化算法。
import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr=0.03)
print(optimizer)
⑥全套训练
num_epochs = 3
for epoch in range(1, num_epochs + 1):
for X, y in data_iter:
output = net(X)
l = loss(output, y.view(-1, 1))
optimizer.zero_grad() # 梯度清零,等价于net.zero_grad()
l.backward()
optimizer.step()
print('epoch %d, loss: %f' % (epoch, l.item()))
4、softmax回归
softmax回归的输出单元从一个变成了多个,且引入了softmax运算使输出更适合离散值的预测和训练。
①softmax表示
也是单层神经网络,但是输出有多个,输出个数等于分类问题中的类别个数。
②softmax运算
(1)解决的问题:
softmax运算将输出变换成一个合法的类别预测分布。
- 模型输出的值范围不统一、不确定。
- 真实标签是离散值,模型输出值范围不确定,误差也难以衡量。
(2)公式
它通过下式将输出值变换成值为正且和为1的概率分布:
softmax运算不改变预测类别输出。
softmax回归对样本i分类的矢量计算表达式为:
小批量样本分类的矢量计算表达式:
③交叉熵损失函数
交叉熵(cross entropy)是一个常用的衡量方法,来衡量两个概率分布差异。交叉熵只关心对正确类别的预测概率,因为只要其值足够大,就可以确保分类结果正确。(因为用one-hot编码,y_i向量中只有一个真实类别索引位置为1,其余都为0,因此交叉熵最后得到的就是真实标签所在类预测概率的对数的相反数。)
假设训练数据集的样本数为nn,交叉熵损失函数定义为
5、softmax回归的简洁实现
在神经网络中,FlattenLayer
是一个非常常见且有用的层,其主要功能是将多维的输入张量“展平”成一维的张量。这通常在从卷积层向全连接层过渡时使用,因为全连接层需要接收一维的输入数据。
比如说,x = torch.randn(10, 3, 28, 28),批次大小是10,通道数是3,图像大小是28*28。展平以后就变成了10*(3*28*28)
class FlattenLayer(nn.Module):
def __init__(self):
super(FlattenLayer, self).__init__()
def forward(self, x): # x shape: (batch, *, *, ...)
return x.view(x.shape[0], -1)