BrainCore 是一个用 Swift 编写的简单但快速的神经网络框架。它使用 Metal,这使得它非常快。如果您想看到它在实际中的表现,请查看 InfiniteMonkeys — 一个使用递归神经网络生成诗歌的应用。
在您构建网络之前,请先构建所有层。这只要调用每个构造函数即可。
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文件。