当前位置: 首页 > news >正文

深度学习——神经网络简单实践(在乳腺癌数据集上的小型二分类示例)

神经网络在乳腺癌数据集上的小型二分类示例 — 逐行详解

概览(一句话)

用 PyTorch 实现一个非常基础的二层全连接神经网络(SimpleNN)对 sklearn 的 load_breast_cancer 数据做二分类:先用 StandardScaler 标准化数据,然后把 NumPy 数据转成 torch.Tensor,用 BCELoss + Adam 在全部训练数据上训练若干 epoch,最终在测试集上预测并计算准确率。

流程:

开始程序

加载数据集 (sklearn.datasets.load_breast_cancer)

划分训练集 / 测试集 (train_test_split)

数据标准化 (StandardScaler.fit_transform / transform)

转为 torch.tensor (float32) → 并调整 y 的形状为 (N,1)

选择运行设备 (CPU / GPU: torch.device)

构建神经网络模型 (继承 nn.Module)
┌───────────────────────────────────────┐
│ 输入层 (nn.Linear(in_dim, 16))        │
│ → ReLU 激活 (nn.ReLU())               │
│ → 输出层 (nn.Linear(16, 1))           │
│ → Sigmoid 激活 (nn.Sigmoid())         │
└───────────────────────────────────────┘

定义损失函数 (nn.BCELoss)

定义优化器 (optim.Adam(model.parameters(), lr=0.01))

================= 训练循环 (epoch) =================

├─ model.train()  → 进入训练模式

├─ 前向传播:y_pred = model(X_train_t)

├─ 计算损失:loss = criterion(y_pred, y_train_t)

├─ 反向传播:loss.backward()

├─ 更新参数:optimizer.step()

└─ 清空梯度:optimizer.zero_grad()

================= 训练结束 =================

进入测试阶段:model.eval()

前向传播得到预测概率:y_prob = model(X_test_t)

将概率转为类别 (≥0.5 → 1, <0.5 → 0)

计算准确率 (accuracy_score)

输出结果 (测试集准确率)

结束程序


0)先看完整代码

import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score# ========== 0. 判断设备 ==========
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("当前设备:", device)# ========== 1. 数据 ==========
X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test  = scaler.transform(X_test)X_train_t = torch.tensor(X_train, dtype=torch.float32).to(device)
y_train_t = torch.tensor(y_train, dtype=torch.float32).view(-1,1).to(device)
X_test_t  = torch.tensor(X_test, dtype=torch.float32).to(device)
y_test_t  = torch.tensor(y_test, dtype=torch.float32).view(-1,1).to(device)# ========== 2. 模型 ==========
class SimpleNN(nn.Module):def __init__(self, in_dim):super().__init__()self.fc1 = nn.Linear(in_dim, 16)    # 第一层self.relu = nn.ReLU()               # 激活函数self.fc2 = nn.Linear(16, 1)         # 第二层self.sigmoid = nn.Sigmoid()         # 输出层激活def forward(self, x):x = self.fc1(x)        # 输入 -> 全连接层1x = self.relu(x)       # ReLU激活x = self.fc2(x)        # 全连接层2x = self.sigmoid(x)    # Sigmoid输出(二分类概率)return xmodel = SimpleNN(in_dim=X_train.shape[1]).to(device)
criterion = nn.BCELoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)# ========== 3. 训练 ==========
for epoch in range(10):model.train()optimizer.zero_grad()y_pred = model(X_train_t)loss = criterion(y_pred, y_train_t)loss.backward()optimizer.step()print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")# ========== 4. 测试 ==========
model.eval()
with torch.no_grad():y_prob = model(X_test_t).cpu().numpy().ravel()   # 转回CPU再转numpyy_pred = (y_prob >= 0.5).astype(int)print("测试集准确率:", accuracy_score(y_test, y_pred))

1)数据部分详解(sklearn → StandardScaler → 转为 Tensor)

torch.device("cuda" if torch.cuda.is_available() else "cpu") 判断 GPU 是否可用。

# ========== 0. 判断设备 ==========
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("当前设备:", device)

from sklearn.datasets import load_breast_cancer

  • 功能:加载一个经典的乳腺癌数据集(已内置于 scikit-learn)。返回 Bunch 或者当 return_X_y=True 时返回 (X, y)

  • 常用参数

    • return_X_y=False(默认)返回一个包含 data、target、feature_names 等字段的 Bunch 对象;

    • return_X_y=True 返回 (X, y) 两个 ndarray。

  • 数据形状(典型)X 通常是 (569, 30)y(569,),这是该内置数据集的常见规模。

StandardScaler() / scaler.fit_transform(X_train) / scaler.transform(X_test)

  • 功能:逐特征去均值并按标准差缩放(Z-score 标准化)。

  • 类参数

    • with_mean=True:是否减去均值(默认 True)。

    • with_std=True:是否除以标准差(默认 True)。

    • copy=True:是否复制数据。

  • 方法

    • fit(X):计算每个特征的均值和方差(或标准差)。

    • transform(X):用已保存的均值/方差转换新数据。

    • fit_transform(X):等同于 fit + transform 一步完成(对训练集使用)。

  • 注意:对训练集 fit,对测试集仅 transform。避免信息泄漏。

torch.tensor(...) vs torch.from_numpy(...)

在代码中你用:

X_train_t = torch.tensor(X_train, dtype=torch.float32).to(device)
y_train_t = torch.tensor(y_train, dtype=torch.float32).view(-1,1).to(device)

(.to(device) 把模型和数据都放到对应CPU或GPU上。)

  • torch.tensor(data, dtype=..., device=..., requires_grad=...)

    • 功能:从 Python 数组/NumPy 等创建一个新的 Tensor(通常会复制数据,默认 requires_grad=False)。

    • 参数

      • data:可以是 list、ndarray、其他 Tensor 等。

      • dtype:指定 dtype,如 torch.float32torch.int64 等。神经网络常用 float32

      • device'cpu''cuda:0' 等。

      • requires_grad(布尔):是否需要追踪梯度(默认 False)。

    • 注意torch.tensor 通常会复制数据(会分配新内存)。

  • torch.from_numpy(np_array)

    • 功能:从 NumPy 数组创建 Tensor,但不会复制内存(共享内存)。对大数据更高效。

    • 限制:只有当 NumPy 数组是 C-contiguous 且 dtype 可对映才可以直接共享(例如 np.float32)。

  • 建议:若数据已是 np.ndarray 并且希望避免额外拷贝,用:

    X_train_t = torch.from_numpy(X_train.astype(np.float32))
    
  • .view(-1, 1)

    • 等价于 reshape,把一维向量 shape (N,) 改为 (N,1)-1 表示自动计算该维度大小。

    • 注意:.view() 需要 contiguous 的内存,可用 .reshape().unsqueeze(1) 更稳妥(.unsqueeze(1) 在这里语义更明确:在第 1 个维度插入一个维度)。


2)模型定义详解(nn.Modulenn.Linear、激活函数等)

class SimpleNN(nn.Module):

  • PyTorch 模型的标准写法:继承自 torch.nn.Module 并实现 __init__forward(self, x)

  • super().__init__():调用父类构造器,必需以便注册子模块参数(self.fc1 等)到父类中,这样 model.parameters() 才能正确返回所有参数。

nn.Linear(in_features, out_features, bias=True)

  • 功能:全连接层(仿射变换),实现 y=xWT+by = xW^T + b(PyTorch 中权重形状为 (out_features, in_features))。

  • 参数

    • in_features:输入特征维数(本例为 in_dim)。

    • out_features:输出特征维数(本例 16 或 1)。

    • bias=True:是否包含偏置 b

  • 默认初始化

    • 权重通常使用 Kaiming/Xavier 类初始化(PyTorch 默认对 nn.Linear.weight 使用 kaiming_uniform_),偏置初始为 0。

nn.ReLU(inplace=False)

  • 功能:ReLU 激活函数,max⁡(0,x)\max(0, x)。

  • 参数

    • inplace=False:是否就地修改输入(节省内存,但可能会与反向传播/计算图某些操作冲突)。建议默认 False,除非确定可用。

nn.Sigmoid()

  • 功能:对标量输出做 S 型映射,把任意实数映射到 (0,1),适合二分类概率输出(sigmoid 将 logits 转为概率)。

  • 参数:无。

forward 里数据流(形状追踪)

假设 batch_size = Nin_dim = D

  1. 输入 x(N, D)

  2. self.fc1(x)(N, 16)(线性变换)

  3. self.relu(...)(N, 16)(非线性,不改变形状)

  4. self.fc2(...)(N, 1)(线性)

  5. self.sigmoid(...)(N, 1)(概率)


3)损失函数与优化器(nn.BCELossoptim.Adam

criterion = nn.BCELoss()

  • 功能:二元交叉熵损失(Binary Cross Entropy),当预测是概率(0~1)并且标签是 0 或 1 时使用。

  • 数学形式(单样本)

    BCE(p,y)=−[ylog⁡(p)+(1−y)log⁡(1−p)]\text{BCE}(p, y) = -\big[ y \log(p) + (1-y)\log(1-p) \big]

    reduction='mean'(默认)时对 batch 平均。

  • 参数

    • weight:可选的每个样本的权重张量(与输入同 shape),用于不平衡样本加权;

    • reduction'none'|'mean'|'sum'

  • 重要注意nn.BCELoss 假设模型输出已经经过 sigmoid 得到概率。如果你把网络的最后一层直接输出 logits(未做 sigmoid),更稳定的做法是使用 nn.BCEWithLogitsLoss()(它在数值上更稳定,内部实现合并了 sigmoid 与 BCE 的数值稳定性处理)。

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

  • 功能:Adam 优化器(自适应学习率)。

  • 参数详解

    • params:模型参数迭代器(model.parameters() 返回的生成器)。

    • lr=0.01:初始学习率(Adam 常用 1e-3,但 0.01 也可——通常需要调参)。

    • betas=(0.9, 0.999):一阶、二阶动量衰减系数(用于计算移动平均)。

    • eps=1e-8:数值稳定项(避免除零)。

    • weight_decay=0:L2 权重衰减(等价于正则化)。

    • amsgrad=False:是否使用 AMSGrad 变体。

  • :学习率对训练稳定性影响非常大。对于小网络和这类数据,lr=1e-3 是常见起点。


4)训练循环详解(逐行)

训练代码(简化形式):

# ========== 3. 训练 ==========
for epoch in range(10):model.train()optimizer.zero_grad()y_pred = model(X_train_t)loss = criterion(y_pred, y_train_t)loss.backward()optimizer.step()print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")

逐行讲解及每个函数/方法的参数与行为:

  1. for epoch in range(10):

    • 执行 10 个训练轮(epoch)。通常 epoch 数需根据验证集性能调整。

  2. model.train()

    • 功能:把模型设为“训练模式”。train() 接收一个可选参数 mode=True(默认)。

    • 影响:对某些模块(如 Dropout, BatchNorm 等)启用训练行为(比如 Dropout 会丢弃一部分神经元)。对 nn.Linearnn.ReLU 没直接影响,但仍应每次训练开始前调用以保证模式正确。

  3. optimizer.zero_grad()

    • 功能:把模型参数上的梯度清零。PyTorch 在每次 backward() 时会累积梯度(默认),因此需在新一轮梯度计算前先 zero_grad()

    • 注意点:PyTorch 1.7+ 提供 optimizer.zero_grad(set_to_none=True) 可降低内存和稍微提高性能(梯度被设为 None 而非 0)。

  4. y_pred = model(X_train_t)

    • 功能:前向传播。调用 SimpleNN.forward(X_train_t)。返回 Tensor,形状 (N,1)

    • 注意:如果数据量大,建议使用 DataLoader 批次(mini-batch)训练而不是一次性把全体训练数据传入模型(这会影响训练动态并且占用大量内存)。

  5. loss = criterion(y_pred, y_train_t)

    • 功能:计算当前 batch 的损失值(y_pred 为概率时使用 BCELoss)。

    • 输入条件

      • y_predy_train_t 形状必须可广播一致(如 (N,1)(N,1)(N,) 等)。

      • 标签 y_train_t 对于 BCELoss 应该是 float(例如 0.01.0),不是 int。你的代码将标签转换为 float32.view(-1,1),是正确的。

  6. loss.backward()

    • 功能:反向传播,计算损失对模型所有可学习参数的梯度,并把这些梯度累加到每个参数的 .grad 属性中。

    • 参数loss.backward(gradient=None, retain_graph=False, create_graph=False),通常不传参数。

      • retain_graph=True 在需要对同一计算图多次反向时使用(在常规训练中不常用)。

      • create_graph=True 若需要二阶导数,用于更高级场景。

    • 注意:在调用 backward() 前必须确保 optimizer.zero_grad() 已清空上一次的梯度(否则会累加)。

  7. optimizer.step()

    • 功能:根据参数的 .grad 值,用所选优化算法(这里是 Adam)更新模型参数。

    • 参数optimizer.step(closure=None):对于某些需要重算 loss 的优化器(LBFGS),可以传入 closure,但是 Adam 不需要。

  8. loss.item()

    • 功能:把标量 Tensor(包含在计算图里)转换成 Python 浮点数,便于打印/记录。

    • 注意:不要用 .numpy() 直接转换有 requires_grad=True 的 tensor(会报错),loss.item() 是安全做法。


5)测试 / 评估详解

代码片段:

# ========== 4. 测试 ==========
model.eval()
with torch.no_grad():y_prob = model(X_test_t).cpu().numpy().ravel()   # 转回CPU再转numpyy_pred = (y_prob >= 0.5).astype(int)print("测试集准确率:", accuracy_score(y_test, y_pred))

解释与注意:

  1. model.eval()

    • 功能:把模型设为“评估模式”(eval() 相当于 train(mode=False))。

    • 影响:关闭 Dropout(不再随机丢弃神经元),BatchNorm 使用训练时记录的 running mean/var 而不是 batch 统计量。

  2. with torch.no_grad():

    • 功能:上下文管理器,禁用梯度计算与计算图构建,节省内存与计算。

    • 用途:在验证或测试阶段应包装前向过程。也可以对单次推断使用 torch.inference_mode()(PyTorch 新增,更快更节省内存)。

  3. model(X_test_t).numpy()

    • 注意:要把 Tensor 转为 NumPy,Tensor 必须在 CPU 且不需要梯度(requires_grad=False)并且不是在 GPU。

    • 更稳妥model(X_test_t).detach().cpu().numpy() 或在 no_grad 环境下 model(...).cpu().numpy()

    • .ravel():把 (N,1) flatten 为 (N,) 方便后续与 y_test 对比。

    • 阈值 0.5:基于 sigmoid 的概率输出,通常以 0.5 作为二分类阈值,但在不平衡数据上你可能希望调整该阈值或使用 ROC/PR 曲线寻找最佳阈值。

  4. accuracy_score(y_test, y_pred)

    • 来自 sklearn.metrics,计算分类准确率(正确预测数量 / 总数)。

    • 注意y_test 应与 y_pred 形状一致;通常 y_test(N,),而 y_pred 也应为 (N,)


6)每个重要方法/类的“速查表”(signature + 一句话说明)

  • torch.tensor(data, dtype=None, device=None, requires_grad=False):从数据创建 Tensor(通常会复制)。

  • torch.from_numpy(ndarray):从 NumPy 创建 Tensor 共享内存(高效)。

  • ndarray.astype(np.float32):转换 NumPy dtype,常在转换前做以确保和 PyTorch dtype 对应。

  • Tensor.view(shape) / Tensor.reshape(shape) / Tensor.unsqueeze(dim):修改形状(view 需要 contiguous)。

  • nn.Module:模型基类,子类化并实现 forward()

  • nn.Linear(in_features, out_features, bias=True):全连接层。

  • nn.ReLU(inplace=False):ReLU 激活。

  • nn.Sigmoid():Sigmoid 激活(将 logit 转为概率)。

  • nn.BCELoss(weight=None, reduction='mean'):二元交叉熵(输入必须是概率)。

  • nn.BCEWithLogitsLoss():把 sigmoid 与 BCE 合并,数值更稳定(输入是 logits)。

  • optim.Adam(params, lr=1e-3, betas=(0.9,0.999), eps=1e-8, weight_decay=0):Adam 优化器。

  • model.train(mode=True) / model.eval():切换训练/评估模式,影响 Dropout/BatchNorm。

  • optimizer.zero_grad():把参数梯度清零。

  • loss.backward():反向传播计算梯度。

  • optimizer.step():根据梯度更新参数。

  • with torch.no_grad()::禁用梯度计算(推理/验证时使用)。

  • tensor.detach().cpu().numpy():安全地将 tensor 转为 NumPy(脱离计算图并确保在 CPU)。

  • sklearn.preprocessing.StandardScaler():标准化。

  • sklearn.model_selection.train_test_split(...):分割数据集。

  • sklearn.metrics.accuracy_score(y_true, y_pred):计算准确率。


7)总结(key takeaways)

  • 你的代码已经覆盖了模型训练的基本流程:准备数据 → 定义模型 → 损失 & 优化器 → 训练循环 → 测试评估。

  • 实务改进建议:

    • BCEWithLogitsLoss 代替 sigmoid + BCELoss(更稳定)。

    • 使用 DataLoader 做 mini-batch 与 shuffle=True

    • 支持 GPU(通过 device = torch.device(...).to(device))。

    • 加入验证集、早停、学习率调度器与更丰富的评估指标(ROC-AUC 等)。

    • 使用 torch.from_numpy(...).float() 来避免不必要的内存复制与确保 dtype 一致。

  • 常见坑:标签 dtype/shape 不匹配、在仍有 grad 的 Tensor 上使用 .numpy()、误用 BCELoss 与 logits。

http://www.dtcms.com/a/350162.html

相关文章:

  • 深度学习入门:从概念到实战,用 PyTorch 轻松上手
  • 【科研绘图系列】R语言浮游植物生态数据的统计与可视化
  • Java 图像处理传 JNI 到 C++(OpenCV):两种高效实现方式对比
  • Element-ui icon鼠标移入显示提示(已解决)
  • C++高级特性与设计模式答案
  • 迭代器设计模式
  • C语言第十三章自定义类型:联合和枚举
  • 高通平台WIFI学习-- 基于WCN6750 Tri-Band 2x2 MIMO 802.11ax的讲解
  • IntelliJ IDEA 新手入门教程-Java、Web、Maven创建(带图解)
  • 2025年金九银十Java面试场景题大全:高频考点+深度解析+实战方案
  • 服务器Docker 安装和常用命令总结
  • vite 项目创建、插件配置
  • [React]Antd Select组件输入搜索时调用接口
  • 第二章 数据通信基础
  • beego v2 处理全局异常
  • 文献阅读笔记:KalmanNet-融合神经网络和卡尔曼滤波的部分已知动力学状态估计
  • Canvas 内凹弧形导航菜单(顶部内凹)
  • 基于MATLAB长时间序列遥感数据处理及在全球变化、物候提取、植被变绿与固碳分析等领域中的应用
  • 权限越权概念
  • centos7 安装coze
  • 【计算星座】2022-10-24
  • 普蓝超强承重越野移动机器人底盘轻松应对复杂路段
  • 《C++进阶:引用补充、内联函数与nullptr 核心用法》
  • 3 系统设计面试的框架
  • Odoo 企业版用户手册[新版]-前言 00.1-手册说明与使用指南
  • EasyClick 生成唯一设备码
  • SP95N65CTO:一款高性能650V SiC MOSFET的全面解析
  • 数据赋能(409)——大数据——合规性和伦理性原则
  • 强化学习基础总结
  • 《分布式系统跨服务数据一致性Bug深度复盘:从现象到本质的排查与破局》