Archyvas

Įrašai pažymėti ‘artimiausių kaimynų metodas’ žyma

k artimiausių kaimynų metodas KNN

2011-10-30

KNN (k nearest neighbors) metodas klasifikuoja tikrindamas kokioms klasėms priklauso artimiausi K kaimynai ir priskiria tai klasei, kuriai priklauso daugelis K kaimynų. Įėjimo vektorius gali turėti bet kokį požymių kiekį, pvz.: 1, 2, 10 ir daugiau. Euklido atstumą naudosime skaičiuojant atstumą tarp kaimynų, kurį lengva paskaičiuoti net ir daug dimensijų turinčiai erdvei. Išbandysime pasirašyti savo KNN funkciją bei palyginti rezultatą su Matlab knnclassify funkcija naudojant dvejų dimensijų duomenų aibę. Abejų klasių duomenys (0 – raudona, 1 – žalia) parodyti žemiau:

Eksperimentas labai paprastas. Užkrausime duomenis, kurie turi du požymius, o pačių duomenų yra kiek daugiau nei tūkstantis. Apmokymui imsime dešimt procentų duomenų atsitiktine tvarka, o tikslumą tikrinsime su likusiais.

Programa:

Selec All Code:
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
clc;
close all;
 
load apple.txt
data = apple;
% duomenys išmaišomi atsitiktine tvarka, nes kas kartą norime
% naudoti vis kitą imti apmokymui, o likutį testavimui
data = data(randperm(size(data, 1)), :);
clear apple;
 
% atvaizduojam abi klases
figure(1); clf; hold on
l = data(:, 3) == 0;
d1 = data(l, 1 : 2)';
d2 = data(~l, 1 : 2)';
plot(d1(1, :), d1(2, :), 'r+');
plot(d2(1, :), d2(2, :), 'g+');
axis('equal');
 
p = 10; % kiek duomenų skirti apmokymui procentais
features = 2; % požymių kiekis
k = 3; % kiek kaimynų naudoti klasifikuojant
q = int16(size(data, 1) * p / 100); % kiek duomenų skirti apmokymui
training = data(1 : q, 1 : features); % apmokymo duomenys
plot(training(:, 1), training(:, 2), 'b.'); % pažymėti, kurie duomenys dalyvauja apmokyme
group = data(1 : q, features + 1); % apmokymo duomenų klasės
total = size(data, 1); % duomenų kiekis
correct1 = 0;
correct2 = 0;
for i = q + 1 : total % testuoti tik su tais duomenimis, kurie nenaudojami apmokymui
    g = data(i, features + 1);
    sample = data(i, 1 : features);
    c = knnclassify(sample, training, group, k);
    if c == g
        correct1 = correct1 + 1;
    else
        fprintf('Klaida: %d \n', i);
    end;
    c = myknnclassify(sample, training, group, k);
    if c == g
        correct2 = correct2 + 1;
    else
        fprintf('Klaida: %d \n', i);
    end;
    fprintf('Total iterations: %d, done: %d\n', total - q, i - q);
end;
fprintf('knnclassify rezultatas: %4.2f %%\n', 100 * correct1 / (total - q));
fprintf('myknnclassify rezultatas: %4.2f %%\n', 100 * correct2 / (total - q));

KNN funkcija:

Selec All Code:
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
function [class] = myknnclassify(sample, training, group, k)
A = zeros(1, 2); % matrica artimiausiems kaimynams saugoti
n = size(training, 1); % iš viso kaimynų kiekis
for i = 1 : n % kiekvienam kaimynui atliekami skaičiavimai
    d = euclidean_distance(training(i, :), sample); % randamas Euklido atstumas
    s = size(A, 1);
    if s < k % jeigu artimiausių kaimynų atrinkta mažiau nei K, tai papildom
        A(s + 1, 1 : 2) = [group(i) d];
    elseif A(k, 2) > d % jeigu artimiausių kaimynų atrinkta K, tolimiausią keičiam artimesniu
        A(k, 1 : 2) = [group(i) d];
    end;
    A = sortrows(A, 2); % išrūšiuojam pagal atstumą
end;
m = max(group) + 1; % klasių kiekis
B = zeros(1, m);
for i = 1 : k % randam po kiek artimiausių kaimynų priklauso kiekvienai klasei
    B(A(i, 1) + 1) = B(A(i, 1) + 1) + 1;
end;
[maxC, maxI] = max(B); % klasės indeksas, kurioje daugiausiai artimiausių kaimynų
[minC, minI] = min(B); % klasės indeksas, kurioje mažiausiai artimiausių kaimynų
if maxC > minC % laimėtoja klasė, kuri turi daugiausia artimiausių kaimynų
    class = maxI - 1;
else % jeigu visos klasės turi vienodą kaimynų kiekį (gali būti ir kitokie kriterijai)
    class = -1; % atmetimo (reject) klasė
end;

Euklido atstumas:

Selec All Code:
1
2
3
4
5
6
7
function [d] = euclidean_distance(x, y)
n = size(x, 2);
d = 0;
for i = 1 : n
    d = d + (x(i) - y(i)) ^ 2;
end;
d = d ^ 0.5;

Abejų funkcijų rezultatai yra gana panašūs. Kadangi duomenų klasės nepersidengia ir yra gana toli viena nuo kitos, tai pakanka vos kelių procentų apmokymo duomenų, kad visus likusius duomenis abi funkcijos klasifikuotų šimto procentų tikslumu. Žemiau esančiame paveiksliuke matosi mėlynai pažymėti taškai, kurie buvo naudojami apmokymui ir yra atrinkti atsitiktine tvarka (vos 10 procentų nuo visų duomenų). Kiekvieną kartą užkrovus duomenis, parenkama vis kita 10 procentų duomenų imtis apmokymui, taip mes galime matyti kaip funkcijų tikslumas priklauso nuo skirtingų duomenų aibių, tačiau išlaikant norimą jos dydį:

myknnclassify funkcija yra dešimt kartų lėtesnė nei Matlab standartinė funkcija knnclassify ir kiek testavau, mažiau tiksli. Be to, aprašytoje gali būti (ir tikriausiai yra) klaidų, nes ji nėra pilnai ištestuota kaip Matlab funkcija. Todėl Matlab funkcija yra gerokai pranašesnė, kuri be to dar turi ir įvairių papildomų parametrų.

Tyrinėjimai , , , ,