Home › Fóruns › Fórum Redes Neurais Artificiais em Python › Dúvida sobre perceptron multi camada
Marcado: (Redes neurais multicamada)
- Este tópico contém 7 respostas, 2 vozes e foi atualizado pela última vez 6 meses, 1 semana atrás por
Denny Ceccon.
- AutorPosts
- 26 de novembro de 2022 às 17:57 #38123
Leonidas Mariano Pinheiro
ParticipanteFiz em python uma rede perceptron multicamadas (7 features de entrada, 12 neurônios na primeira camada oculta, 5 na segunda e 3 neurônios de saída), uso learning rate e momentum, as ocultas são ativadas por relu e a saída por softmax. O erro é calculado por entropia cruzada e retropropagado através do algoritmo ensinado. A rede converge até igualar o erro em aproximadamente 0,33% para cada uma dos 3 neurônios de saída. Por que ocorre das saídas se igualarem nos mesmos valores? Como posso ajustar essa condição e fazer a rede convergir corretamente na direção da minimizacão do erro?
29 de novembro de 2022 às 09:04 #38149Denny Ceccon
ModeradorOlá Leonidas, pode compartilhar seu código?
29 de novembro de 2022 às 13:18 #38150Leonidas Mariano Pinheiro
ParticipantePosso, mas como faço isso? Para onde encaminho?
29 de novembro de 2022 às 14:51 #38151Denny Ceccon
ModeradorPode colocar o texto do seu código aqui mesmo.
29 de novembro de 2022 às 20:23 #38152Leonidas Mariano Pinheiro
ParticipanteA base de dados original está no arquivo “Cotacoes.csv” e lista as cotações dos preços do ativo WIN (B3) no tempo gráfico de 10 min. Abaixo seguem os 20 primeiros registros, mas a base tem 67624 registros:
DATE;TIME;OPEN;HIGH;LOW;CLOSE;TICKVOL;VOL;SPREAD
2017/09/04;09:00:00;72300;72480;72255;72405;6308;24832;5
2017/09/04;09:10:00;72400;72470;72370;72405;3641;14569;5
2017/09/04;09:20:00;72410;72435;72305;72315;4339;19095;5
2017/09/04;09:30:00;72315;72360;72285;72355;3272;14470;5
2017/09/04;09:40:00;72355;72375;72305;72310;2723;12022;5
2017/09/04;09:50:00;72310;72385;72305;72350;2169;9231;5
2017/09/04;10:00:00;72350;72365;72275;72340;4172;16387;5
2017/09/04;10:10:00;72340;72440;72330;72365;5693;23302;5
2017/09/04;10:20:00;72365;72480;72365;72435;5089;20870;5
2017/09/04;10:30:00;72435;72515;72350;72365;7786;31227;5
2017/09/04;10:40:00;72370;72430;72310;72430;6648;27973;5
2017/09/04;10:50:00;72430;72450;72390;72435;3372;16180;5
2017/09/04;11:00:00;72435;72500;72410;72485;4164;17121;5
2017/09/04;11:10:00;72490;72645;72430;72595;8919;37285;5
2017/09/04;11:20:00;72595;72600;72535;72565;3886;15673;5
2017/09/04;11:30:00;72565;72675;72555;72625;6217;25463;5
2017/09/04;11:40:00;72620;72695;72620;72625;3075;12662;5
2017/09/04;11:50:00;72630;72650;72590;72595;2762;10595;5
2017/09/04;12:00:00;72595;72650;72575;72610;2775;11117;5Abaixo segue o código:
import os as os
from matplotlib import pyplot as plt
import numpy as np
from datetime import datetime# Classe Controle rede neural
class cRedeNeural():
def fCriarRede(self, n_inputs = 3, n_hiddena=5, n_hiddenb=4, n_outputs=3, learningrate=0.0015):
# # Pesos
# self.w_ia = 2 * np.random.random((n_hiddena, n_inputs)) – 1
# self.w_ab = 2 * np.random.random((n_hiddenb, n_hiddena)) – 1
# self.w_bo = 2 * np.random.random((n_outputs, n_hiddenb)) – 1
# # Bias
# self.b_ia = 2 * np.random.random((n_hiddena)) – 1
# self.b_ab = 2 * np.random.random((n_hiddenb)) – 1
# self.b_bo = 2 * np.random.random((n_outputs)) – 1# Pesos
self.w_ia = np.random.default_rng(40).random((n_hiddena, n_inputs))
self.w_ab = np.random.default_rng(41).random((n_hiddenb, n_hiddena))
self.w_bo = np.random.default_rng(42).random((n_outputs, n_hiddenb))
# Bias
self.b_ia = np.random.default_rng(43).random((n_hiddena))
self.b_ab = np.random.default_rng(44).random((n_hiddenb))
self.b_bo = np.random.default_rng(45).random((n_outputs))# Somas ponderadas das camadas
self.s_ia = np.zeros((n_hiddena))
self.s_ab = np.zeros((n_hiddenb))
self.s_bo = np.zeros((n_outputs))
# Saídas das camadas após a função de ativação
self.z_ia = np.zeros((n_hiddena))
self.z_ab = np.zeros((n_hiddenb))
self.z_bo = np.zeros((n_outputs))
# Deltas das camadas
self.d_ia = np.zeros((n_hiddena))
self.d_ab = np.zeros((n_hiddenb))
self.d_bo = np.zeros((n_outputs))
# Learning rate
self.eta = learningrate
self.erro = list()def fPropagacao(self, inputs):
self.s_ia = np.dot(self.w_ia, inputs) + self.b_ia
self.z_ia = fRelu(self.s_ia)
self.s_ab = np.dot(self.w_ab, self.s_ia) + self.b_ab
self.z_ab = fRelu(self.s_ab)
self.s_bo = np.dot(self.w_bo, self.s_ab) + self.b_bo
self.z_bo = fSoftmax(self.s_bo)
return self.z_bodef fretropropagacao(self, x, erro, m):
# Calcula deltas para todas as camadas
self.d_bo = erro * fDerivadaSoftmax(self.z_bo)
self.d_ab = np.dot(self.w_bo.T, self.d_bo) * fDerivadaRelu(self.z_ab)
self.d_ia = np.dot(self.w_ab.T, self.d_ab) * fDerivadaRelu(self.z_ia)# Atualiza as matrizes de pesos
n_i = x.shape[0]
n_ha = self.d_ia.shape[0]
n_hb = self.d_ab.shape[0]
n_o = self.d_bo.shape[0]
r = np.reshape(self.d_bo, [n_o, 1])
s = np.reshape(self.z_ab, [1, n_hb])
self.w_bo = (self.w_bo * m) – self.eta * np.dot(r, s)
r = np.reshape(self.d_ab,[n_hb,1])
s = np.reshape(self.z_ia,[1,n_ha])
self.w_ab = (self.w_ab * m) – self.eta * np.dot(r, s)
r = np.reshape(self.d_ia,[n_ha,1])
s = np.reshape(x,[1,n_i])
self.w_ia = (self.w_ia * m) – self.eta * np.dot(r, s)
# Atualiza as matrizes de bias
self.b_bo = (self.b_bo * m) – self.eta * self.d_bo
self.b_ab = (self.b_ab * m) – self.eta * self.d_ab
self.b_ia = (self.b_ia * m) – self.eta * self.d_iadef fTreinarRede(self, x, epocas, lotes, m):
dset = fBaseSplit(x,lotes)
ErroLoteant = 0.0
erromedioant = 0.0
cl = 0
plt.style.use(‘ggplot’)
eixox = list()
eixoy = list()
eixoyy = list()
plt.ion()
for e in range(epocas):
for lote in dset:
for m_lote in lote:
Err = list()
for f in m_lote:
f1 = f[3:]
y_true = f[0:3]
y_pred = self.fPropagacao(f1)
#sErro = fMSE(y_true, y_pred)
sErro = fCross_E(y_true, y_pred)
Err.append(sErro)
ErroLote = np.array(Err).mean()
self.erro.append(ErroLote)
flag = ‘ ■’ # alt 254
if (ErroLote < ErroLoteant ):
flag = ‘ ▼’ # alt 31
elif (ErroLote > ErroLoteant ):
flag = ‘ ▲’ # alt 30
ErroLoteant = ErroLote
erromedio = np.array(self.erro).mean()
flag1 = ‘ ■’ # alt 254
if (erromedio < erromedioant ):
flag1 = ‘ ▼’ # alt 31
elif (erromedio > erromedioant ):
flag1 = ‘ ▲’ # alt 30
erromedioant = erromedio
self.fretropropagacao(f1,ErroLote, m)
eixoy.append(ErroLote)
plt.cla()
plt.clf()
eixox.append(cl)
cl += 1
plt.plot(eixox, eixoy, ‘b’)
plt.pause(0.001)
print(‘Epoca: ‘, ‘{:0>5}’.format(e), ‘/’, ‘{:0>5}’.format(epocas), ‘ – ErroMédio: ‘, f”{erromedio:.5f}” + flag1, ‘ – Erro: ‘, f”{ErroLote:.5f}” + flag)plt.ioff()
plt.show()
return self.erro# Função que calcula os valores de entrada da Rede Neural
def fCalcular(s):
s = np.array(s)
s = np.delete(s,(0), axis = 0)
s = np.delete(s,(0), axis = 1)
s = np.delete(s,(0), axis = 1)
# s -> OPEN, HIGH, LOW, CLOSE, TICKVOL, VOL, SPREAD
m = np.zeros((s.shape[0], s.shape[1]+18))
# Ordenacao crescente
m[:,0] = np.arange(m.shape[0])
m[:,1:8] = s[:,:]
a = np.zeros((s.shape[0]))
# Delta do retorno deslocado – Alvo
a = (s[1:,3] / s[:-1,3]) – 1
m[0:-2,8] = a[1:] #m[0:a.shape[0]-1,8] = a[1:]
# 1,0,0 = buy
m[1:,9] = np.where((m[1:,8] > 0) & (m[:-1,8] < 0), 1, 0)
# 0,0,1 = sell
m[1:,11] = np.where((m[1:,8] < 0) & (m[:-1,8] > 0), 1, 0)
# 0,1,0 = hold
m[1:,10] = np.where((m[1:,9] == 0) & (m[1:,11] == 0), 1, 0)
tcandle = np.nan_to_num(s[:,1] – s[:,2])
# Tamanho corpo do candle
m[:,12] = np.nan_to_num((s[:,3] – s[:,0]) / tcandle)
# Tamanho pavio do candle
m[:,13] = np.nan_to_num(1 – m[:,12])
# Distância entre fechamento e a média: Configuração: 9 -> 21 -> 200
m[1:,14] = np.nan_to_num(s[1:,3] – fMediaMovel(s[1:,3],9))
m[1:,15] = np.nan_to_num(s[1:,3] – fMediaMovel(s[1:,3],21))
m[1:,16] = np.nan_to_num(s[1:,3] – fMediaMovel(s[1:,3],200))
# Delta do volume
m[1:,17] = np.nan_to_num((s[1:,5] / s[:-1,5]) – 1)
# Calcula IFR:
calc = np.nan_to_num(fCalculaIFR(s[:,3]), 14)
m[2:,18] = np.where((calc[:-1] == 0) , 0, calc[:-1]) #np.nan_to_num(calc[1:] / calc[:-1] -1))
# Calcula Didi Index: Configuração: 3 -> 8 > 20
mm8 = np.nan_to_num(fMediaMovel(s[1:,3],8))
m[1:,19] = np.nan_to_num(fMediaMovel(s[1:,3],3) – mm8)
m[1:,20] = np.nan_to_num(fMediaMovel(s[1:,3],20) – mm8)
# Calcula ADX
vadx, vdip, vdin = np.nan_to_num(fCalculaADX(s[:,:4],8))
# vadx = adx -> vdip = di+ -> vdin = di-
t1 = np.zeros(vadx.shape[0])
t1[1:] = np.where(((vadx[1:] < 32) & (vadx[1:] < vadx[:-1])) | ((vadx[1:] <= vdip[1:]) & (vadx[1:] <= vdin[1:])), 0, 1)
#t2 = t3 = t4 = np.zeros(vadx.shape[0])
#t2 = np.where((t1 == 1) & (vdip > vdin ), 1, 0)
#t3 = np.where((t1 == 1) & (vdip < vdin ), 1, 0)
#t4 = np.where((vadx[:-3] < vadx[1:-2]) & (vadx[1:-2] > vadx[2:-1]), 1, 0)
# m[1:,21] -> Indica tendência de alta
m[1:,21] = np.where((t1 == 1) & (vdip > vdin ), 1, 0)
# m[1:,22] -> Indica tendência de baixa
m[1:,22] = np.where((t1 == 1) & (vdip < vdin ), 1, 0)
# m[1:,23] -> topo ou fundo
m[4:,23] = np.where((vadx[:-3] < vadx[1:-2]) & (vadx[1:-2] > vadx[2:-1]), 1, 0)
# Calcula distancia entre as Banda Bollinger: Configuracao: 20 -> 2
m[:,24] = np.nan_to_num(fCalculaBBolinger(s[:,3],20,2))
# Normaliza base de dados
ret = m[201:,8:]
minmax = [[min(column), max(column)] for column in zip(*ret)]
for col in (range(ret.shape[1]-3)):
c = col + 3
ret[:,c] = (ret[:,c] – minmax[c][0]) / (minmax[c][1] – minmax[c][0])
# Retorno da base de dados inteira (normalizados)
return ret# Função que calcula o ADX
def fCalculaADX(x, n=8):
#s -> OPEN, HIGH, LOW, CLOSE, TICKVOL, VOL, SPREAD
vaux = np.zeros((x.shape[0],3))
pDM = np.nan_to_num(fMediaMovel(x[1:,1] – x[:-1,1], n)) # +DM = mma(high[0] – high[-1], periodo)
nDM = np.nan_to_num(fMediaMovel(x[1:,2] – x[:-1,2], n)) # -DM = mma(low[0] – low[-1] , periodo)
vaux[1:,0] = np.nan_to_num(x[1:,1] – x[1:,2]) # high[0] – low[0]
vaux[1:,1] = np.nan_to_num(x[1:,1] – x[:-1,3]) # high[0] – close[-1]
vaux[1:,2] = np.nan_to_num(x[1:,2] – x[:-1,3]) # low[0] – close[-1]
TR = np.amax(vaux,axis=1)
ATR = np.nan_to_num(fMediaMovel(TR, n))
pDI = np.nan_to_num(pDM / ATR[:-1] * 100)
nDI = np.nan_to_num(nDM[:] / ATR[:-1] * 100)
DX = np.nan_to_num(np.abs(pDI – nDI) / np.abs(pDI + nDI) * 100)
ADX = np.nan_to_num(fMediaMovel(DX, n))
return ADX, pDI, nDI# Função que calcula o IFR
def fCalculaIFR(x, n=14):
ifr = np.eye(x.shape[0], 3)
ifr[0,0] = 0
ifr[1:,0] = x[1:] – x[:-1]
ifr[1:,1] = np.where(ifr[1:,0] > 0, ifr[1:,0], 0)
ifr[1:,2] = np.where(ifr[1:,0] < 0, np.abs(ifr[1:,0]), 0)
try:
ret = 100 – 100 / (1 + (fMediaMovel(ifr[1:,1], n) / fMediaMovel(ifr[1:,2], n)))
except ZeroDivisionError:
ret = 0
return ret# Função que calcula abertura das Banda de Bollinger -> abrindo = 1 e fechando = -1
def fCalculaBBolinger(x, n=20, k=2):
mma = np.nan_to_num(fMediaMovel(x,n))
s = np.nan_to_num(np.where(mma == 0, 0, (x – mma)**2))
dp = np.zeros(s.shape[0])
dp[n-1:] = (np.convolve(s, np.ones(n), ‘valid’)) ** 0.5
delta = (mma + dp * k) – (mma – dp * k)
ret = np.zeros(s.shape[0])
ret[1:] = np.where(delta[1:] < delta[:-1], -1, 1)
return ret# Função que calcula a média móvel
def fMediaMovel(x, n):
ret = np.zeros(x.shape[0])
ret[n-1:] = np.convolve(x, np.ones(n), ‘valid’) / n
return ret# Funcao de ativacao RELU
def fRelu(x):
ret = np.zeros(x.shape[0])
for i in range(len(x)):
ret[i] = max(x[i], 0.0015)
return ret# Funcao de ativacao SOFTMAX
def fSoftmax(x):
xs = x – x.max()
n = np.exp(xs)
d = np.sum(n)
ret = n/d
return ret# Calcula derivada – RELU
def fDerivadaRelu(x):
ret = np.zeros(x.shape[0])
for i in range(len(x)):
ret[i] = 1 if x[i] > 0 else 0.0015
return ret# Calcula derivada – Softmax
def fDerivadaSoftmax(x):
ret = np.zeros(3)
ret = x * (1 – x)
return ret# Erro pela entropia cruzada
def fCross_E(y_true, y_pred):
ret = (y_true * np.log10(y_pred))
ret = – np.sum(ret)
return retdef fDerivadaCross_E(y_true, y_pred):
return -y_true/(y_pred + 10**-100)# Erro médio quadrado
def fMSE(y_true, y_pred):
ret = -np.sum(1/2 * (y_true – y_pred) ** 2)
return ret# Split a dataset into k folds
def fBaseSplit(dataset, n_folds):
dataset_split = list()
dataset_copy = list(dataset)
fold_size = int(len(dataset) / n_folds)
for i in range(n_folds):
fold = list()
fold.append(dataset_copy[:fold_size])
dataset_split.append(fold)
dataset_copy = dataset_copy[fold_size:]
return dataset_splitdef fSalvaLog(a , t, x):
if (type(x) is np.ndarray):
x1 = np.array2string(x, formatter={‘float_kind’:lambda x: “%01.5f” % x})
elif ((type(x) is np.float64) or (type(x) is int)):
x1 = str(x)
else:
x1 = x
a.write(“\n\n” + t)
a.write(“\n” + x1)#===========================================================================
# Corpo do programa
# Formatacao da saida do print para notacao cientifica
np.set_printoptions(formatter={‘float’:lambda x: ‘%+01.9f ‘ % x})
#Abre arquivoo log.txtnarq = os.getcwd() + ‘\\Log\\Log’ + datetime.now().strftime(‘%Y%m%d%H%M’) + ‘.txt’
arq = open(narq, “w”)
# Carrega base de dados
arquivo = os.getcwd() + ‘\\Cotacoes.csv’
dataset = np.genfromtxt(arquivo, delimiter=’;’)df = fCalcular(dataset)
#np.savetxt(‘Calculados.csv’, df, fmt=’%01.5f’, header= ‘;Open;High;Low;Close;TickVol;Vol;Spread;Retorno;Buy;Hold;Sell;Candle;Corpo;DeltaMM9;DeltaMM21;DeltaMM200;DeltaVol;DeltaIFR14’, delimiter = ‘;’)
# Filtra base de dados
qTreino = np.int32(df.shape[0] * 0.8)
xTreino = df[:qTreino,1:]
xTeste = df[qTreino:,1:]cabec = ‘Buy;’
cabec += ‘Hold;’
cabec += ‘Sell;’
cabec += ‘CorpoCandle;’
cabec += ‘PavioCandle;’
cabec += ‘DistanciaMMA9;’
cabec += ‘DistanciaMMA21;’
cabec += ‘DistanciaMM200;’
cabec += ‘DeltaVolume;’
cabec += ‘IFR;’
cabec += ‘DIDIMais;’
cabec += ‘DIDIMenos;’
cabec += ‘ADXAlta;’
cabec += ‘ADXBaixa;’
cabec += ‘ADXTopoFundo;’
cabec += ‘DistanciaBandasBollinger’np.savetxt(“xTreino.csv”, xTreino, fmt=’%01.5f’, header=cabec, delimiter = “;”)
np.savetxt(“xTeste.csv”, xTeste, fmt=’%01.5f’, header=cabec, delimiter = “;”)# Executa o Modelo
lote = 10000 # ajuste do erro a cada 100 iteracoes
l_rate = 0.002 # taxa de aprendizado
mo = 0.9 # momentum
nEpocas = 10 # numero de epocas
boolArray = [
True, # Buy
True, # Hold
True, # Sell
True, # Corpo Candle
True, # Pavio Candle
True, # Close – MMA9
True, # Close – MMA21
True, # Close – MM200
True, # Delta do VOLUME
True, # IFR14
True, # DIDI (+)
True, # DIDI(-)
True, # ADX – Tendência de alta
True, # ADX – Tendência de baixa
True, # ADX – topo ou fundo
True # Distancia entre as banda de bollinger
]
ds = xTreino[:,boolArray] # dataset treinoni = ds.shape[1]-3 # nr de entradas
na = 12 # neurônios na primeira camada oculta
nb = 5 # neurônios na segunda camada oculta
no = 3 # nr de saídas#Inicializa a rede
p = cRedeNeural()
p.fCriarRede(ni, na, nb, no, l_rate)fSalvaLog(arq,’**’,’Rede: ‘+ str(ni).replace(‘.’,’,’) + ‘ – ‘ + str(na).replace(‘.’,’,’) + ‘ – ‘ + str(nb).replace(‘.’,’,’)+ ‘ – ‘ + str(no).replace(‘.’,’,’))
fSalvaLog(arq,’**’,’Lote: ‘ + str(lote).replace(‘.’,’,’) + ‘ – Learning rate: ‘ + str(l_rate).replace(‘.’,’,’) + ‘ – Épocas: ‘ + str(nEpocas).replace(‘.’,’,’))
fSalvaLog(arq,’Campos’,np.array(boolArray))
fSalvaLog(arq,’w_ia’,p.w_ia)
fSalvaLog(arq,’b_ia’,p.b_ia)
fSalvaLog(arq,’w_ab’,p.w_ab)
fSalvaLog(arq,’b_ab’,p.b_ab)
fSalvaLog(arq,’w_bo’,p.w_bo)
fSalvaLog(arq,’b_bo’,p.b_bo)erro = p.fTreinarRede(ds, nEpocas, lote, mo)
erro = np.array(erro)
erromedio = erro.mean()# Rotina de teste
ds = xTeste[:,boolArray]
eixoy_true = list()
eixoy_pred = list()
eixox = list()
cl = 0
for e in ds:
f1 = e[3:]
y_true = e[0:3]
y_pred = p.fPropagacao(f1)
eixoy_true.append(np.argmax(y_true))
eixoy_pred.append(np.argmax(y_pred))
eixox.append(cl)
cl +=1
print(str(cl) + ” – ” + str(np.argmax(y_true)) + ” – ” + str(np.argmax(y_pred)))np.savetxt(“ResultadoTeste.csv”, eixox, fmt=’%01.5f’, header=’Contador;True;Pred’, delimiter = “;”)
fSalvaLog(arq,’Erro médio:’,f”{erromedio:.5f}”)
fSalvaLog(arq,’w_ia’,p.w_ia)
fSalvaLog(arq,’b_ia’,p.b_ia)
fSalvaLog(arq,’w_ab’,p.w_ab)
fSalvaLog(arq,’b_ab’,p.b_ab)
fSalvaLog(arq,’w_bo’,p.w_bo)
fSalvaLog(arq,’b_bo’,p.b_bo)# Fechar arquivo log.txt
arq.close()plt.plot(erro,’r’)
# plt.scatter(eixox,eixoy_true, color=’b’, s=5, edgecolor=’none’)
# plt.scatter(eixox,eixoy_pred, color=’g’, s=5, edgecolor=’none’)
plt.show()#===========================================================================
29 de novembro de 2022 às 21:00 #38153Denny Ceccon
ModeradorSua implementação é bastante personalizada, e como debugar código escrito por outros desenvolvedores costuma dar trabalho, nós não temos como oferecer ajuda nestes casos particulares. Entretanto, me parece que você está tentando prever valores numéricos, e a função de perda escolhida é para valores categóricos. Procure utilizar uma função adequada, a mais utilizada nesses casos é RMSE.
29 de novembro de 2022 às 22:29 #38154Leonidas Mariano Pinheiro
ParticipanteBoa noite. Ok. Eu entendo a situação. Algumas considerações: não estou buscando prever valores numéricos, eu crio uma matriz one-hot com 3 dimensões que indicam “compra”, “reter” e “venda” (calculados a partir das cotações que encaminhei na postagem acima), essa matriz é comparada com a saída da softmax para obtenção do erro médio dos micro-lotes utilizados na entrada. Por isso utilizei a entropia cruzada.
Pode, pela sua experiência, me indicar alguma possível solução? Estou meio perdido com essa condição da convergência para 33% em cada um dos neurônios de saída, pois não consegui localizar nenhum material que trate de situação parecida.
Eu ficaria agradecido pela sua ajuda!!
Atenciosamente.
Leo.
30 de novembro de 2022 às 09:09 #38155Denny Ceccon
ModeradorO mais provável é algum erro de implementação mesmo. Você poderia tentar simplificar sua arquitetura para debug, e rodar passo a passo pra ter certeza de que os cálculos estão corretos.
Se você só está interessado em um algoritmo funcional, por que não usa alguma biblioteca com a implementação pronta? Para implementações mais simples, poderia utilizar o Scikit-Learn mesmo, ou se quiser ter mais controle sobre a arquitetura poderia usar o Keras.
- AutorPosts
- Você deve fazer login para responder a este tópico.