Princípio do algoritmo KMeans de aprendizado de máquina e implementação do Spark

Um desenvolvedor de dados que não entende o algoritmo não é um bom engenheiro de algoritmos. Ainda me lembro de alguns dos algoritmos de mineração de dados mencionados pelo meu instrutor quando era estudante de graduação. Fiquei bastante interessado, mas tive menos contato depois do trabalho. A cadeia de desprezo do engenheiro de dados, modelo> tempo real> Armazém de dados offline> Engenheiro de ETL> Engenheiro de BI (se você não gostar), o trabalho que faço agora é principalmente armazém de dados offline. Claro, fiz algum trabalho de ETL no estágio inicial. Para o desenvolvimento a longo prazo da profissão, eu amplio meus limites técnicos. É necessário aprofundar gradualmente o tempo real e o modelo, portanto, a partir deste artigo, também listarei um FLAG para aprender o real -tempo e parte do modelo em profundidade.

Para mudar a si mesmo, comece melhorando aquilo em que você não é bom.

1. KMeans-Introdução ao Algoritmo

O algoritmo K-Means é um algoritmo de agrupamento não supervisionado. É relativamente simples de implementar e tem um bom efeito de agrupamento, por isso é amplamente utilizado.

  • O algoritmo K-means, também conhecido como K-means ou K-means, é geralmente usado como o primeiro algoritmo para dominar algoritmos de agrupamento.

  • Aqui, K é uma constante e precisa ser definido com antecedência. Em termos gerais, o algoritmo agrega M amostras que não são rotuladas em K clusters de maneira iterativa.

  • O processo de coleta de amostras geralmente é dividido pela distância entre as amostras como um índice.

Arquivo Núcleo : o algoritmo de agrupamento K-means é um algoritmo de análise de agrupamento de solução iterativa. Suas etapas são selecionar aleatoriamente K objetos como centros de agrupamento iniciais e, em seguida, calcular a distância entre cada objeto e cada centro de agrupamento de semente. Atribua cada objeto ao centro de agrupamento mais próximo para isso. Os centros do cluster e os objetos atribuídos a eles representam um cluster. Cada vez que uma amostra é alocada, o centro do cluster do cluster será recalculado com base nos objetos existentes no cluster. Este processo será repetido até que uma determinada condição de encerramento seja atendida. A condição de término pode ser que nenhum (ou número mínimo) de objetos sejam reatribuídos a diferentes clusters, nenhum (ou número mínimo) de centros de cluster mudem novamente e a soma dos erros quadrados seja localmente mínimo

2. Fluxo do algoritmo KMeans

2.1 Leia o arquivo, prepare os dados e pré-processe os dados

2.2 Encontre aleatoriamente K pontos como o ponto central inicial

2.3 Percorra o conjunto de dados, calcule a distância de cada ponto até os 3 centros, cujo ponto central é o mais próximo desse ponto central

2.4 Calcule o novo ponto central com base na nova classificação

2.5 Use o novo ponto central para iniciar o próximo ciclo (continue na etapa 2.3 do ciclo)

Condições para sair do loop :

1. Especifique o número de ciclos

2. Todos os pontos centrais quase não se movem mais (ou seja, a soma das distâncias movidas pelo ponto central é menor que o nosso Changshu dado, como 0,00001)

3. Vantagens e desvantagens do algoritmo KMeans

A escolha do valor K : A influência do valor k no resultado final é muito importante, mas deve ser dada com antecedência. Dado um valor adequado de k, é necessário conhecimento prévio, o que é difícil de estimar do nada, ou pode levar a resultados insatisfatórios.

A existência de pontos anormais : O algoritmo K-means usa o valor médio de todos os pontos como o novo ponto de massa (ponto central) no processo iterativo. Se houver pontos anormais no cluster, o desvio médio será mais sério. Por exemplo, há cinco dados de 2, 4, 6, 8 e 100 em um cluster, então o novo ponto de massa é 24. Obviamente, este ponto de massa está mais longe da maioria dos pontos; na situação atual, usando a mediana 6 pode ser melhor do que usar A ideia da média é melhor, e o método de agrupamento usando a mediana é chamado agrupamento de K-Mediods (agrupamento de K mediana)

Sensibilidade do valor inicial : o algoritmo de K-means é sensível ao valor inicial. A escolha de valores iniciais diferentes pode resultar em regras de divisão de cluster diferentes. Para evitar a anormalidade do resultado final causada por essa sensibilidade, vários conjuntos de nós iniciais podem ser inicializados para construir diferentes regras de classificação e, em seguida, a regra de construção ideal pode ser selecionada. Em resposta a isso, ele é derivado do seguinte: algoritmo K-Means de duas divisões, algoritmo K-Means ++, algoritmo K-Means ||, algoritmo Canopy, etc.

As vantagens da implementação simples, mobilidade e boa escalabilidade o tornam um dos algoritmos mais comumente usados ​​em cluster.

4. Implementação do algoritmo KMeans do Spark

4.1 Download de dados e descrição

Link: https://pan.baidu.com/s/1FmFxSrPIynO3udernLU0yQ Código de extração: inferno Depois de copiar este conteúdo, abra o aplicativo Baidu Netdisk para celular, a operação é mais conveniente

Conjunto de dados de flores de íris, o conjunto de dados contém 3 tipos de 150 dados principais, cada tipo contém 50 dados, cada registro contém 4 recursos: comprimento do cálice, largura do cálice, comprimento da pétala, largura da pétala

Após esses 4 recursos, agrupe as flores, assuma que K está definido como 3 e veja a diferença com o resultado real

4.2 Implementação

Não usa a biblioteca mlb, mas usa a implementação nativa de scala

package com.hoult.work

import org.apache.commons.lang3.math.NumberUtils
import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.SparkSession

import scala.collection.mutable.ListBuffer
import scala.math.{pow, sqrt}
import scala.util.Random

object KmeansDemo {

  def main(args: Array[String]): Unit = {

    val spark = SparkSession
      .builder()
      .master("local[*]")
      .appName(this.getClass.getCanonicalName)
      .getOrCreate()

    val sc = spark.sparkContext
    val dataset = spark.read.textFile("data/lris.csv")
      .rdd.map(_.split(",").filter(NumberUtils.isNumber _).map(_.toDouble))
      .filter(!_.isEmpty).map(_.toSeq)


    val res: RDD[(Seq[Double], Int)] = train(dataset, 3)

    res.sample(false, 0.1, 1234L)
      .map(tp => (tp._1.mkString(","), tp._2))
      .foreach(println)
  }

  // 定义一个方法 传入的参数是 数据集、K、最大迭代次数、代价函数变化阈值
  // 其中 最大迭代次数和代价函数变化阈值是设定了默认值,可以根据需要做相应更改
  def train(data: RDD[Seq[Double]], k: Int, maxIter: Int = 40, tol: Double = 1e-4) = {

    val sc: SparkContext = data.sparkContext

    var i = 0 // 迭代次数
    var cost = 0D //初始的代价函数
    var convergence = false   //判断收敛,即代价函数变化小于阈值tol

    // step1 :随机选取 k个初始聚类中心
    var initk: Array[(Seq[Double], Int)] = data.takeSample(false, k, Random.nextLong()).zip(Range(0, k))

    var res: RDD[(Seq[Double], Int)] = null

    while (i < maxIter && !convergence) {

      val bcCenters = sc.broadcast(initk)

      val centers: Array[(Seq[Double], Int)] = bcCenters.value

      val clustered: RDD[(Int, (Double, Seq[Double], Int))] = data.mapPartitions(points => {

        val listBuffer = new ListBuffer[(Int, (Double, Seq[Double], Int))]()

        // 计算每个样本点到各个聚类中心的距离
        points.foreach { point =>

          // 计算聚类id以及最小距离平方和、样本点、1
          val cost: (Int, (Double, Seq[Double], Int)) = centers.map(ct => {

            ct._2 -> (getDistance(ct._1.toArray, point.toArray), point, 1)

          }).minBy(_._2._1)  // 将该样本归属到最近的聚类中心
          listBuffer.append(cost)
        }

        listBuffer.toIterator
      })
      //
      val mpartition: Array[(Int, (Double, Seq[Double]))] = clustered
        .reduceByKey((a, b) => {
          val cost = a._1 + b._1   //代价函数
          val count = a._3 + b._3   // 每个类的样本数累加
          val newCenters = a._2.zip(b._2).map(tp => tp._1 + tp._2)    // 新的聚类中心点集
          (cost, newCenters, count)
        })
        .map {
          case (clusterId, (costs, point, count)) =>
            clusterId -> (costs, point.map(_ / count))   // 新的聚类中心
        }
        .collect()
      val newCost = mpartition.map(_._2._1).sum   // 代价函数
      convergence =  math.abs(newCost - cost) <= tol    // 判断收敛,即代价函数变化是否小于小于阈值tol
      // 变换新的代价函数
      cost = newCost
      // 变换初始聚类中心
      initk = mpartition.map(tp => (tp._2._2, tp._1))
      // 聚类结果 返回样本点以及所属类的id
      res = clustered.map(tp=>(tp._2._2,tp._1))
      i += 1
    }
    // 返回聚类结果
    res
  }

  def getDistance(x:Array[Double],y:Array[Double]):Double={
    sqrt(x.zip(y).map(z=>pow(z._1-z._2,2)).sum)
  }


}

Código completo: https://github.com/hulichao/bigdata-spark/blob/master/src/main/scala/com/hoult/work/KmeansDemo.scala

Captura de tela do resultado: ArquivoWu Xie, Xiao San Ye, um pequeno novato em segundo plano, big data e inteligência artificial. Por favor, preste atenção a maisArquivo

Acho que você gosta

Origin blog.csdn.net/hu_lichao/article/details/113101229
Recomendado
Clasificación