01 线性回归
回归(regression)是能为一个或多个自变量与因变量之间关系建模的一类方法。 在自然科学和社会科学领域,回归经常用来表示输入和输出之间的关系。
在机器学习领域中的大多数任务通常都与预测(prediction)有关。 当我们想预测一个数值时,就会涉及到回归问题。
常见的例子包括:预测价格(房屋、股票等)、预测住院时间(针对住院病人等)、 预测需求(零售销量等)。 但不是所有的预测都是回归问题。
在后面的章节中,我们将介绍分类问题。分类问题的目标是预测数据属于一组类别中的哪一个。
下面我们从“如何在美国买房子”这个案例,了解线性回归:
总结:
- 线性回是对n维输入的加权,外加偏差
- 使用平方损失来衡量预测值和真是值的差异
- 线性回归有显示解
- 显示解意味着问题比较简单,我们之后研究的大多问题比较复杂,是没有显示解的
- 线性回归可以看作是单层神经网络
1、线性回归的基本元素
举个例子:根据房屋的面积和房龄来估算房屋价格
- 收集一个真实的数据集,包括了房屋的销售价格、面积和房龄
- 每行数据(比如一次房屋交易相对应的数据)称为样本(sample), 也可以称为数据点(data point)或数据样本(data instance)。
- 试图预测的目标(比如预测房屋价格)称为标签(label)或目标(target)。
- 预测所依据的自变量(面积和房龄)称为特征(feature)或协变量(covariate)。
1.1、线性模型
线性假设是指目标(房屋价格)可以表示为特征(面积和房龄)的加权和
给定一个数据集,我们的目标是寻找模型的权重w和偏置b, 使得根据模型做出的预测大体符合数据里的真实价格。
在开始寻找最好的模型参数(model parameters)w和b之前, 我们还需要两个东西:
(1)一种模型质量的度量方式;
(2)一种能够更新模型以提高模型预测质量的方法。
1.2、损失函数
损失函数(loss function)能够量化目标的实际值与预测值之间的差距。
1.3、解析解
线性回归的解可以用一个公式简单地表达出来, 这类解叫作解析解(analytical solution)。
1.4、随机梯度下降
通过不断地在损失函数递减的方向上更新参数来降低误差
梯度下降
总结
- 梯度下降通过不断沿着反梯度方向更新参数求解
- 小批量随机梯度下降是深度学习默认的求解算法
- 两个重要的超参数是批量大小和学习率
1.5、用模型进行预测
给定特征估计目标的过程通常称为预测(prediction)或推断(inference)。
2、矢量化加速
在训练我们的模型时,我们经常希望能够同时处理整个小批量的样本。
为了实现这一点,需要我们对计算进行矢量化, 从而利用线性代数库,而不是编写开销高昂的for循环。
3、正太分布与平方损失
pass
4、从线性回归到深度网络
pass
02 线性回归的从零开始实现
%matplotlib inline
import random
import torch
from d2l import torch as d2l
1、生成数据集
在下面的代码中,我们生成一个包含1000个样本的数据集, 每个样本包含从标准正态分布中采样的2个特征。
"""
根据带有噪声的线性模型构造一个人造数据集,
任务是使用这个有限样本的数据集来恢复这个模型的参数。
"""
def synthetic_data(w, b, num_examples):
"""生成y=Xw+b+噪声"""
X = torch.normal(0, 1, (num_examples, 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)
"""
生成一个包含1000个样本的数据集, 每个样本包含从标准正态分布中采样的2个特征。
features中的每一行都包含一个二维数据样本, labels中的每一行都包含一维标签值(一个标量)。
"""
看一下具体数据:
print('features:', features[0],'\nlabel:', labels[0])
features: tensor([-0.1874, -0.4533])
label: tensor([5.3888])
通过生成第二个特征features[:, 1] 和labels 的散点图,直观观察到两者之间的线性关系:
d2l.set_figsize()
d2l.plt.scatter(features[:, (1)].detach().numpy(), labels.detach().numpy(), 1);
2、读取数据集
"""
打乱数据集中的样本并以小批量方式获取数据。
定义一个data_iter函数, 该函数接收批量大小、特征矩阵和标签向量作为输入,生成大小为batch_size的小批量。
"""
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)])
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
tensor([[-0.1557, -1.1682],
[-1.3068, -0.0295],
[ 0.8605, 1.1036],
[-2.1143, -0.0096],
[-0.2705, 1.6564],
[-0.0758, 0.4954],
[-0.4310, 0.4836],
[ 0.2151, -0.3077],
[-0.9516, -2.5226],
[ 0.6774, 1.8962]])
tensor([[ 7.8500e+00],
[ 1.6852e+00],
[ 2.1753e+00],
[ 1.5046e-03],
[-1.9907e+00],
[ 2.3606e+00],
[ 1.6979e+00],
[ 5.6878e+00],
[ 1.0866e+01],
[-8.9715e-01]])
3、初始化模型参数
"""
开始用小批量随机梯度下降优化我们的模型参数之前, 我们需要先有一些参数。
在下面的代码中,我们通过从均值为0、标准差为0.01的正态分布中采样随机数来初始化权重, 并将偏置初始化为0。
"""
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
在初始化参数之后,我们的任务是更新这些参数,直到这些参数足够拟合我们的数据。
4、定义模型
回想一下,要计算线性模型的输出, 我们只需计算输入特征X和模型权重w的矩阵-向量乘法后加上偏置b。
"""
将模型的输入和参数同模型的输出关联起来。
下面,X,w是一个向量,b而是一个标量。
"""
def linreg(X, w, b):
"""线性回归模型"""
return torch.matmul(X, w) + b
5、定义损失函数
"""
计算损失函数的梯度
"""
def squared_loss(y_hat, y):
"""均方损失"""
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
6、定义优化算法
"""
在每一步中,使用从数据集中随机抽取的一个小批量,然后根据参数计算损失的梯度。 接下来,朝着减少损失的方向更新我们的参数。
"""
def sgd(params, lr, batch_size):
"""小批量随机梯度下降"""
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
7、训练
理解这段代码至关重要,因为从事深度学习后, 你会一遍又一遍地看到几乎相同的训练过程。
lr = 0.01
num_epochs = 5
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
for X, y in data_iter(batch_size, features, labels):
l = loss(net(X, w, b), y)
l.sum().backward()
sgd([w, b], lr, batch_size)
with torch.no_grad():
train_l = loss(net(features, w, b), labels)
print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
运行结果:
epoch 1, loss 0.803853
epoch 2, loss 0.106669
epoch 3, loss 0.014265
epoch 4, loss 0.001964
epoch 5, loss 0.000310
看一下误差:
print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b - b}')
"""
w的估计误差: tensor([-0.0004, 0.0009], grad_fn=<SubBackward0>)
b的估计误差: tensor([0.0009], grad_fn=<RsubBackward1>)
"""
8、总结
整合一下上面的代码:
可以多跑几遍下面的代码,调参,感受一下
import random
import torch
from d2l import torch as d2l
def synthetic_data(w, b, num_examples):
"""生成y=Xw+b+噪声"""
X = torch.normal(0, 1, (num_examples, 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)
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)])
yield features[batch_indices], labels[batch_indices]
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
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
def sgd(params, lr, batch_size):
"""小批量随机梯度下降"""
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
lr = 0.01
num_epochs = 5
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
for X, y in data_iter(batch_size, features, labels):
l = loss(net(X, w, b), y)
l.sum().backward()
sgd([w, b], lr, batch_size)
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}')
运行结果展示:
epoch 1, loss 2.183826
epoch 2, loss 0.283514
epoch 3, loss 0.036978
epoch 4, loss 0.004877
epoch 5, loss 0.000684
w的估计误差: tensor([ 0.0093, -0.0181], grad_fn=<SubBackward0>)
b的估计误差: tensor([0.0286], grad_fn=<RsubBackward1>)
总结一下上面的训练过程:
03 线性回归的简介实现
本节我们基于深度学习框架,简洁的实现线性回归模型
1、生成数据集
import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)
2、读取数据集
def load_array(data_arrays, batch_size, is_train=True):
"""构造一个PyTorch数据迭代器"""
dataset = data.TensorDataset(*data_arrays)
return data.DataLoader(dataset, batch_size, shuffle=is_train)
batch_size = 10
data_iter = load_array((features, labels), batch_size)
读取并打印第一个小批量样本:
next(iter(data_iter))
[tensor([[-0.5345, 1.2533],
[-0.2713, 1.3131],
[ 0.1256, 0.4997],
[-1.5909, 1.3276],
[-0.5776, -1.5652],
[-1.9792, 0.5028],
[ 0.6800, -1.9575],
[-0.9301, 0.4195],
[-0.1040, -1.4376],
[-0.5383, 0.3953]]),
tensor([[-1.1386],
[-0.8192],
[ 2.7363],
[-3.5005],
[ 8.3576],
[-1.4721],
[12.2130],
[ 0.9074],
[ 8.8819],
[ 1.7698]])]
3、定义模型
from torch import nn
"""
我们将两个参数传递到nn.Linear中。
- 第一个指定输入特征形状,即2,
- 第二个指定输出特征形状,输出特征形状为单个标量,因此为1。
"""
net = nn.Sequential(nn.Linear(2, 1))
4、初始化模型参数
"""
通过net[0]选择网络中的第一个图层,weight.data和bias.data方法访问参数,
还可以使用替换方法normal_和fill_来重写参数值。
这里,我们指定每个权重参数应该从均值为0、标准差为0.01的正态分布中随机采样,偏置参数将初始化为零。
"""
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
tensor([0.])
5、定义损失函数
loss = nn.MSELoss()
6、定义优化算法
小批量随机梯度下降算法是一种优化神经网络的标准工具。
当我们实例化一个SGD 实例时,我们要指定优化的参数 (可通过net.parameters() 从我们的模型中获得)以及优化算法所需的超参数字典。 小批量随机梯度下降只需要设置lr 值,这里设置为0.03。
"""
小批量随机梯度下降算法是一种优化神经网络的标准工具
"""
trainer = torch.optim.SGD(net.parameters(), lr=0.03)
7、训练
在每个迭代周期里,我们将完整遍历一次数据集(train_data ), 不停地从中获取一个小批量的输入和相应的标签。 对于每一个小批量,我们会进行以下步骤:
- 通过调用
net(X) 生成预测并计算损失l (前向传播)。 - 通过进行反向传播来计算梯度。
- 通过调用优化器来更新模型参数。
num_epochs = 5
for epoch in range(num_epochs):
for X, y in data_iter:
l = loss(net(X) ,y)
trainer.zero_grad()
l.backward()
trainer.step()
l = loss(net(features), labels)
print(f'epoch {epoch + 1}, loss {l:f}')
epoch 1, loss 0.000496
epoch 2, loss 0.000097
epoch 3, loss 0.000097
epoch 4, loss 0.000097
epoch 5, loss 0.000097
下面我们比较生成数据集的真实参数和通过有限数据训练获得的模型参数:
首先从net 访问所需的层,然后读取该层的权重和偏置,我们估计得到的参数与生成数据的真实参数非常接近。
w = net[0].weight.data
print('w的估计误差:', true_w - w.reshape(true_w.shape))
b = net[0].bias.data
print('b的估计误差:', true_b - b)
w的估计误差: tensor([1.1563e-05, 6.6280e-04])
b的估计误差: tensor([0.0007])
8、总结
整合上述代码:
import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
from torch import nn
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)
def load_array(data_arrays, batch_size, is_train=True):
"""构造一个PyTorch数据迭代器"""
dataset = data.TensorDataset(*data_arrays)
return data.DataLoader(dataset, batch_size, shuffle=is_train)
batch_size = 10
data_iter = load_array((features, labels), batch_size)
net = nn.Sequential(nn.Linear(2, 1))
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
loss = nn.MSELoss()
trainer = torch.optim.SGD(net.parameters(), lr=0.03)
num_epochs = 5
for epoch in range(num_epochs):
for X, y in data_iter:
l = loss(net(X) ,y)
trainer.zero_grad()
l.backward()
trainer.step()
l = loss(net(features), labels)
print(f'epoch {epoch + 1}, loss {l:f}')
w = net[0].weight.data
print('w的估计误差:', true_w - w.reshape(true_w.shape))
b = net[0].bias.data
print('b的估计误差:', true_b - b)
运行结果:
epoch 1, loss 0.000232
epoch 2, loss 0.000103
epoch 3, loss 0.000103
epoch 4, loss 0.000102
epoch 5, loss 0.000102
w的估计误差: tensor([ 0.0002, -0.0006])
b的估计误差: tensor([-0.0009])
小结;
- 我们可以使用PyTorch的高级API更简洁地实现模型。
- 在PyTorch中,
data 模块提供了数据处理工具,nn 模块定义了大量的神经网络层和常见损失函数。 - 我们可以通过
_ 结尾的方法将参数替换,从而初始化参数。
9、QA
04 softmax回归
1、Softmax回归
分类问题 VS 回归问题
- 回归估计一个连续值
- 分类预测一个离散类别:如手写数字识别(10分类)、自然语言处理-情感分类
从回归过度到分类:
2、损失函数
介绍了。。。
05 图像分类数据集
以Fashion-MNIST数据集为例,介绍读取数据集
%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()
1、读取数据集
trans = transforms.ToTensor()
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)
看一下数据量:
len(mnist_train), len(mnist_test)
(60000, 10000)
图像信息:
mnist_train[0][0].shape
"""
torch.Size([1, 28, 28]) :
- 数据集由灰度图像组成,其通道数为1
- 图像的高度和宽度均为28像素
"""
torch.Size([1, 28, 28])
Fashion-MNIST中包含的10个类别,以下函数用于在数字标签索引及其文本名称之间进行转换。
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):
"""绘制图像列表"""
figsize = (num_cols * scale, num_rows * scale)
_, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
axes = axes.flatten()
for i, (ax, img) in enumerate(zip(axes, imgs)):
if torch.is_tensor(img):
ax.imshow(img.numpy())
else:
ax.imshow(img)
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
if titles:
ax.set_title(titles[i])
return axes
X, y = next(iter(data.DataLoader(mnist_train, batch_size=18)))
show_images(X.reshape(18, 28, 28), 2, 9, titles=get_fashion_mnist_labels(y));
2、读取小批量
在每次迭代中,数据加载器每次都会读取一小批量数据,大小为batch_size 。
通过内置数据迭代器,我们可以随机打乱了所有样本,从而无偏见地读取小批量。
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'
'5.31 sec'
3、整合所有组件
- 定义
load_data_fashion_mnist 函数,用于获取和读取Fashion-MNIST数据集 - 这个函数返回训练集和验证集的数据迭代器
- 此外,函数可接受一个可选参数
resize ,用来将图像大小调整为另一种形状。
def load_data_fashion_mnist(batch_size, resize=None):
"""下载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()))
通过指定resize 参数来测试load_data_fashion_mnist 函数的图像大小调整功能:
train_iter, test_iter = load_data_fashion_mnist(32, resize=64)
for X, y in train_iter:
print(X.shape, X.dtype, y.shape, y.dtype)
break
torch.Size([32, 1, 64, 64]) torch.float32 torch.Size([32]) torch.int64
06 softmax回归的从零开始实现
我们引入Fashion-MNIST数据集,展开对softmax回归的实现
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)
1、初始化模型参数
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)
2、定义softmax操作
回顾一下对元素求和:
X = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
X.sum(0, keepdim=True), X.sum(1, keepdim=True)
(tensor([[5., 7., 9.]]),
tensor([[ 6.],
[15.]]))
回想一下,实现softmax由三个步骤组成:
def softmax(X):
X_exp = torch.exp(X)
partition = X_exp.sum(1, keepdim=True)
return X_exp / partition
X = torch.normal(0, 1, (2, 5))
X_prob = softmax(X)
X_prob, X_prob.sum(1)
结果展示:
(tensor([[0.1137, 0.3174, 0.3028, 0.1085, 0.1575],
[0.5213, 0.3160, 0.1214, 0.0339, 0.0075]]),
tensor([1.0000, 1.0000]))
PS:这里有点草率,矩阵中的非常大或非常小的元素可能造成数值上溢或下溢,但我们没有采取措施来防止这点。
3、定义模型
实现softmax回归模型:
- 下面的代码定义了输入如何通过网络映射到输出
- 注意:数据传递到模型之前,用
reshape 函数将每张原始图像展平为向量
def net(X):
return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)
4、定义损失函数
这里使用交叉熵损失函数。
下面,我们创建:
- 标签y:包含三个标签
- 数据样本
y_hat ,其中包含2个样本在3个类别的预测概率
"""
有了y,我们知道在第一个样本中,第一类是正确的预测;
而在第二个样本中,第三类是正确的预测。
然后使用y作为y_hat中概率的索引, 我们选择第一个样本中第一个类的概率和第二个样本中第三个类的概率。
"""
y = torch.tensor([0, 2])
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
y_hat[[0, 1], y]
tensor([0.1000, 0.5000])
一行代码实现交叉熵损失函数:
def cross_entropy(y_hat, y):
return - torch.log(y_hat[range(len(y_hat)), y])
cross_entropy(y_hat, y)
array([2.3025851, 0.6931472])
5、分类精度
def accuracy(y_hat, y):
"""计算预测正确的数量"""
if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
y_hat = y_hat.argmax(axis=1)
cmp = y_hat.type(y.dtype) == y
return float(cmp.type(y.dtype).sum())
accuracy(y_hat, y) / len(y)
0.5
同样,对于任意数据迭代器data_iter 可访问的数据集, 我们可以评估在任意模型net 的精度。
def evaluate_accuracy(net, data_iter):
"""计算在指定数据集上模型的精度"""
if isinstance(net, torch.nn.Module):
net.eval()
metric = Accumulator(2)
with torch.no_grad():
for X, y in data_iter:
metric.add(accuracy(net(X), y), y.numel())
return metric[0] / metric[1]
定义一个实用程序类Accumulator ,用于对多个变量进行累加。
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)]
def reset(self):
self.data = [0.0] * len(self.data)
def __getitem__(self, idx):
return self.data[idx]
evaluate_accuracy(net, test_iter)
结果展示:
0.1043
6、训练
首先,我们定义一个函数来训练一个迭代周期
def train_epoch_ch3(net, train_iter, loss, updater):
"""训练模型一个迭代周期(定义见第3章)"""
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.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]
定义一个在动画中绘制数据的实用程序类Animator ,用于可视化
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):
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
使用之前学习的小批量随机梯度下降来优化模型的损失函数
lr = 0.1
def updater(batch_size):
return d2l.sgd([W, b], lr, batch_size)
训练10个迭代周期:
num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)
结果展示:
7、预测
给定一系列图像,我们将比较它们的实际标签(文本输出的第一行)和模型预测(文本输出的第二行)
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)
结果展示:
07 softmax回归的简洁实现
通过深度学习框架的高级API也能更方便地实现softmax回归模型
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)
1、初始化模型参数
softmax回归的输出层是一个全连接层,因此,我们只需在Sequential 中添加一个带有10个输出的全连接层
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)
net.apply(init_weights);
2、重新审视Softmax的实现
我们计算了模型的输出,然后将此输出送入交叉熵损失。
loss = nn.CrossEntropyLoss(reduction='none')
3、优化算法
使用学习率为0.1的小批量随机梯度下降作为优化算法
trainer = torch.optim.SGD(net.parameters(), lr=0.1)
4、训练
用上一节中定义的训练函数train_ch3来训练模型
num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
效果展示:
|