S4TF 基础使用

Swift 是动态强类型语言,也就是说 Swift 支持通过编译器自动检测类型,同时要求变量的使用要严格符合定义,所有变量都必须先定义后使用。

下面的代码,因为最初声明的 n 是整数类型 42 ,所以如果将 'string' 赋值给 n 时,会出现类型不匹配的问题,Swift 将会报错:

  1. var n = 42
  2. n = "string"

报错输出:

  1. Cannot assign value of type 'String' to type 'Int'

下面是一个使用 TensorFlow 计算的基础示例:

  1. import TensorFlow
  2.  
  3. // 声明两个Tensor
  4. let x = Tensor<Float>([1])
  5. let y = Tensor<Float>([2])
  6.  
  7. // 对两个 Tensor 做加法运算
  8. let w = x + y
  9.  
  10. // 输出结果
  11. print(w)

在 Swift 中使用标准的 TensorFlow API

在基础的 Tensorflow API 上,Swift 封装了 Tensorflow 的标准 API 接口。比如看一下处理数字的代码,API 与 Tensorflow 高度保持了一致:

  1. let x = Tensor<BFloat16>(zeros: [32, 128])
  2. let h1 = sigmoid(x w1 + b1)
  3. let h2 = tanh(h1 w1 + b1)
  4. let h3 = softmax(h2 w1 + b1)

再比如 Data API ,也是同名函数直接改写为 Swift 语法即可直接使用:

  1. let imageBatch = Dataset(elements: images)
  2. let labelBatch = Dataset(elements: labels)
  3. let zipped = zip(imageBatch, labelBatch).batched(8)
  4.  
  5. let imageBatch = Dataset(elements: images)
  6. let labelBatch = Dataset(elements: labels)
  7. for (image, label) in zip(imageBatch, labelBatch) {
  8. let y = image w + b
  9. let loss = (y - label).squared().mean()
  10. print(loss)
  11. }

在 Swift 中直接加载 Python 语言库

Swift 语言支持直接加载 Python 函数库(比如 NumPy),也支持直接加载系统动态链接库,很方便的做到即导入即用。

借助 S4TF 强大的集成能力,从 Python 迁移到 Swift 非常简单。您可以逐步迁移 Python 代码(或继续使用 Python 代码库),因为 S4TF 支持直接在代码中加载 Python 原生代码库,使得开发者可以继续使用熟悉的语法在 Swift 中调用 Python 中已经完成的功能。

下面我们以 NumPy 为例,看一下如何在 Swift 语言中,直接加载 Python 的 NumPy 代码库,并且直接进行调用:

  1. import Python
  2.  
  3. let np = Python.import("numpy")
  4. let x = np.array([[1, 2], [3, 4]])
  5. let y = np.array([11, 12])
  6. print(x.dot(y))

输出:

  1. [35 81]

除了能够直接调用 Python 之外,Swift 也快成直接调用系统函数库。比如下面的代码例子展示了我们可以在 Swift 中直接加载 Glibc 的动态库,然后调用系统底层的 malloc 和 memcpy 函数,对变量直接进行操作。

  1. import Glibc
  2. let x = malloc(18)
  3. memcpy(x, "memcpy from Glibc", 18)
  4. free(x)

通过 Swift 强大的集成能力,针对 C/C++ 语言库的加载和调用,处理起来也将会是非常简单高效。

语言原生支持自动微分

我们可以通过 @differentiable 参数,非常容易的定义一个可被微分的函数。

  1. @differentiablefunc frac(_ x:Double) -> Double { return 1/x}

  2. gradient(at:0.5) { x in frac(x) }

输出:

  1. -4.0

MNIST数字分类

本小节的源代码可以在 <https://github.com/huan/tensorflow-handbook-swift> 找到。加载 MNIST 数据集使用了作者封装的 Swift Module swift-MNIST)。

更方便的是在 Google Colab 上直接打开本例子的 Jupyter 直接运行,地址: https://colab.research.google.com/github/huan/tensorflow-handbook-swift/blob/master/tensorflow-handbook-swift-example.ipynb (推荐)

代码:

  1. import TensorFlow
  2. import Python
  3. import Foundation
  4.  
  5. /**
  6. * The Swift Module for MNIST Dataset:
  7. * https://github.com/huan/swift-MNIST
  8. */
  9. import MNIST
  10.  
  11. struct MLP: Layer {
  12. typealias Input = Tensor<Float>
  13. typealias Output = Tensor<Float>
  14.  
  15. var flatten = Flatten<Float>()
  16. var dense = Dense<Float>(inputSize: 784, outputSize: 10)
  17.  
  18. @differentiable
  19. public func callAsFunction(_ input: Input) -> Output {
  20. return input.sequenced(through: flatten, dense)
  21. }
  22. }
  23.  
  24. var model = MLP()
  25. let optimizer = Adam(for: model)
  26.  
  27. let mnist = MNIST()
  28. let ((trainImages, trainLabels), (testImages, testLabels)) = mnist.loadData()
  29.  
  30. let imageBatch = Dataset(elements: trainImages).batched(32)
  31. let labelBatch = Dataset(elements: trainLabels).batched(32)
  32.  
  33. for (X, y) in zip(imageBatch, labelBatch) {
  34. // Caculate the gradient
  35. let (_, grads) = valueWithGradient(at: model) { model -> Tensor<Float> in
  36. let logits = model(X)
  37. return softmaxCrossEntropy(logits: logits, labels: y)
  38. }
  39.  
  40. // Update parameters by optimizer
  41. optimizer.update(&model.self, along: grads)
  42. }
  43.  
  44. let logits = model(testImages)
  45. let acc = mnist.getAccuracy(y: testLabels, logits: logits)
  46.  
  47. print("Test Accuracy: \(acc)" )

以上程序运行输出为:

  1. Downloading train-images-idx3-ubyte ...
  2. Downloading train-labels-idx1-ubyte ...
  3. Reading data.
  4. Constructing data tensors.
  5. Test Accuracy: 0.9116667