《动手学深度学习》第三章-softmax回归总结
softmax回归总结
1.从fashion_mnist导入数据(d2l.load_data_fashion_mnist(batch_size))
def load_data_fashion_mnist(batch_size, resize=None):
"""Download the Fashion-MNIST dataset and then load it into memory.
Defined in :numref:`sec_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()))
torchvision : This library is part of the PyTorch project. PyTorch is an open source machine learning framework.
DataLoader : Combines a dataset and a sampler, and provides an iterable over the given dataset.The DataLoader supports both map-style and iterable-style datasets with single- or multi-process loading, customizing loading order and optional automatic batching (collation) and memory pinning.
返回值:mnist_train和mnist_test的迭代器
2.网络的定义
def net(X):
return softmax(torch.matmul( X.reshape(-1,W.shape[0])) ,W) + b)
torch.matmul:matmul是tensor的乘法,输入可以是高维的。
(1)若输入都是二维,即普通的矩阵乘法。
(2)若输入是多维的,把多出的一维作为batch提出来,其余部分做矩阵乘法。
(5,3,4) @(4,2) -> (5,3,2)
(2,5,3) @ (1,3,4)->(2,5,4)
(2,1,3,4)@(5,4,2)->(2,5,3,2)
在进行tensor的乘法时,需要对输入的X进行reshape,size=(-1,28*28)
X.reshape(-1,W.shape[0])的size为[256,28*28]
W的size为[28*28,10]
b会经过广播机制为[256,10]
3.计算正确分类的样本数
#计算模型精度
def accuracy(y_hat,y):
if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
y_hat = torch.argmax(y_hat,axis=1)
cmp = y_hat.type(y.dtype) == y
return float( cmp.type(y.dtype).sum() )
(1)y_hat = torch.argmax(y_hat,axis=1),y_hat的shape为[256,10],按行选择最大概率的下标,并且将下标保存到y_hat中。
(2)==比较运算符对类型敏感,先对y_hat类型进行转换。
4.累加器实现
(1)add(self,*args)该函数在定义时,实现的可变参数args。
例:Accumulator(2),累加器中有两个元素,第一个是正确数,第二个是总数。传参的时候也有两个参数(正确数,总数)
假设Accumulator中原有元素为[2,10],传参为[3,6]
zip(self.data,args) = [(2,3),(10,6)]
在第一轮for循环时,取出的a=2,b=3,即相加后正确数为5。
在第二轮for循环时,取出的a=10,b=6,即相加后总数为16。
5.训练过程设计思路
def train_epoch(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):
#若优化器使用python内置的
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(net, train_iter, test_iter, loss, num_epochs, updater): #@save
"""训练模型"""
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(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
(1)train_epoch:在一个epoch中,我们将完整遍历一次数据集(train_data
),不停地从中获取一个小批量的输入和相应的标签。对于每一个小批量,我们会进行以下步骤:
- 通过调用
net(X)
生成预测并计算损失l
(前向传播)。 - 通过进行反向传播来计算梯度。
- 通过调用优化器来更新模型参数。
为了更好的衡量训练效果,我们计算每个迭代周期后的损失,并打印它来监控训练过程。
6.softmax简洁实现关于层定义
#初始化网络模型
#需要在线性层前定义flatten,调整网络输入的形状
net = nn.Sequential(nn.Flatten(),nn.Linear(28*28,10))
torch.nn.Flatten(start_dim=1, end_dim=- 1)
输入维度是(32,1,28,28),经过Flatten()为(32,1*28,28)
作业:
- 在本节中,我们直接实现了基于数学定义softmax运算的
softmax
函数。这可能会导致什么问题? - 本节中的函数
cross_entropy
是根据交叉熵损失函数的定义实现的。它可能有什么问题?提示:考虑对数的定义域。 - 你可以想到什么解决方案来解决上述两个问题?
- 返回概率最大的分类标签总是最优解吗?例如,医疗诊断场景下你会这样做吗?
- 假设我们使用softmax回归来预测下一个单词,可选取的单词数目过多可能会带来哪些问题?
(1)经过softmax函数转换时,分母是exp求和,若某个输入很大,会造成分母很大,进而引起数值范围错误。
(2)对数函数在X接近0时,其值趋于负无穷大。若某个输入经过softmax函数,输出的值可能是接近0,会造成loss值无限大,产生范围错误。
(3)softmax-concise课程的重新审视softmax函数设计。
log
(
y
^
j
)
=
log
(
exp
(
o
j
−
max
(
o
k
)
)
∑
k
exp
(
o
k
−
max
(
o
k
)
)
)
=
log
(
exp
(
o
j
−
max
(
o
k
)
)
)
−
log
(
∑
k
exp
(
o
k
−
max
(
o
k
)
)
)
=
o
j
−
max
(
o
k
)
−
log
(
∑
k
exp
(
o
k
−
max
(
o
k
)
)
)
.
\begin{aligned} \log{(\hat y_j)} & = \log\left( \frac{\exp(o_j - \max(o_k))}{\sum_k \exp(o_k - \max(o_k))}\right) \\ & = \log{(\exp(o_j - \max(o_k)))}-\log{\left( \sum_k \exp(o_k - \max(o_k)) \right)} \\ & = o_j - \max(o_k) -\log{\left( \sum_k \exp(o_k - \max(o_k)) \right)}. \end{aligned}
log(y^j)=log(∑kexp(ok−max(ok))exp(oj−max(ok)))=log(exp(oj−max(ok)))−log(k∑exp(ok−max(ok)))=oj−max(ok)−log(k∑exp(ok−max(ok))).
(4)在医疗场景中,比如通过照片判断疾病类别,概率最大的分类标签不一定是最优解,需要具体问题,具体分析。
(5)当可选单词过于多,而神经网络参数学习效果不佳时,可能每个分类的概率都很小,成为随机选择分类。