Este artículo lo lleva a comprender el algoritmo del vecino más cercano K - kNN

Uno: K - Descripción del algoritmo del vecino más cercano

        El método del k-vecino más cercano (k-NN) es un método básico de clasificación y regresión propuesto por Cover T y Hart P en 1967. K-Nearest Neighbors es probablemente el algoritmo más fácil de entender en el aprendizaje automático, cuando en realidad no aprende nada. Su principio de funcionamiento es: hay un conjunto de datos de muestra, también conocido como conjunto de muestra de entrenamiento, y cada dato en el conjunto de muestra tiene una etiqueta, es decir, conocemos la correspondencia entre cada dato en el conjunto de muestra y la categoría a la que pertenece. Pertenece. Después de ingresar nuevos datos sin etiquetas, cada característica de los nuevos datos se compara con las características correspondientes a los datos en el conjunto de muestra, y luego el algoritmo extrae la etiqueta de clasificación de los datos más similares (vecino más cercano) de la muestra. En general, solo seleccionamos los k datos más similares en el conjunto de datos de muestra, que es el origen de k en el algoritmo del vecino más cercano, generalmente k es un número entero no mayor que 20. Finalmente, se selecciona la clasificación con más ocurrencias entre los k datos más similares como la clasificación de los nuevos datos. Si hay muchas características, la hipótesis aprendida puede ajustarse muy bien al conjunto de entrenamiento (la función de costo puede ser casi 0), pero es posible que no se generalice a nuevos datos. Para resumir los pasos son:

  1. Calcule la distancia entre un punto en un conjunto de datos de clases conocidas y el punto actual;
  2. Ordenar en orden ascendente de distancia;
  3. Seleccione k puntos con la distancia más pequeña desde el punto actual;
  4. Determinar la frecuencia de ocurrencia de la categoría a la que pertenecen los primeros k puntos;
  5. Devuelve la categoría con la frecuencia más alta en los primeros k puntos como la categoría pronosticada para el punto actual.

        Los problemas existentes se muestran en la siguiente figura. Considere un problema simple de dos categorías, si elegimos k = 3, habrá dos muestras de categoría 2 y una muestra de categoría 1. De acuerdo con el método de votación simple, es decir, la minoría obedece el principio de la mayoría, el nuevo La muestra se considera de clase 2. Pero debemos tener en cuenta que aunque k = 3, se incluyen tres muestras, pero las distancias entre las tres muestras y nuestras nuevas muestras no son consistentes, y las muestras con distancias más cercanas serán más similares, por lo que también podemos Las muestras de distancia son dados diferentes pesos Por ejemplo, podemos tomar el recíproco de la distancia como el peso, de modo que cuanto más cercana sea la distancia, mayor será la contribución a nuestro juicio. En problemas prácticos, se pueden elegir diferentes K como hiperparámetros.

Dos: Ejemplo 1 - kNN simple - juicio de tipo de película

Descripción del escenario: La siguiente figura da el número de tomas de pelea y tomas de besos para 4 películas, y luego da una película (toma de pelea 10, toma de besos 101) para determinar qué tipo de película es.

import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
import scipy.optimize as opt
data01 = pd.read_csv('knn_data1.txt', names=['kiss','fight','type'])
data01
lovetype = data01[data01.type=='love']
actiontype = data01[data01.type=='action']
fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(lovetype['kiss'], lovetype['fight'], s=50, c='b', marker='o', label='love')
ax.scatter(actiontype['kiss'], actiontype['fight'], s=50, c='r', marker='x', label='action')
ax.legend()
ax.set_xlabel('kiss number')
ax.set_ylabel('fight number')

input = [101,10]
ax.scatter(input[0],input[1],s=50,c='g',marker='.', label='test')
plt.show()

 

def classify_1(input, data, K):    #[101,20]
    datax = data.iloc[:, :-1].as_matrix()   #取前两列数据
    dataSize = datax.shape[0]     # dataSize = 4
    ####计算欧式距离
    diff = np.tile(input,(dataSize,1)) - datax  #diff = array([[11, 7], [13,5], [94,-91], [92,-78]])
    sqdiff = diff ** 2  #sqdiff = array([[121,49], [169, 25],[8836, 8281], [8464, 6084]])
    squareDist = np.sum(sqdiff,axis = 1)###行向量分别相加,[  170,   194, 17117, 14548]
dist = squareDist ** 0.5  #[ 13.03840481,  13.92838828, 130.83195328, 120.61509027]

    ####对距离进行排序
    sortedDistIndex = np.argsort(dist)##argsort()根据元素的值从大到小对元素进行排序,返回下标,{0,1,3,2}

    ####计数
    classCount={}
    for i in range(K):
        voteLabel = data.type[sortedDistIndex[i]]
        ###对选取的K个样本所属的类别个数进行统计
        classCount[voteLabel] = classCount.get(voteLabel,0) + 1
        # classCount = {'action': 1, 'love': 2}

    #取出最大的数据
    maxCount = 0
    for key,value in classCount.items():
        if value > maxCount:
            maxCount = value
            classes = key
    return classes
test01 = [101,20]
test_class = classify_1(test01, data01, 3)
print(test_class)              #love

Tres: Ejemplo 2 - kNN complejo ( datos súper bidimensionales + normalización) - juicio de citas del sitio web

El ejemplo anterior es relativamente simple, por lo que se omite. Esta vez se dan los pasos más generales:

  1. Recopilar datos: puede utilizar rastreadores para recopilar datos, o puede utilizar datos gratuitos o de pago proporcionados por terceros. En términos generales, los datos se colocan en un archivo de texto txt y se almacenan en un formato determinado para facilitar el análisis y el procesamiento.
  2. Preparar datos: Analizar, preprocesar datos usando Python.
  3. Análisis de datos: los datos se pueden analizar de muchas maneras, como usar Matplotlib para visualizar datos.
  4. Algoritmo de prueba: Calcula la tasa de error.
  5. Uso del algoritmo: si la tasa de error se encuentra dentro de un rango aceptable, puede ejecutar el algoritmo del vecino más cercano para la clasificación.

Después de eso hay un ejemplo más complejo:

Descripción del escenario:

        La Sra. Helen ha estado usando sitios de citas en línea para encontrar una cita que se adapte a ella. Aunque los sitios de citas sugieren diferentes opciones, no le agradan todos. Después de algunos resúmenes, descubrió que las personas con las que ha estado se pueden dividir en tres categorías: no me gusta, me gusta un poco y me gusta mucho. Las dimensiones utilizadas incluyen:

        Millas anuales de viajero frecuente ganadas, porcentaje de tiempo dedicado a jugar videojuegos y litros de helado consumidos por semana. ( Siguiendo el código de arriba )

#(1)input
fr = open('ex3data2.txt','r')
arrayOLines = fr.readlines()  #读取文件所有内容            
numberOfLines = len(arrayOLines)   #得到文件行数
returnMat = np.zeros((numberOfLines,3))	#返回的NumPy矩阵,解析完成的数据:numberOfLines行,3列
classLabelVector = []	#返回的分类标签向量
index = 0 	#行的索引值

for line in arrayOLines:
    line = line.strip()	#s.strip(rm),当rm空时,默认删除空白符(包括'\n','\r','\t',' ')
    listFromLine = line.split('\t')	#使用s.split(str="",num=string,cout(str))将字符串根据'\t'分隔符进行切片。        
    returnMat[index,:] = listFromLine[0:3]	#将数据前三列提取出来,存放到returnMat矩阵中,也就是特征矩阵
    #根据文本中标记的喜欢的程度进行分类,1代表不喜欢,2代表魅力一般,3代表极具魅力  
    if listFromLine[-1] == 'didntLike':
        classLabelVector.append(1)
    elif listFromLine[-1] == 'smallDoses':
        classLabelVector.append(2)
    elif listFromLine[-1] == 'largeDoses':
        classLabelVector.append(3)
    index += 1
#(2)picture
fig, axs = plt.subplots(nrows=2, ncols=2,sharex=False, sharey=False, figsize=(13,8))
numberOfLabels = len(classLabelVector)
LabelsColors = []
for i in classLabelVector:
	if i == 1: 
		LabelsColors.append('black') #didntLike
	if i == 2:
		LabelsColors.append('orange') #smallDoses
	if i == 3:
		LabelsColors.append('red') #largeDoses

#画出散点图,以datingDataMat矩阵的第一(飞行常客例程)、第二列(玩游戏)数据画散点数据,散点大小为15,透明度为0.5
axs[0][0].scatter(x=returnMat[:,0], y=returnMat[:,1], color=LabelsColors,s=15, alpha=.5)
#设置标题,x轴label,y轴label
axs0_xlabel_text = axs[0][0].set_xlabel(u'fly distance')
axs0_ylabel_text = axs[0][0].set_ylabel(u'game time')

#画出散点图,以datingDataMat矩阵的第一(飞行常客例程)、第三列(冰激凌)数据画散点数据,散点大小为15,透明度为0.5
axs[0][1].scatter(x=returnMat[:,0], y=returnMat[:,2], color=LabelsColors,s=15, alpha=.5)
#设置标题,x轴label,y轴label
axs1_xlabel_text = axs[0][1].set_xlabel(u'fly distance')
axs1_ylabel_text = axs[0][1].set_ylabel(u'icecream mount')

#画出散点图,以datingDataMat矩阵的第二(玩游戏)、第三列(冰激凌)数据画散点数据,散点大小为15,透明度为0.5
axs[1][0].scatter(x=returnMat[:,1], y=returnMat[:,2], color=LabelsColors,s=15, alpha=.5)
#设置标题,x轴label,y轴label
axs2_xlabel_text = axs[1][0].set_xlabel(u'game time')
axs2_ylabel_text = axs[1][0].set_ylabel(u'icecream mount')

plt.show()

 

#(3)构架kNN
def classify_2(inX, dataSet, labels, k):
	#numpy函数shape[0]返回dataSet的行数
	dataSetSize = dataSet.shape[0]
	#在列向量方向上重复inX共1次(横向),行向量方向上重复inX共dataSetSize次(纵向)
	diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
	#二维特征相减后平方
	sqDiffMat = diffMat**2
	#sum()所有元素相加,sum(0)列相加,sum(1)行相加
	sqDistances = sqDiffMat.sum(axis=1)
	#开方,计算出距离
	distances = sqDistances**0.5
	#返回distances中元素从小到大排序后的索引值
	sortedDistIndices = distances.argsort()
	#定一个记录类别次数的字典
	classCount = {}
	for i in range(k):
		#取出前k个元素的类别
		voteIlabel = labels[sortedDistIndices[i]]
		#dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。
		#计算类别次数
		classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
	#python3中用items()替换python2中的iteritems()
	#key=operator.itemgetter(1)根据字典的值进行排序
	#key=operator.itemgetter(0)根据字典的键进行排序
	#reverse降序排序字典
	sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
	print(sortedClassCount)
	#返回次数最多的类别,即所要分类的类别
	return sortedClassCount[0][0]

Después de eso, los datos se normalizan, si de acuerdo con la fórmula anterior:

Debajo de la raíz cuadrada ((0-67)²+(20000-32000)²+(1.1-0.1)²)

        Es fácil encontrar que el atributo con la mayor diferencia numérica en la ecuación anterior tiene la mayor influencia en el resultado del cálculo, es decir, la influencia de las millas anuales de viajero frecuente en el resultado del cálculo será mucho mayor que el otro. dos características en la Tabla 2.1 - Play Video La proporción de tiempo dedicado al juego y el impacto de la cantidad de kilogramos de helado consumidos por semana. Y la única razón de este fenómeno es simplemente que las millas de viajero frecuente son mucho más grandes que otros valores característicos. Pero Helen cree que las tres características son igualmente importantes, por lo que, como una de las tres características con igual peso, las millas de viajero frecuente no deberían afectar tanto el cálculo.

       Cuando se trata de valores propios con diferentes rangos de valores, el método que generalmente usamos es normalizar los valores, como procesar el rango de valores para que esté entre 0 y 1 o -1 a 1. La siguiente fórmula puede convertir valores propios de cualquier rango de valores en valores en el intervalo de 0 a 1:

newValue = (oldValue - min) / (max - min)

#归一化
def autoNorm(dataSet):
	#获得数据的最小值
	minVals = dataSet.min(0)
	maxVals = dataSet.max(0)
	#最大值和最小值的范围
	ranges = maxVals - minVals
	#shape(dataSet)返回dataSet的矩阵行列数
	normDataSet = np.zeros(np.shape(dataSet))
	#返回dataSet的行数
	m = dataSet.shape[0]
	#原始值减去最小值
	normDataSet = dataSet - np.tile(minVals, (m, 1))
	#除以最大和最小值的差,得到归一化数据
	normDataSet = normDataSet / np.tile(ranges, (m, 1))
	#返回归一化数据结果,数据范围,最小值
	return normDataSet, ranges, minVals
#测试准确率
def datingClassTest():
	#取所有数据的百分之十
	hoRatio = 0.10
	#数据归一化,返回归一化后的矩阵,数据范围,数据最小值
	normMat, ranges, minVals = autoNorm(returnMat)
	#获得normMat的行数
	m = normMat.shape[0]
	#百分之十的测试数据的个数
	numTestVecs = int(m * hoRatio)
	#分类错误计数
	errorCount = 0.0

	for i in range(numTestVecs):
		#前numTestVecs个数据作为测试集,后m-numTestVecs个数据作为训练集
		classifierResult = classify_2(normMat[i,:], normMat[numTestVecs:m,:], classLabelVector[numTestVecs:m], 5)
		if classifierResult != classLabelVector[i]:
			errorCount += 1.0
	print("compute result:%s\t real result:%d" % (numTestVecs-errorCount, numTestVecs))
#test
resultList = ['tired of','a little like','very like']
#三维特征用户输入
precentTats = 15
ffMiles = 100
iceCream = 1
#训练集归一化
normMat, ranges, minVals = autoNorm(returnMat)
#生成NumPy数组,测试集
inArr = np.array([ffMiles, precentTats, iceCream])
#测试集归一化
norminArr = (inArr - minVals) / ranges
#返回分类结果
classifierResult = classify_2(norminArr, normMat, classLabelVector, 3)
#打印结果
print("You may %s this man." % (resultList[classifierResult-1]))
print(datingClassTest())

datos de entrada 15,100,1

Obtuve el siguiente resultado:

Como puede verse

 Los resultados son buenos, usé K = 5 en la precisión de la prueba, si K = 4, ¡entonces la precisión se puede mejorar al 97%! 

Resumen 1: Ventajas de KNN (alta precisión, insensibilidad a los valores atípicos, sin suposición de entrada de datos)/desventajas (complejidad computacional y espacial).

Resumen 2: El rango de datos de KNN (numérico y nominal), K es generalmente un número entero no mayor a 20 .

Los datos de este artículo provienen del aula, si hay alguna similitud, ¡haga un cambio!

Supongo que te gusta

Origin blog.csdn.net/yyfloveqcw/article/details/123964223
Recomendado
Clasificación