按照固有思维方式,人们总以为人工智能是一个莫测高深的行业,这个行业的人都是高智商人群,无论是写文章还是和人讲话,总是讳莫如深,接着就是蹦出一些“高级”词汇,什么“神经网络”,什么“卷积神经”之类,教人半懂不懂的。尤其ChatGPT的风靡一时,更加“神话”了这个行业,用鲁迅先生形容诸葛武侯的话来讲:“多智而近妖”。

事实上,根据二八定理,和别的行业一样,人工智能行业内真正顶尖的天才也就是20%,他们具备真正的行业颠覆能力,可以搞出像ChatGPT这种“工业革命”级别的产品,而剩下的80%也不过就是普通人,每天的工作和我们这些人一样,枯燥且乏味,而之所以会出现类似“行业壁垒”的现象,是因为这个行业的“黑话”太多了,导致一般人听不懂,所谓“黑话”可以理解为行业术语,搞清楚了这些所谓的行业术语,你会发现,所谓的“人工智能”,不过也就是个“套路活儿”。

本次我们试图使用“白话文”来描摹人工智能机器学习的底层逻辑,并且通过Golang1.18来实现人造神经元的原理。

人造神经元 Neural

现在业内比较流行的,比如Transformer模型、GPT模型、深度学习、强化学习、卷积神经网络等等,无论听起来多么高端大气上档次,说出大天去,它也是神经网络架构,换句话说它们的底层都一样,类比的话,就像汽车行业,无论是汽油驱动还是电驱动、三缸发动机还是六缸发动机、单电机还是双电机,混合动力还是插混动力,无论汽车主机厂怎么吹牛逼,无论车评人怎么疯狂恰饭,厂商造出来的车最终还是一个最基本的汽车架构,说白了就是一个底盘四个轮儿,你再牛逼,你也逃不出这个基本架构。

所以说,机器学习的基本架构是神经网络架构,神经网络由大量的人工神经元组成,它们联结之后就可以进行所谓的“机器学习”。所以,必须搞清楚什么是神经元,才能弄懂神经网络。

神经元是神经网络架构中最微小的单位,也是最小的可训练单位,一个基本的神经元结构是下面这样的:

毋庸讳言,大多数人看见这个基本上都会瞬间放弃,数学公式简直就是开发界的洪水猛兽,其劝退能力堪比艾尔登法环中的大树守卫,我们的机器学习之旅还没开始,就已经结束了。

那么我们把这个玩意儿翻译成大多数人能看懂的样子:f( sum( x*w) + b )

x代表输入的数据,w代表权重,sum代表求和,b代表偏差,f代表激活函数,最后这个公式运行的结果,就是机器学习的结果。

简单往里头套点数据,比如我希望机器学习的结果是10,那么,x、w、和b分别应该是什么才能让结果变为10呢?如果 x=2 w=4 b = 2 就是我们想要的结果。这也就是最基本的线性回归,我们只处理一个维度的数据,因为结果已经显而易见了,我们已经不需要机器学习了,因为靠猜也能猜出来结果是什么。

但是生产环境中,x并非是单维度,而是多维度的,比如x1、x2、x3…组成的矩阵,但无论是多维度还是单维度,计算公式用的还是一样的,每一个x对应一个权重w,所以是xn*wn。

说白了,x就是一个多维特征,类比的话,假如我们想让电脑智能识图,比如识别一只猫,那么x就是猫的特征,比如形态、颜色、眼睛、叫声等等,作为多维度的输入特征x,喂给电脑,让电脑给出识别结果,这就是简单的机器学习处理分类问题。

这里需要注意的是,x作为特征参数,并不是越多越好,而是特征越明显越好,举个例子,你想让AI去识别鲁迅的文章,那提供的特征最好应该具备鲁迅文章的特点,而不是全量输入,因为鲁迅就算再“鲁迅”,他写的文字也会和别人重复,也就是说并不是每句话都是他独有的,如果把他所有的文章都喂给电脑,可能就会产生“噪声”,影响机器学习的结果。

另外应该知道的是,x参数特征并不是我们认为的单词或者汉字,而是一串单精度区间在0-1之间的浮点数字,也就是所谓的“向量”,因为只有数字才能套着神经元公式进行计算。

所以所有的文本特征在进行神经元计算之前,必须通过一些方法进行“向量化”操作。说白了就是把汉字转化为数字,就这么简单。

另外这也就证明了,电脑真的没有思想,它不理解什么是猫,或者谁是鲁迅,它就是在进行计算,而已。

随后是w,w指的是权重,权重是指神经元接收到的输入值的重要性,这些输入值通过乘以对应的权重,被加权求和作为神经元的输入。权重值越大,表示该输入在神经元的输出中所占的比重越大。说白了,猫的所有特征的权重并不是统一的,比如黑夜里突然一个东西跳了出来,你怎么判断它是什么物种?很明显,一声“喵呜”我们就可以立刻断定这是一只猫,所以叫声特征的权重一定大于其他特征的权重。

最后是b,也就是偏差(bias),偏差通常是一个实数,与神经元的权重一样,也是通过训练神经网络而调整的参数。偏差的作用是在神经元的输入上增加一个常量,以调整神经元的激活阈值。如果没有偏差,那么神经元的激活函数将仅仅取决于加权和的值,而无法产生任何偏移。

说白了,b就是让x * w的值更活一点,让它不是“死”的数。

最后说说f 也就是激活函数,激活函数通常具有非线性的特性,这使得神经网络能够拟合非线性的复杂函数,从而提高其性能和准确度。

说白了,如果没有激活函数,我们的权重计算就是“线性”的,什么叫线性?如果把x特征从1开始输入,一直到100,然后将计算结果绘制成图:

我们会发现计算结果是一根直线,这显然不符合客观规律,更符合生物特征的形态应该是“曲线”,所以说白了,激活函数f的作用就是把“直线”变成“曲线”。

最后,我们把神经元公式改造成方便我们理解的形式:期望结果 = 激活函数( 求和(特征 * 权重) + 偏差 )。

如果用代码实现这个公式:

func neuron(inputs []float64, weights []float64, bias float64) float64 {  if len(inputs) != len(weights) {  panic("inputs and weights must have the same length")  }  sum := bias  for i := 0; i < len(inputs); i++ {  sum += inputs[i] * weights[i]  }  return sum
}

这个函数接收两个长度相同的浮点数数组 inputs 和 weights,以及一个偏置值 bias。它通过将每个输入值乘以其对应的权重,加上偏置值,得到神经元的加权和。最后,函数返回这个加权和作为神经元的输出值。

使用这个函数时,可以将输入数据和权重作为参数传递给它。例如,假设我们有一个二分类问题,输入数据有两个特征:x1和x2,并且我们有一个包含两个权重和一个偏置的神经元。那么可以这样调用神经元公式:

inputs := []float64{x1, x2}
weights := []float64{w1, w2}
bias := b
output := neuron(inputs, weights, bias)

这里返回神经元的输出值,所以,谁说学习人工智能必须得用Python?我们就骄傲地使用Golang。

机器学习,到底怎么学习

机器学习的过程就是上文中神经元公式的使用过程,第一步收集所有的特征数据,然后进行权重分配,最后向量化操作,把文本数据转换为计算机能计算的浮点数,随后加权求和,之后加一个偏差(bias),最后过一下激活函数,最终得到一个期望结果,就完事了。

难吗?不难,普通人连猜带蒙也能做。

但事实上,这只是原理,也就是最基本的东西,一般人都能掌握,比如打篮球,规则非常简单,核心就是运球和投篮,无对抗下小学生也能瞬间掌握,但小学生没法去打NBA级别的比赛,因为很多高端的篮球技术是构筑于运球和投篮的,需要经年累月的练习和自身天赋的加成,所以全世界能打NBA的就那么几百人,而已。

同理,机器学习的过程也并非如此简单,通过特征输入,经过神经元公式,得到的结果真的一定是我们所期望的结果吗?

其实未必,机器学习还包括两个极其重要的概念:前向传播和反向传播。

前向传播是指将输入数据从神经网络的输入层传递到输出层的过程。在前向传播过程中,输入数据通过神经网络的每一层,每个神经元都会对其进行一定的加权和激活函数计算,最终得到输出层的输出值。这个过程也被称为“正向传播”,因为数据是从输入层依次向前传播到输出层。

反向传播是指在前向传播之后,计算神经网络误差并将误差反向传播到各层神经元中进行参数(包括权重和偏置)的更新。在反向传播过程中,首先需要计算网络的误差,然后通过链式法则将误差反向传播到各层神经元,以更新每个神经元的权重和偏置。这个过程也被称为“反向梯度下降”,因为它是通过梯度下降算法来更新神经网络参数的。

说白了,前向传播就是由特征到结果的过程,反向传播则是逆运算,用结果反推过程。

回到分类问题,我们输入了猫的特征和特征权重,经过计算,结果未必是猫,可能是狗,或者是耗子,也可能是别的什么东西,但这不重要,重要的是我们需要拿到一个结果的误差,这个误差越小越好,而反向传播就是帮我们推算误差到底有多大的方法。

而误差的大小就取决于特征的输入,导致机器学习结果错误的根源是参数,此时,我们需要调整参数的输入,从而减小误差值,这也就是人工智能行业从业人员经常说的“调参”。

比如,我们期望结果是猫,结果计算机返回狗,那么调整参数,结果返回熊猫,那么就说明调大发了,继续调整,直到计算机返回结果:猫。

在 Golang1.18 中,可以通过以下代码实现反向传播:

func backpropagation(inputs []float64, targets []float64, network *Network, learningRate float64) {  // 1. 前向传播,计算每个神经元的输出值  outputs := feedforward(inputs, network)  // 2. 计算输出层的误差  outputErrors := make([]float64, len(outputs))  for i := range outputs {  outputErrors[i] = outputs[i] - targets[i]  }  // 3. 反向传播误差,计算每个神经元的误差值  for i := len(network.layers) - 1; i >= 0; i-- {  layer := network.layers[i]  errors := make([]float64, len(layer.neurons))  // 3.1. 计算神经元的误差值  if i == len(network.layers)-1 {  // 输出层的误差  for j := range layer.neurons {  errors[j] = outputErrors[j] * sigmoidPrime(layer.neurons[j].output)  }  } else {  // 隐藏层的误差  for j := range layer.neurons {  errorSum := 0.0  nextLayer := network.layers[i+1]  for k := range nextLayer.neurons {  errorSum += nextLayer.neurons[k].weights[j] * nextLayer.neurons[k].error  }  errors[j] = errorSum * sigmoidPrime(layer.neurons[j].output)  }  }  // 3.2. 将误差值保存到神经元中  for j := range layer.neurons {  layer.neurons[j].error = errors[j]  }  }  // 4. 更新神经网络的权重和偏置  for i := range network.layers {  layer := network.layers[i]  // 4.1. 更新权重  for j := range layer.neurons {  for k := range layer.neurons[j].weights {  if i == 0 {  // 输入层的权重  layer.neurons[j].weights[k] -= learningRate * layer.neurons[j].error * inputs[k]  } else {  // 隐藏层和输出层的权重  prevLayer := network.layers[i-1]  layer.neurons[j].weights[k] -= learningRate * layer.neurons[j].error * prevLayer.neurons[k].output  }  }  // 4.2. 更新偏置  layer.neurons[j].bias -= learningRate * layer.neurons[j].error  }  }
}

这个函数接收输入数据 inputs、目标数据 targets、神经网络 network 以及学习率 learningRate 作为参数。它首先调用 feedforward 函数进行前向传播,计算每个神经元的输出值。然后,它计算输出层的误差,通过误差反向传播,计算每个神经元的误差值,并将其保存到神经元中。

接下来,函数根据误差值和学习率更新神经网络的权重和偏置。在更新权重时,需要根据神经元所在的层来选择更新的权重类型(输入层、隐藏层或输出层),然后根据误差值和输入数据或上一层神经元的输出值来更新权重。在更新偏置时,只需要根据误差值和学习率来更新即可。

总的来说,这个函数实现了反向传播的所有步骤,可以用于训练神经网络并提高其准确度和性能。

结语

大道不过三俩句,说破不值半文钱,所谓人工智能机器学习就这么回事,没必要神话,也无须贬低,类比的话,就像餐饮行业的厨师岗,所谓做菜,底层原理是什么?就是食材和火候,掌握了做菜的底层原理,就能做出好菜,其他的,比如刀工、颜色等等,不过就是锦上添花的东西,而已。

所以机器学习就是做菜,做出来的东西可能不尽如人意,就得不停地调整食材的搭配和火候的大小,所谓机器学习的最重要技巧,其实是特征的提取以及参数的调整,所谓大道至简,殊途同归。

人工智能机器学习底层原理剖析,人造神经元,您一定能看懂,通俗解释把AI“黑话”转化为“白话文”相关推荐

  1. Go语言底层原理剖析

    作者:郑建勋 出版社:电子工业出版社 品牌:博文视点 出版时间:2021-08-01 Go语言底层原理剖析

  2. 深入理解Go底层原理剖析 (送书)

    互联网迅猛发展的数十年时间里,不断面领着各种新的场景与挑战,例如大数据.大规模集群计算.更复杂的网络环境.多核处理器引起对于高并发的需求,云计算,上千万行的服务器代码-- 那些成熟但上了年纪的语言没能 ...

  3. 『Go 语言底层原理剖析』文末送书

    互联网迅猛发展的数十年时间里,不断面领着各种新的场景与挑战,例如大数据.大规模集群计算.更复杂的网络环境.多核处理器引起对于高并发的需求,云计算,上千万行的服务器代码-- 那些成熟但上了年纪的语言没能 ...

  4. tensorflow67 《深度学习原理与TensorFlow实战》04 CNN看懂世界 04深度残差网络

    00 环境 #<深度学习原理与TensorFlow实战>04 CNN看懂世界 # 书源码地址:https://github.com/DeepVisionTeam/TensorFlowBoo ...

  5. 数组Array.slice()方法应用与底层原理剖析

    1.Array.slice()方法的应用 Array.slice()可以截取数组的任意一端,并将截取到的数组返回,但需注意的是它并不改变原数组. slice(num1,num2)方法可以传入两个参数( ...

  6. Golang底层原理剖析之上下文Context

    Context 前言 Context 前言 如何优雅地使用context点击浅谈Golang上下文Context Context 在Go语言并发编程中,用一个goroutine来处理一个任务 ,而它又 ...

  7. 运放电路的工作原理_图文讲解!教你看懂7款经典运放电路

    引言 运放的基本分析方法:虚断,虚短.对于不熟悉的运放应用电路,就使用该基本分析方法. 运放是用途广泛的器件,接入适当的反馈网络,可用作精密的交流和直流放大器.有源滤波器.振荡器及电压比较器. 1.运 ...

  8. bool查询原理 es_吐血整理:一文看懂ES的R,查询与聚合

    对es查询的索引的company,其有如下字段,下面是一个示例数据 "id": "1", //id "name": "张三&quo ...

  9. 民谣女神唱流行,基于AI人工智能so-vits库训练自己的音色模型(叶蓓/Python3.10)

    流行天后孙燕姿的音色固然是极好的,但是目前全网都是她的声音复刻,听多了难免会有些审美疲劳,在网络上检索了一圈,还没有发现民谣歌手的音色模型,人就是这样,得不到的永远在骚动,本次我们自己构建训练集,来打 ...

最新文章

  1. java filter 返回错误消息_java filter 导致错误310
  2. boost::edmonds_karp_max_flow用法的测试程序
  3. android 仿网易标题栏,仿网易新闻可滑动标题栏TabLayout(文字或图标)
  4. C++自学14:关系运算符(bool/std::boolalpha)
  5. Android ListView反复调用getView和getCount
  6. 斐讯路由器使用说明,校园网破解,breed控制台,华硕固件
  7. Android面试必问之Java基础
  8. 实验2:tga格式图像转换为yuv格式
  9. nyoj 366 D的小L(数的全排)
  10. Docker 目录/var/lib/docker/containers文件过大
  11. 如何在esxi环境安装硬件VIB驱动。
  12. 2000-2020年地级市进出口总额数据
  13. 趣谈网络协议---容器网络:来去自由的日子,不买公寓去合租
  14. 仿中华英才网城市选择
  15. 刷微信步数,运动排名
  16. Columbia Biosciences 山羊抗美洲驼 IgG:SureLight APC
  17. Java鼠标双击事件
  18. 主攻“量子计算+元宇宙”:NTT DATA于六个国家设立创新中心
  19. java项目springboot医院固定资产检修管理系统
  20. 转的大佬的 学习方法

热门文章

  1. Hyperic-Sigar简介
  2. jquery 输入框失去焦点时 (blur)事件
  3. java如何将网页表格导出为excel
  4. 用Python告诉你广州房租现状
  5. 规模指数介绍,如何使用Python获取数据
  6. SV实验3 子系统验证和测试点划分
  7. java 对象查找_Java如何从数组中查找对象元素?
  8. 按钮默认点击事件(打开页面时按钮默认被点击)
  9. 将tensorflow模型转换为uff模型
  10. 水仙花数是指一个n位数(n≥3),它的每个位上的数字的n次幂之和等于它本身。例如:1^3+5^3+3^3=153