- BrainTools - https://www.braintools.ru -
Это восьмая часть проекта.
Часть 7: Экосистема AI‑агентов в PHP — от вызовов OpenAI до мультиагентных платформ [1]
Часть 6: Bag of Words и TF‑IDF — как компьютер превращает текст в математику [2]
Часть 5: От массивов к GPU: как PHP‑экосистема приходит к настоящему ML [3]
Часть 4: Практическое использование TransformersPHP [4]
Часть 3: Практика без Python и data science [5]
Часть 2: Собираем простейшую RAG‑систему на PHP с Neuron AI за вечер [6]
Часть 1: Как я пытался подружить PHP с NER — драма в 5 актах [7]
В предыдущих статьях серии мы разбирались, как компьютер работает с текстом: превращает слова в числовые представления, вычисляет сходство между документами и извлекает полезную информацию из текстовых данных.
Но что насчёт изображений?
Компьютер не видит фотографию так, как её видит человек. Для него изображение — это набор чисел, описывающих яркость и цвет отдельных пикселей. И чтобы научиться распознавать объекты, находить границы или определять визуальные признаки, нужно научиться работать с этими числами.
Для решения таких задач используются свёрточные нейронные сети (Convolutional Neural Networks, CNN [8]). Они широко применяются в компьютерном зрении [9] — от распознавания объектов до анализа медицинских изображений.
Одна из основных операций таких сетей — свёртка (convolution). С её помощью сеть анализирует небольшие участки изображения и обнаруживает локальные признаки: границы, линии, текстуры и другие структуры.
Но как именно это работает?
В этой статье мы разберём механику свёртки и реализуем её самостоятельно на чистом PHP, без сторонних библиотек и готовых ML‑алгоритмов.
Возьмём небольшое изображение в виде двумерной матрицы, создадим фильтр размером 3 × 3 и научимся находить с его помощью вертикальные границы. Затем изменим фильтр, проведём несколько экспериментов и посмотрим, как меняются результаты (все примеры кода в этой статье даны для PHP 8.1 или старше).
Наша цель — не построить полноценную нейронную сеть, а разобраться в одной из операций, которая лежит в основе CNN, и понять, что происходит внутри свёрточного слоя.
Когда мы смотрим на фотографию, мы видим объекты, формы, линии и цвета. Компьютер же работает с числами.
Любое цифровое изображение состоит из небольших элементов — пикселей (pixels). Каждый пиксель содержит информацию о цвете и яркости.
Например, в чёрно‑белом изображении значение пикселя обычно находится в диапазоне от 0 до 255:
0 — чёрный цвет;
255 — белый цвет;
значения между ними — различные оттенки серого.
Таким образом, небольшое изображение можно представить в виде двумерной матрицы чисел.
Например:
В этом примере левая часть изображения тёмная, а правая — светлая.
Если представить эту матрицу визуально, получится простое изображение с вертикальной границей между двумя областями.
Теперь представим, что нам нужно найти границу между тёмной и светлой областями.
Для человека это очевидно: достаточно посмотреть на изображение. Но компьютер должен выполнить определённые вычисления, чтобы обнаружить такое изменение яркости.
Один из способов решить эту задачу — использовать небольшую матрицу весов, которую называют ядром свёртки (kernel).
Она перемещается по изображению, анализирует небольшие участки пикселей и вычисляет, насколько сильно они отличаются друг от друга.
Именно этот механизм мы рассмотрим дальше.
Свёртка — это операция, при которой небольшая матрица весов перемещается по входной матрице. В каждой позиции соответствующие элементы двух матриц перемножаются, после чего результаты складываются.
Матрица весов называется ядром свёртки (kernel), а результат применения ядра ко всем допустимым позициям входа — картой признаков (feature map).
Давайте разберём, как это работает на практике.
Предположим, что у нас есть изображение, представленное матрицей чисел. Чтобы найти на нём вертикальные границы, воспользуемся фильтром размером 3 × 3:
Отрицательные веса находятся слева, положительные — справа.
Такое расположение не случайно. Фильтр сравнивает яркость левой и правой частей небольшого участка изображения. Если значения справа заметно отличаются от значений слева, результат вычисления будет большим по абсолютной величине.
Если же обе части имеют примерно одинаковую яркость, положительные и отрицательные слагаемые будут компенсировать друг друга.
Теперь представим, что мы накладываем наше ядро на изображение.
Фильтр охватывает участок размером 3 × 3 и выполняет вычисление. Затем перемещается на одну позицию вправо и повторяет операцию.
Когда достигает правого края, переходит на следующую строку.
Таким образом фильтр последовательно обрабатывает все допустимые участки изображения.
Каждое вычисление даёт одно число. Собрав все полученные значения, мы получим новую матрицу — карту признаков (Feature Map).
Возьмём небольшой участок изображения:
И применим к нему наше ядро:
Перемножим элементы, расположенные на одинаковых позициях, и сложим результаты:
Получаем:
Результат равен 60.
Это число становится одним элементом выходной карты признаков. Затем фильтр перемещается на следующую позицию, и вычисление повторяется.
Здесь есть небольшая техническая особенность.
В математике [10] операция свёртки предполагает, что ядро предварительно переворачивается на 180 градусов. Однако в большинстве реализаций свёрточных слоёв нейронных сетей используется другая операция — cross‑correlation, при которой ядро применяется без переворота.
Именно такой вариант мы будем использовать в нашей PHP‑программе.
В контексте глубокого обучения [11] эту операцию обычно тоже называют свёрткой (convolution).
После того как фильтр обработает всё изображение, мы получим новую матрицу — Feature Map.
Она показывает, насколько сильно выбранный фильтр реагирует на разные участки изображения.
Большие положительные или отрицательные значения указывают на сильный отклик фильтра, а значения, близкие к нулю, — на отсутствие выраженного признака, который он ищет.
Теперь, когда мы разобрались с принципом работы свёртки, можно перейти к реализации. Попробуем написать эту операцию самостоятельно на PHP и проверить, действительно ли наш фильтр способен находить вертикальные границы.
Теперь, когда мы разобрались с принципом работы свёртки, попробуем реализовать её самостоятельно.
Для этого нам не понадобятся сторонние библиотеки, готовые ML‑алгоритмы или специализированные инструменты для обработки изображений. Достаточно обычного PHP и нескольких вложенных циклов.
Для начала создадим небольшое изображение размером 5 × 5. Как и раньше, будем представлять его в виде двумерного массива чисел.
Левая часть изображения будет тёмной, а правая — светлой:
Между третьим и четвёртым столбцами находится вертикальная граница. Именно её мы хотим обнаружить.
Теперь определим ядро свёртки:
Отрицательные веса слева и положительные справа позволяют фильтру реагировать [12] на разницу яркости между левой и правой частями обрабатываемого участка.
В нашей реализации будем использовать следующие настройки:
stride = 1 — фильтр перемещается на один пиксель за раз;
padding = 0 — дополнительные пиксели по краям не добавляются;
ядро не переворачивается;
значения результата не ограничиваются диапазоном от 0 до 255.
Последний пункт особенно важен. Значения Feature Map — это результаты математических вычислений, а не исходные значения пикселей. Поэтому они могут быть отрицательными или превышать 255.
Создадим файл convolution.php и реализуем функцию convolve().
Она будет принимать две двумерные матрицы: входные данные и ядро свёртки. На выходе мы получим новую матрицу — карту признаков.
Для начала сделаем реализацию универсальной: она будет поддерживать прямоугольные матрицы и ядра произвольного размера.
/**
* Apply a convolution-style filter to a 2D matrix.
*
* This implementation uses cross-correlation:
* the kernel is not flipped.
*
* @param array<array<int|float>> $input
* @param array<array<int|float>> $kernel
* @return array<array<int|float>>
*/
function convolve(array $input, array $kernel): array
{
if (!array_is_list($input) || !array_is_list($kernel)) {
throw new InvalidArgumentException(
'Input matrix and kernel must use sequential numeric indexes.'
);
}
$inputHeight = count($input);
$inputWidth = count($input[0] ?? []);
$kernelHeight = count($kernel);
$kernelWidth = count($kernel[0] ?? []);
if ($inputHeight === 0 || $inputWidth === 0) {
throw new InvalidArgumentException('Input matrix must not be empty.');
}
if ($kernelHeight === 0 || $kernelWidth === 0) {
throw new InvalidArgumentException('Kernel must not be empty.');
}
foreach ($input as $row) {
if (!array_is_list($row) || count($row) !== $inputWidth) {
throw new InvalidArgumentException(
'Input matrix must be rectangular.'
);
}
}
foreach ($kernel as $row) {
if (!array_is_list($row) || count($row) !== $kernelWidth) {
throw new InvalidArgumentException(
'Kernel must be rectangular.'
);
}
}
if ($kernelHeight > $inputHeight || $kernelWidth > $inputWidth) {
throw new InvalidArgumentException(
'Kernel must fit inside the input matrix.'
);
}
$outputHeight = $inputHeight - $kernelHeight + 1;
$outputWidth = $inputWidth - $kernelWidth + 1;
$output = [];
for ($y = 0; $y < $outputHeight; $y++) {
$outputRow = [];
for ($x = 0; $x < $outputWidth; $x++) {
$sum = 0;
for ($ky = 0; $ky < $kernelHeight; $ky++) {
for ($kx = 0; $kx < $kernelWidth; $kx++) {
$pixel = $input[$y + $ky][$x + $kx];
$weight = $kernel[$ky][$kx];
$sum += $pixel * $weight;
}
}
$outputRow[] = $sum;
}
$output[] = $outputRow;
}
return $output;
}
/**
* Print a matrix in a readable format.
*/
function printMatrix(array $matrix): void
{
foreach ($matrix as $row) {
echo implode("t", array_map(
static fn (int|float $value): string => (string) $value,
$row
)) . "n";
}
}
Теперь добавим исходное изображение и ядро:
$image = [
[10, 10, 10, 200, 200],
[10, 10, 10, 200, 200],
[10, 10, 10, 200, 200],
[10, 10, 10, 200, 200],
[10, 10, 10, 200, 200],
];
$verticalEdgeKernel = [
[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1],
];
echo "Input image:n";
printMatrix($image);
echo "nVertical edge kernel:n";
printMatrix($verticalEdgeKernel);
echo "nFeature map:n";
$featureMap = convolve($image, $verticalEdgeKernel);
printMatrix($featureMap);
Теперь посмотрим на основные части реализации.
1. Вычисляем размер выходной матрицы
Поскольку мы не используем padding, фильтр может обрабатывать только те участки, которые полностью помещаются внутри исходного изображения.
Поэтому размеры результата определяются формулами:
Здесь:
( и (
) — ширина и высота входной матрицы;
() и (
) — ширина и высота ядра.
В нашем примере входная матрица имеет размер 5 × 5, а ядро — 3 × 3.
Следовательно:
Результирующая матрица будет иметь размер 3 × 3.
2. Перемещаем фильтр по изображению
За это отвечают два внешних цикла:
for ($y = 0; $y < $outputHeight; $y++) {
for ($x = 0; $x < $outputWidth; $x++) {
// Process current image region
}
}
Переменная $y определяет строку, а $x — столбец, в котором находится левый верхний угол фильтра.
На каждой позиции ядро обрабатывает участок изображения размером 3 × 3.
3. Перемножаем соответствующие элементы
Внутри внешних циклов находятся ещё два цикла:
for ($ky = 0; $ky < $kernelHeight; $ky++) {
for ($kx = 0; $kx < $kernelWidth; $kx++) {
$pixel = $input[$y + $ky][$x + $kx];
$weight = $kernel[$ky][$kx];
$sum += $pixel * $weight;
}
}
Здесь происходит основная работа.
Мы берём очередной пиксель из текущего участка изображения, умножаем его на соответствующий вес ядра и прибавляем результат к переменной $sum.
После обработки всех элементов ядра в $sum находится итоговое значение для текущей позиции фильтра.
4. Сохраняем результат
Когда обработка текущего участка завершена, добавляем полученное значение в выходную строку:
$outputRow[] = $sum;
После обработки всей строки добавляем её в результирующую матрицу:
$output[] = $outputRow;
Когда фильтр обработает все допустимые позиции, функция вернёт готовую Feature Map.
Таким образом, вся операция свёртки реализована с помощью четырёх вложенных циклов: два отвечают за перемещение фильтра, а ещё два — за вычисления внутри текущего участка изображения.
Функция convolve() готова. Теперь запустим программу и посмотрим, какую карту признаков она построит для нашего изображения.
Выполним команду:
php convolution.php
Получим следующий результат:
Input image:
10 10 10 200 200
10 10 10 200 200
10 10 10 200 200
10 10 10 200 200
10 10 10 200 200
Vertical edge kernel:
-1 0 1
-1 0 1
-1 0 1
Feature map:
0 570 570
0 570 570
0 570 570
В результате вместо исходной матрицы размером 5 × 5 мы получили новую матрицу размером 3 × 3.
Обратите внимание [13]: первая колонка карты признаков содержит нули, а две следующие — большие положительные значения.
Это именно то поведение [14], которого мы ожидали от выбранного фильтра.
Но почему получилась именно такая карта признаков? Давайте разберём несколько позиций фильтра вручную.
Начнём с первого участка изображения:
Применим к нему наше ядро:
Вычислим результат:
Все слагаемые взаимно компенсируются:
Фильтр не обнаруживает изменений яркости слева направо, поэтому результат равен нулю.
Теперь переместим фильтр вправо. Он захватит участок:
Для каждой строки вычисление выглядит одинаково:
Поскольку в фильтре три одинаковые строки, складываем три результата:
Получаем:
Фильтр обнаруживает значительное изменение яркости слева направо.
Переместим фильтр ещё на одну позицию вправо:
Для каждой строки:
Итоговое значение:
Результат снова равен 570.
Здесь есть интересный момент.
Может показаться, что два положительных значения в карте признаков означают наличие двух отдельных границ. Но это не так.
Оба результата возникают из‑за одного и того же перехода между тёмной и светлой областями.
Дело в том, что фильтр реагирует на разницу между левым и правым столбцами своего окна. Поэтому сильный отклик сохраняется в двух соседних позициях: в обеих случаях окно захватывает пиксели из тёмной и светлой областей.
Это нормальное поведение [15] выбранного ядра, а не ошибка [16] реализации.
Таким образом, мы не просто получили новую матрицу чисел. Мы увидели, как фильтр реагирует на определённую структуру изображения и преобразует её в карту признаков.
В следующем эксперименте попробуем изменить ядро и посмотрим, как это повлияет на результат.
До этого момента мы использовали один фильтр, который обнаруживает изменения яркости слева направо. Но что произойдёт, если изменить его веса?
Давайте проведём несколько экспериментов на том же изображении.
В предыдущем примере наше ядро реагировало на изменения яркости слева направо.
Теперь попробуем обнаруживать изменения сверху вниз.
Для этого изменим расположение весов:
Отрицательные веса находятся в верхней части ядра, а положительные — в нижней.
Теперь фильтр сравнивает верхнюю и нижнюю части каждого участка изображения.
Добавим новое ядро в программу:
$horizontalEdgeKernel = [
[-1, -1, -1],
[ 0, 0, 0],
[ 1, 1, 1],
];
$featureMap = convolve($image, $horizontalEdgeKernel);
echo "Horizontal edge feature map:n";
printMatrix($featureMap);
Запустим программу.
Результат:
0 0 0
0 0 0
0 0 0
Почему все значения равны нулю?
Посмотрим на наше исходное изображение. Все его строки одинаковые: сверху вниз яркость не меняется.
Поэтому верхняя и нижняя части каждого участка содержат одинаковые значения. Положительные и отрицательные слагаемые взаимно компенсируются.
Это означает, что выбранный фильтр не обнаружил изменений яркости сверху вниз.
Вернёмся к поиску вертикальных границ.
В исходном ядре отрицательные веса находились слева, а положительные — справа:
Теперь поменяем знаки всех весов:
В PHP это выглядит так:
$reverseVerticalKernel = [
[ 1, 0, -1],
[ 1, 0, -1],
[ 1, 0, -1],
];
$featureMap = convolve($image, $reverseVerticalKernel);
echo "Reverse vertical edge feature map:n";
printMatrix($featureMap);
Теперь результат будет другим:
0 -570 -570
0 -570 -570
0 -570 -570
Обратите внимание: абсолютные значения остались прежними, но знак изменился.
Почему?
Потому что теперь положительные веса находятся слева, а отрицательные — справа.
Таким образом, фильтр реагирует не просто на наличие границы, а также на направление изменения яркости.
В нашем примере:
положительный результат соответствует переходу от тёмной области к светлой слева направо;
отрицательный результат соответствует переходу от светлой области к тёмной слева направо.
Если нас интересует сила отклика независимо от направления, можно использовать абсолютное значение результата.
Теперь проведём ещё один эксперимент.
До этого яркость тёмной области составляла 10, а светлой — 200.
Разница между ними была:
Попробуем уменьшить контраст изображения. Для этого заменим значения светлых пикселей с 200 на 100:
$image = [
[10, 10, 10, 100, 100],
[10, 10, 10, 100, 100],
[10, 10, 10, 100, 100],
[10, 10, 10, 100, 100],
[10, 10, 10, 100, 100],
];
$featureMap = convolve($image, $verticalEdgeKernel);
echo "Feature map with lower contrast:n";
printMatrix($featureMap);
Теперь разница яркости составляет:
Посмотрим на результат:
0 270 270
0 270 270
0 270 270
Отклик уменьшился с 570 до 270.
Это ожидаемо: фильтр вычисляет взвешенную сумму значений пикселей. Когда разница между тёмной и светлой областями становится меньше, уменьшается и результат вычисления.
Таким образом, мы видим, что величина отклика зависит не только от расположения границы, но и от контраста между соседними областями.
Мы использовали одно и то же изображение, но меняли ядра и значения пикселей.
В результате увидели несколько важных особенностей:
Разные фильтры реагируют на разные направления изменения яркости.
Изменение знаков весов меняет знак отклика.
Уменьшение контраста приводит к уменьшению абсолютной величины отклика.
Один и тот же фильтр может давать сильный отклик в нескольких соседних позициях.
Именно поэтому в свёрточных нейронных сетях используется множество фильтров. Каждый из них может реагировать на определённые особенности входного изображения.
Но в нашем примере все веса заданы вручную. В настоящей CNN сеть учится подбирать их самостоятельно в процессе обучения.
Давайте разберёмся, как наша реализация связана с работой свёрточных нейронных сетей.
На первый взгляд может показаться, что мы просто написали небольшой алгоритм обработки изображений. Но именно операция, которую мы реализовали, лежит в основе свёрточных слоёв CNN.
Разница заключается в том, откуда берутся веса фильтров.
В нашей программе мы задали ядро вручную:
$verticalEdgeKernel = [
[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1],
];
Мы заранее знали, какой признак хотим обнаружить, и самостоятельно подобрали подходящие значения.
В свёрточной нейронной сети всё происходит иначе.
Вместо одного заранее определённого фильтра сеть использует множество фильтров. Каждый из них имеет собственный набор весов и формирует свою карту признаков.
В процессе обучения значения этих весов корректируются так, чтобы сеть могла лучше решать поставленную задачу.
Например, один фильтр может научиться реагировать на вертикальные границы, другой — на горизонтальные, третий — на определённые текстуры или другие локальные структуры.
При этом нам не нужно заранее объяснять сети, какие именно признаки следует искать. Она учится находить полезные закономерности на основе обучающих данных.
Упрощённо процесс можно представить следующим образом:
На вход поступает изображение.
Свёрточный слой применяет к нему несколько фильтров.
Каждый фильтр формирует собственную карту признаков (Feature Map).
В процессе обучения сеть корректирует веса фильтров, уменьшая ошибку предсказания.
Несколько свёрточных слоёв могут последовательно извлекать всё более сложные признаки.
Например, первые слои могут реагировать на простые структуры: границы, линии и текстуры. Более глубокие слои могут объединять эти признаки в более сложные визуальные представления.
Одна из важных особенностей CNN — повторное использование одних и тех же весов фильтра на разных участках изображения. Этот принцип называется Weight Sharing (разделение весов).
Вместо того чтобы создавать отдельные веса для каждого пикселя и каждой позиции, сеть применяет одно и то же ядро ко всему изображению.
Это позволяет значительно сократить количество обучаемых параметров и использовать локальные зависимости между соседними пикселями.
Именно поэтому свёрточные сети хорошо подходят для работы с изображениями.
Важно понимать разницу между нашим примером и полноценной CNN.
Мы реализовали саму операцию cross‑correlation, которая используется внутри свёрточного слоя. Но мы не обучали фильтры, не применяли функцию активации и не строили последовательность слоёв.
Тем не менее теперь мы понимаем один из основных механизмов CNN не только на уровне теории, но и на уровне кода.
Мы увидели, как небольшое ядро перемещается по изображению, вычисляет локальные признаки и преобразует входную матрицу в карту признаков.
Это и есть основа работы свёрточного слоя.
Мы реализовали свёртку на чистом PHP и увидели, как с помощью небольшого фильтра можно обнаружить вертикальную границу между тёмной и светлой областями.
Но важно понимать: это учебный пример, который демонстрирует только одну из операций, используемых в свёрточных нейронных сетях. У него есть несколько ограничений.
1. Фильтр задан вручную
Мы сами определили веса ядра:
$verticalEdgeKernel = [
[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1],
];
В настоящей CNN фильтры не задаются разработчиком вручную. Их веса подбираются в процессе обучения на примерах данных.
2. Используется фиксированный шаг перемещения
В нашей реализации фильтр каждый раз сдвигается на один пиксель (stride = 1). Другие варианты шага мы не рассматриваем.
3. Не используется Padding
Фильтр обрабатывает только те участки, которые полностью помещаются во входную матрицу. Поэтому размер Feature Map получается меньше размера исходного изображения.
4. Нет дополнительных операций нейронной сети
Наша функция выполняет только умножение элементов матрицы на веса ядра и суммирование результатов. Мы не применяем функцию активации, нормализацию или другие операции, которые могут входить в состав свёрточного слоя.
5. Работаем с одной матрицей яркости
В примере каждый пиксель представлен одним числом. Реальные цветные изображения обычно содержат несколько каналов, например RGB. В таком случае свёрточный слой учитывает все каналы при вычислении отклика фильтра.
6. Используется один фильтр
Мы создали только одно ядро, которое реагирует на вертикальные границы. В настоящей CNN обычно применяется несколько фильтров. Каждый из них может обнаруживать свои признаки и формировать отдельную Feature Map.
Таким образом, наша реализация не является полноценной свёрточной нейронной сетью. Она показывает базовый механизм, на котором строится одна из ключевых операций CNN.
И этого достаточно для нашей задачи: мы увидели, как небольшое ядро перемещается по матрице, вычисляет отклик и выделяет границы между областями с разной яркостью.
В этой статье мы разобрали одну из основных операций свёрточных нейронных сетей — свёртку — и реализовали её на чистом PHP.
Мы взяли небольшую матрицу яркости, создали ядро 3 × 3 и написали функцию, которая перемещает это ядро по матрице, перемножает соответствующие значения и суммирует результаты.
В результате получили Feature Map, на которой хорошо видна граница между тёмной и светлой областями.
Затем мы провели несколько простых экспериментов: изменили направление фильтра, попробовали найти горизонтальные границы и уменьшили контраст между областями. Каждый раз результат менялся в соответствии с тем, какие признаки искал наш фильтр.
Конечно, это ещё не полноценная CNN. Мы не обучали фильтры, не использовали несколько свёрточных слоёв и не работали с настоящими изображениями. Но мы разобрали сам механизм свёртки — без сторонних библиотек и готовых ML‑алгоритмов.
Именно с таких небольших примеров проще всего начинать изучение машинного обучения: не просто использовать готовые инструменты, а понимать, какие вычисления стоят за ними.
Главная идея: свёрточная сеть анализирует изображение не целиком, а небольшими локальными участками, постепенно извлекая из них полезные признаки. Свёртка — один из механизмов, который позволяет ей это делать.
Если вам интересна тема AI и Machine Learning в PHP и вы хотите разобраться в ней глубже, приглашаю продолжить знакомство с помощью моих материалов:
Книга: AI для PHP‑разработчиков: интуитивно и на практике [17] — последовательное знакомство с основами AI и ML с примерами на PHP.
Интерактивные примеры: PHP AI Playground [18] — возможность запустить примеры, поэкспериментировать с параметрами и самостоятельно изучить код.
Онлайн‑курс: AI & ML Foundations for PHP Developers [19] — бесплатный курс, который поможет систематизировать знания и разобраться в основных концепциях AI и Machine Learning.
Все материалы доступны бесплатно. Буду рад, если они помогут вам открыть для себя новые возможности AI в PHP!
Автор: samako
Источник [20]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/38213
URLs in this post:
[1] Экосистема AI‑агентов в PHP — от вызовов OpenAI до мультиагентных платформ: https://habr.com/ru/articles/1041594/
[2] Bag of Words и TF‑IDF — как компьютер превращает текст в математику: https://habr.com/ru/articles/1034874/
[3] От массивов к GPU: как PHP‑экосистема приходит к настоящему ML: https://habr.com/ru/articles/1019250/
[4] Практическое использование TransformersPHP: https://habr.com/ru/articles/993966/
[5] Практика без Python и data science: https://habr.com/ru/articles/984042/
[6] Собираем простейшую RAG‑систему на PHP с Neuron AI за вечер: https://habr.com/ru/articles/966792/
[7] Как я пытался подружить PHP с NER — драма в 5 актах: https://habr.com/ru/articles/948014/
[8] Convolutional Neural Networks, CNN: https://en.wikipedia.org/wiki/Convolutional_neural_network
[9] зрении: http://www.braintools.ru/article/6238
[10] математике: http://www.braintools.ru/article/7620
[11] обучения: http://www.braintools.ru/article/5125
[12] реагировать: http://www.braintools.ru/article/1549
[13] внимание: http://www.braintools.ru/article/7595
[14] поведение: http://www.braintools.ru/article/9372
[15] поведение: http://www.braintools.ru/article/5593
[16] ошибка: http://www.braintools.ru/article/4192
[17] AI для PHP‑разработчиков: интуитивно и на практике: https://apphp.gitbook.io/ai-for-php-developers/
[18] PHP AI Playground: https://aiwithphp.org/books/ai-for-php-developers/examples/
[19] AI & ML Foundations for PHP Developers: https://eventumx.com/
[20] Источник: https://habr.com/ru/articles/1105232/?utm_campaign=1105232&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.