-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathkernel_perceptron.jl
106 lines (92 loc) · 2.86 KB
/
kernel_perceptron.jl
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
# A gaussian kernel function
@inline function Φ(x::Vector{T},
y::Vector{T},
r::T=1.0) where T<:AbstractFloat
n = 1.0 / sqrt(2π*r)
s = 1.0 / (2r^2)
return n*exp(-s*sum((x.-y).^2))
end
# A kernel matrix
function ΦΦ(X::AbstractArray{T},
r::T=1.0) where T<:AbstractFloat
n = size(X,1)
K = zeros(n,n)
for i=1:n
for j=1:i
K[i, j] = Φ(X[i, :], X[j, :],r)
K[j, i] = K[i, j]
end
K[i, i] = Φ(X[i, :], X[i, :],r)
end
K
end
# A kernel matrix for test data
function ΦΦ(X::AbstractArray{T},
Z::AbstractArray{T},
r::T=1.0) where T<:AbstractFloat
(nx,mx) = size(X)
(nz,mz) = size(Z)
K = zeros(T,nz, nx)
for i=1:nz
for j=1:nx
K[i, j] = Φ(Z[i, :], X[j, :],r)
end
end
K
end
@inline ∑(λ,y,n,K) = sum(λ .* y .* K)
function trainer(model::KernelPerceptron{T},
X::AbstractArray{T},
Y::Vector{T}) where T<:AbstractFloat
Y[Y .== 0] .= -1 # fix in the future outside this function
max_epochs = model.max_epochs
λ = model.λ # langrange multipliers
K = ΦΦ(X,model.width) # computing the kernel gram matrix
n = size(X,1)
history = []
nerrors = Inf
epochs = 0
while nerrors>0 && epochs < max_epochs
# stops when error is equal to zero or grater than last_error or reached max iterations
nerrors = 0
# weight updates for all samples
for i=1:n
yp = sign(∑(λ,Y,n,K[:,i]))
if Y[i] != yp
nerrors +=1
λ[i] += 1 # missclassification counter for sample i
end
end
epochs+=1
push!(history,nerrors)
end
if nerrors > 0
warn("[Kernel Perceptron] Train not converged. Max epochs $(max_epochs) reached. Error history: $(history) \n Try to increase max_epochs or change kernel params.")
end
# storing only the tough samples ("support vectors")
sv = λ .> 0
model.λ = λ[sv]
model.sv_x = vec(X[sv,:])
model.sv_y = Y[sv]
model.history = history
model.last_epoch = epochs
#println("[Kernel perceptron] #$(length(model.λ)) support vectors out of $(n) samples.")
end
function predictor(model::KernelPerceptron{T},
X::AbstractArray{T}) where T<:AbstractFloat
width = model.width
sv_x,sv_y,λ = model.sv_x,model.sv_y,model.λ
k = size(sv_y,1)
n = size(X,1)
y = zeros(T,n)
for i=1:n
s = .0
for j=1:k # can be vectorized in the future.
s += λ[j] * sv_y[j] * Φ(X[i,:],sv_x[j,:],width) # this is simply a weighted voting into a kernel space
end
y[i] = s
end
y = sign.(y)
y[y .== -1] .= 0 # fix in the future outside this function!!
return y
end