@@ -4,64 +4,66 @@ import Flux: _big_show
4
4
@compact(forward::Function; name=nothing, parameters...)
5
5
6
6
Creates a layer by specifying some `parameters`, in the form of keywords,
7
- and (usually as a `do` block) a function for the forward pass.
7
+ and a function for the forward pass (often as a `do` block).
8
+
8
9
You may think of `@compact` as a specialized `let` block creating local variables
9
10
that are trainable in Flux.
10
11
Declared variable names may be used within the body of the `forward` function.
11
12
12
- Here is a linear model:
13
+ # Examples
14
+
15
+ Here is a linear model, equivalent to `Flux.Scale`:
13
16
14
17
```
15
- r = @compact(w = rand(3)) do x
16
- w .* x
17
- end
18
- r([1, 1, 1]) # x is set to [1, 1, 1].
18
+ using Flux, Fluxperimental
19
+
20
+ w = rand(3)
21
+ sc = @compact(x -> x .* w; w)
22
+
23
+ sc([1 10 100]) # 3×3 Matrix as output.
24
+ ans ≈ Flux.Scale(w)([1 10 100]) # equivalent Flux layer
19
25
```
20
26
21
- Here is a linear model with bias and activation:
27
+ Here is a linear model with bias and activation, equivalent to Flux's `Dense` layer.
28
+ The forward pass function is now written as a do block, instead of `x -> begin y = W * x; ...`
22
29
23
30
```
24
- d_in = 5
31
+ d_in = 3
25
32
d_out = 7
26
- d = @compact(W = randn(d_out, d_in), b = zeros(d_out), act = relu) do x
33
+ layer = @compact(W = randn(d_out, d_in), b = zeros(d_out), act = relu) do x
27
34
y = W * x
28
35
act.(y .+ b)
29
36
end
30
- d(ones(5, 10)) # 7×10 Matrix as output.
31
- d([1,2,3,4,5]) ≈ Dense(d.variables.W, zeros(7), relu)([1,2,3,4,5]) # Equivalent to a dense layer
37
+
38
+ den = Dense(layer.variables.W, zeros(7), relu)([1,2,3]) # equivalent Flux layer
39
+ layer(ones(3, 10)) ≈ layer(ones(3, 10)) # 7×10 Matrix as output.
32
40
```
33
- ```
34
41
35
- Finally, here is a simple MLP:
42
+ Finally, here is a simple MLP, equivalent to a `Chain` with 5 `Dense` layers :
36
43
37
44
```
38
- using Flux
39
-
40
- n_in = 1
41
- n_out = 1
45
+ d_in = 1
42
46
nlayers = 3
43
47
44
48
model = @compact(
45
- w1=Dense(n_in, 128),
46
- w2=[Dense(128, 128) for i=1:nlayers],
47
- w3=Dense(128, n_out),
48
- act=relu
49
+ lay1 = Dense(d_in => 64),
50
+ lay234 = [Dense(64 => 64) for i=1:nlayers],
51
+ wlast = rand32(64),
49
52
) do x
50
- embed = act(w1 (x))
51
- for w in w2
52
- embed = act(w(embed ))
53
+ y = tanh.(lay1 (x))
54
+ for lay in lay234
55
+ y = relu.(lay(y ))
53
56
end
54
- out = w3(embed)
55
- return out
57
+ return wlast' * y
56
58
end
57
59
58
- model(randn(n_in, 32 )) # 1×32 Matrix as output.
60
+ model(randn(Float32, d_in, 8 )) # 1×8 array as output.
59
61
```
60
62
61
- We can train this model just like any `Chain`:
63
+ We can train this model just like any `Chain`, for example :
62
64
63
65
```
64
- data = [([x], 2x-x^3) for x in -2:0.1f0:2]
66
+ data = [([x], [ 2x-x^3] ) for x in -2:0.1f0:2]
65
67
optim = Flux.setup(Adam(), model)
66
68
67
69
for epoch in 1:1000
71
73
To specify a custom printout for the model, you may find [`NoShow`](@ref) useful.
72
74
"""
73
75
macro compact (_exs... )
76
+ _compact (_exs... ) |> esc
77
+ end
78
+
79
+ function _compact (_exs... )
74
80
# check inputs, extracting function expression fex and unprocessed keyword arguments _kwexs
75
- isempty (_exs) && error (" expects at least two expressions: a function and at least one keyword" )
81
+ isempty (_exs) && error (" @compact expects at least two expressions: a function and at least one keyword" )
76
82
if Meta. isexpr (_exs[1 ], :parameters )
77
- length (_exs) >= 2 || error (" expects an anonymous function" )
83
+ length (_exs) >= 2 || error (" @compact expects an anonymous function" )
78
84
fex = _exs[2 ]
79
85
_kwexs = (_exs[1 ], _exs[3 : end ]. .. )
80
86
else
81
87
fex = _exs[1 ]
82
88
_kwexs = _exs[2 : end ]
83
89
end
84
- Meta. isexpr (fex, :(-> )) || error (" expects an anonymous function" )
85
- isempty (_kwexs) && error (" expects keyword arguments" )
86
- all (ex -> Meta. isexpr (ex, (:kw ,:(= ),:parameters )), _kwexs) || error (" expects only keyword arguments" )
90
+ Meta. isexpr (fex, :(-> )) || error (" @compact expects an anonymous function" )
91
+ isempty (_kwexs) && error (" @compact expects keyword arguments" )
92
+ all (ex -> Meta. isexpr (ex, (:kw ,:(= ),:parameters )), _kwexs) || error (" @compact expects only keyword arguments" )
87
93
88
94
# process keyword arguments
89
95
if Meta. isexpr (_kwexs[1 ], :parameters ) # handle keyword arguments provided after semicolon
@@ -101,20 +107,20 @@ macro compact(_exs...)
101
107
fex_args = fex. args[1 ]
102
108
isa (fex_args, Symbol) ? string (fex_args) : join (fex_args. args, " , " )
103
109
catch e
104
- @warn " Function stringifying does not yet handle all cases. Falling back to empty string for input arguments "
105
- " "
110
+ @warn """ @compact's function stringifying does not yet handle all cases. Falling back to "?" """ maxlog = 1
111
+ " ? "
106
112
end
107
- block = string (Base. remove_linenums! (fex). args[2 ])
113
+ block = string (Base. remove_linenums! (fex). args[2 ]) # TODO make this remove macro comments
108
114
109
115
# edit expressions
110
116
vars = map (ex -> ex. args[1 ], kwexs)
111
- fex = supportself (fex, vars)
117
+ fex = _supportself (fex, vars)
112
118
113
119
# assemble
114
- return esc ( :($ CompactLayer ($ fex, ($ input, $ block); $ (kwexs... ) )))
120
+ return :($ CompactLayer ($ fex, ($ input, $ block); $ (kwexs... )))
115
121
end
116
122
117
- function supportself (fex:: Expr , vars)
123
+ function _supportself (fex:: Expr , vars)
118
124
@gensym self
119
125
@gensym curried_f
120
126
# To avoid having to manipulate fex's arguments and body explicitly, we form a curried function first
@@ -174,7 +180,7 @@ function Flux._big_show(io::IO, obj::CompactLayer, indent::Int=0, name=nothing)
174
180
print (io, " " ^ indent, post)
175
181
end
176
182
177
- input != " " && print (io, " do " , input)
183
+ print (io, " do " , input)
178
184
if block != " "
179
185
block_to_print = block[6 : end ]
180
186
# Increase indentation of block according to `indent`:
0 commit comments