Перейти к содержимому

MNIST Small

MNIST Small — учебный датасет рукописных цифр.

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

В датасете 5000 записей. Каждая запись соответствует одному изображению рукописной цифры от 0 до 9 в разрешении 28 на 28 пикселов в градациях серого.

Изображения в MNIST Small хранятся не как картинки, а как строки таблицы.

Каждая строка имеет вид:

label, pixel_0, pixel_1, ..., pixel_783

label — первое поле. Это правильный ответ: цифра, которая изображена на картинке.

Остальные 784 поля — яркости пикселей изображения.

Почему именно 784?

28 × 28 = 784

То есть каждое изображение имеет размер 28 на 28 пикселей, а в таблице оно развёрнуто в одну длинную строку чисел.

картинка 28×28 → 784 числовых признака

MNIST Small — это задача многоклассовой классификации.

Модель должна определить, какая цифра изображена:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9

В отличие от Iris, здесь не 3 класса, а 10. И не 4 признака, а 784.

uses MLABC;
begin
Datasets.Language := 'ru';
var ds := Datasets.MnistSmall;
ds.Info;
Println;
ds.ClassCounts
.OrderBy(kv -> kv.Key)
.PrintLines(kv -> ds.ClassName(kv.Key) + '' + kv.Value + ' шт');
end.

Эта программа выводит общую информацию о датасете, количество объектов каждого класса и первые строки таблицы.

Вывод:

Датасет: mnist_small
Описание:
Сбалансированный поднабор MNIST из 5000 изображений для классификации рукописных цифр.
Задача: Classification
Строк: 5000
Признаков: 784
Цель: label
0 → 500 шт
1 → 500 шт
2 → 500 шт
3 → 500 шт
4 → 500 шт
5 → 500 шт
6 → 500 шт
7 → 500 шт
8 → 500 шт
9 → 500 шт

Из вывода видно, что датасет сбалансирован: каждая цифра представлена 500 изображениями.

Так как данные фактически являются изображениями, для MNIST Small особенно полезна визуализация.

uses MLABC, PlotML;
begin
var ds := Datasets.MnistSmall;
var df := ds.Data;
var X := df.ToMatrix(ds.Features);
var y := df.ToVector(ds.Target);
var rows := new List<integer>;
for var digit := 0 to 9 do
rows.AddRange(y.Indices(v -> v = digit).Take(12));
var sample := X.TakeRows(rows);
Plot.Title := 'MnistSmall: по 12 изображений каждой цифры';
Plot.ImageGrid(sample, 28, 28,
count := 120,
cols := 12,
invert := True,
spacing := 0);
end.

Здесь для каждой цифры от 0 до 9 выбираются первые 12 изображений. Поэтому на рисунке получается 10 строк: в первой строке нули, во второй единицы, затем двойки и так далее.

Вывод:

MnistSmall: по 12 изображений каждой цифры

Пример может работать несколько секунд: признаков много, а задача уже ближе к реальному распознаванию изображений.

uses MLABC;
begin
var ds := Datasets.MnistSmall;
var df := ds.Data;
var X := df.ToMatrix(ds.Features);
var y := df.EncodeLabels(ds.Target);
var (Xtrain, Xtest, ytrain, ytest) :=
Validation.TrainTestSplit(X, y, testRatio := 0.2, seed := 42);
// Для изображений полезно привести яркости пикселей к сопоставимому масштабу.
var scaler := new MinMaxScaler;
Xtrain := scaler.FitTransform(Xtrain);
Xtest := scaler.Transform(Xtest);
var model := new LogisticRegression(
learningRate := 1.0,
epochs := 50
);
model.Fit(Xtrain, ytrain);
var pred := model.Predict(Xtest);
var acc := Metrics.Accuracy(ytest, pred);
Println($'Accuracy: {acc:F3}');
end.

Вывод:

Accuracy: 0.879

Это означает, что модель правильно распознала примерно 88% цифр из тестовой выборки.

Для первого примера это уже неплохой результат: модель видит не готовые картинки, а 784 числовых признака - яркости пикселей. Но это не предел. Другие модели могут распознавать цифры точнее, поэтому качество результата сильно зависит от выбранной модели и её параметров.

Для многоклассовой классификации полезна матрица ошибок. Она показывает, какие цифры модель распознаёт хорошо, а какие путает между собой.

uses MLABC;
begin
var ds := Datasets.MnistSmall;
var df := ds.Data;
var X := df.ToMatrix(ds.Features);
var y := df.EncodeTarget(ds.Target);
var (Xtrain, Xtest, ytrain, ytest) :=
Validation.TrainTestSplit(X, y.Labels, testRatio := 0.2, seed := 42);
var model := new RandomForestClassifier(
nTrees := 20,
maxDepth := 20,
seed := 42
);
model.Fit(Xtrain, ytrain);
var pred := model.Predict(Xtest);
var acc := Metrics.Accuracy(ytest, pred);
var cm := new ConfusionMatrix(ytest, pred, y.ClassNames);
cm.Println(normalize := MatrixNormalization.Rows, sortClassNames := True);
end.

Вывод:

Предсказано →
Реально ↓ 0 1 2 3 4 5 6 7 8 9
0 97.4% 0.0% 0.0% 0.0% 0.0% 0.0% 1.7% 0.9% 0.0% 0.0%
1 0.0% 95.7% 2.2% 0.0% 0.0% 0.0% 1.1% 0.0% 1.1% 0.0%
2 1.0% 0.0% 90.8% 1.0% 1.0% 0.0% 1.0% 0.0% 2.0% 3.1%
3 0.0% 0.0% 0.9% 83.5% 0.0% 6.4% 0.0% 0.9% 4.6% 3.7%
4 0.0% 0.0% 2.3% 0.0% 93.0% 0.0% 2.3% 0.0% 0.0% 2.3%
5 0.0% 0.0% 0.0% 2.4% 0.0% 90.6% 4.7% 1.2% 0.0% 1.2%
6 0.0% 2.0% 1.0% 0.0% 0.0% 2.0% 95.0% 0.0% 0.0% 0.0%
7 0.0% 0.0% 4.0% 0.0% 0.0% 0.0% 0.0% 90.9% 1.0% 4.0%
8 0.0% 2.8% 0.0% 3.7% 1.9% 1.9% 0.0% 1.9% 87.0% 0.9%
9 0.9% 0.9% 0.0% 2.8% 1.9% 0.0% 0.0% 3.8% 0.0% 89.6%

В матрице ошибок строки соответствуют настоящим цифрам, а столбцы — предсказанным.

Из данной матрицы видно, что реальная цифра 3 правильно распознается всего лишь в 83.5% случаев. При этом алгоритм путает ее с цифрой 5 в 6.4% случаев, а с цифрой 8 — в 4.6% случаев. Из этой же матрицы следует, что алгоритм никогда не путает реальную цифру 3 с цифрами 0, 1, 6. Другое интересное наблюдение: лучше всех распознается цифра 0 — она распознается алгоритмом в 97.4% случаев.

MNIST Small — хороший следующий шаг после Iris.

Iris → 3 класса, 4 признака
MNIST Small → 10 классов, 784 признака

На этом датасете удобно показывать:

  • многоклассовую классификацию;
  • работу с большим числом признаков;
  • представление изображений в виде таблицы;
  • визуализацию изображений;
  • матрицу ошибок для 10 классов.