Abréviations kNN Moyens k-Voisins les plus proches, que es un algoritmo de aprendizaje supervisado. Se puede utilizar para la clasificación, así como para problemas de regresión.
Comment fonctionne l'algorithme kNN?
kNN décide de la classe du nouveau point de données en fonction du nombre maximal de voisins du point de données appartenant à la même classe.
Si les voisins d'un nouveau point de données sont les suivants, NY: 7, NJ: 0, DANS: 4, alors la classe du nouveau point de données sera NEW YORK.
Disons que vous travaillez dans un bureau de poste et que votre travail consiste à organiser et distribuer les lettres entre les facteurs pour minimiser le nombre de déplacements dans les différents quartiers. Et puisque nous n'imaginons que des choses, nous pouvons supposer qu'il n'y a que sept quartiers différents. C'est une sorte de problème de classification. Vous devez diviser les lettres en classes, où les classes ici se réfèrent au Upper East Side, centre-ville de Manhattan, et ainsi de suite.
Si vous aimez perdre du temps et des ressources, vous pouvez donner une lettre de chaque quartier à chaque facteur, en espérant qu'ils se réunissent dans le même quartier et découvrent votre plan corrompu. C'est le pire type de distribution qui puisse être réalisé.
D'autre part, vous pouvez organiser les lettres en fonction des adresses les plus proches les unes des autres.
Tu pourrais commencer par "Si c'est à moins de trois pâtés de maisons, donnez-le au même facteur". Ce nombre de blocs les plus proches est d'où il vient K. Vous pouvez continuer à augmenter le nombre de blocs jusqu'à ce que vous atteigniez une disposition efficace. C'est la valeur la plus efficace de k pour votre problème de classification.
kNN en pratique - Code
Comme nous l'avons fait dans le dernier tutoriel, nous allons utiliser le module KNN de ml.js pour former notre classificateur kNearestNeighbors. Chaque problème d'apprentissage automatique a besoin de données et nous allons utiliser l'ensemble de données IRIS dans ce tutoriel.
L'ensemble de données d'iris se compose de 3 types différents de longueurs de pétales d'iris et de sépales (Setosa, Versicolor y Virginica), avec un champ indiquant son type respectif.
Installer les bibliothèques
Fil $ ajouter l'invite csvtojson ml-knn
Ou si vous préférez npm:
invite npm install ml-knn csvtojson
- ml-knn: k Voisins les plus proches
- csvtojson: Données d'analyse
- au Le bon moment: Para permitir que el Nom d'utilisateur solicite predicciones
Initialisez la bibliothèque et chargez les données
L'ensemble de données Iris est fourni par l'Université de Californie à Irvine et est disponible ici. Sin embargo, debido a la forma en que está organizado, tendrás que copiar el Contenu dans le le navigateur (Seleccionar todo, Copiar) y pegarlo en un archivo llamado iris.csv. Vous pouvez lui donner le nom que vous voulez, sauf que l'extension doit être .csv.
Maintenant, initialisez la bibliothèque et chargez les données.
const KNN = require ('ml-knn'); const csv = require ('csvtojson'); const prompt = require ('prompt'); laissez knn; const csvFilePath = 'iris.csv'; // Data const names = ['sepalLength', 'sepalWidth', 'petalLength', 'petalWidth', 'type']; // pour l'en-tête let seperationSize; // Pour séparer les données d'entraînement et de test, laissez data = [], X = [], y = []; laissez trainingSetX = [], trainingSetY = [], testSetX = [], testSetY = [];
Los nombres de los en-têtes se utilizan para la visualización y la comprensión. Se quitarán más adelante.
En outre, séparationTaille Il est utilisé pour diviser les données en ensembles de données d'entraînement et de test.
Nous avons importé le package csvto.json, et maintenant nous allons utiliser votre méthode fromFile pour charger les données. (Étant donné que nos données n'ont pas de ligne d'en-tête, nous fournissons nos propres noms d'en-tête.)
csv ({noheader: true, headers: names}) .fromFile (csvFilePath) .on ('json', (jsonObj) => {data.push (jsonObj); // Pousser chaque objet vers le tableau de données}) .on ( 'done', (error) => {seperationSize = 0.7 * data.length; data = shuffleArray (data); dressData ();});
Nous poussons chaque ligne vers la variable de dates, et lorsque le processus est terminé, nous ajustons le séparationTaille à 0.7 fois le nombre d'échantillons de notre ensemble de données. Notez que si la taille des échantillons d'apprentissage est trop petite, le classificateur peut ne pas fonctionner aussi bien qu'avec un ensemble plus grand.
Puisque notre ensemble de données est ordonné par rapport aux types (console.log pour confirmer), la fonction shuffleArray est utilisé pour mélanger l'ensemble de données pour permettre la division. (Si vous ne mélangez pas, vous pouvez vous retrouver avec un modèle qui fonctionne bien pour les deux premières classes, mais échoue avec la troisième.)
C'est ainsi que cela est défini.
/ ** * https://stackoverflow.com/a/12646864 * Randomize éléments de tableau en place. * En utilisant l'algorithme de mélange de Durstenfeld. * / function shuffleArray (array) {for (var i = array.length - 1; i> 0; i--) {var j = Math.floor (Math.random () * (i + 1)); var temp = tableau [i]; tableau [i] = tableau [j]; tableau [j] = temp; } return array; }
Dress Data (encore une fois)
Nos données sont organisées comme suit:
{sepalLength: '5.1', sepalWidth: '3.5', petalLength: '1.4', petalWidth: '0.2', tapez: 'Iris-setosa'}
Il y a deux choses que nous devons faire avec nos données avant de les livrer au classificateur kNN:
- Transformez les valeurs de chaîne en flottants. (parseFloat)
- Convertit le type en classes numérotées.
function dressData () {/ ** * Il existe trois types différents de fleurs d'iris que cet ensemble de données * classées: * * 1. Iris Setosa (Iris-setosa) * 2. Iris Versicolor (Iris-versicolor) * 3. Iris Virginica ( Iris-virginica) * * Changeons ces classes de chaînes en nombres. * De telle sorte qu'une valeur de type égale à * 0 signifierait setosa, * 1 signifierait versicolor et * 3 signifierait virginica * / let types = new Set (); // Pour rassembler des classes UNIQUE data.forEach ((row) => {types.add (row.type);}); typesArray = [... types]; // Pour enregistrer les différents types de classes. data.forEach ((row) => {let rowArray, typeNumber; rowArray = Object.keys (row) .map (key => parseFloat (row [key])). slice (0, 4); typeNumber = typesArray.indexOf (row.type); // Convertit le type (String) en type (Number) X.push (rowArray); y.push (typeNumber);}); trainingSetX = X.slice (0, seperationSize); trainingSetY = y.slice (0, seperationSize); testSetX = X.slice (seperationSize); testSetY = y.slice (seperationSize); former (); }
Si vous n'êtes pas familier avec Ensembles, ils sont comme leurs équivalents mathématiques en ce sens qu'ils ne peuvent pas avoir d'éléments en double et que leurs éléments n'ont pas d'index. (Contrairement aux tableaux.)
Et ils peuvent être facilement convertis en tableaux à l'aide de l'opérateur de propagation ou du constructeur Set.
Entraînez votre modèle puis testez-le
function train () {knn = new KNN (trainingSetX, trainingSetY, {k: 7}); test(); }
La méthode d'apprentissage prend deux arguments obligatoires, les données d'entrée, telles que la longueur du pétale, la largeur du pétale et sa classe réelle, comme le Iris-setosa, et ainsi de suite. Il prend également un paramètre d'options facultatif, qui n'est qu'un objet JS qui peut être passé pour ajuster les paramètres internes de l'algorithme. Nous passons la valeur de k Comme une option. La valeur par défaut de k c'est 5.
Ahora que nuestro modelo ha sido entrenado, veamos cómo funciona en el equipo de pruebas. Principalmente, estamos interesados en el número de errores de clasificación que se producen. (Es decir, el número de veces que predice que la entrada es algo, aunque en realidad être otra cosa.)
function test () {const result = knn.predict (testSetX); const testSetLength = testSetX.length; const predictionError = error (result, testSetY); console.log (`Taille de l'ensemble de tests = $ {testSetLength} et nombre d'erreurs de classification = $ {predictionError}`); prédire (); }
L'erreur est calculée comme suit. Nous utilisons la boucle humble for pour boucler sur l'ensemble de données et voir si la sortie prévue n'est pas égale à la sortie réelle. C'est une erreur de classification.
function error(predicted, expected) { let misclassifications = 0; for (var index = 0; index < predicted.length; index++) { if (predicted[index] !== expected[index]) { misclassifications++; } } return misclassifications; }
(Facultatif) Commencez à prédire
Il est temps d'avoir quelques pointeurs et prédictions.
function prédire () {let temp = []; prompt.start (); prompt.get (['Sepal Length', 'Sepal Width', 'Petal Length', 'Petal Width'], function (err, result) {if (! err) {for (var key in result) {temp.push (parseFloat (résultat [clé]));} console.log (`Avec $ {temp} - type = $ {knn.predict (temp)}`);}}); }
N'hésitez pas à sauter cette étape, si vous ne souhaitez pas tester le modèle dans un nouvel article.
Tout fini!
Si tú seguiste todos los pasos, así es como debería verse tu indice.js:
const KNN = require('ml-knn'); const csv = require('csvtojson'); const prompt = require('prompt'); let knn; const csvFilePath = 'iris.csv'; // Datos const names = ['sepalLength', 'sepalWidth', 'petalLength', 'petalWidth', 'type']; // para el header let seperationSize; // Para separar los datos de entrenamiento y de prueba let data = [], X = [], y = []; let trainingSetX = [], trainingSetY = [], testSetX = [], testSetY = []; csv({noheader: true, headers: names}) .fromFile(csvFilePath) .on('json', (jsonObj) => { data.push(jsonObj); // Empujar cada objeto a la matriz de datos }) .on('done', (error) => { seperationSize = 0.7 * data.length; data = shuffleArray(data); dressData(); }); function dressData() { let types = new Set(); // Para reunir clases ÚNICAS data.forEach((row) => { types.add(row.type); }); typesArray = [...types]; // Para grabar los diferentes tipos de clases. data.forEach((row) => { let rowArray, typeNumber; rowArray = Object.keys(row).map(key => parseFloat(row[key])).slice(0, 4); typeNumber = typesArray.indexOf(row.type); // Para grabar los diferentes tipos de clases. X.push(rowArray); y.push(typeNumber); }); trainingSetX = X.slice(0, seperationSize); trainingSetY = y.slice(0, seperationSize); testSetX = X.slice(seperationSize); testSetY = y.slice(seperationSize); train(); } function train() { knn = new KNN(trainingSetX, trainingSetY, {k: 7}); test(); } function test() { const result = knn.predict(testSetX); const testSetLength = testSetX.length; const predictionError = error(result, testSetY); console.log(`Test Set Size = ${testSetLength} and number of Misclassifications = ${predictionError}`); predict(); } function error(predicted, expected) { let misclassifications = 0; for (var index = 0; index < predicted.length; index++) { if (predicted[index] !== expected[index]) { misclassifications++; } } return misclassifications; } function predict() { let temp = []; prompt.start(); prompt.get(['Sepal Length', 'Sepal Width', 'Petal Length', 'Petal Width'], function (err, result) { if (!err) { for (var key in result) { temp.push(parseFloat(result[key])); } console.log(`With ${temp} -- type = ${knn.predict(temp)}`); } }); } /** * https://stackoverflow.com/a/12646864 * Randomize array element order in-place. * Using Durstenfeld shuffle algorithm. */ function shuffleArray(array) { for (var i = array.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; }
Exécutez le nœud index.js. Il devrait vous montrer ceci à l'écran:
$ node index.js Taille de l'ensemble de tests = 45 et nombre d'erreurs de classification = 2 invite: Sepal Length: 1.7 prompt: Sepal Width: 2.5 prompt: Petal Length: 0.5 prompt: Petal Width: 3.4 With 1.7,2.5,0.5,3.4 - type = 2
Bien fait. C'est votre algorithme kNN au travail.
Un aspect énorme de l'algorithme kNN est la valeur de k, et on l'appelle un hyperparamètre. Les hyperparamètres sont un «type de paramètres qui ne peuvent pas être appris directement à partir du processus d'entraînement normal». Ces paramètres expriment des propriétés de «niveau supérieur» du modèle, telles que sa complexité ou la rapidité avec laquelle il doit être appris. Ils sont appelés «hyperparamètres».