Использование предобученной нейросети ResNet для классификации изображений¶
Нейросети – удобный гибкий алгоритм, который можно обучить вычислениям. При успешно выстроенном процессе обучения, одну и ту же нейросеть можно будет использовать во множестве разных задач. Этим, например, знаменито семейство нейросетей ResNet
, созданные для классификации изображений. Частично или целиком, подобные нейросети используются в самых разных задачах, где нужно работать с численными репрезентациями изображений, от графических баз данных до переноса стиля.
В этом примере мы запустим нейросеть ResNet
небольшой глубины (18 слоёв) для классификации изображений. Покажем всю цепочку подготовки данных и обработки выходной информации, которая для любой картинки выдаст нам более или менее подходящую текстовую метку, характеризующую объект, изображенный на картинке.
Подготовительная работа¶
Объект Tape
библиотеки Umlaut
, на данный момент, является основным контейнером для вычислений, куда можно распаковать нейросеть из формата ONNX
. Этот механизм запуска, скорее всего, будет изменен в ближайшем будущем, поскольку библиотека ONNX
сейчас находится в процессе обновления.
import Pkg; Pkg.add("Umlaut", io=devnull)
import Umlaut: Tape, play!
Конечно, нам также понадобятся встроенные в Engee библиотеки для работы с форматом ONNX, в котором часто хранятся предобученные нейросети, и библиотека для работы с изображениями.
using ONNX
using Images
Установим рабочую папку
cd( @__DIR__ )
Классы объектов в ImageNet
¶
Названия всех классов объектов, которые умеет распознавать наша предобученная нейросеть, представлены в качестве упорядоченного вектора и загружаются из следующего файла.
include( "imagenet_classes.jl" );
Функции ввода-вывода¶
Напишем три простые вспомогательные функции для подачи данных в нейросеть и обработки результатов:
- Загрузка изображения: мы масштабируем его до размера 244*244 (нормализация и обрезка краев с сохранением пропорций были бы желательным дополнением)
- Сортировка предсказаний: нейросеть возвращает вектор чисел, которые означают вероятность того, что на изображении наблюдается тот или иной класс из датасета ImageNet. Отберем
k
наиболее вероятно представленных классов - Оболочка для этих функций позволяет за одно действие загрузить изображение и выдать
k
наиболее вероятных предсказаний
# Загрузка изображения из файла
function imread(path::AbstractString; sz=(224,224))
img = Images.load(path);
img = imresize(img, sz);
x = convert(Array{Float32}, channelview(img))
# Заменим порядок слоев: CHW -> WHC
x = permutedims(x, (3, 2, 1))
return x
end
# Выдача индексов первых k предсказаний
function maxk(a, k)
b = partialsortperm(a, 1:k, rev=true)
return collect(zip(b, a[b]))
end
# Загрузка изображения и выдача десяти наиболее вероятных классов в убывающем порядке
function test_image(tape::Tape, path::AbstractString)
x = imread(path)
x = reshape(x, size(x)..., 1)
y = play!(tape, x)
y = reshape(y, size(y, 1))
top = maxk(y, 10)
classes = []
for (i, (idx, val)) in enumerate(top)
name = IMAGENET_CLASSES[idx - 1]
classes = [classes; "$i: $name ($val)"]
end
return join(classes, "\n")
end
Скачаем нейросеть ResNet18
¶
Мы будем пользоваться нейросетью, которая лежит в файле с расширением *.onnx
. Есть библиотеки, которые позволяют создать и загрузить эту нейросеть при помощи еще более высокоуровневых команд (например, библиотека Metalhead.jl из коллекции FluxML
), но пока мы это сделаем без дополнительных библиотек.
Предобученная нейросеть уже находится в указанном каталоге, поэтому команда выполнится без повторного скачивания.
path = "resnet18.onnx"
if !isfile(path)
download("https://github.com/onnx/models/raw/main/vision/classification/resnet/model/resnet18-v1-7.onnx", path)
end
# Создадим пустую матрицу на месте которой будет входное изображение
img = rand( Float32, 224, 224, 3, 1 )
# Загружаем модель в виде объекта Umlaut.Tape
resnet = ONNX.load( path, img );
Загрузим несколько изображений¶
Если они уже загружены, аналогично, повторно они скачаны не будут
path = "data/"
goose_path = download( "https://upload.wikimedia.org/wikipedia/commons/3/3f/Snow_goose_2.jpg", path*"goose.jpg");
dog_path = download( "https://farm4.staticflickr.com/1301/4694470234_6f27a4f602_o.jpg", path*"dog.jpg");
plane_path = download( "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c9/Rossiya%2C_RA-89043%2C_Sukhoi_Superjet_100-95B_%2851271265892%29.jpg/1024px-Rossiya%2C_RA-89043%2C_Sukhoi_Superjet_100-95B_%2851271265892%29.jpg", path*"plane.jpg");
Классифицируем изображения¶
display( load(plane_path)[1:5:end, 1:5:end] )
print( test_image( resnet, plane_path ))
display( load(goose_path)[1:5:end, 1:5:end] )
print( test_image( resnet, goose_path ))
display( load(dog_path)[1:5:end, 1:5:end] )
print( test_image( resnet, dog_path ))
Заключение¶
Мы показали, что в Engee несложно скачать нейросеть и выполнить с ее помощью вычисления.
Этот механизм позволяет организовать сложный конвейер обработки информации, состоящий из высокоуровневых компонентов. В частности, доверить некотореы этапы обработки информации предобученным нейросетям.
Библиография: https://github.com/FluxML/ONNX.jl