简介这份资源面向正在学习神经网络与机器学习入门、希望用 PyCharm 完成二分类实战的开发者围绕猫与非猫图像识别这一经典场景提供了一套可直接运行的 Python 项目源码。压缩包共 6 个文件包含 2 个 py 源码文件、2 个 h5 模型或数据集文件以及 2 个 pyc 编译缓存文件整体约 2.35MB体量轻便便于快速下载与本地复现。项目覆盖数据预处理、模型定义、编译、训练、评估与预测等完整流程涉及 Sigmoid 激活、Binary Crossentropy 损失以及 Keras、TensorFlow 等常用库的配合使用适合作为课程作业、实验报告或自学练手的参考。目前已有 365 人学习读者可借助源码理解二分类网络的搭建思路观察训练日志与准确率变化并在此基础上调整网络结构或超参数逐步排查过拟合与欠拟合等问题。1. 从file_luckyw77说起一个二分类神经网络项目在 PyCharm 里到底长什么样你拿到一个叫file_luckyw77_神经网络_PyCharm_二分类_的目录双击进去大概率会看到几个.py文件、一份数据表、可能还有requirements.txt。它不是一个能跑起来就完事的玩具而是一套典型的「PyCharm 神经网络 二分类」最小工程用 PyCharm 建解释器环境用 pandas 读数据用 sklearn 做预处理和切分用 PyTorch 或 TensorFlow 搭一个前馈网络最后输出 0/1 概率并算准确率、AUC。这类项目在信贷违约、设备故障、用户流失、医学阳性阴性判断里到处都是需求非常明确——把一堆特征映射成一个二分类标签并且要能复现、能调参、能解释。适合谁看如果你刚在 PyCharm 里配好 Python 解释器想跑通第一个神经网络二分类或者你已经会调sklearn的LogisticRegression但一换成神经网络就卡在维度、损失函数、阈值上这篇就是按你的路径写的。我会把「PyCharm 里怎么组织这个工程」「二分类网络的关键参数怎么设」「训练不收敛时先看哪里」拆成能直接抄的步骤也会把那些只有踩过才知道的坑讲清楚。2. PyCharm 工程搭建与数据管道让file_luckyw77先跑起来2.1 解释器、依赖与项目结构的最小闭环PyCharm 最容易翻车的地方不是代码而是解释器。你打开file_luckyw77后第一件事是确认右下角解释器是不是你装好依赖的那个。常见做法是新建一个虚拟环境Python 3.9 到 3.11 都行别用 3.12 去碰老版本 TensorFlow。然后在 PyCharm 的 Terminal 里装依赖# 在 PyCharm 底部 Terminal 中执行确保 pip 属于当前解释器 python -m pip install --upgrade pip python -m pip install numpy pandas scikit-learn torch matplotlib # 如果项目用 TensorFlow把 torch 换成 tensorflow注意版本匹配装完后用一行命令验证避免「PyCharm 里报找不到模块命令行却正常」这种玄学python -c import torch, sklearn, pandas; print(torch.__version__, sklearn.__version__)逻辑说明python -m pip而不是直接pip是为了锁定当前解释器PyCharm 多环境时这一步能省掉大量排查时间。参数上torch默认装 CPU 版就够二分类小数据用如果数据超过十万行且特征维度高再考虑 CUDA 版本。项目结构建议保持扁平file_luckyw77/ data/raw.csv src/config.py src/dataset.py src/model.py src/train.py requirements.txtconfig.py放路径、随机种子、超参数dataset.py只负责读数据和切分model.py定义网络train.py串起来。这样你调参时不用在几百行里翻。2.2 二分类数据管道的四个关键动作二分类神经网络对数据管道比传统模型更敏感因为网络会放大量纲和缺失值的问题。下面这段代码是我一般会先写的最小管道# src/dataset.py import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.impute import SimpleImputer def load_and_split(csv_path, label_col, test_size0.2, seed42): df pd.read_csv(csv_path) # 1. 标签必须是 0/1 整数字符串标签先映射 y df[label_col].astype(int).values X df.drop(columns[label_col]) # 2. 只保留数值列类别列用 one-hot 或 embedding这里先做数值版 X X.select_dtypes(include[np.number]) # 3. 缺失值用中位数填充避免删除样本导致类别失衡 imputer SimpleImputer(strategymedian) X imputer.fit_transform(X) # 4. 标准化神经网络对量纲敏感树模型不需要但这里必须做 scaler StandardScaler() X scaler.fit_transform(X) X_train, X_val, y_train, y_val train_test_split( X, y, test_sizetest_size, random_stateseed, stratifyy ) return X_train, X_val, y_train, y_val, scaler逻辑说明stratifyy是二分类里最容易被忽略的参数不加它验证集里正样本可能只剩几个AUC 波动会大到让你怀疑模型。SimpleImputer用中位数而不是均值是因为二分类特征里常有长尾。StandardScaler只在训练集上fit验证集用同一个 scaler 做transform否则就是数据泄漏。参数上test_size0.2是常规起点样本少于 5000 行时建议改成 0.3 并配合交叉验证。提示PyCharm 里如果pd.read_csv报FileNotFoundError先检查 Working Directory。Run Configuration 里默认是项目根目录但很多人把脚本放在src/下相对路径data/raw.csv就会找不到。改成绝对路径或统一用Path(__file__).parent.parent / data/raw.csv。3. 二分类神经网络的结构与训练前馈网络的最小可用配置3.1 网络层数、激活与输出层的选择理由二分类神经网络不需要深。常见做法是两到三层全连接每层 64 到 256 个单元激活用 ReLU输出层一个单元加 Sigmoid。为什么不是 Softmax因为二分类只需要一个 logit 表示正类概率Sigmoid 配BCEWithLogitsLoss数值更稳。下面是一个可以直接用的 PyTorch 版本# src/model.py import torch import torch.nn as nn class BinaryMLP(nn.Module): def __init__(self, n_features, hidden(128, 64), dropout0.3): super().__init__() layers [] prev n_features for h in hidden: layers [ nn.Linear(prev, h), nn.BatchNorm1d(h), # 加速收敛缓解内部协变量偏移 nn.ReLU(), nn.Dropout(dropout), # 二分类小数据防过拟合 ] prev h layers.append(nn.Linear(prev, 1)) # 输出一个 logit self.net nn.Sequential(*layers) def forward(self, x): return self.net(x).squeeze(-1) # 形状 [batch]逻辑说明BatchNorm1d放在Linear之后、激活之前是常见顺序如果 batch size 小于 16BatchNorm 会不稳定这时换成LayerNorm或直接去掉。Dropout在二分类里通常设 0.2 到 0.5数据越少设越大。输出层不加 Sigmoid是因为损失函数用BCEWithLogitsLoss内部做了 log-sum-exp 技巧比先 Sigmoid 再 BCELoss 更不容易梯度消失。参数上hidden(128, 64)是中小规模表格数据的稳妥起点特征超过 500 维时第一层可以加到 256。3.2 训练循环、阈值与早停三个必须显式处理的点训练代码里优化器、学习率、早停、阈值搜索是四个决定成败的开关。下面这段是训练主循环# src/train.py import numpy as np import torch import torch.nn as nn from torch.utils.data import DataLoader, TensorDataset from sklearn.metrics import roc_auc_score, f1_score from model import BinaryMLP from dataset import load_and_split X_train, X_val, y_train, y_val, _ load_and_split(data/raw.csv, label) device torch.device(cuda if torch.cuda.is_available() else cpu) train_ds TensorDataset(torch.tensor(X_train, dtypetorch.float32), torch.tensor(y_train, dtypetorch.float32)) train_loader DataLoader(train_ds, batch_size64, shuffleTrue) model BinaryMLP(n_featuresX_train.shape[1]).to(device) criterion nn.BCEWithLogitsLoss() optimizer torch.optim.AdamW(model.parameters(), lr1e-3, weight_decay1e-4) best_auc, patience, wait 0.0, 10, 0 for epoch in range(200): model.train() for xb, yb in train_loader: xb, yb xb.to(device), yb.to(device) optimizer.zero_grad() logits model(xb) loss criterion(logits, yb) loss.backward() optimizer.step() model.eval() with torch.no_grad(): val_logits model(torch.tensor(X_val, dtypetorch.float32).to(device)) val_prob torch.sigmoid(val_logits).cpu().numpy() auc roc_auc_score(y_val, val_prob) if auc best_auc: best_auc, wait auc, 0 torch.save(model.state_dict(), best.pt) else: wait 1 if wait patience: print(fearly stop at epoch {epoch}, best AUC{best_auc:.4f}) break # 阈值搜索默认 0.5 在类别失衡时往往不是最优 model.load_state_dict(torch.load(best.pt)) with torch.no_grad(): val_prob torch.sigmoid(model(torch.tensor(X_val, dtypetorch.float32))).numpy() best_thr, best_f1 0.5, 0.0 for thr in np.arange(0.1, 0.9, 0.02): f1 f1_score(y_val, (val_prob thr).astype(int)) if f1 best_f1: best_thr, best_f1 thr, f1 print(fbest threshold{best_thr:.2f}, F1{best_f1:.4f})逻辑说明AdamW比Adam多了正确的权重衰减二分类小数据上更稳。weight_decay1e-4是起点过拟合明显时加到 1e-3。早停的patience10表示验证 AUC 连续 10 轮不提升就停避免你盯着 loss 曲线做无效等待。阈值搜索是二分类里最容易被跳过的一步如果正样本只占 5%0.5 阈值会把大量正类判成负类F1 很难看按 0.02 步长在验证集上搜一遍通常能涨几个点。注意阈值只能在验证集上选选完再在测试集上报告最终指标。注意torch.load在新版本里如果报weights_only相关警告显式写torch.load(best.pt, weights_onlyTrue)避免加载不受信任的 pickle。4. 避坑与排查二分类神经网络在 PyCharm 里最常见的五类翻车4.1 损失不下降先查这四处现象训练几个 epoch 后 loss 在 0.69 附近不动准确率等于多数类占比。原因通常是标签没转成 float、输入没标准化、学习率过大导致震荡、或者输出层维度不对。解决先打印y_train.mean()确认正样本比例再检查X_train.mean(axis0)是否接近 0、std是否接近 1然后把学习率降到 1e-4 试一轮。如果 loss 直接变 NaN多半是学习率太大或出现了 log(0)用BCEWithLogitsLoss并检查输入里有没有 inf。4.2 验证集 AUC 高但测试集崩八成是泄漏现象验证集 AUC 0.95换一批数据掉到 0.6。原因标准化、缺失值填充、特征选择在切分之前做了验证集信息漏进训练。解决把所有fit操作限制在训练集验证和测试只transform。PyCharm 里建议把管道写成sklearn.pipeline.Pipeline从结构上杜绝顺序错误。4.3 PyCharm 报FileNotFoundError或模块导入失败现象命令行能跑PyCharm 里红字一片。原因Run Configuration 的 Working Directory 不是项目根目录或者解释器选错。解决Run - Edit Configurations把 Working Directory 设成项目根Python interpreter 选你装了依赖的那个。模块导入失败时在src/下加__init__.py或者把src标记为 Sources Root。4.4 类别失衡时只看准确率就是自欺现象准确率 95%但模型把所有样本都判成负类。原因正样本占比低准确率被多数类主导。解决训练时给BCEWithLogitsLoss传pos_weight值设为负样本数除以正样本数评估看 AUC、F1、召回率不要只看 accuracy。阈值按第 3 章的方法搜。4.5 过拟合训练 loss 一路降验证 loss 抬头现象训练集 AUC 0.99验证集 0.7 且不再提升。原因模型容量相对数据太大或者 dropout 没开、权重衰减太小。解决先加 dropout 到 0.5再把隐藏层从 (128,64) 降到 (64,32)同时weight_decay提到 1e-3。如果还不行说明特征本身区分度不够回去做特征工程比继续调网络更值。5. 进阶技巧用交叉验证和特征重要性把二分类项目做扎实单次切分的 AUC 波动可能有两三个点尤其是样本量几千行的时候。我一般会在最终定稿前跑一次分层 K 折交叉验证把每一折的 AUC 和 F1 都记下来看均值和标准差。做法是把第 3 章的训练循环包成一个函数外层用StratifiedKFold(n_splits5)每折重新初始化模型和优化器最后汇总。这样你报告的结果不是运气而是分布。from sklearn.model_selection import StratifiedKFold import numpy as np skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) aucs [] for fold, (tr_idx, va_idx) in enumerate(skf.split(X_all, y_all)): # 每折内部重新做标准化避免泄漏 # train_fn 返回该折验证集 AUC auc train_fn(X_all[tr_idx], y_all[tr_idx], X_all[va_idx], y_all[va_idx]) aucs.append(auc) print(ffold {fold}: AUC{auc:.4f}) print(fmean AUC{np.mean(aucs):.4f} ± {np.std(aucs):.4f})另一个值得做的是特征重要性。神经网络不像 XGBoost 那样直接给feature_importances_但你可以用两种轻量方法一是训练完后对每个特征做 permutation看验证 AUC 掉多少二是看第一层权重的绝对值均值。前者更可靠但慢后者快但只能当参考。我通常先跑 permutation 找出前 10 个关键特征再决定要不要删掉那些贡献接近零的列。删特征不仅能提速还能降低过拟合。最后说一个我自己的习惯每次调完参把config.py里的随机种子、学习率、隐藏层、dropout、阈值全部写进一份experiment_log.md连同当次验证 AUC 一起记。二分类项目最怕的就是「上周跑出 0.88这周怎么都复现不了」。种子固定、数据版本固定、预处理顺序固定这三件事做到你的file_luckyw77才真正算跑通。希望帮到你。本文还有配套的精品资源点击获取