DL4S 提供了一个高级 API,用于神经网络和深度学习中常见的许多加速运算。此外,它还内置了自动微分功能,这使您无需手动实现反向传播即可创建和训练神经网络 - 无需特殊的 Swift 工具链。
功能包括许多基本二元和一元运算符的实现、广播、矩阵运算、卷积和循环神经网络、常用优化器、二阶导数等等。 DL4S 提供了常用网络架构的实现,例如 VGG、AlexNet、ResNet 和 Transformers。
虽然其主要目的是深度学习和优化,但 DL4S 可以用作矢量化数学运算库,例如 numpy。
https://github.com/palle-k/DL4S.git
,然后单击 “Next”。注意:较新版本不再支持通过 CocoaPods 安装。
将依赖项添加到您的 Package.swift
文件
.package(url: "https://github.com/palle-k/DL4S.git", .branch("master"))
然后将 DL4S
作为依赖项添加到您的目标
.target(name: "MyPackage", dependencies: ["DL4S"])
DL4S 可以通过 Intel 的 Math Kernel Library、Integrated Performance Primitives 和 OpenMP 加速(安装说明)。
在 Apple 设备上,DL4S 默认使用内置 Accelerate 框架提供的矢量化函数。 如果没有可用的加速库,则使用后备实现。
使用 MKL/IPP 编译
# After adding the APT repository as described in the installation instructions
sudo apt-get install intel-mkl-64bit-2019.5-075 intel-ipp-64bit-2019.5-075 libiomp-dev
export MKLROOT=/opt/intel/mkl
export IPPROOT=/opt/intel/ipp
export LD_LIBRARY_PATH=${MKLROOT}/lib/intel64:${IPPROOT}/lib/intel64:${LD_LIBRARY_PATH}
swift build -c release \
-Xswiftc -DMKL_ENABLE \
-Xlinker -L${MKLROOT}/lib/intel64 \
-Xlinker -L${IPPROOT}/lib/intel64
DL4S-Tensorboard 提供了一个摘要写入器,可以写入与 tensorboard 兼容的日志。
DL4S 包含一个 LLDB python 脚本,该脚本为张量提供自定义描述 (util/debugger_support/tensor.py
)。
要使用增强的摘要,请直接在 LLDB 中执行 command script import /path/to/DL4S/util/debugger_support/tensor.py
,或将该命令添加到您的 ~/.lldbinit
文件中。
然后您可以使用 print
或 frame variable
命令来打印张量的可读描述。
核心
池化
范数
实用工具
激活函数
Transformer
广播运算的行为与 numpy 规则一致。
对于实验性的早期 GPU 加速版本,请查看 feature/arrayfire
。
为以下架构提供了默认实现
一些高级示例已在其他存储库中实现
DL4S 为张量上的许多矢量化运算提供了一个高级接口。
let a = Tensor<Float, CPU>([[1,2],[3,4],[5,6]], requiresGradient: true)
let prod = a.transposed().matrixMultipled(with: a)
let s = prod.reduceSum()
let l = log(s)
print(l) // 5.1873856
当张量被标记为需要梯度时,将捕获计算图。 该图存储所有直接或间接使用该张量作为操作数的操作。
然后可以使用 gradients(of:)
函数通过该图进行反向传播
// Backpropagate
let dl_da = l.gradients(of: [a])[0]
print(dl_da)
/*
[[0.034, 0.034]
[0.078, 0.078]
[0.123, 0.123]]
*/
反向传播期间使用的操作本身是可微分的。 因此,可以通过计算梯度的梯度来计算二阶导数。
当需要更高阶的导数时,必须显式保留反向传递的计算图。
let t = Tensor<Float, CPU>([1,2,3,4], requiresGradient: true)
let result = t * t * t
print(result) // [1, 8, 27, 64]
let grad = result.gradients(of: [t], retainBackwardsGraph: true)[0]
print(grad) // [3, 12, 27, 48]
let secondGrad = grad.gradients(of: [t], retainBackwardsGraph: true)[0]
print(secondGrad) // [6, 12, 18, 24]
let thirdGrad = secondGrad.gradients(of: [t])[0]
print(thirdGrad) // [6, 6, 6, 6]
MNIST 分类示例
// Input must be batchSizex1x28x28
var model = Sequential {
Convolution2D<Float, CPU>(inputChannels: 1, outputChannels: 6, kernelSize: (5, 5))
Relu<Float, CPU>()
MaxPool2D<Float, CPU>(windowSize: 2, stride: 2)
Convolution2D<Float, CPU>(inputChannels: 6, outputChannels: 16, kernelSize: (5, 5))
Relu<Float, CPU>()
MaxPool2D<Float, CPU>(windowSize: 2, stride: 2)
Flatten<Float, CPU>()
Dense<Float, CPU>(inputSize: 256, outputSize: 120)
Relu<Float, CPU>()
Dense<Float, CPU>(inputSize: 120, outputSize: 10)
LogSoftmax<Float, CPU>()
}
var optimizer = Adam(model: model, learningRate: 0.001)
// Single iteration of minibatch gradient descent
let batch: Tensor<Float, CPU> = ... // shape: [batchSize, 1, 28, 28]
let y_true: Tensor<Int32, CPU> = ... // shape: [batchSize]
// use optimizer.model, not model
let pred = optimizer.model(batch)
let loss = categoricalNegativeLogLikelihood(expected: y_true, actual: pred)
let gradients = loss.gradients(of: optimizer.model.parameters)
optimizer.update(along: gradients)
MNIST 分类示例
门控循环单元从上到下扫描图像,并将最终隐藏状态用于分类。
let model = Sequential {
GRU<Float, CPU>(inputSize: 28, hiddenSize: 128, direction: .forward)
Lambda<GRU<Float, CPU>.Outputs, Tensor<Float, CPU>, Float, CPU> { inputs in
inputs.0
}
Dense<Float, CPU>(inputSize: 128, outputSize: 10)
LogSoftmax<Float, CPU>()
}
var optimizer = Adam(model: model, learningRate: 0.001)
let batch: Tensor<Float, CPU> = ... // shape: [batchSize, 28, 28]
let y_true: Tensor<Int32, CPU> = ... // shape: [batchSize]
let x = batch.permuted(to: 1, 0, 2) // Swap first and second axis
let pred = optimizer.model(x)
let loss = categoricalNegativeLogLikelihood(expected: y_true, actual: pred)
let gradients = loss.gradients(of: optimizer.model.parameters)
optimizer.update(along: gradients)