Esse algoritmo e usado no aprendizado de maquina supervisionado.
Algoritmo KNN é utilizado na aprendizagem supervisionada de maquina, basicamente nesse artigo vamos implementar esse algoritmo em um cenário de um restaurante internacional.
Montar o cenário
Estamos em um ilustre restaurante Italiano dentro do Aeroporto Internacional John F. Kennedy na cidade de New York, nosso cliente possui um clientela internacional bastante variada sendo composta por Americanos, Brasileiros, Espanhóis e Franceses. O nosso cliente deseja abrir uma filial, mas não sabe em qual nação seu Cardápio Italiano sera mais apreciado. Seu trabalho será identificar qual pais retem o maior numero de clientes.
Para auxilia lo nosso cliente te enviou as comandas de 5 dias de trabalho com a respectiva quantidade em gramas de Carne vermelha , Carne Branca , Massas, Frutas e Vegetais consumidos por cliente.
Atenção nesse cenário existem varias variáveis que precisamos considerar, mas não vamos abordar essas questões, para facilitar a implementação do algoritmo. Além é claro que nosso cliente poderia simplesmente perguntar ao cliente qual sua nacionalidade.
Como fonte dos hábitos alimentares dos Americanos, Brasileiros , Espanhóis e Franceses você utilizou um estudo da International Scientific Institute of Biology and Agriculture.
Comece bonitão! Go Horse!
Tendo agora um arquivo com a media de consumo de cada nacionalidade em gramas e a quantidade consumida por cliente, construa um programa que leia o arquivo com as medias de cada nacionalidade e as comandas e faça o cruzamento dos dados devolvendo a nacionalidade com mais clientes para ajudar nosso patrão a definir a filial.
Esta na hora de produzir o código que usara a media do estudo e as comandas para através da distancia euclidiana definir a nacionalidade de cada cliente.
Para ser mais abrangente em termos de linguagem eu farei o Algoritmo em Javascript, Python e JAVA, para assegurar que a maioria compreenda em sua linguagem mais familiar.
Antes de procedermos saiba que as descrições podem estar um tanto complexas para falar a verdade, então não exite em pegar uma café e/ou ler uma 2° vez.
Função da Distancia Euclidiana
Para pensar em distancia nos termos da distancia euclidiana, pense que a distancia de uma entidade(Cliente) para outra nada mais é do que quantas diferenças uma entidade(Cliente) possui em relação a outra entidade(Cliente).
Pegar a diferença entre as propriedades das entidades
// Função que retorna a distancia euclidiana de 2 clientesfunctiongetEuclidiana( cliente0 , cliente1){/* Distância euclidiana é a raiz quadrada da soma das diferenças dos valores dos atributos elevado ao quadrado */let soma = (cliente0.carnes_vermelhas -cliente1.carnes_vermelhas)**2+ (cliente0.carnes_brancas -cliente1.carnes_brancas)**2+ (cliente0.massas -cliente1.massas )**2+ (cliente0.frutas -cliente1.frutas )**2+ (cliente0.vegetais -cliente1.vegetais )**2;returnMath.sqrt(soma,2); // ou soma ** (1/2);}
import math;# Função que retorna a distancia euclidiana de 2 clientesdefgetEuclidiana( cliente0,cliente1 ):""" Distância euclidiana é a raiz quadrada da soma das diferenças dos valores dos atributos elevado ao quadrado """ soma = (cliente0['carnes_vermelhas']- cliente1['carnes_vermelhas'])**2 soma += (cliente0['carnes_brancas']- cliente1['carnes_brancas'])**2 soma += (cliente0['massas']- cliente1['massas'] )**2 soma += (cliente0['frutas']- cliente1['frutas'] )**2 soma += (cliente0['vegetais']- cliente1['vegetais'] )**2return math.sqrt(soma, 2); # ou soma ** (1/2);
publicdoublegetEuclidiana(Cliente cliente0 ,Cliente cliente1){/* Distância euclidiana é a raiz quadrada da soma das diferenças dos valores dos atributos elevado ao quadrado */double soma =0; soma+=Math.pow((cliente0.carnes_vermelhas-cliente1.carnes_vermelhas),2); soma +=Math.pow((cliente0.carnes_brancas-cliente1.carnes_brancas),2); soma +=Math.pow((cliente0.massas-cliente1.massas ),2); soma +=Math.pow((cliente0.frutas-cliente1.frutas ),2); soma +=Math.pow((cliente0.vegetais-cliente1.vegetais ),2);returnMath.sqrt(soma);}
doublegetEuclidiana(Cliente cliente0 ,Cliente cliente1){ /* Distância euclidiana é a raiz quadrada da soma das diferenças dos valores dos atributos elevado ao quadrado */double soma =0; soma +=pow((cliente0.getA() -cliente1.getA()),2); soma +=pow((cliente0.getB() -cliente1.getB()),2); soma +=pow((cliente0.getC() -cliente1.getC()),2); soma +=pow((cliente0.getD() -cliente1.getD()),2);returnsqrt(soma);}
Primeiro vamos pegar o cliente A que é Americano e consumiu 10.00 g de carne vermelha e vamos pegar a diferença com o cliente B que consumiu 08.00 g de carne vermelha oque nos da:
Elevar ao quadrado a diferença
Agora com a diferença 2.0 g vamos elevar ao quadrado.
Somar as diferenças
Fazer a raiz quadrada
Para os Curiosos e entusiasmados por programação, a função de raiz na maioria das linguagens que eu conheço implementam uma função com nome de sqrt( x, 2) ao invés de ²√x para as operações, então para evitar aprender a utilizar uma em cada linguagem eu sempre uso a potenciação: x ** ( 1 / 2 ) .
sqrt( x, 2 ) == x ** ( 1 / 2 ) ou sqrt( x, 3 ) == x ** ( 1 / 3 )
Função da Classificação
Dentro dessa função vamos comparar o cliente que precisa ser identificado com as pessoas que já sabemos a nacionalidade pelo estudo que consultamos.
Depois criar uma vetor que indexe as relações de distancia entre as entidades cliente não identificadas(Comanda) para os clientes do estudo.
Pegando os dados
Começamos lendo os dados nos dois arquivo e adicionando nas nossas listas. para isso vamos criar uma função para ler e montar os dados.
functionle_arquivo(nome){// Lista com os dados pegos linha por linha listDados = []; arquivo =fs.readFileSync(nome,'utf8').split('\n');for(let line of arquivo){// Corta o texto da linha para pegar cada dado carne_vermelha =line.slice(4,9) *1; carne_branca =line.slice(13,18) *1; massa =line.slice(22,27) *1; fruta =line.slice(31,36) *1; vegetais =line.slice(40,45) *1; nascionalidade =line.slice(50,57);// Monta o Cliente e adiciona na lista listDados.push({ 'carnes_vermelhas':carne_vermelha,'carnes_brancas':carne_branca,'massas':massa,'frutas':fruta,'vegetais':vegetais,'nascionalidade':nascionalidade }); }// Retorna as informação guardadas na listareturn listDados;}
defle_arquivo(nome):# Lista com os dados pegos linha por linha listDados = [];# Abre o arquivo para leitura 'r'withopen( nome,'r')as arquivo:# Le o arquivo linah por linhafor line in arquivo:# Corta o texto da linha para pegar cada dado carne_vermelha =float(line[4:9]); carne_branca =float(line[13:18]); massa =float(line[22:27]); fruta =float(line[31:36]); vegetais =float(line[40:45]); nascionalidade = line[50:57];# Monta o Cliente e adiciona na lista listDados.append({ 'carnes_vermelhas':carne_vermelha,'carnes_brancas':carne_branca,'massas':massa,'frutas':fruta,'vegetais':vegetais,'nascionalidade':nascionalidade });# Fecha o arquivo aberto arquivo.close();# Retorna as informação guardadas na listareturn listDados;
publicList<Cliente>le_arquivo(String nome){// Lista com os dados pegos linha por linhaList<Cliente> listDados =newArrayList<Cliente>();try {BufferedReader arquivo =newBufferedReader(new FileReader(nome));String line;while ((line =arquivo.readLine()) !=null) {System.out.println(linha);Cliente client =newCliente(); client.carnes_vermelhas=Float.parseFloat(line.substring( 4,9 ));client.carnes_brancas=Float.parseFloat(line.substring( 13,18 ));client.massas=Float.parseFloat(line.substring( 22,27 ));client.frutas=Float.parseFloat(line.substring( 31,36 ));client.vegetais=Float.parseFloat(line.substring( 40,45 ));client.nascionalidade=line.substring( 50,57 );listDados.add(client); } } catch (IOException e) {e.printStackTrace(); }return listDados;}
Agora que já podemos ler vamos pegar os dados do estudo que esta no arquivo de nomemediaPorNasci.knn.algoritmo.txt e os dados das comandas não identificadas no arquivo comandas.knn.algoritmo.txt , e começar a comparação.
Vamos criar uma estrutura de repetição que para pegar cada um dos clientes não identificas e outro loop interno que se em carregara de comparar os dados do cliente não identificado com os dados do estudo.
// Pegando os dados listClassificados =le_arquivo('mediaPorNasci.knn.algoritmo.txt');listNaoClassificados =le_arquivo('comandas.knn.algoritmo.txt');console.log(listClassificados);// Variaveis Acumuladoras de cada nacionalidadeqtdAmericanos =0;qtdEspanhol =0;qtdFrances =0;qtdBrasileiro =0;for(let clienteNaoIdentificado of listNaoClassificados){// Codigo para comparar e determinar a classe mais proximafor(let pessoaIdentificada of listClassificados){// Codigo que vai comparar o// ClienteNãoIdentificado com// cada um dos pessoaIdentificada no estudo }}
# Pegando os dados listClassificados =le_arquivo('mediaPorNasci.knn.algoritmo.txt');listNaoClassificados =le_arquivo('comandas.knn.algoritmo.txt');# Variaveis Acumuladoras de cada nacionalidadeqtdAmericanos =0;qtdEspanhol =0;qtdFrances =0;qtdBrasileiro =0;for clienteNaoIdentificado in listNaoClassificados:# Codigo para comparar e determinar a classe mais proximafor pessoaIdentificada in listClassificados:# Codigo que vai comparar o# ClienteNãoIdentificado com# cada um dos pessoaIdentificada no estudo
Main knn =newMain();// Pegando os dadosList<Cliente> listClassificados = knn.le_arquivo("mediaPorNasci.knn.algoritmo.txt");List<Cliente> listNaoClassificados = knn.le_arquivo("comandas.knn.algoritmo.txt");//import static java.lang.System.out;out.println(listClassificados);// Variaveis Acumuladoras de cada nacionalidadeint qtdAmericanos =0;int qtdEspanhol =0;int qtdFrances =0;int qtdBrasileiro =0;for(Cliente clienteNaoIdentificado : listNaoClassificados){// Codigo para comparar e determinar a classe mais proximafor(Cliente pessoaIdentificada : listClassificados){// Codigo que vai comparar o// ClienteNãoIdentificado com// cada um dos pessoaIdentificada no estudo }}
Para nos ajuda a entender vamos resumir nossas variáveis ate agora:
Bem agora que já pegamos cada clienteNaoIdentificado e cada pessoaIdentificada do estudo, vamos precisar comparar cada um dos clienteNaoIdentificado com todas as pessoaIdentificada e guardar a distancia como mostrada nas imagens abaixo.
Caso nosso clienteNaoIdentificado seja:
Americano
Espanhol
Brasileiro
Frances
Através da comparação com as pessoas já identificadas podemos extrapolar de qual nacionalidade e o nosso cliente.
Então vamos implementar criando uma lista chamada listIndexadaPelaDistancia que conterá a distancia que cada pessoas já identificada estão do cliente ainda não identificado. E depois vamos ordenar a lista para que tenhamos o mais próximo na 1° posição, o segundo na 2° posição e assim por diante deixando o mais distante na ultima posição.
for(let clienteNaoIdentificado of listNaoClassificados){// Lista que ira ordenarar as pessoa mais proximos dele listIndexadaPelaDistancia = [];for(let pessoaIdentificada of listClassificados){// Comparando e pegando a distancia distancia =getEuclidiana(clienteNaoIdentificado,pessoaIdentificada)// Colocando na lista de ordenacaolistIndexadaPelaDistancia.push({'distancia':distancia,'nascionalidade': pessoaIdentificada['nascionalidade'] }); }
for clienteNaoIdentificado in listNaoClassificados:# Lista que ira ordenarar as pessoa mais proximos dele listIndexadaPelaDistancia = [];for pessoaIdentificada in listClassificados:# Comparando e pegando a distancia distancia =getEuclidiana(clienteNaoIdentificado,pessoaIdentificada)# Colocando na lista de ordenacao listIndexadaPelaDistancia.append({'distancia':distancia,'nascionalidade': pessoaIdentificada['nascionalidade'] });
for(Cliente clienteNaoIdentificado : listNaoClassificados){// Lista que ira ordenarar as pessoa mais proximos dele Map<Double,String> listIndexadaPelaDistancia =newTreeMap<Double,String>();for(Cliente pessoaIdentificada : listClassificados){// Comparando e pegando a distanciaDouble distancia =getEuclidiana( clienteNaoIdentificado, pessoaIdentificada );// Colocando na lista de ordenacaolistIndexadaPelaDistancia.put( distancia,pessoaIdentificada.nascionalidade ); }}
Vamos ordenar a lista:
// Hora de ordenar pelos com menor// distancialetfuncaoDeOrdenacao=function(a, b){return a['distancia'] - b['distancia'] }// Usando o recurso de sort da linguagemlistIndexadaPelaDistancia.sort( funcaoDeOrdenacao );
# Hora de ordenar pelos com menor# distanciafuncaoDeOrdenacao =lambdaclienteIndexado: clienteIndexado['distancia'];# Usando o recusro de sort da linguagemlistIndexadaPelaDistancia.sort(key=funcaoDeOrdenacao)
// Como usamos o TreeMap nossa lista se ordena pela função// .keySet()// set = lista ordenadaSet<Double> set =listIndexadaPelaDistancia.keySet();
Agora que já temos a relação entre clienteNaoIdentificado e todas as pessoaIdentificada , precisamos determinar o quão distante a gente vai considerar para classificação , ao seja, quais das pessoaIdentificada estão mais próximas para nos ajudar a classificar o clienteNaoIdentificado , eu decidi escolher os 7 primeiros da lista ordenada.
K=7;for(let i oflistIndexadaPelaDistancia.slice(0,K)){// Codigo que verifica qual nascionalidade é predominate}
K =7;for i in listIndexadaPelaDistancia[:K]:# Codigo que verifica qual nascionalidade é predominate
int K =7;int contador =0;for(Double key :listIndexadaPelaDistancia.keySet()) {// Codigo que verifica qual nascionalidade é predominateif(contador < K)contador++;elsebreak;}
Agora só falta contar a nacionalidade predominante nesse conjunto de 7 pessoas próximas e teremos nosso classificador.
K=7;/* Escolhi 7, mas na minha opniao seria melhor pegar uma porcentagem dos classificados como 30% algo assim, quem sabe , afinal eu não sou um experti em algoritmos de aprendizado supervisionado de maguina */isAmerica =0;isFrances =0;isEspanho =0;isBrasile =0;for(let i oflistIndexadaPelaDistancia.slice(0,K)){// Codigo que verifica qual nascionalidade é predominateif (i['nascionalidade'] =="America") isAmerica +=1;elseif(i['nascionalidade'] =="Espanho") isEspanho +=1;elseif(i['nascionalidade'] =="Frances") isFrances +=1;elseif(i['nascionalidade'] =="Brasile") isBrasile +=1;elseconsole.log("Categoria não identificada");}
K =7;# Escolhi 7, mas na minha opniao seria melhor# pegar uma porcentagem dos classificados como 30% algo assim,# quem sabe , afinal eu não sou um experti em algoritmos de # aprendizado supervisionado de maguinaisAmerica =0;isFrances =0;isEspanho =0;isBrasile =0;for i in listIndexadaPelaDistancia[:K]:if i['nascionalidade']=="America": isAmerica +=1;elif i['nascionalidade']=="Espanho": isEspanho +=1;elif i['nascionalidade']=="Frances": isFrances +=1;elif i['nascionalidade']=="Brasile": isBrasile +=1;else:print("Categoria não identificada")
int K =7;int contador =0;/* Escolhi 7, mas na minha opniao seria melhor pegar uma porcentagem dos classificados como 30% algo assim, quem sabe , afinal eu não sou um experti em algoritmos de aprendizado supervisionado de maguina */int isAmerica =0;int isFrances =0;int isEspanho =0;int isBrasile =0;for(Double key :listIndexadaPelaDistancia.keySet()) {// Codigo que verifica qual nascionalidade é predominateif (listIndexadaPelaDistancia.get(key).equals("America")) isAmerica +=1;elseif(listIndexadaPelaDistancia.get(key).equals("Espanho")) isEspanho +=1;elseif(listIndexadaPelaDistancia.get(key).equals("Frances")) isFrances +=1;elseif(listIndexadaPelaDistancia.get(key).equals("Brasile")) isBrasile +=1;elseout.println("Categoria não identificada");if(contador < K)contador++;elsebreak;}
Agora vamos verificar qual é a predominante verificando qual das variáveis acumuladoras e maior que as outras.