计算loss是会把所有loss层的loss相加。那么如何选取一个较为理想的medel？单独观察训练集loss曲线loss出现NAN联合观察loss曲线和accTensorFlow中loss与val_loss、accuracy和val_accuracy含义震荡修复计算loss是会把所有loss层的loss相加。从验证集误差是和测试集误差的角度分析其实你这个问题就是个伪命题，如果我们刻意的去在训练集上拟合模型，使其准确率达到很高的高度，或者说尽量在验证集合上表现的好，都是相悖的。因为我们不能为了某一特定数据集来刻意拟合，因为模型预测数据是不一定就在这个训练或者验证集合的空间中。还有，如果这个model预测集合acc20% 训练集合acc19% （即训练集精度低于测试集精度），那么这个模型肯定是不好的。还有一点需要注意，就是千万不能为了拟合测试集合而去更改模型，测试集合应该每次都有不同。那么如何选取一个较为理想的medel？首先，要有一个期望的准确率，通过不同模型的实验，找到最能接近的；然后，选定模型后进行参数调优；那么我们要尽可能的提高model的准确率，同时提高其泛化的能力，不能单一看某一指标，此时可参考 准确率、召回率、虚警率、F1Score等指标综合评判。或者采用多重验证随机划分训练、预测、验证集合，多次随机后找到最优参数。有时候训练集合误差很低，但是泛化能力极差，产生过拟合，有时候验证集合误差很低，但是可能验证集合无法代表所有的样本，有特殊性或者其他异常点较多。所以模型问题不能单一从你这两点来评判。一般而言，训练集loss < 验证集loss <测试集loss因为网络 [已见过] 所有训练集samples，故最低。而网络用验证集作为反馈来调节参数，相当于参考了验证集samples中的信息（间接 [已见过]）。又因为网络没有任何测试集的信息，所以测试结果一般而言最差。不过这都不是绝对的，有不符合这个一般现象的task，而我们不可以说哪种情况更“好”。多数情况验证集上错误率更低一点。因为是选择在验证集上准确率最高的模型来进行测试。考虑到数据的随机性，在验证集上准确率最高的模型在测试集上不一定是最高的，所以算出来的指标通常验证集会比测试集上好一点。但是实际情况下都有可能，特别是数据量不太大的时候。样本集合的数据也只是近似整体的分布，肯定会有波动。一个好的网络，二者的差距应该是很低的。但一般情况下因为网络不可避免地存在一定程度上的过拟合，所以肯定是train_loss低于test_lost，但如果低太多，就得考虑是过拟合的问题还是因为样本的特征空间不统一的问题。一般训练集不大时，最终训练的网络及容易过拟合，也就是说train-loss一定会收敛，但是test-loss不会收敛；训练时的loss会低于test的loss大概1~2个数量级，通常是10倍左右。{lamp/}单独观察训练集loss曲线 1）如果你的 learning_rate_policy 是 step 或者其他变化类型的话, loss 曲线可以帮助你选择一个比较合适的 stepsize； 2）如果loss曲线表现出线性（下降缓慢）表明学习率太低； 3）如果loss不再下降，表明学习率太高陷入局部最小值； 4）曲线的宽度和batch size有关，如果宽度太宽，说明相邻batch间的变化太大，应该减小batch size。可能导致不收敛的问题（如loss为87.3365，loss居高不下等）的解决方案 1）在caffe中可以在solver里面设置：debug_info: true,看看各个层的data和diff是什么值，一般这个时候那些值不是NAN（无效数字）就是INF（无穷大）； 2）检查数据标签是否从0开始并且连续； 3）把学习率base_lr调低； 4）数据问题，lmdb生成有误； 5）中间层没有归一化，导致经过几层后，输出的值已经很小了，这个时候再计算梯度就比较尴尬了，可以尝试在各个卷积层后加入BN层和SCALE层； 5）把base_lr调低，然后batchsize也调高； 6）把data层的输入图片进行归一化，就是从0-255归一化到0-1，使用的参数是： transform_param { scale: 0.00390625//像素归一化，1/255 } 7）网络参数太多，网络太深，删掉几层看看，可能因为数据少，需要减少中间层的num_output； 8）记得要shuffle数据，否则数据不够随机，几个batch之间的数据差异很小。loss出现NAN 1）观察loss值的趋势，如果迭代几次以后一直在增大，最后变成nan，那就是发散了，需要考虑减小训练速率，或者是调整其他参数。 2）数据不能太少，如果太少的话很容易发散" 3）Gradient Clipp 处理gradient之后往后传，一定程度上解决梯度爆炸问题。（但由于有了batch normalization，此方法用的不多） 4）原因可能是训练的时间不够长。 5）可以看一下训练时，网络参数的L2或L1联合观察loss曲线和acc 1）单独的 loss 曲线能提供的信息很少的,一般会结合测试机上的 accuracy 曲线来判断是否过拟合； 2）关键是要看你在测试集上的acc如何； 3）可以把accuracy和loss的曲线画出来，方便设定stepsize，一般在accuracy和loss都趋于平缓的时候就可以减小lr了； 4）看第一次test时（即iteration 0），loss和精度，如果太差，说明初始点的设置有问题 5）使用caffe时，train时，看见的loss是训练集的；accuracy才是测试集的 6）所谓的过拟合是：loss下降，accuracy也下降TensorFlow中loss与val_loss、accuracy和val_accuracy含义loss：训练集损失值accuracy:训练集准确率val_loss:测试集损失值val_accruacy:测试集准确率以下5种情况可供参考：train loss 不断下降，test loss不断下降，说明网络仍在学习;（最好的）train loss 不断下降，test loss趋于不变，说明网络过拟合;（max pool或者正则化）train loss 趋于不变，test loss不断下降，说明数据集100%有问题;（检查dataset）train loss 趋于不变，test loss趋于不变，说明学习遇到瓶颈，需要减小学习率或批量数目;（减少学习率）train loss 不断上升，test loss不断上升，说明网络结构设计不当，训练超参数设置不当，数据集经过清洗等问题。（最不好的情况）震荡修复验证集曲线震荡分析原因：训练的batch_size太小目前batch_size = 64，改成128：改成200：可见，增大batch_size 变大，震荡逐渐消失，同时在测试集的acc也提高了。batch_size为200时，训练集acc小于测试集，模型欠拟合，需要继续增大epoch。总结增大batchsize的好处有三点：1）内存的利用率提高了，大矩阵乘法的并行化效率提高。2）跑完一次epoch(全数据集)所需迭代次数减少，对于相同的数据量的处理速度进一步加快，但是达到相同精度所需要的epoch数量也越来越多。由于这两种因素的矛盾， batch_Size 增大到某个时候，达到时间上的最优。3）一定范围内，batchsize越大，其确定的下降方向就越准，引起训练震荡越小。盲目增大的坏处：1）当数据集太大时，内存撑不住。2）过大的batchsize的结果是网络很容易收敛到一些不好的局部最优点。3）batchsize增大到一定的程度，其确定的下降方向已经基本不再变化。4）太小的batch也存在一些问题，比如训练速度很慢，训练不容易收敛等。5）具体的batch size的选取和训练集的样本数目相关。
泛化能力，过拟合，欠拟合，不收敛，奥卡姆剃刀
泛化能力欠拟合过拟合与不收敛解决手段一、模型训练拟合的分类和表现二、欠拟合三、过拟合总结：泛化能力{message type="success" content="泛化能力（generalization ability）是指机器学习算法对新鲜样本的适应能力，简而言之是在原有的数据集上添加新的数据集，通过训练输出一个合理的结果。学习的目的是学到隐含在数据背后的规律，对具有同一规律的学习集以外的数据，经过训练的网络也能给出合适的输出，该能力称为泛化能力。"/}欠拟合过拟合与不收敛用比较直白的话来讲，就是通过数据训练学习的模型，拿到真实场景去试，这个模型到底行不行，如果达到了一定的要求和标准，它就是行，说明泛化能力好，如果表现很差，说明泛化能力就差。为了更好的理解泛化能力，这里引入三种现象，欠拟合、过拟合以及不收敛。{message type="success" content="欠拟合（under-fitting），是指模型拟合程度不高，数据距离拟合曲线较远，或指模型没有很好地捕捉到数据特征，不能够很好地拟合数据。即，在训练数据集上表现差，在测试集数据也表现差。过拟合（over-fitting），是指模型在训练集上表现很好，在测试集上效果差。不收敛（non-convergence）,指误差函数一直在振荡，不能趋近一个定值，没有找到局部或者全局最小值。"/}举个例子来说明下，好比高考数学考试，为了在高考能有个好成绩，高一到高三，好多人会采用“题海战术”来训练自己的做题能力，但高考试卷上的题，都是新题，几乎没有一模一样的题，学生们为了掌握解题规律就不停的刷题，希望最后自己碰到类似的题，能够举一反三，能学以致用，这种规律掌握的适用性，就是泛化能力。有的人对相似题型的解题规律掌握的很好，并且解题效果也很好，这种就是泛化能力强，这种同学往往数学成绩就好。有的同学成绩不好，就是泛化能力差，可能有三种情况{message type="success" content="1.做了不少题，但没有找到解题规律，不管碰到老题和新题都不会做，这种就是欠拟合。2.做了很多题，自认为做过的每一类题型的解题规律都掌握了，而且在之前“题海战术”的题目中，确实表现的很好，但是一碰到新的题目，完全就不会，或者做错，这种学生就是那种喜欢死记硬背的，这种就是过拟合。3.平常也不做题，然后每次一做题就瞎蒙，导致偶尔对，偶尔错，这种就是不收敛。"/}为了更直观展示，引用了几张图来说明，如下图所示，真实曲线是正弦曲线，蓝色的点是训练数据，红色的线为拟合曲线。解决手段在深度学习的模型建立过程中，一般都是用已经产生的数据训练，然后使用训练得到的模型去拟合未来的数据，借此来预测一些东西。在机器学习和深度学习的训练过程中，经常会出现欠拟合和过拟合的现象。训练一开始，模型通常会欠拟合，所以会对模型进行优化，等训练到一定程度后，就需要解决过拟合的问题了。一、模型训练拟合的分类和表现如何判断过拟合呢？我们在训练的时候会定义训练误差，验证集误差，测试集误差（即泛化误差）。训练误差总是减少的，而泛化误差一开始会减少，到了一定程度后不减少反而开始增加，这时候便出现了过拟合的现象。如下图，直观理解，欠拟合就是还没有学习到数据的特征，还有待继续学习，所以此时判断的不准确；而过拟合则是学习的太彻底，以至于把数据的一些不需要的局部特征或者噪声所带来的特征都给学习到了，所以在测试的时候泛化误差也不佳。从方差和偏差的角度来说，欠拟合就是在训练集上高方差，高偏差，过拟合就是高方差，低偏差。为了更加直观，我们看下面的图对比上图，图一的拟合并没有把大体的规律给拟合出来，拟合效果不好，这个就是欠拟合，还需要继续学习规律，此时模型简单；图三的拟合过于复杂，拟合的过于细致，以至于拟合了一些没有必要的东西，这样在训练集上效果很好，但放到测试集和验证集就会不好。图二是最好的，把数据的规律拟合出来了，也没有更复杂，更换数据集后也不会效果很差。在上面的拟合函数中，可以想到，图三过拟合的拟合函数肯定是一个高次函数，其参数个数肯定比图二多，可以说图三的拟合函数比图二要大，模型更加复杂。这也是过拟合的一个判断经验，模型是否过于复杂。另外针对图三，我们把一些高次变量对应的参数值变小，也就相当于把模型变简单了。从这个角度上讲，可以减少参数值，也就是一般模型过拟合，参数值整体比较大。从模型复杂性上讲，可以是： ——模型的参数个数； ——模型的参数值的大小。 个数越多，参数值越大，模型越复杂。二、欠拟合欠拟合的表现有什么方法来判断模型是否欠拟合呢？其实一般都是依靠模型在训练集和验证集上的表现，有一个大概的判断就行了。如果要有一个具体的方法，可以参考机器学中，学习曲线来判断模型是否过拟合，如下图：欠拟合的解决方案（1）增加数据特征：欠拟合是由于学习不足导致的，可以考虑添加特征，从数据中挖掘更多的特征，有时候嗨需要对特征进行变换，使用组合特征和高次特征；（2）使用更高级的模型：模型简单也会导致欠拟合，即模型参数过少，结构过于简单，例如线性模型只能拟合一次函数的数据。尝试使用更高级的模型有助于解决欠拟合，增加神经网络的层数，增加参数个数，或者使用更高级的方法；（3）减少正则化参数：正则化参数是用来防止过拟合的，出现欠拟合的情况就要考虑减少正则化参数。三、过拟合过拟合的定义模型在训练集上表现好，但在测试集和验证集上表现很差，这就是过拟合过拟合的原因（1）数据量太小这是很容易产生过拟合的原因。设想我们有一组数据很好的满足了三次函数的规律，但我们只取了一小部分数据进行训练，那么得到的模型很可能是一个线性函数，把这个线性函数用于测试集上，可想而知肯定效果很差。（此时训练集上效果好，测试集效果差）（2）训练集和验证集分布不一致这也是很大一个原因。训练集上训练出来的模型适合训练集，当把模型应用到一个不一样分布的数据集上，效果肯定大打折扣，这个是显而易见的。（3）网络模型过于复杂选择模型算法时，选择了一个复杂度很高的模型，然而数据的规律是很简单的，复杂的模型反而不适用了。（4）数据质量很差数据有很多噪声，模型在学习的时候，肯定也会把噪声规律学习到，从而减少了一般性的规律。这个时候模型预测效果也不好。（5）过度训练这是同第四个相联系的，只要模型训练时间足够长，那么模型肯定会把一些噪声隐含的规律学习到，这时候降低模型的性能也是显而易见的。解决方法（1）降低模型复杂度处理过拟合的第一步就是降低模型复杂度。为了降低复杂度，我们可以简单地移除层或者减少神经元的数量使得网络规模变小。与此同时，计算神经网络中不同层的输入和输出维度也十分重要。虽然移除层的数量或神经网络的规模并无通用的规定，但如果你的神经网络发生了过拟合，就尝试缩小它的规模。（2）数据集扩增在数据挖掘领域流行着这样的一句话，“有时候往往拥有更多的数据胜过一个好的模型”。因为我们在使用训练数据训练模型，通过这个模型对将来的数据进行拟合，而在这之间又一个假设便是，训练数据与将来的数据是独立同分布的。即使用当前的训练数据来对将来的数据进行估计与模拟，而更多的数据往往估计与模拟地更准确。因此，更多的数据有时候更优秀。但是往往条件有限，如人力物力财力的不足，而不能收集到更多的数据，如在进行分类的任务中，需要对数据进行打标，并且很多情况下都是人工得进行打标，因此一旦需要打标的数据量过多，就会导致效率低下以及可能出错的情况。所以，往往在这时候，需要采取一些计算的方式与策略在已有的数据集上进行手脚，以得到更多的数据。通俗的讲，数据机扩增即需要得到更多的符合要求的数据，即和已有的数据是独立同分布的，或者近似独立同分布的。一般有以下方法： ——从数据源头采集更多数据 ——复制原有数据加上噪声 ——重采样 ——根据当前数据集估计数据分布参数，利用该分布产生更多数据（3）数据增强使用数据增强可以生成多幅相似图像。这可以帮助我们增加数据集从而减少过拟合。因为随着数据量的增加，模型无法过拟合所有样本，因此不得不进行泛化。计算机视觉领域通常的做法有：翻转，平移，旋转，缩放，改变亮度，添加噪声等。（4）正则化正则化是指在进行目标函数或者代价函数优化时，在目标函数或者代价函数后面加上一个正则项，一般有L1正则和L2正则等。L1惩罚项的目的是使权重绝对值最小化，公式如下：L1惩罚项的目的是使权重的平方最小化，公式如下：下面对两种正则化方法进行了比较：如果数据过于复杂以致没有办法进行准确的建模，那么L2是更好的选择，因为它能够学习数据中呈现的内在模式。而当数据足够简单，可以精确建模的话，L1更合适，对于我遇到的大多数计算机视觉问题，L2正则化几乎总是可以给出最好的结果。然而L1不容易受到离群值的影响。所以正确的正则化选项取决于我们想要解决的问题。总结：正则项是为了降低模型的复杂度，从而避免模型过分拟合训练数据，包括噪声与异常点。从另一个角度讲，正则化即是假设模型参数服从先验概率，即为模型参数添加先验，只是不同的正则化方式的先验分布是不一样的。这样就规定了参数的分布，使得模型的复杂度降低（试想一下，限定条件多了，是不是模型的复杂度就降低了呢），这样模型对于噪声和异常点的抗干扰性的能力增强，从而提高模型的泛化能力。还有个解释，从贝叶斯学派来看，加了先验，在数据少的时候，先验知识可以防止过拟合；从频率学派来看，正则项限定了参数的取值，从而提高了模型的稳定性，而稳定性强的模型不会过拟合，即控制模型空间。另外一个角度，过拟合从直观上理解便是，在对训练数据进行拟合时，需要照顾到每个点，从而使得拟合函数波动性非常大，即方差大。在某些小区间里，函数值的变化很剧烈，意味着函数在某些小区间的导数值的绝对值非常大，由于自变量的值在给定的训练数据集中是一定的，因为只有系数足够大，才能保证导数的绝对值足够大，如下图：另一个解释，规则化项的引入，在训练（最小化cost）的过程中，当某一维的特征所对应的权重过大时，而此时模型的预测和真实数据之间的距离很小，通过规则化项就可以使整体的cost取较大的值，从而，在训练的过程中避免了去选择了那些某一维（或几维）特征权重过大的情况，即过分依赖某一维（或几维）的特征。L1和L2的区别是，L1正则是拉普拉斯先验，L2正则则是高斯先验。它们都是服从均值为0，协方差为1/λ。当λ=0，即没有先验，没有正则项，则相当于先验分布具有无穷大的协方差，那么这个先验约束则会非常弱，模型为了拟合拟合所有的训练集数据，参数可以变得任意大从而使得模型不稳定，即方差大而偏差小。λ越大，表明先验分布协方差越小，偏差越大，模型越稳定。即，加入正则项是在偏差bias与方差variance之间做平衡tradeoff。下图即为L2与L1正则的区别：上图中的模型是线性回归，有两个特征，要优化的参数分别是w1和w2，左图的正则化是L2，右图是L1。蓝色线就是优化过程中遇到的等高线，一圈代表一个目标函数值，圆心就是样本观测值（假设一个样本），半径就是误差值，受限条件就是红色边界（就是正则化那部分），二者相交处，才是最优参数。可见右边的最优参数只可能在坐标轴上，所以就会出现0权重参数，使得模型稀疏。其实拉普拉斯分布和高斯分布是数学家从试验中误差服从什么分布研究得出的。一般直观上的认识是服从均值为0的对称分布，并且误差大的概率低，误差小的概率高，因为拉普拉斯使用拉普拉斯分布对误差的分布进行拟合，如下图：而拉普拉斯在最高点，即自变量为0处不可导，因为不便于计算，于是高斯在这基础上使用高斯分布对其进行拟合，如下图：（5）dropout正则时通过再代价函数后面加上正则项来防止过拟合的。而在神经网络中，有一种方法时通过修改神经网络本身结构实现的，其名为dropout。该方法是对网络进行训练时用的一种技巧，对于如下的三层人工神经网络：对于上图所示的网络，在训练开始时，随即删除一些（可自己设定概率）隐藏层神经元，即认为这些神经元不存在，同时保持输入层和输出层的个数不变，这样便得到如下的ANN：然后按照BP学习算法对ANN中的参数进行学习更新（虚线链接的单元不更新，因为认为这些连接元被临时删除了）。这样一次迭代更新便完成了，下一次迭代中，同样随机删除一些神经元，与上次不一样，做随机选择，这样一直进行，直至训练结束。这种技术被证明可以减少很多问题的过拟合，这些问题包括图像分类，图像切割，词嵌入，语义匹配等问题。（6）早停对模型的训练即是对模型的参数进行更新的过程，这个参数学习的过程往往会用到一些迭代方法，如梯度下降（Gradient descent）学习算法。Early stopping一种迭代次数截断的方法来防止过拟合的方法，即在模型对训练数据集迭代收敛之前停止迭代来防止过拟合。Early stopping方法的具体做法是，在每一个Epoch结束时（一个Epoch集为对所有的训练数据的一轮遍历）计算验证集的正确率，当正确率不再提高时，就停止训练。这种做法很符合直观感受，因为正确率都不在提高了，再继续训练也是无益的，只会提高训练的时间。如下图，在几次迭代后，即使训练误差仍然在减少，但测验误差已经开始增加了。那么该做法的一个重点便是怎样才认为验证准确率validation accurary不再提高了呢？并不是说验证准确率validation accurary一降下来便认为不再提高了，因为可能经过这个Epoch后，正确率降低了，但是随后的Epoch又让正确率又上去了，所以不能根据一两次的连续降低就判断不再提高。一般的做法是，在训练的过程中，记录到目前为止最好的验证准确率validation accurary，当连续10次没达到最佳正确率时，认为不再提高了，此时便可以停止迭代。这种策略也称为“No-improvement-in-n”，n即Epoch的次数，可以根据实际情况取，如10、20、30……（7）重新清洗数据把明显异常的数据剔除。（8）使用集成学习方法把多个模型集成在一起，降低单个模型的过拟合风险。
1.标签 Label2.特征 Feature3.样本 Example3.1有标签样本(labeled):3.2无标签样本(unlabeled)：4.模型 Model5.回归 Regression6.分类 Classification7.机器学习算法地图1.标签 Label标签：所预测的东东实际是什么（可理解为结论），如线性回归中的 y 变量，如分类问题中图片中是猫是狗（或图片中狗的种类）、房子未来的价格、音频中的单词等等任何事物，都属于Label。（如一组图片，已经表明了哪些是狗，哪些是猫，这里Label就是分类问题中每一个类）2.特征 Feature特征是事物固有属性，可理解为做出某个判断的依据，如人的特征有长相、衣服、行为动作等等，一个事物可以有N多特征，这些组成了事物的特性，作为机器学习中识别、学习的基本依据。特征是机器学习模型的输入变量。如线性回归中的 x 变量。例如在垃圾邮件分类问题中，特征可能包括：电子邮件中是否包含 “ 广告、贷款、交易” 等短语电子邮件文本中的字词发件人的地址发送电子邮件的时段其中机器学习重要步骤：特征提取就是通过多种方式，对数据的特征数据进行提取。一般，特征数据越多，训练的机器学习模型就会越精确，但处理难度也越大。3.样本 Example样本是指一组或几组数据，样本一般作为训练数据来训练模型样本分为以下两类：有标签样本无标签样本3.1有标签样本(labeled):同时包含特征和标签在监督学习中利用数据做训练时，有标签数据/样本（Labeled data）或叫有/无标记数据，就是指有没有将数据归为要预测的类别。例如以下房价数据集：其中包含特征：卧室数量等，最右边一列是标签：房价中间值（注，因为该问题要预测未来房价走势，所以Label就是某条房屋数据中的房价）3.2无标签样本(unlabeled)：包含特征，但不包含标签如以下数据集： 去掉了Label，但是一样有用（如用在测试训练后的模型，即训练好模型后，输入该数据，那到预测后的房价与原标签进行比较，得到模型误差）4.模型 Model模型定义了特征与标签之间的关系，就是我们机器学习的一组数据关系表示，也是我们学习机器学习的核心例如，垃圾邮件检测模型可能会将某些特征与“垃圾邮件”紧密联系起来。模型生命周期的两个重要阶段：训练 Training是指创建或学习模型。也就是说，向模型展示有标签样本，让模型逐渐学习特征与标签之间的关系。训练模型表示通过有标签样本来学习（确定）所有权重和偏差的理想值推断 Inference是指将训练后的模型应用于无标签样本。也就是说，使用经过训练的模型做出有用的预测 (y’)。例如，模型训练好后，就可以使用模型进行Inference ，可以针对新的无标签样本预测房价medianHouseValue。5.回归 Regression回归就是我们数学学习的线性方程，是一种经典函数逼近算法。在机器学习中，就是根据数据集，建立一个线性方程组，能够无线逼近数据集中的数据点，是一种基于已有数据关系实现预测的算法。回归模型可预测连续值（线性）例如，回归模型做出的预测可回答如下问题：某小区房价的趋势？用户点击此广告的概率是多少？当机器学习模型最终目标（模型输出）是求一个具体数值时，例如房价的模型输出为25000，则大多数可以通过回归问题来解决。线性回归的好处在与模型简单，计算速度快，方便应用在分布式系统对大数据进行处理。线性回归还有个姐妹：逻辑回归（Logistic Regression），主要应用在分类领域6.分类 Classification顾名思义，分类模型可用来预测离散值例如，分类模型做出的预测可回答如下问题：是/否问题，某个指定电子邮件是垃圾邮件还是非垃圾邮件？图片是动物还是人？垃圾分类当机器学习模型最终目标（模型输出）是布尔或一定范围的数时，例如判断一张图片是不是人，模型输出0/1：0不是，1是；又例如垃圾分类，模型输出1-10之间的整数，1代表生活垃圾，2代表厨余垃圾。。等等，这类需求则大多数可以通过分类问题来解决。7.机器学习算法地图机器学习算法多种多样，许多情况下，建模和算法设计是Designer所选择的，具体采用哪种算法也没有一定要求，根据实际具体问题具体分析。
机器学习/深度学习-10个激活函数详解
综述1. Sigmoid 激活函数2. Tanh / 双曲正切激活函数3. ReLU 激活函数4. Leaky ReLU5. ELU6. PReLU（Parametric ReLU）7. Softmax8. Swish9. Maxout10. Softplus综述激活函数（Activation Function）是一种添加到人工神经网络中的函数，旨在帮助网络学习数据中的复杂模式。类似于人类大脑中基于神经元的模型，激活函数最终决定了要发射给下一个神经元的内容。在人工神经网络中，一个节点的激活函数定义了该节点在给定的输入或输入集合下的输出。标准的计算机芯片电路可以看作是根据输入得到开（1）或关（0）输出的数字电路激活函数。因此，激活函数是确定神经网络输出的数学方程式，本文概述了深度学习中常见的十种激活函数及其优缺点。首先我们来了解一下人工神经元的工作原理，大致如下：上述过程的数学可视化过程如下图所示：1. Sigmoid 激活函数Sigmoid 函数的图像看起来像一个 S 形曲线。函数表达式如下：$$\[\mathrm{f(z)=1/(1+e^{\wedge}-z)}\]$${alert type="success"}在什么情况下适合使用 Sigmoid 激活函数呢？Sigmoid 函数的输出范围是 0 到 1。由于输出值限定在 0 到 1，因此它对每个神经元的输出进行了归一化；用于将预测概率作为输出的模型。由于概率的取值范围是 0 到 1，因此 Sigmoid 函数非常合适；梯度平滑，避免「跳跃」的输出值；函数是可微的。这意味着可以找到任意两个点的 sigmoid 曲线的斜率；明确的预测，即非常接近 1 或 0。{/alert}{alert type="warning"}Sigmoid 激活函数有哪些缺点？倾向于梯度消失；函数输出不是以 0 为中心的，这会降低权重更新的效率；Sigmoid 函数执行指数运算，计算机运行得较慢。{/alert}2. Tanh / 双曲正切激活函数tanh 激活函数的图像也是 S 形，表达式如下：$$f(x)=tanh(x)=\frac{2}{1+e^{-2x}}-1$$tanh 是一个双曲正切函数。tanh 函数和 sigmoid 函数的曲线相对相似。但是它比 sigmoid 函数更有一些优势。首先，当输入较大或较小时，输出几乎是平滑的并且梯度较小，这不利于权重更新。二者的区别在于输出间隔，tanh 的输出间隔为 1，并且整个函数以 0 为中心，比 sigmoid 函数更好；在 tanh 图中，负输入将被强映射为负，而零输入被映射为接近零。注意：在一般的二元分类问题中，tanh 函数用于隐藏层，而 sigmoid 函数用于输出层，但这并不是固定的，需要根据特定问题进行调整。3. ReLU 激活函数ReLU 激活函数图像如上图所示，函数表达式如下：{alert type="success"}ReLU 函数是深度学习中较为流行的一种激活函数，相比于 sigmoid 函数和 tanh 函数，它具有如下优点：当输入为正时，不存在梯度饱和问题。计算速度快得多。ReLU 函数中只存在线性关系，因此它的计算速度比 sigmoid 和 tanh 更快{/alert}{alert type="warning"}当然，它也有缺点：Dead ReLU 问题。当输入为负时，ReLU 完全失效，在正向传播过程中，这不是问题。有些区域很敏感，有些则不敏感。但是在反向传播过程中，如果输入负数，则梯度将完全为零，sigmoid 函数和 tanh 函数也具有相同的问题；我们发现 ReLU 函数的输出为 0 或正数，这意味着 ReLU 函数不是以 0 为中心的函数。{/alert}4. Leaky ReLU它是一种专门设计用于解决 Dead ReLU 问题的激活函数：ReLU vs Leaky ReLU为什么 Leaky ReLU 比 ReLU 更好？$$f(y_i)=\begin{cases}y_i,&\text{if }y_i>0\\a_iy_i,&\text{if }y_i\leq0\end{cases}.$$1、Leaky ReLU 通过把 x 的非常小的线性分量给予负输入（0.01x）来调整负值的零梯度（zero gradients）问题；2、leak 有助于扩大 ReLU 函数的范围，通常 a 的值为 0.01 左右；3、Leaky ReLU 的函数范围是（负无穷到正无穷）。注意：从理论上讲，Leaky ReLU 具有 ReLU 的所有优点，而且 Dead ReLU 不会有任何问题，但在实际操作中，尚未完全证明 Leaky ReLU 总是比 ReLU 更好。5. ELUELU vs Leaky ReLU vs ReLUELU 的提出也解决了 ReLU 的问题。与 ReLU 相比，ELU 有负值，这会使激活的平均值接近零。均值激活接近于零可以使学习更快，因为它们使梯度更接近自然梯度。显然，ELU 具有 ReLU 的所有优点，并且：没有 Dead ReLU 问题，输出的平均值接近 0，以 0 为中心；ELU 通过减少偏置偏移的影响，使正常梯度更接近于单位自然梯度，从而使均值向零加速学习；ELU 在较小的输入下会饱和至负值，从而减少前向传播的变异和信息。一个小问题是它的计算强度更高。与 Leaky ReLU 类似，尽管理论上比 ReLU 要好，但目前在实践中没有充分的证据表明 ELU 总是比 ReLU 好。6. PReLU（Parametric ReLU）PReLU 也是 ReLU 的改进版本：$$f(y_i)=\begin{cases}y_i,&\text{if }y_i>0\\a_iy_i,&\text{if }y_i\leq0\end{cases}.$$看一下 PReLU 的公式：参数α通常为 0 到 1 之间的数字，并且通常相对较小。如果 a_i= 0，则 f 变为 ReLU如果 a_i> 0，则 f 变为 leaky ReLU如果 a_i 是可学习的参数，则 f 变为 PReLUPReLU 的优点如下：在负值域，PReLU 的斜率较小，这也可以避免 Dead ReLU 问题。与 ELU 相比，PReLU 在负值域是线性运算。尽管斜率很小，但不会趋于 0。7. SoftmaxSoftmax 是用于多类分类问题的激活函数，在多类分类问题中，超过两个类标签则需要类成员关系。对于长度为 K 的任意实向量，Softmax 可以将其压缩为长度为 K，值在（0，1）范围内，并且向量中元素的总和为 1 的实向量。Softmax 与正常的 max 函数不同：max 函数仅输出最大值，但 Softmax 确保较小的值具有较小的概率，并且不会直接丢弃。我们可以认为它是 argmax 函数的概率版本或「soft」版本。Softmax 函数的分母结合了原始输出值的所有因子，这意味着 Softmax 函数获得的各种概率彼此相关。Softmax 激活函数的主要缺点是：在零点不可微；负输入的梯度为零，这意味着对于该区域的激活，权重不会在反向传播期间更新，因此会产生永不激活的死亡神经元。8. Swish函数表达式：y = x * sigmoid (x)Swish 的设计受到了 LSTM 和高速网络中 gating 的 sigmoid 函数使用的启发。我们使用相同的 gating 值来简化 gating 机制，这称为 self-gating。self-gating 的优点在于它只需要简单的标量输入，而普通的 gating 则需要多个标量输入。这使得诸如 Swish 之类的 self-gated 激活函数能够轻松替换以单个标量为输入的激活函数（例如 ReLU），而无需更改隐藏容量或参数数量。{alert type="success"}Swish 激活函数的主要优点如下：「无界性」有助于防止慢速训练期间，梯度逐渐接近 0 并导致饱和；（同时，有界性也是有优势的，因为有界激活函数可以具有很强的正则化，并且较大的负输入问题也能解决）；导数恒 > 0；平滑度在优化和泛化中起了重要作用。{/alert}9. Maxout在 Maxout 层，激活函数是输入的最大值，因此只有 2 个 maxout 节点的多层感知机就可以拟合任意的凸函数。单个 Maxout 节点可以解释为对一个实值函数进行分段线性近似 (PWL) ，其中函数图上任意两点之间的线段位于图（凸函数）的上方。$ReLU=\max\bigl(0,x\bigr),\mathrm{abs}\bigl(x\bigr)=\max\bigl(x,-x\bigr)$Maxout 也可以对 d 维向量（V）实现：假设两个凸函数 h_1(x) 和 h_2(x)，由两个 Maxout 节点近似化，函数 g(x) 是连续的 PWL 函数。$$g\bigl(x\bigr)=h_1\bigl(x\bigr)-h_2\bigl(x\bigr)$$因此，由两个 Maxout 节点组成的 Maxout 层可以很好地近似任何连续函数。10. SoftplusSoftplus 函数：f（x）= ln（1 + exp x）Softplus 的导数为f ′(x)=exp(x) / ( 1+exp⁡ x )= 1/ (1 +exp(−x ))，也称为 logistic / sigmoid 函数。Softplus 函数类似于 ReLU 函数，但是相对较平滑，像 ReLU 一样是单侧抑制。它的接受范围很广：(0, + inf)。
不同于图像处理，神经网络在处理其他不少领域，常常需要单特征分类，本例实现同半径的圆进行多分类（3分类），特征即为圆的半径。输入层12节点，一个6节点的隐藏层，输出层3个节点。1.目标2.开发环境3.准备数据4.处理数据5.构建BP神经网络总结1.目标通过BP算法实现对不同半径的圆的分类。2.开发环境Python3.10;jupyter notebook3.准备数据目的： 生成3类圆在第一象限内的坐标（圆心都是原点）第1类：半径范围为1~10，分类标识为‘0’第2类：半径范围为10~20，分类标识为‘1’第3类：半径范围为20~30，分类标识为‘2’代码如下：data_generate.pyimport numpy as np import math import random import csv # 只生成第一象限内的坐标即可。每个圆生成12个坐标(x,y)，相当于12个特征维度 def generate_circle(lower, upper): # 圆在第一象限内的坐标 data_ur = np.zeros(shape=(12, 2)) # 在上下限范围内，随机产生一个值作为半径 radius = random.randint(int(lower), int(upper)) # 在0~90度内，每隔7.5度取一次坐标，正好取12次 angles = np.arange(0, 0.5 * np.pi, 1 / 24 * np.pi) for i in range(12): temp_ur = np.zeros(2) x = round(radius * math.cos(angles[i]), 2) y = round(radius * math.sin(angles[i]), 2) temp_ur[0] = x temp_ur[1] = y data_ur[i] = temp_ur return data_ur, label # 将坐标保存到CSV文件中 def save2csv(data, batch, label): out = open("D:\\circles.csv", 'a', newline='') csv_write = csv.writer(out, dialect='excel') length = int(data.size / 2) for i in range(length): string = str(data[i][0]) + ',' + str(data[i][1]) + ',' + str(batch) + ',' + str(label) temp = string.split(',') csv_write.writerow(temp) out.close() if __name__ == "__main__": ''' 生成3类圆，标签（label）分别为：0、1、2 第1类圆的半径下限为1，上限为10 第2类圆的半径下限为10，上限为20 第3类圆的半径下限为20，上限为30 圆心都为原点 ''' lower = [1, 10, 20] # 半径随机值的下限 upper = [10, 20, 30] # 半径随机值的上限 label = ['0', '1', '2'] # 种类的标签 for i in range(len(label)): # 每类数据生成50组 for j in range(50): data, label = generate_circle(lower[i], upper[i]) batch = 50 * i + j + 1 # 数据的批次，用来区分每个坐标是属于哪个圆的 save2csv(data, batch, label[i])共3类圆，每类生成50个圆，每个圆有12个坐标，因此在输出文件D:\circles.csv中总共有3×50×12=1800行数据：通过生成的坐标绘制散点图如下：图中蓝色的点是label为0的圆，绿色的点是label为1的圆，红色的点是label为2的圆。4.处理数据目标： 根据第3步获得的坐标，计算每个圆的半径（勾股定理）作为神经网络的输入。代码如下：data_process.pyimport csv import math def process(file_name): # 要读取的CSV文件 csv_file = csv.reader(open(file_name, encoding='utf-8')) # 要生成的CSV文件 out_file = open("D:\\circles_data.csv", 'a', newline='') csv_write = csv.writer(out_file, dialect='excel') # 将csv_file每一行的圆坐标取出，如果是同一批次的（同一个圆），则写入到out_file的一行中 rows = [row for row in csv_file] current_batch = 'unknown' current_label = 'unknown' data_list = [] for r in rows: # 将无关字符都替换为空格 temp_string = str(r).replace('[', '').replace(']', '').replace('\'', '') # 将字符串以逗号分隔 item = str(temp_string).split(',') # 分别取出x轴坐标、y轴坐标、批次、标签 x = float(item[0]) y = float(item[1]) batch = item[2] label = item[3] # 如果是同一批次（同一个圆），则都放入data_list中 if current_batch == batch: # 根据勾股定理计算半径 distance = math.sqrt(pow(x, 2) + pow(y, 2)) data_list.append(distance) # 如果不是同一批次（同一个圆），则在末尾加上标签后，作为一行写入输出文件 else: if len(data_list) != 0: # 这个地方需注意一下，最后的标签用3列来表示，而不是一列 if label.strip() == '0': data_list.append(1) data_list.append(0) data_list.append(0) elif label.strip() == '1': data_list.append(0) data_list.append(1) data_list.append(0) else: data_list.append(0) data_list.append(0) data_list.append(1) result_string = str(data_list).replace('[', '').replace(']', '').replace('\'', '').strip() csv_write.writerow(result_string.split(',')) # 清空data_list，继续写入下一个批次 data_list.clear() distance = math.sqrt(pow(x, 2) + pow(y, 2)) data_list.append(distance) current_batch = batch current_label = label # 确保最后一个批次的数据能写入 if current_label.strip() == '0': data_list.append(1) data_list.append(0) data_list.append(0) elif current_label.strip() == '1': data_list.append(0) data_list.append(1) data_list.append(0) else: data_list.append(0) data_list.append(0) data_list.append(1) result_string = str(data_list).replace('[', '').replace(']', '').replace('\'', '').strip() csv_write.writerow(result_string.split(',')) # 关闭输出文件 out_file.close() if __name__ == "__main__": process('D:\\circles.csv')需要注意的是，生成的CSV文件共有15列，前12列为坐标对应的半径值，最后三列组合起来表示分类（label）：(1,0,0)表示类型为“0”的圆，(0,1,0)表示类型为“1”的圆，(0,0,1)表示类型为“2”的圆，这样做的目的是为了下一步使用神经网络时处理起来方便。5.构建BP神经网络上一步处理好的数据可以作为训练数据，命名为：circles_data_training.csv重复第3步和第4步，可以生成另一批数据作为测试数据，命名为：circles_data_test.csv当然，也可以手动划分出训练数据和测试数据。训练数据和测试数据在输入时，做了矩阵的转置，将列转置为行。代码如下：data_analysis_bpnn.pyimport pandas as pd import numpy as np import datetime from sklearn.utils import shuffle # 1.初始化参数 def initialize_parameters(n_x, n_h, n_y): np.random.seed(2) # 权重和偏置矩阵 w1 = np.random.randn(n_h, n_x) * 0.01 b1 = np.zeros(shape=(n_h, 1)) w2 = np.random.randn(n_y, n_h) * 0.01 b2 = np.zeros(shape=(n_y, 1)) # 通过字典存储参数 parameters = {'w1': w1, 'b1': b1, 'w2': w2, 'b2': b2} return parameters # 2.前向传播 def forward_propagation(X, parameters): w1 = parameters['w1'] b1 = parameters['b1'] w2 = parameters['w2'] b2 = parameters['b2'] # 通过前向传播来计算a2 z1 = np.dot(w1, X) + b1 # 这个地方需注意矩阵加法：虽然(w1*X)和b1的维度不同，但可以相加 a1 = np.tanh(z1) # 使用tanh作为第一层的激活函数 z2 = np.dot(w2, a1) + b2 a2 = 1 / (1 + np.exp(-z2)) # 使用sigmoid作为第二层的激活函数 # 通过字典存储参数 cache = {'z1': z1, 'a1': a1, 'z2': z2, 'a2': a2} return a2, cache # 3.计算代价函数 def compute_cost(a2, Y, parameters): m = Y.shape[1] # Y的列数即为总的样本数 # 采用交叉熵（cross-entropy）作为代价函数 logprobs = np.multiply(np.log(a2), Y) + np.multiply((1 - Y), np.log(1 - a2)) cost = - np.sum(logprobs) / m return cost # 4.反向传播（计算代价函数的导数） def backward_propagation(parameters, cache, X, Y): m = Y.shape[1] w2 = parameters['w2'] a1 = cache['a1'] a2 = cache['a2'] # 反向传播，计算dw1、db1、dw2、db2 dz2 = a2 - Y dw2 = (1 / m) * np.dot(dz2, a1.T) db2 = (1 / m) * np.sum(dz2, axis=1, keepdims=True) dz1 = np.multiply(np.dot(w2.T, dz2), 1 - np.power(a1, 2)) dw1 = (1 / m) * np.dot(dz1, X.T) db1 = (1 / m) * np.sum(dz1, axis=1, keepdims=True) grads = {'dw1': dw1, 'db1': db1, 'dw2': dw2, 'db2': db2} return grads # 5.更新参数 def update_parameters(parameters, grads, learning_rate=0.0075): w1 = parameters['w1'] b1 = parameters['b1'] w2 = parameters['w2'] b2 = parameters['b2'] dw1 = grads['dw1'] db1 = grads['db1'] dw2 = grads['dw2'] db2 = grads['db2'] # 更新参数 w1 = w1 - dw1 * learning_rate b1 = b1 - db1 * learning_rate w2 = w2 - dw2 * learning_rate b2 = b2 - db2 * learning_rate parameters = {'w1': w1, 'b1': b1, 'w2': w2, 'b2': b2} return parameters # 建立神经网络 def nn_model(X, Y, n_h, n_input, n_output, num_iterations=10000, print_cost=False): np.random.seed(3) n_x = n_input # 输入层节点数 n_y = n_output # 输出层节点数 # 1.初始化参数 parameters = initialize_parameters(n_x, n_h, n_y) # 梯度下降循环 for i in range(0, num_iterations): # 2.前向传播 a2, cache = forward_propagation(X, parameters) # 3.计算代价函数 cost = compute_cost(a2, Y, parameters) # 4.反向传播 grads = backward_propagation(parameters, cache, X, Y) # 5.更新参数 parameters = update_parameters(parameters, grads) # 每1000次迭代，输出一次代价函数 if print_cost and i % 1000 == 0: print('迭代第%i次，代价函数为：%f' % (i, cost)) return parameters # 对模型进行测试 def predict(parameters, x_test, y_test): w1 = parameters['w1'] b1 = parameters['b1'] w2 = parameters['w2'] b2 = parameters['b2'] z1 = np.dot(w1, x_test) + b1 a1 = np.tanh(z1) z2 = np.dot(w2, a1) + b2 a2 = 1 / (1 + np.exp(-z2)) # 结果的维度 n_rows = y_test.shape[0] n_cols = y_test.shape[1] # 预测值结果存储 output = np.empty(shape=(n_rows, n_cols), dtype=int) # 取出每条测试数据的预测结果 for i in range(n_cols): # 将每条测试数据的预测结果（概率）存为一个行向量 temp = np.zeros(shape=n_rows) for j in range(n_rows): temp[j] = a2[j][i] # 将每条结果（概率）从小到大排序，并获得相应下标 sorted_dist = np.argsort(temp) length = len(sorted_dist) # 将概率最大的置为1，其它置为0 for k in range(length): if k == sorted_dist[length - 1]: output[k][i] = 1 else: output[k][i] = 0 print('预测结果：') print(output) print('真实结果：') print(y_test) count = 0 for k in range(0, n_cols): if output[0][k] == y_test[0][k] and output[1][k] == y_test[1][k] and output[2][k] == y_test[2][k]: count = count + 1 acc = count / int(y_test.shape[1]) * 100 print('准确率：%.2f%%' % acc) if __name__ == "__main__": # 读取数据 data_set = pd.read_csv('D:\\circles_data_training.csv', header=None) data_set = shuffle(data_set) # 打乱数据的输入顺序 # 取出“特征”和“标签”，并做了转置，将列转置为行 X = data_set.ix[:, 0:11].values.T # 前12列是特征 Y = data_set.ix[:, 12:14].values.T # 后3列是标签 Y = Y.astype('uint8') # 开始训练 start_time = datetime.datetime.now() # 输入12个节点，隐层6个节点，输出3个节点，迭代10000次 parameters = nn_model(X, Y, n_h=6, n_input=12, n_output=3, num_iterations=10000, print_cost=True) end_time = datetime.datetime.now() print("用时：" + str((end_time - start_time).seconds) + 's' + str(round((end_time - start_time).microseconds / 1000)) + 'ms') # 对模型进行测试 data_test = pd.read_csv('D:\\circles_data_test.csv', header=None) x_test = data_test.ix[:, 0:11].values.T y_test = data_test.ix[:, 12:14].values.T y_test = y_test.astype('uint8') predict(parameters, x_test, y_test)码中需要注意的几个关键参数：learning_rate=0.0075，学习率（可调）n_h=6，隐藏层节点数（可调）n_input=12，输入层节点数n_output=3，输出层节点数num_iterations=10000，迭代次数（可调）另外，对于predict(parameters, x_test, y_test)函数需要说明一下：a2矩阵是最终的预测结果，但是是以概率的形式表示的（可以打印看一下）。通过比较3个类的概率，选出概率最大的那个置为1，其它两个置为0，形成output矩阵。运行结果：上图中第一红框表示神经网络预测出的分类结果，第二个红框表示测试集中真实的分类（(1,0,0)表示这个圆属于类型“0”）。每次运行时，正确率可能不一样，最高能达到100%。通过调整刚才提到的关键参数中的学习率、隐藏层节点数、迭代次数可以提高正确率。总结 神经网络的输入为12个半径值，输出结果为一个3维向量，其中置1的位就是对应的分类。 在实际应用中，12个半径值对应12个特征，3维向量表示能分3类。只要根据实际应用的需要修改特征数和分类数即可将上述程序应用于不同分类场景。
1、描述2、code3、描述4、code1、描述请编写代码实现train_and_predict功能，实现能够根据四个特征对三种类型的鸢尾花进行分类。train_and_predict函数接收三个参数:train_input_features—二维NumPy数组，其中每个元素都是一个数组，它包含:萼片长度、萼片宽度、花瓣长度和花瓣宽度。train_outputs—一维NumPy数组，其中每个元素都是一个数字，表示在train_input_features的同一行中描述的鸢尾花种类。0表示鸢尾setosa，1表示versicolor，2代表Iris virginica。prediction_features—二维NumPy数组，其中每个元素都是一个数组，包含:萼片长度、萼片宽度、花瓣长度和花瓣宽度。该函数使用train_input_features作为输入数据，使用train_outputs作为预期结果来训练分类器。请使用训练过的分类器来预测prediction_features的标签，并将它们作为可迭代对象返回(如list或numpy.ndarray)。结果中的第n个位置是prediction_features参数的第n行。2、code# 导入numpy库，用于处理多维数组 import numpy as np # 导入sklearn库中的数据集、模型选择、度量和朴素贝叶斯模块 from sklearn import datasets from sklearn.model_selection import train_test_split from sklearn import metrics from sklearn.naive_bayes import GaussianNB # 定义train_and_predict函数，接收三个参数:训练输入特征、训练输出标签和预测输入特征 def train_and_predict(train_input_features, train_outputs, prediction_features): # 创建一个高斯朴素贝叶斯分类器对象 clf = GaussianNB() # 使用训练输入特征和训练输出标签来训练分类器 clf.fit(train_input_features, train_outputs) # 使用预测输入特征来预测输出标签，并将结果返回 y_pred = clf.predict(prediction_features) return y_pred # 加载鸢尾花数据集，包含150个样本，每个样本有四个特征和一个标签 iris = datasets.load_iris() # 将数据集随机分成训练集和测试集，其中训练集占70%，测试集占30%，并设置随机种子为0 X_train, X_test, y_train, y_test = train_test_split( iris.data, iris.target, test_size=0.3, random_state=0 ) # 调用train_and_predict函数，使用训练集来训练分类器，并使用测试集来预测标签，将结果赋值给y_pred y_pred = train_and_predict(X_train, y_train, X_test) # 如果y_pred不为空，打印预测标签和真实标签的准确率，即正确预测的比例 if y_pred is not None: print(metrics.accuracy_score(y_test, y_pred))3、描述机器学习库 sklearn 自带鸢尾花分类数据集，分为四个特征和三个类别，其中这三个类别在数据集中分别表示为 0, 1 和 2，请实现 transform_three2two_cate 函数的功能，该函数是一个无参函数，要求将数据集中 label 为 2 的数据进行移除，也就是说仅保留 label 为 0 和为 1 的情况，并且对 label 为 0 和 1 的特征数据进行保留，返回值为 numpy.ndarray 格式的训练特征数据和 label 数据，分别为命名为 new_feat 和 new_label。然后在此基础上，实现 train_and_evaluate 功能，并使用生成的 new_feat 和 new_label 数据集进行二分类训练，限定机器学习分类器只能从逻辑回归和决策树中进行选择，将训练数据和测试数据按照 8:2 的比例进行分割。要求输出测试集上的 accuracy_score，同时要求 accuracy_score 要不小于 0.95。4、code#导入numpy库，它是一个提供了多维数组和矩阵运算等功能的Python库 import numpy as np #导入sklearn库中的datasets模块，它提供了一些内置的数据集 from sklearn import datasets #导入sklearn库中的model_selection模块，它提供了一些用于模型选择和评估的工具，比如划分训练集和测试集 from sklearn.model_selection import train_test_split #导入sklearn库中的preprocessing模块，它提供了一些用于数据预处理的工具，比如归一化 from sklearn.preprocessing import MinMaxScaler #导入sklearn库中的linear_model模块，它提供了一些线性模型，比如逻辑回归 from sklearn.linear_model import LogisticRegression #导入sklearn库中的metrics模块，它提供了一些用于评估模型性能的指标，比如F1分数、ROC曲线面积、准确率等 from sklearn.metrics import f1_score,roc_auc_score,accuracy_score #导入sklearn库中的tree模块，它提供了一些树形模型，比如决策树 from sklearn.tree import DecisionTreeClassifier #定义一个函数transform_three2two_cate，它的作用是将鸢尾花数据集中的三分类问题转化为二分类问题 def transform_three2two_cate(): #从datasets模块中加载鸢尾花数据集，并赋值给data变量 data = datasets.load_iris() #其中data特征数据的key为data，标签数据的key为target #需要取出原来的特征数据和标签数据，移除标签为2的label和特征数据，返回值new_feat为numpy.ndarray格式特征数据，new_label为对应的numpy.ndarray格式label数据 #需要注意特征和标签的顺序一致性，否则数据集将混乱 #code start here #使用numpy库中的where函数找出标签为2的索引，并赋值给index_arr变量 index_arr = np.where(data.target == 2)[0] #使用numpy库中的delete函数删除特征数据中对应索引的行，并赋值给new_feat变量 new_feat = np.delete(data.data, index_arr, 0) #使用numpy库中的delete函数删除标签数据中对应索引的元素，并赋值给new_label变量 new_label = np.delete(data.target, index_arr) #code end here #返回新的特征数据和标签数据 return new_feat,new_label #定义一个函数train_and_evaluate，它的作用是用决策树分类器来训练和评估鸢尾花数据集 def train_and_evaluate(): #调用transform_three2two_cate函数，得到新的特征数据和标签数据，并赋值给data_X和data_Y变量 data_X,data_Y = transform_three2two_cate() #使用train_test_split函数，将数据集划分为训练集和测试集，其中测试集占20%，并赋值给train_x,test_x,train_y,test_y变量 train_x,test_x,train_y,test_y = train_test_split(data_X,data_Y,test_size = 0.2) #已经划分好训练集和测试集，接下来请实现对数据的训练 #code start here #创建一个决策树分类器的实例，并赋值给estimator变量 estimator = DecisionTreeClassifier() #使用fit方法，用训练集的特征和标签来训练决策树分类器 estimator.fit(train_x, train_y) #使用predict方法，用测试集的特征来预测标签，并赋值给y_predict变量 y_predict = estimator.predict(test_x) #code end here #注意模型预测的label需要定义为 y_predict，格式为list或numpy.ndarray #使用accuracy_score函数，计算测试集上的准确率分数，并打印出来 print(accuracy_score(y_predict,test_y)) #如果这个文件是作为主程序运行，则执行以下代码 if __name__ == "__main__": #调用train_and_evaluate函数 train_and_evaluate() #要求执行train_and_evaluate()后输出为： #1、{0,1}，代表数据label为0和1 #2、测试集上的准确率分数，要求>0.95
Jupyter Notebook安装（Windows）下载Jupyter Notebook（1）打开cmd（如果没有把Python安装目录添加到Path，需要切换到Python安装目录的Scripts目录下）；（2）输入pip install jupyter；（推荐在外网环境下安装）2.库安装下载下面文件{cloud title="requirements.txt" type="lz" url="https://wwek.lanzoub.com/iLI2S0xm6a3a" password=""/}然后执行：pip3 install -r requirements.txt启动Juypter Notebook（1）命令行窗口输入jupyter notebook；浏览器会打开Jupyter Notebook窗口，说明Jupyter Notebook安装成功。(2) 配置Jupyter Notebook1、行窗口输入jupyter notebook --generate-config，会发现C:\Users\用户名\ .jupyter下多出了一个配置文件jupyter_notebook_config.py；2、这个配置文件，找到下面这句#c.NotebookApp.notebook_dir = ''。可以把它修改成c.NotebookApp.notebook_dir = 'D:\jupyter-notebook'，当然具体的目录由自己创建的文件夹决定（需要自己创建）。配置文件修改完成后，以后在jupyter notebook中写的代码都会保存在该目录下。现在重新启动jupyter notebook，就进入了新的工作目录；5.添加代码自动补全功能（可选）（1）打开cmd，输入pip install jupyter_contrib_nbextensions，等待安装成功；（2）安装完之后需要配置nbextension（配置前要确保已关闭jupyter notebook），在cmd中输入jupyter contrib nbextension install --user --skip-running-check，等待配置成功；（3）在前两步成功的情况下，启动jupyter notebook，会发现在选项栏中多出了Nbextension的选项，点开该选项，并勾选Hinterland，即可添加代码自动补全功能。
机器学习代码实现:线性回归与岭回归
1.线性回归模型线性回归模型是最简单的一种线性模型，模型的形式就是：$y=W^T x+b$我们可以通过对原本的输入向量x扩增一个为1的维度将参数W和b统一成一个参数W，即模型变成了$y=W^T x$这里的W是原本两个参数合并之后的而其损失函数的形式是残差平方损失RSS$L=\frac{1}{2 m} \sum_{i=1}^m\left(W^T x_i-y_i\right)^2=\frac{1}{2 m}\left(W^T X-y\right)^T\left(W^T X-y\right)$我们很容易就可以通过求导得到线性回归模型的关于W的梯度$\nabla_W L=\frac{1}{m} \sum_{i=1}^m\left(W^T x_i-y_i\right) x_i=\frac{1}{m} X^T\left(W^T X-y\right)$这样一来我们就可以通过梯度下降的方式来训练参数W，可以用下面的公式表示$W:=W-\alpha \frac{1}{m} X^T\left(W^T X-y\right)$但实际上线性模型的参数W可以直接求解出，即：$W=\left(X^T X\right)^{-1} X^T y$2.线性回归的编程实现具体代码中的参数的形式可能和上面的公式推导略有区别，我们实现了一个LinearRegression的类，包含fit，predict和loss三个主要的方法，fit方法就是求解线性模型的过程，这里我们直接使用了正规方程来解class LinearRegression: def fit(self, X: np.ndarray, y: np.ndarray) -> float: N, D = X.shape # 将每个样本的特征增加一个维度，用1表示，使得bias和weight可以一起计算 # 这里在输入的样本矩阵X末尾增加一列来给每个样本的特征向量增加一个维度 # 现在X变成了N*(D+1)维的矩阵了 expand_X = np.column_stack((X, np.ones((N, 1)))) self.w = np.matmul(np.matmul(np.linalg.inv(np.matmul(expand_X.T, expand_X)), expand_X.T), y) return self.loss(X, y)predict实际上就是将输入的矩阵X放到模型中进行计算得到对应的结果，loss给出了损失函数的计算方式：def loss(self, X: np.ndarray, y: np.ndarray): """ 线性回归模型使用的是RSS损失函数 :param X:需要预测的特征矩阵X，维度是N*D :param y:标签label :return: """ delta = y - self.predict(X) total_loss = np.sum(delta ** 2) / X.shape[0] return total_loss3.岭回归Ridge Regression与代码实现岭回归实际上就是一种使用了正则项的线性回归模型，也就是在损失函数上加上了正则项来控制参数的规模，即：$L=\frac{1}{2 m} \sum_{i=1}^m\left(W^T x_i-y_i\right)^2+\lambda\|W\|_2=\frac{1}{2 m}\left(W^T X-y\right)^T\left(W^T X-y\right)+\lambda W^T W$因此最终的模型的正规方程就变成了：$W=\left(X^T X+\lambda I\right)^{-1} X^T y$这里的\lambda是待定的正则项参数，可以根据情况选定，岭回归模型训练的具体代码如下class RidgeRegression: def fit(self, X: np.ndarray, y: np.ndarray): N, D = X.shape I = np.identity(D + 1) I[D][D] = 0 expand_X = np.column_stack((X, np.ones((N, 1)))) self.W = np.matmul(np.matmul(np.linalg.inv(np.matmul(expand_X.T, expand_X) + self.reg * I), expand_X.T), y) return self.loss(X, y)4.数据集实验这里使用了随机生成的二位数据点来对线性模型进行测试，测试结果如下：线性模型测试结果岭回归也使用同样的代码进行测试。