machinelearning-mnpkllps 0.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,611 @@
1
+ import numpy as np
2
+ from metricas import dist
3
+
4
+ # Verifica se os dados de input para treino possuem algum erro
5
+ def train_check(X: np.ndarray, Y: np.ndarray) -> None:
6
+ if not isinstance(X, np.ndarray):
7
+ raise TypeError("X must be numpy array")
8
+ if not isinstance(Y, np.ndarray):
9
+ raise TypeError("Y must be numpy array")
10
+ if X.ndim != 2:
11
+ raise ValueError("X must be 2D array")
12
+ if Y.ndim != 1:
13
+ raise ValueError("Y must be 1D array")
14
+ if X.shape[0] != Y.shape[0]:
15
+ raise ValueError("X and Y must have the same lenght")
16
+ if X.shape[0] == 0:
17
+ raise ValueError("Data must be not empty")
18
+ if X.shape[1] == 0:
19
+ raise ValueError("Samples must have at least one feature")
20
+ if not np.issubdtype(X.dtype, np.number):
21
+ raise TypeError("X must contain numeric values")
22
+
23
+ def predict_element_check(P: np.ndarray, n_features: int) -> None:
24
+ if P.ndim != 1:
25
+ raise ValueError("Input must be 1D")
26
+ if P.shape[-1] != n_features:
27
+ raise ValueError("Test and train must contain same number of features")
28
+ if not np.issubdtype(P.dtype, np.number):
29
+ raise TypeError("Input must contain numerical values")
30
+
31
+ # Converte um valor real em um valor no intervalo (0, 1)
32
+ def sigmoid(z: float) -> float:
33
+ return 1/(1+np.exp(-z))
34
+
35
+ # Versão do sigmoide para mais variáveis (scores). A soma total de probabilities deve ser 1.
36
+ def softmax(scores: np.ndarray) -> np.ndarray:
37
+ exp_scores = np.exp(scores)
38
+ return exp_scores / np.sum(exp_scores, axis=-1, keepdims=True)
39
+
40
+ class KNN():
41
+ """
42
+ O modelo de K nearest neighbors, para um K escolhido
43
+ """
44
+
45
+ def __init__(self, K: int = 5, p=2, normalize=False):
46
+ """
47
+ Inicia o modelo KNN
48
+ --------------
49
+ K: Inteiro que representa o tamanho da vizinhança observada em cada dado. Dessa vizinhança, a maioria elegerá a classe prevista.
50
+
51
+ p: A norma Lp (Σ Pi^p)^(1/p). Por padrão, a distância euclidiana dos pontos (L2).
52
+ normalize: Caso queira manter um desvio padrão consistente para todo preditor, torne isso True. Os dados serão normalizados para média 0 e variância 1.
53
+ """
54
+
55
+ if not isinstance(K, (int, np.integer)):
56
+ raise TypeError("K must be integer number")
57
+ if K <= 0:
58
+ raise ValueError("K must be positive number")
59
+
60
+ self.K = K
61
+ self.X = None
62
+ self.Y = None
63
+ self.p = p
64
+ self.normalize = normalize
65
+ self.standard = None
66
+ self.mean = None
67
+
68
+ def distance(self, P1: np.ndarray, P2: np.ndarray) -> float:
69
+ return dist(P1, P2, self.p)
70
+
71
+ def train(self, X: np.ndarray, Y: np.ndarray) -> None:
72
+ """
73
+ Treina o modelo KNN
74
+ -----------
75
+ X: array com todos os preditores dos dados de treino.
76
+
77
+ Y: array com todas as classes observadas nos dados de treino. Deve ser compatível com a dimensão de X.
78
+ """
79
+
80
+ train_check(X, Y)
81
+ if self.normalize:
82
+ self.mean = X.mean(axis=0)
83
+ self.standard = X.std(axis=0)
84
+ self.X = (X - self.mean) / self.standard
85
+ else:
86
+ self.X = X
87
+ self.Y = Y
88
+
89
+ def predict_element(self, P: np.ndarray):
90
+ if self.X is None:
91
+ raise TypeError("The model must be trained")
92
+
93
+ predict_element_check(P, self.X.shape[1])
94
+
95
+ if self.normalize:
96
+ P = (P-self.mean)/self.standard
97
+
98
+ neighborhood = []
99
+
100
+ for i in range(len(self.X)):
101
+ d = self.distance(P, self.X[i])
102
+ if i < self.K:
103
+ neighborhood.append((d, self.Y[i]))
104
+ else:
105
+ index_max_distance = np.argmax([neighbor[0] for neighbor in neighborhood])
106
+ if d < neighborhood[index_max_distance][0]:
107
+ neighborhood[index_max_distance] = (d, self.Y[i])
108
+
109
+ classes = np.array([neighbor[1] for neighbor in neighborhood])
110
+ values, counting = np.unique(classes, return_counts=True)
111
+ return values[np.argmax(counting)]
112
+
113
+ def predict(self, L: np.ndarray) -> np.ndarray:
114
+ """
115
+ Retorna a previsão do KNN para os dados novos. O modelo deve ter sido treinado antes das previsões.
116
+ -----------
117
+ L: array de novos dados com seus preditores
118
+ """
119
+ predictions = []
120
+ for P in L:
121
+ predictions.append(self.predict_element(P))
122
+ return np.array(predictions)
123
+
124
+
125
+ class Logistic_Regression():
126
+ """
127
+ O modelo de Regressão Logística, com intercepto opcional
128
+ """
129
+
130
+ def __init__(self, learning_rate: float = 0.01, iterations: int = 1000, intercept=True):
131
+ """
132
+ Inicia o modelo de Regressão Logística
133
+
134
+ learning_rate: Valor do passo na descida de gradiente. Se muito pequeno, pode demorar a convergir ou não chegar ao ótimo. Se muito alto, pode divergir.
135
+ iterations: Número de passos que o algoritmo da descida de gradiente executa.
136
+ intercept: Pode ser interpretado como um viés natural para cada classe ser mais ou menos provável. Torne False se quiser desativá-lo.
137
+ """
138
+
139
+ self.beta = None
140
+ self.beta0 = None
141
+ self.values = None
142
+ self.alpha = learning_rate
143
+ self.iter = iterations
144
+ self.intercept = intercept
145
+
146
+ def train(self, X: np.ndarray, Y: np.ndarray) -> None:
147
+ """
148
+ Treina o modelo de Regressão Logística
149
+
150
+ X: array com todos os preditores dos dados de treino.
151
+ Y: array com todas as classes observadas nos dados de treino. Deve ser compatível com a dimensão de X.
152
+ """
153
+
154
+ train_check(X, Y)
155
+
156
+ # Usa a decida de gradiente para estimar beta
157
+ n_samples, n_parameters = X.shape
158
+ self.values = np.unique(Y)
159
+ n_classes = len(self.values)
160
+ self.beta = np.zeros((n_parameters, n_classes))
161
+ self.beta0 = np.zeros(n_classes) if self.intercept else None
162
+
163
+ # Cria uma matriz one hot samples x classes. Marca apenas uma coluna por linha (a classs observada)
164
+ indices = np.searchsorted(self.values, Y)
165
+ T = np.eye(n_classes)[indices]
166
+
167
+ for _ in range(self.iter):
168
+
169
+ Z = X @ self.beta # Soma feita através de broadcast. O resultado é uma matriz de mesmo shape que T
170
+ if self.intercept:
171
+ Z += self.beta0
172
+ P = np.array([softmax(z) for z in Z]) # Apenas traduz score para probabilidade em cada linha
173
+
174
+ # Fórmula dos gradientes. O termo P-T pode ser interpretado como a distância na previsão
175
+ grad_beta = X.T @ (P - T) / n_samples
176
+ grad_beta0 = np.mean(P - T, axis=0)
177
+
178
+ # Atualização dos coeficientes. o processo deve repetir a partir daqui.
179
+ self.beta -= self.alpha * grad_beta
180
+ if self.intercept:
181
+ self.beta0 -= self.alpha * grad_beta0
182
+
183
+ def predict_probabilities(self, X: np.ndarray) -> np.ndarray:
184
+ Z = X @ self.beta
185
+ if self.intercept:
186
+ Z += self.beta0
187
+ return softmax(Z)
188
+
189
+ def predict_element(self, P: np.ndarray):
190
+ predict_element_check(P, self.beta.shape[0])
191
+ ind = np.argmax(self.predict_probabilities(P))
192
+ return self.values[ind]
193
+
194
+ def predict(self, L: np.ndarray) -> np.array:
195
+ """
196
+ Retorna a previsão da Regressão Logística para os dados novos. O modelo deve ter sido treinado antes das previsões.
197
+
198
+ L: array de novos dados com seus preditores
199
+ """
200
+
201
+ if self.beta is None:
202
+ raise TypeError("The model must be trained")
203
+ predictions = []
204
+ for P in L:
205
+ predictions.append(self.predict_element(P))
206
+ return np.array(predictions)
207
+
208
+
209
+ class LDA():
210
+ """
211
+ O modelo de Análise de Discriminante Linear
212
+ """
213
+
214
+ def __init__(self):
215
+ """
216
+ Inicia o modelo de QDA. Não há parâmetros de input para essa classe.
217
+ """
218
+ self.log_pi = None
219
+ self.means = None
220
+ self.sigma_inv = None
221
+ self.values = None
222
+
223
+ def train(self, X: np.ndarray, Y: np.ndarray) -> None:
224
+ """
225
+ Treina o modelo LDA
226
+
227
+ X: array com todos os preditores dos dados de treino.
228
+ Y: array com todas as classes observadas nos dados de treino. Deve ser compatível com a dimensão de X.
229
+ """
230
+
231
+ train_check(X, Y)
232
+
233
+ # Toma a frequência de cada classe como a probabilidade desta ocorrer
234
+ values, counting = np.unique(Y, return_counts=True)
235
+ log_pi = np.log(counting/len(Y))
236
+
237
+ # Toma a média das features em cada classe
238
+ means = []
239
+ for k in values:
240
+ Xk = X[Y==k]
241
+ mean = np.mean(Xk, axis=0)
242
+ means.append(mean)
243
+ means = np.array(means)
244
+
245
+ # Cria a matriz de covariância sigma a partir dos dados
246
+ if len(Y) <= len(values):
247
+ raise ValueError("There's no suficcient information to training LDA (need more samples than classes)")
248
+ sigma_dim = np.shape(X)[1]
249
+ sigma = np.zeros((sigma_dim, sigma_dim))
250
+
251
+ for i, k in enumerate(values):
252
+ Xk = X[Y == k]
253
+ X_centered = Xk - means[i]
254
+ sigma += X_centered.T @ X_centered
255
+ sigma /= len(Y) - len(values)
256
+
257
+ self.log_pi = log_pi
258
+ self.means = means
259
+ self.sigma_inv = np.linalg.inv(sigma)
260
+ self.values = values
261
+
262
+ def aux(self, X, sigma_inv, mean, log_freq):
263
+ # Essa fórmula é a que deve ser maximizada pelo LDA
264
+ return (X.T - 1/2 * mean.T) @ sigma_inv @ mean + log_freq
265
+
266
+ def predict_element(self, P: np.ndarray):
267
+ predict_element_check(P, self.means.shape[1])
268
+
269
+ # Busca a classe que maximiza aux para esses dados novos
270
+ vector_aux = np.zeros(len(self.values))
271
+
272
+ for i in range(len(self.values)):
273
+ vector_aux[i] = self.aux(P, self.sigma_inv, self.means[i], self.log_pi[i])
274
+
275
+ return self.values[np.argmax(vector_aux)]
276
+
277
+ def predict(self, L: np.ndarray) -> np.array:
278
+ """
279
+ Retorna a previsão do LDA para os dados novos. O modelo deve ter sido treinado antes das previsões.
280
+
281
+ L: array de novos dados com seus preditores
282
+ """
283
+
284
+ if self.means is None:
285
+ raise TypeError("The model must be trained")
286
+
287
+ predictions = []
288
+ for P in L:
289
+ predictions.append(self.predict_element(P))
290
+ return np.array(predictions)
291
+
292
+
293
+ class QDA():
294
+ """
295
+ O modelo de Análise de Discriminante Quadrática
296
+ """
297
+ def __init__(self):
298
+ """
299
+ Inicia o modelo de QDA. Não há parâmetros de input para essa classe.
300
+ """
301
+ self.log_pi = None
302
+ self.means = None
303
+ self.sigmas = None
304
+ self.sigmas_inv = None
305
+ self.values = None
306
+
307
+ def train(self, X: np.ndarray, Y: np.ndarray) -> None:
308
+ """
309
+ Treina o modelo QDA
310
+
311
+ X: array com todos os preditores dos dados de treino.
312
+ Y: array com todas as classes observadas nos dados de treino. Deve ser compatível com a dimensão de X.
313
+ """
314
+
315
+ train_check(X, Y)
316
+
317
+ # Toma a frequência de cada classe como a probabilidade desta ocorrer
318
+ values, counting = np.unique(Y, return_counts=True)
319
+ if np.any(counting < 2):
320
+ raise ValueError("All the classes must have at least 2 samples")
321
+ log_pi = np.log(counting/len(Y))
322
+
323
+ # Toma a média das features em cada classe
324
+ means = []
325
+ for k in values:
326
+ Xk = X[Y==k]
327
+ mean = np.mean(Xk, axis=0)
328
+ means.append(mean)
329
+ means = np.array(means)
330
+
331
+ # Cria as matrizes de covariância para cada classe
332
+ sigmas = []
333
+ for i, k in enumerate(values):
334
+ Xk = X[Y == k]
335
+ X_centered = Xk - means[i]
336
+ sigma_k = X_centered.T @ X_centered / (len(Xk)-1)
337
+ sigmas.append(sigma_k)
338
+
339
+ sigmas_inv = []
340
+ for sigma in sigmas:
341
+ try:
342
+ sigma_inv = np.linalg.inv(sigma)
343
+ sigmas_inv.append(sigma_inv)
344
+ except:
345
+ raise ValueError("Some class didn't generate an invertible covariance matrix")
346
+
347
+
348
+ self.log_pi = log_pi
349
+ self.means = means
350
+ self.sigmas = np.array(sigmas)
351
+ self.sigmas_inv = np.array(sigmas_inv)
352
+ self.values = values
353
+ # Necessário verificar possibilidade de erro na inversão
354
+
355
+ def aux(self, X, sigma, sigma_inv, mean, log_freq):
356
+ # Essa fórmula é a que deve ser maximizada pelo QDA
357
+ return -1/2 * (X - mean).T @ sigma_inv @ (X - mean) + log_freq - 1/2 * np.log(np.linalg.det(sigma))
358
+
359
+ def predict_element(self, P: np.ndarray):
360
+ predict_element_check(P, self.means.shape[1])
361
+
362
+ # Busca a classe que maximiza aux para esses dados novos
363
+ vector_aux = np.zeros(len(self.values))
364
+
365
+ for i in range(len(self.values)):
366
+ vector_aux[i] = self.aux(P, self.sigmas[i], self.sigmas_inv[i], self.means[i], self.log_pi[i])
367
+
368
+ return self.values[np.argmax(vector_aux)]
369
+
370
+ def predict(self, L: np.ndarray) -> np.array:
371
+ """
372
+ Retorna a previsão do QDA para os dados novos. O modelo deve ter sido treinado antes das previsões.
373
+
374
+ L: array de novos dados com seus preditores
375
+ """
376
+ if self.means is None:
377
+ raise TypeError("The model must be trained")
378
+
379
+ predictions = []
380
+ for P in L:
381
+ predictions.append(self.predict_element(P))
382
+ return np.array(predictions)
383
+
384
+
385
+ class SVM():
386
+ """
387
+ O modelo de Support Vector Machine linear com margem flexível, treinado por descida do subgradiente no primal.
388
+ Com mais de duas classes, usa a estratégia um-contra-um (one-vs-one).
389
+ """
390
+ def __init__(self, C: float = 1.0, learning_rate: float = 0.01, max_iterations: int = 1000):
391
+ """
392
+ Inicia o modelo SVM
393
+
394
+ C: Peso da penalidade por violar a margem.
395
+ learning_rate: Valor do passo na descida do subgradiente.
396
+ max_iterations: Número de passos (cada um usa todos os dados de treino) que o algoritmo executa.
397
+ """
398
+
399
+ if C <= 0:
400
+ raise ValueError("C must be positive number")
401
+ if learning_rate <= 0:
402
+ raise ValueError("learning_rate must be positive number")
403
+ if not isinstance(max_iterations, (int, np.integer)):
404
+ raise TypeError("max_iterations must be integer number")
405
+ if max_iterations <= 0:
406
+ raise ValueError("max_iterations must be positive number")
407
+
408
+ self.C = C
409
+ self.alpha = learning_rate
410
+ self.max_iterations = max_iterations
411
+ self.values = None
412
+ self.n_features = None
413
+ self.machines = None # Lista de (i, j, w, b): SVM binário entre as classes values[i] (-1) e values[j] (+1)
414
+
415
+ def train_binary(self, X: np.ndarray, y: np.ndarray):
416
+ # Minimiza J(w,b) = 1/2 ||w||^2 + C/N Σ max(0, 1 - y_i (w^T x_i + b)). Os rótulos y são -1 ou +1.
417
+ N, n_parameters = X.shape
418
+ w = np.zeros(n_parameters)
419
+ b = 0.0
420
+
421
+ for _ in range(self.max_iterations):
422
+ # Margens e conjunto M dos exemplos que violam a margem: y_i (w^T x_i + b) < 1
423
+ # influencia que hinge loss penaliza pontos que não respeitam a margem funcional de 1.
424
+ margins = y * (X @ w + b)
425
+ M = margins < 1
426
+
427
+ # Subgradientes de J
428
+ grad_w = w - (self.C / N) * (y[M] @ X[M])
429
+ grad_b = -(self.C / N) * np.sum(y[M])
430
+
431
+ w = w - self.alpha * grad_w
432
+ b = b - self.alpha * grad_b
433
+
434
+ return w, b
435
+
436
+ def train(self, X: np.ndarray, Y: np.ndarray) -> None:
437
+ """
438
+ Treina o modelo SVM
439
+
440
+ X: array com todos os preditores dos dados de treino.
441
+ Y: array com todas as classes observadas nos dados de treino. Deve ser compatível com a dimensão de X. Precisa conter pelo menos 2 classes.
442
+ """
443
+
444
+ train_check(X, Y)
445
+
446
+ values = np.unique(Y)
447
+
448
+ X = X.astype(float)
449
+
450
+ # Um SVM binário para cada par de classes (i < j). A classe values[i] vira -1 e values[j] vira +1.
451
+ machines = []
452
+ for i in range(len(values)):
453
+ for j in range(i + 1, len(values)):
454
+ pair = (Y == values[i]) | (Y == values[j]) #pega apneas as amostras das duas classes atuais
455
+ y = np.where(Y[pair] == values[j], 1.0, -1.0) #colocando rótulos 1 e -1
456
+ w, b = self.train_binary(X[pair], y)
457
+ machines.append((i, j, w, b))
458
+
459
+ self.values = values
460
+ self.n_features = X.shape[1]
461
+ self.machines = machines
462
+
463
+ def predict_element(self, P: np.ndarray):
464
+ if self.machines is None:
465
+ raise TypeError("The model must be trained")
466
+
467
+ predict_element_check(P, self.n_features)
468
+
469
+ return self.predict(P.reshape(1, -1))[0]
470
+
471
+ def predict(self, L: np.ndarray) -> np.ndarray:
472
+ """
473
+ Retorna a previsão do SVM para os dados novos. O modelo deve ter sido treinado antes das previsões.
474
+ Cada SVM binário vota em uma de suas duas classes, e vence a classe com mais votos (em empate, a menor na ordem crescente das classes).
475
+
476
+ L: array de novos dados com seus preditores
477
+ """
478
+
479
+ if self.machines is None:
480
+ raise TypeError("The model must be trained")
481
+
482
+ for P in L:
483
+ predict_element_check(P, self.n_features)
484
+
485
+ votes = np.zeros((len(L), len(self.values)), dtype=int)
486
+ for i, j, w, b in self.machines:
487
+ # w^T x + b >= 0 vota em values[j]; caso contrário, em values[i]
488
+ winners = np.where(L @ w + b >= 0, j, i)
489
+ votes[np.arange(len(L)), winners] += 1 #arange cria um vetor com indices de L
490
+
491
+ return self.values[np.argmax(votes, axis=1)]
492
+ # -----TESTES-----
493
+ print("Sigmoide(0) = ", sigmoid(0))
494
+ print("Sigmoide(1) = ", sigmoid(1))
495
+ print("Sigmoide(-1) = ", sigmoid(-1))
496
+
497
+ n = np.array([1, 1, 1])
498
+ print("Softmax(1, 1, 1) = ", softmax(n))
499
+ n = np.array([2, 1, 1])
500
+ print("Softmax(2, 1, 1) = ", softmax(n))
501
+ n = np.array([1, 2, 2])
502
+ print("Softmax(1, 2, 2) = ", softmax(n))
503
+ n = np.array([1, 3, 1])
504
+ print("Softmax(1, 3, 1) = ", softmax(n))
505
+ n = np.array([1.5, 1, 0, -1, -1.5])
506
+ print("Softmax(1.5, 1, 0, -1, -1.5) = ", softmax(n))
507
+ n = np.array([5])
508
+ print("Softmax(5) = ", softmax(n))
509
+ n = np.array([1, 1.01])
510
+ print("Softmax(1, 1.01) = ", softmax(n))
511
+ print()
512
+
513
+ modelRL = Logistic_Regression()
514
+ modelRLNI = Logistic_Regression(intercept = False)
515
+ modelRLHL = Logistic_Regression(learning_rate = 1)
516
+ modelRLFI = Logistic_Regression(iterations = 2)
517
+ modelKNN5 = KNN()
518
+ modelKNN1 = KNN(K=1)
519
+ modelKNN10 = KNN(K=10)
520
+ modelLDA = LDA()
521
+ modelQDA = QDA()
522
+ modelSVM = SVM()
523
+ modelSVMHC = SVM(C=10)
524
+ modelSVMFI = SVM(max_iterations=2)
525
+
526
+ # Para os testes abaixo, o LDA e o QDA foram testados separadamente nos itens em que se aplicam
527
+ def test(X, Y, X_teste):
528
+ modelRL.train(X, Y)
529
+ modelRLNI.train(X, Y)
530
+ modelRLHL.train(X, Y)
531
+ modelRLFI.train(X, Y)
532
+ modelKNN5.train(X, Y)
533
+ modelKNN1.train(X, Y)
534
+ modelKNN10.train(X, Y)
535
+ modelSVM.train(X, Y)
536
+ modelSVMHC.train(X, Y)
537
+ modelSVMFI.train(X, Y)
538
+
539
+ print("Regressão Logística: ", modelRL.predict(X_teste))
540
+ print("Regressão Logística sem intercepto: ", modelRLNI.predict(X_teste))
541
+ print("Regressão Logística com alto learning rate: ", modelRLHL.predict(X_teste))
542
+ print("Regressão Logística com poucas iterações: ", modelRLFI.predict(X_teste))
543
+ print("KNN com K=5: ", modelKNN5.predict(X_teste))
544
+ print("KNN com K=1: ", modelKNN1.predict(X_teste))
545
+ print("KNN com K=10: ", modelKNN10.predict(X_teste))
546
+ print("SVM: ", modelSVM.predict(X_teste))
547
+ print("SVM com C=10: ", modelSVMHC.predict(X_teste))
548
+ print("SVM com poucas iterações: ", modelSVMFI.predict(X_teste))
549
+
550
+ # Vários pontos
551
+ X = np.array([[0, 0], [1, -0.5], [2, 2], [3, 5], [-3, 10], [4, 2.5], [1.5, 3.5], [-1, 1.5], [-3, 2.5], [-2.5, -0.5], [-0.5, -1], [-2, 7], [-2.5, 6]])
552
+ Y = np.array(["Red", "Red", "Blue", "Blue", "Green", "Blue", "Blue", "Red", "Red", "Red", "Red", "Green", "Green"])
553
+ X_teste = np.array([[0, 0], [3, 3], [-2, 0], [-4, 6], [0, -4]])
554
+ test(X, Y, X_teste)
555
+ modelLDA.train(X, Y)
556
+ modelQDA.train(X, Y)
557
+ print("LDA: ", modelLDA.predict(X_teste))
558
+ print("QDA: ", modelQDA.predict(X_teste))
559
+ print()
560
+
561
+ # Pontos em uma linha reta
562
+ X = np.array([[1, 2], [0.6, 2], [1, 2], [0.7, 2], [-10, 2], [0, 2]])
563
+ Y = np.array(["ONE", "TWO", "ONE", "TWO", "THREE", "THREE"])
564
+ X_teste = np.array([[0.5, 1.9], [1.1, 2.7], [0, 2.1], [-1, 2.3], [0.3, 1.6]])
565
+ test(X, Y, X_teste)
566
+ print()
567
+
568
+ # Pontos em uma linha quase reta
569
+ X = np.array([[1, 2], [0.6, 2.1], [1, 2.1], [0.7, 2], [-10, 1.9], [0, 2.2]])
570
+ Y = np.array(["ONE", "TWO", "ONE", "TWO", "THREE", "THREE"])
571
+ X_teste = np.array([[0.5, 1.9], [1.1, 2.7], [0, 2.1], [-1, 2.3], [0.3, 1.6]])
572
+ test(X, Y, X_teste)
573
+ modelLDA.train(X, Y)
574
+ print("LDA: ", modelLDA.predict(X_teste))
575
+ print()
576
+
577
+ # Um único ponto
578
+ X = np.array([[8, -7]])
579
+ Y = np.array(["Only"])
580
+ X_teste = np.array([[0, 0], [1, 1], [2, 2], [3, 3]])
581
+ test(X, Y, X_teste)
582
+ print()
583
+
584
+ # Prevendo mudança em uma reta
585
+ X = np.array([[-1.51, -1.49], [-1, -1], [1, 1]])
586
+ Y = np.array(["A", "A", "B"])
587
+ X_teste = np.array([[0.9, 0.9], [0.5, 0.5], [0.25, 0.25], [0.1, 0.1], [0, 0], [-0.1, -0.1], [-0.25, -0.25], [-0.5, -0.5], [-0.9, -0.9]])
588
+ test(X, Y, X_teste)
589
+ modelLDA.train(X, Y)
590
+ print("LDA: ", modelLDA.predict(X_teste))
591
+ print()
592
+
593
+ # Pontos iguais em classes diferentes
594
+ X = np.array([[0, 0], [0, 0], [1, 2], [1, 2], [-1, -1], [-1, -1]])
595
+ Y = np.array(["Equal", "Identical", "Equal", "Identical", "Equal", "Identical"])
596
+ X_teste = np.array([[5, -3], [0, 0]])
597
+ test(X, Y, X_teste)
598
+ modelLDA.train(X, Y)
599
+ print("LDA: ", modelLDA.predict(X_teste))
600
+ print()
601
+
602
+ # Teste métrica KNN
603
+ modelKNN2 = KNN(2)
604
+ modelKNN2sigma = KNN(2, normalize=True)
605
+ X = np.array([[10, 10], [11, 11], [8, 9], [0, 0], [1, -1], [-1000, 1]])
606
+ Y = np.array(["Direito", "Direito", "Direito", "Esquerdo", "Esquerdo", "Esquerdo"])
607
+ X_teste = np.array([[11, 1]])
608
+ modelKNN2.train(X, Y)
609
+ modelKNN2sigma.train(X, Y)
610
+ print(modelKNN2.predict(X_teste))
611
+ print(modelKNN2sigma.predict(X_teste))
@@ -0,0 +1,86 @@
1
+ import numpy as np
2
+ from typing import Literal
3
+
4
+
5
+ def sigmoid(x: float | np.ndarray) -> np.ndarray:
6
+ x = np.array(x)
7
+
8
+ return 1 / (1 + np.exp(-x))
9
+
10
+
11
+ def sigmoid_diff(x: float | np.ndarray) -> np.ndarray:
12
+ x = np.array(x)
13
+
14
+ s = sigmoid(x)
15
+ return s * (1 - s)
16
+
17
+
18
+ def tanh(x: float | np.ndarray) -> np.ndarray:
19
+ x = np.array(x)
20
+
21
+ return np.tanh(x)
22
+
23
+
24
+ def tanh_diff(x: float | np.ndarray) -> np.ndarray:
25
+ x = np.array(x)
26
+
27
+ return 1 - tanh(x)**2
28
+
29
+
30
+ def ReLU(x: float | np.ndarray) -> np.ndarray:
31
+ x = np.array(x)
32
+
33
+ return np.maximum(0, x)
34
+
35
+
36
+ def ReLU_diff(x: float | np.ndarray) -> np.ndarray:
37
+ x = np.array(x)
38
+
39
+ return (x > 0).astype(int)
40
+
41
+
42
+ def identity(x: float | np.ndarray) -> np.ndarray:
43
+ x = np.array(x)
44
+
45
+ return x
46
+
47
+
48
+ def identity_diff(x: float | np.ndarray) -> np.ndarray:
49
+ x = np.array(x)
50
+
51
+ return np.ones_like(x)
52
+
53
+
54
+ functions = {
55
+ 'sigmoid': (sigmoid, sigmoid_diff),
56
+ 'tanh': (tanh, tanh_diff),
57
+ 'ReLU': (ReLU, ReLU_diff),
58
+ 'identity': (identity, identity_diff)
59
+ }
60
+
61
+ functions_name = Literal['identity', 'ReLU', 'sigmoid', 'tanh']
62
+
63
+ if __name__ == "__main__":
64
+ x1, x2 = 0, np.pi
65
+
66
+ print(f'Teste das funções para x1 = {x1}')
67
+ print(f'{sigmoid(x1) = }')
68
+ print(f'{sigmoid_diff(x1) = }')
69
+ print(f'{tanh(x1) = }')
70
+ print(f'{tanh_diff(x1) = }')
71
+ print(f'{ReLU(x1) = }')
72
+ print(f'{ReLU_diff(x1) = }')
73
+ print(f'{identity(x1) = }')
74
+ print(f'{identity_diff(x1) = }')
75
+ print()
76
+
77
+ print(f'Teste das funções para x2 = {x2}')
78
+ print(f'{sigmoid(x2) = }')
79
+ print(f'{sigmoid_diff(x2) = }')
80
+ print(f'{tanh(x2) = }')
81
+ print(f'{tanh_diff(x2) = }')
82
+ print(f'{ReLU(x2) = }')
83
+ print(f'{ReLU_diff(x2) = }')
84
+ print(f'{identity(x2) = }')
85
+ print(f'{identity_diff(x2) = }')
86
+ print()