-
Notifications
You must be signed in to change notification settings - Fork 0
/
KNN_CV.m
307 lines (250 loc) · 10.2 KB
/
KNN_CV.m
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
% Universidade Federal do Ceará - UFC
% Reconhecimento de Padrões - 2020.1
% Francisco Igor Felício Linhares - 374874
% K Nearest Neighbors(KNN) e Nearest Prototype Classifier(NPC)
% implementados e avaliados com Cross-Validation
% Comando para ver tempo de execução do script
tic;
% Inicializando o ambiente de trabalho
clc;
clear;
close all;
% Carregando a base de dados
load('data/Classe1.mat');
load('data/Classe2.mat');
Classe1 = Classe1';
Classe2 = Classe2';
% Inicializando a matriz de características.
features1 = zeros(length(Classe1(:,1)), 6);
features2 = ones(length(Classe2(:,1)), 6);
% A última coluna representa a classe do sinal:
% 0 - Classe1 e 1 - Classe2
% Visualizando as amostras
figure('Name', 'Visualizando sinais')
for i=1:4
subplot(2,2, i)
plot(Classe1(i,:))
hold on
plot(Classe2(i,:))
end
% Extraindo atributos de cada amostra
for i=1:length(Classe1(:,1))
% Extraindo a média
features1(i,1) = mean(Classe1(i,:));
features2(i,1) = mean(Classe2(i,:));
% Extraindo o desvio padrão
features1(i,2) = std(Classe1(i,:));
features2(i,2) = std(Classe2(i,:));
% Extraindo a kurtose
features1(i,3) = kurtosis(Classe1(i,:));
features2(i,3) = kurtosis(Classe2(i,:));
% Extraindo a assimetria
features1(i,4) = skewness(Classe1(i,:));
features2(i,4) = skewness(Classe2(i,:));
% Extraindo a amplitude
features1(i,5) = max(Classe1(i,:)) - min(Classe1(i,:));
features2(i,5) = max(Classe2(i,:)) - min(Classe2(i,:));
end
% Nomalizando os atributos de forma que tenham média 0 e variância 1 (z-score)
normalized_features = normalize([features1(:,1:end-1);features2(:,1:end-1)]);
% Adicionando a coluna target
normalized_features = [normalized_features [features1(:,end);features2(:,end)]];
% Plotando os dados
% Nome dos eixos/atributos
feature_names = {'Mean', 'Standard Deviation', 'Kurtosis', 'Skewness', 'Amplitude'};
% Gerando scatterplot em pares
figure('Name', 'Gráficos de Dispersão e Histogramas atributos normalizados');
pairplot(normalized_features, feature_names)
% Testando os classificadores com os dados normalizados
[results_knn, results_npc] = cross_validation(normalized_features, 10, 80);
% Results knn é uma matriz, onde as linhas são os resultados em cada 'fold'
% e as colunas são os resultados para cada valor k, então utilizarei o
% resultado médio para os resultados de cada valor de k
results_knn = mean(results_knn);
% Plotando acurácias do KNN para diferentes valores de k
figure('Name', 'Resultados KNN');
plot(results_knn)
title('Taxa de acertos nas previsões com KNN')
ylabel('Precisão')
xlabel('Quantidade de vizinhos')
% Plotando os resultados do NPC para cada 'fold'
figure('Name', 'Resultados NPC');
plot(results_npc)
title('Taxa de acertos com NPC nas previsões em cada divisão')
ylabel('Precisão')
xlabel('Fold')
% Parando a verificação do tempo de execução
toc;
% --------------------------- FUNÇÕES -------------------------------------
% Função que faz a plotagem em pares, recebe como parâmetro a matriz de
% atributos e os nomes dos atributos.
function pairplot(dataset, feature_names)
m = length(dataset(1,:))-1; % Número de atributos
% Laço que povoa os subplots
for i=1:m
for j=1:m
% Condicional que assegura que não terão gráficos repetidos
if i <= j
% Condicional que plota os scatterplots dos atributos ixj
if i ~= j
subplot(m, m, m*(j-1)+i);
final = length(dataset)/2;
plot(dataset(1:final, i), dataset(1:final, j), '.')
hold on;
begin = final+1;
final = length(dataset);
plot(dataset(begin:final, i), dataset(begin:final, j), '.')
else
% No caso de colunas com i igual à j, é plotado o
% histograma daquele atributo
subplot(m, m, m*(j-1)+i);
final = length(dataset)/2;
histogram(dataset(1:final, i), 10);
hold on;
begin = final+1;
final = length(dataset);
histogram(dataset(begin:final, i), 10);
end
% Adiciona a label no eixo vertical apenas na primeira
% coluna de gráficos
if i == 1
ylabel(feature_names(j))
end
end
end
% Adiciona label no eixo horizontal apenas na última linha de gráficos
if j == 5
xlabel(feature_names(i))
end
end
hold off;
end
% Função que calcula a distância euclidiana entre 2 vetores de atributos
function dist = euclidean_distance(v1, v2)
dist = sqrt(sum((v1-v2).^2));
end
% Função que retorna o vetor de previsões e o zre obtido do dataset de
% teste com base no dataset de treino avaliando os k vizinhos mais próximos
function score = knn(train, test, k)
% inicializando vetor de previsões
predictions = zeros(length(test), 1);
% Calculando a distância de cada vetor de atributos de teste para todos
% os vetores de atributos de treino
for i=1: length(test)
classe1 = 0;
classe2 = 0;
dists = zeros(length(train), 1);
for j=1: length(train)
% Calcula a distância euclidiana
dists(j) = euclidean_distance(test(i,1:end-1), train(j,1:end-1));
% Ordena o vetor de distâncias e retorna as distâncias
% ordenadas e os índices de ordenação
[~ , Indexes_sorted] = sort(dists);
% Conta as classes dos k vizinhos mais próximos
classe1 = sum(train(Indexes_sorted(1:k), end) == 0);
classe2 = sum(train(Indexes_sorted(1:k), end) == 1);
% Certifica-se que não houve empate entre as classes
if classe1 == classe2
classe1 = sum(train(Indexes_sorted(1:k-1), end) == 0);
classe2 = sum(train(Indexes_sorted(1:k-1), end) == 1);
end
end
% Atribui a classe mais frequente entre os k vizinhos ao vetor de
% teste
if classe1 > classe2
predictions(i) = 0;
else
predictions(i) = 1;
end
end
% Calcula a precisão das previsões
hits = 0;
for i=1:length(predictions)
if predictions(i) == test(i, end)
hits = hits + 1;
end
end
score = hits/length(predictions);
end
% Função que retorna o vetor de previsões e o score obtido do dataset de
% teste com base no dataset de treino avaliando o centróide mais próximo.
function score = npc(train, test)
% Inicializando o vetor de previsões
predictions = zeros(length(test), 1);
% Calculando os centróides
% Pegando os índices de todas as amostras de treino de cada classe
ids1 = find(train(:, end) == 0);
ids2 = find(train(:, end) == 1);
% Encontrando os centróides
c1 = mean(train(ids1, 1:end-1));
c2 = mean(train(ids2, 1:end-1));
% Essa linha serve apenas para os valores dos centróides serem
% serem mostrados na janela de comando
% [c1;c2]
% Encontrando o centróide mais próximo de cada amostra de teste
for i=1:length(test)
dist1 = euclidean_distance(test(i, 1:end-1), c1);
dist2 = euclidean_distance(test(i, 1:end-1), c2);
if dist1 < dist2
predictions(i) = 0;
else
predictions(i) = 1;
end
end
% Calculando a precisão das previsões
hits = 0;
for i=1:length(predictions)
if predictions(i) == test(i, end)
hits = hits + 1;
end
end
score = hits/length(predictions);
end
% Função que faz o cross validation e retorna vetor com percentual de
% acertos
function [cv_scores_knn, cv_scores_npc] = cross_validation(dataset, kfolds, max_neighbors)
% Inicializando os vetores de scores
cv_scores_knn = zeros(kfolds,max_neighbors);
cv_scores_npc = zeros(kfolds, 1);
% Tamanho de cada 'fold'
fold_size = length(dataset)/kfolds;
% O intuito de gerar os índices aleatórios por cada classe é manter as
% classes balanceadas, de forma que em todas as divisões de treino e
% teste sempre seja mantida a proporção 50-50 das classes.
middle = length(dataset)/2;
indexes1 = randperm(middle, middle);
indexes2 = randperm(middle, middle)+middle;
% class_size é a quantidade de amostras por classe em cada k
class_size = fold_size/2;
% Laço que faz a divisão dos sets de treino e teste, previsão com knn e
% avaliação da acurácia
for fold=1:kfolds
% Juntando os índices em um vetor temporário de forma que os 500
% primeiros índices pertecem à classe1 e o restante à classe 2
temp_indexes = [indexes1 indexes2];
% Gera a lista de índices a serem utilizados para teste.
test_id1 = temp_indexes(class_size*(fold-1) + 1:fold*class_size); % índices da classe 1
test_id2 = temp_indexes(class_size*(fold-1) + middle+1:middle+fold*class_size); % índices da classe 2
test_indexes = [test_id1 test_id2];
% Separa o dataset de teste
test = dataset(test_indexes, :);
% Remove os índices utilizados para o dataset de teste dos índices
% temporários, restando apenas os índices que não foram utilizados
% no teste
temp_indexes([class_size*(fold-1) + 1:fold*class_size class_size*(fold-1) + middle+1:middle+fold*class_size]) = [];
% Adiciona a parte do dataset não utilizado para teste em um
% dataset de treino
train = dataset(temp_indexes, :);
% Classifica o dataset de treino com o knn com os valores de k
% variando entre 1 e k_max e retorna a acurácia para cada valor de
% k
for k=1: max_neighbors
% O vetor abaixo foi colocado apenas para companhamento
% da execução do algoritmo pela janela de comando
[fold k]
cv_scores_knn(fold, k) = knn(train, test, k);
end
% Calcula a acurácia obtida com o npc
cv_scores_npc(fold) = npc(train, test);
end
end