CH2_(扩展)线性感知机算法(Pocket PLA)及其Spark实现

CH2_(扩展)线性感知机算法(Pocket PLA)及其Spark实现

前言

普通感知机算法存在的问题
上一篇文章中我们实现了普通的感知机算法,但是只能处理线性可分的是数据集,在训练非线性可分数据集时,结果会在一定范围产生震荡,就如下图所示:
震荡效果
图片来源:https://www.leiphone.com/news/201706/QFydbeV7FXQtRIOl.html

优化:口袋算法(Pocket)

要处理非线性可分的数据集,我们就需要对算法进行一定的优化,通常使用的优化算法就是口袋PLA(Pocket 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.SparkSession
import org.apache.spark.sql.functions._
import scala.collection.mutable.ListBuffer
import scala.util.Random

/**
  * Created by WZZC on 2019/3/15
  * 通用感知机模型
  **/
object pocketPla {
  def main(args: Array[String]): Unit = {

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

    import spark.implicits._

  //  数据加载
    val data = spark.read
      .option("inferSchema", true)
      .option("header", true)
      .csv("F:\\DataSource\\pocketPla.csv")

    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")
      .cache()

    var initW: densevector[Double] = densevector.rand[Double](fts.length) //创建一个初始化的随机向量
    var initb: Double = Random.nextDouble()
    var flag = true
    val lrate = 0.1 // 学习率
    var iteration = 0 //迭代次数

    var countError = dataFeatrus.count() //初始化错判个数(取样本大小)
    var resW = initW
    var resB = initb

    // 定义判别函数
    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
    })

    while (flag && iteration < 200) {

      val df = dataFeatrus.withColumn("sign", signudf($"features", $"lable"))
      val loss = df.where($"sign" =!= $"lable")
      val count = loss.count().toInt

     
       //  判断新模型的误判次数是否小于前一次的误判次数
       //  如果小于则更新权值向量和偏置,大于则不更新
    
      if (count < countError) {
        countError = count
        resW = initW
        resB = initb
      }

      println(s"迭代第${iteration}次 error:" + count)

      if (count == 0) {
        flag = false
      } else {
        // w1 = w0 + ny1x1
        //随机选择一个误判样本
        val rand = Random.nextInt(loss.count().toInt) + 1

        val randy = loss
          .withColumn("r", row_number().over(Window.orderBy($"lable")))
          .where($"r" === rand)
          .head()

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

      }
      iteration += 1

    }

    println(countError, resW, resB)

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

    val df = dataFeatrus.withColumn("sign", signudfres($"features", $"lable"))

    df.show(100)

    spark.stop()
  }
}

参考资料:

https://www.leiphone.com/news/201706/QFydbeV7FXQtRIOl.html
林轩田机器学习基石

<think>好的,用户想了解TIM5_CH2_Cap_Init函数的定义和作用,特别是在STM32定时器捕获初始化中的应用。首先,我需要回忆STM32的定时器输入捕获功能相关知识。输入捕获通常用于测量外部信号的频率、脉宽等,涉及定时器的配置,比如时基、捕获通道、中断等。 用户提供的引用[1]和[2]中有相关的代码示例,比如TIM5_Cap_Init和TIM2_Cap_Init。这些函数可能初始化定时器的ARR和PSC寄存器,设置捕获通道,配置中断等。但用户问的是TIM5_CH2_Cap_Init,这里的CH2可能指的是通道2,所以需要针对通道2进行配置。 接下来,我需要分析函数可能的定义。函数名中的CH2表明是针对定时器5的通道2。初始化函数通常会设置定时器的预分频器(PSC)和自动重装载值(ARR),配置输入捕获模式,选择边沿触发(比如上升沿或下降沿),开启中断等。此外,可能还需要配置GPIO引脚为输入模式,并映射到对应的定时器通道。 关于作用,TIM5_CH2_Cap_Init的作用应该是初始化定时器5的通道2为输入捕获模式,使其能够测量输入信号的脉宽或频率。例如,用于测量PWM信号的高电平时间,通过捕获上升沿和下降沿的时间差来计算。 需要检查用户提供的代码是否有类似的结构。引用[2]中的TIM2_Cap_Init可能是一个类似的函数,用于初始化TIM2的输入捕获,可能涉及NVIC配置、GPIO设置、定时器参数配置等。因此,TIM5_CH2_Cap_Init可能遵循相同的模式,但针对TIM5的通道2。 需要注意的是,不同的定时器通道对应的GPIO引脚不同,需要查阅STM32F103的数据手册或引脚分配表,确定TIM5_CH2对应的具体引脚,例如PA0或别的引脚。此外,配置输入捕获时,需要设置捕获/比较模式寄存器(CCMR),使能捕获,配置极性等。 可能还需要开启定时器的中断,在捕获事件发生时触发中断,记录捕获值。例如,在引用[2]中,使用了TIM2CH1_CAPTURE_STA和TIM2CH1_CAPTURE_VAL变量来存储捕获状态和值,类似地,TIM5_CH2可能需要类似的全局变量来跟踪状态。 最后,用户可能需要具体的代码示例,但根据问题,他们可能更关注函数的定义和作用,而不是完整的实现。因此,在回答中需要明确说明函数的参数(如arr和psc),以及各个配置步骤的作用,比如设置时基、捕获通道、中断等,并解释其在输入捕获中的应用场景。</think>### TIM5_CH2_Cap_Init 函数的定义与作用 #### 1. **函数定义** `TIM5_CH2_Cap_Init` 是STM32定时器输入捕获功能的初始化函数,用于配置定时器5的通道2为输入捕获模式。以下是其典型定义(基于STM32标准库): ```c void TIM5_CH2_Cap_Init(u16 arr, u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; GPIO_InitTypeDef GPIO_InitStructure; // 1. 使能时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 2. 配置GPIO(假设TIM5_CH2对应PA1) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 输入下拉模式 GPIO_Init(GPIOA, &GPIO_InitStructure); // 3. 配置定时器时基 TIM_TimeBaseStructure.TIM_Period = arr; // 自动重装载值 TIM_TimeBaseStructure.TIM_Prescaler = psc; // 预分频系数 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); // 4. 配置输入捕获通道2 TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿捕获 TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // 直接输入 TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 不分频 TIM_ICInitStructure.TIM_ICFilter = 0x00; // 不滤波 TIM_ICInit(TIM5, &TIM_ICInitStructure); // 5. 使能捕获中断 TIM_ITConfig(TIM5, TIM_IT_CC2, ENABLE); // 使能通道2捕获中断 // 6. 启动定时器 TIM_Cmd(TIM5, ENABLE); } ``` #### 2. **函数作用** `TIM5_CH2_Cap_Init` 的作用是初始化定时器5的通道2为输入捕获模式,具体功能包括: - **测量信号参数**:通过捕获外部信号边沿(如上升沿/下降沿),计算信号的频率、占空比或高电平持续时间[^1]。 - **硬件关联**:配置GPIO引脚(如PA1)为输入模式,并与定时器5的通道2绑定。 - **中断触发**:在信号边沿到来时触发中断,记录捕获值(`TIM5->CCR2`)用于后续计算。 #### 3. **关键参数说明** - **`arr`**:自动重装载值,决定定时器的计数周期,影响最大可测量的时间范围。 - **`psc`**:预分频系数,将定时器时钟分频以适配不同频率的信号测量需求。 例如,若系统时钟为72MHz,`psc=71`时,定时器实际频率为 $f = \frac{72\ \text{MHz}}{71+1} = 1\ \text{MHz}$,对应时间分辨率为 $1\ \mu\text{s}$[^2]。 --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值