在之前的线性回归中目的是预测一个线性的数值通常是标量,
softmax回归则是输出多个类别的logit 通过softmax函数转化为每个类别的概率,进行分类

softmax函数


其中
e保证函数可导且概率不为负,求和保证概率之和为1

小批量样本的矢量化

O=XW+b
X(B,D)B:batch_size;D:features_size;q:categories_size
W(D,q)
b(1,q)
这里XW+b使用了广播机制将(1,q)复制为(B,q)
O(B,q)

损失函数

softmax输出的是一个条件概率的向量:
y:=[ | P(y=j|x):在x发生的情况下是j的可能性是多少 ]

最大似然函数

X是输入特征集,Y是标签集:是独热编码
对于样本i,模型预测该样本属于标签的概率是P()
由于样本与样本之间独立,所以可以将概率相乘得到数据集的联合条件概率,即似然函数:
这里越接近1则说明模型预测的越好

最小负对数似然

由于上面的似然函数是多个概率的相乘,连乘会导致数值很小,不方便优化
使用对函数两边求对数,log(连乘)可以变成连加log(),
由于在log(x)当0<x<1时,函数为负数,为方便优化,我们计算负对数似然

从信息论基础到信息量\熵\交叉熵\交叉熵损失

信息量:=用来衡量一件事情有多”意外”,如果一件事情总是发生,则它没有带来信息量,概率越低,信息量越大:不常见的事情带来更大的信息量,反之也是
:,即信息量的加权平均,反映从一个分布中抽取一个事件所带来的信息量.可以想象为“知道真实概率的人所经历的惊异程度”
交叉熵:在知道真实分布为P(x),预测分布为Q(x)来编码信息时,所需要的平均信息量,在这里可以理解为对模型预测的惩罚,P(x)越接近Q(x),则交叉熵越小,反之越大,==可以想象为“主观概率为Q的观察者在看到根据概率P生成的数据时的预期惊异”==

交叉熵损失:由于这是个分类问题,真实分布只有一个为1,其余为0,例如
一个样本为[鸡,狗,羊]的真实概率为[0,1,0],即,该样本是狗.
那么该样本的求和项中的i=1,i=3项为0,只剩下i=2项,所以交叉熵损失函数可以简化为

图像分类数据集

使用torchvision下载Fashion-MNIST数据集

Fashion-MINST是有10个类别的 灰度图像数据集

# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式,
#    将灰度图片的像素值从0-255归一化到0-1
#    改变数据的通道顺序(高度, 宽度, 通道)到(通道, 高度, 宽度)
trans = transforms.ToTensor()
# 获取训练集和测试集
#    FashionMINST较为常用,框架内置,导入其他数据集可以使用ImageFolder
#    root指定数据集所在的文件夹路径
#    transform指定预处理的方法
#    train指定训练集还是测试集
#    download设置数据是否下载,如果root中没有数据则自动下载,false则在没有数据时报错
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)
#该数据集包括10个类别,每个类别6000个训练集,1000个测试集
len(mnist_train), len(mnist_test)
#输出
(60000, 10000)
#这里获取第一个数据的第一个图片,[0][1]则为对应的label
mnist_train[0][0].shape
#输出
torch.Size([1, 28, 28])

由于类别以数字分类,这里使用一个函数获取对应数字的标签名称

# 输入[1,5]则输出['t-shirt','coat']
def get_fashion_mnist_labels(labels):  #@save
    """返回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]

使用Dataloader()从数据集随机抽取批量大小的数据

#num_worker设置使用的进程数
batch-size = 256
train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True,
                             num_workers=4)

SoftMax回归的简洁实现

初始化参数

batch_size = 256  
num_inputs = 784  
num_outputs = 10
num_epochs = 20
def get_dataloader_workers():  
    """设置进程数"""  
    return 0

获取Fashion MINST 数据集

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()))  
# 获取训练集和测试集的迭代器
train_iter, test_iter = load_data_fashion_mnist(batch_size)

创建神经网络

# 创建神经网络  
net = nn.Sequential(  
    nn.Flatten(),  
    nn.Linear(num_inputs, num_outputs),  
)

初始化权重参数

def init_weights(m):  
    """初始化权重"""  
    if type(m) == nn.Linear:  
        nn.init.normal_(m.weight, std=0.01)# 均值mean默认0.标准差0.01
  
# 应用权重  
net.apply(init_weights)

设置损失计算方法

# 使用交叉熵损失  
# reduction-->如何对损失值进行聚合 none sum meanloss
nn.CrossEntropyLoss(reduction='none')

设置优化方法

# 使用SGD梯度下降优化器 学习率为0.1  
trainer = torch.optim.SGD(net.parameters(), lr=0.1)

定义评估方法

def evaluate_accuracy(net, data_iter):  
    # 模型切换到评估模式  
    net.eval()  
    acc, num_samples = 0, 0  
    with torch.no_grad():  # 禁用梯度计算  
        for X, y in data_iter:  
            X, y = X.to('cpu'), y.to('cpu')  # 改为 'cuda' 如果有 GPU            y_hat = net(X)  
            acc += (y_hat.argmax(dim=1) == y).sum().item()  
            num_samples += y.shape[0]  
    return acc / num_samples

定义训练方法

def train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer):  
    # 切换到训练模式,eval()为测试模式  
    net.train()  
    for epoch in range(num_epochs):  
        # 每个轮次开始时初始化数据  
        train_loss, train_acc, num_samples = 0, 0, 0  
        for X, y in train_iter:  
            # 将数据移动到 GPU(如果可用)  
            X, y = X.to('cpu'), y.to('cpu')  # 改为 'cuda' 如果你有 GPU  
            # 前向传播  
            y_hat = net(X)  
            # 计算损失  
            l = loss(y_hat, y).sum()  
  
            # 清除梯度  
            trainer.zero_grad()  
            # 反向传播  
            l.backward()  
            # 优化器更新参数  
            trainer.step()  
  
            # 累计训练损失和正确预测数量  
            train_loss += l.item()  
            train_acc += (y_hat.argmax(dim=1) == y).sum().item()  
            num_samples += y.shape[0]  
  
        # 计算测试集准确率  
        test_acc = evaluate_accuracy(net, test_iter)  
  
        print(f'epoch {epoch + 1}, loss {train_loss / num_samples:.4f}, '  
              f'train acc {train_acc / num_samples:.4f}, test acc {test_acc:.4f}')

开始训练

train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

输出

epoch 1, loss 91.0865, train acc 0.6838, test acc 0.7958
epoch 2, loss 48.5813, train acc 0.7607, test acc 0.8081
epoch 3, loss 46.8508, train acc 0.7708, test acc 0.8207
epoch 4, loss 43.4931, train acc 0.7793, test acc 0.8028
epoch 5, loss 39.7991, train acc 0.7850, test acc 0.8100
epoch 6, loss 41.8360, train acc 0.7812, test acc 0.7854
epoch 7, loss 38.8658, train acc 0.7934, test acc 0.8395
epoch 8, loss 36.4773, train acc 0.7935, test acc 0.7982
epoch 9, loss 38.6046, train acc 0.7927, test acc 0.8193
epoch 10, loss 36.8179, train acc 0.7929, test acc 0.8390
epoch 11, loss 35.7401, train acc 0.7977, test acc 0.8211
epoch 12, loss 37.6420, train acc 0.7981, test acc 0.8024
epoch 13, loss 35.7540, train acc 0.8003, test acc 0.8206
epoch 14, loss 35.4840, train acc 0.7990, test acc 0.8049
epoch 15, loss 36.7229, train acc 0.7934, test acc 0.8259
epoch 16, loss 34.4000, train acc 0.8032, test acc 0.8262
epoch 17, loss 37.9761, train acc 0.7995, test acc 0.8340
epoch 18, loss 34.0396, train acc 0.8033, test acc 0.7949
epoch 19, loss 35.5815, train acc 0.8002, test acc 0.8350
epoch 20, loss 35.2162, train acc 0.8036, test acc 0.8316