плохой результат при использовании предварительно вычисленного ядра chi2 с libsvm (matlab) - PullRequest
6 голосов
/ 24 августа 2011

Я пытаюсь использовать libsvm и следую примеру для обучения svm на данных heart_scale, которые поставляются с программным обеспечением.Я хочу использовать ядро ​​chi2, которое я вычисляю сам.Классификация по данным обучения падает до 24%.Я уверен, что правильно вычисляю ядро, но, наверное, я что-то делаю не так.Код ниже.Вы видите какие-либо ошибки?Помощь будет принята с благодарностью.

%read in the data:
[heart_scale_label, heart_scale_inst] = libsvmread('heart_scale');
train_data = heart_scale_inst(1:150,:);
train_label = heart_scale_label(1:150,:);

%read somewhere that the kernel should not be sparse
ttrain = full(train_data)';
ttest = full(test_data)';

precKernel = chi2_custom(ttrain', ttrain');
model_precomputed = svmtrain2(train_label, [(1:150)', precKernel], '-t 4');

Вот как предварительно вычисляется ядро:

function res=chi2_custom(x,y)
a=size(x);
b=size(y);
res = zeros(a(1,1), b(1,1));
for i=1:a(1,1)
    for j=1:b(1,1)
        resHelper = chi2_ireneHelper(x(i,:), y(j,:));
        res(i,j) = resHelper;
    end
end
function resHelper = chi2_ireneHelper(x,y)
a=(x-y).^2;
b=(x+y);
resHelper = sum(a./(b + eps));

При другой реализации svm (vlfeat) я получаю классификацию по тренировочным данным (да, я проверял данные тренировок, просто чтобы посмотреть, что происходит) около 90%.Так что я уверен, что результат libsvm неверен.

Ответы [ 2 ]

15 голосов
/ 23 октября 2011

При работе с машинами опорных векторов очень важно нормализовать набор данных как этап предварительной обработки. Нормализация размещает атрибуты в одном масштабе и не позволяет атрибутам с большими значениями смещать результат.Это также улучшает числовую стабильность (сводит к минимуму вероятность переполнений и переполнений из-за представления с плавающей запятой).

Также, если быть точным, ваше вычисление ядра хи-квадрат немного отклонено.Вместо этого возьмите определение ниже и используйте для этого более быструю реализацию:

chi_squared_kernel

function D = chi2Kernel(X,Y)
    D = zeros(size(X,1),size(Y,1));
    for i=1:size(Y,1)
        d = bsxfun(@minus, X, Y(i,:));
        s = bsxfun(@plus, X, Y(i,:));
        D(:,i) = sum(d.^2 ./ (s/2+eps), 2);
    end
    D = 1 - D;
end

Теперь рассмотрим следующий пример, использующий тот же набор данных, что и вы (код, адаптированный из предыдущий ответ моего):

%# read dataset
[label,data] = libsvmread('./heart_scale');
data = full(data);      %# sparse to full

%# normalize data to [0,1] range
mn = min(data,[],1); mx = max(data,[],1);
data = bsxfun(@rdivide, bsxfun(@minus, data, mn), mx-mn);

%# split into train/test datasets
trainData = data(1:150,:);    testData = data(151:270,:);
trainLabel = label(1:150,:);  testLabel = label(151:270,:);
numTrain = size(trainData,1); numTest = size(testData,1);

%# compute kernel matrices between every pairs of (train,train) and
%# (test,train) instances and include sample serial number as first column
K =  [ (1:numTrain)' , chi2Kernel(trainData,trainData) ];
KK = [ (1:numTest)'  , chi2Kernel(testData,trainData)  ];

%# view 'train vs. train' kernel matrix
figure, imagesc(K(:,2:end))
colormap(pink), colorbar

%# train model
model = svmtrain(trainLabel, K, '-t 4');

%# test on testing data
[predTestLabel, acc, decVals] = svmpredict(testLabel, KK, model);
cmTest = confusionmat(testLabel,predTestLabel)

%# test on training data
[predTrainLabel, acc, decVals] = svmpredict(trainLabel, K, model);
cmTrain = confusionmat(trainLabel,predTrainLabel)

Результат по данным тестирования:

Accuracy = 84.1667% (101/120) (classification)
cmTest =
    62     8
    11    39

и по данным обучения, мы получаем точность около 90%, как вы ожидали:

Accuracy = 92.6667% (139/150) (classification)
cmTrain =
    77     3
     8    62

train_train_kernel_matrix

0 голосов
/ 01 сентября 2011

Проблема в следующей строке:

resHelper = sum(a./(b + eps));

должно быть:

resHelper = 1-sum(2*a./(b + eps));
...