-
Notifications
You must be signed in to change notification settings - Fork 137
Add @_Freezable
property wrapper.
#250
Add @_Freezable
property wrapper.
#250
Conversation
d0b108c
to
28c5991
Compare
@Trainable
property wrapper.@Freezable
property wrapper.
Sources/TensorFlow/Layer.swift
Outdated
@_propertyWrapper | ||
public struct Freezable<Value: Differentiable> : Differentiable { | ||
@noDerivative public var isFrozen: Bool = false | ||
private var _value: Value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fact that this is using another level of property delegation makes me think there can be another property wrapper that splits up this logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the delegation here is necessary only because stored properties can't have custom jvp/vjps. Does it make sense to create a property wrapper to handle that behavior?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely! A separate property wrapper should provide a general way to define a custom VJP for a property. I'll write that out in a sec.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current property wrapper implementation in our branch is incomplete—it does not support the both-initialization-and-default-value pattern. I posted a question here: https://forums.swift.org/t/se-0258-property-wrappers-second-review/25843/76?u=rxwei.
Sources/TensorFlow/Layer.swift
Outdated
@_propertyWrapper | ||
public struct Freezable<Value: Differentiable> : Differentiable { | ||
@noDerivative public var isFrozen: Bool = false | ||
private var _value: Value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current property wrapper implementation in our branch is incomplete—it does not support the both-initialization-and-default-value pattern. I posted a question here: https://forums.swift.org/t/se-0258-property-wrappers-second-review/25843/76?u=rxwei.
XCTAssertEqual(1, gradient.0.$bias) | ||
XCTAssertEqual(2, gradient.1) | ||
} | ||
dense.freezableWeight.isFrozen = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is dense.$weight.isFrozen = true
not working?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't work because underlying storage is currently always private:
/Users/danielzheng/swift-apis/Tests/TensorFlowTests/FreezableTests.swift:62:15: error: '$weight' is inaccessible due to 'private' protection level
dense.$weight.isFrozen = true
^~~~~~~
/Users/danielzheng/swift-apis/Tests/TensorFlowTests/FreezableTests.swift:21:28: note: '$weight' declared here
@Freezable var weight: Float
^
Indicated with a comment:
struct FreezableDense : Layer {
@Freezable var weight: Float
// Workaround for underlying storage private access.
var freezableWeight: Freezable<Float> {
get { $weight }
set { $weight = newValue }
}
}
The ability to specify underlying storage access levels is important for accessing APIs defined on property wrapper types. @tanner0101 made a case for customizing underlying storage access levels in this post.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This indicates that the feature is not quite ready for our use case yet. We should review the 2nd iteration of the proposal and make sure things are going the right direction for us.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are both underlying properties and functions private? For example, could we define a freeze()
/unfreeze()
function in the property wrapper?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue is that wrapper value is private
, or internal
at most.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to @DougGregor's recent post, this may be getting changed:
Here's the set of things I want to revise:
...
When wrapperValue is present, the $foo property becomes available and has the same access level as the original property foo
If so, that should make property wrappers viable for your use case. 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that'd be great!
Property wrappers are defined as structs and so it makes sense that the |
This is because the property wrapper proposal is setting a new standard for attributes in Swift: All custom attributes start with a capital letter, and all compiler builtin attributes use camel case. |
Thanks @rxwei! I was not aware of the new standard. |
141f449
to
41aee7d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but please underscore for now until we get more experience.
`@_Freezable` wraps differentiable values and provides toggleable "trainability" via the `isFrozen` property and `freeze`/`unfreeze` mutating methods. When `isFrozen` is true, accesses to `value` have a derivative of zero. The alternative name `@_Trainable` was considered, but `@_Trainable` gives the impression that "only properties marked with `@_Trainable` are trainable", which is not true: all properties are trainable (i.e. their derivatives are propagated) by default.
41aee7d
to
7c96679
Compare
Done in 7c96679. |
@Freezable
property wrapper.@_Freezable
property wrapper.
Merging |
@_Freezable
wraps differentiable values and provides toggleable "trainability" via theisFrozen
property and thefreeze
/unfreeze
mutating methods.When
isFrozen
is true, accesses tovalue
have a derivative of zero.Example:
The alternative name
@_Trainable
was considered, but@_Trainable
gives the impression that "only properties marked with@_Trainable
are trainable", which is not true: all properties are trainable (i.e. their derivatives are propagated) by default.