numpower / autograd
高性能数学库,支持自动微分和GPU加速。
Requires
- php: >=8.3
- ext-gd: *
- ext-numpower: >=0.5.0
Requires (Dev)
- phpstan/phpstan: 1.10.32
- phpunit/php-code-coverage: >=11.0.3
- phpunit/phpunit: >=11.1.3
- squizlabs/php_codesniffer: 3.7.2
This package is auto-updated.
Last update: 2024-09-09 13:19:17 UTC
README
NumPower Autograd 通过启用反向模式(反向传播)自动微分扩展了NumPower扩展。自动微分是一种计算技术,用于高效准确地计算函数的导数。与数值微分相比,数值微分容易出错且效率低下,自动微分系统地应用链式法则来计算梯度。
反向模式自动微分,通常称为反向传播,在机器学习和优化任务中特别强大。它以高度有效的方式计算关于N维参数的标量函数的梯度,非常适合训练复杂模型。
NumPower Autograd 也支持GPU加速,利用图形处理单元的强大能力进行更快计算。这种能力提高了库的性能,尤其是对于大规模数学和机器学习任务。
通过集成反向传播和GPU支持,NumPower Autograd 提供高性能梯度计算能力,促进高级数学和机器学习应用。
要求
- NumPower 扩展 >= 0.5.x (https://github.com/NumPower/numpower)
- PHP >= 8.3
- Composer
安装
$ composer require numpower/autograd
入门
NumPower\Tensor
类是一种N维数组,支持自动微分。它使用NumPower扩展的NDArray对象作为其引擎,保留了使用NDArray的许多灵活性,例如将其用作算术运算的运算符,简化代码阅读。
use NumPower\Tensor; $a = new Tensor([[1, 2], [3, 4]], requireGrad: True); $b = new Tensor([[5, 6], [7, 8]], requireGrad: True); $c = (($a + $b) / $b)->sum(); $c->backward(); $dc_Da = $a->grad(); $dc_Db = $b->grad(); echo "out: \n"; echo $c; echo "\ndc_Da: \n"; echo $dc_Da; echo "dc_Db: \n"; echo $dc_Db;
out: 5.4619045257568 dc_Da: [[0.2, 0.166667] [0.142857, 0.125]] dc_Db: [[-0.04, -0.0555556] [-0.0612245, -0.0625]]
以下代码演示了使用变量和操作进行自动微分的一个简单示例。它计算了计算结果($c)关于其输入($a和$b)的梯度。
使用支持CUDA的视频卡
如果您已编译了具有GPU利用能力的NumPower扩展,您可以通过在VRAM中分配您的Tensor
在GPU上执行操作。操作将自动识别您的Tensor
是在RAM还是VRAM中,并在正确的设备上执行适当的操作。
$a = new Tensor([[1, 2], [3, 4]], requireGrad: True, useGpu: True); $b = new Tensor([[5, 6], [7, 8]], requireGrad: True, useGpu: True); $c = ($a + $b)->sum(); // (Addition is performed on GPU) $c->backward(); // Back propagation is performed on GPU
当处理多参数操作中的不同设备时,
Tensor
与NDArray
的行为完全相同。除非参数是标量,否则操作的所有N维参数都必须存储在同一个设备上。
使用autograd进行简单训练
这里我们可以看到一个更实际的例子。让我们创建一个具有隐藏层和1个输出层的神经网络。一些常见的神经网络函数是现成的,可以通过NumPower\NeuralNetwork
模块静态访问。
use NDArray as nd; use NumPower\Tensor; use NumPower\NeuralNetwork\Activations as activation; use NumPower\NeuralNetwork\Losses as loss; class SimpleModel { public Tensor $weights_hidden_layer; public Tensor $weights_output_layer; public Tensor $hidden_bias; public Tensor $output_bias; private float $learningRate; public function __construct(int $inputDim = 2, int $outputDim = 1, int $hiddenSize = 16, float $learningRate = 0.01 ) { $this->learningRate = $learningRate; // Initialize hidden layer weights $this->weights_hidden_layer = new Tensor( nd::uniform([$inputDim, $hiddenSize], -0.5, 0.5), name: 'weights_hidden_layer', requireGrad: True ); // Initialize output layer weights $this->weights_output_layer = new Tensor( nd::uniform([$hiddenSize, $outputDim],-0.5, 0.5), name: 'weights_output_layer', requireGrad: True ); // Initialize hidden layer bias $this->hidden_bias = new Tensor( nd::uniform([$hiddenSize], -0.5, 0.5), name: 'hidden_bias', requireGrad: True ); // Initialize output layer bias $this->output_bias = new Tensor( nd::uniform([$outputDim], -0.5, 0.5), name: 'output_bias', requireGrad: True ); } public function forward(Tensor $x, Tensor $y): array { // Forward pass - Hidden Layer $x = $x->matmul($this->weights_hidden_layer) + $this->hidden_bias; $x = activation::ReLU($x); // ReLU Activation // Forward pass - Output Layer $x = $x->matmul($this->weights_output_layer) + $this->output_bias; $x = activation::sigmoid($x); // Sigmoid Activation // Binary Cross Entropy Loss $loss = loss::BinaryCrossEntropy($x, $y, name: 'loss'); return [$x, $loss]; } public function backward(Tensor $loss) { // Trigger autograd $loss->backward(); // SGD (Optimizer) - Update Hidden Layer weights and bias $dw_dLoss = $this->weights_hidden_layer->grad(); $this->weights_hidden_layer -= ($dw_dLoss * $this->learningRate); $this->weights_hidden_layer->resetGradients(); $this->hidden_bias -= ($this->hidden_bias->grad() * $this->learningRate); $this->hidden_bias->resetGradients(); // SGD (Optimizer) - Update Output Layer weights and bias $db_dLoss = $this->weights_output_layer->grad(); $this->weights_output_layer -= ($db_dLoss * $this->learningRate); $this->weights_output_layer->resetGradients(); $this->output_bias -= $this->output_bias->grad() * $this->learningRate; $this->output_bias->resetGradients(); } }
如果您想了解更多关于创建此模型的每个步骤,请访问我们的文档,并查看构建此模型的逐步说明。
上述模型可以用于多种不同的分类问题。为了简单起见,让我们看看我们的模型是否可以解决XOR问题。
$num_epochs = 4000; $x = new Tensor(nd::array([[0, 0], [1, 0], [1, 1], [0, 1]]), name: 'x'); $y = new Tensor(nd::array([[0], [1], [0], [1]]), name: 'y'); $model = new SimpleModel(); for ($current_epoch = 0; $current_epoch < $num_epochs; $current_epoch++) { // Forward Pass [$prediction, $loss] = $model->forward($x, $y); // Backward Pass $model->backward($loss); echo "\n Epoch ($current_epoch): ".$loss->getArray(); } echo "\nPredicted:\n"; print_r($model->forward($x, $y)[0]->toArray());