python神经网络编程入门(二)----从XOR死穴到生物神经元——为什么单一直线搞不定,要堆一堆神经元?
本文属于《Python神经网络入门零基础保姆级路线图》专栏上一篇python神经网络编程入门一—— 分类器下一篇python神经网络编程入门三----矩阵乘法真是神经网络的“偷懒神器”完整目录 更新记录《Python神经网络入门零基础保姆级路线图附全系列免费源码》这篇文章咱们接着上回说。上回我们学会了怎么让一条直线ykx自己学着调整去把两类点分开。但这一期咱们得直面一个“残酷”的现实一条直线真的不够用。咱们不讲高深数学就带大家玩一个“逻辑游戏”看完你立马就能明白为什么后来需要“神经网络”那么多个节点。1. 先复习什么是“一刀切”想象你是一个幼儿园老师手里有一根长长的魔法直尺。你的任务是用这根直尺把桌上的红色积木和绿色积木完全分开。上期我们做的所有事其实就是让计算机找到这根直尺最合适的位置角度和距离。只要两类积木分别堆在桌子的两边这根直尺线性分类器非常好使。但问题来了——生活往往没那么乖巧。2. 两个“听话”的好学生AND与和 OR或在计算机的“数字世界”里只有0和1有两个最基本的逻辑咱们先看看直尺能不能搞定它们。1AND与—— 两个都要真只有A和B同时是1真的时候结果才为真绿色。比如你妈说“写完作业 AND 收拾好书包才能看电视。”只要有一个没做就不能看红色。2OR或—— 有一个就成只要A或B有一个是1真结果就为真绿色。比如你爸说“下雨 OR 降温就给你送外套。”只要满足一条外套就送来了。你会发现一件神奇的事对于AND和OR你手里的魔法直尺直线总能轻轻松松地把绿色和红色一刀切开比如AND斜着画一条线把右上角单独框出来就行。所以单一直线能解决这俩问题没毛病。3. 大麻烦来了XOR异或—— “有你没我”现在来个刺头叫XOR异或。这玩意儿很“矫情”两个输入相同都是0或都是1时输出假红两个输入不同一个是0一个是1时输出真绿。用大白话讲“要么你请客要么我请客但咱俩绝不能同时掏钱”咱们还是把(0,0)(1,0)(0,1)(1,1)这四个点画在网格上涂上颜色(0,0)相同 →红色(1,1)相同 →红色(1,0)不同 →绿色(0,1)不同 →绿色4. 拿起直尺比划一下傻眼了现在你再拿起那根“魔法直尺”直线试着在图上画一道线把红色和绿色分开。你会发现无论你把尺子横着画、竖着画还是斜着画只要它是直的你永远会误伤一个画横线上面有一个红一个绿分不开。画竖线右边有一个红一个绿分不开。画斜线总有一个角落的颜色不对。这就是线性分类器的“死穴”——它天生只能处理“线性可分”的问题而XOR是“线性不可分”的。如果真实世界里的数据长得像XOR这样比如判断一个人是否适合结婚光有钱不行光帅也不行没钱不帅也不行必须又有钱又帅……等等这个逻辑好像反了但总之数据是交叉纠缠的用我们上一期学的单一直线函数算到天荒地老也算不对5. 怎么破局一条不行那就两条既然一条直尺一刀切不开那咱们用两把尺子切两刀行不行回到XOR图我画第一条线比如竖线x0.5把左边的(0,0)(0,1)和右边的(1,0)(1,1)分开。再画第二条线比如横线y0.5把下边的(0,0)(1,0)和上边的(0,1)(1,1)分开。然后我把这两个结果组合起来看我们先看左边竖线分出左右再看下面横线分出上下。如果两次判断的结果一结合是不是就能把“对角线上相同”和“对角线上不同”给揪出来了这就是神经网络的核心思想6. 总结一下重点直尺的极限现实中有很多数据像XOR一样“纠缠不清”单一直线线性分类器这辈子都解不开这类问题。破局的法宝既然一个不行那就堆数量用多个分类器多条直线组合起来层层递进就能切出各种奇形怪状的区域。下次当你听到“神经网络”这个名字时别被唬住。本质上它就是一大堆“直尺”线性分类器堆在一起通过某种巧妙的方式组合起来解决单把尺子搞不定的复杂问题。那么问题来了这么多“尺子”堆在一起怎么让它们学会配合呢总得有个总指挥吧咱们先抛开代码去看看大脑是怎么干这活的——因为神经网络本来就是抄袭大脑的在硬着头皮写代码之前咱们先不急着堆数字。先抬头看看自然界——因为神经网络本来就是抄袭大脑的。这一期咱们就把它“抄袭”的原型扒个底朝天。7. 计算机那么快为啥连只鸽子都不如你可能会想现在的电脑主频都飙到几G赫兹了算个加减乘除快到飞起难道还比不过一坨“肉乎乎”的大脑但现实很打脸一只鸽子的大脑比世界上任何超级计算机都更擅长识别它面前的食物是不是面包屑。一台顶级电脑要识别一张猫图得费老大劲跑几亿次运算而你家那只傻狗0.1秒就认出你是主人。为什么电脑传统程序像个死心眼的会计。必须一步一步、毫不含糊地算串行处理。输入稍有模糊比如图片有点模糊它就抓瞎了。动物大脑像个包容的居委会大妈。它同时处理一大堆信息并行处理而且允许模糊——哪怕只看清半张脸它也能猜出你是谁。所以速度不是关键架构玩法才是关键8. 大脑里的“最小单位”——神经元长啥样咱们把大脑拆开看最基础的小零件叫神经元。它长得很像一棵倒着的树树突输入端像树根或树枝负责接收前面神经元传来的电信号。细胞体处理器把收到的信号汇总一下。轴突输出端像一根长长的电线把汇总后的结果传给下一个神经元。9. 几个神经元能干多大的事数字游戏你可能觉得大脑有860亿个神经元当然厉害。但咱们看看“小角色”一只果蝇只有10万个神经元。但它能飞、能躲苍蝇拍、能找烂水果吃。这10万个元件现代计算机用传统程序至今难以完美模拟。一种线虫只有可怜巴巴的302个神经元。但就靠这302个小零件它能蠕动、能找到食物、能避开有害物质。用传统编程逻辑去写一个同样灵活的机器人几十万行代码都搞不定这说明什么说明神经元这种“并行模糊”的算法架构效率极高。所以我们学它绝对没错10. 神经元的“暴脾气”——不到阈值不放电现在咱们看单个神经元具体怎么工作。它和咱们上期学的线性函数ykx不一样。线性函数是你给个输入无论多小我都给你吐个输出哪怕是0.001。生物神经元很“傲娇”如果输入信号太弱它绝不反应直接忽略这叫抑制噪声。只有输入信号累积到足够强超过某个门槛阈值它才“啪”地一下放电把信号传出去。这就像往杯子里倒水。水没满没到阈值一滴都不往外溢水装满了超过阈值猛地就溢出来了。11. 改进一下不要“冷冰冰”的台阶要“丝滑”的S形上面的阶跃函数虽然像生物但在数学上太“硬”了——在门槛那里突然从0蹦到1自然界很少有这么生硬的转折。所以科学家们用了下面这个S形函数Sigmoid函数它平滑、圆润更像大自然的风格第一次看见这个公式的读者别怕它就是个“纸老虎”。咱们把它拆开公式里的 e 是个常数约等于2.71828不用记知道是个无限不循环小数就行。−x 就是取反。假设 x2那么就是 e 的平方分之一。然后加1最后用1除以它。咱们来套个数试试当输入 x0 时任何数的0次方都是1所以1。代入公式看当输入为0时输出正好是0.5正中间。当输入很大时比如 x10趋近于0y 趋近于1。当输入很小时比如 x−10巨大分母巨大y 趋近于0。为什么偏要用这个S函数除了长得像生物神经元还有一个极其重要的原因它求导算斜率特别简单这在我们以后做“反向传播”时能省下大量的算力。这是祖师爷赏饭吃的地方。12. 神经元不止一个输入——它是“话痨”咱们前面学的线性分类器只接受一个 x横坐标。但生物神经元有好多“树突”它同时接收好多个信号比如这个神经元同时接收来自A、B、C三个前一个细胞的信号。那它怎么处理呢很简单全部加起来我们把所有输入信号相加得到一个总和 x然后把这个总和 x 扔进上面的 S 函数里算出最终输出 y这就很有意思了哪怕单独一个信号很小不够触发阈值但是三个信号加起来很大超过了阈值神经元照样会“啪”地放电激发这就解释了为什么大脑能处理“模糊逻辑”——三个臭皮匠顶个诸葛亮。单独看不清凑一起就看清楚了。13. 最重要的“调节旋钮”连接权重Connection Weights好现在问题来了我们搭建了这么多神经元互相像蜘蛛网一样连着我们到底该“调整”什么东西才能让它学会做数学题呢我们不能去改生物结构吧所以人工神经网络里我们调节的是连接强度也就是权重Weight。想象一下前后两层神经元之间每一条连线上都有一个音量旋钮权重。如果旋钮拧得大比如 5.3信号通过时就放大对下一个神经元影响巨大。如果旋钮拧得小比如 0.1信号通过时就衰弱几乎不影响下一个神经元。重点解释一下这个下标的读法极其重要比如你看到一个符号前面的数字2表示信号来自第2层前一层的节点。后面的数字3表示信号去往第3层后一层的节点。再看一个表示“第1层节点”传给“第2层节点”的那条线上的权重。那数学上怎么算呢下一层某个节点的输入总和 X 就等于也就是前一层每个节点的输出值乘以它们之间连线的权重最后全部加起来。这就是神经网络最核心的“加权求和”。14. 为什么要把“蜘蛛网”连得这么密全连接你看上面那张图密密麻麻全是线你可能会想“这也太浪费了吧很多线是不是根本没用”问得好咱们之所以设计成每一层都跟下一层全部连起来全连接有两个偷懒又聪明的理由方便写代码这种规规矩矩的“网格状”连线用计算机的矩阵乘法算起来飞快几行代码就搞定了。让它自己“断舍离”虽然连线很多但我们在训练的时候会让计算机自己去调这些权重。如果某条线没用神经网络会自动把它的权重调到接近 0大家想想任何数乘以 0 都得 0信号完全传不过去。这就相当于这条连线被“物理断开了”。所以刚开始多连几条冗余的线无所谓反正最后没用的线会自动“失联”。这省了我们人类设计电路的大量脑细胞。15.总结一下核心思想计算机快但死板大脑慢但灵活并行 模糊处理。单个神经元接收一堆输入求和 - 超过阈值 - 通过 S 函数Sigmoid产生平滑的输出。公式其中 x 是所有输入的和。学习的关键调整连接线上的权重w。放大权重 放大信号缩小权重 减弱信号权重归零 断开连接。好了到这里我们已经把“零件”认全了一个神经元 ≈ 一个会“加权求和”再“S形压扁”的小盒子一个神经网络 ≈ 一大堆这样的小盒子像蜘蛛网一样连在一起学习的“旋钮” ≈ 连接线上的权重调大就放大信号调小就减弱信号。但问题来了——如果这个“蜘蛛网”很大很大比如3层、每层100个神经元我们要算的东西有多少手动算光是第一层传到第二层就要做100 × 100 10000次乘法再加10000次加法。这还只是一层要命的是第二层传到第三层还要再来10000次……手算到天黑都算不完。那怎么办总不能把计算机累死吧别怕数学家早就发明了一个“偷懒神器”——矩阵乘法。它能把上面那一大堆密密麻麻的乘法和加法压缩成一行字母就这么简单下一期咱们就翻开这个“偷懒神器”的使用说明书看看怎么用一张“数字表格”轻轻松松算出整个神经网络前向传播的结果——哪怕它有100层我们也只写一行公式顺便剧透一下算完前向传播还不算完我们算出来的结果和正确答案有差距这个“差距”该怎么一层一层传回去调整权重等我们把矩阵这个工具用顺手了就进入整个神经网络最核心的“绝活”——误差反向传播。咱们一步一步来不着急。代码片段1简单神经网络 — 解决 XOR 问题承上启下还记得上一篇文章说的吗一条直线搞不定 XOR。这里用代码搭建一个极简神经网络3层输入层 → 隐藏层 → 输出层让它自己学会 XOR。这个代码稍微长一点但别怕——它刚好把今天讲的神经元和上期讲的XOR难题串起来了。跑完你会直观看到一个神经元不行但多个神经元配合起来就行了import numpy as np # ---------- 1. 定义Sigmoid函数及其导数 ---------- def sigmoid(x): return 1 / (1 np.exp(-x)) def sigmoid_derivative(x): return x * (1 - x) # 这是S函数求导的简化形式 # ---------- 2. 准备XOR数据 ---------- # 四个样本输入A、输入B → 期望输出 X np.array([[0, 0], [0, 1], [1, 0], [1, 1]]) y np.array([[0], # 0 XOR 0 0 [1], # 0 XOR 1 1 [1], # 1 XOR 0 1 [0]]) # 1 XOR 1 0 # ---------- 3. 搭建神经网络 ---------- # 输入层2个节点隐藏层4个节点输出层1个节点 input_size 2 hidden_size 4 output_size 1 # 随机初始化权重这就是那些旋钮的初始值 np.random.seed(42) # 固定随机种子让大家跑的结果一样 W1 np.random.randn(input_size, hidden_size) # 输入→隐藏的权重 W2 np.random.randn(hidden_size, output_size) # 隐藏→输出的权重 # ---------- 4. 训练让网络自己调整权重 ---------- learning_rate 0.5 epochs 10000 for epoch in range(epochs): # --- 前向传播信号往前走 --- hidden_input np.dot(X, W1) # 输入层 → 隐藏层加权求和 hidden_output sigmoid(hidden_input) # 过S函数 final_input np.dot(hidden_output, W2) # 隐藏层 → 输出层加权求和 final_output sigmoid(final_input) # 过S函数 # --- 计算误差看看差多少 --- error y - final_output # --- 反向传播把误差传回去调整权重 --- # 这部分是下一篇文章的重点这里先直接给出公式 d_output error * sigmoid_derivative(final_output) error_hidden d_output.dot(W2.T) d_hidden error_hidden * sigmoid_derivative(hidden_output) # 更新权重拧动旋钮 W2 hidden_output.T.dot(d_output) * learning_rate W1 X.T.dot(d_hidden) * learning_rate # ---------- 5. 看结果 ---------- print(训练完成XOR预测结果) for i in range(4): pred final_output[i][0] print(f输入 ({X[i][0]}, {X[i][1]}) → 期望 {y[i][0]} → 网络预测 {pred:.4f})看到没四个预测值都非常接近正确答案0或1。虽然我们还没有正式讲“反向传播”但代码已经跑通了——这就是神经网络的力量多个神经元配合搞定了单一直线搞不定的XOR