In [234]:
import csv
import random
import numpy as np
class LVQ():
def __init__(self, dataset):
"""
Construtor da classe
:param nome_arquivo: nome do arquivo csv que contem os dados
"""
self.dados = dataset
self.dataset = dataset
self.qtd_caracteristicas = 0
self.amplitudes = []
self.qtd_caracteristicas = len(self.dados[0])-1
def normalizar(self):
"""
Normalizada todas as caracteristicas para um intervalo de 0 - 1, para todas tenham o mesmo peso na
classificacao
"""
lista = []*(len(self.dados[0])-1)
self.amplitudes = []
for caracteristica in range(len(self.dados[0])-1):
lista = [elemento[caracteristica] for elemento in self.dados]
self.amplitudes += [[max(lista), min(lista)]]
for elemento in self.dados:
elemento[caracteristica] = (elemento[caracteristica] - min(lista))/(max(lista)+min(lista))
def triagem(self, split: float=0.65):
"""
Divide aleatoriament os elementos do conjunto de dados em dois subconjuntos: teste e treino
:param split: de 0 a 1 -> 'porcentagem' dos elementos que serao do conjunto de treino
"""
self.treino, self.teste = [], []
for elemento in self.dados:
if random.random() < split:
self.treino += [elemento]
else:
self.teste += [elemento]
def resumir(self, n: float=10, e: float=10, t: float=0.4):
"""
Retorna o codebook dos dados, ou seja, os elementos que melhor representam o todo
:param t: taxa de aprendizado inicial
:param e: numero de epocas
:param n: numero de elementos do coodbook
"""
#Geracacao aleatorio dos elementos iniciais do codebook
self.codebook = [[]]*n
for i in range(n):
self.codebook[i] = [0] * (self.qtd_caracteristicas + 1)
for caracteristica in range(self.qtd_caracteristicas + 1):
self.codebook[i][caracteristica] = random.choice(self.dados)[caracteristica]
for epoca in range(e):
taxa = t * (1.0-(epoca/float(e)))
for elemento in self.treino:
representante = self.encontrar_mais_proximo(elemento, self.codebook)
o = -1
if representante[-1] == elemento[-1]:
o = 1
for caracteristica in range(self.qtd_caracteristicas):
erro = (elemento[caracteristica]-representante[caracteristica])
representante[caracteristica] += (erro * taxa * o)
def testar(self):
"""
Executa a classificacao para cada elemento do conjunto teste e retorna a precisao do algoritmo
"""
qtd_teste = len(self.teste)
precisao = 100.0
for elemento in self.teste:
bmu = self.encontrar_mais_proximo(elemento, self.codebook)
if bmu[-1] != elemento[-1]:
precisao -= (1/qtd_teste)*100
return precisao
def encontrar_mais_proximo(self, elemento, lista):
"""
Executa a classificacao para cada elemento do conjunto teste e retorna a precisao do algoritmo
:param elemento: vetor para o qual deve-se vetor mais proximo de uma dada lista
:param lista: lista de vetores
"""
resposta = [lista[0], spatial.distance.euclidean(elemento[0:-1], lista[0][0:-1])]
for i in lista:
distancia = spatial.distance.euclidean(elemento[0:-1], i[0:-1])
if distancia < resposta[1]:
resposta = [i, distancia]
return resposta[0]
@property
def representantes(self):
"""
Retorna o codebook "original", com as caracteristicas em seus intervalos originais. Ou seja,
retorna o codebook desnormalizado, caso ele tenha sido normalizado
"""
representantes_desnormalizados = [[]]*len(self.codebook)
if self.amplitudes:
for index, representante in enumerate(self.codebook):
representante_desnormalizado = []
for caracteristica in range(self.qtd_caracteristicas):
aux = ((self.amplitudes[caracteristica][0] + self.amplitudes[caracteristica][1])\
* representante[caracteristica]) + self.amplitudes[caracteristica][1]
representante_desnormalizado += [aux]
representante_desnormalizado += [representante[-1]]
representantes_desnormalizados[index] = representante_desnormalizado
else:
return self.codebook
return representantes_desnormalizados
@property
def classes(self):
"""
Retorna as classes do dataset
"""
classes = []
for elemento in self.dados:
if elemento[-1] not in classes:
classes.append(elemento[-1])
return classes
In [281]:
import random
def importar_dataset(arquivo_csv: str=None):
"""
Carrega os dados iniciais da classe através de um arquivo csv. Esperar-se um arquivo possua linhas
com n colunas, de modo que a n-ézima represente a classe do elemento e as anteriores representem,
cada uma, uma caracteristica diferente.
:param arquivo_csv: nome do arquivo csv
"""
dados = []
with open(arquivo_csv, 'r') as arquivo_csv:
arquivo = csv.reader(arquivo_csv)
for index, linha in enumerate(arquivo):
if linha:
dados += [list(map(float, linha[0:-1]))]
dados[index] += [linha[-1]]
return dados
def random_cores(qtd: int=3):
"""
Retorna aleatoriamente cores no formato hexademal de acordo com a quantidade pedida
"""
lista = [(210,180,140), (139,69,19), (244,164,96), (85,107,47), (0,255,0), (102,205,170), (127,255,212),
(72,209,204), (0,255,255), (176,196,222), (30,144,255), (0,0,255), (220,20,60), (255,105,180),
(255,0,255), (139,0,139), (255,192,203), (255,0,0), (250,128,114), (255,165,0), (255,255,0)]
random.shuffle(lista)
cores = lista[0:qtd]
resposta = []
for cor in cores:
resposta += ['#%02x%02x%02x' % cor]
return resposta
In [294]:
import matplotlib.pyplot as plt
dataset = importar_dataset("datas/IRIS.csv")
# Dados normalizados
print("Algoritmo com os dados normalizados entre 0 - 1")
iris_norm = LVQ(dataset)
iris_norm.triagem(0.75)
iris_norm.normalizar()
iris_norm.resumir(n=8, e=13, t=0.5)
print("Precisão: ", iris_norm.testar(), "% \n")
classes = iris_norm.classes
classes_cor = {}
cores = random_cores(len(classes))
for index, classe in enumerate(classes):
classes_cor[classe] = cores[index]
for elemento in iris_norm.dataset:
plt.plot(elemento[0], elemento[1], 'o', color=classes_cor[elemento[-1]])
for representante in iris_norm.codebook:
plt.plot(representante[0], representante[1], 'D' , ms=10, mfc='none', color=classes_cor[representante[-1]])
plt.show()
# Sem normalização
print("Algoritmo com os dados não normalizados")
iris = LVQ(dataset)
iris.triagem(0.75)
iris.resumir(n=8, e=13, t=0.5)
print("Precisão: ", iris.testar(),"% \n")
# for representante in iris.representantes:
# print(representante)