课程开源地址及相关视频链接:(当然这里也希望大家支持一下正版西瓜书和南瓜书图书,支持文睿、秦州等等致力于开源生态建设的大佬✿✿ヽ(°▽°)ノ✿)
Datawhale-学用 AI,从此开始
【吃瓜教程】《机器学习公式详解》(南瓜书)与西瓜书公式推导_哔哩哔哩_bilibili
正文部分
神经网络类算法可以堪称当今最主流的一类机器学习算法,其本质上和前几章讲到的线性回归、对数几率回归、决策树等算法一样均属于机器学习算法,也是被发明用来完成分类和回归等任务。不过由于神经网络类算法在如今超强算力的加持下效果表现极其出色,且从理论角度来说神经网络层堆叠得越深其效果越好,因此也单独称用深层神经网络类算法所做的机器学习为深度学习,属于机器学习的子集。(南瓜书介绍)
什么是神经网络?
A neural network is a machine learning program, or model, that makes decisions in a manner similar to the human brain, by using processes that mimic the way biological neurons work together to identify phenomena, weigh options and arrive at conclusions.
神经网络是一种机器学习程序或模型,它以类似于人脑的方式做出决策,方法是使用模仿生物神经元协同工作的方式来识别现象、权衡选项并得出结论的过程。每个神经网络都由节点层或人工神经元组成,即输入层、一个或多个隐藏层以及输出层。每个节点都连接到其他节点,并有自己关联的权重和阈值。如果任何单个节点的输出高于指定的阈值,则该节点将被激活,并将数据发送到网络的下一层。否则,不会将任何数据传递到网络的下一层。神经网络依靠训练数据来学习并随着时间的推移提高其准确性。一旦它们被微调以提高准确性,它们就会成为计算机科学和人工智能领域的强大工具,使我们能够高速地对数据进行分类和聚类。与人类专家的手动识别相比,语音识别或图像识别任务可能需要几分钟而不是几小时。神经网络有时称为人工神经网络 (ANN) 或模拟神经网络 (SNN)。它们是机器学习的一个子集,也是深度学习模型的核心。
1、CNN(卷积神经网络)
卷积神经网络是LeCun于1989年提出,用Lenet卷积网络来识别信封或邮件上的手写数字。卷积神经网络是一种专门用来处理具有类似网格结构的数据的 神经网络。例如时间序列数据(可以认为是在时间轴上有规律地采样形成的一维网格)和图像数据(可以看作是二维的像素网格)。卷积网络在诸多应用领域都表现优异。“卷积神经网络” 一词表明该网络使用了 卷积(convolution)这种数学运算。卷积是一种特殊的线性运算。卷积网络是指那些至少在网络的一层中使用卷积运算来替代一般的矩阵乘法运算的神经网络。
如下是卷积运算的示意图,步幅为一,填充为0,无偏置。 (图片来源:花书)
卷积运算通过三个重要的思想来帮助改进机器学习系统:
(1)稀疏交互(sparse interactions):举个例子, 当处理一张图像时,输入的图像可能包含成千上万个像素点,但是我们可以通过只占用几十到上百个像素点的核来检测一些小的有意义的特征,例如图像的边缘。
(2)参数共享(parameter sharing:在卷积神经网络中,核的每一个元素都作用在输入的每一位置上(是否考虑边界像素取决于对边界决策的设计)。卷积运算中的参数共享保证了我们只需要学习一个参数集合,而不是对于每一位置都需要学习一个单独的参数集合。
(3)等变表示(equivariant representations)。对于卷积,参数共享的特殊形式使得神经网络层具有对平移等变(equivariance)的性质。如果一个函数满足输入改变,输出也以同样的方式改变这一性质,我们就说它是等变(equivariance)的。例如在卷积操作中我们先移动图像上的物体再进行卷积得到的结果与我们先卷积在移动卷积图上的输出,这两个操作的结果是一致的。
2、RNN(循环神经网络)
循环神经网络(recurrent neural network)或 RNN (Rumelhart et al., 1986c)是一类用于处理序列数据的神经网络。就像卷积网络是专门用于处理网格化数据 X(如一个图像)的神经网络,循环神经网络是专门用于处理序列 x (1), . . . , x(τ) 的神经网络。正如卷积网络可以很容易地扩展到具有很大宽度和高度的图像,以及处理大小可变的图像,循环网络可以扩展到更长的序列(比不基于序列的特化网络长得多)。大多数循环网络也能处理可变长度的序列。
一般情况下,卷积网络用于处理图像,循环网络用于处理自然语言。如下是循环神经网络的前向传播过程,后续每个神经元都要取前一个神经元的输出作为输入的一部分。这种网络结构,可以让我们的模型对序列关系进行建模。
因为这种依赖关系,循环神经网络表现出两个大的问题:
(1)训练不稳定,梯度的累积容易产生梯度爆炸与梯度消失,不利于神经网络的训练。
(2)因为模型是一种串行的结构,下一个神经元的输入必须依赖于前一个的输出,所以导致了循环神经网络的处理速度慢。
3、Tansformer(注意力机制)
卷积只能通过叠加很深的层才能获得全局感受野,循环神经网络不好训练且慢。2017年《Attention is all you need》诞生了一种全新的网络架构,self-attention。使用self-attention的BERT巨大的提升了自然语言处理的精度和速度,近些年来BERT,GPT2,GPT3,基本都是基于Transformer的架构了。另外从2020年的vit开始,Transformer也开始进入图像处理领域大放异彩,如swin transformer。
下图为self-attention的运算过程。我们可以看到这是一个可并行运算的过程,而且从一开始就在捕捉全局关系。
误差逆传播算法
对训练例 (xk,yk) , 假定神经网络的输出为a(3)=(a1(3) ,a2(3)) , 则网络在该样例上的均方误差为:
通过该误差对神经网络上的参数进行调整。BP算法基于梯度下降策略,以目标的负梯度方向对参数进行调整,对误差Ek,给定的学习率η,有:
可以看到在输出层神经元中,θ(2) 先影响到 z(3), 再影响到输出值 a(3), 最后影响到 Ek,有:
注意,h=0 时,a0(2)=1。输出层的参数θ(2)就可以更新为:
同样,隐层的参数θ(1)更新为:
其中
神经网络上的参数通过BP算法完成一次迭代了,该迭代过程循环进行,直到达到某些停止条件为止。
#创建神经元import numpy as npdef sigmoid(x):# 定义sigmoid函数,BP网络的神经元(激活函数) f(x) = 1 / (1 * e^(-x))return 1 / (1 + np.exp(-x))
class Neuron(): #定义一个神经元def __init__(self, weights, bias): #用weights表示权重,bias表示阈值或偏移值,self表示自身本身(神经元)self.weights = weightsself.bias = biasdef feedforward(self, inputs):# weight inputs, add bias, then use the activation functiontotal = np.dot(self.weights, inputs) + self.bias #得到权值并且加上自身阈值return sigmoid(total) #将得到的权值代入到sigmoid函数中weights = np.array([0, 1]) # 设置权重: w1 = 0, w2 = 1
bias = 4 #设置阈值
n = Neuron(weights, bias) #初始化神经元
x = np.array([2, 3]) # x1 = 2, x2 = 3
print(n.feedforward(x)) # 0.9990889488055994class NeuralNetwork(): #开始创建def __init__(self):np.random.seed(1) #创建随机数self.synaptic_weights = 2 * np.random.random((3, 1)) - 1 #生成-1到1之间的随机值def sigmoid(self, x):return 1 / (1 + np.exp(-x)) #设置激活函数def sigmoid_derivative(self, x):return x * (1 - x) #利用输出计算sigmoid函数的导数,以调整权值def train(self, training_inputs, training_outputs, training_iterations): #测试输入for iteration in range(training_iterations): #对模型做出预测并调整权值output = self.think(training_inputs) #调整输入error = training_outputs - output #通过输出反向计算出错误率adjustments = np.dot(training_inputs.T, error * self.sigmoid_derivative(output)) #从反映的容错率进行调整self.synaptic_weights += adjustments #对权重的调整def think(self, inputs):inputs = inputs.astype(float)output = self.sigmoid(np.dot(inputs, self.synaptic_weights)) #通过神经元传递的输入以获得输出return outputif __name__ == "__main__":neural_network = NeuralNetwork() #初始化神经网络print("随机生成权值: ")print(neural_network.synaptic_weights)training_inputs = np.array([[0,0,1],[1,1,1],[1,0,1],[0,1,1]])training_outputs = np.array([[0,1,1,0]]).Tneural_network.train(training_inputs, training_outputs, 15000)print("Ending Weights After Training: ")print(neural_network.synaptic_weights)#输入三个值user_input_one = str(input("User Input One: "))user_input_two = str(input("User Input Two: "))user_input_three = str(input("User Input Three: "))print("继续调整输入: ", user_input_one, user_input_two, user_input_three)print("New Output data: ")print(neural_network.think(np.array([user_input_one, user_input_two, user_input_three])))
参考文献
[1] 李航. 统计学习方法. 清华大学出版社, 2012.
[2] A Gentle Introduction to Graph Neural Networks