-
Notifications
You must be signed in to change notification settings - Fork 3
/
svmResultsCV.m
364 lines (287 loc) · 11.7 KB
/
svmResultsCV.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
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
%code modified from:
%http://www.vlfeat.org/applications/caltech-101-code.html
%TODO
%tweak params
%comment,
%understand
%make soft output!
function svmResultsCV
% PHOW_CALTECH101 Image classification in the Caltech-101 dataset
% This program demonstrates how to use VLFeat to construct an image
% classifier on the Caltech-101 data. The classifier uses PHOW
% features (dense SIFT), spatial histograms of visual words, and a
% Chi2 SVM. To speedup computation it uses VLFeat fast dense SIFT,
% kd-trees, and homogeneous kernel map. The program also
% demonstrates VLFeat PEGASOS SVM solver, although for this small
% dataset other solvers such as LIBLINEAR can be more efficient.
%
% By default 15 training images are used, which should result in
% about 64% performance (a good performance considering that only a
% single feature type is being used).
%
% Call PHOW_CALTECH101 to train and test a classifier on a small
% subset of the Caltech-101 data. Note that the program
% automatically downloads a copy of the Caltech-101 data from the
% Internet if it cannot find a local copy.
%
% Edit the PHOW_CALTECH101 file to change the program configuration.
%
% To run on the entire dataset change CONF.TINYPROBLEM to FALSE.
%
% The Caltech-101 data is saved into CONF.CALDIR, which defaults to
% 'data/caltech-101'. Change this path to the desired location, for
% instance to point to an existing copy of the Caltech-101 data.
%
% The program can also be used to train a model on custom data by
% pointing CONF.CALDIR to it. Just create a subdirectory for each
% class and put the training images there. Make sure to adjust
% CONF.NUMTRAIN accordingly.
%
% Intermediate files are stored in the directory CONF.DATADIR. All
% such files begin with the prefix CONF.PREFIX, which can be changed
% to test different parameter settings without overriding previous
% results.
%
% The program saves the trained model in
% <CONF.DATADIR>/<CONF.PREFIX>-model.mat. This model can be used to
% test novel images independently of the Caltech data.
%
% load('data/baseline-model.mat') ; # change to the model path
% label = model.classify(model, im) ;
%
% AUTORIGHTS
conf.calDir = 'images/data/herc-data' ;
%conf.calDir = 'images/data/holdout/' ;
conf.dataDir = 'images/data/' ;
conf.autoDownloadData = false ;
conf.numTrain = 50;
conf.numTest = 230;
conf.numClasses = 32;
conf.numWords = 300 ;
conf.numSpatialX = [2 4] ;
conf.numSpatialY = [2 4] ;
conf.quantizer = 'kdtree' ;
conf.svm.C = 10 ;
conf.svm.solver = 'pegasos' ;
conf.svm.biasMultiplier = 1 ;
conf.phowOpts = {'Step', 3} ;
conf.clobber = true;%false ;
conf.tinyProblem = false;%true ;
conf.prefix = 'cvbaseline' ;
conf.randSeed = 1 ;
if conf.tinyProblem
conf.prefix = 'tiny' ;
conf.numClasses = 5 ;
conf.numSpatialX = 2 ;
conf.numSpatialY = 2 ;
conf.numWords = 300 ;
conf.phowOpts = {'Verbose', 2, 'Sizes', 7, 'Step', 5} ;
end
conf.vocabPath = fullfile(conf.dataDir, [conf.prefix '-vocab.mat']) ;
conf.histPath = fullfile(conf.dataDir, [conf.prefix '-hists.mat']) ;
conf.modelPath = fullfile(conf.dataDir, [conf.prefix '-model.mat']) ;
conf.resultPath = fullfile(conf.dataDir, [conf.prefix '-result']) ;
randn('state',conf.randSeed) ;
rand('state',conf.randSeed) ;
vl_twister('state',conf.randSeed) ;
cvFoldK = 4;
acc = zeros(1,cvFoldK);
cvNumTest = ((conf.numTrain + conf.numTest)*3)/4;
%wtf = zeros(1,cvFoldK);
% --------------------------------------------------------------------
% Setup data
% --------------------------------------------------------------------
classes = dir(conf.calDir) ;
classes = classes([classes.isdir]) ;
classes = {classes(3:conf.numClasses+2).name} ;
images = {} ;
imageClass = {} ;
for ci = 1:length(classes)
ims = dir(fullfile(conf.calDir, classes{ci}, '*.jpg'))' ;
ims = vl_colsubset(ims, conf.numTrain + conf.numTest) ;
ims = cellfun(@(x)fullfile(classes{ci},x),{ims.name},'UniformOutput',false) ;
images = {images{:}, ims{:}} ;
imageClass{end+1} = ci * ones(1,length(ims)) ;
end
cvInd = cvpartition(images,'kfold',cvFoldK);
imageClass = cat(2, imageClass{:}) ;
%save('cvind2.mat','cvInd');
for i = 1: cvFoldK
conf.prefix = strcat(conf.prefix,int2str(i));
conf.modelPath = fullfile(conf.dataDir, [conf.prefix '-model.mat']) ;
conf.vocabPath = fullfile(conf.dataDir, [conf.prefix '-vocab.mat']) ;
conf.histPath = fullfile(conf.dataDir, [conf.prefix '-hists.mat']) ;
%selTrain = find(mod(0:length(images)-1, conf.numTrain+conf.numTest) < conf.numTrain) ;
selTrain = 1: length(images);%conf.numTrain+conf.numTest
%length(cvInd.test(i))
length(images)
selTrain = selTrain(cvInd.test(i));%test is 70, training 230 so doing 70 one
selTest = setdiff(1:length(images), selTrain) ;
model.classes = classes ;
model.phowOpts = conf.phowOpts ;
model.numSpatialX = conf.numSpatialX ;
model.numSpatialY = conf.numSpatialY ;
model.quantizer = conf.quantizer ;
model.vocab = [] ;
model.w = [] ;
model.b = [] ;
model.classify = @classify ;
% --------------------------------------------------------------------
% Train vocabulary
% --------------------------------------------------------------------
if ~exist(conf.vocabPath) || conf.clobber
% Get some PHOW descriptors to train the dictionary
selTrainFeats = vl_colsubset(selTrain, 30) ;
descrs = {} ;
for ii = 1:length(selTrainFeats)
%parfor ii = 1:length(selTrainFeats)
im = imread(fullfile(conf.calDir, images{selTrainFeats(ii)})) ;
im = standarizeImage(im) ;
[drop, descrs{ii}] = vl_phow(im, model.phowOpts{:}) ;
end
descrs = vl_colsubset(cat(2, descrs{:}), 10e4) ;
descrs = single(descrs) ;
% Quantize the descriptors to get the visual words
vocab = vl_kmeans(descrs, conf.numWords, 'verbose', 'algorithm', 'elkan') ;
save(conf.vocabPath, 'vocab') ;
else
load(conf.vocabPath) ;
end
model.vocab = vocab ;
if strcmp(model.quantizer, 'kdtree')
model.kdtree = vl_kdtreebuild(vocab) ;
end
% --------------------------------------------------------------------
% Compute spatial histograms
% --------------------------------------------------------------------
if ~exist(conf.histPath) || conf.clobber
hists = {} ;
%parfor ii = 1:length(images)
for ii = 1:length(images)
fprintf('Processing %s (%.2f %%)\n', images{ii}, 100 * ii / length(images)) ;
im = imread(fullfile(conf.calDir, images{ii})) ;
hists{ii} = getImageDescriptor(model, im);
end
hists = cat(2, hists{:}) ;
save(conf.histPath, 'hists') ;
else
load(conf.histPath) ;
end
% --------------------------------------------------------------------
% Compute feature map
% --------------------------------------------------------------------
psix = vl_homkermap(hists, 1, 'kchi2', 'gamma', .5) ;
% --------------------------------------------------------------------
% Train SVM
% --------------------------------------------------------------------
if ~exist(conf.modelPath) || conf.clobber
switch conf.svm.solver
case 'pegasos'
lambda = 1 / (conf.svm.C * length(selTrain)) ;
w = [] ;
for ci = 1:length(classes)
%parfor ci = 1:length(classes)
perm = randperm(length(selTrain)) ;
fprintf('Training model for class %s\n', classes{ci}) ;
y = 2 * (imageClass(selTrain) == ci) - 1 ;
w(:,ci) = vl_pegasos(psix(:,selTrain(perm)), ...
int8(y(perm)), lambda, ...
'NumIterations', 50/lambda, ...
'BiasMultiplier', conf.svm.biasMultiplier) ;
end
case 'liblinear'
svm = train(imageClass(selTrain)', ...
sparse(double(psix(:,selTrain))), ...
sprintf(' -s 3 -B %f -c %f', ...
conf.svm.biasMultiplier, conf.svm.C), ...
'col') ;
w = svm.w' ;
end
model.b = conf.svm.biasMultiplier * w(end, :) ;
model.w = w(1:end-1, :) ;
save(conf.modelPath, 'model') ;
else
load(conf.modelPath) ;
end
% --------------------------------------------------------------------
% Test SVM and evaluate
% --------------------------------------------------------------------
% Estimate the class of the test images
scores = model.w' * psix + model.b' * ones(1,size(psix,2)) ;
[drop, imageEstClass] = max(scores, [], 1) ;
%save('cvimageEstClass.mat','imageEstClass');
%save('cvStuff.mat','selTrain','imageClass','images','psix');
%save('cvStuff.mat')
% Compute the confusion matrix
idx = sub2ind([length(classes), length(classes)], ...
imageClass(selTest), imageEstClass(selTest)) ;
confus = zeros(length(classes)) ;
confus = vl_binsum(confus, ones(size(idx)), idx) ;
% Plots
figure(1) ; clf;
subplot(1,2,1) ;
imagesc(scores(:,[selTrain selTest])) ; title('Scores') ;
set(gca, 'ytick', 1:length(classes), 'yticklabel', classes) ;
subplot(1,2,2) ;
imagesc(confus) ;
title(sprintf('Confusion matrix (%.2f %% accuracy)', ...
100 * mean(diag(confus)/ cvNumTest ))) ;
acc(i) = 100 * mean(diag(confus)/cvNumTest);
wtf = (diag(confus))
print('-depsc2', [conf.resultPath '.ps']) ;
save([strcat(conf.resultPath,int2str(i), '.mat')], 'confus', 'conf') ;
end%(cv stuff)for i = 1:4
acc
mean(acc)
save('cvAcc.mat','acc');
% -------------------------------------------------------------------------
function im = standarizeImage(im)
% -------------------------------------------------------------------------
im = im2single(im) ;
if size(im,1) > 480, im = imresize(im, [480 NaN]) ; end
% -------------------------------------------------------------------------
function hist = getImageDescriptor(model, im)
% -------------------------------------------------------------------------
im = standarizeImage(im) ;
width = size(im,2) ;
height = size(im,1) ;
numWords = size(model.vocab, 2) ;
% get PHOW features
[frames, descrs] = vl_phow(im, model.phowOpts{:}) ;
% quantize appearance
switch model.quantizer
case 'vq'
[drop, binsa] = min(vl_alldist(model.vocab, single(descrs)), [], 1) ;
case 'kdtree'
binsa = double(vl_kdtreequery(model.kdtree, model.vocab, ...
single(descrs), ...
'MaxComparisons', 15)) ;
end
for i = 1:length(model.numSpatialX)
binsx = vl_binsearch(linspace(1,width,model.numSpatialX(i)+1), frames(1,:)) ;
binsy = vl_binsearch(linspace(1,height,model.numSpatialY(i)+1), frames(2,:)) ;
% combined quantization
bins = sub2ind([model.numSpatialY(i), model.numSpatialX(i), numWords], ...
binsy,binsx,binsa) ;
hist = zeros(model.numSpatialY(i) * model.numSpatialX(i) * numWords, 1) ;
hist = vl_binsum(hist, ones(size(bins)), bins) ;
hists{i} = single(hist / sum(hist)) ;
end
hist = cat(1,hists{:}) ;
hist = hist / sum(hist) ;
% -------------------------------------------------------------------------
function [className, score] = classify(model, im)
% -------------------------------------------------------------------------
hist = getImageDescriptor(model, im) ;
psix = vl_homkermap(hist, 1, 'kchi2', 'gamma', .5) ;
%psix = vl_homkermap(hist, 1, 'kchi2') ;%MODIFIED???? removed arg 3 = .7
%psix = vl_homkermap(hist, 1, .7, 'kchi2') ;
%psix = vl_homkermap(hist, 1, 'kchi2','gamma',.7) ;
%psix = vl_homkermap(hist, 1, 'KJS', 'gamma', .5) ; %changed!
scores = model.w' * psix + model.b';
%sortIndex has the sorted class outputs
%[sortedValues,sortIndex] = sort(scores(:),'descend');
%[score, best] = max(scores) ;
[score, best] = max(scores, [], 1) ;
className = model.classes{best} ;
%className = model.classes{sortIndex(1:3)} ;