教程文档示例工程【免费下载链接】website-archiveArchive of the Coding Train website (first version)项目地址https://gitcode.com/gh_mirrors/we/website-archive点击查看免费下载本文以 Coding Train 网站存档中《Nature of Code》第 10 章 Neural Networks 系列第 12 课10.12-Neural-Networks-Feedforward-Algorithm-Part-1为核心系统讲解神经网络最基础的算法——前馈Feedforward。文章将说明该算法在多层感知机Multi-layered Perceptron中的工作方式并把前馈计算与前几课10.610.11铺垫的矩阵数学联系起来。读完本文你将理解前馈算法的数学本质掌握权重、偏置、激活函数的矩阵表示并能读懂仓库中Toy-Neural-Network-JS风格的predict()完整实现为后续反向传播与训练打下基础。一、课程定位前馈算法在神经网络系列中的位置第 10 章 Neural Networks 系列是《Nature of Code》一书的配套视频课程见 系列索引。10.12 这一课是整条学习链上承前启后的关键节点前置铺垫10.210.3 先构建了单层感知机Perceptron10.410.5 引入多层感知机fully-connected network 全连接网络的概念10.610.11 则系统讲解了实现神经网络所需的矩阵数学10.6-Neural-Networks-Matrix-Math-Part-1 起直到 10.11-Neural-Networks-Matrix-Class-Improvements 对 Matrix 类做了静态方法、print()、map()的改进其中 10.8-Neural-Networks-Updating-Code-to-ES6 把矩阵代码升级为 ES6 的 class 写法。本课内容前馈算法——输入数据在多层网络中的单向传播过程。后续延伸10.13-Neural-Networks-Feedforward-Algorithm-Part-2 用 JavaScript 的NeuralNetwork类实现该算法10.1410.18 则进入反向传播Backpropagation与训练。课程视频编号、日期等元信息记录在文档的 frontmatter 中video_number: 12date: 2018-01-18。另外直播 114.2-revisiting-feedforward-algorithm 也把这一课10.12作为重点内容做了二次讲解。二、前馈算法的核心思想数据如何在多层感知机中流动前馈Feedforward是神经网络最基础、最根本的算法信息从输入层出发逐层向输出层单向流动途中不发生任何回环。它对应的是网络思考推理的过程。在一个**多层感知机Multi-layered Perceptron, MLP**中网络由若干层组成输入层input layer接收外部数据节点数等于特征维度隐藏层hidden layer位于输入与输出之间节点数由设计者指定输出层output layer产生最终结果节点数等于预测目标维度。各层之间全连接前一层的每个节点都与后一层的每个节点相连每条连接都带有一个权重weight每个节点还带有一个偏置bias。从结构推断这正是 10.410.5 所讲的 fully-connected network。前馈计算的每一步都在做两件事加权求和把上一层所有节点的输出乘以对应权重再求和并加上该节点的偏置激活activation把求和结果送入激活函数如 sigmoid得到该节点的输出再作为下一层的输入。当网络只有一个输入层和一个输出层即单层感知机时前馈就是一次加权求和加一次激活而多层网络只不过是把这个过程逐层重复。这就是前馈二字的含义一层算完把结果传给下一层。三、矩阵数学基础为什么前馈要用矩阵10.610.11 系列视频的核心动机是当网络层数变多、节点变多时逐个节点手写加权求和的代码会迅速失控而矩阵乘法可以把整层的加权求和压缩成一条语句。仓库中的矩阵实现 matrix.js 就是这一思想的直接落地class Matrix { constructor(rows, cols) { this.rows rows; this.cols cols; this.data Array(this.rows) .fill() .map(() Array(this.cols).fill(0)); } // ... }与 10.11 课改进的 Matrix 类对应的关键方法与维度规则如下方法作用维度约束static fromArray(arr)把一维数组转成 n×1 列向量矩阵输入数组长度为 nstatic multiply(a, b)矩阵乘积标准矩阵乘法a.cols必须等于b.rows否则报错add(n)逐元素相加矩阵或标量矩阵相加时行列必须完全一致multiply(n)逐元素相乘Hadamard 积或标量乘矩阵相乘时行列一致map(func)/static map(matrix, func)对每个元素套用函数用于激活无static transpose(matrix)转置矩阵无randomize()用Math.random() * 2 - 1随机填充取值区间 [-1, 1)其中static multiply(a, b)的实现matrix.js直接体现了矩阵乘法的点积运算static multiply(a, b) { // Matrix product if (a.cols ! b.rows) { console.log(Columns of A must match rows of B.); return; } return new Matrix(a.rows, b.cols).map((e, i, j) { // Dot product of values in col let sum 0; for (let k 0; k a.cols; k) { sum a.data[i][k] * b.data[k][j]; } return sum; }); }理解前馈算法本质上就是理解上面这张表里的矩阵操作如何按顺序组合。四、前馈算法的矩阵表达与逐步流程用矩阵语言描述前馈算法设输入为列向量I输入层到隐藏层的权重矩阵为W_ih隐藏层偏置为B_h隐藏层输出为H隐藏层到输出层的权重矩阵为W_ho输出层偏置为B_o最终输出为O。那么一次前馈就是H activation(W_ih · I B_h) # 隐藏层 O activation(W_ho · H B_o) # 输出层其中·是矩阵乘法是逐元素加法activation 逐元素作用于矩阵的每个元素。仓库中 nn.js 的predict()方法把这个流程完整实现为 JavaScript 代码与 10.13 课在NeuralNetwork类中实现算法的代码同源predict(input_array) { // Generating the Hidden Outputs let inputs Matrix.fromArray(input_array); let hidden Matrix.multiply(this.weights_ih, inputs); hidden.add(this.bias_h); // activation function! hidden.map(this.activation_function.func); // Generating the outputs output! let output Matrix.multiply(this.weights_ho, hidden); output.add(this.bias_o); output.map(this.activation_function.func); // Sending back to the caller! return output.toArray(); }逐行拆解这一流程正是 10.12 课所讲算法的完整落地输入转矩阵Matrix.fromArray(input_array)把形如[x1, x2]的输入数组转成 2×1 列向量隐藏层加权求和Matrix.multiply(this.weights_ih, inputs)计算W_ih · I。这里的weights_ih是hidden_nodes × input_nodes的矩阵与input_nodes × 1的输入向量相乘得到hidden_nodes × 1的中间结果加偏置hidden.add(this.bias_h)逐元素加上隐藏层偏置激活hidden.map(this.activation_function.func)对每个元素套用激活函数输出层加权求和Matrix.multiply(this.weights_ho, hidden)其中weights_ho是output_nodes × hidden_nodes矩阵输出层加偏置并激活得到output_nodes × 1的最终结果转回数组output.toArray()把矩阵拍平成一维数组返回给调用者。注意第 4、6 步的map是 10.11 课引入的 Matrix 改进能力——正是它让逐元素激活从嵌套 for 循环变成了声明式的一行代码。五、激活函数前馈中的非线性来源如果只做加权求和而不做激活多层网络无论堆多少层本质上都等价于一个线性变换无法解决非线性问题这正是 XOR 等经典难题需要的。激活函数为网络注入非线性。仓库中的ActivationFunction类nn.js把激活函数与其导数打包既服务于前馈也服务于后续反向传播class ActivationFunction { constructor(func, dfunc) { this.func func; this.dfunc dfunc; } } let sigmoid new ActivationFunction( x 1 / (1 Math.exp(-x)), y y * (1 - y) ); let tanh new ActivationFunction( x Math.tanh(x), y 1 - y * y );sigmoidf(x) 1 / (1 e^(-x))输出压缩到 (0, 1)其导数f(y) y(1-y)可仅由输出值算出计算非常方便tanh输出压缩到 (-1, 1)导数f(y) 1 - y²。NeuralNetwork默认使用 sigmoidsetActivationFunction(func sigmoid)也允许随时切换成 tanh。在 Processing/Java 版本 Neural_Network.pde 中sigmoid 以float sigmoid(float x)与float dsigmoid(float y)的独立函数形式出现作用完全相同。六、参数初始化前馈从哪里开始前馈运算的输入是权重和偏置因此这些参数的初始值决定了网络第一次思考的结果。在NeuralNetwork构造函数nn.js中constructor(input_nodes, hidden_nodes, output_nodes) { this.input_nodes input_nodes; this.hidden_nodes hidden_nodes; this.output_nodes output_nodes; this.weights_ih new Matrix(this.hidden_nodes, this.input_nodes); this.weights_ho new Matrix(this.output_nodes, this.hidden_nodes); this.weights_ih.randomize(); this.weights_ho.randomize(); this.bias_h new Matrix(this.hidden_nodes, 1); this.bias_o new Matrix(this.output_nodes, 1); this.bias_h.randomize(); this.bias_o.randomize(); // ... }weights_ih维度为hidden_nodes × input_nodesweights_ho维度为output_nodes × hidden_nodes两组偏置bias_h、bias_o均为 n×1 列向量全部通过randomize()在[-1, 1)区间随机初始化。维度设计完全与前馈公式咬合W_ih的列数等于输入节点数、行数等于隐藏节点数才能与输入向量相乘得到隐藏层向量W_ho同理。学习率默认0.1setLearningRate(learning_rate 0.1)在本课的前馈推理阶段不会用到但会作为超参数影响后续 10.1410.18 课的训练。七、Processing / Java 版本对照实现为了让不同语言生态的读者都能对照学习仓库还保留了 Processing 版本的前馈实现。 Neural_Network.pde 的feedforward()方法与 JS 版逐步骤对应float[] feedforward(float[] inputArray) { input Matrix.fromArray(inputArray); //generating hidden inputs hidden Matrix.Product(ihWeights, input); hidden.add(hbias); //activation function for hidden nodes! for (int i 0; i hidden.rows; i) { for (int j 0; j hidden.cols; j) { float val hidden.values[i][j]; hidden.values[i][j] sigmoid(val); } } //generating hidden output output Matrix.Product(hoWeights, hidden); output.add(oBias); //activation function for ouput nodes! for (int i 0; i output.rows; i) { for (int j 0; j output.cols; j) { float val output.values[i][j]; output.values[i][j] sigmoid(val); } } //generating the output array return output.toArray(); }两份实现的结构完全一致Matrix.Product对应 JS 的Matrix.multiply标准矩阵乘积add对应逐元素加偏置唯一的差异是 Java 版没有map能力因此激活函数用双重 for 循环逐元素套用。这恰好印证了 10.11 课为 JS 版 Matrix 引入map()的动机——消除这类样板循环。Java 版还附带mutate(rate)变异方法为神经进化第 11 章 Neuroevolution预留了接口。八、实战验证XOR 问题中的前馈调用前馈算法最经典的验证场景是 XOR异或问题输入[0,0]、[0,1]、[1,0]、[1,1]期望输出0, 1, 1, 0——这是一个线性不可分问题单层感知机无法解决必须依赖带隐藏层的多层感知机。在 CC_092_xor 挑战中网络被构造为new NeuralNetwork(2, 4, 1)2 个输入节点、4 个隐藏节点、1 个输出节点训练数据如下sketch.jslet training_data [ { inputs: [0, 0], outputs: [0] }, { inputs: [0, 1], outputs: [1] }, { inputs: [1, 0], outputs: [1] }, { inputs: [1, 1], outputs: [0] } ];draw()循环中每帧随机抽取样本训练然后用nn.predict(inputs)对画布上每个坐标点做前馈推理并把输出值y01 之间映射为灰度fill(y * 255)绘制出来sketch.js。随着训练推进画面会逐渐收敛成 XOR 的棋盘形态。这里predict()内部执行的正是本文第三节拆解的前馈流程——它是理解整个挑战如何工作的关键入口。九、从前馈到学习下一步是什么前馈解决的是给定参数如何得到输出而学习训练要解决的是如何调整参数让输出逼近目标。这一步由反向传播Backpropagation完成对应系列后续课程10.13-Neural-Networks-Feedforward-Algorithm-Part-2用NeuralNetwork类完成前馈算法的 JS 实现10.14-Neural-Networks-Backpropagation-Part-1 至 10.18反向传播算法系列。在 nn.js 的train()方法中可以看到两者如何衔接train()先执行前馈得到hidden与outputs再计算误差ERROR TARGETS - OUTPUTS随后用激活函数的导数dfunc计算梯度并反向更新两层权重与偏置。前馈为反向传播保留了hidden这一中间结果——这正是前馈算法必须完整走一遍的价值所在。如果你想继续深入学习整个系列可参考 第10章系列索引 与直播回顾 114.2-revisiting-feedforward-algorithm。赞分享教程文档示例工程【免费下载链接】website-archiveArchive of the Coding Train website (first version)项目地址https://gitcode.com/gh_mirrors/we/website-archive点击查看免费下载相关推荐前馈神经网络完全指南从感知机到深度网络的终极教程 前馈神经网络完全指南从感知机到深度网络的终极教程 前馈神经网络 是深度学习的基础构建模块也是现代人工智能技术的核心组成部分。如果你正在寻找一份完整的前文档教程机器学习深度学习遗传算法实战Nature of Code 第 9 章遗传算法系列website-archive 归档解读遗传算法实战Nature of Code 第 9 章遗传算法系列website archive 归档解读 本篇技术指南以 website archive教程文档示例工程《神经网络与深度学习》第4章前馈神经网络反向传播算法详解《神经网络与深度学习》第4章前馈神经网络反向传播算法详解 你是否还在为多层神经网络的参数更新而困扰反向传播算法Backpropagation作为训练前馈创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考