Overview¶
In previous notebooks we have demonstrated how xbatcher
converts both toy xarray
objects into tensors and back again. In this notebook we incorporate these functions in an end-to-end workflow training an autoencoder on an elevation dataset. Once trained, the model is used to reconstruct two datasets:
- The overall elevation tile
- A data cube of the autoencoder’s latent dimension
Prerequisites¶
This notebook assumes familiarity with xarray
, xbatcher
, and torch
. You don’t have to know how autoencoders work - we explain that when necessary.
Concepts | Importance | Notes |
---|---|---|
Intro to Xarray | Necessary | Array indexing |
Xbatcher fundamentals | Necessary | Passing data to models |
PyTorch fundamentals | Helpful | Model training loop |
Autoencoders | Helpful | More information on how autoencoders work. |
- Time to learn: 30 minutes.
- System requirements:
- Windows users may hit an import error on
rioxarray
(link). If that happens, addimport osgeo
aboveimport rioxarray
and that seems to fix the issue.
- Windows users may hit an import error on
Imports¶
import os
if os.name == 'nt':
import osgeo
from importlib import reload
# DL stuff
import matplotlib.pyplot as plt
import torch
torch.set_default_dtype(torch.float64)
# Geospatial stuff
import xarray as xr
import xbatcher
import rioxarray
from xbatcher.loaders.torch import MapDataset
# Etc
import numpy as np
from numpy.linalg import norm
from matplotlib import pyplot as plt
# Locals
import functions
import autoencoder
Get data¶
We will start by pulling a segment of NASADEM for Washington’s Olympic peninsula. The entire DEM is also available on NASA Earthdata and Planetary Computer.
# Rasterio adds a blank edge. Trim these out.
dem = rioxarray.open_rasterio("../ASTGTMV003_N47W124_dem.tif")
dem = dem.isel(y=slice(0, -1), x=slice(0, -1))
dem = (dem - dem.min()) / (dem.max() - dem.min())
dem
Note that we rescaled the DEM to be in the range [0, 1]. This modification makes it easier for the autoencoder to train.
dem.isel(band=0).plot.imshow(cmap="terrain")

Generate training examples¶
Here, we use xbatcher to generate patches of terrain. Even though we did not specify the band
axis in the BatchGenerator
, this axis still propagates to the output tensor’s second axis. Remember that torch data loaders add a batch dimension, so the tensor’s first axis is not relevant to the original xarray object.
bgen_x = xbatcher.BatchGenerator(
dem,
input_dims=dict(x=32, y=32),
input_overlap=dict(x=16, y=16)
)
ds = MapDataset(
X_generator=bgen_x
)
loader = torch.utils.data.DataLoader(ds, batch_size=16, shuffle=True)
X = next(iter(loader))
print("Input tensor shape:", X.shape)
Input tensor shape: torch.Size([16, 1, 32, 32])
Model setup¶
Here we instantiate the autoencoder class and verify that the output shape is what we expect. Autoencoders compress input data to a latent space and then reconstruct the input from the compressed representation. Usually, we are interested in the compressed result, but during training we are trying to recreate the input exactly. Therefore, the output tensor should match the shape of the input tensor.
Input tensor:
Output tensor:
import dask.array.svg as svg
from IPython.display import HTML
svg.svg(((50,), (10,), (10,)), size=100).replace("\n", "")
'<svg width="153" height="143" style="stroke:rgb(0,0,0);stroke-width:1" > <!-- Horizontal lines --> <line x1="10" y1="0" x2="68" y2="58" style="stroke-width:2" /> <line x1="10" y1="35" x2="68" y2="93" style="stroke-width:2" /> <!-- Vertical lines --> <line x1="10" y1="0" x2="10" y2="35" style="stroke-width:2" /> <line x1="68" y1="58" x2="68" y2="93" style="stroke-width:2" /> <!-- Colored Rectangle --> <polygon points="10.0,0.0 68.82352941176471,58.82352941176471 68.82352941176471,93.8317713259397 10.0,35.00824191417499" style="fill:#ECB172A0;stroke-width:0"/> <!-- Horizontal lines --> <line x1="10" y1="0" x2="45" y2="0" style="stroke-width:2" /> <line x1="68" y1="58" x2="103" y2="58" style="stroke-width:2" /> <!-- Vertical lines --> <line x1="10" y1="0" x2="68" y2="58" style="stroke-width:2" /> <line x1="45" y1="0" x2="103" y2="58" style="stroke-width:2" /> <!-- Colored Rectangle --> <polygon points="10.0,0.0 45.00824191417499,0.0 103.8317713259397,58.82352941176471 68.82352941176471,58.82352941176471" style="fill:#ECB172A0;stroke-width:0"/> <!-- Horizontal lines --> <line x1="68" y1="58" x2="103" y2="58" style="stroke-width:2" /> <line x1="68" y1="93" x2="103" y2="93" style="stroke-width:2" /> <!-- Vertical lines --> <line x1="68" y1="58" x2="68" y2="93" style="stroke-width:2" /> <line x1="103" y1="58" x2="103" y2="93" style="stroke-width:2" /> <!-- Colored Rectangle --> <polygon points="68.82352941176471,58.82352941176471 103.8317713259397,58.82352941176471 103.8317713259397,93.8317713259397 68.82352941176471,93.8317713259397" style="fill:#ECB172A0;stroke-width:0"/> <!-- Text --> <text x="86.327650" y="113.831771" font-size="1.0rem" font-weight="100" text-anchor="middle" >10</text> <text x="123.831771" y="76.327650" font-size="1.0rem" font-weight="100" text-anchor="middle" transform="rotate(0,123.831771,76.327650)">10</text> <text x="29.411765" y="84.420007" font-size="1.0rem" font-weight="100" text-anchor="middle" transform="rotate(45,29.411765,84.420007)">50</text></svg>'
m = autoencoder.Autoencoder(base_channel_size=32, latent_dim=64, num_input_channels=1, width=32, height=32)
opt = m._configure_optimizers()
out = m(X)
print(out.shape)
assert(out.shape == X.shape)
torch.Size([16, 1, 32, 32])
Model training¶
We aren’t using pytorch-lightning and load a pre-trained model here to keep the notebook environment lean. For your project, we highly recommend using a framework to abstract away much of the boilerplate code below.
def train_one_epoch(epoch_index):
last_loss = 0.
running_loss = 0.
for i, batch in enumerate(tqdm(loader)):
# Zero your gradients for every batch!
opt.zero_grad()
# Make predictions for this batch
outputs = m(batch)
# Compute the loss and its gradients
loss = m._get_reconstruction_loss(batch, outputs)
loss.backward()
running_loss += loss.item()
# Adjust learning weights
opt.step()
return running_loss / len(loader)
n_epochs = 5
for i_epoch in range(n_epochs):
loss = train_one_epoch(i_epoch)
print(f"Epoch {i_epoch+1:>3}: {loss:.3e}")
torch.save(m.state_dict(), "../autoencoder.torch")
m.load_state_dict(torch.load("../autoencoder.torch", weights_only=True))
<All keys matched successfully>
m.eval()
n_examples = 4
inputs = next(iter(loader))
outputs = m(inputs)
inputs = inputs.detach().cpu().numpy()
outputs = outputs.detach().cpu().numpy()
fig, axes = plt.subplots(2, n_examples)
for i_col in range(n_examples):
axes[0, i_col].imshow(inputs[i_col, 0, ...])
axes[1, i_col].imshow(outputs[i_col, 0, ...])
for a in axes.flat:
a.set_xticks([])
a.set_yticks([])
axes[0, 0].set_ylabel("Original patch")
axes[1, 0].set_ylabel("Reconstruction")
fig.tight_layout()
plt.show()

Reconstruction 1: Getting the full array back¶
Suppose we would like to evaluate how the autoencoder does on reconstructing the entire terrain patch by combining outputs across all input patches. To do so we can use the predict_on_array
function described in the previous notebook. Our model outputs tensors with shape (band=1, x=32, y=32)
. We need to specify each of these axes in the call to predict_on_array
. band
does not change size and is not used by the BatchGenerator
, so it goes in core_dim
. Both x
and y
are used by the BatchGenerator
, so although they do not change size they still go in resample_dim
. That accounts for all tensor axes, so we can leave the new_dim
argument as an empty list.
dem_reconst = functions.predict_on_array(
dataset=ds,
model=m,
output_tensor_dim=dict(band=1, y=32, x=32),
new_dim=[],
core_dim=["band"],
resample_dim=["x", "y"]
)
0%| | 0/3136 [00:00<?, ?it/s]
0%| | 3/3136 [00:00<02:01, 25.87it/s]
0%| | 6/3136 [00:00<02:01, 25.81it/s]
0%| | 9/3136 [00:00<02:00, 25.94it/s]
0%| | 12/3136 [00:00<02:00, 25.98it/s]
0%| | 15/3136 [00:00<01:59, 26.18it/s]
1%| | 18/3136 [00:00<01:56, 26.69it/s]
1%| | 21/3136 [00:00<01:55, 27.04it/s]
1%| | 24/3136 [00:00<01:54, 27.22it/s]
1%| | 27/3136 [00:01<01:53, 27.27it/s]
1%| | 30/3136 [00:01<01:53, 27.29it/s]
1%| | 33/3136 [00:01<01:53, 27.27it/s]
1%| | 36/3136 [00:01<01:53, 27.25it/s]
1%| | 39/3136 [00:01<01:54, 27.15it/s]
1%|▏ | 42/3136 [00:01<01:53, 27.17it/s]
1%|▏ | 45/3136 [00:01<01:53, 27.27it/s]
2%|▏ | 48/3136 [00:01<01:53, 27.20it/s]
2%|▏ | 51/3136 [00:01<01:52, 27.54it/s]
2%|▏ | 55/3136 [00:01<01:39, 30.92it/s]
2%|▏ | 59/3136 [00:02<01:43, 29.60it/s]
2%|▏ | 63/3136 [00:02<01:39, 30.85it/s]
2%|▏ | 67/3136 [00:02<01:36, 31.65it/s]
2%|▏ | 71/3136 [00:02<01:35, 31.95it/s]
2%|▏ | 75/3136 [00:02<01:34, 32.34it/s]
3%|▎ | 79/3136 [00:02<01:33, 32.55it/s]
3%|▎ | 83/3136 [00:02<01:33, 32.81it/s]
3%|▎ | 88/3136 [00:02<01:26, 35.35it/s]
3%|▎ | 93/3136 [00:03<01:21, 37.35it/s]
3%|▎ | 98/3136 [00:03<01:18, 38.51it/s]
3%|▎ | 103/3136 [00:03<01:16, 39.69it/s]
3%|▎ | 108/3136 [00:03<01:15, 40.31it/s]
4%|▎ | 113/3136 [00:03<01:13, 40.93it/s]
4%|▍ | 118/3136 [00:03<01:13, 41.12it/s]
4%|▍ | 123/3136 [00:03<01:12, 41.31it/s]
4%|▍ | 128/3136 [00:03<01:13, 40.69it/s]
4%|▍ | 133/3136 [00:04<01:14, 40.17it/s]
4%|▍ | 138/3136 [00:04<01:13, 40.81it/s]
5%|▍ | 143/3136 [00:04<01:12, 41.33it/s]
5%|▍ | 148/3136 [00:04<01:11, 41.75it/s]
5%|▍ | 153/3136 [00:04<01:11, 41.97it/s]
5%|▌ | 158/3136 [00:04<01:10, 42.23it/s]
5%|▌ | 163/3136 [00:04<01:10, 42.23it/s]
5%|▌ | 168/3136 [00:04<01:10, 42.37it/s]
6%|▌ | 173/3136 [00:05<01:09, 42.43it/s]
6%|▌ | 178/3136 [00:05<01:09, 42.53it/s]
6%|▌ | 183/3136 [00:05<01:09, 42.36it/s]
6%|▌ | 188/3136 [00:05<01:09, 42.57it/s]
6%|▌ | 193/3136 [00:05<01:09, 42.56it/s]
6%|▋ | 198/3136 [00:05<01:09, 42.43it/s]
6%|▋ | 203/3136 [00:05<01:09, 42.42it/s]
7%|▋ | 208/3136 [00:05<01:09, 42.15it/s]
7%|▋ | 213/3136 [00:05<01:09, 42.23it/s]
7%|▋ | 218/3136 [00:06<01:09, 42.19it/s]
7%|▋ | 223/3136 [00:06<01:08, 42.29it/s]
7%|▋ | 228/3136 [00:06<01:08, 42.44it/s]
7%|▋ | 233/3136 [00:06<01:08, 42.46it/s]
8%|▊ | 238/3136 [00:06<01:08, 42.37it/s]
8%|▊ | 243/3136 [00:06<01:08, 42.46it/s]
8%|▊ | 248/3136 [00:06<01:07, 42.52it/s]
8%|▊ | 253/3136 [00:06<01:07, 42.47it/s]
8%|▊ | 258/3136 [00:07<01:07, 42.34it/s]
8%|▊ | 263/3136 [00:07<01:07, 42.27it/s]
9%|▊ | 268/3136 [00:07<01:08, 42.14it/s]
9%|▊ | 273/3136 [00:07<01:07, 42.37it/s]
9%|▉ | 278/3136 [00:07<01:07, 42.32it/s]
9%|▉ | 283/3136 [00:07<01:09, 40.95it/s]
9%|▉ | 288/3136 [00:07<01:15, 37.69it/s]
9%|▉ | 293/3136 [00:07<01:13, 38.72it/s]
10%|▉ | 298/3136 [00:08<01:11, 39.85it/s]
10%|▉ | 303/3136 [00:08<01:09, 40.70it/s]
10%|▉ | 308/3136 [00:08<01:08, 41.09it/s]
10%|▉ | 313/3136 [00:08<01:07, 41.53it/s]
10%|█ | 318/3136 [00:08<01:08, 41.44it/s]
10%|█ | 323/3136 [00:08<01:07, 41.79it/s]
10%|█ | 328/3136 [00:08<01:06, 42.12it/s]
11%|█ | 333/3136 [00:08<01:06, 42.28it/s]
11%|█ | 338/3136 [00:08<01:06, 42.20it/s]
11%|█ | 343/3136 [00:09<01:06, 42.29it/s]
11%|█ | 348/3136 [00:09<01:05, 42.35it/s]
11%|█▏ | 353/3136 [00:09<01:05, 42.39it/s]
11%|█▏ | 358/3136 [00:09<01:05, 42.55it/s]
12%|█▏ | 363/3136 [00:09<01:05, 42.44it/s]
12%|█▏ | 368/3136 [00:09<01:04, 42.60it/s]
12%|█▏ | 373/3136 [00:09<01:05, 42.20it/s]
12%|█▏ | 378/3136 [00:09<01:05, 42.17it/s]
12%|█▏ | 383/3136 [00:10<01:06, 41.65it/s]
12%|█▏ | 388/3136 [00:10<01:05, 41.81it/s]
13%|█▎ | 393/3136 [00:10<01:05, 41.69it/s]
13%|█▎ | 398/3136 [00:10<01:05, 41.93it/s]
13%|█▎ | 403/3136 [00:10<01:05, 42.02it/s]
13%|█▎ | 408/3136 [00:10<01:04, 42.08it/s]
13%|█▎ | 413/3136 [00:10<01:04, 42.20it/s]
13%|█▎ | 418/3136 [00:10<01:04, 42.21it/s]
13%|█▎ | 423/3136 [00:10<01:03, 42.42it/s]
14%|█▎ | 428/3136 [00:11<01:03, 42.52it/s]
14%|█▍ | 433/3136 [00:11<01:03, 42.42it/s]
14%|█▍ | 438/3136 [00:11<01:03, 42.55it/s]
14%|█▍ | 443/3136 [00:11<01:03, 42.61it/s]
14%|█▍ | 448/3136 [00:11<01:03, 42.45it/s]
14%|█▍ | 453/3136 [00:11<01:02, 42.61it/s]
15%|█▍ | 458/3136 [00:11<01:02, 42.59it/s]
15%|█▍ | 463/3136 [00:11<01:03, 42.39it/s]
15%|█▍ | 468/3136 [00:12<01:02, 42.46it/s]
15%|█▌ | 473/3136 [00:12<01:02, 42.55it/s]
15%|█▌ | 478/3136 [00:12<01:02, 42.55it/s]
15%|█▌ | 483/3136 [00:12<01:02, 42.61it/s]
16%|█▌ | 488/3136 [00:12<01:02, 42.40it/s]
16%|█▌ | 493/3136 [00:12<01:02, 42.61it/s]
16%|█▌ | 498/3136 [00:12<01:01, 42.68it/s]
16%|█▌ | 503/3136 [00:12<01:05, 40.45it/s]
16%|█▌ | 508/3136 [00:12<01:04, 40.90it/s]
16%|█▋ | 513/3136 [00:13<01:03, 41.38it/s]
17%|█▋ | 518/3136 [00:13<01:02, 41.72it/s]
17%|█▋ | 523/3136 [00:13<01:02, 42.05it/s]
17%|█▋ | 528/3136 [00:13<01:01, 42.22it/s]
17%|█▋ | 533/3136 [00:13<01:01, 42.30it/s]
17%|█▋ | 538/3136 [00:13<01:01, 42.26it/s]
17%|█▋ | 543/3136 [00:13<01:01, 42.12it/s]
17%|█▋ | 548/3136 [00:13<01:01, 42.20it/s]
18%|█▊ | 553/3136 [00:14<01:01, 42.27it/s]
18%|█▊ | 558/3136 [00:14<01:00, 42.36it/s]
18%|█▊ | 563/3136 [00:14<01:00, 42.36it/s]
18%|█▊ | 568/3136 [00:14<01:00, 42.50it/s]
18%|█▊ | 573/3136 [00:14<01:00, 42.51it/s]
18%|█▊ | 578/3136 [00:14<01:00, 42.59it/s]
19%|█▊ | 583/3136 [00:14<00:59, 42.57it/s]
19%|█▉ | 588/3136 [00:14<00:59, 42.49it/s]
19%|█▉ | 593/3136 [00:14<01:00, 42.04it/s]
19%|█▉ | 598/3136 [00:15<01:00, 42.24it/s]
19%|█▉ | 603/3136 [00:15<00:59, 42.27it/s]
19%|█▉ | 608/3136 [00:15<00:59, 42.50it/s]
20%|█▉ | 613/3136 [00:15<00:59, 42.68it/s]
20%|█▉ | 618/3136 [00:15<00:59, 42.63it/s]
20%|█▉ | 623/3136 [00:15<00:58, 42.72it/s]
20%|██ | 628/3136 [00:15<00:58, 42.63it/s]
20%|██ | 633/3136 [00:15<00:58, 42.54it/s]
20%|██ | 638/3136 [00:16<01:01, 40.61it/s]
21%|██ | 643/3136 [00:16<01:00, 41.23it/s]
21%|██ | 648/3136 [00:16<00:59, 41.74it/s]
21%|██ | 653/3136 [00:16<01:01, 40.09it/s]
21%|██ | 658/3136 [00:16<01:00, 40.90it/s]
21%|██ | 663/3136 [00:16<00:59, 41.45it/s]
21%|██▏ | 668/3136 [00:16<00:58, 41.95it/s]
21%|██▏ | 673/3136 [00:16<00:58, 42.22it/s]
22%|██▏ | 678/3136 [00:17<00:57, 42.48it/s]
22%|██▏ | 683/3136 [00:17<00:57, 42.69it/s]
22%|██▏ | 688/3136 [00:17<00:57, 42.61it/s]
22%|██▏ | 693/3136 [00:17<00:57, 42.78it/s]
22%|██▏ | 698/3136 [00:17<00:56, 42.85it/s]
22%|██▏ | 703/3136 [00:17<00:57, 42.67it/s]
23%|██▎ | 708/3136 [00:17<00:57, 41.97it/s]
23%|██▎ | 713/3136 [00:17<00:57, 41.93it/s]
23%|██▎ | 718/3136 [00:17<00:58, 41.46it/s]
23%|██▎ | 723/3136 [00:18<00:57, 41.91it/s]
23%|██▎ | 728/3136 [00:18<00:57, 42.07it/s]
23%|██▎ | 733/3136 [00:18<00:56, 42.21it/s]
24%|██▎ | 738/3136 [00:18<00:56, 42.41it/s]
24%|██▎ | 743/3136 [00:18<00:56, 42.50it/s]
24%|██▍ | 748/3136 [00:18<00:56, 42.61it/s]
24%|██▍ | 753/3136 [00:18<00:55, 42.71it/s]
24%|██▍ | 758/3136 [00:18<00:55, 42.80it/s]
24%|██▍ | 763/3136 [00:19<00:55, 42.69it/s]
24%|██▍ | 768/3136 [00:19<00:55, 42.69it/s]
25%|██▍ | 773/3136 [00:19<00:55, 42.73it/s]
25%|██▍ | 778/3136 [00:19<00:55, 42.84it/s]
25%|██▍ | 783/3136 [00:19<00:54, 42.87it/s]
25%|██▌ | 788/3136 [00:19<00:54, 43.03it/s]
25%|██▌ | 793/3136 [00:19<00:55, 42.08it/s]
25%|██▌ | 798/3136 [00:19<00:56, 41.72it/s]
26%|██▌ | 803/3136 [00:19<00:55, 41.89it/s]
26%|██▌ | 808/3136 [00:20<00:55, 42.13it/s]
26%|██▌ | 813/3136 [00:20<00:54, 42.34it/s]
26%|██▌ | 818/3136 [00:20<00:54, 42.36it/s]
26%|██▌ | 823/3136 [00:20<00:54, 42.56it/s]
26%|██▋ | 828/3136 [00:20<00:54, 42.70it/s]
27%|██▋ | 833/3136 [00:20<00:53, 42.80it/s]
27%|██▋ | 838/3136 [00:20<00:53, 42.85it/s]
27%|██▋ | 843/3136 [00:20<00:53, 42.77it/s]
27%|██▋ | 848/3136 [00:21<00:53, 42.80it/s]
27%|██▋ | 853/3136 [00:21<00:53, 42.85it/s]
27%|██▋ | 858/3136 [00:21<00:53, 42.65it/s]
28%|██▊ | 863/3136 [00:21<00:53, 42.80it/s]
28%|██▊ | 868/3136 [00:21<00:52, 42.85it/s]
28%|██▊ | 873/3136 [00:21<00:52, 42.94it/s]
28%|██▊ | 878/3136 [00:21<00:52, 43.01it/s]
28%|██▊ | 883/3136 [00:21<00:52, 42.67it/s]
28%|██▊ | 888/3136 [00:21<00:52, 42.68it/s]
28%|██▊ | 893/3136 [00:22<00:52, 42.74it/s]
29%|██▊ | 898/3136 [00:22<00:52, 42.84it/s]
29%|██▉ | 903/3136 [00:22<00:52, 42.87it/s]
29%|██▉ | 908/3136 [00:22<00:51, 42.93it/s]
29%|██▉ | 913/3136 [00:22<00:51, 42.86it/s]
29%|██▉ | 918/3136 [00:22<00:51, 42.87it/s]
29%|██▉ | 923/3136 [00:22<00:51, 42.94it/s]
30%|██▉ | 928/3136 [00:22<00:51, 42.81it/s]
30%|██▉ | 933/3136 [00:23<00:51, 42.72it/s]
30%|██▉ | 938/3136 [00:23<00:53, 41.11it/s]
30%|███ | 943/3136 [00:23<00:52, 41.54it/s]
30%|███ | 948/3136 [00:23<00:52, 42.03it/s]
30%|███ | 953/3136 [00:23<00:51, 42.29it/s]
31%|███ | 958/3136 [00:23<00:51, 42.39it/s]
31%|███ | 963/3136 [00:23<00:51, 42.59it/s]
31%|███ | 968/3136 [00:23<00:51, 42.36it/s]
31%|███ | 973/3136 [00:23<00:50, 42.60it/s]
31%|███ | 978/3136 [00:24<00:50, 42.57it/s]
31%|███▏ | 983/3136 [00:24<00:50, 42.63it/s]
32%|███▏ | 988/3136 [00:24<00:50, 42.67it/s]
32%|███▏ | 993/3136 [00:24<00:50, 42.67it/s]
32%|███▏ | 998/3136 [00:24<00:49, 42.78it/s]
32%|███▏ | 1003/3136 [00:24<00:49, 42.89it/s]
32%|███▏ | 1008/3136 [00:24<00:49, 42.83it/s]
32%|███▏ | 1013/3136 [00:24<00:49, 42.95it/s]
32%|███▏ | 1018/3136 [00:25<00:49, 43.04it/s]
33%|███▎ | 1023/3136 [00:25<00:49, 42.93it/s]
33%|███▎ | 1028/3136 [00:25<00:49, 42.95it/s]
33%|███▎ | 1033/3136 [00:25<00:48, 43.04it/s]
33%|███▎ | 1038/3136 [00:25<00:48, 43.01it/s]
33%|███▎ | 1043/3136 [00:25<00:48, 42.85it/s]
33%|███▎ | 1048/3136 [00:25<00:48, 42.83it/s]
34%|███▎ | 1053/3136 [00:25<00:48, 42.60it/s]
34%|███▎ | 1058/3136 [00:25<00:48, 42.69it/s]
34%|███▍ | 1063/3136 [00:26<00:48, 42.70it/s]
34%|███▍ | 1068/3136 [00:26<00:48, 42.63it/s]
34%|███▍ | 1073/3136 [00:26<00:48, 42.71it/s]
34%|███▍ | 1078/3136 [00:26<00:48, 42.78it/s]
35%|███▍ | 1083/3136 [00:26<00:47, 42.82it/s]
35%|███▍ | 1088/3136 [00:26<00:47, 42.93it/s]
35%|███▍ | 1093/3136 [00:26<00:47, 42.93it/s]
35%|███▌ | 1098/3136 [00:26<00:47, 42.93it/s]
35%|███▌ | 1103/3136 [00:26<00:47, 42.76it/s]
35%|███▌ | 1108/3136 [00:27<00:47, 42.73it/s]
35%|███▌ | 1113/3136 [00:27<00:47, 42.78it/s]
36%|███▌ | 1118/3136 [00:27<00:47, 42.85it/s]
36%|███▌ | 1123/3136 [00:27<00:47, 42.74it/s]
36%|███▌ | 1128/3136 [00:27<00:46, 42.85it/s]
36%|███▌ | 1133/3136 [00:27<00:46, 42.87it/s]
36%|███▋ | 1138/3136 [00:27<00:46, 42.64it/s]
36%|███▋ | 1143/3136 [00:27<00:46, 42.70it/s]
37%|███▋ | 1148/3136 [00:28<00:46, 42.70it/s]
37%|███▋ | 1153/3136 [00:28<00:46, 42.69it/s]
37%|███▋ | 1158/3136 [00:28<00:46, 42.58it/s]
37%|███▋ | 1163/3136 [00:28<00:46, 42.73it/s]
37%|███▋ | 1168/3136 [00:28<00:46, 42.73it/s]
37%|███▋ | 1173/3136 [00:28<00:45, 42.89it/s]
38%|███▊ | 1178/3136 [00:28<00:45, 42.88it/s]
38%|███▊ | 1183/3136 [00:28<00:45, 43.01it/s]
38%|███▊ | 1188/3136 [00:28<00:45, 42.81it/s]
38%|███▊ | 1193/3136 [00:29<00:45, 42.52it/s]
38%|███▊ | 1198/3136 [00:29<00:45, 42.60it/s]
38%|███▊ | 1203/3136 [00:29<00:45, 42.68it/s]
39%|███▊ | 1208/3136 [00:29<00:44, 42.86it/s]
39%|███▊ | 1213/3136 [00:29<00:44, 42.90it/s]
39%|███▉ | 1218/3136 [00:29<00:44, 42.84it/s]
39%|███▉ | 1223/3136 [00:29<00:44, 42.75it/s]
39%|███▉ | 1228/3136 [00:29<00:44, 42.80it/s]
39%|███▉ | 1233/3136 [00:30<00:44, 42.81it/s]
39%|███▉ | 1238/3136 [00:30<00:44, 42.75it/s]
40%|███▉ | 1243/3136 [00:30<00:44, 42.71it/s]
40%|███▉ | 1248/3136 [00:30<00:44, 42.63it/s]
40%|███▉ | 1253/3136 [00:30<00:44, 42.69it/s]
40%|████ | 1258/3136 [00:30<00:43, 42.81it/s]
40%|████ | 1263/3136 [00:30<00:43, 42.87it/s]
40%|████ | 1268/3136 [00:30<00:43, 42.92it/s]
41%|████ | 1273/3136 [00:30<00:43, 42.82it/s]
41%|████ | 1278/3136 [00:31<00:43, 42.83it/s]
41%|████ | 1283/3136 [00:31<00:43, 42.93it/s]
41%|████ | 1288/3136 [00:31<00:43, 42.85it/s]
41%|████ | 1293/3136 [00:31<00:42, 42.88it/s]
41%|████▏ | 1298/3136 [00:31<00:42, 42.92it/s]
42%|████▏ | 1303/3136 [00:31<00:42, 42.97it/s]
42%|████▏ | 1308/3136 [00:31<00:42, 42.93it/s]
42%|████▏ | 1313/3136 [00:31<00:42, 42.78it/s]
42%|████▏ | 1318/3136 [00:32<00:42, 42.79it/s]
42%|████▏ | 1323/3136 [00:32<00:42, 42.98it/s]
42%|████▏ | 1328/3136 [00:32<00:42, 43.03it/s]
43%|████▎ | 1333/3136 [00:32<00:41, 43.10it/s]
43%|████▎ | 1338/3136 [00:32<00:41, 43.12it/s]
43%|████▎ | 1343/3136 [00:32<00:41, 43.09it/s]
43%|████▎ | 1348/3136 [00:32<00:41, 42.96it/s]
43%|████▎ | 1353/3136 [00:32<00:41, 42.70it/s]
43%|████▎ | 1358/3136 [00:32<00:41, 42.50it/s]
43%|████▎ | 1363/3136 [00:33<00:41, 42.65it/s]
44%|████▎ | 1368/3136 [00:33<00:41, 42.82it/s]
44%|████▍ | 1373/3136 [00:33<00:41, 42.58it/s]
44%|████▍ | 1378/3136 [00:33<00:41, 42.75it/s]
44%|████▍ | 1383/3136 [00:33<00:40, 42.88it/s]
44%|████▍ | 1388/3136 [00:33<00:40, 42.97it/s]
44%|████▍ | 1393/3136 [00:33<00:40, 42.94it/s]
45%|████▍ | 1398/3136 [00:33<00:40, 42.75it/s]
45%|████▍ | 1403/3136 [00:34<00:40, 42.71it/s]
45%|████▍ | 1408/3136 [00:34<00:41, 41.87it/s]
45%|████▌ | 1413/3136 [00:34<00:41, 41.59it/s]
45%|████▌ | 1418/3136 [00:34<00:40, 41.92it/s]
45%|████▌ | 1423/3136 [00:34<00:40, 42.28it/s]
46%|████▌ | 1428/3136 [00:34<00:40, 42.48it/s]
46%|████▌ | 1433/3136 [00:34<00:39, 42.65it/s]
46%|████▌ | 1438/3136 [00:34<00:39, 42.69it/s]
46%|████▌ | 1443/3136 [00:34<00:39, 42.70it/s]
46%|████▌ | 1448/3136 [00:35<00:39, 42.77it/s]
46%|████▋ | 1453/3136 [00:35<00:39, 42.86it/s]
46%|████▋ | 1458/3136 [00:35<00:39, 42.83it/s]
47%|████▋ | 1463/3136 [00:35<00:39, 42.89it/s]
47%|████▋ | 1468/3136 [00:35<00:38, 42.94it/s]
47%|████▋ | 1473/3136 [00:35<00:38, 42.89it/s]
47%|████▋ | 1478/3136 [00:35<00:38, 42.78it/s]
47%|████▋ | 1483/3136 [00:35<00:38, 42.46it/s]
47%|████▋ | 1488/3136 [00:36<00:38, 42.50it/s]
48%|████▊ | 1493/3136 [00:36<00:38, 42.69it/s]
48%|████▊ | 1498/3136 [00:36<00:38, 42.74it/s]
48%|████▊ | 1503/3136 [00:36<00:38, 42.83it/s]
48%|████▊ | 1508/3136 [00:36<00:37, 42.98it/s]
48%|████▊ | 1513/3136 [00:36<00:37, 42.98it/s]
48%|████▊ | 1518/3136 [00:36<00:37, 43.04it/s]
49%|████▊ | 1523/3136 [00:36<00:37, 43.09it/s]
49%|████▊ | 1528/3136 [00:36<00:37, 43.10it/s]
49%|████▉ | 1533/3136 [00:37<00:37, 43.10it/s]
49%|████▉ | 1538/3136 [00:37<00:37, 43.06it/s]
49%|████▉ | 1543/3136 [00:37<00:37, 42.97it/s]
49%|████▉ | 1548/3136 [00:37<00:36, 43.06it/s]
50%|████▉ | 1553/3136 [00:37<00:36, 43.05it/s]
50%|████▉ | 1558/3136 [00:37<00:36, 43.13it/s]
50%|████▉ | 1563/3136 [00:37<00:36, 43.01it/s]
50%|█████ | 1568/3136 [00:37<00:36, 42.67it/s]
50%|█████ | 1573/3136 [00:37<00:36, 42.76it/s]
50%|█████ | 1578/3136 [00:38<00:36, 42.87it/s]
50%|█████ | 1583/3136 [00:38<00:36, 42.83it/s]
51%|█████ | 1588/3136 [00:38<00:36, 42.61it/s]
51%|█████ | 1593/3136 [00:38<00:36, 42.76it/s]
51%|█████ | 1598/3136 [00:38<00:35, 42.77it/s]
51%|█████ | 1603/3136 [00:38<00:35, 42.87it/s]
51%|█████▏ | 1608/3136 [00:38<00:35, 42.90it/s]
51%|█████▏ | 1613/3136 [00:38<00:35, 42.90it/s]
52%|█████▏ | 1618/3136 [00:39<00:35, 42.89it/s]
52%|█████▏ | 1623/3136 [00:39<00:35, 42.65it/s]
52%|█████▏ | 1628/3136 [00:39<00:35, 42.67it/s]
52%|█████▏ | 1633/3136 [00:39<00:35, 42.83it/s]
52%|█████▏ | 1638/3136 [00:39<00:35, 42.70it/s]
52%|█████▏ | 1643/3136 [00:39<00:34, 42.84it/s]
53%|█████▎ | 1648/3136 [00:39<00:35, 42.06it/s]
53%|█████▎ | 1653/3136 [00:39<00:35, 41.88it/s]
53%|█████▎ | 1658/3136 [00:39<00:35, 42.15it/s]
53%|█████▎ | 1663/3136 [00:40<00:34, 42.21it/s]
53%|█████▎ | 1668/3136 [00:40<00:34, 42.45it/s]
53%|█████▎ | 1673/3136 [00:40<00:34, 42.49it/s]
54%|█████▎ | 1678/3136 [00:40<00:34, 42.62it/s]
54%|█████▎ | 1683/3136 [00:40<00:34, 42.57it/s]
54%|█████▍ | 1688/3136 [00:40<00:33, 42.72it/s]
54%|█████▍ | 1693/3136 [00:40<00:33, 42.78it/s]
54%|█████▍ | 1698/3136 [00:40<00:33, 42.85it/s]
54%|█████▍ | 1703/3136 [00:41<00:33, 42.79it/s]
54%|█████▍ | 1708/3136 [00:41<00:33, 42.70it/s]
55%|█████▍ | 1713/3136 [00:41<00:33, 42.65it/s]
55%|█████▍ | 1718/3136 [00:41<00:33, 42.74it/s]
55%|█████▍ | 1723/3136 [00:41<00:33, 42.77it/s]
55%|█████▌ | 1728/3136 [00:41<00:32, 42.76it/s]
55%|█████▌ | 1733/3136 [00:41<00:32, 42.78it/s]
55%|█████▌ | 1738/3136 [00:41<00:32, 42.52it/s]
56%|█████▌ | 1743/3136 [00:41<00:32, 42.76it/s]
56%|█████▌ | 1748/3136 [00:42<00:32, 42.73it/s]
56%|█████▌ | 1753/3136 [00:42<00:32, 42.86it/s]
56%|█████▌ | 1758/3136 [00:42<00:32, 42.77it/s]
56%|█████▌ | 1763/3136 [00:42<00:32, 42.70it/s]
56%|█████▋ | 1768/3136 [00:42<00:31, 42.83it/s]
57%|█████▋ | 1773/3136 [00:42<00:31, 42.99it/s]
57%|█████▋ | 1778/3136 [00:42<00:31, 42.72it/s]
57%|█████▋ | 1783/3136 [00:42<00:32, 41.39it/s]
57%|█████▋ | 1788/3136 [00:43<00:32, 41.12it/s]
57%|█████▋ | 1793/3136 [00:43<00:32, 41.60it/s]
57%|█████▋ | 1798/3136 [00:43<00:31, 41.98it/s]
57%|█████▋ | 1803/3136 [00:43<00:31, 42.32it/s]
58%|█████▊ | 1808/3136 [00:43<00:31, 42.48it/s]
58%|█████▊ | 1813/3136 [00:43<00:30, 42.74it/s]
58%|█████▊ | 1818/3136 [00:43<00:30, 42.82it/s]
58%|█████▊ | 1823/3136 [00:43<00:30, 42.55it/s]
58%|█████▊ | 1828/3136 [00:43<00:30, 42.70it/s]
58%|█████▊ | 1833/3136 [00:44<00:30, 42.70it/s]
59%|█████▊ | 1838/3136 [00:44<00:30, 42.71it/s]
59%|█████▉ | 1843/3136 [00:44<00:30, 42.80it/s]
59%|█████▉ | 1848/3136 [00:44<00:30, 42.73it/s]
59%|█████▉ | 1853/3136 [00:44<00:30, 42.70it/s]
59%|█████▉ | 1858/3136 [00:44<00:29, 42.90it/s]
59%|█████▉ | 1863/3136 [00:44<00:29, 42.85it/s]
60%|█████▉ | 1868/3136 [00:44<00:29, 42.93it/s]
60%|█████▉ | 1873/3136 [00:45<00:29, 42.97it/s]
60%|█████▉ | 1878/3136 [00:45<00:29, 42.82it/s]
60%|██████ | 1883/3136 [00:45<00:29, 42.87it/s]
60%|██████ | 1888/3136 [00:45<00:29, 42.93it/s]
60%|██████ | 1893/3136 [00:45<00:28, 42.94it/s]
61%|██████ | 1898/3136 [00:45<00:28, 42.93it/s]
61%|██████ | 1903/3136 [00:45<00:28, 42.96it/s]
61%|██████ | 1908/3136 [00:45<00:28, 42.68it/s]
61%|██████ | 1913/3136 [00:45<00:28, 42.76it/s]
61%|██████ | 1918/3136 [00:46<00:28, 42.53it/s]
61%|██████▏ | 1923/3136 [00:46<00:28, 42.64it/s]
61%|██████▏ | 1928/3136 [00:46<00:28, 42.53it/s]
62%|██████▏ | 1933/3136 [00:46<00:28, 42.50it/s]
62%|██████▏ | 1938/3136 [00:46<00:28, 42.60it/s]
62%|██████▏ | 1943/3136 [00:46<00:28, 42.48it/s]
62%|██████▏ | 1948/3136 [00:46<00:27, 42.57it/s]
62%|██████▏ | 1953/3136 [00:46<00:27, 42.62it/s]
62%|██████▏ | 1958/3136 [00:47<00:27, 42.58it/s]
63%|██████▎ | 1963/3136 [00:47<00:27, 42.68it/s]
63%|██████▎ | 1968/3136 [00:47<00:27, 42.72it/s]
63%|██████▎ | 1973/3136 [00:47<00:27, 42.81it/s]
63%|██████▎ | 1978/3136 [00:47<00:26, 42.92it/s]
63%|██████▎ | 1983/3136 [00:47<00:26, 42.93it/s]
63%|██████▎ | 1988/3136 [00:47<00:26, 42.84it/s]
64%|██████▎ | 1993/3136 [00:47<00:26, 42.52it/s]
64%|██████▎ | 1998/3136 [00:47<00:26, 42.67it/s]
64%|██████▍ | 2003/3136 [00:48<00:26, 42.71it/s]
64%|██████▍ | 2008/3136 [00:48<00:26, 42.74it/s]
64%|██████▍ | 2013/3136 [00:48<00:26, 42.60it/s]
64%|██████▍ | 2018/3136 [00:48<00:26, 42.68it/s]
65%|██████▍ | 2023/3136 [00:48<00:25, 42.86it/s]
65%|██████▍ | 2028/3136 [00:48<00:25, 42.95it/s]
65%|██████▍ | 2033/3136 [00:48<00:25, 42.91it/s]
65%|██████▍ | 2038/3136 [00:48<00:25, 42.94it/s]
65%|██████▌ | 2043/3136 [00:48<00:25, 42.98it/s]
65%|██████▌ | 2048/3136 [00:49<00:25, 42.89it/s]
65%|██████▌ | 2053/3136 [00:49<00:25, 42.81it/s]
66%|██████▌ | 2058/3136 [00:49<00:25, 42.69it/s]
66%|██████▌ | 2063/3136 [00:49<00:25, 42.87it/s]
66%|██████▌ | 2068/3136 [00:49<00:24, 42.83it/s]
66%|██████▌ | 2073/3136 [00:49<00:24, 42.90it/s]
66%|██████▋ | 2078/3136 [00:49<00:24, 42.70it/s]
66%|██████▋ | 2083/3136 [00:49<00:24, 42.55it/s]
67%|██████▋ | 2088/3136 [00:50<00:24, 42.56it/s]
67%|██████▋ | 2093/3136 [00:50<00:24, 42.69it/s]
67%|██████▋ | 2098/3136 [00:50<00:24, 42.77it/s]
67%|██████▋ | 2103/3136 [00:50<00:24, 42.69it/s]
67%|██████▋ | 2108/3136 [00:50<00:24, 42.78it/s]
67%|██████▋ | 2113/3136 [00:50<00:23, 42.78it/s]
68%|██████▊ | 2118/3136 [00:50<00:23, 42.90it/s]
68%|██████▊ | 2123/3136 [00:50<00:23, 42.99it/s]
68%|██████▊ | 2128/3136 [00:50<00:23, 43.09it/s]
68%|██████▊ | 2133/3136 [00:51<00:23, 42.91it/s]
68%|██████▊ | 2138/3136 [00:51<00:23, 42.65it/s]
68%|██████▊ | 2143/3136 [00:51<00:23, 42.50it/s]
68%|██████▊ | 2148/3136 [00:51<00:23, 42.66it/s]
69%|██████▊ | 2153/3136 [00:51<00:23, 42.72it/s]
69%|██████▉ | 2158/3136 [00:51<00:22, 42.81it/s]
69%|██████▉ | 2163/3136 [00:51<00:22, 42.69it/s]
69%|██████▉ | 2168/3136 [00:51<00:22, 42.74it/s]
69%|██████▉ | 2173/3136 [00:52<00:22, 42.79it/s]
69%|██████▉ | 2178/3136 [00:52<00:22, 42.86it/s]
70%|██████▉ | 2183/3136 [00:52<00:22, 42.74it/s]
70%|██████▉ | 2188/3136 [00:52<00:22, 42.91it/s]
70%|██████▉ | 2193/3136 [00:52<00:21, 42.97it/s]
70%|███████ | 2198/3136 [00:52<00:21, 43.10it/s]
70%|███████ | 2203/3136 [00:52<00:21, 43.08it/s]
70%|███████ | 2208/3136 [00:52<00:21, 42.94it/s]
71%|███████ | 2213/3136 [00:52<00:21, 42.79it/s]
71%|███████ | 2218/3136 [00:53<00:21, 42.73it/s]
71%|███████ | 2223/3136 [00:53<00:21, 42.80it/s]
71%|███████ | 2228/3136 [00:53<00:21, 42.62it/s]
71%|███████ | 2233/3136 [00:53<00:21, 42.83it/s]
71%|███████▏ | 2238/3136 [00:53<00:20, 42.81it/s]
72%|███████▏ | 2243/3136 [00:53<00:20, 42.80it/s]
72%|███████▏ | 2248/3136 [00:53<00:20, 42.77it/s]
72%|███████▏ | 2253/3136 [00:53<00:20, 42.58it/s]
72%|███████▏ | 2258/3136 [00:54<00:20, 42.73it/s]
72%|███████▏ | 2263/3136 [00:54<00:20, 42.90it/s]
72%|███████▏ | 2268/3136 [00:54<00:20, 42.84it/s]
72%|███████▏ | 2273/3136 [00:54<00:20, 42.78it/s]
73%|███████▎ | 2278/3136 [00:54<00:20, 42.65it/s]
73%|███████▎ | 2283/3136 [00:54<00:19, 42.80it/s]
73%|███████▎ | 2288/3136 [00:54<00:19, 42.89it/s]
73%|███████▎ | 2293/3136 [00:54<00:19, 42.91it/s]
73%|███████▎ | 2298/3136 [00:54<00:19, 42.89it/s]
73%|███████▎ | 2303/3136 [00:55<00:19, 43.00it/s]
74%|███████▎ | 2308/3136 [00:55<00:19, 43.00it/s]
74%|███████▍ | 2313/3136 [00:55<00:19, 42.93it/s]
74%|███████▍ | 2318/3136 [00:55<00:19, 43.04it/s]
74%|███████▍ | 2323/3136 [00:55<00:18, 42.85it/s]
74%|███████▍ | 2328/3136 [00:55<00:18, 42.85it/s]
74%|███████▍ | 2333/3136 [00:55<00:18, 42.90it/s]
75%|███████▍ | 2338/3136 [00:55<00:18, 42.70it/s]
75%|███████▍ | 2343/3136 [00:56<00:18, 42.81it/s]
75%|███████▍ | 2348/3136 [00:56<00:18, 42.89it/s]
75%|███████▌ | 2353/3136 [00:56<00:18, 42.84it/s]
75%|███████▌ | 2358/3136 [00:56<00:18, 42.82it/s]
75%|███████▌ | 2363/3136 [00:56<00:18, 42.89it/s]
76%|███████▌ | 2368/3136 [00:56<00:17, 42.89it/s]
76%|███████▌ | 2373/3136 [00:56<00:17, 43.05it/s]
76%|███████▌ | 2378/3136 [00:56<00:17, 42.99it/s]
76%|███████▌ | 2383/3136 [00:56<00:17, 42.87it/s]
76%|███████▌ | 2388/3136 [00:57<00:17, 42.93it/s]
76%|███████▋ | 2393/3136 [00:57<00:17, 42.91it/s]
76%|███████▋ | 2398/3136 [00:57<00:17, 42.87it/s]
77%|███████▋ | 2403/3136 [00:57<00:17, 42.93it/s]
77%|███████▋ | 2408/3136 [00:57<00:16, 42.86it/s]
77%|███████▋ | 2413/3136 [00:57<00:16, 42.94it/s]
77%|███████▋ | 2418/3136 [00:57<00:16, 42.89it/s]
77%|███████▋ | 2423/3136 [00:57<00:16, 42.52it/s]
77%|███████▋ | 2428/3136 [00:57<00:16, 42.69it/s]
78%|███████▊ | 2433/3136 [00:58<00:16, 42.65it/s]
78%|███████▊ | 2438/3136 [00:58<00:16, 42.61it/s]
78%|███████▊ | 2443/3136 [00:58<00:16, 42.62it/s]
78%|███████▊ | 2448/3136 [00:58<00:16, 42.73it/s]
78%|███████▊ | 2453/3136 [00:58<00:15, 42.70it/s]
78%|███████▊ | 2458/3136 [00:58<00:15, 42.83it/s]
79%|███████▊ | 2463/3136 [00:58<00:15, 42.89it/s]
79%|███████▊ | 2468/3136 [00:58<00:15, 42.95it/s]
79%|███████▉ | 2473/3136 [00:59<00:15, 42.98it/s]
79%|███████▉ | 2478/3136 [00:59<00:15, 42.86it/s]
79%|███████▉ | 2483/3136 [00:59<00:15, 42.68it/s]
79%|███████▉ | 2488/3136 [00:59<00:15, 42.65it/s]
79%|███████▉ | 2493/3136 [00:59<00:15, 42.78it/s]
80%|███████▉ | 2498/3136 [00:59<00:14, 42.89it/s]
80%|███████▉ | 2503/3136 [00:59<00:14, 42.75it/s]
80%|███████▉ | 2508/3136 [00:59<00:14, 42.46it/s]
80%|████████ | 2513/3136 [00:59<00:14, 42.67it/s]
80%|████████ | 2518/3136 [01:00<00:14, 42.70it/s]
80%|████████ | 2523/3136 [01:00<00:14, 42.72it/s]
81%|████████ | 2528/3136 [01:00<00:14, 42.66it/s]
81%|████████ | 2533/3136 [01:00<00:14, 42.62it/s]
81%|████████ | 2538/3136 [01:00<00:13, 42.79it/s]
81%|████████ | 2543/3136 [01:00<00:13, 42.94it/s]
81%|████████▏ | 2548/3136 [01:00<00:13, 42.92it/s]
81%|████████▏ | 2553/3136 [01:00<00:13, 42.98it/s]
82%|████████▏ | 2558/3136 [01:01<00:13, 42.98it/s]
82%|████████▏ | 2563/3136 [01:01<00:13, 42.79it/s]
82%|████████▏ | 2568/3136 [01:01<00:13, 42.83it/s]
82%|████████▏ | 2573/3136 [01:01<00:13, 42.83it/s]
82%|████████▏ | 2578/3136 [01:01<00:13, 42.77it/s]
82%|████████▏ | 2583/3136 [01:01<00:12, 42.91it/s]
83%|████████▎ | 2588/3136 [01:01<00:12, 42.91it/s]
83%|████████▎ | 2593/3136 [01:01<00:12, 42.65it/s]
83%|████████▎ | 2598/3136 [01:01<00:12, 42.78it/s]
83%|████████▎ | 2603/3136 [01:02<00:12, 42.74it/s]
83%|████████▎ | 2608/3136 [01:02<00:12, 42.82it/s]
83%|████████▎ | 2613/3136 [01:02<00:12, 42.79it/s]
83%|████████▎ | 2618/3136 [01:02<00:12, 42.82it/s]
84%|████████▎ | 2623/3136 [01:02<00:11, 42.90it/s]
84%|████████▍ | 2628/3136 [01:02<00:11, 42.98it/s]
84%|████████▍ | 2633/3136 [01:02<00:11, 42.85it/s]
84%|████████▍ | 2638/3136 [01:02<00:11, 42.70it/s]
84%|████████▍ | 2643/3136 [01:03<00:11, 42.55it/s]
84%|████████▍ | 2648/3136 [01:03<00:11, 42.57it/s]
85%|████████▍ | 2653/3136 [01:03<00:11, 42.64it/s]
85%|████████▍ | 2658/3136 [01:03<00:11, 42.52it/s]
85%|████████▍ | 2663/3136 [01:03<00:11, 42.41it/s]
85%|████████▌ | 2668/3136 [01:03<00:10, 42.62it/s]
85%|████████▌ | 2673/3136 [01:03<00:10, 42.66it/s]
85%|████████▌ | 2678/3136 [01:03<00:10, 42.57it/s]
86%|████████▌ | 2683/3136 [01:03<00:10, 42.72it/s]
86%|████████▌ | 2688/3136 [01:04<00:10, 42.40it/s]
86%|████████▌ | 2693/3136 [01:04<00:10, 42.65it/s]
86%|████████▌ | 2698/3136 [01:04<00:10, 42.55it/s]
86%|████████▌ | 2703/3136 [01:04<00:10, 42.77it/s]
86%|████████▋ | 2708/3136 [01:04<00:09, 42.99it/s]
87%|████████▋ | 2713/3136 [01:04<00:09, 43.02it/s]
87%|████████▋ | 2718/3136 [01:04<00:09, 43.01it/s]
87%|████████▋ | 2723/3136 [01:04<00:09, 42.99it/s]
87%|████████▋ | 2728/3136 [01:05<00:09, 42.98it/s]
87%|████████▋ | 2733/3136 [01:05<00:09, 43.03it/s]
87%|████████▋ | 2738/3136 [01:05<00:09, 43.01it/s]
87%|████████▋ | 2743/3136 [01:05<00:09, 42.93it/s]
88%|████████▊ | 2748/3136 [01:05<00:09, 43.10it/s]
88%|████████▊ | 2753/3136 [01:05<00:08, 43.15it/s]
88%|████████▊ | 2758/3136 [01:05<00:08, 42.92it/s]
88%|████████▊ | 2763/3136 [01:05<00:08, 42.74it/s]
88%|████████▊ | 2768/3136 [01:05<00:08, 42.74it/s]
88%|████████▊ | 2773/3136 [01:06<00:08, 42.81it/s]
89%|████████▊ | 2778/3136 [01:06<00:08, 42.95it/s]
89%|████████▊ | 2783/3136 [01:06<00:08, 42.88it/s]
89%|████████▉ | 2788/3136 [01:06<00:08, 42.99it/s]
89%|████████▉ | 2793/3136 [01:06<00:07, 43.11it/s]
89%|████████▉ | 2798/3136 [01:06<00:07, 42.98it/s]
89%|████████▉ | 2803/3136 [01:06<00:07, 43.10it/s]
90%|████████▉ | 2808/3136 [01:06<00:07, 43.14it/s]
90%|████████▉ | 2813/3136 [01:06<00:07, 43.04it/s]
90%|████████▉ | 2818/3136 [01:07<00:07, 43.07it/s]
90%|█████████ | 2823/3136 [01:07<00:07, 43.06it/s]
90%|█████████ | 2828/3136 [01:07<00:07, 42.83it/s]
90%|█████████ | 2833/3136 [01:07<00:07, 42.95it/s]
90%|█████████ | 2838/3136 [01:07<00:06, 42.79it/s]
91%|█████████ | 2843/3136 [01:07<00:06, 42.83it/s]
91%|█████████ | 2848/3136 [01:07<00:06, 42.77it/s]
91%|█████████ | 2853/3136 [01:07<00:06, 42.52it/s]
91%|█████████ | 2858/3136 [01:08<00:06, 42.59it/s]
91%|█████████▏| 2863/3136 [01:08<00:06, 42.71it/s]
91%|█████████▏| 2868/3136 [01:08<00:06, 42.59it/s]
92%|█████████▏| 2873/3136 [01:08<00:06, 42.70it/s]
92%|█████████▏| 2878/3136 [01:08<00:06, 42.71it/s]
92%|█████████▏| 2883/3136 [01:08<00:05, 42.81it/s]
92%|█████████▏| 2888/3136 [01:08<00:05, 42.91it/s]
92%|█████████▏| 2893/3136 [01:08<00:05, 42.94it/s]
92%|█████████▏| 2898/3136 [01:08<00:05, 42.95it/s]
93%|█████████▎| 2903/3136 [01:09<00:05, 43.06it/s]
93%|█████████▎| 2908/3136 [01:09<00:05, 42.69it/s]
93%|█████████▎| 2913/3136 [01:09<00:05, 42.52it/s]
93%|█████████▎| 2918/3136 [01:09<00:05, 42.72it/s]
93%|█████████▎| 2923/3136 [01:09<00:04, 42.63it/s]
93%|█████████▎| 2928/3136 [01:09<00:04, 42.80it/s]
94%|█████████▎| 2933/3136 [01:09<00:04, 41.89it/s]
94%|█████████▎| 2938/3136 [01:09<00:04, 42.06it/s]
94%|█████████▍| 2943/3136 [01:10<00:04, 42.34it/s]
94%|█████████▍| 2948/3136 [01:10<00:04, 42.57it/s]
94%|█████████▍| 2953/3136 [01:10<00:04, 42.50it/s]
94%|█████████▍| 2958/3136 [01:10<00:04, 42.59it/s]
94%|█████████▍| 2963/3136 [01:10<00:04, 42.50it/s]
95%|█████████▍| 2968/3136 [01:10<00:03, 42.58it/s]
95%|█████████▍| 2973/3136 [01:10<00:03, 42.76it/s]
95%|█████████▍| 2978/3136 [01:10<00:03, 42.77it/s]
95%|█████████▌| 2983/3136 [01:10<00:03, 42.85it/s]
95%|█████████▌| 2988/3136 [01:11<00:03, 42.96it/s]
95%|█████████▌| 2993/3136 [01:11<00:03, 42.72it/s]
96%|█████████▌| 2998/3136 [01:11<00:03, 42.77it/s]
96%|█████████▌| 3003/3136 [01:11<00:03, 42.89it/s]
96%|█████████▌| 3008/3136 [01:11<00:02, 42.85it/s]
96%|█████████▌| 3013/3136 [01:11<00:02, 42.98it/s]
96%|█████████▌| 3018/3136 [01:11<00:02, 42.90it/s]
96%|█████████▋| 3023/3136 [01:11<00:02, 42.78it/s]
97%|█████████▋| 3028/3136 [01:12<00:02, 43.08it/s]
97%|█████████▋| 3033/3136 [01:12<00:02, 42.96it/s]
97%|█████████▋| 3038/3136 [01:12<00:02, 42.95it/s]
97%|█████████▋| 3043/3136 [01:12<00:02, 43.07it/s]
97%|█████████▋| 3048/3136 [01:12<00:02, 42.82it/s]
97%|█████████▋| 3053/3136 [01:12<00:01, 42.76it/s]
98%|█████████▊| 3058/3136 [01:12<00:01, 42.87it/s]
98%|█████████▊| 3063/3136 [01:12<00:01, 42.94it/s]
98%|█████████▊| 3068/3136 [01:12<00:01, 42.50it/s]
98%|█████████▊| 3073/3136 [01:13<00:01, 42.66it/s]
98%|█████████▊| 3078/3136 [01:13<00:01, 42.70it/s]
98%|█████████▊| 3083/3136 [01:13<00:01, 42.80it/s]
98%|█████████▊| 3088/3136 [01:13<00:01, 42.99it/s]
99%|█████████▊| 3093/3136 [01:13<00:01, 42.88it/s]
99%|█████████▉| 3098/3136 [01:13<00:00, 42.98it/s]
99%|█████████▉| 3103/3136 [01:13<00:00, 42.73it/s]
99%|█████████▉| 3108/3136 [01:13<00:00, 42.51it/s]
99%|█████████▉| 3113/3136 [01:13<00:00, 42.80it/s]
99%|█████████▉| 3118/3136 [01:14<00:00, 42.75it/s]
100%|█████████▉| 3123/3136 [01:14<00:00, 42.58it/s]
100%|█████████▉| 3128/3136 [01:14<00:00, 42.65it/s]
100%|█████████▉| 3133/3136 [01:14<00:00, 42.67it/s]
100%|██████████| 3136/3136 [01:14<00:00, 42.07it/s]
dem_reconst.isel(band=0).plot.imshow(cmap="terrain")

That certainly looks like the original DEM. Let’s try plotting the error in the reconstruction.
err = (dem_reconst - dem)
err.isel(band=0).plot.imshow()
plt.show()

err.plot.hist()
plt.show()

Not bad!
Reconstruction 2: Getting the latent dimension¶
A common application of autoencoders is to use the latent dimension for some application. Let’s turn our autoencoder’s predictions into a data cube. To do so we will modify the batch generator to not have overlapping windows. We also have to slightly clip the size of the input DEM. This is because we are effectively downscaling the spatial axes by a factor of 32. Since 3600 / 32
is not an integer, predict_on_array
will not know how to rescale the array size. So, we have to clip the DEM to the nearest integer multiple of 32. In this case the nearest multiple is 3584, which we achieve by clipping 8 pixels from each side.
bgen_no_overlap = xbatcher.BatchGenerator(
dem.isel(x=slice(8, -8), y=slice(8, -8)),
input_dims=dict(x=32, y=32),
input_overlap=dict(x=0, y=0)
)
ds_no_overlap = MapDataset(
X_generator=bgen_no_overlap
)
loader = torch.utils.data.DataLoader(ds_no_overlap, batch_size=16, shuffle=True)
ex_input = next(iter(loader))
# Same as before
print("Input shape:", ex_input.shape)
Input shape: torch.Size([16, 1, 32, 32])
Next we will write a function that the calls the encoder arm of the autoencoder and adds a fake x and y dimension.
def infer_with_encoder(x):
return m.encoder(x)[:, None, None, :]
ex_output = infer_with_encoder(ex_input)
print("Output shape:", ex_output.shape)
Output shape: torch.Size([16, 1, 1, 64])
To be clear, we started with the usual x/y/band tensor from the input dataset.
...and end up with a tensor that has singleton x/y dimensions and a new, 64-element channel dimension.
We can go through the same process as before to see how we put together the predict_on_array
call. Both the x
and y
dimensions change size and are used by the batch generator, so they go in resample_dims
. The remaining dimension, channel
, is a new dimension and goes in the new_dim
list.
latent_dim_cube = functions.predict_on_array(
dataset=ds_no_overlap,
model=infer_with_encoder,
output_tensor_dim=dict(y=1, x=1, channel=64),
new_dim=["channel"],
core_dim=[],
resample_dim=["x", "y"]
)
0%| | 0/784 [00:00<?, ?it/s]
1%| | 7/784 [00:00<00:12, 64.63it/s]
2%|▏ | 14/784 [00:00<00:11, 64.88it/s]
3%|▎ | 21/784 [00:00<00:11, 64.77it/s]
4%|▎ | 28/784 [00:00<00:11, 65.09it/s]
4%|▍ | 35/784 [00:00<00:11, 65.17it/s]
5%|▌ | 42/784 [00:00<00:11, 65.10it/s]
6%|▋ | 49/784 [00:00<00:11, 64.85it/s]
7%|▋ | 56/784 [00:00<00:11, 64.37it/s]
8%|▊ | 63/784 [00:00<00:11, 64.40it/s]
9%|▉ | 70/784 [00:01<00:11, 64.53it/s]
10%|▉ | 77/784 [00:01<00:10, 64.49it/s]
11%|█ | 84/784 [00:01<00:10, 64.08it/s]
12%|█▏ | 91/784 [00:01<00:10, 64.23it/s]
12%|█▎ | 98/784 [00:01<00:10, 64.44it/s]
13%|█▎ | 105/784 [00:01<00:10, 64.63it/s]
14%|█▍ | 112/784 [00:01<00:10, 64.69it/s]
15%|█▌ | 119/784 [00:01<00:10, 64.58it/s]
16%|█▌ | 126/784 [00:01<00:10, 64.52it/s]
17%|█▋ | 133/784 [00:02<00:10, 64.67it/s]
18%|█▊ | 140/784 [00:02<00:09, 64.51it/s]
19%|█▉ | 147/784 [00:02<00:09, 64.68it/s]
20%|█▉ | 154/784 [00:02<00:09, 64.54it/s]
21%|██ | 161/784 [00:02<00:09, 64.80it/s]
21%|██▏ | 168/784 [00:02<00:09, 64.90it/s]
22%|██▏ | 175/784 [00:02<00:09, 64.72it/s]
23%|██▎ | 182/784 [00:02<00:09, 64.26it/s]
24%|██▍ | 189/784 [00:02<00:09, 63.94it/s]
25%|██▌ | 196/784 [00:03<00:09, 63.98it/s]
26%|██▌ | 203/784 [00:03<00:09, 64.28it/s]
27%|██▋ | 210/784 [00:03<00:08, 64.46it/s]
28%|██▊ | 217/784 [00:03<00:08, 64.22it/s]
29%|██▊ | 224/784 [00:03<00:08, 64.53it/s]
29%|██▉ | 231/784 [00:03<00:08, 64.79it/s]
30%|███ | 238/784 [00:03<00:08, 64.95it/s]
31%|███▏ | 245/784 [00:03<00:08, 64.96it/s]
32%|███▏ | 252/784 [00:03<00:08, 64.94it/s]
33%|███▎ | 259/784 [00:04<00:08, 64.84it/s]
34%|███▍ | 266/784 [00:04<00:07, 64.89it/s]
35%|███▍ | 273/784 [00:04<00:07, 64.61it/s]
36%|███▌ | 280/784 [00:04<00:07, 64.57it/s]
37%|███▋ | 287/784 [00:04<00:07, 64.55it/s]
38%|███▊ | 294/784 [00:04<00:07, 64.52it/s]
38%|███▊ | 301/784 [00:04<00:07, 64.74it/s]
39%|███▉ | 308/784 [00:04<00:07, 64.71it/s]
40%|████ | 315/784 [00:04<00:07, 64.31it/s]
41%|████ | 322/784 [00:04<00:07, 63.98it/s]
42%|████▏ | 329/784 [00:05<00:07, 64.23it/s]
43%|████▎ | 336/784 [00:05<00:06, 64.35it/s]
44%|████▍ | 343/784 [00:05<00:06, 64.43it/s]
45%|████▍ | 350/784 [00:05<00:06, 64.47it/s]
46%|████▌ | 357/784 [00:05<00:06, 64.46it/s]
46%|████▋ | 364/784 [00:05<00:06, 64.31it/s]
47%|████▋ | 371/784 [00:05<00:06, 64.23it/s]
48%|████▊ | 378/784 [00:05<00:06, 64.33it/s]
49%|████▉ | 385/784 [00:05<00:06, 63.84it/s]
50%|█████ | 392/784 [00:06<00:06, 63.78it/s]
51%|█████ | 399/784 [00:06<00:06, 64.14it/s]
52%|█████▏ | 406/784 [00:06<00:05, 64.36it/s]
53%|█████▎ | 413/784 [00:06<00:05, 64.72it/s]
54%|█████▎ | 420/784 [00:06<00:05, 64.36it/s]
54%|█████▍ | 427/784 [00:06<00:05, 64.64it/s]
55%|█████▌ | 434/784 [00:06<00:05, 64.67it/s]
56%|█████▋ | 441/784 [00:06<00:05, 64.12it/s]
57%|█████▋ | 448/784 [00:06<00:05, 64.37it/s]
58%|█████▊ | 455/784 [00:07<00:05, 64.33it/s]
59%|█████▉ | 462/784 [00:07<00:04, 64.63it/s]
60%|█████▉ | 469/784 [00:07<00:04, 64.94it/s]
61%|██████ | 476/784 [00:07<00:04, 63.58it/s]
62%|██████▏ | 483/784 [00:07<00:04, 63.98it/s]
62%|██████▎ | 490/784 [00:07<00:04, 64.09it/s]
63%|██████▎ | 497/784 [00:07<00:04, 64.35it/s]
64%|██████▍ | 504/784 [00:07<00:04, 64.50it/s]
65%|██████▌ | 511/784 [00:07<00:04, 64.64it/s]
66%|██████▌ | 518/784 [00:08<00:04, 64.65it/s]
67%|██████▋ | 525/784 [00:08<00:03, 64.82it/s]
68%|██████▊ | 532/784 [00:08<00:03, 64.80it/s]
69%|██████▉ | 539/784 [00:08<00:03, 64.51it/s]
70%|██████▉ | 546/784 [00:08<00:03, 64.38it/s]
71%|███████ | 553/784 [00:08<00:03, 64.40it/s]
71%|███████▏ | 560/784 [00:08<00:03, 64.37it/s]
72%|███████▏ | 567/784 [00:08<00:03, 64.17it/s]
73%|███████▎ | 574/784 [00:08<00:03, 64.18it/s]
74%|███████▍ | 581/784 [00:09<00:03, 64.33it/s]
75%|███████▌ | 588/784 [00:09<00:03, 64.39it/s]
76%|███████▌ | 595/784 [00:09<00:02, 64.33it/s]
77%|███████▋ | 602/784 [00:09<00:02, 64.48it/s]
78%|███████▊ | 609/784 [00:09<00:02, 64.60it/s]
79%|███████▊ | 616/784 [00:09<00:02, 64.63it/s]
79%|███████▉ | 623/784 [00:09<00:02, 64.78it/s]
80%|████████ | 630/784 [00:09<00:02, 64.85it/s]
81%|████████▏ | 637/784 [00:09<00:02, 64.65it/s]
82%|████████▏ | 644/784 [00:09<00:02, 64.35it/s]
83%|████████▎ | 651/784 [00:10<00:02, 64.50it/s]
84%|████████▍ | 658/784 [00:10<00:01, 64.69it/s]
85%|████████▍ | 665/784 [00:10<00:01, 64.45it/s]
86%|████████▌ | 672/784 [00:10<00:01, 64.30it/s]
87%|████████▋ | 679/784 [00:10<00:01, 64.23it/s]
88%|████████▊ | 686/784 [00:10<00:01, 64.31it/s]
88%|████████▊ | 693/784 [00:10<00:01, 64.48it/s]
89%|████████▉ | 700/784 [00:10<00:01, 64.13it/s]
90%|█████████ | 707/784 [00:10<00:01, 64.37it/s]
91%|█████████ | 714/784 [00:11<00:01, 64.16it/s]
92%|█████████▏| 721/784 [00:11<00:00, 64.35it/s]
93%|█████████▎| 728/784 [00:11<00:00, 64.15it/s]
94%|█████████▍| 735/784 [00:11<00:00, 64.16it/s]
95%|█████████▍| 742/784 [00:11<00:00, 64.03it/s]
96%|█████████▌| 749/784 [00:11<00:00, 64.03it/s]
96%|█████████▋| 756/784 [00:11<00:00, 64.31it/s]
97%|█████████▋| 763/784 [00:11<00:00, 64.42it/s]
98%|█████████▊| 770/784 [00:11<00:00, 64.51it/s]
99%|█████████▉| 777/784 [00:12<00:00, 64.38it/s]
100%|██████████| 784/784 [00:12<00:00, 63.96it/s]
100%|██████████| 784/784 [00:12<00:00, 64.44it/s]
latent_dim_cube
Note that despite substantially re-arranging the input DataArray
, we have retained the coordinate information at a resampled resolution.
If we simply sum the output over the channel dimension, we see that the encoder clearly distinguishes between upland and lowland areas.
latent_dim_cube.sum(dim="channel").plot.imshow()

As a final demonstration of this workflow, let’s compute the cosine similarity of each of the below pixels with the latent encoding of Mt. Olympus.
olympus = dict(x=-123.7066, y=47.7998)
olympus_latent = latent_dim_cube.sel(**olympus, method="nearest")
olympus_latent
def numpy_cosine_similarity(x, y):
return np.dot(x, y)/(norm(x)*norm(y))
olympus_similarity = xr.apply_ufunc(
numpy_cosine_similarity,
latent_dim_cube,
input_core_dims = [["channel"]],
output_core_dims = [[]],
vectorize=True,
kwargs=dict(y=olympus_latent.data)
)
olympus_similarity.plot.imshow()
plt.scatter(olympus["x"], olympus["y"], marker="*", c="purple", edgecolor="black", s=200)
plt.title("Cosine similarity with Mt. Olympus, WA")
plt.show()

Similarly, we can identify foothills with similar topography to Grisdale, WA.
grisdale = dict(y=47.356625678465925, x=-123.61183314426664)
grisdale_latent = latent_dim_cube.sel(**grisdale, method="nearest")
grisdale_similarity = xr.apply_ufunc(
numpy_cosine_similarity,
latent_dim_cube,
input_core_dims = [["channel"]],
output_core_dims = [[]],
vectorize=True,
kwargs=dict(y=grisdale_latent.data)
)
grisdale_similarity.plot.imshow()
plt.scatter(grisdale["x"], grisdale["y"], marker="*", c="purple", edgecolor="black", s=200)
plt.show()

This result is admittedly very similar to if we had just selected elevation bands :)
Summary¶
Our goal with this notebook has been to show how xbatcher supports linking xarray
objects with deep learning models, and with converting model output back into labeled xarray
objects. We have demonstrated two examples of reconstructing model output, both when tensor shape changes and when it does not.
If you encounter any issues, please open an issue on the GitHub repository for this cookbook. Other feedback is welcome!