CH2_感知机算法原理及其spark 实现

本文深入探讨感知机算法的基本原理,这是一种用于解决二分类线性分类问题的经典模型。感知机由Rosenblatt在1957年提出,尽管历史悠久,但其简单性和实用性使其成为深度学习和神经网络领域的基石。文章详细介绍了感知机模型的输入输出空间,以及如何通过符号函数进行分类。此外,还提供了感知机算法的实现步骤,包括初始化参数、样本选择、错误检测和权重更新过程。最后,通过Spark框架实现了感知机算法,展示了从数据读取、预处理到模型训练的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CH2_感知机算法原理及其spark 实现

一.感知机

感知机是二分类的线性分类问题,由Rosenblatt在1957年提出,虽然年代久远,但是感知机简单实用易实现,并且是深度学习神经网络的基础。感知机模型的输入是样本的特征向量,输出是样本的类别,分别为“+1”,“-1”,如果训练样本是线性可分的数据集,则感知机学习将会得到一个将训练数据集的两类样本点完全正确分开的超平面。非线性可分数据集则不能训练得到这个超平面,对于非线性可分的数据集做训练只能通过其他优化方法得出一个较好的模型。
线性可分数据集
非线性可分数据集

二.感知机模型

输 入 空 间 ( 特 征 空 间 ) : X ⊑ R n 输入空间(特征空间):X\sqsubseteq R^n ():XRn
输 出 空 间 : y = { − 1 , + 1 } 输出空间: y=\{-1,+1\} :y={1,+1}
由输入空间到输出空间的函数:f(x) = sing(w·x+b),其中w称为权值向量,b称为偏置,sign是符号函数:
s i g n ( x ) = { + 1 x ≥ 0 − 1 x < 0 sign(x)= \left\{ \begin{aligned} +1 & & x\ge 0 \\ -1& & x<0 \end{aligned} \right. sign(x)={+11x0x<0
感知机模型的几何解释:线性方程 w·x+b = 0 对应于特征空间R的一个超平面S,其中
w是超平面的法向量,b是超平面的截距。这个超平面正好将样本空间划分成两部分,将正负两类数据完全分开。
超平面

三.感知机算法实现步骤

感知机算法原始形式的实现步骤:
输入:训练数据集T=(x1,y1),(x2,y2),…,(xN,yN),yi∈{−1,+1},学习率η(0<η<1)
输出:w,b;感知机模型f(x)=sign(w⋅x+b)
(1)赋初值 w0,b0
(2)选取数据点(xi,yi)
(3)根据判定函数判断样本点是否误判:即yi(w·xi+b)<=0,如果误判则更新w和b:
w = w + η y i x i w=w+ηy_ix_i w=w+ηyixi
b = b + η y i b = b+ηy_i b=b+ηyi
(4)转到第二步,知道训练集中没有误判点。

感知机算法有两种形式 1. 原始形式算法,2.对偶形式,两种形式的实现步骤基本一样,只是对于权值向量(w)和偏置(b)的计算不同。本文只实现了算法的原始形式。更多信息请读者自行参考相关资料。

关于感知机算法的损失函数计算,对偶形式、以及相关证明等其他信息,读者可以参考李航老师的《统计学习方法》一书。

四.代码实现

package ML2_3.classification.pla


import breeze.linalg.{DenseVector => densevector}
import org.apache.spark.ml.feature.VectorAssembler
import org.apache.spark.ml.linalg.DenseVector
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.{Row, SparkSession}
import org.apache.spark.sql.functions._
import org.apache.spark.sql.types.IntegerType
import scala.util.Random

/**
  * Created by WZZC on 2019/3/4
  * 针对线性可分的数据集构建感知机模型
  **/
object pla {
  def main(args: Array[String]): Unit = {


    val spark = SparkSession.builder()
      .appName(s"${this.getClass.getSimpleName}")
      .master("local[*]")
      .getOrCreate()

    import spark.implicits._

    val sc = spark.sparkContext
    val data = spark.read.option("inferSchema", true)
      .option("header", true)
      .csv("F:\\DataSource\\pla.csv")

    data .show()
    
    val schema = data.schema

    val fts = schema.filterNot(_.name == "lable").map(_.name).toArray

    val amountVectorAssembler: VectorAssembler = new VectorAssembler()
      .setInputCols(fts)
      .setOutputCol("features")

    val vec2Array = udf((vec: DenseVector) => vec.toArray)

    val dataFeatrus = amountVectorAssembler
      .transform(data)
      .select($"lable", vec2Array($"features") as "features")

    var initW = densevector.rand[Double](fts.length) //创建一个初始化的随机向量作为初始权值向量
    var initb = Random.nextDouble()  // 初始偏置
    var flag = true
    val lrate = 0.2  //学习率

    // 定义判定函数
    val signudf = udf((t: Seq[Double], y: Double) => {
      val wx = initW.dot(  densevector(t.toArray)  )
      val d = wx + initb
      val ny = if (d >= 0) 1 else -1
      ny
    })

    var resDf = spark.createDataFrame(sc.emptyRDD[Row], dataFeatrus.schema.add("nG", IntegerType))

    while (flag) {
      val df = dataFeatrus.withColumn("sign", signudf($"features", $"lable"))
      val loss = df.where($"sign" =!= $"lable")
      val count = loss.count()
      println("error:" + count)
      if (count == 0) {
        resDf = df
        flag = false
      }
      else {
        // w1 = w0 + ny1x1
        //随机选择一个误判样本
        val rand = Random.nextInt(loss.count().toInt) + 1
        println(rand)
        val randy = loss
          .withColumn("r", row_number().over(Window.partitionBy($"lable").orderBy($"lable")))
          .where($"r" === rand)
          .head()

        val y = randy.getAs[Int]("lable")
        
       //更新w和b
        initW = initW + densevector(randy.getAs[Seq[Double]]("features").toArray).map(_ * y * lrate)
        // b1 = b0 + y
        initb = initb + y * lrate
      }
    }

    println(initW, initb)

    spark.stop()
  }
}

数据原始格式与训练结果对比

// 数据源格式
+---+---+-----+
| x1| x2|lable|
+---+---+-----+
|5.1|3.0|    1|
|5.7|4.1|    1|
|5.1|1.4|   -1|
|4.9|1.4|   -1|
// 训练结果展示
+-----+----------+----+
|lable|  features|sign|
+-----+----------+----+
|    1|[5.1, 3.0]|   1|
|    1|[5.7, 4.1]|   1|
|   -1|[5.1, 1.4]|  -1|
|   -1|[4.9, 1.4]|  -1|

// 模型系数
(DenseVector(-1.107892749276022, 2.043043387141929),-0.3484139882261944)

在本案例中,只针对线性可分的数据集进行训练,对于非线性可分的处理在下次分享中再给读者们介绍。由于作者水平有限,在介绍及实现过程中难免有纰漏之处,感谢细心的朋友指正。

参考资料:

《统计学习方法》 --李航

<think>好的,用户现在的问题是关于CH32V307单片机中TIM3_CH2_2的具体含义、定义、功能和作用。我需要先理解这个术语的结构。 首先,拆解这个术语:TIM3是定时器3,CH2是通道2,而这里的“_2”可能指的是复用功能的重映射或备用功能。在STM32系列中,定时器的通道可能有多个引脚可选,通过重映射寄存器来配置。例如,TIM3_CH2可能默认在某个引脚,但通过重映射可以换到另一个引脚,这时候可能用TIM3_CH2_1和TIM3_CH2_2来区分不同的映射选项。 不过用户提到的CH32V307是南京沁恒微电子的产品,可能与STM32有所不同,需要查阅相关资料。根据引用内容,用户之前的问题涉及STM32的定时器配置,比如TIM2的中断配置、TIM3的PWM驱动舵机,以及TIM1的输入捕获问题。虽然这些引用是关于STM32的,但用户现在询问的是CH32V307,需要确认两者的异同。 在STM32中,定时器通道的重映射通常由AFIO寄存器控制,比如部分重映射或完全重映射。例如,TIM3_CH2可能有主端口和重映射后的端口,分别对应不同的引脚。类似的,CH32V307可能也有类似的重映射机制,所以TIM3_CH2_2可能表示通道2的第二个可选引脚位置。 需要验证CH32V307的数据手册或参考手册,查看定时器通道的复用功能映射。例如,TIM3的通道2可能有多个可选的GPIO引脚,通过配置某个寄存器来选择使用哪一个。例如,默认情况下,TIM3_CH2映射到PA7,而重映射后可能映射到PB5或其他引脚,这时候TIM3_CH2_2可能对应PB5。 此外,用户提供的引用[2]中提到了PWM驱动舵机使用TIM3的通道2,可能涉及到复用功能的配置,需要禁用某些IO口或启用复用功能。这也支持了重映射的可能性。 总结:TIM3_CH2_2中的“_2”应该指该通道的第二个复用功能映射选项,用于将定时器通道连接到不同的GPIO引脚。功能上,作为定时器的通道2,可以用于输出比较(PWM)、输入捕获或脉冲计数等。作用是根据应用需求灵活选择引脚,方便PCB布局或外设连接。 需要确保回答中提到可能的重映射机制,并建议用户查阅具体的数据手册确认引脚分配,同时结合功能应用如PWM输出或输入捕获来解释其作用。</think>关于CH32V307单片机中$TIM3\_CH2\_2$的具体定义和功能,需从定时器通道复用功能和引脚重映射角度解析: #### 1. **定义解析** - **$TIM3$**:通用定时器模块,支持PWM输出、输入捕获、编码器接口等功能 - **$CH2$**:定时器通道2Channel 2),对应物理引脚或内部信号通路 - **$CH2\_2$**:表示该通道的**第二复用映射选项**,通过AFIO寄存器配置实现引脚重映射 #### 2. **功能实现** 在CH32V307中,$TIM3\_CH2\_2$主要涉及: ```c // 示例代码:配置TIM3_CH2_2复用功能 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; // 假设对应PB5 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出 GPIO_Init(GPIOB, &GPIO_InitStructure); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); // 部分重映射 ``` 此时$TIM3\_CH2$信号将映射到**备用引脚**(具体引脚需查数据手册)[^1][^2] #### 3. **典型作用** - **PWM生成**:驱动电机/舵机(占空比控制) - **输入捕获**:测量脉冲宽度或频率 - **比较输出**:生成精确时间事件 #### 4. **配置要点** | 配置项 | 说明 | |-----------------|-------------------------------| | 时钟使能 | 需开启TIM3和对应GPIO时钟 | | 重映射选择 | 通过AFIO寄存器选择映射方案 | | 工作模式 | 输出比较/PWM/输入捕获模式选择 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值