BrainCore 0.4.0

BrainCore 0.4.0

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布最后发布2016年10月
SPM支持 SPM

Alejandro Isaza 维护。



BrainCore 0.4.0

  • Alejandro Isaza

BrainCore 是一个用 Swift 编写的简单但快速的神经网络框架。它使用 Metal,这使得它非常快。如果您想看到它在实际中的表现,请查看 InfiniteMonkeys — 一个使用递归神经网络生成诗歌的应用。

特点

  • [x] 内积层
  • [x] 线性整流器(ReLU)层
  • [x] Sigmoid 层
  • [x] LSTM 层
  • [x] L2 损失层

要求

  • iOS 8.0+ / Mac OS X 10.11+
  • Xcode 7.2+
  • 支持 Metal 的设备(不适用于 iOS 模拟器)

使用

网络定义

在您构建网络之前,请先构建所有层。这只要调用每个构造函数即可。

let dataLayer = MyDataLayer()
let lstmLayer = LSTMLayer(weights: lstmWeights, biases: lstmBiases)
let ipLayer = InnerProductLayer(weights: ipWeights, biases: ipBiases)
let reluLayer = ReLULayer(size: ipBiases.count)
let sinkLayer = MySinkLayer()

BrainCore 使用重载运算符来使网络定义更加简洁。要连接层,只需在 Net.build {} 闭包中使用 => 运算符。

let net = Net.build {
    dataLayer => lstmLayer => ipLayer => reluLayer => sinkLayer
}

如果您需要连接两个层的输出,请将它们放在方括号中。

let net = Net.build {
    [dataLayer1, dataLayer2] => lstmLayer => ipLayer => reluLayer => sinkLayer
}

同样地,如果您需要分割一个层的输出,请将目标层放在方括号中。

let net = Net.build {
    dataLayer => lstmLayer => ipLayer => reluLayer => [sinkLayer1, sinkLayer2]
}

分割时,目标层的 inputSize 将决定分割的位置。如果目标层 inputSize 的总和与源层的 outputSize 不匹配,则会抛出错误。

如果您在分割后想继续处理几个独立的分支,请将定义拆分为单独的行。

let net = Net.build {
    dataLayer => lstmLayer => [ipLayer1, ipLayer2]
    ipLayer1 => reluLayer1 => sinkLayer1
    ipLayer2 => reluLayer2 => sinkLayer2
}

最后,如果您想将一个层的输出向多个层发送,请使用 =>> 运算符。

let net = Net.build {
    dataLayer => lstmLayer
    lstmLayer =>> ipLayer1 => reluLayer1 => sinkLayer1
    lstmLayer =>> ipLayer2 => reluLayer2 => sinkLayer2
}

评估

目前,BrainCore 仅支持执行预训练的网络。理想情况下,您将使用已建立的神经网络框架之一在服务器上训练您的网络,并将训练好的权重导入到 BrainCore 中。我们正在努力实现求解器,以便您可以在 BrainCore 中完成所有工作,敬请关注。

我们先从创建层开始。

// Load weights and biases from a pre-trained network
let lstmWeights = ...
let lstmBiases = ...
let ipWeights = ...
let ipBiases = ...

// Create layers
let dataLayer = MyDataLayer()
let lstmLayer = LSTMLayer(weights: lstmWeights, biases: lstmBiases)
let ipLayer = InnerProductLayer(weights: ipWeights, biases: ipBiases)
let reluLayer = ReLULayer(size: ipBiases.count)
let sinkLayer = MySinkLayer()

然后我们将构建网络。

let net = Net.build {
    dataLayer => lstmLayer => ipLayer => reluLayer => sinkLayer
}

最后执行!您需要向运行器提供 Metal 设备,这通常是默认设备。

guard let device = MTLCreateSystemDefaultDevice() else {
    fatalError("Failed to create a Metal device.")
}

let evaluator: Evaluator
do {
    evaluator = try Evaluator(net: net, device: device)
} catch let e {
    fatalError("Failed to create an Evaluator: \(e)")
}

evaluator.evaluate { snapshot in
    print("Feed-forward pass complete!")
}

评估器如果在创建缓冲区或初始化所有Metal代码时遇到任何问题,可能无法构建,这就是为什么需要添加一个try语句。

调用evaluate()将执行一次正向传递,但是您可以随意多次调用它。实际上,您可能需要在得到任何结果之前多次调用evaluate()以最大化GPU带宽。您还可以增加批次大小以并行执行多个传递。

您的数据层可能希望每次您调用evaluate()时都提供新的数据。因此,您的代码可能如下所示

while !shouldStop {
    dataLayer.gather()
    evaluator.evaluate(completion)
}

注意:目的层的数据consume()函数和完成闭包将在后台线程中调用。请确保根据需要同步对数据的访问,尽量避免在这两个调用上长时间阻塞。


许可协议

Upsurge在MIT许可协议下提供。有关更多信息,请参阅LICENSE文件。