Скахин Алексей / pihel
Это краткий пересказ курса Нейронные сети и компьютерное зрение.
В этой заметке больше внимания будет уделено практической части. Теорию можно почитать в предыдщей статье «Введение в нейронные сети».
Код практической части на гитхабе курса.
Библиотека PyTorch
Для ML будет использоваться библиотека PyTorch
Преобразование из numpy и обратно
Преобразование Numpy массива в torch tensor и обратно: x = torch.from_numpy(x) x = x.numpy()
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') x_cuda = x.to(device)
import torch #тензор с данными w = torch.tensor([8., 8.], requires_grad=True) #функция потерь, для которой считаем производную def my_func(variable): return 10 * (variable ** 2).sum() optimizer = torch.optim.SGD([w], lr=0.001) #шаг градиента в сторону уменьшения потерь (в обратную сторону от производной) def make_gradient_step(function, variable): function_result = function(variable) function_result.backward() optimizer.step() optimizer.zero_grad()
Задачи, решаемые при помощи нейронных сетей
Регрессия
Аппроксимация обучающей цифровой выборки в виде функции (нахождение вещественного числа от входных параметров)
Пример регрессии на PyTorch для предсказания функции 2**x * torch.sin(2**-x)
Бинарная классификация
Разделение выборки на 2 класса (да / нет)
Функция активации — сигмойда.
Для валидации лучше подходит функцию потерь: бинарная кросс-энтропия
BCE(p,t) = (t-1)*log(1-p) — t*log(p) Производная функции потерь BCE = сигма — реальное значение
Квадрат ошибки (MSE) — очень часто дает 0 при производной, т.е. по ней плохо обучаться (замедления обучения при уменьшении ошибки)
Классификация нескольких классов
Если выходов много, то лучше использовать softmax функцию активации = e^ответ/SUM(e^ответы)
Каждый выход дает результат от 0 до 1, но в сумме все выходы дают 1
Функция потерь = кросс энтропия
Производная функции потурь = pi-ti
Пример классификации вина на 3 типа по 2 признакам
Локализация
Выходы = вероятность, координаты, ширина/высота
Выходы «вероятность, координаты» можно обработать сигмойдой (ограничение: центр объекта должен быть на картинке, т.к. сигма 0,5 указывает на середину)
Функция потерь = BCE
Ширина/высота — как exp(выход), т.к. она (0-бесконечности)
функция потерь = выход — log(ширины или высоты)
Сегментация
Отделение искомого объекта от остальных (для каждого пикселя выдается скор, что в этом пикселе есть искомый объект)
Активация = сигмойда, т.к. ищем вероятность
Функция потерь = SUM(BCE) по всем пикселям картинки
Сжатие размерности
Кодирование картинки в меньший массив чисел
потеря = BCE по всем пикселям между исходной и на выходе после расшифрования
SuperreSolution
улучшение разрешения картинки (1пкс входной = 4 выходной)
обучение на сжатых больших картинках и сравнение с большими
Методы оптимизации скорости обучения
SGD — градиентный спуск
* стохастический — спуск на 1 примере
* батчевый — спуск на N примеров (в питоне также называется стохастическим)
стохастический спуск плох для вытянутых функций, т.к. нужно много шагов зигзагом, которые почти не смещаются
* стохастический спуск с моментумом (импульсом) — физическое моделирование катящегося шара (спуск идет не зигзагами, а затухающей синусойдой)
+ лосс функция больше влияет на скорость, а не только координаты (шар как бы ускоряется в сторону минимума)
* экспеденциальное скользящее среднее — к координате прибавляется среднее лосс функции
RPROP — learning rate = учитывает только знак градиента * learning rate *1.2 (+) или 0,6 (-)
RMSprop — подстройка learning rate под величину градиента (если медленно меняется — больше LR, быстро спускаемся — уменьшаем LR)
Adam — подстройка скорости (стохастически с импульсом + RMSprop) под величину градиента
Пример классификации рукописных цифр используя Adam (MNIST — тестовый сет)
Сверточные нейронные сети
Сверточные нейронные сети — для распознавания объекта на изображении в любой его части
(для обычной сети пришлось бы иметь картинки с объектами во всех возможных координатах)
Используется для решения проблемы ограниченных ресурсов и вычисления дополнительных данных изображения (какие пиксели находятся рядом)
Свертка:
* исходное изображение дополняется padding по 1 пикселю со всех сторону
* выбирается ярдро сверки: к примеру, матрица 3*3 из -1,0,1
* ядро последовательно прикладывается своим центром к каждому пикселю изображения (захватывая краями паддинг)
* каждый пиксель ядра перемножается с пикселем наложенного изображения и складывается, результат перезаписывает пиксель изображения
=> в каждом пикселе изображения у нас есть информация о соседях на удалении ядра
=> чем больше ядро, тем больше паддинг (максимальный размер паддинга = ядру)
=> сверку можно делать с большим другим шагом (stride) — это сожмет нам размер изображения / шаг (шаг увеличивается по обоим осям)
для каждого канала цвета (RGB) свое ядро — что является выходом
Пулинг:
* исходной изображение бьется на области размером strideX, strideY (обычно не пересекающиеся)
* выбирается максимум (или другая агрегация) из области
* двигаемся по областям
* записываем максимумы в результирующую матрицу
Что дает сжатие за счет выбора наиболее значимого значения
Пример программы расчета размера выходной матрицы после свертки на основе входящих размеров, шага и отступов:
input_matrix_shape содержит: число изображений в батче (0), число слоев в одном изображении (1), высота изображения (2), ширина изображения (3)
def calc_out_shape(input_matrix_shape, out_channels, kernel_size, stride, padding): hout = np.fix((input_matrix_shape[2] + 2*padding - 1 * (kernel_size-1)-1)/stride + 1) wout = np.fix((input_matrix_shape[3] + 2*padding - 1 * (kernel_size-1)-1)/stride + 1) out_shape = [input_matrix_shape[0], out_channels, hout, wout] return out_shape
torch.nn.Conv2d(input_matrix_shape[1], out_channels, kernel_size, stride, padding )
Пример расчета выходных значений на основе реальных чисел:
Какого размера получится результат свёртки 5 на 5 без паддингов, со страйдом (1,1), если на выходе должно быть 6 каналов? Входное изображение имеет размер 32 на 32:
calc_out_shape([1,1,32,32], 6, 5, 1, 0) #== [1, 6, 28.0, 28.0]
Архитектуры предобученных сетей
LeNet
1. Свертка ядром 5*5 без паддингов (края по 4 пкс теряются) с шагом 1 в 6 разных фильтров
2. Пулинг 2*2 => в итоге получится 6 каналов изображение 28/2 = 14
3. Активация черзе гиперболический тангенс
4. сверка по 16 каналов с фильтром 5 на 5 => получится изображение 16 каналов размером 10*10
5. max пуллинг 2*2 => 16 каналов 5*5
6. Активация через гиперболический тангенс
7. 5*5 матрица сводится к массиву 25 элементов * 16 слоев == 1*1*400 тензор
8. полносвязанный слой (torch.nn.Linear(400, 120) ) => 120 элементов + тангенс
9. полносвязанный слой => 84 элементов + тангенс
10. полносвязанный слой => 10 элементов + softmax
функция потерь = кросс энтропия (т.к. хорошо работает с softmax)
оптимизатор: adam (с параметрами по умолчанию)
Пример реализаций через макспулинг и Relu
AlexNet
классификация на 1000 классов 15 млн изображений
масштабирование до: 224*224 (3 канала) — свертка 11*11 (шаг 4, паддинг = 5) — свертка 5*5 — пулинг — свертка 3*3 — макс пулинг — . — полносвязанные списки (активация Relu) — вконце softmax
В AlexNet сигмойда заменена на ReLu функцию активации
Минус сигмойды: несколько сигмойд друг за другому дают затухание градиента, т.к. сигма’=сигма(1-сигма) , т.е. это число всегда очень маленькое число (затухание градиента)
Минус гиперболического тангенса: производная = (1-tan)*(1+tan) SUM(log(вероятность независимости наблюдения)) ==> среднеквадратическая ошибка