Beispiel #1
0
    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()
Beispiel #2
0
    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)])