ArtORias1 commited on
Commit
e30cc6b
1 Parent(s): 9a32c6f

Upload lyrics_generation_rnn.py

Browse files
Files changed (1) hide show
  1. lyrics_generation_rnn.py +468 -0
lyrics_generation_rnn.py ADDED
@@ -0,0 +1,468 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """lyrics_generation_rnn.ipynb
3
+
4
+ Automatically generated by Colaboratory.
5
+
6
+ Original file is located at
7
+ https://colab.research.google.com/drive/1MkBq8eqZoPqaVDczKmYhSThcV4r23z25
8
+ """
9
+
10
+ !pip install pickle
11
+ import pickle
12
+ !pip install string
13
+ import string
14
+
15
+ import tensorflow as tf
16
+ from string import punctuation
17
+ import numpy as np
18
+ import os
19
+ import time
20
+ import pickle
21
+ model_path='/content/drive/MyDrive/Colab Notebooks'
22
+ # create directory to store pickled files in
23
+ if not os.path.exists(f'/content/drive/MyDrive/Colab Notebooks/pkl'):
24
+ os.mkdir(f'/content/drive/MyDrive/Colab Notebooks/pkl')
25
+
26
+ # ----------------------------------------------------------------------
27
+
28
+ ### LIMITING GPU MEMORY GROWTH ###
29
+
30
+ # get list of visible GPUs
31
+ gpus = tf.config.experimental.list_physical_devices('GPU')
32
+
33
+ if gpus: # if GPU(s) is detected
34
+ try: # try setting memory growth to true for all GPUs
35
+ for gpu in gpus:
36
+ tf.config.experimental.set_memory_growth(gpu, True) # enabling memory growth
37
+ logical_gpus = tf.config.experimental.list_logical_devices('GPU')
38
+ print('\n', len(gpus), 'Physical GPUs,', len(logical_gpus), 'Logical GPU')
39
+ except RuntimeError as e:
40
+ # memory growth must be set before GPUs have been initialized
41
+ print('\n', e)
42
+
43
+ # ----------------------------------------------------------------------
44
+
45
+ ### READ IN AND CLEAN THE LYRICS DATA ###
46
+
47
+ # ******TAKE IN USER INPUT FOR LYRICS (ARTIST NAME? FILE NAME?)******
48
+
49
+ # read in the lyrics text file
50
+ text = str(open('/content/drake.txt', 'r').read())
51
+ # artist_name = input('\nPlease ')
52
+
53
+ # make all letters lowercase and make line breaks into its own "word"
54
+ words = text.lower().replace('\n', ' \n ')
55
+
56
+ # remove punctuation
57
+ for punc in punctuation:
58
+ words = words.replace(punc, '')
59
+
60
+ # split the entire words string into a Python list of words
61
+ words = words.split(' ')
62
+
63
+ # obtain list of unique words across all lyrics
64
+ vocab = sorted(set(words))
65
+ print(f'\nThere are {len(vocab)} unique words in the lyrics file.')
66
+
67
+ # pickle the vocab file - will need it for the generation script
68
+ outfile = open(file='/content/drive/MyDrive/Colab Notebooks/pkl/vocab', mode='wb')
69
+ pickle.dump(vocab, outfile)
70
+ outfile.close()
71
+
72
+ # ----------------------------------------------------------------------
73
+
74
+ ### WORD MAPPING ###
75
+
76
+ # map unique characters to indices
77
+ word2idx = {u:i for i, u in enumerate(vocab)}
78
+
79
+ # pickle this since it is needed in text generation
80
+ outfile = open(file='/content/drive/MyDrive/Colab Notebooks/pkl/word2idx', mode='wb')
81
+ pickle.dump(word2idx, outfile)
82
+ outfile.close()
83
+
84
+ # reverse the map - use this to specify an index to obtain a character
85
+ idx2word = np.array(vocab)
86
+
87
+ # pickle this since it is needed in text generation
88
+ outfile = open(file='/content/drive/MyDrive/Colab Notebooks/pkl/idx2word', mode='wb')
89
+ pickle.dump(idx2word, outfile)
90
+ outfile.close()
91
+
92
+ # entire text document represented in the above character-to-indices mapping
93
+ words_as_int = np.array([word2idx[c] for c in words])
94
+
95
+ # ----------------------------------------------------------------------
96
+
97
+ ### CREATING TRAINING EXAMPLES & TARGETS ###
98
+
99
+ # ******TAKE IN USER INPUT FOR SEQUENCE LENGTH?******
100
+
101
+ # max sentence length (in number of words) desired for training
102
+ seq_length = 100
103
+ # seq_length = input('\nPlease enter a desired sequence length (in number of words) to train the model on: ')
104
+ examples_per_epoch = len(words) // (seq_length + 1)
105
+
106
+ # create training examples/targets
107
+ word_dataset = tf.data.Dataset.from_tensor_slices(words_as_int)
108
+
109
+ # data type of train examples/targets
110
+ print('\n', type(word_dataset))
111
+
112
+ # create sequence batches from the word_dataset
113
+ sequences = word_dataset.batch(seq_length + 1, drop_remainder=True)
114
+ print('\n', type(sequences))
115
+
116
+ # define the shifting (splitting) function
117
+ def split_input_target(chunk):
118
+ input_text = chunk[:-1] # up to but not including the last character
119
+ target_text = chunk[1:] # everything except for the firs tcharacter
120
+ return input_text, target_text
121
+
122
+ # apply the shifting to create input texts and target texts that comprise of our dataset
123
+ dataset = sequences.map(split_input_target)
124
+
125
+ # ----------------------------------------------------------------------
126
+
127
+ ### CREATE TRAINING BATCHES ###
128
+
129
+ # batch size
130
+ BATCH_SIZE = 64
131
+
132
+ # buffer size to shuffle the dataset
133
+ # (TensorFlow data is designed to work with possibly infinite sequences,
134
+ # so it doesn't attempt to shuffle the entire sequence in memory. Instead,
135
+ # it maintains a buffer in which it shuffles elements)
136
+ BUFFER_SIZE = 10000
137
+
138
+ # create a dataset that has been shuffled and batched
139
+ dataset_sb = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)
140
+
141
+ # display batch dataset shapes and data types
142
+ print('\n', dataset_sb)
143
+
144
+ # ----------------------------------------------------------------------
145
+
146
+ ### BUILDING THE RNN ###
147
+
148
+ # vocabulary length (number of unique words in dataset)
149
+ vocab_size = len(vocab)
150
+
151
+ # embedding dimension
152
+ embedding_dim = 256
153
+
154
+ # number of RNN units
155
+ rnn_units = 1024
156
+
157
+ # pickle model parameters - will need in the generation script
158
+ model_params = [vocab_size, embedding_dim, rnn_units]
159
+ outfile = open(file='/content/drive/MyDrive/Colab Notebooks/pkl/model_params', mode='wb')
160
+ pickle.dump(model_params, outfile)
161
+ outfile.close()
162
+
163
+ # helper function to quickly build the RNN model based on vocab size, embedding dimension, number of RNN units, and batch size
164
+ def build_model(vocab_size, embedding_dim, rnn_units, batch_size):
165
+
166
+ # initialize sequential model architecture
167
+ model = tf.keras.Sequential()
168
+
169
+ # add embedding layer
170
+ model.add(tf.keras.layers.Embedding(
171
+ input_dim = vocab_size,
172
+ output_dim = embedding_dim,
173
+ batch_input_shape=[batch_size, None]
174
+ ))
175
+
176
+ # add recurrent layer
177
+ model.add(tf.keras.layers.GRU(
178
+ units = rnn_units,
179
+ return_sequences = True,
180
+ stateful = True,
181
+ recurrent_initializer = 'glorot_uniform'
182
+ ))
183
+
184
+ # add dense layer
185
+ model.add(tf.keras.layers.Dense(units=vocab_size))
186
+ model_path= '/content/drive/MyDrive/Colab Notebooks'
187
+
188
+ def save_model(self, model_path):
189
+ # Save the model weights
190
+ self.save_weights(model_path)
191
+ print(f"Model saved to {model_path}")
192
+ return model
193
+
194
+ # build the model using the above helper function
195
+ rnn = build_model(
196
+ vocab_size = vocab_size,
197
+ embedding_dim = embedding_dim,
198
+ rnn_units = rnn_units,
199
+ batch_size = BATCH_SIZE
200
+ )
201
+
202
+ # check the shape of the output
203
+ for input_example_batch, target_example_batch in dataset_sb.take(1):
204
+ example_batch_predictions = rnn(input_example_batch)
205
+ print('\n', example_batch_predictions.shape, '# (batch_size, sequence_length, vocab_size)')
206
+
207
+ # model architecture summary
208
+ print('\n', rnn.summary(), '\n')
209
+
210
+ # ----------------------------------------------------------------------
211
+
212
+ ### SET UP METRICS ###
213
+
214
+ # helper function to obtain the loss function
215
+ def loss(labels, logits):
216
+ return tf.keras.losses.sparse_categorical_crossentropy(labels, logits, from_logits=True)
217
+
218
+ # compile the model
219
+ rnn.compile(
220
+ optimizer = 'adam',
221
+ loss = loss,
222
+ metrics = ['accuracy']
223
+ )
224
+
225
+ # create directory where the checkpoints will be saved
226
+ checkpoint_dir = '/content/drive/MyDrive/Colab Notebooks/training_checkpoints'
227
+
228
+ # name of the checkpoint files
229
+ checkpoint_prefix = os.path.join(checkpoint_dir, 'checkpoint')
230
+
231
+ # create checkpoints-saving object
232
+ checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(
233
+ filepath = checkpoint_prefix,
234
+ monitor = 'loss',
235
+ save_best_only = True,
236
+ mode = 'min',
237
+ save_weights_only = True
238
+ )
239
+
240
+ # ----------------------------------------------------------------------
241
+
242
+ ### MODEL TRAINING ###
243
+
244
+ # set number of desired epochs
245
+ EPOCHS = 200
246
+
247
+ # training!
248
+ history = rnn.fit(
249
+ x = dataset_sb,
250
+ epochs = EPOCHS,
251
+ callbacks = [checkpoint_callback]
252
+ )
253
+
254
+ build_model.save('/content/drive/MyDrive/Colab Notebooks')
255
+
256
+ import tensorflow as tf
257
+ from string import punctuation
258
+ import pickle
259
+
260
+ # ----------------------------------------------------------------------
261
+
262
+ ### LIMITING GPU MEMORY GROWTH ###
263
+
264
+ # get list of visible GPUs
265
+ gpus = tf.config.experimental.list_physical_devices('GPU')
266
+
267
+ if gpus: # if GPU(s) is detected
268
+ try: # try setting memory growth to true for all GPUs
269
+ for gpu in gpus:
270
+ tf.config.experimental.set_memory_growth(gpu, True) # enabling memory growth
271
+ logical_gpus = tf.config.experimental.list_logical_devices('GPU')
272
+ print('\n', len(gpus), 'Physical GPUs,', len(logical_gpus), 'Logical GPU')
273
+ except RuntimeError as e:
274
+ # memory growth must be set before GPUs have been initialized
275
+ print('\n', e)
276
+
277
+ # -------------------------------------------------------------------------
278
+
279
+ ### MODEL BUILDING FUNCTION FROM TRAINING SCRIPT ###
280
+
281
+ # helper function to quickly build the RNN model based on vocab size, embedding dimension, number of RNN units, and batch size
282
+ def build_model(vocab_size, embedding_dim, rnn_units, batch_size):
283
+ model = tf.keras.Sequential()
284
+
285
+ model.add(tf.keras.layers.Embedding(
286
+ input_dim = vocab_size,
287
+ output_dim = embedding_dim,
288
+ batch_input_shape=[batch_size, None]
289
+ ))
290
+
291
+ model.add(tf.keras.layers.GRU(
292
+ units = rnn_units,
293
+ return_sequences = True,
294
+ stateful = True,
295
+ recurrent_initializer = 'glorot_uniform'
296
+ ))
297
+
298
+ model.add(tf.keras.layers.Dense(units=vocab_size))
299
+
300
+ model_path= '/content/drive/MyDrive/Colab Notebooks'
301
+
302
+ def save_model(self, model_path):
303
+ # Save the model weights
304
+ self.save_weights(model_path)
305
+ print(f"Model saved to {model_path}")
306
+
307
+ return model
308
+
309
+ # -------------------------------------------------------------------------
310
+
311
+ ### INITIATE MODEL AND LOAD IN WEIGHTS FROM CHECKPOINT ###
312
+
313
+ # unpickle the model parameters from the training script
314
+ infile = open(file='pkl/model_params', mode='rb')
315
+ vocab_size, embedding_dim, rnn_units = pickle.load(infile)
316
+ infile.close()
317
+
318
+ # initiate new model instance
319
+ rnn_cp = build_model(vocab_size, embedding_dim, rnn_units, batch_size=1)
320
+
321
+ # load saved weights from checkpoint into new model instance
322
+ rnn_cp.load_weights(tf.train.latest_checkpoint('./training_checkpoints'))
323
+
324
+ # build the model with a new input shape
325
+ rnn_cp.build(tf.TensorShape([1, None]))
326
+
327
+ # -------------------------------------------------------------------------
328
+
329
+ ### TEXT PREDICTION FUNCTION ###
330
+
331
+ # unpickle the index-word files that were pickled from the training script
332
+ infile = open(file='pkl/word2idx', mode='rb')
333
+ word2idx = pickle.load(infile)
334
+ infile.close()
335
+ infile = open(file='pkl/idx2word', mode='rb')
336
+ idx2word = pickle.load(infile)
337
+ infile.close()
338
+ #build_model.is_valid():
339
+ #build_model.save('/content/drive/MyDrive/Colab Notebooks')
340
+
341
+ def generate_text(model, start_string, num_generate=500, temperature=1.0):
342
+
343
+ # num of chars to generate
344
+ num_generate = num_generate
345
+
346
+ # vectorizing the start string to numbers
347
+ input_eval = [word2idx[s] for s in start_string]
348
+ input_eval = tf.expand_dims(input=input_eval, axis=0) # returns a tensor with a length-1 axis inserted at index `axis`
349
+
350
+ # empty string to store results
351
+ text_generated = list()
352
+
353
+ # "temperature"
354
+ # low temperature results in more predictable text,
355
+ # high temperature results in more surprising text.
356
+ # feel free to experiment with this parameter
357
+ temperature = 1.0
358
+
359
+ # the batch size was defined when we loaded model weights from training
360
+
361
+ model.reset_states()
362
+ for i in range(num_generate):
363
+ predictions = model(input_eval)
364
+
365
+ # remove the batch dimension
366
+ predictions = tf.squeeze(predictions, 0)
367
+
368
+ # use a categorical distribution to predict the character returned by the model
369
+ preidctions = predictions / temperature
370
+ predicted_id = tf.random.categorical(predictions, num_samples=1)[-1, 0].numpy()
371
+
372
+ # pass the predicted character as the next input to the model along with the previous hidden state
373
+ input_eval = tf.expand_dims([predicted_id], 0)
374
+
375
+ text_generated.append(idx2word[predicted_id])
376
+
377
+ return(' '.join(start_string + text_generated))
378
+
379
+ # -------------------------------------------------------------------------
380
+
381
+ ### TAKE IN INPUT STRING AND CHECK IF ALL WORDS IN IT ARE IN THE VOCABULARY ###
382
+ # (this is a requirement for text generation)
383
+
384
+ # unpickle the vocabulary file that was pickled from the training script
385
+ infile = open(file='pkl/vocab', mode='rb')
386
+ vocab = pickle.load(infile)
387
+ infile.close()
388
+
389
+ # initialize the checking loop
390
+ check = True
391
+
392
+ while check:
393
+
394
+ # take in user input for starting lyrics
395
+ start_string = input('\nPlease input some text to initiate the lyrics generation (caps insensitive):\n')
396
+
397
+ # lowercase
398
+ start_string = start_string.lower()
399
+
400
+ # remove punctuation
401
+ for punc in punctuation:
402
+ start_string = start_string.replace(punc, '')
403
+
404
+ # create a list where each element is one word from the start string
405
+ start_string = start_string.split(' ')
406
+
407
+ # store all words that aren't in the vocabulary
408
+ non_vocab = []
409
+
410
+ # for every word in the start string
411
+ for word in start_string:
412
+
413
+ # if the word is NOT in the vocabulary
414
+ if word not in vocab:
415
+
416
+ # add the word to the non_vocab variable
417
+ non_vocab.append(word)
418
+
419
+ # if the non-vocab list is empty (i.e. all words in the start string are in the vocab)
420
+ if non_vocab == []:
421
+
422
+ # break out of the loop
423
+ check = False
424
+
425
+ # if there are words not in the vocabulary
426
+ else:
427
+
428
+ # print what those words are
429
+ print(f'\nWords in the input text not present in the vocabulary are: {", ".join(non_vocab)}')
430
+ print('\nAll input words must be in the vocabulary.')
431
+
432
+ # -------------------------------------------------------------------------
433
+
434
+ ### TEXT GENERATION ###
435
+
436
+ # text generation!
437
+ print('\n', generate_text(rnn_cp, start_string=start_string, num_generate=250))
438
+
439
+ ### SAVE TO FILE??? ###
440
+
441
+ # -------------------------------------------------------------------------
442
+
443
+ # -------------------------------------------------------------------------
444
+
445
+
446
+
447
+ build_model.save('/content/drive/MyDrive/Colab Notebooks')
448
+
449
+ model = build_model
450
+
451
+ """import tensorflow as tf
452
+ build_model.state_dict()
453
+ # Assuming you have a trained model named 'model'
454
+ model = ...
455
+
456
+ # Define the path to save the model
457
+ model_path = 'path_to_save_model'
458
+
459
+ # Save the entire model (architecture, weights, and optimizer state)
460
+ model.save(model_path)
461
+
462
+ [link text](https:// [link text](https://))# Alternatively, you can save only the model weights
463
+ model.save_weights('path_to_save_weights')
464
+
465
+ # You can also save the model in a format optimized for serving
466
+ tf.saved_model.save(model, 'path_for_serving')
467
+
468
+ """