<jupyter_start><jupyter_text>Latent space explorationIn this notebook we will analyze latent features obtained with the VAE+PCA model. Required imports<jupyter_code>import sys
sys.path.append('..')
import numpy as np
import pandas as pd
import torch
import matplotlib
import matplotlib.pyplot as plt
from tqdm import tqdm
import pickle as pk

from helio.helio import FilesIndex, HelioBatch, BatchSampler
from helio.helio.models import VAE

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')<jupyter_output><empty_output><jupyter_text>Loading the VAE model<jupyter_code>net = VAE(in_channels=1,
    filters_enc=[16, 24, 32, 64],
    filters_dec=[48, 32, 24, 16],
    z_dim=16,
    output=dict(layout='c', filters=1),
    norm=True,
    variational=True).to(device)
net.load_state_dict(torch.load('model_dumps/vae'))<jupyter_output><empty_output><jupyter_text>Loading the PCA model<jupyter_code>pca = pk.load(open("model_dumps/pca.pkl",'rb'))<jupyter_output><empty_output><jupyter_text>Let's prepare a function that will decode images for various values of latent parameters. Specifically, we select two latent parameters (`i` and `j`) and vary them in ranges`i_range` and `j_range` correspondingly. All other latent parameters are set to zero. The function applies the inverse PCA transformation, then the VAE decoder and returns the decoded images:<jupyter_code>def make_samples(i, j, i_range, j_range, net, pca):
    net = net.eval()
    with torch.no_grad():
        z = np.zeros((len(i_range), len(j_range), pca.components_.shape[0]))
        z[:, :, [i-1, j-1]] = np.array(np.meshgrid(i_range, j_range)).T
        z = z.reshape(-1, z.shape[-1])
        t = pca.inverse_transform(z)
        inp = torch.tensor(t).to(device).float()
        inp = inp.reshape(t.shape[0], 16, 16, 16)
        y = net.decode(inp)
    return y.detach().cpu().numpy()<jupyter_output><empty_output><jupyter_text>Z1 and Z2 Consider the latent parameters Z1 and Z2 and let's vary Z1 between -20 and 20, while Z2 is varied between -5 to 5. Plot the decoded images:<jupyter_code>rows = 5
cols = 5

i = 1
j = 2
i_range = np.linspace(-20, 20, rows)
j_range = np.linspace(-5, 5, cols)
samples = make_samples(i, j, i_range, j_range, net, pca)

fig, ax = plt.subplots(rows, cols, figsize=(5*rows, 5*cols))
fig.patch.set_facecolor('xkcd:white')
for r in range(rows):
    for c in range(cols):
        ax[r, c].imshow(1-samples[r*cols+c, 0], vmin=0, vmax=1)
        ax[r, c].axis(False)
        ax[r, c].text(15, 30,
                      r'$Z_{}={}, Z_{}={}$'.format(i, i_range[r], j, j_range[c]),
                      size=18)
plt.subplots_adjust(wspace=0.05, hspace=0.05)
plt.show()<jupyter_output><empty_output><jupyter_text>We find that Z1 defines the unipolar or bipolar structure, while Z2 defines the size of the sunspot group.  Z1 and Z5 Now we consider the latent parameters Z1 and Z5 and plot the decoded images:<jupyter_code>i = 1
j = 5
i_range = np.linspace(-20, 20, rows)
j_range = np.linspace(-5, 5, cols)
samples = make_samples(i, j, i_range, j_range, net, pca)

fig, ax = plt.subplots(rows, cols, figsize=(5*rows, 5*cols))
fig.patch.set_facecolor('xkcd:white')
for r in range(rows):
    for c in range(cols):
        ax[r, c].imshow(1-samples[r*cols+c, 0], vmin=0, vmax=1)
        ax[r, c].axis(False)
        ax[r, c].text(15, 30,
                      r'$Z_{}={}, Z_{}={}$'.format(i, i_range[r], j, j_range[c]),
                      size=18)
plt.subplots_adjust(wspace=0.05, hspace=0.05)
plt.show()<jupyter_output><empty_output><jupyter_text>One can observe that Z5 is responsible for the tilt angle of the group.  Z1 and Z9 One more interesting observation  is related to  the latent parameter Z9:<jupyter_code>i = 1
j = 9
i_range = np.linspace(-20, 20, rows)
j_range = np.linspace(-5, 5, cols)
samples = make_samples(i, j, i_range, j_range, net, pca)

fig, ax = plt.subplots(rows, cols, figsize=(5*rows, 5*cols))
fig.patch.set_facecolor('xkcd:white')
for r in range(rows):
    for c in range(cols):
        ax[r, c].imshow(1-samples[r*cols+c, 0], vmin=0, vmax=1)
        ax[r, c].axis(False)
        ax[r, c].text(15, 30,
                      r'$Z_{}={}, Z_{}={}$'.format(i, i_range[r], j, j_range[c]),
                      size=18)
plt.subplots_adjust(wspace=0.05, hspace=0.05)
plt.show()<jupyter_output><empty_output><jupyter_text>One can conclude that Z9 defines a ratio between two spots in the bipolar group.  Physical propertiesNow we consider latent vectors of sunspot groups and relate them with physical properties of sunspot group. First we load sunspot group properties:<jupyter_code>dfp = pd.read_csv('../dataset/sunspot_group_properties.csv').set_index('fname')
dfp.head()<jupyter_output><empty_output><jupyter_text>Note the full list of properties:<jupyter_code>dfp.columns<jupyter_output><empty_output><jupyter_text>Then we load latent vectors:<jupyter_code>dfv = pd.read_csv('../latent_vectors/latent_vectors.csv').set_index('fname')
dfv.head()<jupyter_output><empty_output><jupyter_text>Merge both tables:<jupyter_code>df = dfp.join(dfv)
df.head()<jupyter_output><empty_output><jupyter_text>Now let's plot a distribution of Z1 over single-spot and multi-spot groups:<jupyter_code>fig, ax = plt.subplots(1, figsize=(7, 5))
fig.patch.set_facecolor('xkcd:white')

mask = (df.nspots == 1) & (df.ncores >= 1)
plt.hist(df.loc[mask, '0'], bins=30, color='tab:blue', label='Single-spot groups')

mask = (dfp.nspots >= 2) & (dfp.ncores >= 2)
plt.hist(df.loc[mask, '0'], bins=np.linspace(-5, 12, 25),
         color='tab:orange', alpha=0.8, label='Multi-spot groups')

plt.ylabel('Counts', fontsize=16)
plt.xlabel(r'$Z_1$', fontsize=16)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.legend(fontsize=16)
plt.show()<jupyter_output><empty_output><jupyter_text>The above plot confirms the interpretation of Z1. As we can see, Z1 separates single-spot and multi-spot groups. In the next plot we demonstrate the distribution of sunspot group area in the space of latent variables Z1 and Z2:<jupyter_code>fig, ax = plt.subplots(1, figsize=(8, 6))
fig.patch.set_facecolor('xkcd:white')

plt.scatter(*df[['0', '1']].values.T, s=4,
            cmap='jet',
            c=np.clip(df.area, 0, 1000),
            norm=matplotlib.colors.LogNorm())

plt.ylabel(r'$Z_2$', fontsize=16)
plt.xlabel(r'$Z_1$', fontsize=16)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
cbar = plt.colorbar()
cbar.ax.set_ylabel(r'Area (MSH)', rotation=90, fontsize=16)
cbar.ax.tick_params(labelsize=14)
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14727495407225966, 'fraction_numerical': 0.030006123698714023, 'mean_word_length': 4.569479965899403, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 56, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '229165', 'license': 'MIT', 'n_tokens_mistral': 2617, 'n_tokens_neox': 2455, 'n_words': 590, 'path': 'notebooks/2.Latent_space_exploration.ipynb', 'repo_name': 'observethesun/sunspot_groups', 'size': '6532'}