Содержание

AI: Keras, TensorFlow

Памятка по книге "Искусственный интеллект и компьютерное зрение", O'Reilly. Примеры на GitHub

Компоненты, необходимые для работы с ИИ:

  1. Наборы данных для обучения. Общедоступные:
    1. Изображения: ImageNet (Google), COCO (Microsoft),
    2. Видео: YouTube-8M
    3. Видео и изображение вождения: BBD100K, Waymo Open Dataset
    4. Текст: SQuAD, Yelp Reviews
    5. Аудио: Goolge AudioSet, LibriSpeed
  2. Фреймворки: TensorFlow, Keras (теперь часть TensorFlow), PyTorch
  3. Архитектуры моделей:
    1. CNN: сверточные нейронные сети,
    2. RNN: рекуррентные нейронные сети
    3. LSTM: сети с долгой краткосрочной памятью (Long short-term memory)
  4. Оборудование: MCU, GPU, TPU, FPGA, ASICs
Задача Примеры моделей
Классификация изображений ResNet-152 (2015), MobileNet (2017)
Классификация текста BERT (2018), XLNet (2019)
Сегментация изображений U-Net (2015), DeepLabv3 (2018)
Преобразование изображений Pix2Pix (2017)
Распознавание объектов YOLO9000 (2016), Mask R-CNN (2017)
Генерация речи WaveNet (2016)

Примеры из книги можно запускать в браузере на портале Google Colab. Либо необходимо на домашнем компьютере поставить необходимые пакеты.

Установка пакетов для работы с Keras

Сначала необходимо установить интерпретатор языка Python. Краткая инструкция по установке- здесь.

Версии Python, пакетов и venv

Под разные проекты бывает необходимо использовать разные версии Pytnon. Для этого в систему необходимо установить эти необходимые версии Python. Различные версии устанавливаются в разные директории. На мой взгляд, путь к каждой версиям лучше прописывать в системную переменную окружения PATH. Тогда,

Например, использование различных версий Python для установки пакетного менеджера pip:

 A> python -m pip install pip
 B> py -3.8 -m pip install package

 НЕ работает> python3.8 -m pip install pip

NOTE: В интернете встречается вызов Python с указанием версии ("python3.8 -m pip install pip"). Но чтобы такой вызов исполнялся системой, в директории Python должен быть exe-файл с названием python3.8.exe. А такого файла в директории установки нет, есть только python.exe, python3.dll, python312.dll. Dll файлы не могут запускаться напрямую как exe, они подгружаются к использующему их приложению. Возможно python312.dll используется при работе утилиты py. Файл python.exe исполняется при вызове "python -m pip install pip". Но тут, чтобы использовался Python нужной версии необходимо указать путь к нужной директории, например "с:/Python38/python -m pip install pip". Иначе, как уже было сказано выше, система запустит python.exe из первой же директории в путях PATH где найдет такой файл.

Каждый проект на Python рекомендуется реализовывать в своем виртуальном окружении, VENV. Для этого:

Установка пакетов для примеров

Устанавливаем вспомогательные пакеты:

Сохраняем используемые пакеты в "requirements.txt" и выходим из виртуального окружения. Для запуска своих скриптов нам надо будет снова войти в окружение!

(venv) PS E:\Code\AI> pip freeze > requirements.txt
(venv) PS E:\Code\AI> deactivate
PS E:\Code\AI>

Хотя мы вручную поставили лишь несколько пакетов, но они автоматически подтянули за собой все необходимые им дополнительные пакеты. Получился внушительный список, содержимое "requirements.txt":

absl-py==2.0.0
astunparse==1.6.3
cachetools==5.3.2
certifi==2023.11.17
charset-normalizer==3.3.2
contourpy==1.2.0
cycler==0.12.1
flatbuffers==23.5.26
fonttools==4.47.0
gast==0.5.4
google-auth==2.26.1
google-auth-oauthlib==1.2.0
google-pasta==0.2.0
grpcio==1.60.0
h5py==3.10.0
idna==3.6
imageio==2.33.1
keras==2.15.0
kiwisolver==1.4.5
lazy_loader==0.3
libclang==16.0.6
Markdown==3.5.1
MarkupSafe==2.1.3
matplotlib==3.8.2
ml-dtypes==0.2.0
networkx==3.2.1
numpy==1.26.3
oauthlib==3.2.2
opt-einsum==3.3.0
packaging==23.2
pillow==10.2.0
protobuf==4.23.4
pyasn1==0.5.1
pyasn1-modules==0.3.0
pyparsing==3.1.1
python-dateutil==2.8.2
requests==2.31.0
requests-oauthlib==1.3.1
rsa==4.9
scikit-image==0.22.0
scipy==1.11.4
six==1.16.0
tensorboard==2.15.1
tensorboard-data-server==0.7.2
tensorflow==2.15.0
tensorflow-estimator==2.15.0
tensorflow-intel==2.15.0
tensorflow-io-gcs-filesystem==0.31.0
termcolor==2.4.0
tifffile==2023.12.9
typing_extensions==4.9.0
urllib3==2.1.0
Werkzeug==3.0.1
wrapt==1.14.1

Классифицируем кошку и собаку

Из архива на GitHub скачиваем фото кошки с собаки, директория sample-images. Эту директорию я положил в директорию проекта. Скрипт взят из книги, и немного поправлен:

import tensorflow as tf
from tensorflow.keras.applications.resnet50 import preprocess_input, decode_predictions
from tensorflow.keras.preprocessing import image
import numpy as np
import matplotlib.pyplot as plt

def classify(img_path):
  img = image.load_img(img_path, target_size=(224, 224))
  model = tf.keras.applications.resnet50.ResNet50()
  img_array = image.img_to_array(img)
  img_batch = np.expand_dims(img_array, axis=0)
  img_preprocessed = preprocess_input(img_batch)
  prediction = model.predict(img_preprocessed)
  print(decode_predictions(prediction, top=3)[0])


cat_path = "sample-images/cat.jpg"
classify(cat_path)

img = image.load_img(cat_path, target_size=(224, 224))
plt.imshow(img)
plt.show()

При запуске скрипта скачалась модель ResNet50 (~10MB) и распозналась кошка породы 'tabby' с вероятностью 0.5719771:

(venv) PS E:\Code\AI> python script1.py
2024-01-07 02:11:11.597174: I tensorflow/core/util/port.cc:113] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.
WARNING:tensorflow:From E:\Code\AI\venv\Lib\site-packages\keras\src\losses.py:2976: The name tf.losses.sparse_softmax_cross_entropy is deprecated. Please use tf.compat.v1.losses.sparse_softmax_cross_entropy instead.

WARNING:tensorflow:From E:\Code\AI\venv\Lib\site-packages\keras\src\backend.py:1398: The name tf.executing_eagerly_outside_functions is deprecated. Please use tf.compat.v1.executing_eagerly_outside_functions instead.

2024-01-07 02:11:14.462242: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: SSE SSE2 SSE3 SSE4.1 SSE4.2 AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
WARNING:tensorflow:From E:\Code\AI\venv\Lib\site-packages\keras\src\layers\normalization\batch_normalization.py:979: The name tf.nn.fused_batch_norm is deprecated. Please use tf.compat.v1.nn.fused_batch_norm instead.

Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/resnet/resnet50_weights_tf_dim_ordering_tf_kernels.h5
102967424/102967424 [==============================] - 10s 0us/step
1/1 [==============================] - 1s 764ms/step
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/imagenet_class_index.json
35363/35363 [==============================] - 0s 1us/step
[('n02123045', 'tabby', 0.5719771), ('n02124075', 'Egyptian_cat', 0.21763451), ('n02123159', 'tiger_cat', 0.060934357)]

Наблюдается много ворнингов из-за deprecated функций, которые не рекомендуется использовать и которые скоро будут удалены. А так-же рекомендуется перекомпилировать tensorflow для поддержки инструкций CPU для оптимизации. Не обращаем на это внимание.

Попробуем классифицировать еще и фото собаки.

import tensorflow as tf
from tensorflow.keras.applications.resnet50 import preprocess_input, decode_predictions
from tensorflow.keras.preprocessing import image
import numpy as np
import matplotlib.pyplot as plt

def classify(img_path):
  img = image.load_img(img_path, target_size=(224, 224))
  model = tf.keras.applications.resnet50.ResNet50()
  img_array = image.img_to_array(img)
  img_batch = np.expand_dims(img_array, axis=0)
  img_preprocessed = preprocess_input(img_batch)
  prediction = model.predict(img_preprocessed)
  print(decode_predictions(prediction, top=3)[0])

cat_path = "sample-images/cat.jpg"
classify(cat_path)

dog_path = "sample-images/dog.jpg"
classify(dog_path)

img = image.load_img(cat_path, target_size=(224, 224))
plt.imshow(img)
plt.show()

img = image.load_img(dog_path, target_size=(224, 224))
plt.imshow(img)
plt.show()

Результат получаем как в книжке - собака породы корги с вероятностью 0,83.

1/1 [==============================] - 1s 751ms/step
[('n02123045', 'tabby', 0.5719771), ('n02124075', 'Egyptian_cat', 0.21763451), ('n02123159', 'tiger_cat', 0.060934357)]
1/1 [==============================] - 1s 714ms/step
[('n02113186', 'Cardigan', 0.8349694), ('n02113023', 'Pembroke', 0.15543425), ('n02110806', 'basenji', 0.0028011024)]

Модели доступные в Keras

Таблица доступных моделей представлена здесь - Keras Available models

В книге используются следующие модели, обученные на наборе данных ImageNet:

Модель Размер Топ 1 Топ 5 Параметров Кол. слоев
ResNet-50 98 MB 74.9% 92.1% 25.6M 107
MobileNet 16 MB 70.4% 89.5% 4.3M 55

Топ 1 - сколько раз ответ с наибольшей оценкой был верен. Топ 5 - был верен один из 5-ти ответов с наибольшими оценками.

Тепловая карта

"Тепловая карта" показывает какие пиксели дают наибольший вклад в распознавание объекта на картинке. В книге приведен скрипт visualization.py, который подсвечивает более теплыми цветами эти пиксели. Для работы скрипта необходимо установить следующие пакеты:

(venv) PS E:\Code\AI> pip install keras-vis
(venv) PS E:\Code\AI> pip install tf-explain
(venv) PS E:\Code\AI> pip install opencv-python

Запуск скрипта:

(venv) PS E:\Code\AI> python visualization.py --process image --path sample-images/dog.jpg

При запуске скрипта с GitHub выяснилось, что он использует модель VGG16, которая скачивается автоматически и занимает ~528MB. В предыдущем примере с классификацией кошки и собаки использовалась модель ResNet50, которая тоже скачалась при запуске скрипта и заняла 98MB. Не хотелось бы выкачивать все модели и занимать слишком много места на диске, поэтому я поправил скрипт для использования ResNet50 вместо VGG16.

import numpy as np
import tensorflow
from tensorflow.keras.preprocessing import image
from tensorflow.keras.utils import get_file

from tensorflow.keras.applications.vgg16 import preprocess_input
from tensorflow.keras.applications.resnet50 import preprocess_input, decode_predictions
from tf_explain.core.grad_cam import GradCAM

import PIL
from PIL import Image, ImageDraw, ImageFont

import matplotlib.pyplot as plt
import matplotlib.cm as cm
import matplotlib.image as mpimg

from argparse import ArgumentParser

import glob
import os

USE_VGG16 = False

if USE_VGG16:
    #Select a model to use, in this case VGG16
    model = tensorflow.keras.applications.vgg16.VGG16(weights='imagenet', include_top=True, input_tensor=None, input_shape=None, pooling=None, classes=1000)

    #Check with 'print(model.summary())', in this case it is "block5_conv3"
    last_conv_layer_name = "block5_conv3"

    #Must include layers between last convolutional layer and prediction layer
    #Layer names can be found through 'print(model.summary())'
    classifier_layer_names = ["block5_pool", "flatten", "fc1", "fc2", "predictions"]
else:
    model = tensorflow.keras.applications.resnet50.ResNet50()
    last_conv_layer_name = "conv5_block3_3_conv"
    #classifier_layer_names = ["conv5_block3_3_bn", "conv5_block3_out", "avg_pool", "predictions"]  #case 1
    classifier_layer_names = ["conv5_block3_3_bn", "avg_pool", "predictions"]                       #case 2
    
    ....

Согласно переменной USE_VGG16 будет использоваться модель либо VGG16, либо ResNet50.

Судя по описанию в скрипте, для визуализации необходимо выбрать последний сверточный слой, и все следующие слои до слоя "predictions" (Переменные last_conv_layer_name и classifier_layer_names). Посмотреть какие слои есть в модели можно скриптом:

import tensorflow as tf

USE_VGG16 = True

if USE_VGG16:
    model = tf.keras.applications.vgg16.VGG16(weights='imagenet', include_top=True, input_tensor=None, input_shape=None, pooling=None, classes=1000)
else:
    model = tf.keras.applications.resnet50.ResNet50()

print(model.summary())

Если использовать все слои, согласно рекомендации, то меня смутило обилие зеленого цвета поверх песка слева от морды собаки. Поэтому я попровал исключить слой "conv5_block3_out", стало чуть лучше и более похоже на то, что выдала модель VGG16 в оригинальном скрипте. Но результат все равно выглядит странно, т.к. ожидалось, что сеть окажет большее внимание ушам собаки, как это было на иллюстрации в книге. Не понятно, что я сделал не так :).

"Тепловой цвет" можно сделать более выраженным, если повысить коэффициент модифицированного слоя при наложении на оригинальное изображение. Например использовать 0.7, вместо 0.4 в исходном скрипте.

  ...
  # Superimpose the heatmap on original image
  #superimposed_img = jet_heatmap * 0.4 + img  - ORIGINAL
  superimposed_img = jet_heatmap * 0.7 + img
  superimposed_img = tensorflow.keras.preprocessing.image.array_to_img(superimposed_img)
  ...

Запуск скриптов на кошке показал, что слой "conv5_block3_out" все-таки надо использовать, иначе засвечивается совсем что-то неадекватное. Итоговый результат тепловой карты для разных моделей, (коэффициент подкрашивания 0.7):

Похоже, что сеть VGG16 справляется все-таки лучше, чем ResNet50.

WSL: Windows For Linux

В книге используются команды Linux для скачивания наборов данных, их сортировки и прочего. Искать аналоги этим командам в Windows - лишняя трата времени, потребуется ставить различные утилиты и смотреть как это все работает. Гораздо проще запустить Linux внутри Windows, если используется Windows 10 и выше. Есть много наглядных видео-инструкций как это сделать, например PyLounge: WSL На Windows 10. Краткая напоминалка:

Команды Powershell для управления WSL:

// Посмотреть список запущенных подсистем
  wsl --list --running  
// Закрыть конкретную подсистему  
  wsl -t Ubuntu-20.04   
// Закрыть все подсистемы
  wsl --shutdown

Доступ к диску С в окне Ubuntu:

// ChangeDirectory
  cd /mnt/c  
// List содержимого
  ls

Команды Linux:

  ls : вывести список (list)
  grep cat : выбрать в списке имена содержащие шаблон, в данном случае "cat" в имени файла
  sort : упорядочить имена, 
    sort -R : упорядочить в случайном порядке (Random)
  head -250 : выбирает 250 имен с начала списка (tail с конца)
  mv : Move file
  xargs : конструирует строку параметров и исполняет с ней команду
    xargs -I : ReplaceString 
    xargs -I {} mv {} train/cat/ : перемещает каждый файл из входного списка в директорию
    
  // В текущей директории выбираем все файлы содержащие в имени "cat", 
  // случайно сортируем и 250 первых файлов перемещаем в директорию train/cat/
  ls | grep cat | sort -R | head -250 | xargs -I {} mv {} train/cat/