Example #1
0
 def __init__(self):
     self.fruit = ''
     self.taste = ''
     DBObject.__init__(self)
Example #2
0
 def __init__(self):
     self.taste = ''
     self.goodbad = ''
     DBObject.__init__(self)
Example #3
0
class PredictionEngine:
    def __init__(self, log_path, model_path):
        """
        The PredictionEngine wraps the pair of generators/discriminators for both
        layers and configures the batch window

        Parameters:
            - window :: The batch window
            - log_path :: The directory path to which the logs will be saved
            model_path :: The directory path to which the models will be saved
        """
        self.window_size = False
        self.default_model_save_path = "/home/dan/vestra/models/"
        self.log_path = log_path
        self.log_file = log_file
        self.db = DBObject()
        self.logger = ModelLogger(self.log_path, self.log_file)
        # check for cuda
        self.cuda = torch.cuda.is_available()
        self.learning_rate = 0.0002 

    def train(self, full=False, period=False, tickers=False, output_path=False, window_size=False, epochs=400, crit_interval=4, learning_rate=False):
        """
        Trains the data and outputs the tensors to the selected `output_path`. Note* the output path must be a directory,
        not a file.

        Parameters:
            - full :: If True, a full retrain will be performed in which the model is initialized from scratch. If False,
                      The model will be loaded and any new data specified in the `period` will be added to the model

            - period :: A list of two date strings (%Y-%M-%d) where the first index is the start date and the second index is
                        the end date (both inclusive) of the period of which to get the training data from. Note the period must
                        be longer than the `window_size` of the class.

            - tickers :: A list of tickers to include in the model

            - output_path :: The output path of the model (defaults to self.default_model_save_path

            - window_size :: The size of the batch window

            - epochs :: The number of training epochs to perform (default 4000)

            - crit_interval :: How many times per batch to train the generator (default 4)

        Returns:
            - save_path :: The full path of the saved checkpoint files for each of the models. The filenames have the following
                           format: `AMD_l1gw15.pt` where `AMD` signifies the ticker, `l1g` signifies that it's the first layer generator
                           and `w15` signifies the window length.
        """
        if not tickers:
            return False

        if period and type(period) != list:
            return False

        if not window_size and not self.window_size:
            return False

        if window_size:
            self.window_size = window_size

        if output_path:
            self.default_model_save_path = output_path

        if type(tickers) == str:
            tickers = [tickers]

        if learning_rate:
            self.learning_rate = learning_rate

        checkpoints = {}
        # the checkpoint file, if there are multiple tickers, is saved with each ticker
        # concatenated with a - (AMD-AAPL-BRKA_l1dw15.pt)
        tick = "-".join(tickers)

        if not full:
            # stores the layers (0, 1) checkpoint files for each ticker (the key)
            checkpoints = {}
            # try finding a file in the model storage directory that matches the given window size and stock parameters
            storage_directory = [f for f in os.listdir(
                self.default_model_save_path) if os.path.isfile(os.path.join(self.default_model_save_path, f))]
            checkpoints[tick] = []
            checkpoints[tick].append(
                {"generator": False, "discriminator": False})
            checkpoints[tick].append(
                {"generator": False, "discriminator": False})
            for f in storage_directory:
                params = f.split("_")[1]
                if tick in f and "l1" in params:
                    if "g" in params:
                        checkpoints[tick][0]["generator"] = f
                    elif "d" in params:
                        checkpoints[tick][0]["discriminator"] = f

        else:
            # generate our own checkpoint files bases
            checkpoints[tick] = []
            for i in range(2):
                generator_file = "{}_l{}gw{}.pt".format(
                    tick, i+1, self.window_size)
                discriminator_file = "{}_l{}dw{}.pt".format(
                    tick, i+1, self.window_size)
                checkpoints[tick].append(
                    {"generator": generator_file, "discriminator": discriminator_file})

        # get and format the stock data and spectrogram data
        input_tensors = []
        input_specgrams = []
        # since the minimum input size for the Spectrogram function is [200, 200] we need
        # to do a two-sided padding of the input
        specgram_padding = int(math.floor(201 - self.window_size / 2))

        for ticker in tickers:
            stock_data = self._get_stock_data(
                ticker, window_size=self.window_size)
            for tensor in stock_data:
                for i in range(tensor.shape[1]):
                    if tensor.shape[0] != self.window_size:
                        continue
                        # do not bother
                    input_tensors.append(tensor[:, i])
                    specgram = audiotransforms.Spectrogram(
                        pad=specgram_padding).cuda()(tensor[:, i])
                    input_specgrams.append(specgram)

        if len(input_specgrams) != len(input_tensors):
            raise Exception("Tensor and specgram input size mismatch")

        # instantiate the models, begin training

        models = {
            "l1g": L1Generator(input_specgrams[0].shape).cuda(),
            "l1d": L1Discriminator(input_specgrams[0].shape).cuda(),
            "l2g": L2Generator(input_specgrams[0].shape, input_tensors[0].shape).cuda(),
            "l2d": L2Discriminator(input_tensors[0].shape).cuda()
        }

        optimizers = {
            "l1g": torch.optim.RMSprop(models["l1g"].parameters(), lr=self.learning_rate),
            "l1d": torch.optim.RMSprop(models["l1d"].parameters(), lr=self.learning_rate),
            "l2g": torch.optim.RMSprop(models["l2g"].parameters(), lr=self.learning_rate),
            "l2d": torch.optim.RMSprop(models["l2d"].parameters(), lr=self.learning_rate)
        }

        # TODO: Write loader if full=False
        if not full:
            raise Exception(
                "Loading in checkpoints for further training is not supported yet")

        # begin the training
        batches_done = 0
        for epoch in range(epochs):

            for x in range(len(input_tensors)):

                tensor = autograd.Variable(input_tensors[x]).float().cuda()
                specgram = autograd.Variable(input_specgrams[x]).cuda()
                optimizers["l1d"].zero_grad()
                optimizers["l2d"].zero_grad()

                z = torch.from_numpy(np.random.normal(
                    0, 1, (specgram.shape[0], specgram.shape[1]))).float().cuda()

                # Train the first layer to get the first loss value
                z = autograd.Variable(z)
                fake_spectrogram = models["l1g"](z).detach()
                fake_spectrogram = fake_spectrogram[0]

                # second 1D->2D layer
                l2_z = autograd.Variable(fake_spectrogram).float().cuda()
                fake_tensor = models["l2g"](l2_z).detach()
                fake_tensor = fake_tensor[:, 0]

                # compute the loss function for l1
                loss_l1d = -torch.mean(models["l1d"](specgram)).cuda()
                loss_l1d = loss_l1d + \
                    torch.mean(models["l1d"](fake_spectrogram)).cuda()

                loss_l2d = -torch.mean(models["l2d"](tensor)).cuda()
                loss_l2d = loss_l2d + \
                    torch.mean(models["l2d"](fake_tensor)).cuda()

                loss_l1d.backward()
                loss_l2d.backward()
                optimizers["l1d"].step()
                optimizers["l2d"].step()

                if x % crit_interval == 0:
                    optimizers["l1g"].zero_grad()
                    optimizers["l2g"].zero_grad()

                    gen_specgrams = models["l1g"](z).detach()
                    gen_tensor = models["l2g"](fake_spectrogram).detach()

                    loss_l1g = - \
                        torch.mean(models["l1d"](gen_specgrams[0])).cuda()
                    loss_l2g = - \
                        torch.mean(models["l2d"](gen_tensor[:, 0])).cuda()

                    loss_l1g.backward()
                    loss_l2g.backward()
                    optimizers["l1g"].step()
                    optimizers["l2g"].step()

                    # log every crit interval
            self.logger.log_epoch(epoch, tick, loss_l1g.item(
            ), loss_l2g.item(), loss_l1d.item(), loss_l2d.item())

        # once done the training, save the generated generators and discriminators
        if full:
            print("Saving...")
            l1gs = checkpoints[tick][0]["generator"]
            l1ds = checkpoints[tick][0]["discriminator"]

            l2gs = checkpoints[tick][1]["generator"]
            l2ds = checkpoints[tick][1]["discriminator"]

            torch.save(models["l1d"].state_dict(),
                       self.default_model_save_path+l1ds)
            torch.save(models["l1g"].state_dict(),
                       self.default_model_save_path+l1gs)

            torch.save(models["l2g"].state_dict(),
                       self.default_model_save_path+l2gs)
            torch.save(models["l2d"].state_dict(),
                       self.default_model_save_path+l2ds)

            print("saved")

    def _get_stock_data(self, tickers, window_size=0, period=False, cuda=True):
        """
        Gets the stock data and automatically cleans and batches it into batches each
        `window_size` big.

        Parameters:
            - tickers :: An array (or string) of ticker(s) of which to get the data from
            - window_size :: The size of each batch window
            - period :: An array of string dates in the form %Y-%m-%d %H:%M:%S where index 0 is the
                        start date and index 1 is the end date
            - cuda :: If True, all training data will be loaded onto VRAM

        Returns:
            - input_dict :: An object containing a list of training tensors for each ticker split
                            into `data_length / window_size` batches
        """

        if type(tickers) == str:
            tickers = [tickers]

        input_dict = {}

        data_limit = "false"
        current_batch = False if window_size == 0 else 0
        if period:
            delta = (datetime.datetime.strptime(period[1], "%Y-%m-%d %H:%M:%S")) - (
                datetime.datetime.strptime(period[0], "%Y-%m-%d %H:%M:%S"))
            data_limit = delta.days + 1

        for ticker in tickers:
            ticker_data = self.db.get_stock_data(
                ticker, limit=data_limit, period=period)
            ticker_data = ticker_data[ticker]
            cleaned_data = []
            keys = ['open', 'high', 'low', 'close']

            for key in keys:
                key_data = [x[key] for x in ticker_data]
                cleaned_data.append(key_data)

        cleaned_data = np.array(cleaned_data)
        cleaned_data = np.transpose(cleaned_data)

        if cuda:
            cleaned_data = torch.from_numpy(cleaned_data).cuda()
        else:
            cleaned_data = torch.from_numpy(cleaned_data)

        if window_size > 0:
            cleaned_data = torch.split(cleaned_data, window_size)
        return cleaned_data