Skip to content

Commit b65b124

Browse files
committed
Add flag for TF32 self-attention layers in Psiformer. Thanks to Seb Bodenstein for investigating.
PiperOrigin-RevId: 831808752 Change-Id: Id5d61e4d47adb5ceddb8354ac5bf5bd14a2d1d2d
1 parent 6848678 commit b65b124

3 files changed

Lines changed: 19 additions & 6 deletions

File tree

ferminet/base_config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ def default() -> ml_collections.ConfigDict:
270270
'heads_dim': 64,
271271
'mlp_hidden_dims': (256,),
272272
'use_layer_norm': True,
273+
'tf32': False,
273274
},
274275
# Config common to all architectures.
275276
'determinants': 16, # Number of determinants.

ferminet/psiformer.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ class PsiformerOptions(networks.BaseNetworkOptions):
3838
mlp_hidden_dims: Tuple of sizes of hidden dimension of the MLP. Note that
3939
this does not include the final projection to the embedding dimension.
4040
use_layer_norm: If true, include a layer norm on both attention and MLP.
41+
tf32: If true, use TF32-precision matmuls where appropriate.
4142
"""
4243

4344
num_layers: int = 2
4445
num_heads: int = 4
4546
heads_dim: int = 64
4647
mlp_hidden_dims: Tuple[int, ...] = (256,)
4748
use_layer_norm: bool = False
49+
tf32: bool = False
4850

4951

5052
def make_layer_norm() ->...:
@@ -68,12 +70,15 @@ def apply(params: networks.ParamTree,
6870
return init, apply
6971

7072

71-
def make_multi_head_attention(num_heads: int, heads_dim: int) ->...:
73+
def make_multi_head_attention(num_heads: int,
74+
heads_dim: int,
75+
tf32: bool = False) ->...:
7276
"""FermiNet-style version of MultiHeadAttention."""
77+
prec = jax.lax.DotAlgorithmPreset.TF32_TF32_F32 if tf32 else None
7378

7479
# Linear layer plus reshape final dimensions to num_heads, heads_dim.
7580
def linear_projection(x: jnp.ndarray, weights: jnp.ndarray) -> jnp.ndarray:
76-
y = jnp.dot(x, weights)
81+
y = jnp.dot(x, weights, precision=prec)
7782
return y.reshape(*x.shape[:-1], num_heads, heads_dim)
7883

7984
def init(key: chex.PRNGKey,
@@ -123,13 +128,13 @@ def apply(params: networks.ParamTree, query: jnp.ndarray, key: jnp.ndarray,
123128
k = linear_projection(key, params['k_w'])
124129
v = linear_projection(value, params['v_w'])
125130

126-
attn_logits = jnp.einsum('...thd,...Thd->...htT', q, k)
131+
attn_logits = jnp.einsum('...thd,...Thd->...htT', q, k, precision=prec)
127132
scale = 1. / np.sqrt(heads_dim)
128133
attn_logits *= scale
129134

130135
attn_weights = jax.nn.softmax(attn_logits)
131136

132-
attn = jnp.einsum('...htT,...Thd->...thd', attn_weights, v)
137+
attn = jnp.einsum('...htT,...Thd->...thd', attn_weights, v, precision=prec)
133138

134139
# Concatenate attention matrix of all heads into a single vector.
135140
# Shape [..., q_index_dim, num_heads * heads_dim]
@@ -174,10 +179,11 @@ def make_self_attention_block(num_layers: int,
174179
num_heads: int,
175180
heads_dim: int,
176181
mlp_hidden_dims: Tuple[int, ...],
177-
use_layer_norm: bool = False) ->...:
182+
use_layer_norm: bool = False,
183+
tf32: bool = False) ->...:
178184
"""Create a QKV self-attention block."""
179185
attention_init, attention_apply = make_multi_head_attention(
180-
num_heads, heads_dim)
186+
num_heads, heads_dim, tf32)
181187
if use_layer_norm:
182188
layer_norm_init, layer_norm_apply = make_layer_norm()
183189
mlp_init, mlp_apply = make_mlp()
@@ -253,6 +259,7 @@ def make_psiformer_layers(
253259
heads_dim=options.heads_dim,
254260
mlp_hidden_dims=options.mlp_hidden_dims,
255261
use_layer_norm=options.use_layer_norm,
262+
tf32=options.tf32,
256263
)
257264

258265
def init(key: chex.PRNGKey) -> Tuple[int, networks.ParamTree]:
@@ -342,6 +349,7 @@ def make_fermi_net(
342349
heads_dim: int,
343350
mlp_hidden_dims: Tuple[int, ...],
344351
use_layer_norm: bool,
352+
tf32: bool,
345353
) -> networks.Network:
346354
"""Psiformer with stacked Self Attention layers.
347355
@@ -365,6 +373,7 @@ def make_fermi_net(
365373
heads_dim: Embedding dimension per-head for self-attention.
366374
mlp_hidden_dims: Tuple of hidden dimensions of the MLP.
367375
use_layer_norm: If true, use layer_norm on both attention and MLP.
376+
tf32: If true, use TF32-precision matmuls where appropriate.
368377
369378
Returns:
370379
Network object containing init, apply, orbitals, options, where init and
@@ -404,6 +413,7 @@ def make_fermi_net(
404413
heads_dim=heads_dim,
405414
mlp_hidden_dims=mlp_hidden_dims,
406415
use_layer_norm=use_layer_norm,
416+
tf32=tf32,
407417
) # pytype: disable=wrong-keyword-args
408418

409419
psiformer_layers = make_psiformer_layers(nspins, charges.shape[0], options)

ferminet/tests/psiformer_test.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ def test_antisymmetry(self, jastrow):
7979
heads_dim=32,
8080
mlp_hidden_dims=(64, 128),
8181
use_layer_norm=True,
82+
tf32=False,
8283
)
8384

8485
key, subkey = jax.random.split(key)
@@ -145,6 +146,7 @@ def test_psiformer(self, **network_options):
145146
'heads_dim': 128,
146147
'mlp_hidden_dims': (64, 32),
147148
'use_layer_norm': True,
149+
'tf32': False,
148150
}
149151

150152
network = psiformer.make_fermi_net(

0 commit comments

Comments
 (0)