def display(self, style): """Display mode. This subcommand displays a plot of the latent space. Args: style (str): Name of the style. """ self.stl = Style(style, mc_version='1.15.2') vae = self.stl.models.vae vae_data = Tile.vectorize_all(self.stl.info['mc_version']) encodings = vae.encoder.predict(vae_data)[0] tiles = [ Tile('minecraft:quartz_stairs[half=bottom]', version='1.15.2'), Tile('minecraft:birch_stairs[half=bottom]', version='1.15.2'), Tile('minecraft:brick_stairs[half=bottom]', version='1.15.2'), Tile('minecraft:bricks', version='1.15.2'), Tile('minecraft:nether_bricks', version='1.15.2'), Tile('minecraft:white_carpet', version='1.15.2'), Tile('minecraft:snow[layers=1]', version='1.15.2') ] vae_data = vectorize(tiles, pad_to=vae.input_dim) encodings_subset = vae.encoder.predict(vae_data)[0] import matplotlib.pyplot as plt fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(encodings[:, 0], encodings[:, 1], c=[[.9, .9, .9]], marker='x') ax.scatter(encodings_subset[:, 0], encodings_subset[:, 1], color='r', marker='x') for idx, t in enumerate(tiles): ax.annotate(t.name, (encodings_subset[idx, 0], encodings_subset[idx, 1])) ax.set_title('Minecraft tile-ok 2D látenstere') plt.show()
def train(self, vae=False, generator=False, schem_pth: PathLike = None, batch_size=128, epochs=100): """Training a style for a specific tilemap. Training takes place in two steps. First, a variation autoencoder is trained to encode all existing blockstates of a given Minecraft version. Tiles are passed to the model as vectors, and a low-dimensional representation of them is obtained during training. Because the vectors are created based on the properties of the three-dimensional models of the tile, the visually similar blocks in the latent-space are close to each other. Tiles and their associated codes are saved in a dictionary. We then teach a convolutional upscaling network (generator) that generates random noise tensors into the latent-space of the variational autoencoder, thus generating tilemaps. The size of the input noise tensor determines its output, the ratio of the two is 1: 8. During training, the size of the input is varied at random, for which the RandomNoise class is responsible. The generator grid is taught to minimize a so-called 'feature-loss'. Args: vae (bool, optional): Training the VAE part of the model. generator (bool, optional): Training the generator part of the model. schem_pth (os.PathLike, optional): Path to the schematic of the example tilemap. batch_size (int, optional): Batch size. epochs (int, optional): Number of epochs. """ # Importing Tensorflow libraries import tensorflow as tf from tensorflow.keras.callbacks import Callback tf.compat.v1.disable_eager_execution() # Importing the the implementation of the different parts of the model from creAI.ml.models import VAE, GeneratorNetwork from creAI.ml.data_generators import RandomNoise from creAI.ml.train import init_generator, init_vae, train_generator, train_vae # Defining a custom callback function that saves the whole style at # the end of each epoch. class StyleTrainingCallback(Callback): def __init__(self, style: Style): self.style = style def on_epoch_end(self, epoch, logs=None): self.style.save() try: mc_version = self.info['mc_version'] except: raise UndefinedStyleMinecraftVersion(self.name) # Training VAE if vae: print('Loading training data...') # Vectorizing all tiles from the given Minecraft version vae_data = Tile.vectorize_all(mc_version) # Init VAE self.models.vae = init_vae(vae_data.shape[-1], 2, self.models.vae) # Train train_vae( self.models.vae, vae_data, batch_size=batch_size, epochs=epochs, callbacks=[StyleTrainingCallback(self)] ) # Training the generator if generator: if self.models.vae is None: raise VAEModelMissing(self.name) if not exists(schem_pth): raise SchematicFileMissing(schem_pth) # Opening example tilemap with open(schem_pth, 'rb') as schem_file: tlmp = Schematic.load(schem_file, version=mc_version) # Encoding the tilemap's palette with the previously trained VAE encoded_palette = self.models.vae.encoder.predict( tlmp.palette_to_vecs(pad_to=self.models.vae.input_dim) )[0] # Saving encoding for tile, z in zip(tlmp.palette, encoded_palette): self.palette[str(tile)] = z.tolist() # Mapping codes to the data array of the tilemap, thus creating a # 4D tensor. mapped = np.array([encoded_palette[idx] for idx in list(tlmp.data.flat)]) encoded_tlmp = mapped.reshape( tlmp.shape+(self.models.vae.latent_dim,)) # Init generator self.models.generator = init_generator( encoded_tlmp, 256, self.models.vae.latent_dim, self.models.generator) # Creating random training data and validation data. data_generator = RandomNoise( 10000, channels=self.models.generator.input_channels, batch_size=batch_size, min_shape=[2,2,2], max_shape=[3,3,3], seed=0) validation_data_generator = RandomNoise( 100, channels=self.models.generator.input_channels, batch_size=batch_size, min_shape=[2,2,2], max_shape=[3,3,3], seed=12345) # Training self.models.generator.model.fit( data_generator, epochs=epochs, validation_data=validation_data_generator, callbacks=[StyleTrainingCallback(self)])