This repository has been archived by the owner on Sep 1, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMultilayer_Perceptron_Classificador.py
147 lines (106 loc) · 6.28 KB
/
Multilayer_Perceptron_Classificador.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import numpy as np # a grande biblioteca salvadora de todas as ias
import pandas as pd #somente para aceitar pandas pra devolver para numpy
import FuncoesAtivacao as func #funções de ativação para qualquer coisa n
class MultiPerceptron:
# https://towardsdatascience.com/softmax-regression-in-python-multi-class-classification-3cb560d90cb2
# https://colab.research.google.com/drive/1Y1Id58f1OjWd-bauF1MZccGDW_pUYU9n?usp=sharing
# Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process
# .\.venv\Scripts\activate
def __init__(self, hiddenNodes, taxa, epocas, funcao, parada, outputNodes=7):
self.hiddenNodes = hiddenNodes
self.outputNodes = outputNodes
self.epocas = epocas
self.pesoEntradaMeio = None
self.pesoMeioSaida = None
self.min = 0
self.max = 0
self.parada = parada or False
self.taxa = taxa or 0.1
if(funcao == 'sigmoid'):
self.funcao = func.FuncoesAtivacao.sigmoid
self.derivativa = func.FuncoesAtivacao.dsigmoid
self.min = func.FuncoesAtivacao.minsigmoid
self.max = func.FuncoesAtivacao.maxsigmoid
#efif(funcao == 'relu'):
#self.funcao = func.FuncoesAtivacao.relu
#self.derivativa = func.FuncoesAtivacao.drelu
#elif(funcao == 'tanh'):
#self.funcao = func.FuncoesAtivacao.tanh
#self.derivativa = func.FuncoesAtivacao.dtanh
#elif(funcao == 'softmax'): #não use o softmax, ô derivada desgraçada
#self.funcao = func.FuncoesAtivacao.softmax
#self.derivativa = func.FuncoesAtivacao.dsoftmax
else:
raise Exception('Função de ativação não suportada')
#@vectorize(["float32(float32, float32)"], target='cuda') se o numba funcionasse, claro.
def train(self,X,y):
#transformações de dataframe do pandas pra array do numpy
if isinstance(X, pd.DataFrame):
inputM = X.to_numpy()
else:
inputM = X
if isinstance(y, pd.DataFrame):
outputM = y.to_numpy()
else:
outputM = y
#dimensões das entradas
m, n = inputM.shape # aqui temos m = número de entradas, n = número de *features*
lin_2, col_2 = outputM.shape #lin_2 = número de respostas, col_2 = número de *labels*
#inicializar as matrizes de peso
self.pesoEntradaMeio = np.random.uniform(self.min, self.max,(n,self.hiddenNodes))
self.pesoMeioSaida = np.random.uniform(self.min, self.max,(self.hiddenNodes,col_2))
self.vies_1 = np.random.uniform(self.min, self.max, (1, self.hiddenNodes))
self.vies_2 = np.random.uniform(self.min, self.max, (1, self.outputNodes))
#self.vies_1 = np.random.rand(n)
#self.vies_2 = np.random.rand(self.hiddenNodes)
custo_anterior = 100 # necessário para a verificação de convergência abaixo
# precisa dar muito errado para que (isso) menos (custo depois de 20k épocas) dê entre 0 e 0.001
contador_de_epocas = 0
# isso faz entrada x pesos da camada escondida, depois
# a saida disso x pesos da camada de saida
# estou muito feliz que isso dá certo eu demorei 2 dias pra entender
while(contador_de_epocas <= self.epocas):
#forward step:
inputHidden = np.dot(inputM, self.pesoEntradaMeio) + self.vies_1
outputHidden = self.funcao(inputHidden)
inputSaida = np.dot(outputHidden, self.pesoMeioSaida) + self.vies_2
outputSaida = self.funcao(inputSaida)
#função de custo:
custo = (-1/m) * np.sum(outputM * np.log(outputSaida) + (1 - outputM) * np.log(1 - outputSaida ))
#----------------------------------------------------------------------------
#HORA DO BACKPROPAGATION
#calculando o gradiente de [hidden -> saida] : Y-Y^
erro_saida = np.subtract(outputM, outputSaida) # error
gradienteOutput = np.multiply(erro_saida, self.derivativa(outputSaida)) #GRADIENTE CAMADA X \ erro * derivada(y^)
#"recursivamente" calculo o peso da próxima camada:
#calculando o gradiente de [entrada -> hidden] GRADIENTE DA CAMADA X-1
erro_hidden = gradienteOutput.dot(self.pesoMeioSaida.T) # error_1 =
gradienteHidden = np.multiply(erro_hidden, self.derivativa(outputHidden))
#ajustando os pesos da camada entrada -> hidden
self.pesoEntradaMeio += inputM.T.dot(gradienteHidden) * self.taxa
#ajustando os pesos da camada hidden -> saida
self.pesoMeioSaida += outputHidden.T.dot(gradienteOutput) * self.taxa
# ajustando os pesos do vies
self.vies_2 = np.sum(self.taxa * gradienteOutput)
self.vies_1 = np.sum((self.taxa * gradienteHidden))
#o meu método de parada antecipada, checa o quanto a função de custo convergiu a cada 1 mil iterações.
contador_de_epocas += 1
if(contador_de_epocas % 1000 == 0 and self.parada == True ):
if(custo_anterior - custo < 0.1 and custo_anterior - custo > 0):
print("O custo convergiu abaixo de .1, saíndo.")
break
print('Além de %d epocas.' % contador_de_epocas)
print('Custo = %.16f' % custo)
custo_anterior = custo
def query(self, X):
# é basicamente o forwardstep. os dados passam 1 vez e retornam a imagem.
tabela = X
if isinstance(tabela, pd.DataFrame):
inputM = tabela.to_numpy()
else:
inputM = tabela
inputHidden = np.dot(inputM, self.pesoEntradaMeio)
outputHidden = self.funcao(inputHidden)
inputSaida = np.dot(outputHidden, self.pesoMeioSaida)
outputSaida = self.funcao(inputSaida)
return outputSaida