Most is done in Python
"Reference" for ML and AI is NumPy
Hugging Face's "transformers" framework is in Python and NodeJS
There are options!
If you want to go bare-bones and nitty-gritty
foreach (['positive', 'negative'] as $label) {
foreach (glob("train/$label/*.txt") as $file) {
$samples[] = [file_get_contents($file)];
$labels[] = $label;
}
}
$dataset = new Labeled($samples, $labels);
$estimator = new PersistentModel(
new Pipeline([
new TextNormalizer(),
new WordCountVectorizer(10000, 2, 0.4, new NGram(1, 2)),
new TfIdfTransformer(),
new ZScaleStandardizer(),
], new MultilayerPerceptron([
new Dense(100),
new Activation(new LeakyReLU()),
new Dense(100),
new Activation(new LeakyReLU()),
new Dense(100, 0.0, false),
new BatchNorm(),
new Activation(new LeakyReLU()),
new Dense(50),
new PReLU(),
new Dense(50),
new PReLU(),
], 256, new AdaMax(0.0001))),
new Filesystem('sentiment.rbx', true)
);
$estimator->train($dataset);
$estimator->save();
this runs for almost an hour
$estimator = PersistentModel::load(
new Filesystem('sentiment.rbx')
);
$text = $argv[1];
$dataset = new Unlabeled([
[$text],
]);
$prediction = current($estimator->predict($dataset));
echo "The sentiment is: $prediction" . PHP_EOL;
Well it is pretty negative
All three use lapack(e) and OpenBLAS, both C libraries for scientific mathematics, that can be GPU accelerated!
numpy.dot(a, b)
NumPower::dot( $a, $b );
nDArrays and GPU copy are available, as used in many Python based examples
function distance($a,$b):float
{
return 1 - (dotp($a,$b) /
sqrt(dotp($a,$a) * dotp($b,$b))
);
}
// calculating the dot-product
function dotp($a,$b):float
{
$products = array_map(function($da, $db) {
return $da * $db;
}, $a, $b);
return (float)array_sum($products);
}
function distance($a,$b) {
return 1 - (
NumPower::dot( $a, $b ) /
NumPower::sqrt(
NumPower::multiply(
NumPower::dot( $a, $a ),
NumPower::dot( $b, $b )
)
)
);
}
ONNX (Open Neural Network Exchange) is an open source standard for ML/AI models
Developed by Microsoft and Facebook in 2017 but is now part of the Linux foundation
Is intended as an universal translator, to translate from PyTorch, Tensorflow or other models
Runs in a multiplatform runtime environment, and is GPU accelerated (even on Mac Metal)!
ONNX models can be run in PHP using the composer req ankane/onnxruntime composer package, which will install the runtime for your platform as well
You will need FFI enabled in PHP!
$model = new OnnxRuntime\Model('models/sentiment/onnx/model.onnx');
$inputs = $model->inputs();
$outputs = $model->outputs();
$meta = $model->metadata();
$input will show how the input has to be formated and output how the output will be returned
As you can see it needs to be a tensor(int64)
Input data usually needs to be a series of numbers, no matter if it is text, audio or images
So, we need a Tokenizer that does that for us
composer req codewithkyrian/tokenizers
Model: onnx-community/twitter-xlm-roberta-base-sentiment-ONNX
use Codewithkyrian\Tokenizers\Tokenizer;
$model = new OnnxRuntime\Model('models/sentiment/onnx/model.onnx');
$tokenizer = Tokenizer::fromFile(
'models/sentiment/tokenizer.json',
'models/sentiment/tokenizer_config.json'
);
$tokens = $tokenizer->encode( $argv[1] );
$result = $model->predict([
'input_ids'=>[$tokens->ids,1],
'attention_mask'=>[[1],[1]]
]);
$sentiment = $result['logits'][0]
natsort($sentiment);
print_r($sentiment);
$map = ['negative','neutral','positive'];
$lastkey = array_key_last( $sentiment );
echo $argv[1]." : ".$map[$lastkey];
Usually the models on Huggingface are build with HF's transformers framework, which provide tokenizer configurations
But there are groups or projects which ship their own Python framework
Like in the following example
Model: onnx-community/KittenTTS-Mini-v0.8-ONNX
$model = new OnnxRuntime\Model('models/tts/onnx/model.onnx');
$voices = json_decode( file_get_contents( "voices.json" ), true );
$style = $voices['expr-voice-4-m'][0];
$tokenizer = Tokenizer::fromFile(
'models/kitten-tokenizer.json',
'models/kitten-tokenizer_config.json',
);
$text = ipatranscribe($argv[1]);
$tokens = $tokenizer->encode($text);
$result = $model->predict(
[
'input_ids'=>[ array_merge( $tokens->ids, [ 10,0 ] ) ],
'style'=>[$style],
'speed'=>[1.0],
]
);
write_wav( 'output.wav', $result['waveform'], 24000, 1 );
printf( "wrote output.wav - %.2f s\n",
count( $result['waveform'] ) / 24000 );
The Python examples look much simpler...
composer req codewithkyrian/transformers
$input = $argv[1];
use function Codewithkyrian\Transformers\Pipelines\pipeline;
$pipe = pipeline('sentiment-analysis');
$out = $pipe($input);
echo $input." : ".$out['label'] . PHP_EOL;
input = sys.argv[1]
from transformers import pipeline
pipe = pipeline("sentiment-analysis")
out = pipe(input)
print(f"{input} : {out[label]}")
It will download and run ONNX Models and is analog to how the Python Library works
use Codewithkyrian\Transformers\Models\Auto\AutoModel;
use Codewithkyrian\Transformers\PreTrainedTokenizers\AutoTokenizer;
$input = $argv[1];
$modelName = 'onnx-community/twitter-xlm-roberta-base-sentiment-ONNX';
$tokenizer = AutoTokenizer::fromPretrained($modelName);
$encodedInput = $tokenizer($input);
$model = AutoModel::fromPretrained($modelName);
$output = $model($encodedInput);
$result = $output['logits']->toArray();
$map = ['negative','neutral','positive'];
// ...
use function Codewithkyrian\Transformers\Pipelines\pipeline;
$captioner = pipeline('image-to-text');
$result = $captioner($argv[1]);
print_r($result);
a laptop computer sitting on top of a wooden tablea mountain range with a mountaina man with a beard and a red tie sitting at a laptopRasmus Lerdorf himself (Father of PHP) wrote a PHP extension to run GGUF models via llama.cpp
GPU accelerated (CUDA and Mac Metal)
$modelfile = 'models/gemma-4-E4B-it-ultra-uncensored-heretic-Q4_K_M.gguf';
$model = new Llama\Model(realpath($modelfile));
print_r([
$model->desc(), // "llama 3B Q4_K - Medium"
$model->size(), // model file size in bytes
$model->nParams(), // parameter count
$model->nEmbd(), // embedding dimensions
$model->nLayer(), // layer count
$model->chatTemplate(), // built-in Jinja chat template, or null
$model->meta('general.name'),// read GGUF metadata by key
]);
$modelfile = 'models/gemma-4-E4B-it-ultra-uncensored-heretic-Q4_K_M.gguf';
$model = new Llama\Model(realpath($modelfile));
$ctx = new Llama\Context($model, [
'n_ctx' => 2048,
'n_gpu_layers' => -1, // (-1=all, 0=CPU only)
]);
print( $ctx->complete(
$argv[1],
['max_tokens' => 2048]
));
$modelfile = 'models/gemma-4-E4B-it-ultra-uncensored-heretic-Q4_K_M.gguf';
$model = new Llama\Model(realpath($modelfile));
$ctx = new Llama\Context($model, [
'n_ctx' => 2048,
'n_gpu_layers' => -1, // (-1=all, 0=CPU only)
]);
print( $ctx->chat([
['role' => 'system','content' => 'You are an aspiring writer'],
['role' => 'user', 'content' => $argv[1]]
],
['max_tokens' => 2048]
));
$modelfile = 'models/gemma-4-E4B-it-ultra-uncensored-heretic-Q4_K_M.gguf';
$model = new Llama\Model(realpath($modelfile));
$ctx = new Llama\Context($model, [
'n_ctx' => 2048,
'n_gpu_layers' => -1, // (-1=all, 0=CPU only)
]);
foreach( $ctx->stream($argv[1], ['max_tokens' => 2048])
as $chunk) {
print($chunk);
flush();
}
$modelfile = 'models/snowflake-arctic-embed-l-v2.0.F16.gguf';
$model = new Llama\Model(realpath($modelfile));
$ctx = new Llama\Context($model, [
'n_ctx' => 2048,
'n_gpu_layers' => -1,
'embeddings'=>true,
]);
$dimensions = $model->nEmbd();
$woman = $ctx->embed('woman');
$monarch = $ctx->embed('monarch');
$queen = $ctx->embed('queen');
// ... you know the rest
You absolutely can do ML/AI in PHP
But you have to read Python to understand the examples
And like the whole topic, it is constantly evolving
Caveat
The ecosystem of php extensions needs to be maintained and further developed. And some things are missing that would be beneficial (libsndfile for example, a new version of ext-opencv )
Fediverse: @foppel@phpc.social | Slack @FoppelFB
GitHub @FoppelFB | fberger@sudhaus7.de | https://sudhaus7.de/