- 2024-11-01
-
加入了学习《led立方体》,观看 led立方体
-
加入了学习《【2024 DigiKey创意大赛】- 基于毫米波雷达的生命体征检测及健康监护系统》,观看 【2024 DigiKey创意大赛】- 基于毫米波雷达的生命体征检测及健康监护系统-作品提交
- 2024-10-28
-
发表了主题帖:
【2024 DigiKey创意大赛】车载环境监控系统
本帖最后由 lospring 于 2024-10-29 22:28 编辑
车载环境监控系统
作者:lospring
一、作品简介
本作品主要实现对车内环境进行监控,自动触发设定好的规则动作,提高车内用户的体验。如温度过高时自动开启空调,加速时提醒乘客寄好安全带,车内空气质量实时监控提醒等。作品主要使用Teensy 4.1做为主控板,结合BME680和ICM20948进行环境数据采集,并将结果显示在LCD上,使用电机模拟开关空调操作。
二、系统框图
设计思路、系统软硬件介绍及实现框图,以图文结合的方式展示本作品主要由ICM20948和BME680进行环境数据采集,采集数据包括温度,湿度,大气压强,气体质量,海拔,加速度及陀螺仪状态。系统框图如下:
先对传感器进行初始化,判断传感器的状态是否正常。
通过ICM20948采集温度,湿度,压强,环境气体质量及海拔,BME680采集温度,加速度及陀螺仪状态数据,传至主控模块Teensy 4.1进行判断转换。
将相关数据显示在3.5寸480*320的LCD上
结合采集的数据进行电机控制,如在32°以上打开电机,32°以下关闭电机;加速度大于5m/s2时文字提醒乘客系上安全带;陀螺仪严重偏移时发出警报提醒司机检查车辆状态等。
三、各部分功能说明
本作品开发主要使用Arduino IDE开发,使用官方代码进行做为基础进行修改使用,包括Teensy,BME680和ICM20948 library。
初始化部分:通过接口函数对BME680和ICM20948进行初始化,判断传感器模块是否正常,并在LCD上现实相关信息。
传感器数据采集显示:通过接口函数读取传感器数据,并显示在LCD上。
对两个传感器的温度进行平均,模拟车内两个不同位置的温度情况,当温度大于32°时打开电机降温;当温度小于32°时停止电机。
四、作品源码
源码地址 https://download.eeworld.com.cn/detail/lospring/634609
源码说明:
1、使用Arduino 2.3.2编译下载,其它版本不保障可以正常使用
2、library版本为Adafruit ICM20X 2.0.7;Adafruit BME680 2.0.5;Teensy 1.59.0
五、作品功能演示视频
六、项目总结
本次作品主要是一套车载环境监控系统,可以自动调节车内环境的舒适度,并且可以对一些危险进行判断和提醒,以保证车内人员的安全。本次作品打板出现意外,没有考虑到LCD已反向焊接,设计有待改进,另外作品的完成度也不是非常高,对一些危险情况并没有很好的完成。希望后期有时间再进行持续性的修改。再次感谢得捷电子和EEWORLD。
- 2024-10-27
-
加入了学习《【2024 DigiKey 创意大赛】AI全功能环境监测站作品功能演示视频》,观看 【2024 DigiKey 创意大赛】AI全功能环境监测站作品功能演示视频
-
加入了学习《 【2024 DigiKey创意大赛】 《智能起居室环境控制台》任务报告汇总》,观看 【2024 DigiKey创意大赛】 《智能起居室环境控制台》任务报告汇总
- 2024-10-24
-
上传了资料:
Teensy41_sensor车载环境监控系统
- 2024-10-12
-
发表了主题帖:
《智能驾驶之激光雷达算法详解》深度学习基础
1、本章先是介绍了一些神经网络的基础,神经元模型、感知机和多层感知机及正向传导和误差反向传播机制。
神经元模型主要介绍了M-P神经元模型。通过对神经元各输入值和权重值相乘后的累加值与神经元阈值进行比较,可以得到神经元的输出值。主要使用的是sing函数。由于sign会使得神经元的输出具有不连续性,为了使神经元模型能够更好地处理非线性问题,并使得其输出光滑连续,一些学者将sing函数替换成了sigmoid、ReLU函数、反正切函数等非线性函数。
感知机是基于M-P模型提出搭建的,感知机由两层组成-输入层和输出层。感知机中的层概念便于搭建复杂的网络结构。由此提出了多层感知机,包含输入层、隐藏层和输出层。多层感知机的隐藏层中,同一层的神经元节点共享同一个激活函数和偏置,并且在处理二分类问题时,神经网络的输出层通常使用sigmoid函数作为激活函数;而在处理多分类问题时,神经网络的输出层通常使用softmax函数作为激活函数。
通常将使用误差反向传播机制的前馈神经网络称为BP神经网络。是一种全连接网络,在处理高维、非线性问题时,BP神经网络所需的层数较多,这就使得模型参数量较大,训练困难且模型精度难以提升。在处理图像问题时,通常需要将二维张量或三维张量表示的图片拉伸成一维向量,送入全连接网络。这种处理方式忽略了图像的局部不变性,破坏了像素间的联系。这些缺陷极大限制了其应用和推广。
2、之后介绍了卷积神经网络基础。
为了模拟大脑神经元的感觉野机制,并且降低模型的参数量,将图像领域的滤波操作引入神经网络。该滤波器又称为卷积核。这种将卷积操作和神经网络相结合的方式模拟了大脑视觉中的感觉野机制,同时也可以大大降低模型的参数量,这正是卷积神经网络名称的由来。原理包含局部连接、权值共享和局部不变性。
其结构通常由输入层、卷积层、池化层、全连接层和输出层组成。
卷积层主要使用卷积操作对输入数据进行线性权重求和,为了提升神经网络的非线性,还需要在执行卷积操作后引入sigmoid函数或ReLU函数等激活函数以进行非线性化。一个卷积核只能抽取图片的一种特征,因此我们在CNN中需要引入多个卷积核,以得到多通道的特征图,进而抽取原始图片中的轮廓,色彩,纹理等不同类型的特征。
池化层的作用是对卷积层获取的特征图进行降采样,进一步缩减特征图的尺寸。该过程不需要引入额外的权值参数,通常是通过对一定范围内的像素值取最大值,平均值等来达到最大池化,平均池化的目的。池化操作在一定程度上可以使神经网络对输入数据的位置变化具有更强的鲁棒性。
全连接层用于将卷积、池化后得到的矩阵特征图映射到样本的标空间中,实现最终的逻辑判断
- 2024-10-09
-
发表了主题帖:
《智能驾驶之激光雷达算法详解》基于3D激光点云的地面分割
本章是激光雷达感知算法部分,3D激光点云的地面分割算法大致划分为以下5种
1、基于高程地图的地面点云分割
这类算法通常将3D激光点云投影至XOY平面内,而后将该平面划分为多个网格,并在每个网格中计算点云子集的平均高度、最大-最小高度、最大相对高度差、高度方差等指标。最后在对上述指标与设定阈值进行比较,分析哪些网格中包含非地面点,哪些网格中为地面点云。这类算法对算力需求较小,不依赖激光雷达的扫描模式,并且可以处理一定坡度的地面场景,但当一些网格中包含悬空物体时,会将整个网格识别为非地面。
2、基于相邻几何关系的地面点云分割
传统机械激光雷达凭借其固定的扫描模式,根据实际测量点云中相邻激光点之间的距离、角度差是否超出设定阈值,可以进行地面点和非地面点的识别。为进一步节省计算开销,可结合激光雷达的扫描参数,将3D激光点云转换为2.5D的深度图,通过在深度图中分析相邻元素间的几何关系,得到对应的地面点集和非地面点集。
3、基于地面模型拟合的地面点云分割
由于地平面往往不是一个水平面,所以将点云划分为多个区域,在每个区域中使用RANSAC算法进行地面拟合并去除离群点。将点云空间细分为多个扇区后,尝试将各扇区内的3D激光点云投影到其中心平面上,得到2D点集,进而在2D点集中提取最低点、均值等关键点并进行2D直线拟合,然后结合直线的斜率以及点到直线的距离进行地面点集和非地面点集的划分。为了更有效地处理起伏地形,将点云划分为多个区域后,分别采用2D和1D高斯过程回归模型对地面进行拟合,并采用增量采样一致性算法去除离群点,最终得到地面的近似模型和地面点集。
4、基于机器学习模型的地面点云分割
将3D激光点云划分为多个栅格区域,并分别利用栅格的梯度和均值高度等信息构建马尔科夫随机场,然后利用信念传播算法将周围环境分为多个类别。Zhang提出GS-LBF算法,该算法通过将一种基于损失函数的地面估计模型引入多标签马尔科夫随机场,并使用循环信念传播算法以求解每个栅格中地面的最大置信度,实现了对3D激光点云的地面分割。
5、基于深度学习网络的地面点云分割
基于CNN直接对3D激光点云进行语义分割,获取每帧所有激光点的类别标签。
- 2024-09-24
-
发表了主题帖:
《智能驾驶之激光雷达算法详解》车体的外参标定
本章主要介绍激光雷达的动态外参标定。可以分为以下三个方向:
1、基于道路、标定物的外参标定
这类方法通常假设地面绝对水平,然后对地面进行拟合,并反过来推算雷达相对车体、地面的垂向高度、俯仰角和翻滚角。对于偏航角的标定,可以参考垂直杆状物或者通过其照射到地面的波形进行推算。
车载3D激光雷达分步自动标定(SSAC)算法在总体上分为两个阶段:1、通过地面点云进行平面拟合,得到水平地面在激光雷达坐标系下的方程,并构造水平度函数,然后利用粒子群优化算法都求得水平度最小时对应激光雷达相对车体的俯仰角pitch,横滚角roll和高度△Z;2、在车辆直线行驶过程中分析同一个标定标对应的点云特征,具体则是在每一帧中通过聚类获取标定杆的中心点,而后将我们从多帧中获取的标定杆中心点连成直线,由该直线与车辆前进方向的夹角计算求解出激光雷达相对 车体的偏航角yaw。
2、基于手眼模型的外参标定
主要使用Navy算法,该算法是由Frank C.Park和Bryan J.Martin在1994年提出的,其针对我们在机器人传感器标定中遇到的AX=XB等式求解问题,引入了李群、李代数理论,并结合非线性最小二乘法,推导出了手眼模型的封装解。英伟达DriveWorks的LSC标定方法主要标定激光雷达相对车体的翻滚角roll、俯仰角pitch、偏航角yaw及高度差height。与SSAC算法类似,LSC通过进行地面拟合实现了激光雷达外参中roll、pitch和height的标定。具体来说LSC主要通过对拟合地平面的法向量进行分析来求解这三个参数。LSC采用手眼模型基于激光雷达和车体的运动变化量进行yaw和pitch的标定。随着车辆行驶,LSC将得到多组标定结果,可根据所标定数据的分布,计算出标定最优值。
3、基于累积点云特征优化的外参标定
基本思想如下:当外参标定正确时,世界坐标系下一个静止物体的表面,在累积点云地图中应表现出清晰的边界特征;而当外参没有得到正确标定时,在点云地图中,一个物体的表面会出现模糊、错层。主要使用AESC-MMS算法,该算法用于激光雷达与位姿估计传感器的外参标定。其中位姿传感器可以是IMU\GPS也可以是运行SLAM算法的摄像头等.AESC-MMS算法基本流程如下:首先结合雷达的初始外参和获取的多帧激光点云、位姿信息,构建世界坐标系下的累积点云地图;然后根据当前栅格尺寸参数,对累积点云用体素栅格滤波进行降采样,以减少点云地图中点的个数,从而降低后续优化过程的计算开销;在降采样后的点云地图中,对每个激光点及其最近的K个领域点进行几何特征分析,并利用损失函数量化点云地图的质量,通过对损失函数进行最小化得到外参的估计值。此外,AESC-MMS算法会在最外层循环中,递归地减小体素栅格的尺寸,从而在一定程度上避免内层优化陷入买咗部极值,提升最终外参标定的精度。
- 2024-09-08
-
发表了主题帖:
《智能驾驶之激光雷达算法详解》空间变换数学基础
本帖最后由 lospring 于 2024-9-8 23:50 编辑
下面介绍一些坐标系欧氏变换的基础数学。
在进行激光雷达的外参标定时,我们实际上是求解激光雷达坐标系相对车体坐标系的姿态和相对位置;在进行车辆定位时,我们通常是在求解车体坐标系的相对变化量及其相对世界参考坐标系的变化量。这两个坐标系之间的位置和姿态变化构成了欧氏变换。其中,姿态描述的是一个坐标系的轴系相对另一个坐标系轴系的旋转或方向关系,相对位置描述的是一个坐标系的原点相对另一个坐标系原点的平移关系。
1.1、旋转和平移变换
首先考虑两坐标系间具有共同原点且仅有相对旋转的情况。则点P的坐标系1和坐标系2下的三维向量可分别表示为
=
=
结合假设两坐标系的单位正交基分别为;。以及坐标系2在坐标系1下单位正交基的投影关系最终可得
=
其中旋转矩阵=
我们可以进一步分析得到旋转矩阵是行列式为1的正交阵,因此旋转矩阵R与其转置矩阵的相乘结果为单位阵E,所以,所以
再进一步可以看出物体相对于坐标轴的旋转和坐标轴相对于物体的等角度反向旋转在描述上是等效的。
齐次矩阵则是考虑了两坐标系间同时具有旋转和平移的情况。最终可得。
1.2、旋转的欧拉角表示
在使用旋转矩阵R表示三维空间中的旋转和姿态时,共需要9个变量来表示,而对应的旋转本身则通常只有3个自由度。这种表达明显带来参数冗余,使得求解复杂。所以采用欧拉角姿态表示。
根据旋转轴顺序的不同,欧拉角有多种形式。以RPY为例,绕X轴旋转角称为翻滚角R,绕Y轴旋转角称为俯仰角P,绕Z轴旋转角称为Y。欧拉角描述的空间旋转与其转动顺序强相关,且欧拉角的三个分量不具有互换性,在求解RPY的逆变换时,不能仅对角度取负实现,而应该按照相反的旋转顺序反转相应的角度。当俯仰角pitch=时,欧拉角描述的旋转存在奇异性,此时滚动角和领航角无法区分,其描述的旋转会出现退化现象,称为欧拉角的万向锁现象。所以在优化和滤波等迭代算法中通常不使用欧拉角表示较大的旋转变换。
1.3、旋转的轴角表示/旋转向量表示
旋转向量到旋转矩阵的转换关系如下:
1.4、旋转的单位四元数表示
四元数是将二维空间中的复数扩展至三维空间中得到的超复数:
i,j,k为虚数单位,分别对应坐标系的三个轴,并满足
采用矢量形式表示为
2.1 李群、李代数
为简化位姿估计相关的求解过程,引入李群和李代数。
群通常表示为由有限或无限个元素构成的集合加上一种运算的代数结构。具有群结构的光滑微分流形为李群,即若G为一个群,同时它又是D维空间的一个流形,并且其群乘积和取逆操作都是平滑函数,则G为一个李群。李代数是一个由集合V,数域F和一个李括号运算组成的代数结构,用于表示被赋予李括号运算的线性空间。在三维空间中,向量的叉乘运算即为该空间的李括号运算,所以李代数实际上是李群在其幺元处的切空间,它能够完全捕获李群的局部结构,并且李群M,李代数m可表示为。之后可通过李群和李代数的映射关系将流形空间中待求解的问题表示成对应的线性空间的李代数结构,从而使得利用线性空间中的模型和算法成为可能。
-
发表了主题帖:
《智能驾驶之激光雷达算法详解》初识激光雷达
1、激光雷达基本原理
激光雷达是光探测和测距的简称,常见的车载机械式激光雷达大致原理为:激光雷达以激光作为信号源,由激光器发射出脉冲激光,打到地面、树木、车辆、道路、桥梁和建筑物等被测物体表面上,随后激光发生散射,一部分光波会被反射到激光雷达的接收器上,再根据激光测距原理,即可得到从激光雷达到目标点的距离信息,进而通过激光器不断地水平旋转,便可得到周围目标物上的全部激光点数据;再能此数据进行成像处理,便可得到周围环境的三维立体点云。
2、激光雷达的分类
激光雷达可以按光源、发射端、接收端、扫描系统进行分类
机械扫描式技术成熟,但成本较高,供货周期长,体积大,运动部件较多以及难以符合车规要求等,尚无法满足量产车辆的内嵌安装需求,目前多作为robottaxi的激光雷达解决方案或用于数据采集车辆中。MEMS振镜式、透射棱镜式和转镜式是目前影响力较大的半固态激光雷达,已逐步应用于量产车型中。FLASH和OPA为纯固态激光雷达,业界认为是未来车载激光雷达的技术趋势,但由于现阶段探测范围、技术成熟度和成本等问题,应用较少。
3、车载激光雷达的应用功能
激光雷达多应用于智能车的感知系统和定位系统中
下图为感知系统流程示例
在记忆泊车、自动辅助导航四驾驶等L2+的智能驾驶中,除了需要进行目标、道路环境的感知检测外,还需要实时确定车辆的行驶位置。由于成本原因,车载GPS能够提供的定位精度通常为米级,信号频率为1Hz,在地下停车场,高架桥下等场景还会由于遮挡丢失GPS信号。为了弥补GPS的上述缺点,通过实时激光雷达点云和图像与事先建立的高精度地图进行帧-图匹配,可得到车辆的绝对位置估计再将其与GPS信号结合,在一定程度上可以降低GPS信号丢失对绝对位置计算的影响。
- 2024-09-02
-
回复了主题帖:
《智能驾驶之激光雷达算法详解》概述
秦天qintian0303 发表于 2024-9-1 18:27
彩色书籍还是比较良心的,看着赏心悦目
书籍质量挺不错的
- 2024-09-01
-
发表了主题帖:
《智能驾驶之激光雷达算法详解》概述
本书是系统性介绍车载激光雷达关键算法的技术书,全书共14章,涉及激光雷达和智能驾驶的基础知识,以及激光雷达在智能驾驶中的标定、感知和定位方法,基本涵盖了当前车载激光雷达的应用场景。需要的数学知识比较多,包括了大量的公式推导。
智能驾驶是辅助驾驶和自动驾驶的统称,通常指汽车通过搭载先进的传感器、控制器等设备,结合人工智能算法以辅助驾驶员对车辆的操控甚至实现无人驾驶。随着智能驾驶的兴起,大量的技术研究,加快了智能驾驶技术的商业化落地。目前智能驾驶的感知技术主要分为以相机和激光雷达为主导的多传感器融合方案两个方向。目前激光雷达的硬件成本正快速下降,致使车企明显加速了激光雷达的应用进度。所以激光雷达的算法工程师需求量也在逐步增大。
第1章介绍了激光雷达的基本硬件原理、发展历程及特点、功能和商业化应用现状。第2章介绍了坐标系欧氏变的基础和李群、李代数基础。第3、4章介绍了有代表性的激光雷达与车体的外参标定算法以及激光雷达和相机间的外参标定,涵盖了基于标定物、无标定物、离线标定和在线标定等多种标定模式。5-6,8-10章为感知算法,介绍了基于激光雷达进行地面检测、障碍物聚类、目标检测、多目标跟踪、路沿检测的代表性算法。11-13章介绍了几种定位算法,14章对车载激光雷达的未来发展进行了介绍。
本书可以作为高校教材,也可代智能驾驶或机器人领域的技术爱好者以及激光雷达标定、感知、定位算法工程师使用。
- 2024-08-16
-
回复了主题帖:
充电十分钟,续航1000公里!油车时代彻底结束了?
还是担心安全问题,这么大的功率电池是不是就更容易损伤?
- 2024-08-15
-
发表了主题帖:
【2024 DigiKey 创意大赛】物料开箱
感谢DigiKey提供的这次机会,活动很棒。
本次项目主要用到RT1062 Teensy 4.1 i.MX ARM® Cortex®-M7 MPU 评估板;AK09916,ICM-20948加速计,陀螺仪,磁力计,传感器Qwiic,STEMMA QT平台评估扩展板;BME680气体,湿度,压力,温度传感器Qwiic,STEMMA QT平台评估扩展板。发货速度非常快。
- 2024-08-13
-
回复了主题帖:
共读入围名单: 《智能驾驶之激光雷达算法详解》
个人信息无误,确认可以完成评测计划
- 2024-08-11
-
发表了主题帖:
《机器学习算法与实现 —— Python编程与应用实例》深度学习AlexNet
AlexNet的网络架构如下图所示:
可见Alexnet模型由5个卷积层和3个池化Pooling和3个全连接层构成,共有6×1076×10^7个参数和65000个神经元,最终的输出层是1000通道的Softmax。AlexNet使⽤了更多的卷积层和更⼤的参数空间来拟合⼤规模数据集 ImageNet,它是浅层神经⽹络和深度神经⽹络的分界线。
以下使用PyTorch实现AlexNet
import torch.nn as nn
import torch
from torch.autograd import Variable
class AlexNet(nn.Module):
def __init__(self, num_classes=1000, init_weights=False):
super(AlexNet, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 96, kernel_size=11, stride=4, padding=2),
nn.ReLU(inplace=True), #inplace可以载入更大的模型
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(96, 256, kernel_size=5, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(256, 384, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(384, 384, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(384, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
)
self.classifier = nn.Sequential(
nn.Dropout(p=0.5),
nn.Linear(256*6*6, 4096), #全连接
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Linear(4096, num_classes),
)
if init_weights:
self._initialize_weights()
def forward(self, x):
x = self.features(x)
x = torch.flatten(x, start_dim=1) #展平或者view()
x = self.classifier(x)
return x
由于AlexNet较复杂,使用简单的数据集无法体现其优势,因此本次试验需要用到CIFAR10数据集,CIFAR10 这个数据集一共有 50000 张训练集,10000 张测试集,两个数据集里面的图片都是 png 彩色图片,图片大小是 32 x 32 x 3,一共是 10 分类问题,分别为飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船和卡车。这个数据集是对网络性能测试一个非常重要的指标,可以说如果一个网络在这个数据集上超过另外一个网络,那么这个网络性能上一定要比另外一个网络好,目前这个数据集最好的结果是 95% 左右的测试集准确率。CIFAR10 已经被 PyTorch 内置了,使用非常方便,只需要调用torchvision.datasets.CIFAR10就可以了。
以下是训练AlexNet网络模型
from torchvision.datasets import CIFAR10
from torch.utils.data import DataLoader
from torchvision import transforms as tfs
from utils import train
import matplotlib.pyplot as plt
%matplotlib inline
# 数据转换
def data_tf(x):
im_aug = tfs.Compose([
tfs.Resize(227),
tfs.ToTensor(),
tfs.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
])
x = im_aug(x)
return x
train_set = CIFAR10('../../data', train=True, transform=data_tf)
train_data = torch.utils.data.DataLoader(train_set, batch_size=64, shuffle=True)
test_set = CIFAR10('../../data', train=False, transform=data_tf)
test_data = torch.utils.data.DataLoader(test_set, batch_size=128, shuffle=False)
net = AlexNet(num_classes=10)
optimizer = torch.optim.Adam(net.parameters(), lr=1e-3)
criterion = nn.CrossEntropyLoss()
res = train(net, train_data, test_data, 20, optimizer, criterion, use_cuda=True)
plt.plot(res[0], label='train')
plt.plot(res[2], label='valid')
plt.xlabel('epoch')
plt.ylabel('Loss')
plt.legend(loc='best')
plt.show()
plt.plot(res[1], label='train')
plt.plot(res[3], label='valid')
plt.xlabel('epoch')
plt.ylabel('Acc')
plt.legend(loc='best')
plt.show()
AlexNet网络模型需要输入的图像尺寸是227*227,所以需要使用tfs.Resize(227)将输入的图像重新采样为227*227大小,然后再转换成Tensor,最后对数据进行归一化处理。试验进行了20次迭代,损失和准确率变化如下,随着训练的进行,损失一直下降,准确率一直上升。
- 2024-08-04
-
发表了主题帖:
《机器学习算法与实现 —— Python编程与应用实例》神经网络的训练 - 反向传播算法
在多层神经网络中有这样一个问题:最后一层的参数可以用这样的方式求解得到;隐层节点没有输出的真值,因此无法直接构建损失函数来求解。
反向传播算法可以解决该问题,反射传播自满其实就是链式求导法则的应用。
按照机器学习的通用求解思路,我们先确定神经网络的目标函数,然后用随机梯度下降优化算法去求目标函数最小值时的参数值。
取网络所有输出层节点的误差平方和作为目标函数:
其中,Ed表示是样本d的误差, t是样本的标签值,y是神经网络的输出值。
然后,使用随机梯度下降算法对目标函数进行优化:
随机梯度下降算法也就是需要求出误差Ed对于每个权重wji的偏导数(也就是梯度),如何求解?
观察上图,可发现权重wji仅能通过影响节点j的输入值影响网络的其它部分,设netj是节点j的加权输入,即
Ed是netj的函数,而netj是wji的函数。根据链式求导法则,可以得到:
上式中,xji是节点传递给节点j的输入值,也就是节点i的输出值。
对于的∂Ed/∂netj推导,需要区分输出层和隐藏层两种情况。
1、输出层权值训练
对于输出层来说,netj仅能通过节点j的输出值yj来影响网络其它部分,也就是说Ed是yj的函数,而yj是netj的函数,其中yj=sigmod(netj)。所以我们可以再次使用链式求导法则:
其中:
将第一项和第二项带入,得到:
如果令δj=−∂Ed/∂netj,也就是一个节点的误差项δ是网络误差对这个节点输入的偏导数的相反数。带入上式,得到:
将上述推导带入随机梯度下降公式,得到:
2、隐藏层权值训练
现在我们要推导出隐藏层的∂Ed/∂netj∂:
首先,我们需要定义节点j的所有直接下游节点的集合Downstream(j)。例如,对于节点4来说,它的直接下游节点是节点8、节点9。可以看到netj只能通过影响Downstream(j)再影响Ed。设netk是节点j的下游节点的输入,则Ed是netk的函数,而netk是netj的函数。因为netk有多个,我们应用全导数公式,可以做出如下推导:
因为δj=−∂Ed/∂netj,带入上式得到:
至此,我们已经推导出了反向传播算法。需要注意的是,我们刚刚推导出的训练规则是根据激活函数是sigmoid函数、平方和误差、全连接网络、随机梯度下降优化算法。如果激活函数不同、误差计算方式不同、网络连接结构不同、优化算法不同,则具体的训练规则也会不一样。但是无论怎样,训练规则的推导方式都是一样的,应用链式求导法则进行推导即可。
3、具体解释
然后,按照下面的方法计算出每个节点的误差项δi:
对于输出层节点i
其中,δi是节点i的误差项,yi是节点i的输出值,ti是样本对应于节点i的目标值。举个例子,根据上图,对于输出层节点8来说,它的输出值是y1,而样本的目标值是t1,带入上面的公式得到节点8的误差项应该是:
对于隐藏层节点
其中,ai是节点i的输出值,wki是节点i到它的下一层节点k的连接的权重,δk是节点i的下一层节点k的误差项。例如,对于隐藏层节点4来说,计算方法如下:
最后,更新每个连接上的权值:
其中,wji是节点i到节点j的权重,η是一个成为学习速率的常数,δj是节点j的误差项,xji是节点i传递给节点j的输入。例如,权重w84的更新方法如下:
类似的,权重w41的更新方法如下:
偏置项的输入值永远为1。例如,节点4的偏置项w4b应该按照下面的方法计算:
计算一个节点的误差项,需要先计算每个与其相连的下一层节点的误差项,这就要求误差项的计算顺序必须是从输出层开始,然后反向依次计算每个隐藏层的误差项,直到与输入层相连的那个隐藏层,这就是反向传播算法的名字的含义。当所有节点的误差项计算完毕后,就可以根据式5来更新所有的权重。
以上就是反向传播算法的一个求解过程,整个过程也是搬抄其它大佬的结果,希望对大家有点帮助。
-
发表了主题帖:
《机器学习算法与实现 —— Python编程与应用实例》评估聚性能的方法
本帖最后由 lospring 于 2024-8-4 19:23 编辑
k-Means是无监督学习的算法之一。k-means就是将n个数据点进行聚类分析,得到 k 个聚类,使得每个数据点到聚类中心的距离最小。
k-Means的相关过程很容易获得,整体的推导过程实在是没什么意义再复制出来,下面就看看评估聚性能的方法:
方法1 - ARI
如果被用来评估的数据本身带有正确的类别信息,则利用Adjusted Rand Index(ARI)对聚类结果进行评估,ARI与分类问题中计算准确性的方法类似,兼顾了类簇无法和分类标记一一对应的问题。
1、RI
为了方便理解ARI,先讨论一下RI,也就是rand index,是ARI的基础方法。
假如有两类,那么针对这两类的的RI评价指标为:
a,b,c,d分别代表的含义为:
a : 应该在一类,最后聚类到一类的数量,
b : 不应该在一类,最后聚类结果也没把他们聚类在一起的数量。
c和d那么就是应该在一起而被分开的和不应该在一起而被迫在一起的。毕竟强扭的瓜不甜,c和d固然是错误的。
所以从R的表达式中可以看出,a和b是对的,这样能够保证R在0到1之间,而且,聚类越准确,指标越接近于1.
这里有一个关键性的问题,就是什么叫数量?怎么去计算?准确的说,是配对的数量。比如说a是应该在一起而真的幸福的在一起了的数量,这显然就应该像人类一样按照小夫妻数量计算,但是我们的样本可不管一夫一妻制,任意选两个就是一个配对,所以,就是 n(n−1)/2这样来计算,也就是组合数,n个当中选两个的选法。同时我们看到,分母其实是所有配对的总和,所以,我们最后可以写成这样:
2、ARI
有了先前RI的感性理解之后,接下来解释一下ARI。
RI有一个缺点,就是惩罚力度不够,换句话说,大家普遍得分比较高,没什么区分度,普遍80分以上。这样的话,往往是评价区分性不是特别好,于是就诞生出了ARI,这个指标相对于RI就很有区分度了。
具体的公式是:
ARI取值范围为[-1,1],值越大越好,反映两种划分的重叠程度,使用该度量指标需要数据本身有类别标记。
关联表(contingency table)的定义:
表中:
公式中:
假设配对矩阵是这样的,共有 n(n−1)/2 个配对方法。在行方向计算出可能取到的配对数,在列方向计算可能取到的配对数,相乘以后,除以总的配对数,这就是a的期望了。
from sklearn.metrics import adjusted_rand_score
ari_train = adjusted_rand_score(y_train, kmeans.labels_)
print("ari_train = %f" % ari_train)
方法2 - 轮廓系数
如果被用来评估的数据没有所属类别,则使用轮廓系数(Silhouette Coefficient)来度量聚类结果的质量,评估聚类的效果。轮廓系数同时兼顾了聚类的凝聚度和分离度,取值范围是[-1,1],轮廓系数越大,表示聚类效果越好。
轮廓系数的具体计算步骤:
import numpy as np
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize']=(10,10)
plt.subplot(3,2,1)
x1=np.array([1,2,3,1,5,6,5,5,6,7,8,9,7,9]) #初始化原始数据
x2=np.array([1,3,2,2,8,6,7,6,7,1,2,1,1,3])
X=np.array(list(zip(x1,x2))).reshape(len(x1),2)
plt.xlim([0,10])
plt.ylim([0,10])
plt.title('Instances')
plt.scatter(x1,x2)
colors=['b','g','r','c','m','y','k','b']
markers=['o','s','D','v','^','p','*','+']
clusters=[2,3,4,5,8]
subplot_counter=1
sc_scores=[]
for t in clusters:
subplot_counter +=1
plt.subplot(3,2,subplot_counter)
kmeans_model=KMeans(n_clusters=t).fit(X) #KMeans建模
for i,l in enumerate(kmeans_model.labels_):
plt.plot(x1[i],x2[i],color=colors[l],marker=markers[l],ls='None')
plt.xlim([0,10])
plt.ylim([0,10])
sc_score=silhouette_score(X,kmeans_model.labels_,metric='euclidean') #计算轮廓系数
sc_scores.append(sc_score)
plt.title('k=%s,silhouette coefficient=%0.03f'%(t,sc_score))
plt.figure()
plt.plot(clusters,sc_scores,'*-') #绘制类簇数量与对应轮廓系数关系
plt.xlabel('Number of Clusters')
plt.ylabel('Silhouette Coefficient Score')
plt.savefig('fig-res-k-means_silhouette_coef.pdf')
plt.show()
- 2024-07-21
-
发表了主题帖:
《机器学习算法与实现 —— Python编程与应用实例》KNN算法
K最近邻(k-Nearest Neighbor,kNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。简单来说,kNN可以看成:有那么一堆你已经知道分类的数据,然后当一个新数据进入的时候,就开始跟训练数据里的每个点求距离,然后挑选这个训练数据最近的K个点,看看这几个点属于什么类型,然后用少数服从多数的原则,给新数据归类。kNN算法不仅可以用于分类,还可以用于回归。
跟着书中的实验学习一下
生成训练数据和测试数据
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
# 生成模拟数据
np.random.seed(314)
data_size1 = 1000
x1 = np.random.randn(data_size1, 2)*2 + np.array([4,4])
y1 = [0 for _ in range(data_size1)]
data_size2 = 1000
x2 = np.random.randn(data_size2, 2)*2 + np.array([10,10])
y2 = [1 for _ in range(data_size2)]
# 合并生成全部数据
x = np.concatenate((x1, x2), axis=0)
y = np.concatenate((y1, y2), axis=0)
data_size_all = data_size1 + data_size2
shuffled_index = np.random.permutation(data_size_all)
x = x[shuffled_index]
y = y[shuffled_index]
# 分割训练与测试数据
split_index = int(data_size_all*0.7)
x_train = x[:split_index]
y_train = y[:split_index]
x_test = x[split_index:]
y_test = y[split_index:]
# 绘制结果
for i in range(split_index):
if y_train[i] == 0:
plt.scatter(x_train[i,0],x_train[i,1], s=38, c = 'r', marker='.')
else:
plt.scatter(x_train[i,0],x_train[i,1], s=38, c = 'b', marker='^')
#plt.rcParams['figure.figsize']=(12.0, 8.0)
mpl.rcParams['font.family'] = 'SimHei'
plt.title("训练数据")
plt.savefig("fig-res-knn-traindata.pdf")
plt.show()
for i in range(data_size_all - split_index):
if y_test[i] == 0:
plt.scatter(x_test[i,0],x_test[i,1], s=38, c = 'r', marker='.')
else:
plt.scatter(x_test[i,0],x_test[i,1], s=38, c = 'b', marker='^')
#plt.rcParams['figure.figsize']=(12.0, 8.0)
mpl.rcParams['font.family'] = 'SimHei'
plt.title("测试数据")
plt.savefig("fig-res-knn-testdata.pdf")
plt.show()
KNN实现主要分为三步:计算距离,按距离取样本,投票决定类别。分别定义这三步函数为knn_distance(),knn_vote(),knn_predict()
import numpy as np
import operator
def knn_distance(v1, v2):
"""计算两个多维向量的距离"""
return np.sum(np.square(v1-v2))
def knn_vote(ys):
"""根据ys的类别,挑选类别最多一类作为输出"""
vote_dict = {}
for y in ys:
if y not in vote_dict.keys():
vote_dict[y] = 1
else:
vote_dict[y] += 1
method = 1
# 方法1 - 使用循环遍历找到类别最多的一类
if method == 1:
maxv = maxk = 0
for y in np.unique(ys):
if maxv < vote_dict[y]:
maxv = vote_dict[y]
maxk = y
return maxk
# 方法2 - 使用排序的方法
if method == 2:
sorted_vote_dict = sorted(vote_dict.items(), \
#key=operator.itemgetter(1), \
key=lambda x:x[1], \
reverse=True)
return sorted_vote_dict[0][0]
def knn_predict(x, train_x, train_y, k=3):
"""
针对给定的数据进行分类
参数
x - 输入的待分类样本
train_x - 训练数据的样本
train_y - 训练数据的标签
k - 最近邻的样本个数
"""
dist_arr = [knn_distance(x, train_x[j]) for j in range(len(train_x))]
sorted_index = np.argsort(dist_arr)
top_k_index = sorted_index[:k]
ys=train_y[top_k_index]
return knn_vote(ys)
# 对每个样本进行分类
y_train_est = [knn_predict(x_train[i], x_train, y_train, k=5) for i in range(len(x_train))]
print(y_train_est)
# 绘制结果
for i in range(len(y_train_est)):
if y_train_est[i] == 0:
plt.scatter(x_train[i,0],x_train[i,1], s=38, c = 'r', marker='.')
else:
plt.scatter(x_train[i,0],x_train[i,1], s=38, c = 'b', marker='^')
#plt.rcParams['figure.figsize']=(12.0, 8.0)
mpl.rcParams['font.family'] = 'SimHei'
plt.title("Train Results")
plt.savefig("fig-res-knn-train-res.pdf")
plt.show()
计算一下训练和测试精度
# 计算训练数据的精度
n_correct = 0
for i in range(len(x_train)):
if y_train_est[i] == y_train[i]:
n_correct += 1
accuracy = n_correct / len(x_train) * 100.0
print("Train Accuracy: %f%%" % accuracy)
# 计算测试数据的精度
y_test_est = [knn_predict(x_test[i], x_train, y_train, 3) for i in range(len(x_test))]
n_correct = 0
for i in range(len(x_test)):
if y_test_est[i] == y_test[i]:
n_correct += 1
accuracy = n_correct / len(x_test) * 100.0
print("Test Accuracy: %f%%" % accuracy)
print(n_correct, len(x_test))
Train Accuracy: 97.857143%
Train Accuracy: 96.666667%%
58 60
至此验证了KNN算法的可行性和可靠性。