在PyCharm中跑通神经网络二分类猫图识别工程:从HDF5数据到超参数调参
发布时间:2026/9/28 17:11:25来源:尧图网络
简介这是一套基于PyCharm开发的神经网络二分类入门项目面向希望结合IDE上手深度学习的Python学习者。核心代码围绕猫/非猫图片分类任务展开演示从数据预处理、模型构建到训练评估的完整链路。压缩包共6个文件约2.35MB主体为2个Python脚本、2个h5数据集文件以及2个pyc缓存文件其中py脚本承担数据加载与模型逻辑h5文件提供训练集和测试集整体轻量适合快速复现。压缩包内已包含可直接运行的训练脚本与配套数据无需额外下载数据集打开PyCharm即可从零跑通流程。已有365人学习下载。通过阅读和运行项目可直观理解神经网络在二分类中的应用包括Sigmoid激活函数、Binary Crossentropy损失函数以及Keras序贯模型的搭建方式同时也能学习PyCharm中导入数据、训练模型和保存权重的基本操作为后续更复杂的图像分类任务打下基础。1. 先用 PyCharm 把这个猫图二分类工程跑起来再谈神经网络调参如果你下载过那个pycharm file_luckyw77_神经网络_PyCharm_二分类压缩包会发现里面不是一堆花哨的模型文件而是一个非常“课程作业感”的工程catdemo.py、lr_utils.py、venv、__pycache__里躺着的cpnet.cpython-37.pyc再加上datasets/train_catvnoncat.h5和test_catvnoncat.h5。这套组合拳我太熟了——它本质上就是神经网络二分类里最经典的“猫图识别”项目输入一张 64×64 的 RGB 图片模型输出这张图里有没有猫。压缩包里的.pyc是编译后的 Python 3.7 字节码说明作者在 PyCharm 里用 CPython 3.7 跑通过venv也一并打了进去。别被.pyc吓到这个工程的价值在于数据流完整、代码路径清晰正好适合在 PyCharm 里逐行复现二分类网络的训练流程。想了解神经网络二分类到底怎么做、参数设多少、损失曲线长什么样这份工程是好起点新手能顺着数据加载一路跑到测试集预测熟手也能拿它当“改写成 PyTorch/TensorFlow”的实验底座。2. 二分类与猫图数据集为什么从 h5 起步而不是直接读文件夹2.1 train_catvnoncat.h5 内部的真实结构整个工程的核心不是代码而是datasets下的两个 HDF5 文件。HDF5 这类格式在深度学习数据集里很常见它的特点是能把数组、标签、属性打包进一个文件训练时不需要像读图片文件夹那样逐个 IO。PyCharm 里最常见的做法是先写一段探针脚本把 h5 的 key 打出来看内部结构再动手。import h5py for name in [datasets/train_catvnoncat.h5, datasets/test_catvnoncat.h5]: with h5py.File(name, r) as f: print(name) print(keys:, list(f.keys())) for key in f.keys(): data f[key][()] print(key, data.shape, data.dtype)这段代码里的f[key][()]是关键[()]是把整个数据集一次性读进内存不像[:]在某些高维分块存储场景下有歧义。运行后你会看到train_set_x、train_set_y、test_set_x、test_set_y、list_classes这几类键名。train_set_x的形状是(209, 64, 64, 3)含义是 209 张训练图每张 64×64 像素、RGB 三个通道train_set_y是(1, 209)对应每张图是猫还是不是猫。看到这个形状你就能明白为什么后续代码里要把三维图片展平成(64*64*3, 209)的二维矩阵——很多经典网络实现都要求样本按列排布计算时np.dot(W, X)可以直接做矩阵乘法。2.2 lr_utils.py 里的数据加载与展平逻辑压缩包里的lr_utils.py是这套工程的“数据入口”。它的作用不光是读 h5还帮你做了两件很重要的事情归一化和标签整形。常见实现里load_dataset()会根据文件路径读取 train 和 test 数据再把train_set_x_orig的形状从(209, 64, 64, 3)转成(209, 64*64*3)最后转置成(12288, 209)。与此同时标签从(209,)变成(1, 209)方便后面做广播计算。import h5py import numpy as np def load_dataset(): train_dataset h5py.File(datasets/train_catvnoncat.h5, r) train_set_x_orig np.array(train_dataset[train_set_x][:]) train_set_y_orig np.array(train_dataset[train_set_y][:]) test_dataset h5py.File(datasets/test_catvnoncat.h5, r) test_set_x_orig np.array(test_dataset[test_set_x][:]) test_set_y_orig np.array(test_dataset[test_set_y][:]) classes np.array(test_dataset[list_classes][:]) train_set_y_orig train_set_y_orig.reshape((1, train_set_y_orig.shape[0])) test_set_y_orig test_set_y_orig.reshape((1, test_set_y_orig.shape[0])) train_set_x_orig train_set_x_orig.reshape(train_set_x_orig.shape[0], -1).T test_set_x_orig test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T train_set_x train_set_x_orig / 255. test_set_x test_set_x_orig / 255. return train_set_x, train_set_y, test_set_x, test_set_y, classes这段代码有几个细节值得注意。reshape(shape[0], -1).T是展平加转置的一体化操作-1表示自动推算维度也就是把每张图的 64×64×3 全部拉成一列。除以 255 这一步是把 RGB 像素值从 0-255 压到 0-1这能避免神经网络反向传播时梯度过大。如果你在自己的工程里复现记得先确认train_set_y的 shape很多二分类项目的坑都出在标签是(209,)而不是(1, 209)会导致广播维度对不上。2.3 数据形状和标签对应关系神经网络二分类的数据形态通常是“特征矩阵 标签向量”而这份资源的特征矩阵是(12288, 209)其中 12288 64×64×3。PyCharm 的 Debugger 里你可以在load_dataset()返回后加断点直接看 Variables 面板里train_set_x.shape、train_set_y.shape这样比任何文档都直观。我建议你第一步先确认训练集 209 张、测试集 50 张标签里1表示猫、0表示非猫list_classes里默认是[bnon-cat, bcat]。如果你的输出里non-cat索引是 0、cat索引是 1那说明lr_utils里的标签顺序没有做翻转后续np.squeeze(A2)和 0.5阈值判断就能直接用。这里也顺带说明二分类不是必须输出 0 或 1而是输出一个介于 0 到 1 的概率再靠阈值切分。3. PyCharm 里跑通 cpnet.pyc 前馈网络前向传播、损失与超参数3.1 只有 .pyc 没有 .py怎么处理压缩包里__pycache__目录下的cpnet.cpython-37.pyc是编译产物原始.py没打进来。PyCharm 直接双击.pyc是没法编辑的但你可以把它当黑盒 runner 用。最稳妥的做法不是去反编译而是参照catdemo.py的调用方式新建一个同名.py或者用python -m py_compile把新版代码编译成同名.pyc。很多初学者的误区是想用工具把.pyc还原成一模一样的源码实际上这个工程里的网络结构并不复杂自己重写一个前馈网络反而更好调试。常见做法是先看cpnet.cpython-37.pyc被谁 import通常catdemo.py里会有import cpnet然后调用cpnet.model(X_train, Y_train, X_test, Y_test, num_iterations2000, learning_rate0.005)之类的入口。.pyc 文件不能直接阅读但它的调用约定可以从catdemo.py的报错信息里摸出来——运行一次PyCharm 的 Traceback 会告诉你要传几个参数、返回值是什么。3.2 前向传播、损失与反向传播的常规写法这类课程工程里最可能的核心网络是“单隐藏层神经网络”也就是输入层 12288 个节点、隐藏层若干节点、输出层 1 个节点。二分类最后一层通常用 Sigmoid把 logits 压到 (0, 1) 区间损失函数用交叉熵。下面这段代码是符合该资源场景的参考实现适合放进 PyCharm 新建的cpnet_new.py里直接跑import numpy as np def initialize_parameters(n_x, n_h, n_y): np.random.seed(1) W1 np.random.randn(n_h, n_x) * 0.01 b1 np.zeros((n_h, 1)) W2 np.random.randn(n_y, n_h) * 0.01 b2 np.zeros((n_y, 1)) return {W1: W1, b1: b1, W2: W2, b2: b2} def forward_prop(X, params): Z1 np.dot(params[W1], X) params[b1] A1 np.tanh(Z1) Z2 np.dot(params[W2], A1) params[b2] A2 1 / (1 np.exp(-Z2)) cache {Z1: Z1, A1: A1, Z2: Z2, A2: A2} return cache def compute_cost(A2, Y): m Y.shape[1] cost -np.mean(Y * np.log(A2 1e-8) (1 - Y) * np.log(1 - A2 1e-8)) return cost def backward_prop(X, Y, params, cache): m X.shape[1] A1 cache[A1] A2 cache[A2] dZ2 A2 - Y dW2 np.dot(dZ2, A1.T) / m db2 np.sum(dZ2, axis1, keepdimsTrue) / m dA1 np.dot(params[W2].T, dZ2) dZ1 dA1 * (1 - np.square(A1)) dW1 np.dot(dZ1, X.T) / m db1 np.sum(dZ1, axis1, keepdimsTrue) / m return {dW1: dW1, db1: db1, dW2: dW2, db2: db2} def update_params(params, grads, learning_rate): for key in [W1, b1, W2, b2]: gkey d key params[key] - learning_rate * grads[gkey] return params这段代码里的1e-8是为了防止log(0)出现数值溢出训练时如果你用的是原始 h5 而不是归一化数据这里可能直接变成 NaN。tanh在隐藏层做非线性映射输出层sigmoid的二分类语义是“P(y1|x)”。dZ2 A2 - Y是交叉熵 Sigmoid 联合梯度化简后的结果很多推导教程里的反向传播最后都会收敛到这一步这个式子看着简单但它是整个训练能收敛的大前提。3.3 超参数怎么设学习率、迭代次数、隐藏层单元这类猫图二分类工程里最常见的超参组合是隐藏层 7 个节点、learning_rate0.005、迭代 2000 次。7 这个数字完全来自课程作业的惯例不是数学推导出来的最优值但它能让你观察到“损失下降、准确率到 70% 左右”的正常现象。如果换成 20 个节点训练集正确率会更高但测试集可能开始抖动。PyCharm 里跑训练时我一般会先把num_iterations设成 100看cost前几步是不是稳定下降如果 loss 直接变成nan先不要调网络回去检查X有没有归一化、Y的 shape 是不是(1, m)。学习率太大比如0.5前几次迭代损失可能不减反增学习率太小比如0.00012000 次迭代后损失还在 0.6 附近明显欠拟合。训练循环里最好每 100 次打印一次当前代价而不是只在最后打印for i in range(num_iterations): cache forward_prop(X_train, params) cost compute_cost(cache[A2], Y_train) grads backward_prop(X_train, Y_train, params, cache) params update_params(params, grads, learning_rate) if i % 100 0: print(fiter {i}, cost{cost:.6f})打印频率设成 100 次刚好能看出训练曲线是平滑下降还是有周期性震荡。如果 cost 在 0.65 上下卡住不动很可能是隐藏层节点数太少、非线性表达能力不够。这个阶段不要急着上 BatchNorm先把数据形状和损失函数核对清楚再考虑模型容量。4. 常见问题与避坑路径、h5 读取、解释器、pycache四类高发故障4.1 FileNotFoundErrordatasets 相对路径在不同运行方式下的差异现象从 PyCharm 里直接右键运行catdemo.py报错FileNotFoundError: [Errno 2] Unable to open file: datasets/train_catvnoncat.h5但明明文件就在工程目录下。原因PyCharm 的 Run 默认工作目录是工程根目录但如果你把catdemo.py放在子目录或者使用Run with Python Console当前工作目录会变成脚本所在目录或者调试配置里的 working directory。lr_utils.py里硬编码的datasets/train_catvnoncat.h5是相对路径一旦工作目录不对就找不到。解决打开 PyCharm 的Run - Edit Configurations把Working directory改成工程根目录更稳的方法是在代码里基于__file__定位数据集绝对路径。常见做法是import os BASE_DIR os.path.dirname(os.path.abspath(__file__)) DATA_PATH os.path.join(BASE_DIR, datasets, train_catvnoncat.h5)改完之后不要再用相对字符串拼接。这一点在catdemo.py里尤其要注意因为原始工程可能依赖 PyCharm 默认配置换到命令行或者 Docker 里跑立刻崩。4.2 OSError: Unable to open file 与 h5py 版本不匹配现象在 PyCharm 的 Terminal 里执行pip install h5py后运行加载代码报OSError: Unable to open file (file read write: unable to read from file)或者AttributeError: HDF5 object has no attribute File。原因h5py 是依赖 HDF5 动态库的 Python 包Windows 上如果安装的是预编译 wheel通常没问题但如果你的venv是从另一台机器直接拷贝过来的里面的 h5py 和当前系统的底层库对不上就会出现文件打不开或元数据读取异常。另外还有一个常见情况是文件被另一进程占用Windows 下 HDF5 文件句柄冲突不会报 PermissionError而是报 unable to read from file。解决在 PyCharm 里为当前venv重新安装匹配 Python 3.7 的 h5py 版本建议先卸载再安装pip uninstall h5py -y pip install h5py3.7.0如果还报错检查数据集文件是否处于只读保护或云盘同步目录中把文件复制到本地工程目录再跑。我遇到过的“h5 读取黑匣子”问题最后多半是压缩包解压不完整导致文件头缺失用h5py打开时不会立刻报错会在f[train_set_x][:]时才断掉。4.3 ModuleNotFoundError: No module named lr_utils现象catdemo.py第一次运行时提示ModuleNotFoundError: No module named lr_utils。原因PyCharm 的Sources Root没有被正确标记或者脚本运行目录里没有lr_utils.py。压缩包里文件名可能是lr_utils.py但如果被解压成lr_utils.py.txt就没辙。解决在工程根目录右键选择Mark Directory as - Sources Root同时在 Run Configuration 里把Add content roots to PYTHONPATH勾上。如果这两个都没问题检查__init__.py——不一定需要但如果你把代码放到包目录下没有__init__.py会导致 import 失败。这里的根因是 Python 模块搜索路径sys.path里没有工程根目录PyCharm 的 Sources Root 设置是最常见的修复手段。4.4pycache与 .pyc 同名文件覆盖问题现象你新建了一个cpnet.py想替代cpnet.cpython-37.pyc但 PyCharm 运行时总是提示“代码没有更新”或者看起来还在执行旧逻辑。原因.pyc文件是编译缓存Python 解释器在 import 模块时优先检查.py和.pyc的时间戳。如果你保留旧的__pycache__/cpnet.cpython-37.pyc同时又新建cpnet.py某些清理不彻底的 PyCharm 缓存可能导致旧字节码被复用。另一个坑是原始压缩包里的.pyc是从cpnet.py编译的但源文件不存在你新建cpnet.py后import cpnet会优先加载新的源文件编译产物但__pycache__里同名的.pyc如果时间戳比.py新仍有可能被加载。解决删除__pycache__整个目录重新运行一次。如果还有问题在catdemo.py顶部加一句强制刷新import importlib import cpnet importlib.reload(cpnet)但要注意reload不能解决模块内全局状态被缓存的问题最干净的办法是把旧.pyc改名存到备份目录然后让 PyCharm 重建__pycache__。4.5 venv 解释器选错导致 NumPy 版本和编译产物对不上现象用压缩包里的venv作为项目解释器运行时提示ImportError: NumPy version mismatch或者cpnet.cpython-37.pyc能导入但np.ndarray操作报奇怪的内存错误。原因压缩包里的venv是别人机器上的虚拟环境里面 Python 3.7、NumPy 版本和当前 PyCharm 的环境可能不同。.pyc文件的 magic number 是 Python 3.7 就能跨小版本运行但 NumPy 二进制接口在版本间不保证完全兼容。再说直白点传输过程中虚拟环境里的.pyd和动态库很容易损坏。解决不要直接用压缩包里的venv在 PyCharm 里新建一个虚拟环境选 Python 3.7然后装依赖pip install numpy h5py matplotlib如果你的机器没有 Python 3.7用 3.8 或 3.9 也能跑但cpnet.cpython-37.pyc对应的是 CPython 3.7最好保证解释器主版本一致。这里我踩过最大的坑是压缩包里可能带着venv和__pycache__新手一股脑把整个目录作为解释器结果lr_utils.py没问题反而是.pyc的字节码与当前 Python 版本不兼容导致ValueError: bad marshal data这个报错一旦出现直接重建环境比 debug 快得多。5. 不反编译 .pyc用一个“保存参数 随机抽查”技巧接管训练结果很多人拿到.pyc之后第一反应是找反编译工具把字节码还原成源码但在这个资源场景下完全没必要。神经网络二分类的训练结果本质上是权重矩阵和偏置你可以不碰.pyc只通过调用它得到预测输出再用 Pickle 或 NumPy 文件把模型参数导出成自己的资产。我一般会在catdemo.py的训练结束后追加两行代码先把当前权重存下来import numpy as np # 假设 params 是模型参数字典A2 是测试集预测输出 np.save(cat_params.npy, params, allow_pickleTrue) np.save(test_pred.npy, cache[A2], allow_pickleTrue)保存之后在 PyCharm 里新建一个verify_result.py做随机抽查不依赖任何.pycimport numpy as np from lr_utils import load_dataset train_set_x, train_set_y, test_set_x, test_set_y, classes load_dataset() params np.load(cat_params.npy, allow_pickleTrue).item() def predict(X, params): Z1 np.dot(params[W1], X) params[b1] A1 np.tanh(Z1) Z2 np.dot(params[W2], A1) params[b2] return 1 / (1 np.exp(-Z2)) preds predict(test_set_x, params) for i in range(5): idx np.random.randint(test_set_x.shape[1]) true_label int(test_set_y[0, idx]) pred_label int(preds[0, idx] 0.5) print(f样本 {idx}: 真实{true_label}, 预测{pred_label}, 概率{preds[0, idx]:.4f})这里没有反编译却把你从“只能黑盒跑”变成了“能保存、能加载、能单测”。里面的preds[0, idx] 0.5就是二分类决策边界概率恰好等于 0.5 时归为哪一类都行但这种情形在真实数据里极少见。如果抽查后发现准确率远低于训练时打印的数值先检查test_set_x有没有归一化——很多人保存权重后加载测试却忘了测试集需要除以 255导致模型输入分布漂移。再补一个通用技巧如果你想把这份 numpy 版网络无缝搬到 PyTorch不需要手动写反向传播只要把np.random.randn初始化的权重作为nn.Linear的weight和bias初值传进去然后继续用BCEWithLogitsLoss微调几轮。这样既保住了原工程训练好的先验又能快速进入现代框架的生态。自从我拿这份猫图工程练手之后每拿到一个陌生机器学习压缩包都会先用它导出一份参数快照再决定是继续训练还是迁移学习而不是花两小时反编译一个并不复杂的.pyc。这个习惯帮我省掉了大量“看代码却跑不出结果”的返工希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网