Pytorch -----多分类问题 (Softmax Classifier)原理和代码实现,损失函数NLLLoss和CrossEntropyLossr的用法和区别。

这篇博客介绍了如何使用深度学习解决多分类问题,以MNIST手写数字识别为例。通过神经网络模型,利用softmax函数确保概率分布和训练过程中使用交叉熵损失函数。代码实现包括数据预处理、模型构建、训练和测试,最终在测试集上达到97%的准确率。
摘要由CSDN通过智能技术生成

之前的分类都是只有两个分类,是或者不是。

今天学一下多分类问题,比如下面这个图。识别这个图中的数字,当输出结果的时候 有 0-9 十个分类结果。

在这里插入图片描述
比如第一个数字5,经过训练输出可能是 P(Y=5) = 0.9 ,即理解为等于5的概率是90%。
但这样有一个问题,如果这个数字特别模糊,可能出现的情况是P(Y=1) = 0.8 ,P(Y=2) = 0.8 , P(Y=5) = 0.9 ,也就是说,这个数字是1的概率是0.8,是2的概率也是0.8,是5的概率是0.9。这样的结果并不利于训练结果的分类。
所以我们让所有结果符合概率要求,即全部概率相加 = 1。输出一个分布。

用于训练的神经网络中间依旧使用 Sigmoid,最后输出10个概率的分布使用 softmax。
在这里插入图片描述
Softmax 函数计算公式:
在这里插入图片描述

这里的 Zi 就是最后一层线性层(上图蓝色的层)的输出值,给softmax层的值。线性层输出的值是一般值,还不是概率值。经过sotfmax层之后才能变成概率值。
分子上面的e的指数形式。必然大于0.
分子下面的求和。就是e的每一项都除以全部的e的Zi次方的和。.
在这里插入图片描述
等价于
在这里插入图片描述
显然这样操作之后,每一项单独的都除以全部总和,保证了结果的值之和为1.

损失函数:
在这里插入图片描述
计算过程代码实现:
在这里插入图片描述
这里的y就是值真实值,z就是 最后一层的线性层给Softmax的值。
先用 softmax 的公式算出来 y(预测值)。
然后用上面的损失函数算出来y的损失值。

在Pytorch中使用:
这条函数包括了上面的softmax算预测值和算损失值的全部过程。
在使用CrossEntropyLossr的时候,最后一层线性层不要做非线性变换,就是乘以那个α 或 sigmoid激活函数。这条函数(交叉熵)会自动帮你激活。
在这里插入图片描述
关于上面的整体流程可以用下面这张图表示:
在这里插入图片描述
课上老师问了一个问题 就是 两个损失函数 NLLLoss和CrossEntropyLossr的区别。

其实看到这应该能知道,CrossEntropyLossr交叉熵损失函数是整体计算的。就是从最后一层的线性层开始计算到最后,里面包含了softmax.在这里插入图片描述
而NLLLoss仅仅做了最后一步:在这里插入图片描述
NLLLoss 对数似然损失函数(log-likehood loss function) :
在这里插入图片描述
其中,ak表示第k个神经元的输出值,yk表示第k个神经元对应的真实值,取值为0或1。

CrossEntropyLossr = softmax + NLLLoss

回到刚开始的那个数字图像。拿出第一个数字。
该图像由28*28的矩阵像素点构成。颜色深浅由0-255表示,映射到0-1.每个矩阵中的值为0-1,表示该点的颜色的深浅。
在这里插入图片描述

代码实现过程还是之前的四步,
1.数据准备 2.设计模型类 3.选择优化器和损失函数 4. 循环训练

其中用到的包:
在这里插入图片描述

用于图像的处理。
在这里插入图片描述
不在使用之前的sigmoid了 使用 relu()作为激活函数。
在这里插入图片描述
数据准备:

在这里插入图片描述
datasets.MNIST参数:

train=True 代表我们读入的数据作为训练集(如果为true则从training.pt创建数据集,否则从test.pt创建数据集)

download=True则是当我们的根目录(root)下没有数据集时,便自动下载。

python3使用pillow处理图片,
将输入进来的图像(0-255像素值 28 * 28)值变为图像张量(映射0-1像素值 1 * 28 * 28)。
1 * 28 * 28 通道 * 宽 * 高

灰白图成为单通道,
读进来的为 W * H * C 变为 C * W * H之后交给Pytorch处理。
在这里插入图片描述
下面这个函数就是做变换处理的。
在这里插入图片描述
下面这个函数后面的两个参数, 均值(mean) 标准差(std)。
这俩值并不是固定的,而是根据图像算出来的,不过这个图比较经典,所以就用这俩最好。
在这里插入图片描述
模型训练:
在这里插入图片描述
下图表示输入N个样本,1维 28 *28 。
在这里插入图片描述

全连接神经网络要求输入样本二维矩阵。
使用view函数。改变张量形状。
变成二维矩阵。第一个数为 -1 表示自动计算。
在这里插入图片描述
所以第一层拿到的数据是N * 784的矩阵

注意,最后一层 self.15(x) 不需要relu()函数激活,直接交给后面的softmax。

优化器和损失函数:
在这里插入图片描述

本次将训练和测试封装到函数中:
训练:
在这里插入图片描述
测试:
在这里插入图片描述
其中:
下图中的代码不会计算后面的梯度,因为已经是测试数据了。就是要测试前面训练好的权重W,所以不需要再次计算梯度。
在这里插入图片描述
首先拿测试数据去计算预测值,
然后取出来预测值每一行中的最大值,因为最大值才是我们要的那个最大概率的分类嘛。dim表示沿着那个维度去找。
ps:想一下!~组成二维数组中的一维数组是0号维度,一维数组中的值是1号维度。

torch.max:
这里拿到的预测值 每一行都对应10个分类,这10个分类就是该样本对应0-9的概率, 我们要拿到最大的那个概率和其对应的下标。
dim=1 就是从每一个一维数组中找到最大的值和其对应下标。
返回值是两个,最大值是多少,其下标是多少。
在这里插入图片描述
下面的 labels就是我们的初始的y数据,应该是一个N *1的矩阵,N就是一共有多少个样本。所以size就是元组(N,1).去0号元素,自然拿到的就是样本数。
在这里插入图片描述

下面的 == 号 就是在比较真实值和预测值之间的差距,相等就是1不相等就是0,然后求和 吧数值拿出来。
等所有的数都跑完了,看正确的数除以总数,就能得到正确率了。
在这里插入图片描述

主函数:
十轮训练+测试
在这里插入图片描述

完整代码:

import torch
from torch import optim
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F

batch_size = 64
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

train_dataset = datasets.MNIST(root='../dataset/mnist/', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
test_dataset = datasets.MNIST(root='../dataset/mnist/', train=False, download=True, transform=transform)
test_loader = DataLoader(test_dataset, shuffle=False, batch_size=batch_size)


# 模型类设计
class DiabetesDataset(torch.nn.Module):
    def __init__(self):
        super(DiabetesDataset, self).__init__()
        self.l1 = torch.nn.Linear(784, 512)
        self.l2 = torch.nn.Linear(512, 256)
        self.l3 = torch.nn.Linear(256, 128)
        self.l4 = torch.nn.Linear(128, 64)
        self.l5 = torch.nn.Linear(64, 10)

    def forward(self, x):
        x = x.view(-1, 784)  # -1就是自动获取mini_batch
        x = F.relu(self.l1(x))
        x = F.relu(self.l2(x))
        x = F.relu(self.l3(x))
        x = F.relu(self.l4(x))
        return self.l5(x)  # 最后一层不做激活,不进行非线性变换


model = DiabetesDataset()

# 损失函数
criterion = torch.nn.CrossEntropyLoss()
# 优化器
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)


def train(epoch):
    runing_loss = 0.0
    for i, data in enumerate(train_loader):
        x, y = data
        # 清零 正向传播  损失函数  反向传播 更新
        optimizer.zero_grad()
        y_pre = model(x)
        loss = criterion(y_pre, y)
        loss.backward()
        optimizer.step()
        runing_loss += loss.item()
# 每轮训练一共训练1W个样本,这里的runing_loss是1W个样本的总损失值,要看每一个样本的平均损失值, 记得除10000
    print("这是第 %d轮训练,当前损失值 %.5f" % (epoch+1, runing_loss/10000))



def test(epoch):
    correct = 0
    total = 0
    with torch.no_grad():
        for data in test_loader:
            x, y = data
            pre_y = model(x)
            # 这里拿到的预测值 每一行都对应10个分类,这10个分类都有对应的概率,
            # 我们要拿到最大的那个概率和其对应的下标。
            j, pre_y = torch.max(pre_y.data, dim=1)  # dim = 1 列是第0个维度,行是第1个维度

            total += y.size(0)  # 统计方向0上的元素个数 即样本个数
            correct += (pre_y == y).sum().item()  # 张量之间的比较运算
    print("第%d轮测试结束,当前正确率:%d %%" % (epoch+1,correct / total * 100))


if __name__ == '__main__':
    for epoch in range(10):
        train(epoch)
        test(epoch)

看看结果:

这是第 1轮训练,当前损失值 0.104401轮测试结束,当前正确率:89 %
这是第 2轮训练,当前损失值 0.025632轮测试结束,当前正确率:94 %
这是第 3轮训练,当前损失值 0.016363轮测试结束,当前正确率:95 %
这是第 4轮训练,当前损失值 0.011994轮测试结束,当前正确率:96 %
这是第 5轮训练,当前损失值 0.009315轮测试结束,当前正确率:96 %
这是第 6轮训练,当前损失值 0.007456轮测试结束,当前正确率:97 %
这是第 7轮训练,当前损失值 0.006037轮测试结束,当前正确率:97 %
这是第 8轮训练,当前损失值 0.004978轮测试结束,当前正确率:97 %
这是第 9轮训练,当前损失值 0.004009轮测试结束,当前正确率:97 %
这是第 10轮训练,当前损失值 0.0033310轮测试结束,当前正确率:97 %
好的,首先你需要安装几个Python包:librosa、numpy、torch、torchvision。 然后,你可以按照以下代码来提取音频信号的mel语谱图特征: ```python import librosa import numpy as np def extract_mel_spectrogram(file_path, n_mels=128, n_fft=2048, hop_length=512): # 读取音频文件 y, sr = librosa.load(file_path, sr=22050) # 计算mel语谱图 S = librosa.feature.melspectrogram(y=y, sr=sr, n_fft=n_fft, hop_length=hop_length, n_mels=n_mels) # 将幅度转换为对数刻度 log_S = librosa.power_to_db(S, ref=np.max) # 归一化特征 norm_S = (log_S - np.mean(log_S)) / np.std(log_S) return norm_S ``` 这个函数将返回一个大小为 (n_mels, T) 的ndarray,其中n_mels是要提取的mel滤波器的数量,T是时间步数。 接下来,你可以按照以下代码来构建CNN-LSTM-Attention模型: ```python import torch.nn as nn class Model(nn.Module): def __init__(self, n_mels=128, n_classes=4): super(Model, self).__init__() # CNN self.conv = nn.Sequential( nn.Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.BatchNorm2d(32), nn.ReLU(), nn.MaxPool2d(kernel_size=(2, 2)), nn.Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.BatchNorm2d(64), nn.ReLU(), nn.MaxPool2d(kernel_size=(2, 2)), ) # LSTM self.lstm = nn.LSTM(input_size=n_mels//4 * 64, hidden_size=128, bidirectional=True, batch_first=True) # Attention self.attention = nn.Sequential( nn.Linear(128 * 2, 64), nn.Tanh(), nn.Linear(64, 1), nn.Softmax(dim=1) ) # 分类器 self.classifier = nn.Linear(128 * 2, n_classes) def forward(self, x): # CNN x = x.unsqueeze(1) x = self.conv(x) x = x.view(x.size(0), -1, x.size(3)) # LSTM x, _ = self.lstm(x) # Attention alpha = self.attention(x).transpose(1, 2) x = alpha @ x x = x.squeeze(1) # 分类器 x = self.classifier(x) return x ``` 这个模型有三个部分:CNN、LSTM和Attention。首先,CNN用于提取特征,LSTM用于处理时序信息,Attention用于对不同时间步的特征进行加权平均。最后,分类器用于进行分类。 最后,你可以按照以下代码来训练和测试模型: ```python import torch import torch.optim as optim from torch.utils.data import DataLoader, Dataset # 数据集 class MyDataset(Dataset): def __init__(self, file_paths, labels): self.file_paths = file_paths self.labels = labels def __getitem__(self, index): file_path = self.file_paths[index] label = self.labels[index] x = extract_mel_spectrogram(file_path) x = torch.from_numpy(x).float() y = torch.tensor(label) return x, y def __len__(self): return len(self.file_paths) # 训练集和测试集 train_file_paths = [...] # 训练集音频文件路径列表 train_labels = [...] # 训练集标签列表 train_dataset = MyDataset(train_file_paths, train_labels) train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True) test_file_paths = [...] # 测试集音频文件路径列表 test_labels = [...] # 测试集标签列表 test_dataset = MyDataset(test_file_paths, test_labels) test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False) # 模型和优化器 model = Model() optimizer = optim.Adam(model.parameters(), lr=1e-3) # 训练 n_epochs = 10 for epoch in range(n_epochs): for i, (x, y) in enumerate(train_loader): optimizer.zero_grad() outputs = model(x) loss = nn.CrossEntropyLoss()(outputs, y) loss.backward() optimizer.step() if i % 100 == 0: acc = (outputs.argmax(1) == y).float().mean().item() print(f"Epoch {epoch}, batch {i}, train loss: {loss.item():.4f}, train acc: {acc:.4f}") # 测试 model.eval() with torch.no_grad(): test_loss = 0. test_acc = 0. for x, y in test_loader: outputs = model(x) test_loss += nn.CrossEntropyLoss()(outputs, y).item() * x.size(0) test_acc += (outputs.argmax(1) == y).float().sum().item() test_loss /= len(test_dataset) test_acc /= len(test_dataset) print(f"Epoch {epoch}, test loss: {test_loss:.4f}, test acc: {test_acc:.4f}") model.train() ``` 这个训练循环会训练模型10个epoch,每个epoch会先用训练集训练模型,然后用测试集测试模型。在每个epoch结束后,会输出训练集和测试集的平均损失和准确率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深度不学习!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值