Построение и обучение нейросети для распознавания рукописных цифр
В данном примере будет рассмотрена обработка данных и обучение на их основе нейросетевой модели для классификации изображений. В качестве набора объектов наблюдений выбран датасет MNIST, в котором содержатся 70000 размеченных изображений рукописных цифр. В примере будет использоваться файл формата .csv, в котором изображения развёрнуты в виде табличных данных, содержащих значения яркости для каждого пикселя.
Подключение библиотек для обработки данных:
Pkg.add(["Colors", "CSV", "Flux", "Optimisers"])
using CSV, DataFrames
Загрузка данных в переменную:
df = DataFrame(CSV.File("$(@__DIR__)/mnist_784.csv"));
Вывод первых пяти строк датафрейма:
first(df,5)
Вывод первых пяти строк и последнего столбца с данными, который говорит о том, к какому классу пренадлежит объект наблюдения:
df[1:5,780:785]
Разбиение набора данных на тренировочную и тестовую выборку в соотношении 8 к 2:
X_train, y_train = Matrix(df[1:56000,1:784]), df[1:56000,785]
X_test, y__test = Matrix(df[56001:end,1:784]), df[56001:end,785]
Конвертация выборок в форматы, приемлимые для обработки нейросетью:
X_train, X_test = convert(Matrix{Float32}, X_train), convert(Matrix{Float32}, X_test)
y_train, y__test = convert(Vector{Float32}, y_train), convert(Vector{Float32}, y__test)
Подключение библиотеки для визуализации данных:
using Plots
Отображение объекта и его класса:
test_img = Vector(df[60000,1:784])
test_img = (reshape(test_img, 28, 28)) / 256
using Colors
println("Класс объекта: ", df[60000,785])
plot(Gray.(test_img))
Итоговое преобразование данных для обработки нейросетью:
X_train, X_test = X_train', X_test'
y_train, y__test = y_train', y__test'
Подключение библиотеки машинного обучения:
using Flux, Optimisers;
Определение структуры нейросети:
model = Chain(
Dense(784, 15,elu),
Dense(15, 10,sigmoid),
softmax
)
Тестовый результат распознавания (до обучения модели):
Определение параметров обучения:
learning_rate = 0.01f0
opt = Optimisers.Adam(learning_rate)
state = Optimisers.setup(opt, model)
function loss(model, x, y)
y_oh = Flux.onehotbatch(y, 0:9) # размер (10, 1, N)
y_pred = model(x) # размер (10, N)
# Добавляем размерность для совпадения с y_oh
y_pred_reshaped = Flux.unsqueeze(y_pred, dims=2) # теперь (10, 1, N)
return Flux.mse(y_pred_reshaped, y_oh)
end
Определение функции для подсчёта точности модели:
function accuracy(model, X, y)
correct = 0
for i in 1:length(y)
# Подготовка входа: добавить измерение батча
x_input = reshape(X[:, i], :, 1) # (features, 1)
# Предсказание модели
probs = model(x_input) # размер (10, 1)
# Преобразование в цифру
predicted_digit = argmax(probs)[1] - 1
# Сравнение с истинной меткой
if predicted_digit == y[i]
correct += 1
end
end
return correct / length(y)
end
Итеративный процесс обучения модели:
loss_history = []
epochs = 100
for epoch in 1:epochs
# Вычисление градиентов
grads = gradient(model) do m
loss(m, X_train, y_train)
end
# Обновление модели и состояния
state, model = Optimisers.update(state, model, grads[1])
# Расчет и сохранение потерь
current_loss = loss(model, X_train, y_train)
push!(loss_history, current_loss)
# Расчет точности
acc = accuracy(model, X_test, y__test) * 100
# Логирование
if epoch == 1 || epoch % 1 == 0
println("Epoch $epoch: Training Loss = $current_loss, Accuracy = $acc%")
end
end
Визуализация изменения функции потерь на каждом шаге обучения:
plot((1:epochs), loss_history, title="Изменение функции потерь", xlabel="Шаг обучения", ylabel="Функция потерь")
Отображение результатов:
number = 3000
test_img = Vector(df[56000+number,1:784])
test_img = (reshape(test_img, 28, 28)) / 256
using Colors
result = model(X_test[:,number])
println("Известный класс объекта: ", df[56000+number,785], "\n ", "Распознанная нейросетью цифра: ", (findfirst(x -> x == maximum(result), result)-1))
plot(Gray.(test_img'))
Вывод
В данном примере были предобработаны данные о яркостях пикселей, а также определена архитектура нейросети, параметры оптимизатора и функция потерь.
Модель была обучена и показала достаточно точное, но не идеальное разбиение по классам. Для улучшения качества распознавания нейросеть может быть модифицирована путём изменения архитектуры слоёв и увеличения обучающей выборки.