PyCharm手写Python神经网络实现猫图二分类:从原理到代码全解析

发布时间:2026/10/14 15:05:10

PyCharm手写Python神经网络实现猫图二分类:从原理到代码全解析 简介压缩包内是一个基于 PyCharm 开发的神经网络二分类 Python 项目面向机器学习初学者和希望快速上手深度学习流程的开发者适合通过实例理解神经网络的核心机制。项目围绕图像数据的二分类任务展开代码覆盖数据预处理、模型搭建、损失函数选择、编译、训练、评估与预测等完整步骤在 PyCharm 中可直接打开工程运行并逐步调试便于观察中间结果。资源共 6 个文件含 2 个 Python 源码模型主程序与辅助工具函数、2 个数据集文件训练与测试样本及 2 个缓存文件整体仅 2.35MB轻量紧凑解压即可实践。目前已有 365 人学习适合作为入门神经网络的动手练习。借助该资源可快速复现二分类实验理解激活函数与二进制交叉熵损失的作用也可尝试调整网络结构或超参数观察性能变化为后续学习卷积网络或深度学习框架打基础。1. PyCharm 里跑神经网络二分类这份 catdemo 项目到底能学到什么拿到这份压缩包第一反应是典型的某高校深度学习课程作业结构catdemo.py主脚本、lr_utils.py数据工具、h5格式数据集外加一个venv虚拟环境和一份cpnet相关缓存。核心场景是用一个不含 TensorFlow、不调 Keras 的纯 Python 神经网络完成猫图二分类——判断一张图片里有没有猫训练集 209 张、测试集 50 张RGB 三通道 64×64 像素。如果你正在找「神经网络到底怎么一步步算出来的」这份确定性而不是只想model.fit()完事这个项目恰好能补上原理到代码之间那块最关键的拼图。适合刚学完 Python 基础、准备啃深度学习原理的初学者也适合想快速回顾反向传播代码实现的从业者。2. 数据准备h5 文件加载、学图片展平与归一化三个细节2.1 为什么要用 h5 作为数据集载体train_catvnoncat.h5和test_catvnoncat.h5是 HDF5 格式的二进制数据文件很多第一次接触的同学会在这卡住——打不开、看不到、不知道怎么转成数组。常见做法是用h5py库读取。选择 h5 而不是直接把图片散落成几百个 jpg 文件原因有三一是 209 张 64×64×3 的图连同标签打包进单个文件结构清晰且单文件传输、加载都方便二是 h5 支持索引切片和部分读取在内存受限时比一次性np.load所有原始图片更灵活三是课程与实验场景下 h5 是标准交换格式很多公开数据集都沿用它掌握加载方式以后换数据集不吃亏。lr_utils.py里的load_dataset()函数做了解压、读取、展平、归一化四件事。注意先把h5py引入装好 venv 之后直接pip install h5py numpy matplotlib即可。2.2 读取 h5 并构造训练集、测试集与标签import numpy as np import h5py def load_dataset(): # 训练集与测试集的 h5 文件放在 datasets 目录下 train_dataset h5py.File(datasets/train_catvnoncat.h5, r) test_dataset h5py.File(datasets/test_catvnoncat.h5, r) # 取出原始图片数据与标签key 分别是 train_set_x、train_set_y train_set_x_orig np.array(train_dataset[train_set_x][:]) train_set_y_orig np.array(train_dataset[train_set_y][:]) test_set_x_orig np.array(test_dataset[test_set_x][:]) test_set_y_orig np.array(test_dataset[test_set_y][:]) # 标签 shape 是 (m,)需要转成 (1, m) 便于后续矩阵运算 train_set_y train_set_y_orig.reshape((1, train_set_y_orig.shape[0])) test_set_y test_set_y_orig.reshape((1, test_set_y_orig.shape[0])) return train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes这里最容易忽略的是train_set_y的 reshape原始标签从 h5 读出来是(209,)的一维数组而后续计算损失要用(1, 209)这样才能和预测输出(1, 209)直接做矩阵运算。如果不 reshape广播机制会捣乱算出的准确率会莫名掉一半而且报错位置不在标签处而在反向传播的维度匹配上。建议读完立刻打印每个数组的shape确认一下。2.3 展平、转置与归一化为什么是 (n, m) 而不是 (m, n)# 展平将每张 64x64x3 的图片转成一维向量再整体转置为 (样本数, 特征数) train_set_x_flatten train_set_x_orig.reshape(train_set_x_orig.shape[0], -1).T test_set_x_flatten test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T # 归一化像素值 0-255 缩放到 0-1 train_set_x train_set_x_flatten / 255. test_set_x test_set_x_flatten / 255.这段代码里.T是关键。reshape(train_set_x_orig.shape[0], -1)先得到(209, 12288)每一行是一张图转置后变成(12288, 209)每一列是一张图。在纯 Python 手写神经网络的代码里约定X的 shape 为(n_features, m_samples)w为(n_features, 1)b为标量。这样Z np.dot(w.T, X) b直接得到(1, m)的线性输出。反过来如果用(m, n)后面每个公式都要跟着调整还容易和框架习惯混淆。归一化在这里用的是除以 255这是最朴素的缩放方式。注意不要把归一化和标准化混为一谈——标准化通常指减去均值再除以标准差适用于特征分布差异大的场景图像像素本身取值区间固定且各通道分布均匀直接除 255 就够。如果强行对稀疏或非正态特征做标准化某些情况下反而会放大噪声这是新手常犯的过度预处理。2.4 索引与维度的坑h5 的 key 拼写与数组取片h5py 读取时train_dataset[train_set_x][:]如果写成train_dataset[train_set_x]拿到的是 HDF5 数据集对象而不是 NumPy 数组后续np.array和reshape都会报错或行为异常。还有一点h5py.File用完建议close()或者在with块里打开不然 Windows 下文件会被占用第二次运行时常报权限错误。我看到不少同学在 PyCharm 里反复运行同一脚本第二次报「Unable to open file」往往就是这个原因。数据加载完之后的检查动作也很重要。我会先打印train_set_x.shape、train_set_y.shape、classes的内容再看一张图确认数据没反。这个环节如果跳过后面训练出来的模型准确率再高也可能是数据错位练出来的假象。3. 网络实现从零手写前向传播、交叉熵损失与反向传播3.1 为什么这个项目不直接用 Keras 或 PyTorch现在主流做法是调框架但这份资源里 catdemo 走的是纯 NumPy 手写路线对应的是某高校深度学习课程前几周的经典作业结构。好处在于能逼你理解矩阵运算的实际形状变换——框架帮你把model.compile()、model.fit()都封装好了你不需要关心梯度怎么算但一旦模型不收敛排查起来完全是黑匣子。手写版本里每一步的矩阵 shape、每个梯度的推导都能对应到纸上公式这对打基础价值很大。我一般会建议新手哪怕后面转 PyTorch也至少手写一次单隐层网络的前向和反向。等你真上手框架后调试RuntimeError: size mismatch时心里是有底的因为你知道每个中间张量应该是什么形状。3.2 Sigmoid 激活与交叉熵二分类的标配组合def sigmoid(z): return 1 / (1 np.exp(-z)) def propagate(w, b, X, Y): m X.shape[1] # 前向传播 A sigmoid(np.dot(w.T, X) b) # 预测概率 cost -np.mean(Y * np.log(A) (1 - Y) * np.log(1 - A)) # 反向传播 dw np.dot(X, (A - Y).T) / m db np.mean(A - Y) grads {dw: dw, db: db} return grads, cost为什么二分类选择 Sigmoid 而不是 ReLU因为输出层需要一个落在 (0,1) 区间的概率值Sigmoid 天然满足而 ReLU 输出无上界不能直接解释成概率。在隐藏层可以用 ReLU 解决梯度消失问题但输出层配 Sigmoid、损失函数配交叉熵是二分类的黄金组合。数学上Sigmoid 加交叉熵的联合梯度是(A - Y) * X / m形式非常简洁且没有sigmoid导数的连乘项数值稳定性好。如果你把损失换成均方误差梯度里会多出A * (1 - A)因子当 A 接近 0 或 1 时梯度趋近于 0学不动。另外一个细节是np.dot(X, (A - Y).T)为什么要除以m。dw 是对整个训练集的平均梯度除以样本数是为了让梯度大小不受 batch 规模影响。如果这里漏掉/ m学习率不变的情况下样本越多梯度越大训练直接发散。3.3 梯度下降迭代learning_rate 与 num_iterations 的调法def optimize(w, b, X, Y, num_iterations, learning_rate): costs [] for i in range(num_iterations): grads, cost propagate(w, b, X, Y) dw grads[dw] db grads[db] w w - learning_rate * dw b b - learning_rate * db if i % 100 0: costs.append(cost) params {w: w, b: b} grads {dw: dw, db: db} return params, grads, costs这个循环就是神经网络训练最原始的样子算梯度、沿负方向更新、重复上千次。learning_rate决定了每一步迈多大太大 overshoot 导致 loss 震荡甚至发散太小训练慢到怀疑人生num_iterations决定总步数。这个数据集上learning_rate0.005、num_iterations2000是常见的可行配置loss 能降到 0.2 以下训练准确率 90% 上下。实际跑的时候我建议把costs收集起来画 loss 曲线每 100 次迭代记一个点。判断模型是否正常最直观的就是这条曲线平滑下降说明学习率合适剧烈震荡说明学习率偏高缓慢下降说明学习率偏低或模型容量不够。仅看最终准确率很容易误判因为测试集可能恰好猜对了几个。3.4 初始化全零初始化在这个结构里真的可以def initialize_with_zeros(dim): w np.zeros((dim, 1)) b 0.0 return w, b全零初始化在单层逻辑回归结构里没有问题因为每个特征对应的权重只通过输入参与计算对称性不会被破坏。但如果换成多层网络全零初始化会导致同一层所有神经元学到完全相同的东西无论迭代多少次都无法分化。这份资源只做单隐层或单层结构全零初始化够用如果你在这个基础上加隐藏层初始化一定要换成随机小值比如np.random.randn(...) * 0.01不然隐藏层等于白设。这也解释了为什么课程作业会特意给单层版本用全零——学生能直观理解逻辑回归是神经网络的特例。4. 训练与预测准确率评估、决策边界和代价函数曲线怎么看4.1 预测函数概率怎么转成类别def predict(w, b, X): m X.shape[1] Y_prediction np.zeros((1, m)) w w.reshape(X.shape[0], 1) A sigmoid(np.dot(w.T, X) b) for i in range(A.shape[1]): Y_prediction[0, i] 1 if A[0, i] 0.5 else 0 return Y_prediction预测过程本质上是把训练好的参数拿来做一次前向传播再按 0.5 阈值二值化。这里有个隐性参数阈值。默认 0.5 只是惯例如果你的应用场景里假阳性代价更高阈值可以上调到 0.6、0.7如果召回率更重要阈值可以下调到 0.4。不过在这个课程项目里直接用 0.5 就够了不用过度优化。完整调用方式在 catdemo.py 里是这样组织的train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes load_dataset() # 展平 归一化 train_set_x_flatten train_set_x_orig.reshape(train_set_x_orig.shape[0], -1).T test_set_x_flatten test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T train_set_x train_set_x_flatten / 255. test_set_x test_set_x_flatten / 255. d train_set_x.shape[0] w, b initialize_with_zeros(d) params, grads, costs optimize(w, b, train_set_x, train_set_y, num_iterations2000, learning_rate0.005) w, b params[w], params[b] Y_prediction_train predict(w, b, train_set_x) Y_prediction_test predict(w, b, test_set_x) print(train accuracy: {} %.format(100 - np.mean(np.abs(Y_prediction_train - train_set_y)) * 100)) print(test accuracy: {} %.format(100 - np.mean(np.abs(Y_prediction_test - test_set_y)) * 100))这里所有矩阵乘法的 shape 在运行前就能心算出来w是(12288, 1)X是(12288, 209)w.T X是(1, 209)加b广播成(1, 209)。建议你养成这个习惯——每一个np.dot之前先写注释标出参与运算的 shape这比报错后再去改维度高效得多。4.2 训练准确率与测试准确率的差距说明了什么这个项目里常见的结果是训练准确率接近 100%测试准确率 70% 左右。这个差距本身就是信号单层线性结构在 64×64 像素的猫图分类任务上容量不够换句话说模型太简单学不全猫和非猫的复杂边界。此时不要盲目增加迭代次数因为训练准确率已经很高继续迭代只会过拟合训练集测试准确率不会明显提升。正确的方向是加隐藏层、增大隐藏层神经元数或者做数据增强。反过来如果训练准确率也上不去比如一直卡在 65%先检查归一化和数据展平有没有做对再看 learning_rate 是不是太小导致没收敛。二分定位的思路先确认训练集 loss 能降——不能降是代码 bug 或学习率问题能降但测试集差——是模型容量或正则化问题。4.3 画 loss 曲线PyCharm 里直接用 matplotlib 即可import matplotlib.pyplot as plt plt.plot(np.squeeze(costs)) plt.ylabel(cost) plt.xlabel(iterations (per hundreds)) plt.title(Learning rate str(0.005)) plt.show()注意costs每 100 次迭代只记录一次所以横轴单位是「百次迭代」。曲线如果在前 500 次迭代内快速下降然后趋于平缓说明优化过程正常。如果曲线在前 100 次迭代内先冲高再回落不是 bug是初始权重偏大、前几步跨得太大后面逐步收敛了如果全程几乎水平基本是先检查数据有没有归一化再看是不是梯度计算写错。4.4 学习率对比实验一组可复现的小实验我习惯在调参阶段跑一组学习率对比而不是凭感觉改learning_rate [0.01, 0.005, 0.001, 0.0005]分别训练 2000 次迭代画在同一条轴上比较 loss 曲线。结论通常很典型0.01 在 100 次迭代时开始震荡最终 loss 高于 0.0050.005 平滑收敛0.001 收敛变慢但最终也不错0.0005 明显欠拟合训练到 2000 次还远远没到底。这组实验做完你对学习率的感受会比读十篇文章都直观。5. 避坑指南手写神经网络最常见的五个翻车现场5.1 报错 ValueError: shapes not aligned现象np.dot时提示shapes (1, 209) and (12288, 209) not aligned。原因w和X的 shape 约定不一致。w被初始化成(12288, 1)但传进propagate后如果之前做过w.reshape(X.shape[0], 1)之外的操作比如某处多了一次转置维度就乱了。解决在每个函数入口统一约定——w恒为(n_features, 1)X恒为(n_features, m)b恒为标量。我在optimize和predict里都会先打印w.shape和X.shape再继续确认后把注释写在代码旁避免改着改着失忆。5.2 训练后预测全输出 0现象测试集准确率恰好等于非猫样本占比比如 50% 的测试准确率且所有预测结果都是 0。原因大概率是Y的 shape 没 reshape 成(1, m)导致A - Y广播出来的矩阵不是预期形状梯度方向被带偏。另一个常见原因是数据加载时train_set_y_orig被当成(m,)参与运算逻辑回归学成了常数输出。解决加载数据后立即验证train_set_y.shape (1, 209)。如果发现是(209,)立刻reshape((1, -1))。同时检查classes数组内容确认 0 和 1 的语义没有反转。5.3 loss 变成 NaN现象训练中cost打印出来是nan或者前几步还是正常数字后面突然变nan。原因np.log(0)导致无穷。当模型对某个样本预测概率 A 接近 0 时Y * np.log(A)计算得到-inf再进入np.mean就污染整个 loss。常见诱发因素是 learning_rate 设置过大梯度更新把权重推到了极端值。解决先降低 learning_rate 一两个数量级比如从 0.005 降到 0.001多数情况下能解决如果仍然出现在np.log(A)里加1e-8做数值保护np.log(A 1e-8)。这段场景里我一般先跑 100 次迭代看前几个 cost 是否正常再放量到 2000 次。5.4 训练极慢CPU 单核疯狂但准确率不动现象PyCharm 里运行脚本 CPU 占用很高但 loss 下降极慢2000 次迭代后还在 0.6 以上。原因可能是venv解释器路径不正确实际用的是系统 PythonNumPy 没有走 BLAS 加速也可能是数据没归一化特征值范围差异大梯度下降在椭圆等高线上走折线。解决在 PyCharm 右下角确认解释器指向项目内的venv目录在Settings Project Python Interpreter里检查。NumPy 是否走 BLAS 可以用np.show_config()查看。归一化问题直接用X / 255.即可解决。5.5 Windows 下 h5 文件被占用打不开现象第一次运行成功第二次运行直接报错无法打开 h5 文件。原因h5py.File打开后没有close()Windows 下文件句柄未释放后续进程无法访问。解决写完load_dataset后在函数内部加上train_dataset.close()、test_dataset.close()或者改用with h5py.File(...) as f:的写法。这个坑在 Linux 下不明显Windows 用户会经常遇到值得记牢。6. 从单层到多层给这份资源加上隐藏层和验证集资源原版是单层结构但你在理解透之后完全可以给它加一个隐藏层这是把这份资源吃透的最有效方式。加隐藏层需要改动的地方集中在初始化、前向和反向三处def initialize_with_hidden(n_input, n_hidden, n_output): np.random.seed(1) W1 np.random.randn(n_hidden, n_input) * 0.01 b1 np.zeros((n_hidden, 1)) W2 np.random.randn(n_output, n_hidden) * 0.01 b2 np.zeros((n_output, 1)) parameters {W1: W1, b1: b1, W2: W2, b2: b2} return parameters隐藏层激活函数用 tanh 或 ReLU输出层仍然用 Sigmoid。反向传播公式对应为# 前向 Z1 np.dot(W1, X) b1 A1 np.tanh(Z1) Z2 np.dot(W2, A1) b2 A2 sigmoid(Z2) # 反向 dZ2 A2 - Y dW2 np.dot(dZ2, A1.T) / m db2 np.sum(dZ2, axis1, keepdimsTrue) / m dZ1 np.dot(W2.T, dZ2) * (1 - np.power(A1, 2)) dW1 np.dot(dZ1, X.T) / m db1 np.sum(dZ1, axis1, keepdimsTrue) / m加完隐藏层后我一般会做几件事验证第一把n_hidden从 4 依次调到 10、20观察训练准确率是否逐步上升这能直观感受模型容量对拟合能力的影响第二把训练集切出一部分做验证集用验证集准确率判断是否过拟合——如果训练准确率 98%、验证准确率 70%就是典型的过拟合信号此时可以引入 L2 正则化或 dropout第三对比隐藏层用 tanh 和 ReLU 的收敛速度差异这个数据量下不明显但能帮你建立对不同激活函数的直觉。我自己的使用习惯是每跑一轮就在 PyCharm 的 Python Console 里执行一段print检查 shape同时在项目根目录建一个analysis.py专门记录每次实验的配置与结果相当于实验日志。从那以后我每次拿到别人的课程项目都会强制走一遍「先看数据 shape、再跑基线、最后画 loss 曲线」这三个动作能少踩很多无谓的坑。希望这些记录对你也有用。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/14 15:05:10

2026年网络安全与AI技术岗位薪资趋势分析

1. 行业薪资现状全景扫描2026年技术岗位薪资格局已经呈现出明显的结构性分化,网络安全与人工智能领域持续领跑全行业。根据最新发布的《2026中国数字经济人才发展报告》显示,AI算法工程师岗位中位数薪资首次突破3.2万元/月,较2023年增长42%&a…

2026/10/14 15:00:09

手机远控AI Agent:架构、安全与落地实践

1. 为什么我决定用一台手机管所有 AI Agent先聊点扎心的。我自己的 AI Agent 服务从三台服务器上的十几个任务扩张到几十个之后,最大的痛点不是写 Agent 本身,而是“运维式管理”开始失控。晚上在外面吃饭,手机突然弹出告警说某个 Agent 任务…

2026/10/14 21:33:22

COMSOL三维声表面波驱动液滴模拟:从建模到结果提取全解析

1. 项目概述与核心痛点做微流控数值模拟这几年,声表面波(SAW)驱动液滴一直是个看着简单、做起来坑极多的方向。标题里提到的“comso三维声表面波作用液滴”,实际就是COMSOL Multiphysics里搭三维模型,模拟叉指换能器激…

2026/10/14 21:33:22

大语言模型+接口自动化:从数据生成到失败分析

1. 这个项目到底在解决什么问题先说我观察到的普遍现象。不少团队把接口自动化做成"用例仓库",一开始靠录制工具快速堆出几百条脚本,跑起来绿油油一片,看着很舒心。但上线两个月后就开始变味:接口字段一调整&#xff0c…

2026/10/14 21:33:22

基于BS的绿植领养系统:Thymeleaf服务端渲染与并发控制实战

简介:一套基于 BS 架构的绿植领养系统完整源码项目,面向 Java 学习者与毕业设计、课程设计人群,覆盖从用户注册登录、绿植信息展示到领养申请与后台管理的闭环流程。后端基于 Spring Boot 集成 MyBatis Plus 与 Shiro 权限框架,前…

2026/10/14 21:33:22

应急管理指挥系统源码拆解:Spring Boot与WebSocket调度闭环实战

简介:这是一套面向PHP后端与前端可视化开发者的应急管理综合指挥调度系统实战案例,以可运行源码形式提供,适合想学习大屏可视化与地图API集成的初中级开发者。项目以企业为中心,结合Echarts与百度地图,实现周边5公里资…

2026/10/14 21:33:22

ComfyUI模特姿势更换工作流:从OpenPose骨架到ControlNet参数调优实战

简介:这份源码包面向使用Stable Diffusion进行图像生成的开发者与设计爱好者,聚焦ComfyUI中一键更换模特姿势的工作流实现,适合希望借助节点式操作快速搭建图像处理流程的中初级用户。包内共3个文件,以inscode工程配置、html页面与…

2026/10/14 21:28:21

滑动窗口算法解析:LeetCode最小覆盖子串问题

1. 问题背景与核心挑战这道来自LeetCode的Hard题目要求我们在字符串S中找到包含字符串T所有字符的最短连续子串。举个例子,如果S"ADOBECODEBANC",T"ABC",那么正确结果应该是"BANC",因为它同时包含A…

2026/10/14 11:31:03

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/14 17:41:07

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/14 2:32:55

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/14 0:00:33

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:33

macOS/iOS高精度计时:mach_absolute_time原理与实战

1. 项目概述:为什么mach_absolute_time是 macOS/iOS 时间测量的“黄金标准”在 macOS 和 iOS 开发中,我见过太多人用CFAbsoluteTimeGetCurrent()或NSDate.timeIntervalSinceReferenceDate做性能打点,结果在高精度场景下被系统时钟跳变、NTP 同…

2026/10/14 0:00:33

SCORM课件演示与测试全解析:从API通信到自动化验证

简介:这是一套遵循SCORM标准的交互式课件演示与测试资源,面向E-Learning课程开发者、LMS平台测试人员及在线教学从业者,用于验证课件在不同学习管理系统中的识别、传递与跟踪能力。压缩包共28个文件,约12.07MB,以swf动…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑