Компьютерное зрение можно описать как вычислительный метод анализа и понимания цифровых изображений.

В нашем случае мы хотим, чтобы наша программа могла распознавать цифровой почерк на изображениях. Я буду использовать набор данных MNIST от Deep AI, который представляет собой набор данных рукописных цифр.

Хорошо, поскольку нейронные сети полагаются на некоторую форму ввода, этот ввод может быть некоторой формой числовых данных. Итак, в контексте распознавания рукописного ввода мы знаем, что изображение — это просто сетка пикселей, и каждый пиксель содержит некоторые числовые данные, которые представляют цвет от 0 до 255 для каждого значения красного, зеленого и синего.

Приступаем к кодированию!

Во-первых, мы собираемся импортировать наши библиотеки и набор данных и подготовить наши данные для обучения.

import tensorflow as tf
# Use MNIST dataset that is built in tensorflow
mnist = tf.keras.datasets.mnist
# Prepare data for training
( x_train, y_train ), ( x_test, y_test ) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
y_test = tf.keras.utils.to_categorical(y_test)
y_train = tf.keras.utils.to_categorical(y_train)
x_train = x_train.reshape(
    x_train.shape[0], x_train.shape[1], x_train.shape[2], 1
)
x_test = x_test.reshape(
    x_test.shape[0], x_test.shape[1], x_test.shape[2], 1
)

Следующим шагом является определение нашей модели, которая будет содержать массив всех наших слоев.

model = tf.keras.models.Sequential([])

Теперь, поскольку меня интересует только число, а не их цвет, я могу игнорировать цвета и сосредоточиться только на общей форме изображения.

Для этого мы можем применить фильтр к каждому значению пикселя изображения по отношению к его соседям, взвешенным в соответствии с [матрицей ядра](https://en.wikipedia.org/wiki/Kernel_(linear_алгебра)#:~: text=Ядро m,y ∈ Null(A).). Это иначе известно как свертка изображения. Итак, внутри нашего массива мы поместим следующий код:

tf.keras.layers.Conv2D(
   32, (3, 3), activation="relu", input_shape=(28,28,1)
),

Идея здесь состоит в том, чтобы взять пиксель и попытаться сравнить его с соседними пикселями и попытаться увидеть, формируется ли какая-то кривая или линия. И я буду использовать функцию ReLu (Выпрямленная линейная единица) **** в качестве функции активации.

Следующим шагом является уменьшение размера изображений с помощью max-pooling и извлечения максимального значения из определенной части изображения, как показано ниже.

# Max-pooling layer, using 2x2 pool size
tf.keras.layers.MaxPool2D(pool_size=(2,2)),

Давайте выровняем эти значения, чтобы они могли работать в нашей нейронной сети.

# Flatten units
tf.keras.layers.Flatten(),

Теперь я могу добавить слои и выходной слой для всех десяти цифр.

# Add hidden layer with no dropout
tf.keras.layers.Dense(128, activation="relu"),
tf.keras.layers.Dropout(0.5),
# Add an output layer with output units for all 10 digits
tf.keras.layers.Dense(10, activation="softmax")

В итоге наша модель должна выглядеть примерно так:

# Create a convolutional neural network
model = tf.keras.models.Sequential([
      # Convolution Layer. Learn 32 filters useing a 3x3 kernal
      tf.keras.layers.Conv2D(
          32, (3, 3), activation="relu", input_shape=(28,28,1)
      ),
      
      # Max-pooling layer, using 2x2 pool size
      tf.keras.layers.MaxPool2D(pool_size=(2,2)),
      # Flatten units
      tf.keras.layers.Flatten(),
      # Add hidden layer with no dropout
      tf.keras.layers.Dense(128, activation="relu"),
      tf.keras.layers.Dropout(0.5),
      # Add an output layer with output units for all 10 digits
      tf.keras.layers.Dense(10, activation="softmax")
])

Теперь мы можем скомпилировать и обучить модель.

model.compile(
    optimizer="adam",
    loss="categorical_crossentropy",
    metrics=["accuracy"]
)
model.fit(x_train, y_train, epochs=10)

Мы должны получить такой вывод:

Epoch 1/10
1875/1875 [==============================] - 39s 20ms/step - loss: 0.2335 - accuracy: 0.9295
Epoch 2/10
1875/1875 [==============================] - 38s 20ms/step - loss: 0.0970 - accuracy: 0.9706
Epoch 3/10
1875/1875 [==============================] - 37s 19ms/step - loss: 0.0738 - accuracy: 0.9778
Epoch 4/10
1875/1875 [==============================] - 36s 19ms/step - loss: 0.0589 - accuracy: 0.9818
Epoch 5/10
1875/1875 [==============================] - 36s 19ms/step - loss: 0.0509 - accuracy: 0.9833
Epoch 6/10
1875/1875 [==============================] - 37s 20ms/step - loss: 0.0430 - accuracy: 0.9864
Epoch 7/10
1875/1875 [==============================] - 37s 20ms/step - loss: 0.0368 - accuracy: 0.9880
Epoch 8/10
1875/1875 [==============================] - 37s 20ms/step - loss: 0.0323 - accuracy: 0.9893
Epoch 9/10
1875/1875 [==============================] - 37s 20ms/step - loss: 0.0299 - accuracy: 0.9902
Epoch 10/10
1875/1875 [==============================] - 37s 20ms/step - loss: 0.0265 - accuracy: 0.9911
<keras.callbacks.History at 0x7f6b1fb7d9d0>

И точность обучающих данных 99,1 процента, неплохо. Но тогда мы не хотим переобучать нашу модель, чтобы посмотреть, как она будет работать на невидимых тестовых данных.

# Eveluate neural network performance
model.evaluate(x_test, y_test, verbose=2)
313/313 - 2s - loss: 0.0364 - accuracy: 0.9896 - 2s/epoch - 6ms/step
[0.036372505128383636, 0.9896000027656555]

На тестовых данных получаем 98,9%, скачать код можно здесь.