网站建设vip教程,四川路桥建设股份有限公司网站,百度怎么在视频下方投放广告,wordpress 小米官网主题下载深度学习基础深度学习是指基于“深度”(⾄少具有两个隐藏层)神经⽹络的机器学习⽅法。在本模块将学习神经⽹络的基本原理#xff0c;了解深度学习的计算环境#xff0c;掌握深度学习框架PyTorch的编程技能#xff0c;最后基于全连接结构的神经⽹络完成MNIST数字⼿写体识别任…深度学习基础深度学习是指基于“深度”(⾄少具有两个隐藏层)神经⽹络的机器学习⽅法。在本模块将学习神经⽹络的基本原理了解深度学习的计算环境掌握深度学习框架PyTorch的编程技能最后基于全连接结构的神经⽹络完成MNIST数字⼿写体识别任务。深度学习发展史神经网络的基本原理神经⽹络是⼀种模仿⽣物神经系统的计算模型它由⼤量互相连接的节点⼜被称为“神经元”组成它们之间存在复杂的连接关系通过连接权重来传递信息。神经元节点在收到其它节点传递过来的信息之后⾸先通过线性模型的⽅式集成信息再通过节点上定义的激活函数有选择性地透出信息继续传递给其它神经元。神经⽹络模型在上世纪50年代就已经出现但由于算⼒的限制只能训练简单结构的⽹络因⽽⼀直没有做出突破性的成果。直到2011年在GPU算⼒的⽀持下Geoffrey Hinton和邓⼒⾸次成功地将深度神经⽹络应⽤在语⾳识别上将识别错误率相对降低了20〜30%深度学习时代正式到来。1.神经元神经元是构成神经⽹络的基本单元。下图所示的是⼀个⽣物神经元的结构由细胞体、树突和轴突等结构组成。其中细胞体是神经元的代谢中⼼负责维持神经元的⽣存和功能。树突是神经元的输⼊部分接收来⾃其它神经元的信号。细胞体对接收到的信号进⾏整合当整合之后的信号强度超过某个阈值就会将其通过轴突输出传递给其它的神经元。上述⽣物神经元之间信息传递的机制被早期的神经科学家抽象为线性感知机在上图所示的计算模型中x0,x1,x2为神经元所接收到的数据w0,w1,w2为对应连接上的权重神经元在接收到数据之后⾸先按照线性模型的⽅式进⾏集成再通过阶跃函数进⾏输出。上图所示线性感知机的计算公式为其中zw0x0w1x1w2x2由于阶跃函数不连续不利于优化因此经常将阶跃函数替换为Sigmoid函数此时神经元的输出为可以看出此时神经元的输出符合逻辑回归模型因此逻辑回归模型可以看作是最简单的神经⽹络只有⼀个神经元构成。它实质上是⼀个线性的分类模型当时预测的类别为1当时预测的类别为0。两类数据之间的分类边界为是⼀条直线或超平⾯。sigmoid函数的性质2.逻辑回归逻辑回归是⼀个⼆分类模型 函数的输出是样本属于⽬标类y1的概率即如果收集了⼀个训练数据集那么对于其中的每⼀个训练样本通过模型预测其类别为的概率为其中显然希望极⼤化这个概率。为了计算⽅便取对数概率得到再对取负数就得到样本相关的损失函数训练集上总体的损失函数为模型参数 (即权重) 通过梯度下降进⾏优化。为了平衡训练速度和效果⼀般采⽤⼩批量梯度下降。对于每个训练epoch⾸先打乱训练集中的样本顺序然后在乱序之后的数据列表中依次取B个样本 (微批) 进⾏权重更新。如果在epoch开始的时候模型权重为那么在进⾏了第1次更新之后变为之后是以此类推。下图说明了⼩批量梯度下降的权重更新过程注意在每次更新权重时⽤于计算梯度的数据是不同的。梯度下降算法依赖梯度 (偏导向量) 的计算。基于链式法则对于权重的偏导函数为1其中第⼀个因式为损失函数对于逻辑回归输出的偏导2(2)式实际上与模型的函数表达式⽆关它只与损失函数如何度量与的差异有关。第⼆个因式为逻辑回归模型中Sigmoid函数的输出对其输⼊的偏导3第三个因式为线性模型输出对权重的偏导4(3)、(4)两式只与模型的函数表达式有关。将(2)-(4)式带⼊(1)式即可得到完整的偏导函数(5)化简5可得(6)公式需不需要记住常⽤的公式应该记住例如sigmoid函数softmax函数回归问题和分类问题的损失函数等。梯度下降中的求导公式可以不⽤记忆着重理解因变量、中间变量和⾃变量之间的依赖关系以及通过链式法则偏导传递的过程。有些公式虽然可以不记忆但掌握它们的性质很重要。例如sigmoid函数的导函数虽然不⼀定需要背下来具体的公式形式但掌握当⾃变量⾜够⼩或⾜够⼤时sigmoid的导数趋近于0这⼀性质⾮常有必要梯度即为偏导向量⼩批量梯度下降中使⽤的梯度为微批中所有样本对权重的平均梯度在计算出梯度之后权重按照下列公式进⾏更新其中t表示权重迭代次数。η是学习率是⼀个训练超参数。由于⼩批量梯度下降的梯度估计也带有随机性因此通常也被称为“随机梯度下降”即SGD (Stochastic Gradient Descent)。⾯试点神经⽹络为什么采⽤随机梯度下降⽽不是批量梯度下降进⾏优化对于绝⼤部分的神经⽹络模型其损失函数没有全局极⼩值但在参数空间(权重空间)中存在多个梯度为0的点称为稳定点。稳定点不⼀定是⼀个可接受的优化结果它可能是⼀个损失值较⼤的局部极⼩点、⼀个局部极⼤点或者是⼀个鞍点 (对于某个权重⽅向是局部极⼤点⽽对于另外⼀个权重⽅向是局部极⼩点 )。如果采⽤批量梯度下降进⾏权重的优化那么如果优化过程中的权重刚好落⼊某个不理想的稳定点由于此时梯度为0权重的数值不会发⽣任何变化。⽽到了下⼀次权重更新时由于梯度是基于全体训练样本估计的偏导函数在每⼀轮参数更新中都是相同的因此梯度仍然是0权重仍然不会得到更新......这样训练就被“卡”住了权重⼀直得不到更新训练loss⼀直不会下降。再来看基于随机梯度下降的情况 (⼩批量梯度下降也可以被称为随机梯度下降因为梯度计算同样具有随机性)。如果在优化过程中权重落⼊了稳定点由于梯度为0权重的数值不会发⽣任何变化本次更新结束程序进⼊下⼀次参数更新的流程。由于每次权重更新时基于的训练数据不同下⼀次参数更新时的损失函数和前⼀次会有所差异因此即使权重相同梯度也可能会有所不同因此参数会得到更新可能就会跳出当前这个不理想的稳定点。因此除了参数更新的速度快随机梯度下降还具有⼀定的逃逸不理想稳定点包括局部极⼩点的能⼒这⼀点对于神经⽹络的训练⾄关重要。接下来从神经⽹络的⻆度⽤代码实现逻辑回归。在这个代码实现中可以着重关注对神经⽹络LogisticNetwork类和数据集Dataset类的抽象。随机梯度下降的流程。Numpy ndarray的运算。import numpy as np class LogisticNetwork: 将逻辑回归模型从神经网络的角度重新实现 对于神经网络我们只关注模型的前向计算和反向计算过程 前向计算从输入的特征计算出模型的输出即预测值 反向计算根据损失函数对于模型输出的导数计算模型权重和偏置的梯度 def __init__(self, input_dim, weightNone, biasNone): self.input_dim input_dim self.weight np.random.randn(input_dim, 1) # 对象内部保存的模型权重基于正态分布进行了初始化 self.bias np.random.randn(1, 1) # 对象内部保存的模型权重偏置基于正态分布进行了初始化 if weight is not None: self.weight[:] weight if bias is not None: self.bias[:] bias # 其他更新参数所需要的变量 self.weight_grad np.zeros([input_dim, 1]) # 对象内部保存的权重梯度是经过样本之间平均处理的因此一个权重对应一个梯度值 self.bias_grad 0.0 # 对象内部保存的偏置梯度是经过样本之间平均处理的因此一个偏置对应一个梯度值 self.inputs_buff None self.outputs_buff None def forward(self, inputs): forward method: compute the output of the network inputs: 输入的特征shape(sample_num, input_dim) return: 经过逻辑回归计算出的预测值shape: (sample_num, 1) z np.matmul(inputs, self.weight) self.bias outputs 1 / (1 np.exp(-z)) self.inputs_buff np.copy(inputs) self.outputs_buff np.copy(outputs) return outputs def backward(self, out_grads): backward method: compute the gradient of the loss with respect to the weight and bias out_grads: 损失函数对于逻辑回归输出的导数每条训练样本对应一个导数值shape: (sample_num, 1) inputs: 与out_grads相对应的输入特征shape: (sample_num, input_dim) preds: 将inputs通过forward()方法计算得到的预测值shape: (sample_num, 1) return: None (计算出的梯度保存在对象内部) sample_num self.inputs_buff.shape[0] # 首先计算损失函数对于线性模型输出的梯度z_grads # z_grads out_grads * preds * (1 - preds) z_grads out_grads * self.outputs_buff * (1 - self.outputs_buff) # 然后计算损失函数对于权重梯度 # z_grads shape: (sample_num, 1) # inputs shape: (sample_num, input_dim) # 输出的weight_grads shape: (sample_num, input_dim) # 需要将z_grads进行广播扩展到(sample_num, input_dim), 再与inputs进行对应元素相乘的运算 weight_grads z_grads * self.inputs_buff # 同样的方式计算损失函数对于偏置的梯度可视为输入特征为1.0的情况 bias_grads z_grads # 对梯度进行平均 self.weight_grad weight_grads.mean(axis0).reshape(-1, 1) # 在样本之间取平均 self.bias_grad bias_grads.mean().reshape(-1, 1) # 在样本之间取平均 def update(self, lr, weight_decay): self.weight (1 - lr * weight_decay) * self.weight - lr * self.weight_grad self.bias (1 - lr * weight_decay) * self.bias - lr * self.bias_grad self.weight_grad[:] 0.0 self.bias_grad[:] 0.0 class Dataset: 数据集 def __init__(self, sample_num, seed): sample_num: 生成随机样本的个数 seed: 随机种子 self.sample_num sample_num # 生成特征 np.random.seed(seedseed) self.x np.random.rand(sample_num, 2) # 服从[0, 1]之间均匀分布的随机数 # 生成标签如果 x2 x1则y1 否则y0 self.y np.ones((sample_num, 1)) * (self.x[:, 1] self.x[:, 0]).reshape(-1, 1) def __len__(self): return self.sample_num def __getitem__(self, index): return self.x[index], self.y[index] def shuffle(self): # 打乱数据集 index np.arange(self.sample_num) np.random.shuffle(index) self.x self.x[index] self.y self.y[index] def plot(self): import matplotlib.pyplot as plt plt.scatter(self.x[self.y[:, 0] 1][:, 0], self.x[self.y[:, 0] 1][:, 1], marker, colorblue) plt.scatter(self.x[self.y[:, 0] 0][:, 0], self.x[self.y[:, 0] 0][:, 1], markero, colorgreen) plt.show() def calc_loss(preds, labels): # 计算损失函数 return -labels * np.log(preds1e-10) - (1 - labels) * np.log(1-preds1e-10) def calc_loss_grad(preds, labels): return -labels / (preds1e-10) (1 - labels) / (1 - preds1e-10) def test_model(model, dataset): # 测试模型 preds model.forward(dataset.x) pred_labels (preds 0.5).astype(np.int32) accuracy (pred_labels dataset.y) loss calc_loss(preds, dataset.y) return loss.mean(), accuracy.mean() if __name__ __main__: # 生成数据集 train_dataset Dataset(500, 0) valid_dataset Dataset(100, 1) test_dataset Dataset(100, 2) #train_dataset.plot() #test_dataset.plot() # 训练超参数设置 config { lr: 0.5, epoch: 1000000, weight_decay: 0.001, batch_size: 256, max_no_improve_epoch: 100 } # 创建模型对象 model LogisticNetwork(2) # 开始模型训练 valid_loss [] best_valid_loss 1e10 best_model { weight: None, bias: None } no_improve_epoch 0 for epoch in range(config[epoch]): # 打乱训练集中的样本顺序 train_dataset.shuffle() iter 0 while (iter config[batch_size]) len(train_dataset): # 获取一个batch的数据 batch_x train_dataset.x[iter: iter config[batch_size]] batch_y train_dataset.y[iter: iter config[batch_size]] # 前向计算 preds model.forward(batch_x) # 计算loss loss calc_loss(preds, batch_y) # 计算损失函数对于模型输出的导数, shape: (batch_size, 1) out_grads calc_loss_grad(preds, batch_y) # 反向计算 model.backward(out_grads) # 更新权重和偏置 model.update(config[lr], config[weight_decay]) iter config[batch_size] # 在验证集上测试模型 valid_loss, valid_accuracy test_model(model, valid_dataset) print(fepoch: {epoch}, valid_loss: {valid_loss}, valid_accuracy: {valid_accuracy}, no_improve_epoch: {no_improve_epoch}) if valid_loss best_valid_loss: best_valid_loss valid_loss best_model[weight] model.weight.copy() best_model[bias] model.bias.copy() no_improve_epoch 0 else: no_improve_epoch 1 if no_improve_epoch config[max_no_improve_epoch]: break # 测试模型 print(fbest model:\n{best_model}) best_model LogisticNetwork(2, best_model[weight], best_model[bias]) test_loss, test_accuracy test_model(best_model, test_dataset) print(ftest_loss: {test_loss}, test_accuracy: {test_accuracy})3.异或问题异或 (XOR) 是⼀种⼆值逻辑运算对于两个输⼊x1与x2异或运算的输出值如下表所示。如果将异或运算的结果作为类别标签样本的分布如图所⽰。显然不存在任何⼀条直线能够将样本正确分类因此简单神经⽹络是⽆法解决异或问题的需要更多的神经元。图示为⼀个由三个神经元构成的神经⽹络紫⾊的神经元负责将原始的特征x1x2 变换为h1h2⻩⾊的神经元则以h1h2作为特征作出最后的预测。每个神经元同样包含了线性模型和sigmoid函数变换。在通过紫⾊神经元之后原始特征空间中的点(1,1)被映射到了原点附近⽽其他三个点还在原有位置附近因此很容易能够被线性分类器给区分开。紫⾊神经元构成了“隐藏层”它们的作⽤是提取更有效的特征。⻩⾊神经元构成了“输出层”作⽤是利⽤隐藏层提取的特征进⾏预测。表示原始特征输⼊的⿊⾊节点在逻辑上也被看作神经⽹络的⼀层叫作“输⼊层”。4.多层感知机我们在异或问题上证明了多层神经⽹络可以解决线性不可分的问题。这种相邻两层节点之间是全连接结构的神经⽹络叫做多层感知机 (Multi-Layer Perceptron)缩写为MLP。多层感知机由输⼊层、隐藏层和输出层组成其中隐藏层的作⽤是提取⾮线性特征输出层的作⽤是进⾏预测。4.1.隐藏层隐藏层的作⽤是提取特征可以有多层每层的神经元数量可以不相同。越靠近输出层所提取特征的语义层级 或抽象层级越⾼。具有两个及两个以上隐藏层的神经⽹络被称为“深度神经⽹络”。隐藏层神经元的激活函数有多种选择除了Sigmoid 之外还可以采⽤Tanh (双曲正切函数)、ReLu 和LeakyReLu等。但需要注意的是神经⽹络依靠激活函数带来⾮线性输出因此隐藏层的激活函数⼀定是⾮线性的。主要的激活函数4.2.输出层输出层的作⽤是完成预测因此输出层的结构取决于预测任务的类型。对于回归问题输出层的节点数量为1并且没有⾮线性函数其中为输出层的权重向量为最后⼀个隐藏层的输出,b为偏置项。对于⼆分类问题输出层的节点数量为1节点上的激活函数为Sigmoid所预测的是样本属于正类的概率对于多分类问题输出层节点的数量等于类别数输出层的激活函数为Softmax每个节点所预测的是样本属于相应类别的概率。具体公式在MNIST数字⼿写体实验的部分进⾏讲解。⽹络结构的设计问题隐藏层和输出层具有不同的作⽤因此隐藏层和输出层在结构设计⽅⾯是解耦的。隐藏层的作⽤是提取特征因此隐藏层的结构设计和数据特点有关。例如结构化数据⼀般采⽤全连接的⽹络结构图像数据⼀般先经过卷积神经⽹络提取局部特征再通过全连接⽹络进⾏整体特征的抽象和变换⽂本数据⼀般通过Transformer提取语义特征。输出层的作⽤是进⾏预测因此输出层的结构只与预测问题的类型有关⽽与数据特点⽆关。5.正向传播和反向传播神经⽹络有两个计算问题正(前)向传播 (Forward Propagation)从特征输⼊开始从下到上逐层计算神经⽹络的输出直到计算出最终的预测结果。每⼀层的输出都是下⼀层的输⼊。反(后)向传播 (Backward Propagation)从损失函数对于神经⽹络预测结果的偏导开始从上到下逐层计算损失函数对于每⼀层⽹络输出的偏导⽤于计算每⼀层权重的梯度。下边结合⼀个例⼦说明正向传播和反向传播的计算过程。图⽰的神经⽹络包含两个隐藏层隐藏层节点的激活函数为ReLU。输出层节点只包含线性模型⽤于解决数值预测的问题即回归问题。假设我们输⼊的样本x10.5x21.0y0.8。正向传播的过程如下1、以x1、x2为输⼊计算h1、h22、以h1、h2为输⼊计算h3、h43、以h3、h4 为输⼊计算4、将与y相⽐对计算损失函数值正向传播的过程结束之后开始反向传播1、计算对于的导数2、已知损失函数对于的导数计算损失函数对于 [w9,w10]的梯度同时将梯度传播到[h3,h4]1计算损失函数对于w9、w10的偏导其中第⼀个因式在上⼀步已算出因此只需要计算第⼆个因式即可将两个因式相乘同理损失函数对于w10的偏导(2) 计算损失函数对于h3、h4的偏导其中第⼀个因式已知第⼆个因式:将两个因式相乘同理可得3、已知损失函数对于[h3h4] 的梯度计算损失函数对于[w5,w6w7,w8] 的梯度同时将梯度传播到 [h1h2] 。1计算损失函数对于w5、w6、w7、w8的偏导2计算损失函数对于h1、h2的偏导由于因此同理可得4、已知损失函数对于 [h1,h2]的梯度计算损失函数对于[w1,w2,w3,w4]的梯度已知、显然可得在求出损失函数对所有权重的梯度之后就可以进⾏权重更新设学习率η 0.1可以验证⼀下在进⾏了⼀次参数更新之后预测值是否更加接近真实值。重新进⾏⼀次正向传播1、以x1 、x2 为输⼊计算h1 、h2 h1 w1x1 w2x2 1.015 × 0.5 0.47 × 1.0 0.9775h2 w3x1 w4x2 0.5 × 0.5 0.7 × 1.0 0.952、以h1 、h2 为输⼊计算h3 、h4 h3 w5h1 w6h2 −1.06 × 0.9775 1.943 × 0.95 0.8097h4 w7h1 w8h2 −0.44 × 0.9775 2.057 × 0.95 1.5243、以h3、h4 为输⼊计算4、将与y相⽐对计算损失函数值可⻅权重更新之后预测值的确更加接近真实值了。6.全连接层的向量化计算现在我们来梳理⼀下每⼀层⽹络的计算。对于图示的全连接⽹络输⼊节点数为3输出节点数为5设神经元上的激活函数为Sigmoid函数即。