{"action":"created","comment":{"url":"https://api.github.com/repos/tensorflow/swift/pulls/comments/207787258","pull_request_review_id":143467992,"id":207787258,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIwNzc4NzI1OA==","diff_hunk":"@@ -0,0 +1,687 @@\n+# Parameter Update Design\n+\n+* Authors: [Daniel Zheng](https://github.com/dan-zheng), [Richard Wei](https://github.com/rxwei)\n+\n+## Introduction\n+\n+The concept of parameter update is crucial for implementing general machine learning optimization algorithms. This document explains the concept of parameters and parameter update, shows how TensorFlow (graph mode) and PyTorch handle parameter update, and describes the current design for Swift.\n+\n+### Parameters and parameter aggregates\n+\n+Machine learning models are data structures with mutable properties called parameters. Machine learning optimizers \"train\" models by applying an algorithm (e.g. stochastic gradient descent) to update the parameters of a model.\n+\n+In Swift, this might look like:\n+\n+```swift\n+struct MyMLModel {\n+  // Parameters.\n+  var weight1, weight2: Tensor<Float>\n+  var bias1, bias2: Float\n+\n+  func applied(to input: Tensor<Float>) {\n+    let h = relu(input • weight1 + bias1)\n+    return sigmoid(h • weight2 + bias2)\n+  }\n+}\n+\n+let model = MyMLModel(...)\n+let input = Tensor<Float>([0.2, 0.4])\n+print(model.applied(to: input))\n+```\n+\n+Here are some additional rules about models and parameters:\n+\n+1. Not all properties of a model are required to be parameters: a model may have properties which aren't meant to be trainable (e.g. configuration flags or state-caching variables). This requires a clear way to distinguish parameters from other properties.\n+\n+```swift\n+struct MyMLModel {\n+  // These are parameters.\n+  var weight: Tensor<Float>\n+  var bias: Float\n+\n+  // Need to distinguish from non-parameter stored properties.\n+  let useBias: Bool\n+  var previousWeight: Tensor<Float>\n+}\n+```\n+\n+2. There must exist some mechanism to update all parameters of a model given their gradients.\n+\n+Let \"parameter aggregate\" refer to some representation of all of the parameters of a model (e.g. `model.parameters()` in PyTorch). A parameter aggregate representation is important because it enables writing general optimizers that can be used with any model. A basic optimizer parameter update function looks like:\n+\n+```swift\n+// Pseudocode: `Parameters` is some data structure representing a model's \n+// parameters.\n+func optimize(parameters: inout Parameters, gradients: Parameters) {\n+  for (inout θ, dθ) in zip(parameters, gradients) {\n+    // Different values of θ may have different types. It should be \n+    // possible to perform any operation on θ and dθ that they support.\n+    θ -= learningRate * dθ\n+  }\n+}\n+```\n+\n+The ability to jointly iterate over parameters and gradients is crucial for writing simple, generic code that works with all models. Without this ability to perform \"aggregate parameter update\", users must duplicate code for each parameter, with no potential for generalization:\n+\n+```swift\n+// w1, w2: Tensor<Float>\n+// b1, b2: Float\n+w1 -= learningRate * dw1\n+w2 -= learningRate * dw2\n+b1 -= learningRate * db1\n+b2 -= learningRate * db2\n+...\n+```\n+\n+### Challenges and existing approaches\n+\n+The \"aggregate parameter update\" problem can be split into two subproblems:\n+* How can we represent an aggregate of parameters with different types?\n+  * This must be done in a way that's compatible with automatic differentiation, e.g. differentiation with respect to \"all parameters\" must be possible.\n+* How can we update parameter aggregates with their gradients?\n+\n+Dynamic machine learning frameworks like PyTorch allow ad-hoc model parameter registration and mutation. The base class `Module` defines a custom `__setattr__` function, which internally calls `register_parameter`. `Module` also defines a `parameters()` function which returns all registered parameters.\n+\n+```python\n+class Linear(Module):\n+  def __init__(self, in_features, out_features, bias=True):\n+    super(Linear, self).__init__()\n+    self.in_features = in_features\n+    self.out_features = out_features\n+    # Following line calls `__setattr__`, which internally calls\n+    # `register_parameter`.\n+    self.weight = Parameter(torch.Tensor(out_features, in_features))\n+    if bias:\n+      # Following line internally calls `register_parameter`.\n+      self.bias = Parameter(torch.Tensor(out_features))\n+    else:\n+      self.register_parameter('bias', None)\n+    self.reset_parameters()\n+\n+# Optimizers are general and work with the parameters of any model.\n+optimizer = optim.SGD(model.parameters(), lr=0.01)\n+\n+# The optimizer `step` function loops over parameters/parameter groups.\n+# Pseudocode:\n+class SGD(Optimizer):\n+  def step(self):\n+    for param in parameters:\n+      ...\n+```\n+\n+However, in Swift, this is difficult. Constructing a collection of parameters is difficult because parameters aren't required to have the same type: for example, a model may have parameters with types `Float`, `Tensor<Float>`, and `Tensor<Double>`. To represent parameters as a collection, advanced type-erasure is needed to generalize all parameter types. It's also not clear how parameter updates would work (at compile-time, how is it possible to identify the correct concrete `+` function for a type-erased parameter value?).\n+\n+Additionally, while we want to enable code that achieves the following:\n+```swift\n+for (inout θ, dθ) in zip(parameters, gradients) {\n+  θ -= learningRate * dθ\n+}\n+```\n+\n+We don't want to actually lower the for-loop or zip operation to TensorFlow (lowering wouldn't be straightforward or and lowered representation wouldn't be efficient). Instead, we want to fully unroll the loop into individual straight-line statements:\n+\n+```swift\n+// w1, w2: Tensor<Float>\n+w1 -= learningRate * dw1\n+w2 -= learningRate * dw2\n+// b1, b2: Float\n+b1 -= learningRate * db1\n+b2 -= learningRate * db2\n+```\n+\n+---\n+\n+TensorFlow (graph mode) handles parameters differently. Let's look at the optimizer example below:\n+\n+```python\n+import tensorflow as tf\n+# Model parameters\n+W = tf.Variable([0.3], dtype=tf.float32)\n+b = tf.Variable([-0.2], dtype=tf.float32)\n+\n+# Training data (inputs/outputs)\n+x = tf.placeholder(dtype=tf.float32)\n+y = tf.placeholder(dtype=tf.float32)\n+x_train = [1, 2, 3, 4]\n+y_train = [0, 1, 2, 3]\n+\n+linear_model = W * x + b\n+squared_deltas = tf.square(linear_model - y_train)\n+loss = tf.reduce_sum(squared_deltas)\n+\n+# Optimizer\n+optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)\n+train = optimizer.minimize(loss=loss)\n+```\n+\n+In the last line above: how does the optimizer determine which tensors are parameters to be minimized? This is done implicitly by examining the graph of `loss`: since the only Variables in the graph are `W` and `B`, they are determined to be the parameters and are minimized.\n+\n+In Swift, TensorFlow graphs are an implementation detail and aren't visible to users: there's no way to inspect whether tensors are placeholders/constants/variables, so the TensorFlow style of implicit parameter analysis is not really suitable. With implicit parameters, it's difficult to work with parameters directly (e.g. to implement a custom optimizer for arbitrary parameters). The authors believe that parameter representation and aggregate parameter update are language-design problems and should be explicitly clear in Swift.\n+\n+## Parameter update in Swift\n+\n+The current Swift parameter update design is based on two protocols: `ParameterAggregate` and `Parameterized`.\n+\n+They enable:\n+* Parameter update for parameter aggregates and parameterized models, where all parameters have the same type.\n+* Nested parameter aggregates and parameterized models as parameters, which is important for layer-based high level APIs.\n+* General optimizers that work with any floating-point parameter aggregate type.\n+\n+Examples:\n+* [MNIST](https://github.com/tensorflow/swift-models/blob/master/MNIST/MNIST.swift): demonstrates `ParameterAggregate`.\n+* [Autoencoder](https://github.com/tensorflow/swift-models/blob/master/Autoencoder/Autoencoder.swift): demonstrates `Parameterized`.\n+* Optimizers are a work-in-progress, see a sample sketch in this [PR description](https://github.com/apple/swift/pull/18171#issue-203387822).\n+\n+Implementation:\n+* [Part 1: ParameterAggregate protocol and synthesis](https://github.com/apple/swift/pull/18140)\n+* [Part 2: Parameterized protocol and synthesis](https://github.com/apple/swift/pull/18171)\n+\n+---\n+\n+Two protocols have been added to the TensorFlow module: `ParameterAggregate` and `Parameterized`.\n+\n+The `ParameterAggregate` protocol represents an aggregate of parameters. Types that conform to `ParameterAggregate` must specify a `Parameter` associated type and an update method.\n+\n+```swift\n+/// A type representing an aggregate of parameters.\n+public protocol ParameterAggregate {\n+  /// The parameter type.\n+  associatedtype Parameter\n+\n+  /// Update parameters with their gradient values, using an updater function.\n+  mutating func update(\n+    withGradients gradients: Self,\n+    _ updater: (inout Parameter, Parameter) -> Void\n+  )\n+}\n+```\n+\n+For structs whose stored properties all have the same type, the compiler can synthesize the `ParameterAggregate` protocol requirements.\n+\n+Here's an example:\n+\n+```swift\n+struct Parameters : ParameterAggregate {\n+  // Note: all stored properties have the same type.\n+  var w: Tensor<Float>\n+  var b: Tensor<Float>\n+\n+  // Note: all code below is compiler synthesized.\n+\n+  // The `Parameter` associated type is synthesized to be the common stored\n+  // property type.\n+  // typealias Parameter = Tensor<Float>\n+\n+  // The synthesized `update` method applies the `updater` argument to each\n+  // parameter-gradient pair.\n+  // mutating func update(\n+  //   withGradients gradients: Parameters,\n+  //   _ updater: (inout Parameter, Parameter) -> Void\n+  // ) {\n+  //   updater(&w, gradients.w)\n+  //   updater(&b, gradients.b)\n+  // }\n+}\n+\n+// Contrived parameter update example.\n+var parameters = Parameters(w: [[1, 1], [1, 1]]), b: Tensor(1))\n+let gradients = Parameters(w: [[0.5, 0.5], [0.5, 0.5]], b: Tensor(0.5))\n+parameters.update(withGradients: gradients) { p, g in\n+  p -= 0.1 * g\n+}\n+print(parameters)\n+// Parameters(w: [[0.95, 0.95], [0.95, 0.95]], b: 0.95)\n+```\n+\n+Model creators can define custom structs that conforms to `ParameterAggregate`, and use them in functions:\n+\n+```swift\n+/// Parameters of an MNIST classifier.\n+struct MNISTParameters : ParameterAggregate {\n+    var w1 = Tensor<Float>(randomUniform: [784, 30])\n+    var w2 = Tensor<Float>(randomUniform: [30, 10])\n+    var b1 = Tensor<Float>(zeros: [1, 30])\n+    var b2 = Tensor<Float>(zeros: [1, 10])\n+}\n+\n+func inference(input: Tensor<Float>, parameters: MNISTParameters) {\n+  // Forward pass.\n+  let z1 = images • parameters.w1 + parameters.b1\n+  let h1 = sigmoid(z1)\n+  let z2 = h1 • parameters.w2 + parameters.b2\n+  return sigmoid(z2)\n+}\n+```\n+\n+Automatic differentiation in Swift will support differentiating with respect to structs like `MNISTParameters`:\n+\n+```swift\n+@differentiable(reverse, wrt: (.0, .1))","path":"proposals/ParameterUpdate.md","position":259,"original_position":259,"commit_id":"4ae226322e2555042275fc34a233a86680a93d75","original_commit_id":"4ae226322e2555042275fc34a233a86680a93d75","user":{"login":"dan-zheng","id":5590046,"node_id":"MDQ6VXNlcjU1OTAwNDY=","avatar_url":"https://avatars0.githubusercontent.com/u/5590046?v=4","gravatar_id":"","url":"https://api.github.com/users/dan-zheng","html_url":"https://github.com/dan-zheng","followers_url":"https://api.github.com/users/dan-zheng/followers","following_url":"https://api.github.com/users/dan-zheng/following{/other_user}","gists_url":"https://api.github.com/users/dan-zheng/gists{/gist_id}","starred_url":"https://api.github.com/users/dan-zheng/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dan-zheng/subscriptions","organizations_url":"https://api.github.com/users/dan-zheng/orgs","repos_url":"https://api.github.com/users/dan-zheng/repos","events_url":"https://api.github.com/users/dan-zheng/events{/privacy}","received_events_url":"https://api.github.com/users/dan-zheng/received_events","type":"User","site_admin":false},"body":"Is `@differentiable` attribute not necessary to mark the function as differentiable? (No adjoint is specified.)","created_at":"2018-08-06T06:46:38Z","updated_at":"2018-08-06T06:46:38Z","html_url":"https://github.com/tensorflow/swift/pull/53#discussion_r207787258","pull_request_url":"https://api.github.com/repos/tensorflow/swift/pulls/53","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/tensorflow/swift/pulls/comments/207787258"},"html":{"href":"https://github.com/tensorflow/swift/pull/53#discussion_r207787258"},"pull_request":{"href":"https://api.github.com/repos/tensorflow/swift/pulls/53"}},"in_reply_to_id":207786358},"pull_request":{"url":"https://api.github.com/repos/tensorflow/swift/pulls/53","id":206298520,"node_id":"MDExOlB1bGxSZXF1ZXN0MjA2Mjk4NTIw","html_url":"https://github.com/tensorflow/swift/pull/53","diff_url":"https://github.com/tensorflow/swift/pull/53.diff","patch_url":"https://github.com/tensorflow/swift/pull/53.patch","issue_url":"https://api.github.com/repos/tensorflow/swift/issues/53","number":53,"state":"open","locked":false,"title":"Add parameter update design document.","user":{"login":"dan-zheng","id":5590046,"node_id":"MDQ6VXNlcjU1OTAwNDY=","avatar_url":"https://avatars0.githubusercontent.com/u/5590046?v=4","gravatar_id":"","url":"https://api.github.com/users/dan-zheng","html_url":"https://github.com/dan-zheng","followers_url":"https://api.github.com/users/dan-zheng/followers","following_url":"https://api.github.com/users/dan-zheng/following{/other_user}","gists_url":"https://api.github.com/users/dan-zheng/gists{/gist_id}","starred_url":"https://api.github.com/users/dan-zheng/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dan-zheng/subscriptions","organizations_url":"https://api.github.com/users/dan-zheng/orgs","repos_url":"https://api.github.com/users/dan-zheng/repos","events_url":"https://api.github.com/users/dan-zheng/events{/privacy}","received_events_url":"https://api.github.com/users/dan-zheng/received_events","type":"User","site_admin":false},"body":"","created_at":"2018-08-06T06:17:18Z","updated_at":"2018-08-06T06:46:38Z","closed_at":null,"merged_at":null,"merge_commit_sha":"cd3e1caf61ef493cfd97772eefa23ed35dd5d57c","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/tensorflow/swift/pulls/53/commits","review_comments_url":"https://api.github.com/repos/tensorflow/swift/pulls/53/comments","review_comment_url":"https://api.github.com/repos/tensorflow/swift/pulls/comments{/number}","comments_url":"https://api.github.com/repos/tensorflow/swift/issues/53/comments","statuses_url":"https://api.github.com/repos/tensorflow/swift/statuses/4ae226322e2555042275fc34a233a86680a93d75","head":{"label":"dan-zheng:parameter-update","ref":"parameter-update","sha":"4ae226322e2555042275fc34a233a86680a93d75","user":{"login":"dan-zheng","id":5590046,"node_id":"MDQ6VXNlcjU1OTAwNDY=","avatar_url":"https://avatars0.githubusercontent.com/u/5590046?v=4","gravatar_id":"","url":"https://api.github.com/users/dan-zheng","html_url":"https://github.com/dan-zheng","followers_url":"https://api.github.com/users/dan-zheng/followers","following_url":"https://api.github.com/users/dan-zheng/following{/other_user}","gists_url":"https://api.github.com/users/dan-zheng/gists{/gist_id}","starred_url":"https://api.github.com/users/dan-zheng/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dan-zheng/subscriptions","organizations_url":"https://api.github.com/users/dan-zheng/orgs","repos_url":"https://api.github.com/users/dan-zheng/repos","events_url":"https://api.github.com/users/dan-zheng/events{/privacy}","received_events_url":"https://api.github.com/users/dan-zheng/received_events","type":"User","site_admin":false},"repo":{"id":141501851,"node_id":"MDEwOlJlcG9zaXRvcnkxNDE1MDE4NTE=","name":"tensorflow-swift","full_name":"dan-zheng/tensorflow-swift","owner":{"login":"dan-zheng","id":5590046,"node_id":"MDQ6VXNlcjU1OTAwNDY=","avatar_url":"https://avatars0.githubusercontent.com/u/5590046?v=4","gravatar_id":"","url":"https://api.github.com/users/dan-zheng","html_url":"https://github.com/dan-zheng","followers_url":"https://api.github.com/users/dan-zheng/followers","following_url":"https://api.github.com/users/dan-zheng/following{/other_user}","gists_url":"https://api.github.com/users/dan-zheng/gists{/gist_id}","starred_url":"https://api.github.com/users/dan-zheng/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dan-zheng/subscriptions","organizations_url":"https://api.github.com/users/dan-zheng/orgs","repos_url":"https://api.github.com/users/dan-zheng/repos","events_url":"https://api.github.com/users/dan-zheng/events{/privacy}","received_events_url":"https://api.github.com/users/dan-zheng/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/dan-zheng/tensorflow-swift","description":"Swift for TensorFlow project home page","fork":true,"url":"https://api.github.com/repos/dan-zheng/tensorflow-swift","forks_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/forks","keys_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/teams","hooks_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/hooks","issue_events_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/issues/events{/number}","events_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/events","assignees_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/assignees{/user}","branches_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/branches{/branch}","tags_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/tags","blobs_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/git/refs{/sha}","trees_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/statuses/{sha}","languages_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/languages","stargazers_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/stargazers","contributors_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/contributors","subscribers_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/subscribers","subscription_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/subscription","commits_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/commits{/sha}","git_commits_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/git/commits{/sha}","comments_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/comments{/number}","issue_comment_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/issues/comments{/number}","contents_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/contents/{+path}","compare_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/merges","archive_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/downloads","issues_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/issues{/number}","pulls_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/pulls{/number}","milestones_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/milestones{/number}","notifications_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/labels{/name}","releases_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/releases{/id}","deployments_url":"https://api.github.com/repos/dan-zheng/tensorflow-swift/deployments","created_at":"2018-07-18T23:55:10Z","updated_at":"2018-08-06T06:16:22Z","pushed_at":"2018-08-06T06:28:46Z","git_url":"git://github.com/dan-zheng/tensorflow-swift.git","ssh_url":"git@github.com:dan-zheng/tensorflow-swift.git","clone_url":"https://github.com/dan-zheng/tensorflow-swift.git","svn_url":"https://github.com/dan-zheng/tensorflow-swift","homepage":"","size":876,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"cc-by-4.0","name":"Creative Commons Attribution 4.0","spdx_id":"CC-BY-4.0","url":"https://api.github.com/licenses/cc-by-4.0","node_id":"MDc6TGljZW5zZTI1"},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"tensorflow:master","ref":"master","sha":"c34a74aa78991e9b8bf6f68894e17d69227231f3","user":{"login":"tensorflow","id":15658638,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE1NjU4NjM4","avatar_url":"https://avatars1.githubusercontent.com/u/15658638?v=4","gravatar_id":"","url":"https://api.github.com/users/tensorflow","html_url":"https://github.com/tensorflow","followers_url":"https://api.github.com/users/tensorflow/followers","following_url":"https://api.github.com/users/tensorflow/following{/other_user}","gists_url":"https://api.github.com/users/tensorflow/gists{/gist_id}","starred_url":"https://api.github.com/users/tensorflow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tensorflow/subscriptions","organizations_url":"https://api.github.com/users/tensorflow/orgs","repos_url":"https://api.github.com/users/tensorflow/repos","events_url":"https://api.github.com/users/tensorflow/events{/privacy}","received_events_url":"https://api.github.com/users/tensorflow/received_events","type":"Organization","site_admin":false},"repo":{"id":130902948,"node_id":"MDEwOlJlcG9zaXRvcnkxMzA5MDI5NDg=","name":"swift","full_name":"tensorflow/swift","owner":{"login":"tensorflow","id":15658638,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE1NjU4NjM4","avatar_url":"https://avatars1.githubusercontent.com/u/15658638?v=4","gravatar_id":"","url":"https://api.github.com/users/tensorflow","html_url":"https://github.com/tensorflow","followers_url":"https://api.github.com/users/tensorflow/followers","following_url":"https://api.github.com/users/tensorflow/following{/other_user}","gists_url":"https://api.github.com/users/tensorflow/gists{/gist_id}","starred_url":"https://api.github.com/users/tensorflow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tensorflow/subscriptions","organizations_url":"https://api.github.com/users/tensorflow/orgs","repos_url":"https://api.github.com/users/tensorflow/repos","events_url":"https://api.github.com/users/tensorflow/events{/privacy}","received_events_url":"https://api.github.com/users/tensorflow/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/tensorflow/swift","description":"Swift for TensorFlow project home page","fork":false,"url":"https://api.github.com/repos/tensorflow/swift","forks_url":"https://api.github.com/repos/tensorflow/swift/forks","keys_url":"https://api.github.com/repos/tensorflow/swift/keys{/key_id}","collaborators_url":"https://api.github.com/repos/tensorflow/swift/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/tensorflow/swift/teams","hooks_url":"https://api.github.com/repos/tensorflow/swift/hooks","issue_events_url":"https://api.github.com/repos/tensorflow/swift/issues/events{/number}","events_url":"https://api.github.com/repos/tensorflow/swift/events","assignees_url":"https://api.github.com/repos/tensorflow/swift/assignees{/user}","branches_url":"https://api.github.com/repos/tensorflow/swift/branches{/branch}","tags_url":"https://api.github.com/repos/tensorflow/swift/tags","blobs_url":"https://api.github.com/repos/tensorflow/swift/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/tensorflow/swift/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/tensorflow/swift/git/refs{/sha}","trees_url":"https://api.github.com/repos/tensorflow/swift/git/trees{/sha}","statuses_url":"https://api.github.com/repos/tensorflow/swift/statuses/{sha}","languages_url":"https://api.github.com/repos/tensorflow/swift/languages","stargazers_url":"https://api.github.com/repos/tensorflow/swift/stargazers","contributors_url":"https://api.github.com/repos/tensorflow/swift/contributors","subscribers_url":"https://api.github.com/repos/tensorflow/swift/subscribers","subscription_url":"https://api.github.com/repos/tensorflow/swift/subscription","commits_url":"https://api.github.com/repos/tensorflow/swift/commits{/sha}","git_commits_url":"https://api.github.com/repos/tensorflow/swift/git/commits{/sha}","comments_url":"https://api.github.com/repos/tensorflow/swift/comments{/number}","issue_comment_url":"https://api.github.com/repos/tensorflow/swift/issues/comments{/number}","contents_url":"https://api.github.com/repos/tensorflow/swift/contents/{+path}","compare_url":"https://api.github.com/repos/tensorflow/swift/compare/{base}...{head}","merges_url":"https://api.github.com/repos/tensorflow/swift/merges","archive_url":"https://api.github.com/repos/tensorflow/swift/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/tensorflow/swift/downloads","issues_url":"https://api.github.com/repos/tensorflow/swift/issues{/number}","pulls_url":"https://api.github.com/repos/tensorflow/swift/pulls{/number}","milestones_url":"https://api.github.com/repos/tensorflow/swift/milestones{/number}","notifications_url":"https://api.github.com/repos/tensorflow/swift/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/tensorflow/swift/labels{/name}","releases_url":"https://api.github.com/repos/tensorflow/swift/releases{/id}","deployments_url":"https://api.github.com/repos/tensorflow/swift/deployments","created_at":"2018-04-24T19:18:14Z","updated_at":"2018-08-06T01:07:23Z","pushed_at":"2018-08-06T06:28:47Z","git_url":"git://github.com/tensorflow/swift.git","ssh_url":"git@github.com:tensorflow/swift.git","clone_url":"https://github.com/tensorflow/swift.git","svn_url":"https://github.com/tensorflow/swift","homepage":"","size":948,"stargazers_count":2715,"watchers_count":2715,"language":null,"has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":193,"mirror_url":null,"archived":false,"open_issues_count":4,"license":{"key":"cc-by-4.0","name":"Creative Commons Attribution 4.0","spdx_id":"CC-BY-4.0","url":"https://api.github.com/licenses/cc-by-4.0","node_id":"MDc6TGljZW5zZTI1"},"forks":193,"open_issues":4,"watchers":2715,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/tensorflow/swift/pulls/53"},"html":{"href":"https://github.com/tensorflow/swift/pull/53"},"issue":{"href":"https://api.github.com/repos/tensorflow/swift/issues/53"},"comments":{"href":"https://api.github.com/repos/tensorflow/swift/issues/53/comments"},"review_comments":{"href":"https://api.github.com/repos/tensorflow/swift/pulls/53/comments"},"review_comment":{"href":"https://api.github.com/repos/tensorflow/swift/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/tensorflow/swift/pulls/53/commits"},"statuses":{"href":"https://api.github.com/repos/tensorflow/swift/statuses/4ae226322e2555042275fc34a233a86680a93d75"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"dan-zheng"}}