
简介这份压缩包提供了一套基于PyCharm开发的神经网络二分类项目面向机器学习初学者和希望快速体验深度学习流程的开发者解决如何用Python搭建并训练一个二分类器的问题。PyCharm作为IDE提供了便捷的调试与运行环境配合TensorFlow/Keras等库可以完整复现从数据预处理、模型定义、编译、训练到评估的流程。包内共6个文件包含2个Python源码脚本、2个已训练好的H5模型文件以及2个pyc缓存文件整体仅2.35MB体量轻巧适合直接下载复现。已有365人学习浏览项目聚焦猫图与非猫图的识别任务是典型的二分类应用场景。其中catdemo.py构建并训练网络lr_utils.py负责数据加载与预处理两个H5模型文件保存了训练好的权重可直接加载进行预测方便对照结果快速验证。通过运行源码可以直观理解神经网络中权重调整、Sigmoid激活函数和Binary Crossentropy损失函数的作用为后续修改网络结构或迁移到其他分类问题打下基础。1. 从 PyCharm 到二分类神经网络一个完整跑通的最小闭环很多刚接触深度学习的人不是被算法难倒的而是被环境和流程卡死的。装好了 PyCharm却不知道选哪个解释器从网上抄了一段 MNIST 代码改了数据路径就跑不起来或者模型倒是能训练但结果根本没达到二分类预期的精度。这个标题里的神经网络与二分类本质上要解决的就是三件事把 PyCharm 配置成能开发 PyTorch 或 TensorFlow 的环境、把数据集整理成二分类能直接消费的格式、再训练出一个能真实工作的分类模型。无论你是刚接手数据分析任务还是想给现有业务加一个自动判定的入口这个方向都值得先完整走一遍。我在实际工作中几乎每天都会用 PyCharm 打开一个工作区目录——目录名叫什么并不重要重要的是里面的解释器、依赖和代码是干净且可复现的。luckyw77 这个标识符可以理解为你在本地建的这个项目工作区的名字。接下来要做的不是讲理论而是完整走一遍「配置环境 → 造数据 → 搭网络 → 跑训练 → 排坑」的流程每个步骤都能直接照抄。2. 把 PyCharm 调成能跑神经网络的状态解释器、虚拟环境与三处必改设置2.1 为什么优先用 conda 虚拟环境而不是全局 Python跑神经网络项目时依赖冲突是最常见的翻车现场今天装的 NumPy 版本和明天装的 Torch 版本互相不兼容或者在另一个项目里动了 TensorFlow 的依赖回来就发现之前能跑的网络全废了。我用 conda 一个非常核心的原因就是环境隔离。在 PyCharm 里新建项目时如果你直接选了系统自带的 Python那等于把所有项目堆在一个沙坑里出问题只是时间问题。常见做法是先用 conda 创建一个干净的虚拟环境比如指定 Python 3.9 或 3.10专门给这个神经网络项目用。创建好之后PyCharm 的设置里把 Project Interpreter 指到这个虚拟环境对应的 pythonw.exe 或 python 可执行文件上。2.2 在 PyCharm 里挂上 conda 环境的具体步骤创建环境的时候在 PyCharm 的终端里直接执行下面命令conda create -n luckyw77_env python3.9 -y conda activate luckyw77_env这里的参数要解释一下-n后面跟的是环境名叫 luckyw77_env 只是便于识别和后续conda activate luckyw77_env切换python3.9是指定版本在 2024 年之后 PyTorch 对 3.9 到 3.11 都支持得不错选 3.9 主要是图一个生态成熟且兼容老代码-y表示跳过提问直接创建。创建完环境后打开 PyCharm 的 File - Settings - Project - Python Interpreter点右上角的齿轮选择 Add Interpreter - Conda Environment - Existing Environment然后浏览到你刚才创建的文件路径下找到 python.exe。conda install pytorch torchvision torchaudio cpuonly -c pytorch -y如果机器有 NVIDIA 显卡并且配置好了 CUDA把cpuonly去掉换成合适的pytorch-cuda版本号。这一步是为了验证环境是否真的被 PyCharm 识别到了。很多人到这里会忽略一个细节PyCharm 的终端和系统终端是两个不同的 shell你在 PyCharm 底部面板里执行conda activate时如果提示找不到 conda 命令需要在 PyCharm 的 Terminal 设置里把 Shell path 指向你安装 Git Bash 或 cmd.exe 的完整路径并确保 conda 已经被初始化。2.3 项目解释器设置好后必须确认的三处第一处是 File - Settings - Project - Python Interpreter确保下拉框里显示的不是python.exe而是你新建的luckyw77_env环境。第二处是 Run/Debug Configurations如果你直接右键跑脚本PyCharm 可能默认用旧解释器要在 Run 菜单下 Edit Configurations 里把 Python interpreter 改成虚拟环境。第三处是右下角状态栏的 Python 版本标识点开它会列出所有可用解释器选对了再开始写代码。这三点一处没做对典型症状就是代码在命令行里跑得通一放到 PyCharm 里按运行就报 ModuleNotFoundError 或 ImportError。我收到过不止一次这样的求助最后都是指向解释器没切换。3. 二分类数据集从 pandas 表格到 PyTorch 认识的 Tensor 格式3.1 二分类场景和 MNIST 的差别别拿识别任务硬套很多教程里用 MNIST 做十分类演示到了二分类项目里直接改几个数字就开跑这是不对的。二分类最常见的场景是给定一条样本的特征向量输出一个概率或者一个 0/1 标签比如判断一个交易是否异常、一个设备是否即将故障、一个文本是否属于某类。这种任务的特征维度可能几十维到几千维但类别只有两个所以数据准备阶段的着眼点在于特征矩阵的标准化和类别分布的合理性而不在于图像尺寸和通道数。3.2 手写一个可复现的数据构造脚本如果没有现成数据可以用 numpy 造一个模拟二分类数据集来验证整个流程。下面这段代码生成 2000 条样本每条样本有 20 维特征前 10 维来自两个不同的高斯分布标签设为 0 或 1这就形成了天然可分的二分类数据。import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler np.random.seed(42) n_samples 2000 n_features 20 # 类别0中心在 0.5 附近类别1中心在 -0.5 附近 X0 np.random.normal(loc0.5, scale0.8, size(n_samples // 2, n_features)) X1 np.random.normal(loc-0.5, scale0.8, size(n_samples // 2, n_features)) X np.vstack([X0, X1]) y np.hstack([np.zeros(n_samples // 2), np.ones(n_samples // 2)]) # 打乱并划分 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, stratifyy, random_state42 ) scaler StandardScaler() X_train scaler.fit_transform(X_train) X_test scaler.transform(X_test) print(X_train.shape, X_test.shape) print(np.bincount(y_train.astype(int)), np.bincount(y_test.astype(int)))逻辑说明np.random.normal用不同的均值构造了本来就可分的两类只是为了后面演示网络训练真实数据不需要这么理想但流程一样。train_test_split里的stratifyy保证训练集和测试集的类别比例和原始数据一致这个参数必须写否则数据不平衡时随机切分会把少数类全切到测试集里训练就废了。StandardScaler做标准化用fit_transform在训练集上拟合参数再在测试集上只做transform不能混起来。参数说明test_size0.2表示留 20% 做验证random_state42固定了随机种子只有固定了才能复现实验关于种子和复现性见后面避坑章。loc和scale是高斯分布的均值和标准差二者差距越小分类难度越大想完全不可分就把差值降到接近零。3.3 把 numpy 数据转成 PyTorch 的 Dataset 与 DataLoader有了数组还不够PyTorch 的训练循环要用到 Dataset 和 DataLoader。下面的代码定义了一个最朴素的二分类数据集类并把它包装成一个可批量取数据的加载器。import torch from torch.utils.data import Dataset, TensorDataset, DataLoader X_train_t torch.tensor(X_train, dtypetorch.float32) y_train_t torch.tensor(y_train, dtypetorch.float32).reshape(-1, 1) X_test_t torch.tensor(X_test, dtypetorch.float32) y_test_t torch.tensor(y_test, dtypetorch.float32).reshape(-1, 1) train_dataset TensorDataset(X_train_t, y_train_t) test_dataset TensorDataset(X_test_t, y_test_t) train_loader DataLoader(train_dataset, batch_size64, shuffleTrue) test_loader DataLoader(test_dataset, batch_size128, shuffleFalse) for batch_x, batch_y in train_loader: print(batch_x.shape, batch_y.shape) break逻辑说明torch.tensor把 numpy 数组转成 CPU 上的浮点张量reshape(-1, 1)把标签从形状(N,)变成(N,1)这是因为后续计算二分类交叉熵时BCE 损失函数要求的标签形状是单列。TensorDataset是一种按元组存储输入和输出的简单数据集。DataLoader负责把数据集切成小批次batch_size64表示每批 64 条样本shuffleTrue只在训练集上打乱顺序测试集不需要打乱。参数说明batch_size是神经网络训练里最容易被忽略的超参64 是一个对大多数内存都友好的默认值如果训练 Loss 波动很大可以改成 128特征维度更大或 GPU 显存小就改成 32。num_workers在 Windows 下建议先不设置后面避坑章节会讲为什么。4. 搭一个小而完整的二分类网络结构设计、损失函数与训练循环4.1 输出层的神经元数量为什么是 1 而不是 2二分类问题的输出设计有两条路线一种是输出层用 1 个神经元经过 Sigmoid 得到 0 到 1 之间的概率大于 0.5 判为类别 1另一种用 2 个神经元经过 Softmax 得到两个类别各自的概率。二者在数学上等价但实际工程里更建议用 1 个神经元加 Sigmoid。原因是配合 BCEWithLogitsLoss 时数值更稳定——PyTorch 会把 Sigmoid 和交叉熵的计算合并成一个融合操作避免中间过程出现 log 0 或指数爆炸。4.2 定义一个轻量 MLP 模型并跑完整训练对于表格类二分类数据不需要上 CNN 或 Transformer一个两到三层的全连接网络就够。下面的代码定义了一个输入 20 维、隐藏层 32 维、带 ReLU 激活和 Dropout 的模型并完成了训练与测试。import torch.nn as nn import torch.optim as optim class BinaryMLP(nn.Module): def __init__(self, n_features, hidden_dim32): super(BinaryMLP, self).__init__() self.net nn.Sequential( nn.Linear(n_features, hidden_dim), nn.ReLU(), nn.Dropout(0.3), nn.Linear(hidden_dim, 1) ) def forward(self, x): return self.net(x) model BinaryMLP(n_features20) criterion nn.BCEWithLogitsLoss() optimizer optim.Adam(model.parameters(), lr0.001) epochs 30 for epoch in range(epochs): model.train() total_loss 0.0 for x_batch, y_batch in train_loader: optimizer.zero_grad() logits model(x_batch) loss criterion(logits, y_batch) loss.backward() optimizer.step() total_loss loss.item() * x_batch.size(0) avg_loss total_loss / len(train_dataset) if (epoch 1) % 5 0: print(fEpoch {epoch1:02d}/{epochs}, Loss: {avg_loss:.4f}) model.eval() with torch.no_grad(): logits model(X_test_t) probs torch.sigmoid(logits) preds (probs 0.5).float() accuracy (preds y_test_t).float().mean().item() print(fTest Accuracy: {accuracy:.4f})逻辑说明nn.Sequential把线性层、激活、Dropout 串联起来nn.Linear(20, 32)完成从 20 维输入到 32 维隐层特征的映射nn.Dropout(0.3)在训练时随机丢弃 30% 的隐层神经元目的是缓解过拟合验证时不生效。BCEWithLogitsLoss的输入是模型直接输出的 logits不需要先手动 Sigmoid计算测试指标时才用torch.sigmoid把 logits 转成概率。参数说明lr0.001是 Adam 优化器最常见的初始学习率如果你的 Loss 一直不下降先尝试调这个值而不是增加网络层数。epochs30对这个模拟数据能收敛得不错真实任务建议结合早停去判断什么时候停而不是盲目固定轮数。4.3 训练过程中的监控点看 Loss 曲线而不是只看准确率训练时输出每一轮的 Loss 还不够我一般会记录每一轮的验证集 Loss 和准确率画成曲线来判断模型是否过拟合。如果训练 Loss 持续下降但验证 Loss 在某个点开始回升那就是典型的过拟合模型把训练集的特征背下来了泛化能力不够。解决办法有三种增大 Dropout 的比例、减小网络容量、增加数据量或做数据增强。5. PyCharm 里跑二分类神经网络的五个高频坑现象、原因与解法5.1 解释器错误导致 import torch 失败现象代码在命令行里能执行一到 PyCharm 里运行就提示 ModuleNotFoundError: No module named ‘torch’。原因PyCharm 项目解释器仍然指向系统自带的 Python不是前面创建的 conda 环境。你虽然在终端里激活了 luckyw77_env但 PyCharm 的运行配置不会自动跟随终端。解决进入 File - Settings - Project - Python Interpreter确认解释器路径包含envs/luckyw77_env然后在 Run - Edit Configurations 里把该脚本的 Python interpreter 显式指定为这个环境。改完后关掉 PyCharm 再重新打开项目让配置彻底生效。5.2 DataLoader 设置 num_workers 导致程序卡死或报错现象在 Windows 上给 DataLoader 设置num_workers4后运行脚本直接卡住不动或抛BrokenPipeError。原因Windows 下多进程数据加载是通过 spawn 方式创建的在脚本直接运行时子进程会重新导入主模块容易出现异常。很多教程在 Linux 下这么写照搬到 Windows 就翻车。解决在 Windows 下安全做法是先不设置num_workers默认是 0即主进程加载数据如果数据量特别大再考虑用if __name__ __main__:保护主流程后设置num_workers2。5.3 标准化参数混用导致训练和测试指标虚高现象模型在训练集上准确率 99%在测试集上验证时也很高但一到真实新数据上准确率骤降。原因对测试集单独做了fit_transform而不是使用训练集拟合后的均值与方差。这样等于让模型在测试时看到了测试集的统计信息属于典型的数据泄漏结果不可信。解决严格按前面代码的写法训练集上调用scaler.fit_transform(X_train)测试集只调用scaler.transform(X_test)并且把训练用的 scaler 通过 pickle 或 joblib 保存下来后续接入新数据时用同一个 scaler 变换。5.4 固定随机种子缺失导致结果不可复现现象同一份代码连续跑两次两次 Loss 和准确率不一样甚至模型效果差异很大。原因numpy、random、PyTorch 各自维护随机状态如果不固定种子参数初始化和数据打乱的顺序每次都不同实验等于没有可对比性。解决在脚本最开头加上固定种子的代码import random, numpy as np, torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed) set_seed(42)5.5 类别标签形状搞错导致 BCEWithLogitsLoss 报错现象训练到第二步计算 loss 时报ValueError: Using a target size (torch.Size([64])) that is different to input size (torch.Size([64, 1])) is not allowed。原因模型输出的 logits 形状是(batch, 1)而标签是(batch,)形状不一致PyTorch 在广播时严格拒绝这类不匹配。解决确保标签在进入损失函数之前被reshape(-1, 1)处理过。如果你用的数据加载方式是自定义 Dataset在__getitem__里返回的 y 也必须保持(1,)的形状而不是一个标量。6. 进阶把二分类原型变成可用的方案早停、类别不均衡与模型导出基础模型跑通之后下一步就是把这个原型往真实场景推。这里有几个方向值得投入早停策略、类别不均衡处理和模型的导出与嵌入。早停用一句话概括就是在验证集 Loss 不再下降时停止训练而不是固定跑多少轮。实现上可以在每个 epoch 结束后记录验证集 Loss当连续 N 轮没改善时恢复最优模型权重并终止循环。N 一般取 5 到 10配合torch.save(model.state_dict(), best_model.pt)保存最优状态。类别不均衡是这个方向里极易踩坑的点。真实二分类场景里负样本可能占 95% 以上此时模型只需要全预测为多数类就能拿到虚高的准确率。处理方式有两种一是损失函数里加权重BCEWithLogitsLoss(pos_weighttorch.tensor([ratio]))ratio 是负样本数除以正样本数二是在数据层面过采样少数类或欠采样多数类。建议先用 pos_weight因为它不需要动数据分布改动一行代码就能看到效果。模型导出这一步决定了方案能不能被真正使用。训练完成后除了保存 state_dict还应把模型转换成 TorchScript 或 ONNX 格式方便在非 Python 环境里推理。ONNX 转换代码简单dummy_input torch.randn(1, 20) model.eval() torch.onnx.export(model, dummy_input, model.onnx, input_names[features], output_names[logits], dynamic_axes{features: {0: batch_size}, logits: {0: batch_size}})dynamic_axes这段配置要说明一下它允许推理时不要固定 batch 大小打进真实业务时可以一次只送一条样本也可以一次送一批。ONNX 模型可以直接有专门的推理引擎负责加载不用再盯着 PyTorch 的依赖。我自己在跑这一类任务时最后养成的一个习惯是每次训练完不只看准确率还要看 Precision、Recall 和 F1。二分类问题如果正样本很少准确率再高也可能是假的。把这三个指标打印出来再配合混淆矩阵观察错误都集中在哪一类才算真正相信这个模型能往外推。希望这些流程能帮你把这个方向走通也让你在 PyCharm 里跑第一个二分类神经网络的时候少踩几个坑。本文还有配套的精品资源点击获取