diff --git a/project/extract.py b/project/extract.py index c98d112..6d4d907 100644 --- a/project/extract.py +++ b/project/extract.py @@ -54,7 +54,7 @@ def extract_from_folder(model_workflow): instrument=instrument, bar_in_seq=BARS_IN_SEQ) - pickle.dump((x_train, y_train, program), open(save_path,'wb')) + pickle.dump((x_train, y_train, program, BARS_IN_SEQ), open(save_path,'wb')) if __name__ == '__main__': args = parse_argv() diff --git a/project/generate.py b/project/generate.py index ddbb19e..fb9ad11 100644 --- a/project/generate.py +++ b/project/generate.py @@ -9,21 +9,21 @@ parser = argparse.ArgumentParser() parser.add_argument('n', help='name for experiment', type=str) parser.add_argument('s', help='session name', type=str) parser.add_argument('--i', help='number of midis to generate', type=int) -parser.add_argument('--l', help='latent_dim_of_model', type=int) +# parser.add_argument('--l', help='latent_dim_of_model', type=int) parser.add_argument('--m', help="mode {'from_seq', 'from_state}'", type=str) args = parser.parse_args() EXPERIMENT_NAME = args.n SESSION_NAME = args.s GENERETIONS_COUNT = args.i -LATENT_DIM = args.l +# LATENT_DIM = args.l MODE = args.m if not GENERETIONS_COUNT: GENERETIONS_COUNT = 1 -if not LATENT_DIM: - LATENT_DIM = 256 +# if not LATENT_DIM: +# LATENT_DIM = 256 if not MODE: MODE = 'from_seq' @@ -42,17 +42,19 @@ for key, value in model_workflow.items(): band[instrument] = [None, None, generator] '''LOAD MODELS''' +print('Loading models...') for instrument in tqdm(band): data_path = os.path.join('training_sets', EXPERIMENT_NAME, instrument.lower() + '_data.pkl') model_path = os.path.join('models', EXPERIMENT_NAME, instrument.lower() + '_model.h5') - x_train, y_train, program = pickle.load(open(data_path,'rb')) - model = Seq2SeqModel(LATENT_DIM, x_train, y_train) + x_train, y_train, program, bars_in_seq = pickle.load(open(data_path,'rb')) + model = Seq2SeqModel(x_train, y_train, bars_in_seq=bars_in_seq) model.load(model_path) band[instrument][0] = model band[instrument][1] = program +print('Generating music...') for midi_counter in tqdm(range(GENERETIONS_COUNT)): ''' MAKE MULTIINSTRUMENTAL MUSIC !!!''' notes = dict() @@ -89,6 +91,6 @@ for midi_counter in tqdm(range(GENERETIONS_COUNT)): except: pass - save_path = os.path.join('generated_music', EXPERIMENT_NAME, SESSION_NAME, f'{EXPERIMENT_NAME}_{midi_counter}_{MODE}_{LATENT_DIM}.mid') + save_path = os.path.join('generated_music', EXPERIMENT_NAME, SESSION_NAME, f'{EXPERIMENT_NAME}_{midi_counter}_{MODE}.mid') generated_midi.save(save_path) # print(f'Generated succefuly to {save_path}') diff --git a/project/midi_processing.py b/project/midi_processing.py index 3110f74..2a123ae 100644 --- a/project/midi_processing.py +++ b/project/midi_processing.py @@ -519,7 +519,7 @@ def parse_pretty_midi_instrument(instrument, resolution, time_to_tick, key_offse if instrument.is_drum: notes[tick][0].add(note.pitch) else: - notes[tick][0].add(note.pitch+key_offset) + notes[tick][0].add(note.pitch + key_offset) notes[tick][1].add(note_lenth) diff --git a/project/model.py b/project/model.py index 677c0fe..6ff9d22 100644 --- a/project/model.py +++ b/project/model.py @@ -85,6 +85,8 @@ class Seq2SeqTransformer(): decoder_target_data[i, t - 1, self.y_transform_dict[char]] = 1. return encoder_input_data, decoder_input_data, decoder_target_data + + class Seq2SeqModel(): @@ -92,11 +94,12 @@ class Seq2SeqModel(): The network is created based on training data ''' - def __init__(self, latent_dim, x_train, y_train): + def __init__(self, x_train, y_train, latent_dim=256, enc_dropout=0, dec_dropout=0, bars_in_seq=4): self.has_predict_model = False self.has_train_model = False self.x_train = x_train self.y_train = y_train + self.bars_in_seq = bars_in_seq self.latent_dim = latent_dim self.transformer = Seq2SeqTransformer() self.encoder_input_data, self.decoder_input_data, self.decoder_target_data = self.transformer.transform(self.x_train, self.y_train) @@ -115,7 +118,7 @@ class Seq2SeqModel(): self.encoder_inputs = Input(shape=(None, self.transformer.x_vocab_size )) # 2 layer - LSTM_1, LSTM - self.encoder = LSTM(latent_dim, return_state=True) + self.encoder = LSTM(latent_dim, return_state=True, dropout=enc_dropout) #self.encoder = LSTM(latent_dim, return_state=True) # 2 layer - LSTM_1 : outputs @@ -130,7 +133,7 @@ class Seq2SeqModel(): self.decoder_inputs = Input(shape=(None, self.transformer.y_vocab_size)) # 2 layer - LSTM_1, LSTM - self.decoder_lstm = LSTM(latent_dim, return_sequences=True, return_state=True) + self.decoder_lstm = LSTM(latent_dim, return_sequences=True, return_state=True, dropout=dec_dropout) #self.decoder_lstm = LSTM(latent_dim, return_sequences=True, return_state=True) # 2 layer - LSTM_2 : outputs, full sequance as lstm layer @@ -221,7 +224,6 @@ class Seq2SeqModel(): # 3 layer: Dense output: one-hot-encoded representation of element of sequance self.decoder_outputs = self.decoder_dense(self.decoder_outputs) - self.decoder_model = Model( [self.decoder_inputs] + self.decoder_states_inputs, [self.decoder_outputs] + self.decoder_states) @@ -286,7 +288,8 @@ class Seq2SeqModel(): def develop(self, mode='from_seq'): # music generation for seq2seq for melody - input_seq_start = random_seed_generator(16, + # TODO: Hardcoded 16 ?? + input_seq_start = random_seed_generator(self.bars_in_seq * 4, self.transformer.x_max_seq_length, self.transformer.x_vocab_size, self.transformer.x_transform_dict, @@ -300,7 +303,7 @@ class Seq2SeqModel(): # generate sequnce iterativly for melody input_seq = input_seq_start.copy() melody = [] - for i in range(4): + for i in range(self.bars_in_seq): if mode == 'from_seq': decoded_sentence = self.predict(input_data)[:-1] elif mode == 'from_state': @@ -309,8 +312,8 @@ class Seq2SeqModel(): raise ValueError('mode must be in {from_seq, from_state}') melody.append(decoded_sentence) input_seq.extend(decoded_sentence) - input_bars = stream_to_bars(input_seq, 4) - input_bars = input_bars[1:5] + input_bars = stream_to_bars(input_seq, self.bars_in_seq) + input_bars = input_bars[1:self.bars_in_seq+1] input_seq = [note for bar in input_bars for note in bar] input_data = seq_to_numpy(input_seq, self.transformer.x_max_seq_length, @@ -348,7 +351,6 @@ def random_seed_generator(time_of_seq, max_encoder_seq_length, num_encoder_token random_seq = [] items = 0 stop_sign = False - return random_seq diff --git a/project/train.py b/project/train.py index 44c0529..2f70fde 100644 --- a/project/train.py +++ b/project/train.py @@ -16,6 +16,8 @@ def parse_argv(): parser.add_argument('--b', help='batch_size', type=int) parser.add_argument('--l', help='latent_dim', type=int) parser.add_argument('--e', help='epochs', type=int) + parser.add_argument('--ed', help='encoder dropout', type=float) + parser.add_argument('--dd', help='decoder dropout', type=float) parser.add_argument('--i', help='refrance to instrument to train, if you want to train only one instument') parser.add_argument('-r', help='reset, use when you want to reset waights and train from scratch', action='store_true') args = parser.parse_args() @@ -37,17 +39,21 @@ def train_models(model_workflow): found = False for instrument in instruments: - if INSTRUMENT == None or INSTRUMENT == instrument: + if not INSTRUMENT or INSTRUMENT == instrument: data_path = os.path.join('training_sets', EXPERIMENT_NAME, instrument.lower() + '_data.pkl') model_path = os.path.join('models', EXPERIMENT_NAME, f'{instrument.lower()}_model.h5') - x_train, y_train, _ = pickle.load(open(data_path,'rb')) - model = Seq2SeqModel(LATENT_DIM, x_train, y_train) + x_train, y_train, _, bars_in_seq = pickle.load(open(data_path,'rb')) + if os.path.isfile(model_path) and not RESET: + model = Seq2SeqModel(x_train, y_train) model.load(model_path) + else: + model = Seq2SeqModel(x_train, y_train, LATENT_DIM, ENCODER_DROPOUT, DECODER_DROPOUT, bars_in_seq) print(f'Training: {instrument}') model.fit(BATCH_SIZE, EPOCHS, callbacks=[]) + make_folder_if_not_exist(os.path.join('models', EXPERIMENT_NAME)) model.save(model_path) found = True @@ -65,6 +71,8 @@ if __name__ == '__main__': EPOCHS = args.e RESET = args.r INSTRUMENT = args.i + ENCODER_DROPOUT = args.ed + DECODER_DROPOUT = args.dd # default settings if not args passed if not BATCH_SIZE: @@ -75,5 +83,9 @@ if __name__ == '__main__': EPOCHS = 1 if not RESET: RESET = False + if not ENCODER_DROPOUT: + ENCODER_DROPOUT = 0.0 + if not DECODER_DROPOUT: + DECODER_DROPOUT = 0.0 train_models(load_workflow()) \ No newline at end of file