- BrainTools - https://www.braintools.ru -
Почему PHP-массивы плохо подходят для математики [1], как появились Tensor и NDArray, и зачем RubixML в итоге решил пойти в сторону GPU.
Это четвёртая часть проекта.
Часть 4: Практическое использование TransformersPHP [2]
Часть 3: Практика без Python и data science [3]
Часть 2: Собираем простейшую RAG-систему на PHP с Neuron AI за вечер [4]
Часть 1: Как я пытался подружить PHP с NER — драма в 5 актах [5]
Когда я разговариваю с кем-то или пишу статью на тему “машинное обучение [6] на PHP”, то реакция [7] большинства обычно предсказуема: в ответ мне либо усмехаются, либо тычут в лицо Python-ом, либо минусуют (за исключением тех, кто искренне интересуется этой темой).
Но в целом вывод такой – это просто не та задача для этого языка.
И я честно признаю – в этом есть доля правды. Но…
Вот уже несколько лет я занимаюсь тем, что пытаюсь делать всё возможное в деле продвижения “ML в PHP”. Почему? Точно не знаю, если честно. Я сам не раз задавал себе этот вопрос и точного ответа на него у меня нет (даже для самого себя).
Друзья, я не сумасшедший и не повёрнутый на некой идее “фикс” психопат.
Я обычный программист, который любит программировать PHP.
Этот язык кормил меня долгие годы (да и сейчас продолжает кормить). Он не идеален, но в целом мне нравится как он растёт и куда развивается.
В общем, кажется что просто сама постановка задачи “ML в PHP” звучит для меня неким вызовом, что заставляет сидеть по ночам, пытаясь самому разбираться в математических формулах или реализовать алгоритм очередной ML модели на PHP. И если честно – мне это в кайф!
Конечно, PHP никогда не проектировался как платформа для численных вычислений. У него нет встроенной поддержки векторных операций, контроля над памятью [8] или доступа к low-level оптимизациям, которые считаются нормой в мире scientific computing.
И всё же – машинное обучение в PHP существует.
Причём не как эксперимент, а как часть реальных систем:
inference прямо в веб-приложениях
обработка данных внутри SaaS
автоматизация и встроенная аналитика
и с каждым годом месяцем использование ML в PHP продолжает расти.
Если вы сомневаетесь, то гляньте сюда:
👉 https://github.com/apphp/awesome-php-ml [9]
Это наиболее полный тщательно подобранный список библиотек для машинного обучения, искусственного интеллекта [10], обработки естественного языка, LLM и анализа данных для PHP. Этот, далеко не полный список, содержит ссылки на более чем 140 ресурсов. Экосистема ML в PHP не шумная, но довольно зрелая. Она состоит из четырёх слоев:
библиотеки классического ML
математический фундамент
инструменты интеграции с современными ML-системами
интеграция с внешними ML-сервисами
В этой статье мы затронем первые два слоя, два других коснёмся чисто условно.
Итак, несмотря на то, что экосистема ML в PHP живёт и процветает, если посмотреть глубже, то станет понятно: развивалась она не благодаря, а вопреки ограничениям языка и с помощью его отдельных фанатов–энтузиастов (постараюсь в статье упомянуть всех, кого я знаю).
В очередной раз скажу банальность – эта статья не про обзор библиотек и не попытка сравнить PHP с Python.
Это история того, как PHP-сообщество шаг за шагом приближалось к одному и тому же выводу:
проблема не в алгоритмах, а в том, как устроена сама среда выполнения
Сначала векторы и матрицы реализовывались как обычные PHP-массивы. Потом появились попытки оптимизации. Затем – нативные структуры, написанные на C и Rust. И в какой-то момент стало очевидно: даже этого недостаточно.
Следующий шаг оказался неизбежным – вынос вычислений на GPU.
Мы с вами попробуем пройти этот путь последовательно: от наивных реализаций до современных решений вроде Tensor, NDArray и GPU-бэкендов в RubixML.
И по ходу увидим, как менялось не только API, но и само понимание: где заканчивается PHP – и начинается настоящая вычислительная система или оркестрация.
Если коротко: это история не про то, как делать ML в PHP, а про то, почему его пришлось делать по-другому.
Если отбросить теорию, матрица – это просто таблица чисел. А раз в PHP уже есть массивы – значит, можно представить матрицу как массив массивов:
$matrix = [
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
];
Именно на этой идее и были построены первые библиотеки машинного обучения в PHP:
PHP-ML – https://github.com/jorgecasas/php-ml [11] (от Arkadiusz Kondas)
ранние версии RubixML – https://github.com/RubixML/RubixML [12] (от Andrew DalPino)
и несколько прочих
Всё делалось в чистом PHP – без расширений и нативного кода (то есть все вычисления выполнялись самим интерпретатором, без низкоуровневых оптимизаций).
С точки зрения [13] простого разработчика это было максимально удобно: не нужно ничего компилировать, можно дебажить привычными инструментами и код полностью прозрачен.
Даже такие операции, как умножение матриц, реализовывались буквально “в лоб” – с помощью трёх вложенных циклов (triple loop)
function matmul(array $a, array $b): array {
$result = [];
$rowsA = count($a);
$colsA = count($a[0]);
$colsB = count($b[0]);
for ($i = 0; $i < $rowsA; $i++) {
for ($j = 0; $j < $colsB; $j++) {
$sum = 0.0;
for ($k = 0; $k < $colsA; $k++) {
$sum += $a[$i][$k] * $b[$k][$j];
}
$result[$i][$j] = $sum;
}
}
return $result;
}
Или, например, скалярное произведение матриц:
function dot(array $a, array $b): float {
$sum = 0.0;
for ($i = 0, $n = count($a); $i < $n; $i++) {
$sum += $a[$i] * $b[$i];
}
return $sum;
}
С точки зрения алгоритмов – всё корректно.
С точки зрения PHP – тоже всё “по учебнику”.
Для человека – всё очень чётко и понятно. И, что важно, это действительно работает.
На самом деле – этот подход никуда не делся.
Даже сейчас появляются новые разборы и реализации, где матрицы нейросети пишутся на чистом PHP – без внешних библиотек и расширений. Обычно это делается не для продакшена, а в учебных целях: чтобы лучше понять, как всё работает “под капотом” и из каких базовых операций складываются более сложные алгоритмы.
Например (реализация прямого прохода (forward pass) одного слоя нейросети “вручную”, без библиотек):
function forward(array $inputs, array $weights, array $biases): array {
$outputs = [];
foreach ($weights as $i => $neuronWeights) {
$sum = 0.0;
foreach ($neuronWeights as $j => $weight) {
$sum += $inputs[$j] * $weight;
}
$sum += $biases[$i];
$outputs[$i] = 1 / (1 + exp(-$sum));
}
return $outputs;
}
И это важный момент.
С одной стороны, это показывает, что:
подход всё ещё понятен
он по-прежнему используется для обучения и экспериментов
С другой – очень быстро становится видно его ограничения, когда речь заходит о масштабировании или о продакшен-среде.
👉 Если вам интересно заглянуть немного вперёд, то рекомендую посмотреть относительно свежий доклад Алексея Нечаева (обещаю – вы получите море удовольствие от просмотра этого видео):
В нём шаг за шагом разбирается реализация нейронной сети прямо на PHP – с теми же идеями: массивы, циклы, базовые математические операции.
Главное преимущество – простота.
Если вы загорелись идеей, то можно буквально за вечер реализовать:
k-NN
простую линейную регрессию
базовый классификатор
и т.д.
И всё это – не выходя за пределы PHP.
Например, кусок логистической регрессии:
function sigmoid(float $x): float {
return 1 / (1 + exp(-$x));
}
function predict(array $weights, array $features): float {
return sigmoid(dot($weights, $features));
}
Как говорится – “И все так чинно, благородно, по старому” (с).
На маленьких данных это работает вполне нормально.
Для обучения, экспериментов и прототипов – более чем достаточно.
Проблемы начинаются чуть позже.
Проблемы появляются не сразу.
Сначала всё выглядит нормально. Потом данные растут – и код начинает тормозить. Сначала чуть-чуть, затем в разы, а потом становится просто непригодным.
Первая мысль – “надо оптимизировать циклы”. Но довольно быстро становится понятно: дело не в алгоритмах и не в конкретном коде и даже оптимизация циклов не помогает.
Проблема глубже – в том, как PHP устроен внутри.
В PHP число – это не примитив, а структура zval [14], которая хранит:
тип
значение
служебную информацию (например, счётчик ссылок)
То есть вместо компактных 8 байт, как в C, каждый элемент занимает значительно больше памяти.
Если у вам миллион элементов – это миллион таких структур.
PHP-массив – это не contiguous memory, а хеш-таблица (hash table [15]).
Это значит, что элементы не лежат подряд в памяти. Каждый доступ – это не просто “взять по индексу”, а кое-что побольше:
поиск ключа
проход по внутренним структурам
извлечение zval
Например:
$value = $matrix[10][5];
под капотом – это серия операций, а не прямой доступ к памяти.
И это сразу бьёт по нескольким вещам:
хуже работает CPU cache
доступ к данным становится дороже
невозможна нормальная векторизация
В численных вычислениях это критично.
Есть ещё менее очевидная проблема – copy-on-write [16].
PHP может неявно копировать массивы при изменении:
$b = $a;
$b[0][0] = 42;
И мы не всегда можем контролировать, когда именно произойдёт копирование.
Всё это значит, что в задачах с матрицами это может:
резко увеличится потребление памяти
добавятся лишние аллокации и т.п.
Все операции выполняются через циклы:
for (...) {
$c[$i] = $a[$i] + $b[$i];
}
В то время как в NumPy это одна операция, выполняемая на уровне C с использованием SIMD (Single Instruction, Multiple Data) [17].
В итоге получается странная ситуация: алгоритм правильный, код простой – но каждая операция стоит слишком дорого.
И именно поэтому всё “вдруг” перестаёт работать при росте данных.
Чтобы не быть голословным, давайте сравним простую операцию – умножение матриц. Как говорится – “Практика – критерий истины” (с).
Вариант 1: чистый PHP
function matmul(array $a, array $b): array {
$result = [];
$rowsA = count($a);
$colsA = count($a[0]);
$colsB = count($b[0]);
for ($i = 0; $i < $rowsA; $i++) {
for ($j = 0; $j < $colsB; $j++) {
$sum = 0.0;
for ($k = 0; $k < $colsA; $k++) {
$sum += $a[$i][$k] * $b[$k][$j];
}
$result[$i][$j] = $sum;
}
}
return $result;
}
Вариант 2: Tensor
(это https://github.com/RubixML/Tensor [18])
use TensorMatrix;
$a = Matrix::rand(500, 500);
$b = Matrix::rand(500, 500);
$c = $a->matmul($b);
Вариант 3: NumPower (потенциально GPU)
(это https://github.com/RubixML/numpower [19])
$c = NumPower::multiply($a, $b);
// или
$c = $a * $b;
Результаты (примерные, но показательные)
|
Подход |
Время (500×500) |
|---|---|
|
PHP массивы |
~10–20 сек |
|
Tensor (CPU) |
~0.3–0.8 сек |
|
GPU (NumPower) |
~0.05–0.2 сек |
Цифры могут отличаться в зависимости от железа, но порядок остаётся тем же.
Главное здесь не абсолютные значения, а разница на порядки!
И именно в этот момент становится понятно: дело не в том, что PHP “медленный” или “плохой”, а в том, что он решает задачу не тем инструментом.
Например, можно убрать лишние вызовы count() из условия цикла и сократить количество обращений к массивам:
$count = count($a);
for ($i = 0; $i < $count; $i++) {
$ai = $a[$i];
$bi = $b[$i];
$sum += $ai * $bi;
}
Здесь мы:
вызываем count() один раз вместо выполнения его на каждой итерации
уменьшаем количество обращений к массивам
работаем с локальными переменными, что быстрее, чем повторно читать значения из массива
Это действительно может дать небольшой прирост производительности, особенно в больших циклах. Однако выигрыш обычно умеренный и сильно зависит от конкретного кода и версии PHP – говорить о каком-то серьёзном приросте производительности не приходится.
Но довольно быстро упираешься в предел таких микрооптимизаций.
Главная проблема в том, что это оптимизация “на уровне синтаксиса”.
Фундаментальных ограничениях PHP остаются как и прежде:
нет контроля над памятью
нет contiguous storage [20]
нет SIMD [17]
нет BLAS [21]
Узкое место – это не реализация цикла, а сама модель памяти PHP.
Но тем не менее, на этом оптимизации не заканчиваются. Давайте посмотрим, что можно сделать ещё.
Один из интереснейших трюков, который довольно быстро появляется в таких реализациях – это предварительное транспонирование матрицы.
Проблема классического умножения матриц в PHP в том, что мы постоянно обращаемся к элементам столбцов:
$sum += $a[$i][$k] * $b[$k][$j];
Для $a[$i][$k] всё более-менее нормально – мы идём по строке.
А вот $b[$k][$j] – это уже доступ к столбцу, который в PHP-массиве реализован максимально неэффективно.
В итоге мы постоянно прыгаем по памяти.
👉 В качестве приятного времяпровождения настоятельно рекомендую посмотреть видео с выступлением Andrew DalPino (автором библиотеки RubixML):
Решение тут убийственно простое – заранее развернуть (транспонировать) вторую матрицу:
function transpose(array $matrix): array {
$result = [];
foreach ($matrix as $i => $row) {
foreach ($row as $j => $value) {
$result[$j][$i] = $value;
}
}
return $result;
}
И дальше работать уже с ней:
function matmulOptimized(array $a, array $b): array {
$bT = transpose($b);
$result = [];
$rowsA = count($a);
$colsB = count($bT);
for ($i = 0; $i < $rowsA; $i++) {
for ($j = 0; $j < $colsB; $j++) {
$sum = 0.0;
for ($k = 0; $k < count($a[0]); $k++) {
$sum += $a[$i][$k] * $bT[$j][$k];
}
$result[$i][$j] = $sum;
}
}
return $result;
}
Теперь вместо доступа к столбцу мы всегда работаем со строками:
$bT[$j][$k] // вместо прежнего $b[$k][$j]
Почему это быстрее
уменьшается количество “прыжков” по памяти
лучше используется CPU cache
доступ становится более предсказуемым
На практике это может дать заметный прирост производительности даже в чистом PHP.
Но тут, как говорится – “есть нюанс” (с)
Это хорошая оптимизация, но она не решает основную проблему.
Мы всё ещё:
работаем с zval
используем хеш-таблицу
остаёмся в интерпретаторе
То есть мы делаем вычисления чуть менее плохими, но не делаем их по-настоящему эффективными.
И в какой-то момент приходится признать неприятный, но важный факт: даже продвинутые трюки на уровне PHP (продвинутые, Карл!), не заменят нормальную модель данных. Ну вот… я это произнёс.
PHP просто не предназначен для численных вычислений.
Ещё одна идея, которая кажется очевидной, – это попытаться “подружиться” с CPU-кэшем.
В низкоуровневых языках (C, C++, NumPy) огромное значение имеет cache locality [22] – то, насколько близко друг к другу лежат данные в памяти.
Если данные лежат подряд, процессор может читать их блоками (cache lines [23]), и такие операции:
for ($i = 0; $i < $n; $i++) {
$sum += $a[$i] * $b[$i];
}
в C работает очень быстро не потому, что он простой, а потому что:
данные лежат подряд
CPU читает их блоками (cache lines)
доступ предсказуем
Поэтому естественная попытка оптимизации выглядит так: будем писать код, который идёт по памяти последовательно и будем избегать “прыжков” по массивам.
Например:
использовать строки вместо столбцов (через transpose)
минимизировать случайный доступ
работать с линейными структурами
Где это ломается в PHP
Проблема в том, что в PHP такая оптимизация почти не работает.
Даже если у нас есть “плоский” массив:
$a = [1.0, 2.0, 3.0, 4.0];
в памяти это:
не последовательность float-ов
а массив zval
плюс внутренняя структура всё ещё хеш-таблица
И в результате:
данные не лежат компактно
между ними есть служебные структуры
CPU не может эффективно использовать cache
В общем, у нас получается парадокс [24]:
мы пишем код так, как будто оптимизируем cache но реальной выгоды почти нет
А как же packed arrays, спросил внимательный читатель? Отличный вопрос!
В PHP 7+ появились так называемые packed arrays [25] – оптимизация для “простых” массивов.
Например:
$a = [1, 2, 3, 4, 5];
Такой массив PHP может хранить данные более компактно – без полноценной хеш-таблицы.
И это действительно ускоряет работу.
Но… в контексте работы с матрицами это почти не помогает.
Как только появляется вложенность:
$matrix = [
[1, 2, 3],
[4, 5, 6],
];
мы снова получаем:
массив массивов
сложную структуру
непрерывность данных теряется
Плюс:
каждый элемент всё ещё zval
настоящего contiguous memory нет
Более того, packed array легко “сломать”:
$a = [1, 2, 3];
$a[10] = 4;
Упс… и после этого PHP возвращается к обычной хеш-таблице.
В ML-задачах это происходит постоянно:
фильтрация
reshaping
работа с индексами
Если собрать всё вместе – транспонирование, попытки учитывать cache locality, использование packed arrays – становится видно одну и ту же картину:
мы можем немного ускорить код – но не можем изменить фундамент!
И тут мы снова приходим к ключевому пониманию:
проблема не в том, как мы пишем циклы –
проблема в том, как данные представлены в памяти
И здесь есть ещё один аспект, о котором редко говорят напрямую и который я хотел бы упомянуть.
Многие библиотеки того периода просто переставали развиваться. Интернет завален заброшенными PHP пет-проектами по машинному обучению.
Не потому что идея была плохой – наоборот, она была логичной. А потому что разработчики довольно быстро упирались в один и тот же потолок.
Можно было:
переписать внутренности
добавить ещё оптимизаций
улучшить API
Но кардинального прироста это не давало.
В какой-то момент авторам библиотек становилось понятно:
сколько бы они ни оптимизировали PHP-массивы, они не превратятся в структуру, подходящую для численных вычислений
И дальше оставалось два варианта:
либо переписывать всё на C / Rust
либо терять мотивацию [26]
Часто происходило второе.
Энтузиазм угасал, потому что каждый следующий шаг давал всё меньший результат, а фундаментальная проблема оставалась.
И это, пожалуй, один из самых показательных сигналов:
дело было не в конкретных библиотеках
дело было в самой модели
Следующим этапом эволюции – был отказ от идеи, что матрица должна быть PHP-массивом. Как просто, да?
Появились библиотеки и расширения, которые хранят данные уже не в PHP-структурах, а в более подходящем виде.
Например:
Rubix Tensor (от уже известного нам Andrew DalPino) – https://github.com/RubixML/Tensor [18]
NDArray (реализация с использованием Rust от Kyrian Obikwelu) – https://github.com/phpmlkit/ndarray [27]
Теперь данные лежат в contiguous memory, как в NumPy. Это уже совсем другой уровень.
Код при этом остаётся похожим:
use TensorMatrix;
$a = Matrix::quick([
[1, 2],
[3, 4],
]);
$b = Matrix::quick([
[5, 6],
[7, 8],
]);
$c = $a->matmul($b);
Но внутри происходит совсем другое.
Нет zval на каждый элемент, нет хеш-таблицы, данные лежат плотно, и CPU может работать с ними гораздо эффективнее.
Интересно, как здесь в игру зашёл Rust. В том же NDArray он используется как способ получить производительность и при этом не словить типичные проблемы C с памятью. Это хороший пример того, как PHP начинает делегировать тяжёлую работу другим языкам.
И это уже работает. Ускорение может быть в разы.
Хотя даже этого оказывается недостаточно.
Если упростить, вся эволюция [28] до этого момента выглядит так:
PHP arrays
↓
(осознание ограничений)
↓
Native structures (Tensor, ndarray)
↓
C / Rust extensions
Если посмотреть чуть шире – это не просто смена инструментов, а смена ролей:
раньше: PHP занимался вычислениями
теперь: PHP управляет процессом (orchestration)
Самое интересное, что на каждом этапе код становился не просто быстрее – он становится менее “PHP-шным” и всё больше напоминает нормальную математическую модель.
Что же дальше?
Например, сложение:
$c = $a->add($b);
Или нормализация:
$c = $a->divide($a->sum());
Код становится гораздо ближе к математике – читаешь его почти как формулу, а не как набор низкоуровневых операций.
И тут происходит интересный момент
Даже после перехода на нативные структуры остаётся ощущение, что чего-то не хватает.
API всё ещё “объектный”, а не математический.
Поэтому здесь появляется следующий шаг эволюции.
NumPower и новый уровень абстракции
Проект NumPower, изначально созданый Henrique Borba (https://github.com/RubixML/numpower [19]) идёт ещё дальше.
Он не только ускоряет вычисления, но и меняет сам способ их записи.
Теперь можно писать так:
$c = NumPower::multiply($a, $b);
А можно даже и так:
$c = $a * $b; // operator overloading
Это уже очень близко к тому, как выглядит код в NumPy.
Ещё пример:
$y = $a / $b;
Это не просто синтаксический сахар. Это попытка сделать математические выражения нативными для языка.
Однако с ростом задач становится ясно, что проблема не только в PHP.
Даже если у нас идеальные структуры данных и быстрый нативный код, CPU остаётся CPU. У него ограниченное количество ядер и ограниченная пропускная способность.
А задачи машинного обучения – это, по сути, огромные матричные операции.
Например, работа с embedding-векторами или нейросетями быстро упирается в объёмы, где даже хорошо оптимизированный CPU начинает сдавать.
Соответственно мы переходим к следующему логичному шагу.
GPU изначально заточен под параллельные вычисления. Тысячи потоков, высокая пропускная способность памяти – всё это идеально подходит для линейной алгебры.
На сегодняшний момент очевидно уже всем: пытаться выжать максимум из CPU – это тупиковая ветка.
Нужно менять не реализацию, а архитектуру.
Что происходит сейчас.
Сейчас появляется следующий уровень.
В RubixML v3 появляется движение в сторону GPU через такие проекты, как numpower:
👉 https://github.com/RubixML/numpower [19]
Это смена парадигмы:
PHP → orchestration
Tensor / NumPower → вычисления
GPU → heavy math
Работа ведётся энтузиастами, и проект всё ещё находится в стадии активного развития.
Если вам это интересно – это как раз тот момент, когда можно подключиться:
👉. https://github.com/RubixML/ML/tree/3.0 [29]
Это уже не просто “ускорим вычисления”. Это переход на другую модель: PHP больше не считает, он управляет.
Если окинуть взлядом всю вышеописанную эволюцию, то картина получается довольно логичная.
Сначала PHP использовался как вычислительная среда. Потом стало понятно, что он для этого не подходит. После этого вычисления начали постепенно выноситься: сперва в нативные структуры, потом в расширения, а теперь – на GPU.
Сегодня PHP в ML – это скорее orchestration layer. Он связывает компоненты, управляет процессом, обрабатывает данные, но тяжёлая математика живёт в других местах:
в GPU
в Rust
в нативных библиотеках
И это, пожалуй, главный вывод всей этой истории.
Хороший пример того, как это выглядит на практике сегодня:
У нас есть проекты вроде:
👉 https://github.com/CodeWithKyrian/transformers-php [30] (от Kyrian Obikwelu)
Это библиотека, которая даёт доступ к современным моделям (в духе Hugging Face / transformers), но уже в контексте PHP.
При этом важно понимать, что происходит под капотом: PHP здесь не занимается тяжёлыми вычислениями.
Он:
управляет пайплайном (pipeline)
загружает модели
обрабатывает результаты
А сами вычисления выполняются вне PHP – через нативные библиотеки или внешние рантаймы.
Пример использования может выглядеть довольно “нативно”:
use function CodewithkyrianTransformersPipelinespipeline;
// Allocate a pipeline for sentiment analysis
$classifier = pipeline('sentiment-analysis');
$out = $classifier(['I love transformers!']);
echo print_r($out, true) . PHP_EOL;
$out = $classifier(['I hate transformers!']);
echo print_r($out, true);
И это, пожалуй, лучший показатель того, как изменилась роль PHP:
раньше:
// сами реализуем математику
$sum += $inputs[$i] * $weights[$i];
сейчас:
// используем готовую ML-инфраструктуру
$result = $pipeline($text);
Похожую роль играют и библиотеки вроде:
👉 https://github.com/llphant/llphant [31] (от Franco Lombardo, Fabrizio Balliano и других)
LLPhant – это уже следующий шаг: работа не просто с моделями, а с полноценными LLM-сценариями.
Например:
генерация текста
embeddings
retrieval (RAG)
чат-интерфейсы
и т.д.
Код при этом остаётся максимально “прикладным”:
use LLPhantChatEnumsChatRole;
use LLPhantChatMessage;
use LLPhantChatOpenAIChat;
use LLPhantOpenAIConfig;
$chat = new OpenAIChat(new OpenAIConfig(...));
$message = new Message();
$message->role = ChatRole::User;
$message->content = 'What is the capital of France? ';
$response = $chat->generateText((string)$message);
echo $response;
Или, например, работа с embeddings:
use LLPhantEmbeddingsOpenAIEmbeddingGenerator;
$generator = new EmbeddingGenerator();
$embedding = $generator->embed("PHP and machine learning");
Всё это – уже совершенно другой уровень абстракции.
Мы больше не думаем:
о матрицах
о циклах
о памяти
Мы думаем:
о данных
о запросах
о поведении [32] системы
Но и на этом трансформация не заканчивается.
Появляются и более высокоуровневые инструменты, где PHP используется уже не просто как “обёртка” над моделями, а как среда для построения AI-логики.
Например:
👉 https://github.com/neuron-core/neuron-ai [33] (от Valerio Barbera)
Neuron AI – это уже не про матрицы и даже не про сами модели.
Это про построение AI-приложений:
агентов
цепочек (chains)
интеграции с LLM
Пример использования выглядит ещё дальше от “низкого уровня”:
$message = MyAgent::make()
->chat(new UserMessage("Hi!"))
->getMessage();
echo "Q1: Hi!n";
echo "A: " . $message->getContent() . "nn";
echo "Q2: Explain who you are?n";
echo "A: ". MyAgent::make()
->chat(new UserMessage("Explain who you are?"))
->getMessage()
->getContent();
Здесь особенно хорошо видно, как изменилась роль PHP:
раньше мы реализовывали математику
потом – выносили её в нативные структуры
теперь – мы описываем поведение [34] системы
Если посмотреть на весь пройденный путь, становится очевидно: это не просто эволюция инструментов – это постепенное вытеснение PHP из вычислений.
Сначала мы честно умножаем матрицы в массивах.
Потом начинаем оптимизировать и упираемся в потолок.
Дальше – выносим всё в нативные структуры.
Потом – в C / Rust.
И в конце концов… уходим на GPU.
PHP arrays
↓
(осознание ограничений)
↓
Native structures (Tensor, ndarray)
↓
C / Rust extensions
↓
GPU (NumPower, RubixML v3)
И здесь у меня напрашивается не самый утешительный вывод:
PHP не становится быстрее в ML.
Он просто перестаёт считать.
Он больше не вычислительный движок – он диспетчер, оркестратор, клей между компонентами.
И, возможно, именно это и есть его шанс выжить в большой игре.
Но тогда вопрос остаётся открытым:
Is the game still going on?
Или PHP уже давно играет не в ту игру?
Если вам интересна эта тема, можно глубже погрузиться в неё в моей бесплатной книге:
https://apphp.gitbook.io/ai-for-php-developers/ [35]
А также посмотреть и поиграться с онлайн-примерами:
https://aiwithphp.org/books/ai-for-php-developers/examples/ [36]
Автор: samako
Источник [37]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/28266
URLs in this post:
[1] математики: http://www.braintools.ru/article/7620
[2] Практическое использование TransformersPHP: https://habr.com/ru/articles/993966/
[3] Практика без Python и data science: https://habr.com/ru/articles/984042/
[4] Собираем простейшую RAG-систему на PHP с Neuron AI за вечер: https://habr.com/ru/articles/966792/
[5] Как я пытался подружить PHP с NER — драма в 5 актах: https://habr.com/ru/articles/948014/
[6] обучение: http://www.braintools.ru/article/5125
[7] реакция: http://www.braintools.ru/article/1549
[8] памятью: http://www.braintools.ru/article/4140
[9] https://github.com/apphp/awesome-php-ml: https://github.com/apphp/awesome-php-ml
[10] интеллекта: http://www.braintools.ru/article/7605
[11] https://github.com/jorgecasas/php-ml: https://github.com/jorgecasas/php-ml
[12] https://github.com/RubixML/RubixML: https://github.com/RubixML/RubixML
[13] зрения: http://www.braintools.ru/article/6238
[14] zval: https://www.php.net/manual/en/features.gc.refcounting-basics.php
[15] hash table: https://www.phpinternalsbook.com/php5/hashtables.html
[16] copy-on-write: https://wiki.php.net/rfc/implicit_move_optimisation
[17] SIMD (Single Instruction, Multiple Data): https://en.wikipedia.org/wiki/Single_instruction,_multiple_data
[18] https://github.com/RubixML/Tensor: https://github.com/RubixML/Tensor
[19] https://github.com/RubixML/numpower: https://github.com/RubixML/numpower
[20] contiguous storage: http://watson.latech.edu/book/information/informationOrganization1.html
[21] BLAS: https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms
[22] cache locality: https://en.wikipedia.org/wiki/Locality_of_reference
[23] cache lines: https://en.algorithmica.org/hpc/cpu-cache/cache-lines/
[24] парадокс: http://www.braintools.ru/article/8221
[25] packed arrays: https://www.zend.com/resources/php-extensions/php-arrays
[26] мотивацию: http://www.braintools.ru/article/9537
[27] https://github.com/phpmlkit/ndarray: https://github.com/phpmlkit/ndarray
[28] эволюция: http://www.braintools.ru/article/7702
[29] https://github.com/RubixML/ML/tree/3.0: https://github.com/RubixML/ML/tree/3.0
[30] https://github.com/CodeWithKyrian/transformers-php: https://github.com/CodeWithKyrian/transformers-php
[31] https://github.com/llphant/llphant: https://github.com/llphant/llphant
[32] поведении: http://www.braintools.ru/article/9372
[33] https://github.com/neuron-core/neuron-ai: https://github.com/neuron-core/neuron-ai
[34] поведение: http://www.braintools.ru/article/5593
[35] https://apphp.gitbook.io/ai-for-php-developers/: https://apphp.gitbook.io/ai-for-php-developers/
[36] https://aiwithphp.org/books/ai-for-php-developers/examples/: https://aiwithphp.org/books/ai-for-php-developers/examples/
[37] Источник: https://habr.com/ru/articles/1019250/?utm_campaign=1019250&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.