共计 7029 个字符,预计需要花费 18 分钟才能阅读完成。
❝
脑机接口时代曾经到来?作为万能分类器,各类神经网络在近年有了长足的倒退
比方卷积神经网络,也在现在的图片辨认畛域获得最好的问题
明天的指标是 JS 手撕通用神经网络????,无任何库依赖
❞
神经网络概述
- 人工神经网络(Artificial Neural Networks,简写为 ANNs)也简称为神经网络(NNs)或称作连贯模型(Connectionist Model),是对人脑或天然神经网络(Natural Neural Network)「若干」 根本个性的形象和模仿。
- 俺们的神经元看上去很厉害
- 目前在神经网络钻研办法上已造成多个流派,最富有成果的钻研工作包含:多层网络 BP 算法,Hopfield 网络模型,自适应共振实践,自组织特色映射实践等。人工神经网络是在古代神经科学的根底上提出来的。它尽管反映了人脑性能的基本特征,但远不是天然神经网络的真切刻画,而只是它的某种简化形象和模仿。
- 明天大抵的学习流程,日常问题 => 感知机 => 向前流传 => 向后流传 => 多层 BP 神经网络 -> 案例 => 小结
- 为啥要学习?机器始终在学习,作为人类请放弃学习????
日常数据分类问题
简略感受一下
- 先看个动图
- 上图的过程,就是通过机器学习,最终确定一组参数,最初将 2 组数据尽可能离开????
- 通过一条直线进行分类,就是线性分类器
再来看一个经典分类例子
- 逻辑电路中咱们都学过与门,或门,与非门,或非门,异或门
- 「题外话」:门电路造成组合逻辑,触发器组合造成时序逻辑,2 者独特实现数字逻辑,最终造成俺们的 CPU????。。。
- 输出 => 输入 的真值表,如下图
- 除了 XOR,其余逻辑操作真值表 都能够被一条直线划分,如下图
- 1 条直线根本闯天下,但 XOR 呢,无奈用一条直线搞定,那应该如何????? 一朵小乌云?
- 看一下 无奈线性分类的动图,机器一直学习,但陷入震荡????
相似 XOR 分类的解决方案
- 咱们能够用各种机器学习实现,类 XOR 数据集分类,如下图
- 明天要用的是上图中,左下模式 - 神经网络,实现非线性分类器????
人工神经网络
感知机
- 感知机(perceptron)是由美国学者 Frank Rosenblatt 在 1957 年提出来的,感知机是神经网络(深度学习)的起源算法
- 能够简略的认为一个感知机就是一个神经元,承受输出,解决后输入,也能够输出到下一个感知机????
- 一个感知机,由多个输出,其中,参数 w 叫做权值向量 (weight),b 称为偏置 (bias),activation function 是激活函数,最终得出 Oj
- 留神 b 在上图没有明确标注出, 能够认为是一个截距, 每个感知机对应 1 个 b
- 整个感知机,了解起来是很简略的,他模仿了生物神经元的特定性能????
- 下面的 f(x),你能够简略认为就是一条直线,所以对于除了 XOR 的逻辑门,咱们能够通过 1 个感知机进行分类
- 上面咱们会用 JS 建设一个感知单元类。
- 神经网络次要有三个基本要素:「权重、偏置和激活函数」,咱们的指标就是调整这 3 个参数
激活函数
- 起非线性映射的作用,其可将神经元的输入幅度限度在肯定范畴内,个别限度在(-1~1)或(0~1)之间。最罕用的激活函数是 Sigmoid 函数。
- Sigmoid 函数在之前文章中有提及
- 上面代码中咱们会实现,3 种激活函数,及其导数
多层感知机 - 神经网络
- 先看动图
- 上图有 1 层输出层,2 层暗藏层,1 层输入层
- 输出层有 2 个维度输出,输入层有 2 个维度输入
- 大部分的分类工作,用 2 层暗藏层网络足矣
正向流传
- 多层神经网络的正向流传大概是这样的:
- 输出的手写数字,通过 2 层,暗藏层后,取得了 0-9,10 个分类
- 为啥会失去 0 -9,那是由外面的 w 参数,b 参数,激活函数,确定的。你要做的只是乘起来求和
- 784 是什么鬼?28×28,每张图片的像素个数 ????,这里应用了 one-hot 独热编码
- 如何确定这些参数值呢?应用反向流传,也就是常说的学习(训练)
反向流传
- 如果正向流传是小学生,那反向流传一下就跳到了大学预备班????,可能这就是所谓后方高能吧
- 咱们尽可能简略的叙述这个过程
复合函数的偏导 链式法则
- 为啥要这么简单?
- 因为在 w,b 不确定的时候,咱们先通过正向流传得出一个值,这个值和实在的值的差,造成一个损失函数 Loss()
- 咱们要做的就是 调整 这些 w 和 b 值,求 Loss 函数的最小值
- 咱们通过几百上千次的微调 这些 w 和 b 来让残差(理论和预测的差)变小
- 而这些微调,咱们用数学语言就是求 L 函数的 w 偏导 b 偏导
- 整个过程如下:
- “正向流传”求损失,“反向流传”传误差。同时,神经网络每层的每个神经元都能够依据误差信号修改每层的 w 和 b 的值
- 上图的 δ 就是梯度,别搞错是一个向量哦????
- 了解这个过程,写出代码并不难????
- 而这个一直微调的过程咱们叫梯度降落法:
- 上图对 J 函数求,θ0 和 θ1 的偏导,每次对多个参数调一点点,摸索着,找到复合函数的最小值 ????
- 最初,依据这些梯度值,更新 w,b 参数调整后的新值,进入下一轮正向流传
- 多层神经网络好比丹炉,而咱们失去的参数好比金丹。于是 AI 工程师,又被成为炼丹师
干货,上代码
- 首先是辅助函数
function
activeFunc (
x, type =
'relu') {
let lamb =
1.0507009873554804934193349852946
let alpha =
1.6732632423543772848170429916717
let temp =
0
switch (type) {
case
'sign':
return x >=
0 ?
1 :
-1
case
'sigmoid':
return
1 / (
1 +
Math.exp(-x))
case
'tanh':
return (Math.exp(x) -
Math.exp(-x)) / (Math.exp(x) +
Math.exp(-x))
case
'relu':
return x >
0 ? lamb * x : lamb * alpha * (Math.exp(x) -
1)
case
'diffsigmoid':
x =
1 / (
1 +
Math.exp(-x))
return x * (1 - x)
case
'difftanh':
x =
Math.tanh(x)
return
1 - x **
2
case
'diffrelu':
return x >
0 ?
1 : alpha *
Math.exp(x)
}
}
function
mse (realArr = [], predArr = []) {
return (
realArr.reduce(
(a, b, idx) => a + (b - predArr[idx] ||
0) **
2,
0) /
realArr.length
)
}
// 穿插熵损失函数备选
function
crossEntropy (a, y) {
return -y *
Math.log(a) - (1 - y) *
Math.log(1 - a)
}
- 3 个罕用激活函数及其导数函数,做了一个 6 合 1 的函数,目标为了处处可微,并是输入非线性化。你也能够离开写
- 这点很重要否则,线性组合还是线性的,模型无奈进行简单拟合
- mse 平方差求和,目标为了计算残差,更好的察看模型逐渐收敛的过程
- 感知机类
class
Perceptron {constructor (inputObj = {}) {this.config = inputObj}
set config (o) {
this.type = o.type ||
'hidden'
// input hidden output 神经元的类型 cov pool
this.inputDimNum = o.inputDimNum ||
2
// 输出维度
this.outputDimNum = o.outputDimNum ||
2
// 输入维度
this.inputArr = []
this.beforeOutput =
0
this.output =
0
this.activeFunc = o.activeFunc ||
'sigmoid'
this.wb =
o.wb ||
$.math.genRange(
1,
this.inputDimNum,
0).map(
x =>
randMinMax(
-
Math.sqrt(
1 /
this.inputDimNum),
// 传统的初始化种子
Math.sqrt(
1 /
this.inputDimNum)
)
)
//w 向量 0.5
if (!o.wb)
this.wb.push(1)
//b 设置成 1
this.wbT = $.math.mat.transpose([this.wb])
// w+ b 转置矩阵
this.derivative =
0
// 导数值
}
setWb (a) {
this.wb = a
this.wbT = $.math.mat.transpose([this.wb])
// w+ b 转置矩阵
}
predict (inputArr) {
if (inputArr.length <
this.inputDimNum)
throw
new
Error('输出维度太少')
this.inputArr = inputArr
this.beforeOutput = $.math.mat.mul([[...inputArr,
1]],
this.wbT)[0][0]
this.output = activeFunc(
this.beforeOutput,
this.activeFunc)
return
this.output
}
}
- 感知机类,次要实现 向前流传,就是属性多,其余很简略????
- 这里应用 predict 来实现 向前流传
- 向后流传,须要乘以误差矩阵,与偏导的乘积,作为上一层的误差矩阵,从尾部想神经网的头部传输
function
backPropagation (realArr) {
if (
typeof realArr ===
'number') {realArr = [realArr]
}
// 第 L 行
let L = networkArr.length -
1
let deltaArr = []
// 计算偏导
for (
let i = L; i >
0; i--) {deltaArr[i] = []
if (i === L) {
for (
let d =
0; d < networkArr[i]; d++) {deltaArr[i].push((networkObj[i][d].output - realArr[d]) *
activeFunc(networkObj[i][d].beforeOutput,
'diffsigmoid')
// 最初一层的误差
)
}
}
else {let weightArr = []
for (
let k =
0; k < networkArr[i +
1]; k++) {
weightArr.push(networkObj[i +
1][k].wb)
}
let weightArrT = $.math.mat.transpose(weightArr)
for (
let d =
0; d < weightArrT.length -
1; d++) {deltaArr[i][d] =
0
weightArrT[d].map(
(x, idx) => {deltaArr[i][d] +=
deltaArr[i +
1][idx] *
x *
activeFunc(networkObj[i][d].beforeOutput,
'diffsigmoid')
// 矩阵点乘
})
}
}
}
// 更新每一层的每个 wb 值
for (
let i = L; i >
0; i--) {
for (
let k =
0; k < networkArr[i]; k++) {let wbArr = networkObj[i][k].inputArr.map(
(x, idx) => {return x * deltaArr[i][k]
})
wbArr.push(deltaArr[i][k])
networkObj[i][k].wb.map(
(x, idx) => {wbArr[idx] = x - learnRate * wbArr[idx]
})
networkObj[i][k].setWb(wbArr)
}
}
}
- 学习 Train 的过程,非常简单暴力
let epoch =
1e5
// 世代 你也能够认为是迭代次数
for (i =
0; i < epoch; i++) {let mseArr = []
for (
let k =
0; k < data.length; k++) {
// 数据行数
forwardPropagation(data[k])
// 每个数据的维度
backPropagation(dataTag[k])
// 每个数据的分类标记
}
}
- 其次你须要一个 createNetwork 函数循环产生多个,感知机实例。代码太简略就列举了
- 对,这就完了,你的丹炉曾经搞定,能够开始炼丹了。????
- 以上矩阵基本操作,封装在 meeko 的代数外围库中。
实例执行
XOR 分类
- xor 无奈通过一般回归进行宰割,咱们看看本人的丹炉成果
- 2,2,2 1 个隐层网络
- 在 14485 次迭代后,误差忽然减小,这也是大部分神经网络学习的,特色,要急躁炼丹
- 最初输入的 就是金丹了,间接,拿这些 w 和 b 的参数,就能够间接断言 XOR 操作了
- 有了丹药,输入是霎时的。这也是大部分同学在网上下载他人训练好的模型的用法????
鸢尾花分类
- 咱们应用之前的 150 个 鸢尾花数据集,对花的种类进行分类
- 咱们把 3 类花,进行 one-hot 编码 [1,0,0] [0,1,0] [0,0,1] 代表 3 类,输出 4 维
- 在之前的《特色工程文章中》,咱们应用 PCA 将鸢尾花的维度从 4 维升高到了 2 维
- 本次,咱们先不降维,间接暴力应用神经网络分类????
- 咱们创立一个 10 个神经元的暗藏层
let networkArr = [
4,
10,
3]
networkObj = createNetwork(networkArr)
// 循环产生多层多个感知机
- 139 秒后,咱们在 87170 次迭代后,失去了满足阀值 eps 的丹药
- 咱们调整参数创立新的神经网络,2 个暗藏层,1 个有 10 个神经元的,1 个有 3 个神经元
let networkArr = [
4,
10,
3,
3]
// 这个构造是看到 matlab 鸢尾花例子用了这个参数
networkObj = createNetwork(networkArr)
- 因为随机参数造成的差别,广泛来说 4,10,3,3 构造会比 4,10,3 好一些,训练工夫随网络变大而迅速减少。
- 但同样的 epoch,多层能够用更多的 参数可调,当然对于不同的数据集 状况有所不同。
- 几次运行下来,会发现常常有 1 个点 [6, 2.7, 5.1, 1.6] 没有被正确辨认
- 咱们作图看一下
- 这个点是在 2 个花种类之间,人工筛选也会呈现谬误。太高的网络层数 一样会造成过拟合,这个丹药(参数),最终是十分称心的。
- 用 784,100,10 这样的网络模型,能够训练手写 MINST 数据集 到 97%+ 的精准率,这是十分好玩的事件。
- 至此自建的多层神经网络,还是有肯定的精准性的????
❝
浏览器中运行也是妥妥的????
❞
进一步学习
- 支流神经网络退出了更多不同性能的节点,但万变不离其宗
- 比方罕用的卷积神经网,就退出了,卷积层和池化层????
- 参数的优化,次要有动静学习率,参数初始化办法,归一,动量,扭转降落形式等
- 对于神经网络咱们曾经现龙在田了
- 其余类型的神经网络
总结
- 理解通用人工神经网络的正、反向流传
- 理解多层神经网络,神经网络就是丹炉,取得的参数就是金丹
- 通过 JS 实现之,将算法用到理论业务场景,自动化所有,缩小人工
- 逐渐把握多层神经分类的技巧
- 理解其余支流神经网络
正文完
发表至: javascript
2020-09-25