@@ -43,48 +43,86 @@ Example
43
43
Below is a fully worked out example demonstrating how to use it to declare and
44
44
optimize a small `multilayer perceptron
45
45
<https://en.wikipedia.org/wiki/Multilayer_perceptron> `__ (MLP). This network
46
- implements a 2D neural field that we fit to an image.
46
+ implements a 2D neural field (right) that we then fit to a low-resolution image of `The
47
+ Great Wave off Kanagawa
48
+ <https://en.wikipedia.org/wiki/The_Great_Wave_off_Kanagawa> `__ (left).
49
+
50
+ .. image :: https://rgl.s3.eu-central-1.amazonaws.com/media/uploads/wjakob/2024/06/coopvec-screenshot.png
51
+ :width: 300
52
+ :align: center
47
53
48
54
.. code-block :: python
49
55
56
+ from tqdm.auto import tqdm
57
+ import imageio.v3 as iio
50
58
import drjit as dr
51
59
import drjit.nn as nn
60
+ from drjit.opt import Adam, GradScaler
61
+ from drjit.auto.ad import Texture2f, TensorXf, TensorXf16, Float16, Float32, Array2f, Array3f
62
+
63
+ # Load a test image and construct a texture object
64
+ ref = TensorXf(iio.imread(" https://rgl.s3.eu-central-1.amazonaws.com/media/uploads/wjakob/2024/06/wave-128.png" ) / 256 )
65
+ tex = Texture2f(ref)
52
66
53
- from drjit.llvm.ad import TensorXf16
54
- from drjit.opt import Adam
67
+ # Ensure consistent results when re-running the following
68
+ dr.seed( 0 )
55
69
56
70
# Establish the network structure
57
71
net = nn.Sequential(
58
- nn.Linear(- 1 , 32 , bias = False ),
59
- nn.ReLU(),
60
- nn.Linear(- 1 , - 1 ),
61
- nn.ReLU(),
72
+ nn.TriEncode(16 , 0.2 ),
73
+ nn.Cast(Float16),
74
+ nn.Linear(- 1 , - 1 , bias = False ),
75
+ nn.LeakyReLU(),
76
+ nn.Linear(- 1 , - 1 , bias = False ),
77
+ nn.LeakyReLU(),
78
+ nn.Linear(- 1 , - 1 , bias = False ),
79
+ nn.LeakyReLU(),
62
80
nn.Linear(- 1 , 3 , bias = False ),
63
- nn.Tanh ()
81
+ nn.Exp ()
64
82
)
65
83
66
84
# Instantiate the network for a specific backend + input size
67
85
net = net.alloc(TensorXf16, 2 )
68
86
69
- # Pack coefficients into a training-optimal layout
87
+ # Convert to training-optimal layout
70
88
coeffs, net = nn.pack(net, layout = ' training' )
89
+ print (net)
71
90
72
- # Optimize a float32 version of the packed coefficients
91
+ # Optimize a single precision copy of the parameters
73
92
opt = Adam(lr = 1e-3 , params = {' coeffs' : Float32(coeffs)})
74
93
75
- # Update network state from optimizer
76
- for i in range ( 1000 ):
77
- # Update neural network state
78
- coeffs[:] = Float16(opt[ ' coeffs ' ] )
94
+ # This is an adaptive mixed-precision (AMP) optimization, where a half
95
+ # precision computation runs within a larger single precision program.
96
+ # Gradient scaling is required to make this numerically well-behaved.
97
+ scaler = GradScaler( )
79
98
80
- # Create input
81
- out = net(nn.CoopVec(... ))
99
+ res = 256
82
100
83
- # Unpack
84
- out = Array3f16(result)
85
-
86
- # Backpropagate
87
- dr.backward(dr.square(reference- out))
101
+ for i in tqdm(range (40000 )):
102
+ # Update network state from optimizer
103
+ coeffs[:] = Float16(opt[' coeffs' ])
88
104
89
- # Take a gradient step
90
- opt.step()
105
+ # Generate jittered positions on [0, 1]^2
106
+ t = dr.arange(Float32, res)
107
+ p = (Array2f(dr.meshgrid(t, t)) + dr.rand(Array2f, (2 , res* res))) / res
108
+
109
+ # Evaluate neural net + L2 loss
110
+ img = Array3f(net(nn.CoopVec(p)))
111
+ loss = dr.squared_norm(tex.eval(p)- img)
112
+
113
+ # Mixed-precision training: take suitably scaled steps
114
+ dr.backward(scaler.scale(loss))
115
+ scaler.step(opt)
116
+
117
+ # Done optimizing, now let's plot the result
118
+ t = dr.linspace(Float32, 0 , 1 , res)
119
+ p= Array2f(dr.meshgrid(t, t))
120
+ img = Array3f(net(nn.CoopVec(p)))
121
+ img = dr.reshape(TensorXf(img, flip_axes = True ), (res, res, 3 ))
122
+
123
+ import matplotlib.pyplot as plt
124
+ fig, ax = plt.subplots(1 , 2 , figsize = (10 ,5 ))
125
+ ax[0 ].imshow(ref)
126
+ ax[1 ].imshow(dr.clip(img, 0 , 1 ))
127
+ fig.tight_layout()
128
+ plt.show()
0 commit comments