news 2026/5/9 1:23:36

神经网络和深度学习 第三周:浅层神经网络 课后作业和代码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
神经网络和深度学习 第三周:浅层神经网络 课后作业和代码

结果并不优秀,只有58%左右的准确率。

而本周我们了解了浅层神经网络的各部分原理,知道了其如何提高拟合效果,现在便延续上一周的内容,再次在这个数据集上应用本周更新的内容,来看一看效果。

2.1 逻辑回归模型代码

先回看一下之前的模型代码:

class LogisticRegressionModel(nn.Module):

# 类继承自nn.Module,是 PyTorch 所有模型的基类

#初始化方法

def __init__(self):

super().__init__() #父类初始化,用于注册子模块等,涉及源码,这里当成固定即可。

self.flatten = nn.Flatten() #把张量后三维展平为一维(通道C*高H*宽W)

self.linear = nn.Linear(128 * 128 * 3, 1) # 输入是128x128x3,输出1个加权和

# nn.Linear接受的是二维输入[batch_size, features],这里是[32,128 * 128 * 3]

# 但Linear层不需要在参数里写 batch 维度,它内部会自动处理批量输入,只关心每个样本的特征数和每个样本输出的维度,这也是广播机制的应用。

self.sigmoid = nn.Sigmoid() #激活函数

#向前传播方法

def forward(self, x):

# 现在,x的维度是[32,3,128,128]

x = self.flatten(x) #1.展平

# 现在,x的维度是[32,128 * 128 * 3]

x = self.linear(x) #2.过线性组合得到加权和

# 现在,x的维度是[32,1]

x = self.sigmoid(x) #3.过激活函数得到输出

# 现在,x的维度是[32,1]

return x

虽然注释较多,但其原理并不复杂,我们看向前传播函数,实际上一次正向传播只有三步显式计算。

2.2 浅层神经网络模型代码

class ShallowNeuralNetwork(nn.Module):

def __init__(self):

super().__init__()

self.flatten = nn.Flatten() #

# 隐藏层:输入128*128*3维特征,输出4个神经元

self.hidden = nn.Linear(128 * 128 * 3, 4)

# 隐藏层激活函数:ReLU,作用是加入非线性特征

self.ReLU = nn.ReLU()

# 输出层:将隐藏层的 4 个输出映射为 1 个加权和

self.output = nn.Linear(4, 1)

self.sigmoid = nn.Sigmoid()

# 前向传播方法

def forward(self, x):

# 输入 x 的维度为 [32, 3, 128, 128]

x = self.flatten(x)

# 展平后 x 的维度为 [32, 128 * 128 * 3]

x = self.hidden(x)

# 通过隐藏层线性变换后,x 的维度为 [32, 4]

x = self.ReLU(x)

# 经过 ReLU 激活后形状不变,仍为 [32, 4]

x = self.output(x)

# 通过输出层得到加权和,形状变为 [32, 1]

x = self.sigmoid(x)

# 经过 sigmoid 激活后得到 0~1 的概率值,形状仍为 [32, 1]

return x

这就是设置了四个隐藏层神经元的浅层神经网络的代码,可以发现,在Pytorch的内置方法帮助下,我们只不过相对逻辑回归改动了几行代码,便实现了从逻辑回归到浅层神经网络进行二分类的效果。

2.3 验证模型优化

现在,我们依旧使用上周的代码,只将模型替换为浅层神经网络而不改变其他任何部分(完整代码附在文末)。

先来看一张测试结果:

Pasted image 20251021100322

如果只看这两张图的对比,可能会想,只是简单的扩大一下网络的规模就实现了准确率的提升,确实差别展示的非常明显。

但可惜,事实并非如此,实际上,图中右侧的结果只是多次运行结果中最好的一次,几乎不可控,就像抽卡游戏里非常小概率的金卡一样。而实际上,最终测试的准确率依旧在50%到60%之间徘徊。

那问题又来了,那增加隐藏层就一点作用也没有吗?

答案当然是否定的,我们再来看一组对比:

Pasted image 20251021102101

我们观察两幅图像,会很明显的发现区分。

在逻辑回归的图像中,准确率的曲线几乎看不出幅度,而在浅层神经网络中,它甚至已经和损失曲线相相交了。

回看刚刚每轮的损失,我们会发现,造成这种差别的原因是:准确率只在0到1间波动,而逻辑回归的平均损失却在1-10这个量级内,相比之下,浅层神经网络的平均损失已经降到了0到1之间。

逻辑回归的损失在数值高的情况下,相比浅层神经网络的波动也较大,而浅层神经网络的下降趋势几乎成了一条直线。

而这是因为逻辑回归因为模型太简单、只能学习线性关系,训练时更容易在高损失区震荡;而浅层神经网络具备初步非线性表达能力,能更稳定地逼近最优解,损失下降更平滑、更接近直线。

从这方面来看,增加隐藏层又有其提升。

可是还有一个问题,损失函数是用来反向传播更新参数从而提高拟合效果,提高准确率的,那现在损失变小准确率不提升是为什么?

在这次代码实践中,我们重点讨论一下这个问题。

2.4 为什么损失减少不增加准确率?

虽然损失函数的值在不断下降,但准确率却没有显著提高,这并不矛盾,我们来细究一下:

首先,损失函数本质上衡量的是模型输出概率与真实标签的差距,而准确率衡量的是最终分类结果是否正确,两者关注的维度不同。

当模型预测的概率逐渐接近真实标签(例如从 0.49 提高到 0.51 或从 0.4 提高到 0.6),损失会明显下降,但是只要这些概率在 0.5 的分类阈值附近徘徊,模型的预测仍可能被判定为错误,于是准确率就不会显著改变。

更进一步来说,在模型训练初期,损失的下降主要来自于“模型更有信心地接近正确答案”,但这种接近往往还不足以跨过分类边界。

例如,对于真实标签为 1 的样本,输出可能从 0.3 提升到 0.4,这的确让损失下降了很多,但它仍被判断为 0 类,这对准确率没有任何贡献。同样地,若对于真实为 0 的样本,输出从 0.8 降为 0.6,也会使损失变小,但预测依旧是错误的类别,如图所示:

Pasted image 20251021110924

还有一种情况也会导致损失在下降,但准确率“卡住不动”,那就是数据本身太复杂。 我们可以把它想象成两类数据混在一起,不是泾渭分明地分成左右两堆,而是交错、环绕,甚至夹杂着噪声。浅层神经网络虽然比逻辑回归更聪明一点,但它的“想象力”依然有限,只能学会把整体误差变小,却画不出一条真正把两类数据明显分开的界线,就像这张图:

Pasted image 20251021111334

因此,在本次实操中我们会发现:损失下降说明模型确实在学习,但如果模型能力不足、特征没有被有效分离,或者分类阈值附近的样本占比过高,那么准确率依旧可能停留在50%到60%之间。

而如何进一步提升准确率,便需要我们继续学习,继续优化。

最后附上完整代码,依旧要强调的是,在规范流程里,我们应该根据每次验证的准确率调整超参数,最后再进行测试,只是这部分内容还在后面,我们经过系统学习后再正式引入这部分。

import torch

import torch.nn as nn

import torch.optim as optim

from torchvision import datasets, transforms

from torch.utils.data import DataLoader, random_split

import matplotlib.pyplot as plt

from sklearn.metrics import accuracy_score

transform = transforms.Compose([

transforms.Resize((128, 128)),

transforms.ToTensor(),

transforms.Normalize((0.5,), (0.5,))

])

dataset = datasets.ImageFolder(root='./cat_dog', transform=transform)

train_size = int(0.8 * len(dataset))

val_size = int(0.1 * len(dataset))

test_size = len(dataset) - train_size - val_size

train_dataset, val_dataset, test_dataset = random_split(dataset, [train_size, val_size, test_size])

train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)

val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)

test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)

class ShallowNeuralNetwork(nn.Module):

def __init__(self):

super().__init__()

self.flatten = nn.Flatten()

# 隐藏层

self.hidden = nn.Linear(128 * 128 * 3, 4) # 1 个隐藏层,4 个神经元

self.ReLU = nn.ReLU()

# 输出层

self.output = nn.Linear(4, 1)

self.sigmoid = nn.Sigmoid()

def forward(self, x):

x = self.flatten(x)

x = self.hidden(x)

x = self.ReLU(x)

x = self.output(x)

x = self.sigmoid(x)

return x

model = ShallowNeuralNetwork()

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

model.to(device)

criterion = nn.BCELoss()

optimizer = optim.SGD(model.parameters(), lr=0.01)

epochs =10

train_losses = []

val_accuracies = []

for epoch in range(epochs):

model.train()

epoch_train_loss = 0

for images, labels in train_loader:

images, labels = images.to(device), labels.to(device).float().unsqueeze(1)

outputs = model(images)

loss = criterion(outputs, labels)

optimizer.zero_grad()

loss.backward()

optimizer.step()

epoch_train_loss += loss.item()

avg_train_loss = epoch_train_loss / len(train_loader)

train_losses.append(avg_train_loss)

model.eval()

val_true, val_pred = [], []

with torch.no_grad():

for images, labels in val_loader:

images = images.to(device)

outputs = model(images)

preds = (outputs.cpu().numpy() > 0.5).astype(int).flatten()

val_pred.extend(preds)

val_true.extend(labels.numpy())

val_acc = accuracy_score(val_true, val_pred)

val_accuracies.append(val_acc)

print(f"轮次: [{epoch + 1}/{epochs}], 训练损失: {avg_train_loss:.4f}, 验证准确率: {val_acc:.4f}")

plt.rcParams['font.sans-serif'] = ['SimHei']

plt.rcParams['axes.unicode_minus'] = False

plt.plot(train_losses, label='训练损失')

plt.plot(val_accuracies, label='验证准确率')

plt.title("训练损失与验证准确率随轮次变化图")

plt.xlabel("训练轮次(Epoch)")

plt.ylabel("数值")

plt.legend()

plt.grid(True)

plt.show()

model.eval()

y_true, y_pred = [], []

with torch.no_grad():

for images, labels in test_loader:

images = images.to(device)

outputs = model(images)

preds = (outputs.cpu().numpy() > 0.5).astype(int).flatten()

y_pred.extend(preds)

y_true.extend(labels.numpy())

acc = accuracy_score(y_true, y_pred)

print(f"测试准确率: {acc:.4f}")

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 0:41:15

免费复古游戏模拟器终极指南:5分钟开启怀旧游戏之旅 [特殊字符]

想要重温童年经典游戏却不知从何入手?这款开源复古游戏模拟器让你无需安装任何软件,直接在浏览器中体验DOS、Windows 95/98等经典游戏。作为非盈利的数字文化保存项目,它通过网页模拟技术让那些珍贵的游戏作品重新焕发生机,无论你…

作者头像 李华
网站建设 2026/5/9 1:33:05

Windows 11完整安装指南:轻松绕过硬件限制实现旧电脑升级

Windows 11完整安装指南:轻松绕过硬件限制实现旧电脑升级 【免费下载链接】MediaCreationTool.bat Universal MCT wrapper script for all Windows 10/11 versions from 1507 to 21H2! 项目地址: https://gitcode.com/gh_mirrors/me/MediaCreationTool.bat 还…

作者头像 李华
网站建设 2026/5/8 11:16:09

[数据分析/BI] Microsoft Power BI 使用指南

1 概述:Power BI什么是 Power BI?Power BI 是由微软发布的一款商业智能(BI)软件,或者说数据分析工具。Microsoft Power BI 是一个完整的报表解决方案,通过开发工具和联机平台提供数据准备、数据可视化、分发和管理。P…

作者头像 李华
网站建设 2026/5/9 2:35:38

如何用CUDA_VISIBLE_DEVICES优化AI模型训练

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容: 创建一个Python脚本,演示如何使用CUDA_VISIBLE_DEVICES环境变量在多GPU系统中分配计算资源。脚本应包含以下功能:1) 检测可用GPU设备数量;2) 允许…

作者头像 李华
网站建设 2026/5/9 1:44:26

5分钟搞定DBeaver数据丢失防护:三重复合保险机制详解

5分钟搞定DBeaver数据丢失防护:三重复合保险机制详解 【免费下载链接】dbeaver 项目地址: https://gitcode.com/gh_mirrors/dbe/dbeaver 你是否曾在深夜加班时,因DBeaver插件崩溃而丢失了整晚的SQL成果?据用户反馈统计,数…

作者头像 李华
网站建设 2026/5/9 2:02:01

用DB-GPT快速构建数据驱动的应用原型

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容: 创建一个快速原型开发工具,集成DB-GPT技术,允许用户通过自然语言描述快速生成数据模型和API接口。工具应支持实时预览和修改,帮助开发者快速验证…

作者头像 李华