2. Типы данных в ML и их поддержка
Почему я так привязался к ML-типам данных и к индексам (позициям)?
Есть определённая связь с этим пунктом:
1. Генерация датасета как основа понимания
Очень много видов информации - это тензоры, в которых признаки разбросаны по позициям с какой-то абсолютной или относительной логикой, с шумом, с систематической ошибкой и др. Свёрточные слои осматривают пространство в окрестности, это простой вариант без перехода к изучению позиций. Если я вывожу ручкой цифру "7", я тыкаю её где-то в район (0.25, 0.25), потом веду вправо в точку (0.75, 0.25), затем вниз - в точку (0.5, 0.75). Назовём это генерацией шаблона цифры "7". Если я немного отклонился от шаблона, например, немного промахнулся от стартовой точки или вместо горизонтальной прямой линии начал рисовать загогулину, то это всё влияет на конечное отображение. То есть мы пишем цифры не по шаблону, а динамически. Иными словами, положения опорных элементов цифры зависят друг от друга, а не от абсолютных координат опорных точек шаблона. И вот именно так разбор генерации датасета приводит к пониманию.
Если бы мы могли получить опорные точки реальной цифры и их координаты. Например, для цифры "7" таких три опорных точки, как минимум. Но, например, у цифры "0" трудно выделить опорные точки, скорее паттерн типа "дуга". Тут нужно говорить ещё и на языке радиусов изгиба дуг... Вверху/внизу дуги загнуты сильнее, по бокам - меньше.
Разработал слои GatedMaxPePool1D и ConvMaxPePool1D, первый проявил неплохую выживаемость на последовательностях, не хуже второго. Видимо, свёртка избыточна в той задаче, а может и вообще в принципе.
Сравним со стандартным макс-пулингом. Макс-пулинг (1D-вариант) делает из последовательности другую последовательность, меньшую по размеру. Sequence-to-sequence. GatedMaxPePool1D делает то же самое, но несколько сложнее. Во-первых, вместе с вычислением max производится вычисление положения максимума (argmax). Pe - обозначает Positional encoding. При этом для каждого пула можно вычислить два вида позиции - локальная и глобальная. Локальная позиция в пределах окна макс-пулинга, а глобальная - это позиция в последовательности в целом. Тест на выживаемость пока прошла только глобальная позиция. Я рассматривал макс-пулинг целостно совместно со свёрточным слоем, после которого он обычно стоит. Поэтому присоединил на входе gate-слой. Это слой весов, которые умножаются на входные признаки по правилу Адамара, т.е. просто почленно. Считаю, что свёртка не нужна, так как макс-пулинг с позицией максимума выполняет ту же работу не хуже. Естественно, слой должен быть многоканальным (многоголовым). В итоге на выходе довольно большой тензор, который я анализирую выходным линейным слоем с ReLU и заодно резко сокращаю размер. Вроде взял за основу MaxPool1D, но сильно всё переиграл. На самом деле всё-таки они сильно родственны.
-- 01.12.2025, 20:49 --seq_len=11
in_features = 2
Сначала осуществляется умножение Адамара на весы, получаем последовательность той же размерности, например, рассмотрим одно окно пулинга:
-0.01 -0.75
-0.75 +0.30
+0.45 -0.70
+0.11 +0.52
+0.12 +0.80
-0.08 -1.00
-0.98 +0.07
+0.06 +0.08
-0.61 -0.77
+0.88 +0.11
+0.48 -0.99
Добавляем локальные позиции (в пределах окна пулинга), находим max и argmax для каждого признака:
0.0 -0.01 0.0 -0.75
0.1 -0.75 0.1 +0.30
0.2 +0.45 0.2 -0.70
0.3 +0.11 0.3 +0.52
0.4 +0.12
0.4 +0.800.5 -0.08 0.5 -1.00
0.6 -0.98 0.6 +0.07
0.7 +0.06 0.7 +0.08
0.8 -0.61 0.8 -0.77
0.9 +0.88 0.9 +0.11
1.0 +0.48 1.0 -0.99
Выход:
0.9 +0.88 0.4 +0.80Это только одно окно и один канал.
В итоге данных собирается гораздо больше. И эта информация намного информативнее, чем в связке conv-maxpooling.