Рассмотрим сценарий: путешествуя по всему миру, вы идете в центр этого нового города, наслаждаясь новыми пейзажами. Внезапно ваш разум сосредотачивается на человеке, идущем к вам. По мере того, как вы приближаетесь друг к другу, ваш мозг начинает распознавать кусочки и кусочки; сначала глаза, потом улыбка, походка, форма лица. Вдруг тебя поражает. Это ваш друг, которого вы не видели десять лет!

Как мы видим?

Как мы узнаем предметы и людей? Как отличить стул от стола? Как мы можем отличить наших друзей после того, как они меняют прическу, набирают или худеют и вырастают? Эта способность связана с тем, что мозг хранит не точные изображения, а скорее абстракции. Определение стула хранится не как четырехногий коричневый древесный объект с задней стороной, а как объект, который поднимается с земли с некоторой опорой и подходит человеку для сидения. Он может иметь или не иметь колеса, он может иметь или не иметь подлокотники и так далее. Более того, эта абстракция может развиваться со временем, поскольку мы видим все больше и больше стульев. Фактически, мы склонны проецировать эти абстракции назад во времени и изменять нашу память о прошлом на основе новой информации.

Как видит компьютер?

Точно так же компьютерная система распознает объекты по их абстракциям. Один простой способ сделать это - использовать анализ основных компонентов (PCA). Я проиллюстрирую это на примере одного из первых реальных приложений машинного обучения для распознавания образов, распознавания почтовых индексов. В этом простом примере я покажу, как построить очень примитивную систему для преобразования рукописного числа в цифру. На рисунке ниже показан пример некоторых рукописных чисел, которые будут проанализированы. Ссылка на набор данных приведена в конце сообщения.

% Open and read the file
fileID = fopen('optdigits-orig.tra','r');
formatSpec = '%s';
data = textscan(fileID,formatSpec); data = char(data{1});
% Define important parameters
nx = 32; ny = 32; nxny = nx*ny;
nNumbers = size(data,1)/ (nx + 1);
% Initialize the output
trainingMatrix = zeros(nxny, nNumbers);
output = zeros(nNumbers, 1);
% Subdevide data into images
for i = 1:nNumbers
    startingLocation = ((i-1)*(nx+1))+1;
    endingLocation = startingLocation + ny - 1; 
    outputLocation = endingLocation +1;
    trainingMatrix(:,i) = str2num(reshape(data (startingLocation:endingLocation, :), nxny, 1)); 
    output(i) = str2double(data(outputLocation, :));
end
% Visualize some the images
figure('Color', 'White');
for i = 1:6
    subplot(2,3,i)
    imagesc(reshape(trainingMatrix(:,i),32,32));
    axis equal; axis tight;
    set(gca,'xTickLabel', [], 'yTickLabel', []);
    colormap gray; 
end

Общая процедура выглядит следующим образом: 1) сначала получается большой обучающий набор рукописных чисел с их правильным цифровым представлением, 2) система распознает шаблон для каждого числа с помощью PCA, 3) и, наконец, шаблоны будут использоваться сделать прогноз по новому рукописному номеру.

Извлечение шаблонов (построение системы)

Главный компонент находит независимые направления в заданном наборе данных, так что первое направление объясняет максимальный объем информации в наборе данных, второе направление объясняет второй максимальный объем информации и так далее. На рисунке ниже показан пример двухмерного набора данных и соответствующих основных компонентов первого и второго основных принципов. Обратите внимание, что первый компонент имеет максимальную дисперсию данных. Я не буду вдаваться в подробности того, как работает PCA, в этом посте, так как в Интернете есть множество веб-сайтов, которые занимаются этим. Если у вас есть какие-либо вопросы по этому поводу, пожалуйста, задавайте их в комментариях, и я обновлю сообщение этой информацией.

Применяя аналогичный анализ к изображениям, можно получить основные компоненты изображений. На рисунке ниже показаны первые несколько основных компонентов набора данных, содержащего 1934 рукописных чисел. Обратите внимание, что каждый компонент состоит из шаблона, который можно рассматривать как линейную комбинацию всех чисел набора обучающих данных.

[PC,score,latent,tsquare] = pca(trainingMatrix’);
varianceAccounted = latent./sum(latent);
figure(‘Color’, ‘White’);
for i = 1:12
 subplot(4,3,i)
 imagesc(reshape(PC(:,i),32,32))
 axis equal; axis tight;
 set(gca,’xTickLabel’, [], ‘yTickLabel’, []);
 title([‘PC ‘ num2str(i) ‘ — Variance %: ‘ num2str(varianceAccounted(i)*100,’%10.1f’)]);
end

Более важно отметить, что каждое число можно рассматривать как линейный компонент этих основных компонентов. Например, число 4 (рисунок ниже) можно представить как составное из 0,3835 первого основного компонента и -5,9564 второго компонента и так далее. Этот вес будет использоваться при распознавании нового рукописного номера. На рисунке ниже показано одно рукописное число, восстановленное с использованием первых 30 основных компонентов.

% Number index
numberIndex = 4;
% Extract the number and its weights
realNumber = trainingMatrix(:,numberIndex);
scoreNumber = score(numberIndex,:);
% Reconstruct the number with the first 30 PC
reconstructedNum = zeros(size(realNumber));
for i = 1:30
reconstructedNum = reconstructedNum + (PC(:,i) * scoreNumber(i));
end
% Figure
figure(‘Color’, ‘White’);
subplot(1,2,1); imagesc(reshape(realNumber,32,32)); title(‘Original’);
axis equal; axis tight; set(gca,’xTickLabel’, [], ‘yTickLabel’, []);
subplot(1,2,2); imagesc(reshape(reconstructedNum,32,32)); title(‘Reconstructed’);
axis equal; axis tight; set(gca,’xTickLabel’, [], ‘yTickLabel’, []);
colormap gray;

Предсказание новых рукописных чисел (прогнозирование)

Учитывая новый рукописный номер, шаблоны внутри него могут быть извлечены с помощью PCA. Веса паттернов можно сравнить с таблицей, построенной с использованием обучающей выборки. Чем ближе веса к определенной цифре, тем более вероятно, что рукописное число является этой цифрой. Квадратичный анализ распознавания используется для прогнозирования. Фактически, мы можем использовать веса для построения вероятностного решения. Конечно, в этом приложении за правильное решение берется наиболее вероятная цифра. Однако вы можете подумать о некоторых приложениях, в которых относительно однородное вероятностное решение приведет к вмешательству человека, чтобы выбрать лучшее решение.

% Create Classifier
P = fitcdiscr(score(:,1:7),output’,’DiscrimType’,’pseudoQuadratic’);
% Predict and Show
figure(‘Color’, ‘White’)
for i = 1:6
 subplot(2,3,i)
 randomImage = round(rand() * nNumbers);
 label = predict(P,score(randomImage,1:7));
 imagesc(reshape(trainingMatrix(:,randomImage),32,32))
 title([‘This is a ‘ num2str(label)]); colormap gray;
 axis equal; axis tight; set(gca,’xTickLabel’, [], ‘yTickLabel’, []);
end

Заключительные замечания

Одна вещь, которую я не описал и не использовал, - это использование набора данных для обучения и тестирования. На самом деле я должен был использовать часть набора данных для обучения, а другую часть - для тестирования. Кроме того, в вычислительно большом наборе данных некоторые не очень важные принципиальные компоненты могут быть отброшены. Я оставлю это для обсуждения в других постах.

Надеюсь, теперь вы понимаете один из способов заставить компьютеры распознавать предметы и людей. Более точные классификаторы, которые обычно используются сегодня, относятся к более сложным сверточным нейронным сетям. Я мог бы написать об этом в один прекрасный день.

Примечания к используемым данным и программному обеспечению

В этом посте использовался Mathworks Matlab 2016b. Однако для выполнения того же анализа можно использовать другое программное обеспечение, включая Octave (бесплатно), R (бесплатно), Wolfram Mathematica и библиотеки машинного обучения, такие как Tensor Flow или Microsoft Cognitive Toolkit. Набор данных получен из Центра машинного обучения и интеллектуальных систем Калифорнийского университета в Ирвине (ссылка).