目录
一、Softmax模型(多分类)定义:
逻辑回归是线性回归的推广,引入激活函数使得使得模型输出层单个神经元的单一输出值限制在0和1之间,适合于二分类问题。
Softmax模型是逻辑回归模型的推广,通过在输出层加入多个神经元,将单输出问题(二分类)映射到多输出问题(多分类)。
- Softmax回归中,输出为一维向量(o1,o2,o3),其中oi表示样本在类别i上的置信度。
- 一个特征对应多个权重参数,表示该特征在各个分类上的权重。
二、训练过程:
1.参数维度:
注意这里xi是一个特征。
- 分类数/输出维度决定偏置值b维度:每个分类的预测单元(神经元)都有一个偏置值b,而分类数又和输出维度相同。
- 特征数×分类数/输出维度决定权重W维度:每个特征都作为所有类别的神经元的输入。
2.对真实值标签编码:
- 分类问题得到的是分类结果,为了更好的计算损失,需要对真实值标签进行一位有效编码。
3.对预测值标签编码:
3.1Softmax激活函数:
Softmax激活函数可以将样本在各个分类上预测结果数值组成的一维向量O转换为在0~1上的概率,并且所有分类预测概率之和为1。
具体来说,对于在每个分类上的置信度(预测概率),Softmax激活函数的计算公式如下:
3.2对预测结果编码:
多分类问题的Softmax回归输出是一个一维向量,通过Softmax激活函数后向量中的各个值分别表示对各个分类的置信度(预测概率)。
其中向量中最高置信度oi对应的类别即为预测结果(使用独热编码实现)。
注意这里xi是一个特征。
4.损失函数:
4.1单个样本上的损失函数:
4.1.1损失函数定义:
对真实值和预测值进行合理的编码后,y和yhat都是关于类别概率的一维向量,这样就可以通过比较概率之间的区别来衡量预测损失。
4.1.2交叉熵损失函数:
注意这里的yi是一个数,而不是一个向量,这里求的是对一个样本的损失而不是一个batch的损失。
4.1.3交叉熵损失函数化简:
由于真实值向量y中仅有一个1,其余全是0,因此损失函数可以化简如下:
注意这里的yi是一个数,而不是一个向量,这里求的是对一个样本的损失而不是一个batch的损失。
此时损失函数就是当前样本的真实类别y上的预测概率yhaty的对数再取反。 即对分类问题只关心 模型在正确类别上的预测概率(yhaty) 而不关心模型在错误类别上的预测概率。
4.1整个batch上的损失函数:
训练过程中计算损失是按照整个batch计算的。
5.梯度下降算法:
5.1梯度计算:
注意这里xi是一个特征。
5.2梯度下降算法公式:
6.训练过程举例:******
以十分类模型的一次训练过程为例:
1.获取一个batch,里面包含batch_size张图片。
2.将batch_size张图片展成一维(例如24×24的图片展成784),获得输入维度为:batch_size×784×1(图片数×特征维度[784×1])。
3.参数W维度计算为784×10,参数b维度计算为10×1。
4.每张图片分别输入来计算各个分类j上的预测值yj=wij·xij+bj(其中i表示特征,i从1遍历到784),最终十个yj组合成输出维度1×10。
5.整个batch中的输出组合成维度batch_size×10。
6.使用softmax回归将输出映射成概率,维度为batch_size×10,并且每行概率之和为1。
7.使用交叉熵损失函数计算batch中所有图片的概率损失,并取均值。
7.计算各个参数wmn、bn关于损失函数的梯度。
8.反向传播算法修改参数值。
9.输入下一个batch进行训练。
三、底层代码实现:
import torch
from IPython import display
from d2l import torch as d2l
# 1.获取数据集,并返回一个指定batch大小的DataLoader
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
num_inputs = 784# 由于softmax回归输入要求是向量,所以对于24×24的图片,应该拉长成784的向量作为输入
num_outputs = 10# 10分类问题,输出为长度为10的向量,里面记录样本在各个类别上的预测概率
# 2.初始化模型参数值
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)# 均值为0方差为1,形状为784×10(784个特征,每个特征都作为10个类别的输入)
b = torch.zeros(num_outputs, requires_grad=True)# 形状为10×1,因为10个类别的预测都有一个
# 3.激活函数
def softmax(X):
X_exp = torch.exp(X)
partition = X_exp.sum(1, keepdim=True)
return X_exp / partition
# 4.交叉熵损失函数
def cross_entropy(y_hat, y):
return -torch.log(y_hat[range(len(y_hat)), y])
# 5.梯度下降算法
lr = 0.1
def updater(batch_size):
return d2l.sgd([W, b], lr, batch_size)
# 6.模型架构,输出每行概率之和为1的矩阵
def net(X):
return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)
# 7.计算batch中分类正确的样本数
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())
# 8.计算每个epoch模型精度
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]
def evaluate_accuracy(net, data_iter):
"""计算在指定数据集上模型的精度。"""
if isinstance(net, torch.nn.Module):
net.eval()
metric = Accumulator(2)
for X, y in data_iter:
metric.add(accuracy(net(X), y), y.numel())
return metric[0] / metric[1]
# 9.一次epoch训练
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.backward()
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]
# 10.训练过程
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):
"""训练模型"""
for epoch in range(num_epochs):
train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
test_acc = evaluate_accuracy(net, test_iter)
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
# 11.绘制训练过程图像
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)
# 训练
num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)
# 预测
def predict_ch3(net, test_iter, n=6):
"""预测标签(定义见第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])
predict_ch3(net, test_iter)
四、Pytorch版代码:
import torch
from torch import nn
from d2l import torch as d2l
# 1.获取训练集和测试集的DataLoader迭代器
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
# 2.定义网络模型
num_inputs = 784# 由于softmax回归输入要求是向量,所以对于24×24的图片,应该拉长成784的向量作为输入
num_outputs = 10# 10分类问题,输出为长度为10的向量,里面记录样本在各个类别上的预测概率
net = nn.Sequential(
nn.Flatten(),# 将输入展平
nn.Linear(num_inputs, num_outputs)# 线性全连接层
)
# 3.初始化参数。这个函数检查m是否是一个线性层(nn.Linear),如果是,则使用正态分布(均值为0,标准差为0.01)初始化该层的权重参数
def init_weights(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, std=0.01)
net.apply(init_weights)
# 4.交叉熵损失函数
loss = nn.CrossEntropyLoss()
# 5.梯度下降算法
trainer = torch.optim.SGD(net.parameters(), lr=0.1)
# 6.训练过程
num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)