El aprendizaje automático debe saber y saber: análisis detallado e implementación del algoritmo KNN

¡Acostúmbrate a escribir juntos! Este es el segundo día de mi participación en el "Nuggets Daily New Plan · April Update Challenge", haz clic para ver los detalles del evento .

1. Diseño de algoritmos experimentales

  1. Leer el conjunto de datos de sandía
  2. Seleccione aleatoriamente k muestras como el centro del grupo inicial
  3. Calcule la distancia entre cada muestra y cada centro de conglomerado, y asigne cada muestra al centro de conglomerado más cercano a él. En este momento, todas las muestras se han dividido en k grupos
  4. Actualice los centros de conglomerados, usando la media de las muestras en cada grupo como el nuevo centro de conglomerados para el grupo
  5. Repita los pasos segundo y tercero hasta que el centro del clúster se estabilice o se alcance el número máximo de iteraciones.

análisis de experimentos

Clasificación usando KNN en el conjunto de datos de sandía

Un simple análisis del conjunto de datos de sandía arroja los siguientes resultados:

rasgo Características de los datos Papel
número de serie discreto número de serie
densidad continuo rasgo
Contenido de azúcar continuo rasgo
buen melon discreto Etiqueta

Por lo tanto, se seleccionaron la densidad de características y el contenido de azúcar para el análisis de conglomerados.

En segundo lugar, el código central del análisis de conglomerados de K-means

  1. Importar bibliotecas requeridas

    import matplotlib.pyplot as plt
    import numpy as np
    import pandas as pd
    复制代码

    En este experimento, elegí pandas 1 como la herramienta principal para leer conjuntos de datos, numpy 2 para acelerar las principales operaciones matemáticas y matplotlib 3 para el análisis de visualización de datos.

  2. Definición de K-Means Agrupación de KMeans

    1. Definir __init__()para inicializar el clasificador.

      class KNNClassifier:
          def __init__(self, x: pd.DataFrame):
              self.x = x
          ...
      复制代码

      donde Xrepresenta las características del conjunto de datos.

    2. Funciones de distancia predefinidasdistanceAll()

      def distanceAll(center, rest):
          distances = np.apply_along_axis(_distances, 1, rest, center)
          return distances.sum()
      
      def _distances(point: np.ndarray, centers: np.ndarray):
          distances = np.apply_along_axis(_distance, 1, centers, point)
          return distances
      
      def _distance(x, y):
          return np.sqrt(np.dot(x, x) - 2 * np.dot(x, y) + np.dot(y, y))
      复制代码

      Aquí he realizado varias optimizaciones, los puntos de optimización específicos son los siguientes:

      Evita for-loopcorrer más rápido

      En la primera función , la suma distanceAllentrante es una matriz multidimensional. Aquí, se implementa la función de distancia entre la suma y entre sí , y no se utiliza ningún bucle, lo que mejora en gran medida la velocidad de ejecución.centerrestcenterrestfor

      Reutilizar los resultados de los _distance(x, y) cálculos

      La fórmula general de cálculo de la distancia euclidiana es:

      D = ( k = 1 metro x k i x k j 2 ) d = \left( \sum_{k=1}^m \left | x_{ki} - x_{kj} \right |^2 \right)

      但是我在此处使用的公式为其展开形式

      d = k = 1 m ( x k i 2 2 × x k i × x k j + x k j 2 ) d = \sum_{k=1}^m\left( \red{ x_{ki}^2} - 2\times x_{ki}\times x_{kj}+ \red {x_{kj}^2} \right)

      此公式中红色部分在计算欧氏距离时会多次使用,因此,使用此公式可以充分利用numpy的缓存机制,减少不必要的重复运算量。

    3. 预定义 allocate() 核心方法为每个点找到最近的聚类中心

      def allocateAll(center, rest):
          # 2. 计算每个样本到各个聚类中心之间的距离,将每个样本分配给距离它最近的聚类中心
          allocates = np.apply_along_axis(_allocate, 1, rest, center)
          # sns.scatterplot(data=rest, x=0, y=1, hue=allocates)
          copied = rest.copy()
          copied["allocations"] = allocates
          groups = copied.groupby("allocations").groups
          # 绘图
          fig = plt.figure()
          ax = rest.plot.scatter(x=0, y=1, c=allocates, colormap='viridis', legend=True)
          center.iloc[list(groups.keys())].plot.scatter(x=0,
                                                        y=1,
                                                        c=list(groups.keys()),
                                                        marker="x",
                                                        colormap='viridis',
                                                        s=200,
                                                        ax=ax)
          plt.show()
          return groups
      
      def _allocate(point: np.ndarray, centers: np.ndarray):
          distances = np.apply_along_axis(_distance, 1, centers, point, "euclidean")
          nearest_center = np.argmin(distances)
          return nearest_center
      复制代码

      同时,在对每个点寻找中心进行聚类的过程中,还集合了绘图可视化方法。此处的可视化方法将绘制出之后聚类的过程。

    4. 定义 train() 在训练集上进行迭代训练

      class KMeans:
          ...
          def train(self, k):
              print(f" === k = {k} === ")
              batch = self.x.shape[0]
              features = self.x.shape[1]
              # 1. 随机选取 k 个样本作为初始的聚类中心
              index = np.random.randint(0, batch, size=k)
              centers: pd.DataFrame = self.x.iloc[index]  # 聚类中心
              # rest: pd.DataFrame = self.x.loc[~self.x.index.isin(index)]
              allocations = allocateAll(centers, self.x)
              for i in range(10):
                  last_centers = centers
                  centers = np.empty((k, 2))
                  for label, points in allocations.items():
                      center = self.x.iloc[points]
                      new_center = np.average(center, axis=0)
                      centers[label] = new_center
                  if np.isclose(last_centers, centers).all():
                      print(f"k = {k} 收敛,停止!")
                      return distanceAll(pd.DataFrame(centers), self.x)
                  allocations = allocateAll(pd.DataFrame(centers), self.x)
      复制代码

      在本段代码中,我指定每次训练最多进行10轮,一般来说,只需要迭代5次即可收敛到聚类中心。

      代码分为两部分,第一次的聚类中心在样本中随机选取,进行第一次聚类之后,再依据上一次的聚类结果,选择每一类的均值点作为中心进行循环迭代,当下一轮迭代的循环中心与上一轮相差不大时,终止迭代,返回此时的wss距离值。

三、实验数据及结果分析

在西瓜数据集上使用K均值聚类

  1. 导入所需库

    import matplotlib.pyplot as plt
    import pandas as pd
    
    from model import KMeans
    复制代码

    此处导入刚刚编写的KMeans以及绘图工具matplotlib进行wss曲线的绘制。

  2. 读取数据集并构建模型

    df = pd.read_csv("kmeansdata.csv")
    model = KMeans(df[["m", "h"]])
    复制代码

    此处读入西瓜数据集,并选定特征mh构建模型。

  3. KMeans 模型训练,可视化,WSS曲线分析

    wss = []
    for i in range(2, 10):
        wss.append(model.train(k=i))
    plt.plot(range(2, 10), wss)
    plt.savefig("result.png")
    复制代码

    此处我在2到15中选择k值,分别使用这些k值在KMeans模型上进行训练,并保存每一次训练之后返回的wss距离,最后对wss距离进行可视化分析。

    训练过程可视化 k=3

    首先,在数据集中随机选取三个样本作为聚类中心:

    imagen.png

    可以看出,选择的聚类中心偏下,然后进行第一次迭代:

    imagen.png

    在每一类中,选择其中心点作为下一次聚类中心,然后对每个点重新决定其类别,并进行下一次迭代:

    imagen.png

    可以看出,此时中心往中间偏移,分类更加合理。再进行一次迭代:

    imagen.png

    imagen.png

    此后迭代中心不再产生明显变化,代表聚类中心收敛,本轮聚类结束。

    WSS曲线可视化

    imagen.png

四、总结及心得体会

  1. 在简单的数据集(如西瓜数据集)上,聚类效果较好,在几次迭代内便可达到收敛。
  2. 根据对不同k值的可视化分析,可以发现,在k=3时达到"肘部",此时K为最优值,大于3的k值会因为类别过多而失去统计意义,k值太小会导致类别过少,使类内距离急剧上升。
  3. 使用C接口实现Python程序比使用Python-based-coding效率更高。
  4. 掌握了一些简单的数据可视化方法,学会使用一些简单的matplotlib库中有关pyplot的函数,利用简单的数据可视化方法将大量的数据转化成图片,极大地简化了我们对结果数据的分析和比对,能够更轻易的获得一些结果上的规律和结论。

5. Sugerencias para la mejora del proceso experimental, métodos y medios

  1. Al visualizar el conjunto de datos, la selección aproximada de las dos primeras dimensiones para características de alta dimensión para el análisis visual perderá la información de características de otras dimensiones. Aquí, puede elegir un método de reducción de dimensionalidad, como PCA 4 , para proyectar la alta características dimensionales en un plano bidimensional Realice un análisis visual.
  2. Se pueden probar conjuntos de datos más complejos.
  3. Puede intentar considerar más funciones de distancia.

Referencias

Supongo que te gusta

Origin juejin.im/post/7083118254645837861
Recomendado
Clasificación