Create notebooks/lyrics_development.ipynb
Browse filesAdd lyrics-development base code to start.
notebooks/lyrics_development.ipynb
ADDED
@@ -0,0 +1,729 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Lyrics Generation Model Development
|
2 |
+
# Author: [Your Name]
|
3 |
+
# Project: Opentunes.ai
|
4 |
+
|
5 |
+
import torch
|
6 |
+
import torch.nn as nn
|
7 |
+
from transformers import (
|
8 |
+
AutoModelForCausalLM,
|
9 |
+
AutoTokenizer,
|
10 |
+
Trainer,
|
11 |
+
TrainingArguments,
|
12 |
+
GPT2LMHeadModel,
|
13 |
+
GPT2Tokenizer
|
14 |
+
)
|
15 |
+
import pandas as pd
|
16 |
+
import numpy as np
|
17 |
+
from pathlib import Path
|
18 |
+
import json
|
19 |
+
import wandb
|
20 |
+
from tqdm import tqdm
|
21 |
+
|
22 |
+
# 1. Data Loading and Preprocessing
|
23 |
+
class LyricsDataset(torch.utils.data.Dataset):
|
24 |
+
"""
|
25 |
+
Custom Dataset for lyrics data.
|
26 |
+
|
27 |
+
Features:
|
28 |
+
- Loads and processes lyrics text
|
29 |
+
- Handles style/genre tags
|
30 |
+
- Manages rhyme patterns
|
31 |
+
- Tokenization for transformer models
|
32 |
+
"""
|
33 |
+
|
34 |
+
def __init__(self, data_dir, max_length=512):
|
35 |
+
self.data_dir = Path(data_dir)
|
36 |
+
self.max_length = max_length
|
37 |
+
|
38 |
+
# Initialize tokenizer (using GPT-2 as base)
|
39 |
+
self.tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
|
40 |
+
self.tokenizer.pad_token = self.tokenizer.eos_token
|
41 |
+
|
42 |
+
# Load lyrics data
|
43 |
+
self.lyrics_files = list(self.data_dir.glob("*.txt"))
|
44 |
+
self.lyrics_data = self._load_lyrics_data()
|
45 |
+
|
46 |
+
def _load_lyrics_data(self):
|
47 |
+
"""Load and preprocess lyrics from files."""
|
48 |
+
data = []
|
49 |
+
for file in self.lyrics_files:
|
50 |
+
with open(file, 'r', encoding='utf-8') as f:
|
51 |
+
lyrics = f.read()
|
52 |
+
|
53 |
+
# Extract metadata from filename or content
|
54 |
+
metadata = self._extract_metadata(file)
|
55 |
+
|
56 |
+
data.append({
|
57 |
+
'lyrics': lyrics,
|
58 |
+
'genre': metadata.get('genre', 'unknown'),
|
59 |
+
'style': metadata.get('style', 'unknown'),
|
60 |
+
'structure': metadata.get('structure', 'verse-chorus')
|
61 |
+
})
|
62 |
+
return data
|
63 |
+
|
64 |
+
def _extract_metadata(self, file):
|
65 |
+
"""Extract metadata from filename or content."""
|
66 |
+
# Example filename format: pop_love_verse-chorus.txt
|
67 |
+
parts = file.stem.split('_')
|
68 |
+
return {
|
69 |
+
'genre': parts[0] if len(parts) > 0 else 'unknown',
|
70 |
+
'style': parts[1] if len(parts) > 1 else 'unknown',
|
71 |
+
'structure': parts[2] if len(parts) > 2 else 'verse-chorus'
|
72 |
+
}
|
73 |
+
|
74 |
+
def __len__(self):
|
75 |
+
return len(self.lyrics_data)
|
76 |
+
|
77 |
+
def __getitem__(self, idx):
|
78 |
+
item = self.lyrics_data[idx]
|
79 |
+
|
80 |
+
# Prepare input text with metadata
|
81 |
+
input_text = f"<|genre|>{item['genre']}<|style|>{item['style']}<|lyrics|>{item['lyrics']}"
|
82 |
+
|
83 |
+
# Tokenize
|
84 |
+
encoding = self.tokenizer(
|
85 |
+
input_text,
|
86 |
+
max_length=self.max_length,
|
87 |
+
padding='max_length',
|
88 |
+
truncation=True,
|
89 |
+
return_tensors='pt'
|
90 |
+
)
|
91 |
+
|
92 |
+
return {
|
93 |
+
'input_ids': encoding['input_ids'].squeeze(),
|
94 |
+
'attention_mask': encoding['attention_mask'].squeeze()
|
95 |
+
}
|
96 |
+
|
97 |
+
# 2. Model Architecture
|
98 |
+
class LyricsTransformer(nn.Module):
|
99 |
+
"""
|
100 |
+
Transformer model for lyrics generation.
|
101 |
+
|
102 |
+
Features:
|
103 |
+
- GPT-2 based architecture
|
104 |
+
- Style conditioning
|
105 |
+
- Rhyme awareness
|
106 |
+
- Structure control
|
107 |
+
"""
|
108 |
+
|
109 |
+
def __init__(self,
|
110 |
+
vocab_size=50257, # GPT-2 vocabulary size
|
111 |
+
d_model=768,
|
112 |
+
nhead=12,
|
113 |
+
num_layers=6):
|
114 |
+
super().__init__()
|
115 |
+
|
116 |
+
# Load pretrained GPT-2
|
117 |
+
self.transformer = GPT2LMHeadModel.from_pretrained('gpt2')
|
118 |
+
|
119 |
+
# Add style embedding
|
120 |
+
self.style_embedding = nn.Embedding(100, d_model) # 100 different styles
|
121 |
+
|
122 |
+
# Add additional layers for style conditioning
|
123 |
+
self.style_projection = nn.Linear(d_model, d_model)
|
124 |
+
self.genre_embedding = nn.Embedding(50, d_model) # 50 different genres
|
125 |
+
|
126 |
+
def forward(self, input_ids, attention_mask=None, style_ids=None):
|
127 |
+
"""
|
128 |
+
Forward pass with style conditioning.
|
129 |
+
|
130 |
+
Args:
|
131 |
+
input_ids: Tokenized input text
|
132 |
+
attention_mask: Attention mask for padding
|
133 |
+
style_ids: Optional style conditioning ids
|
134 |
+
"""
|
135 |
+
# Get base transformer outputs
|
136 |
+
outputs = self.transformer(
|
137 |
+
input_ids=input_ids,
|
138 |
+
attention_mask=attention_mask,
|
139 |
+
return_dict=True
|
140 |
+
)
|
141 |
+
|
142 |
+
# Add style conditioning if provided
|
143 |
+
if style_ids is not None:
|
144 |
+
style_embeds = self.style_embedding(style_ids)
|
145 |
+
style_projection = self.style_projection(style_embeds)
|
146 |
+
outputs.logits += style_projection
|
147 |
+
|
148 |
+
return outputs
|
149 |
+
|
150 |
+
# 3. Training Pipeline
|
151 |
+
class LyricsTrainer:
|
152 |
+
"""
|
153 |
+
Training pipeline for lyrics generation model.
|
154 |
+
|
155 |
+
Features:
|
156 |
+
- Custom training loop
|
157 |
+
- Style-conditional training
|
158 |
+
- Rhyme pattern learning
|
159 |
+
- Metrics tracking
|
160 |
+
"""
|
161 |
+
|
162 |
+
def __init__(self, model, config, device='cuda'):
|
163 |
+
self.model = model.to(device)
|
164 |
+
self.config = config
|
165 |
+
self.device = device
|
166 |
+
|
167 |
+
self.optimizer = torch.optim.AdamW(
|
168 |
+
model.parameters(),
|
169 |
+
lr=config['learning_rate'],
|
170 |
+
weight_decay=config.get('weight_decay', 0.01)
|
171 |
+
)
|
172 |
+
|
173 |
+
self.scheduler = torch.optim.lr_scheduler.OneCycleLR(
|
174 |
+
self.optimizer,
|
175 |
+
max_lr=config['learning_rate'],
|
176 |
+
epochs=config['epochs'],
|
177 |
+
steps_per_epoch=config['steps_per_epoch']
|
178 |
+
)
|
179 |
+
|
180 |
+
def train_epoch(self, train_loader):
|
181 |
+
"""Train for one epoch."""
|
182 |
+
self.model.train()
|
183 |
+
epoch_loss = 0
|
184 |
+
|
185 |
+
for batch in tqdm(train_loader):
|
186 |
+
# Move batch to device
|
187 |
+
input_ids = batch['input_ids'].to(self.device)
|
188 |
+
attention_mask = batch['attention_mask'].to(self.device)
|
189 |
+
|
190 |
+
# Forward pass
|
191 |
+
outputs = self.model(input_ids, attention_mask=attention_mask)
|
192 |
+
|
193 |
+
# Calculate loss
|
194 |
+
loss = outputs.loss
|
195 |
+
|
196 |
+
# Backward pass
|
197 |
+
self.optimizer.zero_grad()
|
198 |
+
loss.backward()
|
199 |
+
torch.nn.utils.clip_grad_norm_(self.model.parameters(), 1.0)
|
200 |
+
self.optimizer.step()
|
201 |
+
self.scheduler.step()
|
202 |
+
|
203 |
+
epoch_loss += loss.item()
|
204 |
+
|
205 |
+
return epoch_loss / len(train_loader)
|
206 |
+
|
207 |
+
def evaluate(self, val_loader):
|
208 |
+
"""Evaluate the model."""
|
209 |
+
self.model.eval()
|
210 |
+
val_loss = 0
|
211 |
+
|
212 |
+
with torch.no_grad():
|
213 |
+
for batch in val_loader:
|
214 |
+
input_ids = batch['input_ids'].to(self.device)
|
215 |
+
attention_mask = batch['attention_mask'].to(self.device)
|
216 |
+
|
217 |
+
outputs = self.model(input_ids, attention_mask=attention_mask)
|
218 |
+
loss = outputs.loss
|
219 |
+
|
220 |
+
val_loss += loss.item()
|
221 |
+
|
222 |
+
return val_loss / len(val_loader)
|
223 |
+
|
224 |
+
# 4. Generation and Inference
|
225 |
+
class LyricsGenerator:
|
226 |
+
"""
|
227 |
+
Lyrics generation interface.
|
228 |
+
|
229 |
+
Features:
|
230 |
+
- Prompt-based generation
|
231 |
+
- Style control
|
232 |
+
- Rhyme scheme enforcement
|
233 |
+
- Structure management
|
234 |
+
"""
|
235 |
+
|
236 |
+
def __init__(self, model, tokenizer, device='cuda'):
|
237 |
+
self.model = model.to(device)
|
238 |
+
self.tokenizer = tokenizer
|
239 |
+
self.device = device
|
240 |
+
|
241 |
+
def generate(self, prompt, style=None, max_length=200):
|
242 |
+
"""Generate lyrics from prompt."""
|
243 |
+
self.model.eval()
|
244 |
+
|
245 |
+
# Prepare input
|
246 |
+
input_text = prompt if style is None else f"<|style|>{style}<|prompt|>{prompt}"
|
247 |
+
input_ids = self.tokenizer.encode(input_text, return_tensors='pt').to(self.device)
|
248 |
+
|
249 |
+
# Generate
|
250 |
+
outputs = self.model.generate(
|
251 |
+
input_ids,
|
252 |
+
max_length=max_length,
|
253 |
+
num_return_sequences=1,
|
254 |
+
no_repeat_ngram_size=3,
|
255 |
+
do_sample=True,
|
256 |
+
top_k=50,
|
257 |
+
top_p=0.95,
|
258 |
+
temperature=0.7
|
259 |
+
)
|
260 |
+
|
261 |
+
# Decode
|
262 |
+
generated_lyrics = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
|
263 |
+
|
264 |
+
return generated_lyrics
|
265 |
+
|
266 |
+
# 5. Evaluation
|
267 |
+
class LyricsEvaluator:
|
268 |
+
"""
|
269 |
+
Evaluation metrics for lyrics generation.
|
270 |
+
|
271 |
+
Metrics:
|
272 |
+
- Rhyme quality
|
273 |
+
- Syllable count
|
274 |
+
- Theme consistency
|
275 |
+
- Style adherence
|
276 |
+
"""
|
277 |
+
|
278 |
+
def __init__(self, model, tokenizer):
|
279 |
+
self.model = model
|
280 |
+
self.tokenizer = tokenizer
|
281 |
+
|
282 |
+
def evaluate_rhyme(self, lyrics):
|
283 |
+
"""Evaluate rhyme patterns."""
|
284 |
+
# Implement rhyme detection and scoring
|
285 |
+
pass
|
286 |
+
|
287 |
+
def evaluate_structure(self, lyrics):
|
288 |
+
"""Evaluate lyrical structure."""
|
289 |
+
# Implement structure analysis
|
290 |
+
pass
|
291 |
+
|
292 |
+
def evaluate_theme(self, lyrics, prompt):
|
293 |
+
"""Evaluate theme consistency."""
|
294 |
+
# Implement theme analysis
|
295 |
+
pass
|
296 |
+
|
297 |
+
# Example Usage
|
298 |
+
def main():
|
299 |
+
# Load config
|
300 |
+
with open('models/lyrics-gen/config/model_config.json') as f:
|
301 |
+
config = json.load(f)
|
302 |
+
|
303 |
+
# Initialize model and tokenizer
|
304 |
+
model = LyricsTransformer()
|
305 |
+
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
|
306 |
+
|
307 |
+
# Create datasets
|
308 |
+
train_dataset = LyricsDataset('datasets/lyrics/train')
|
309 |
+
val_dataset = LyricsDataset('datasets/lyrics/val')
|
310 |
+
|
311 |
+
# Initialize trainer
|
312 |
+
trainer = LyricsTrainer(model, config)
|
313 |
+
|
314 |
+
# Train model
|
315 |
+
for epoch in range(config['epochs']):
|
316 |
+
train_loss = trainer.train_epoch(train_dataset)
|
317 |
+
val_loss = trainer.evaluate(val_dataset)
|
318 |
+
print(f"Epoch {epoch}: Train Loss = {train_loss:.4f}, Val Loss = {val_loss:.4f}")
|
319 |
+
|
320 |
+
# Generate sample
|
321 |
+
generator = LyricsGenerator(model, tokenizer)
|
322 |
+
lyrics = generator.generate(
|
323 |
+
prompt="Write a love song about summer",
|
324 |
+
style="pop"
|
325 |
+
)
|
326 |
+
print("Generated Lyrics:", lyrics)
|
327 |
+
|
328 |
+
if __name__ == "__main__":
|
329 |
+
main()
|
330 |
+
|
331 |
+
|
332 |
+
|
333 |
+
|
334 |
+
# Additional Features for Lyrics Generation
|
335 |
+
|
336 |
+
# 1. Enhanced Style Control and Structure
|
337 |
+
class EnhancedLyricsGenerator(LyricsGenerator):
|
338 |
+
"""
|
339 |
+
Enhanced lyrics generator with advanced features.
|
340 |
+
|
341 |
+
Additional Features:
|
342 |
+
- Song structure management (verse, chorus, bridge)
|
343 |
+
- Rhyme scheme control
|
344 |
+
- Syllable count management
|
345 |
+
- Emotional tone control
|
346 |
+
- Genre-specific patterns
|
347 |
+
- Metaphor and imagery enhancement
|
348 |
+
"""
|
349 |
+
|
350 |
+
def generate_structured_song(self, prompt, structure_dict):
|
351 |
+
"""
|
352 |
+
Generate a complete song with specified structure.
|
353 |
+
|
354 |
+
Args:
|
355 |
+
prompt (str): Main theme/topic
|
356 |
+
structure_dict (dict): Song structure specification
|
357 |
+
Example:
|
358 |
+
{
|
359 |
+
'verse1': {'lines': 4, 'syllables_per_line': 8, 'rhyme_scheme': 'AABB'},
|
360 |
+
'chorus': {'lines': 4, 'syllables_per_line': 6, 'rhyme_scheme': 'ABAB'},
|
361 |
+
'verse2': {'lines': 4, 'syllables_per_line': 8, 'rhyme_scheme': 'AABB'},
|
362 |
+
'bridge': {'lines': 2, 'syllables_per_line': 10, 'rhyme_scheme': 'AA'},
|
363 |
+
}
|
364 |
+
|
365 |
+
Returns:
|
366 |
+
dict: Generated song sections with metadata
|
367 |
+
"""
|
368 |
+
song_parts = {}
|
369 |
+
|
370 |
+
for section, specs in structure_dict.items():
|
371 |
+
section_prompt = self._create_section_prompt(
|
372 |
+
base_prompt=prompt,
|
373 |
+
section_type=section,
|
374 |
+
specifications=specs
|
375 |
+
)
|
376 |
+
|
377 |
+
generated_section = self.generate_section(
|
378 |
+
prompt=section_prompt,
|
379 |
+
rhyme_scheme=specs['rhyme_scheme'],
|
380 |
+
syllables=specs['syllables_per_line'],
|
381 |
+
num_lines=specs['lines']
|
382 |
+
)
|
383 |
+
|
384 |
+
song_parts[section] = generated_section
|
385 |
+
|
386 |
+
return self._compile_song(song_parts)
|
387 |
+
|
388 |
+
def generate_with_emotion(self, prompt, emotion_params):
|
389 |
+
"""
|
390 |
+
Generate lyrics with specific emotional qualities.
|
391 |
+
|
392 |
+
Args:
|
393 |
+
prompt (str): Base prompt
|
394 |
+
emotion_params (dict): Emotional parameters
|
395 |
+
Example:
|
396 |
+
{
|
397 |
+
'primary_emotion': 'joy',
|
398 |
+
'intensity': 0.8,
|
399 |
+
'tone': 'uplifting',
|
400 |
+
'imagery_type': 'nature',
|
401 |
+
'word_choices': 'positive'
|
402 |
+
}
|
403 |
+
"""
|
404 |
+
# Enhance prompt with emotional context
|
405 |
+
enhanced_prompt = self._add_emotional_context(prompt, emotion_params)
|
406 |
+
|
407 |
+
# Generate with emotion-specific settings
|
408 |
+
return self.generate(
|
409 |
+
prompt=enhanced_prompt,
|
410 |
+
temperature=self._get_emotion_temperature(emotion_params),
|
411 |
+
top_p=self._get_emotion_top_p(emotion_params)
|
412 |
+
)
|
413 |
+
|
414 |
+
def generate_with_metaphors(self, prompt, theme_params):
|
415 |
+
"""
|
416 |
+
Generate lyrics rich in metaphors and imagery.
|
417 |
+
|
418 |
+
Args:
|
419 |
+
prompt (str): Base prompt
|
420 |
+
theme_params (dict): Theme and metaphor specifications
|
421 |
+
Example:
|
422 |
+
{
|
423 |
+
'primary_theme': 'love',
|
424 |
+
'metaphor_source': 'ocean',
|
425 |
+
'imagery_type': 'visual',
|
426 |
+
'complexity_level': 'advanced'
|
427 |
+
}
|
428 |
+
"""
|
429 |
+
metaphor_enhanced_prompt = self._enhance_with_metaphors(prompt, theme_params)
|
430 |
+
return self.generate(prompt=metaphor_enhanced_prompt)
|
431 |
+
|
432 |
+
def _enhance_with_metaphors(self, prompt, theme_params):
|
433 |
+
"""Add metaphorical elements to prompt."""
|
434 |
+
metaphor_templates = {
|
435 |
+
'love': {
|
436 |
+
'ocean': [
|
437 |
+
"deep as the ocean",
|
438 |
+
"waves of emotion",
|
439 |
+
"tidal force of feeling"
|
440 |
+
],
|
441 |
+
'fire': [
|
442 |
+
"burning passion",
|
443 |
+
"flame of desire",
|
444 |
+
"scorching intensity"
|
445 |
+
]
|
446 |
+
}
|
447 |
+
# Add more themes and metaphor sources
|
448 |
+
}
|
449 |
+
|
450 |
+
# Select appropriate metaphors
|
451 |
+
chosen_metaphors = self._select_metaphors(
|
452 |
+
metaphor_templates,
|
453 |
+
theme_params
|
454 |
+
)
|
455 |
+
|
456 |
+
# Enhance prompt with metaphors
|
457 |
+
return f"{prompt} {' '.join(chosen_metaphors)}"
|
458 |
+
|
459 |
+
class RhymeController:
|
460 |
+
"""
|
461 |
+
Controls rhyme patterns in generated lyrics.
|
462 |
+
|
463 |
+
Features:
|
464 |
+
- Multiple rhyme scheme support
|
465 |
+
- Syllable counting
|
466 |
+
- Assonance detection
|
467 |
+
- Alliteration management
|
468 |
+
"""
|
469 |
+
|
470 |
+
def __init__(self):
|
471 |
+
self.pronunciations = self._load_pronunciations()
|
472 |
+
|
473 |
+
def enforce_rhyme_scheme(self, lines, scheme):
|
474 |
+
"""
|
475 |
+
Modify lines to follow rhyme scheme.
|
476 |
+
|
477 |
+
Args:
|
478 |
+
lines (list): Generated lines
|
479 |
+
scheme (str): Rhyme scheme (e.g., 'AABB', 'ABAB')
|
480 |
+
|
481 |
+
Returns:
|
482 |
+
list: Modified lines with proper rhyming
|
483 |
+
"""
|
484 |
+
rhyme_groups = self._create_rhyme_groups(scheme)
|
485 |
+
return self._modify_line_endings(lines, rhyme_groups)
|
486 |
+
|
487 |
+
def _get_rhyming_words(self, word):
|
488 |
+
"""Find words that rhyme with given word."""
|
489 |
+
pronunciation = self.pronunciations.get(word.lower())
|
490 |
+
if not pronunciation:
|
491 |
+
return []
|
492 |
+
|
493 |
+
rhyming_words = []
|
494 |
+
for w, p in self.pronunciations.items():
|
495 |
+
if self._is_rhyme(pronunciation, p):
|
496 |
+
rhyming_words.append(w)
|
497 |
+
|
498 |
+
return rhyming_words
|
499 |
+
|
500 |
+
# Advanced Evaluation Metrics
|
501 |
+
class EnhancedLyricsEvaluator(LyricsEvaluator):
|
502 |
+
"""
|
503 |
+
Comprehensive evaluation suite for lyrics generation.
|
504 |
+
|
505 |
+
Metrics Categories:
|
506 |
+
1. Technical Quality
|
507 |
+
2. Musical Compatibility
|
508 |
+
3. Content Analysis
|
509 |
+
4. Style Adherence
|
510 |
+
"""
|
511 |
+
|
512 |
+
def evaluate_comprehensive(self, lyrics, target_style=None):
|
513 |
+
"""
|
514 |
+
Run comprehensive evaluation on generated lyrics.
|
515 |
+
|
516 |
+
Returns:
|
517 |
+
dict: Complete evaluation metrics
|
518 |
+
"""
|
519 |
+
return {
|
520 |
+
'technical': self.evaluate_technical(lyrics),
|
521 |
+
'musical': self.evaluate_musical(lyrics),
|
522 |
+
'content': self.evaluate_content(lyrics),
|
523 |
+
'style': self.evaluate_style(lyrics, target_style)
|
524 |
+
}
|
525 |
+
|
526 |
+
def evaluate_technical(self, lyrics):
|
527 |
+
"""Evaluate technical aspects of lyrics."""
|
528 |
+
return {
|
529 |
+
'rhyme_quality': self._analyze_rhyme_patterns(lyrics),
|
530 |
+
'syllable_consistency': self._analyze_syllable_patterns(lyrics),
|
531 |
+
'vocabulary_richness': self._calculate_vocabulary_metrics(lyrics),
|
532 |
+
'grammar_score': self._check_grammar(lyrics)
|
533 |
+
}
|
534 |
+
|
535 |
+
def evaluate_musical(self, lyrics):
|
536 |
+
"""Evaluate musical compatibility."""
|
537 |
+
return {
|
538 |
+
'rhythm_score': self._analyze_rhythm(lyrics),
|
539 |
+
'singability': self._evaluate_singability(lyrics),
|
540 |
+
'phrase_length': self._analyze_phrase_length(lyrics),
|
541 |
+
'stress_patterns': self._analyze_stress_patterns(lyrics)
|
542 |
+
}
|
543 |
+
|
544 |
+
def evaluate_content(self, lyrics):
|
545 |
+
"""Evaluate lyrical content quality."""
|
546 |
+
return {
|
547 |
+
'theme_coherence': self._analyze_theme_consistency(lyrics),
|
548 |
+
'emotional_impact': self._analyze_emotional_content(lyrics),
|
549 |
+
'imagery_score': self._evaluate_imagery(lyrics),
|
550 |
+
'narrative_strength': self._analyze_narrative(lyrics)
|
551 |
+
}
|
552 |
+
|
553 |
+
# Example Usage and Scenarios
|
554 |
+
|
555 |
+
def demonstrate_lyrics_generation():
|
556 |
+
"""
|
557 |
+
Demonstrate various lyrics generation scenarios.
|
558 |
+
"""
|
559 |
+
|
560 |
+
# Initialize generator
|
561 |
+
generator = EnhancedLyricsGenerator(model, tokenizer)
|
562 |
+
|
563 |
+
# 1. Generate a complete pop song
|
564 |
+
pop_structure = {
|
565 |
+
'verse1': {
|
566 |
+
'lines': 4,
|
567 |
+
'syllables_per_line': 8,
|
568 |
+
'rhyme_scheme': 'AABB'
|
569 |
+
},
|
570 |
+
'chorus': {
|
571 |
+
'lines': 4,
|
572 |
+
'syllables_per_line': 6,
|
573 |
+
'rhyme_scheme': 'ABAB'
|
574 |
+
},
|
575 |
+
'verse2': {
|
576 |
+
'lines': 4,
|
577 |
+
'syllables_per_line': 8,
|
578 |
+
'rhyme_scheme': 'AABB'
|
579 |
+
}
|
580 |
+
}
|
581 |
+
|
582 |
+
pop_song = generator.generate_structured_song(
|
583 |
+
prompt="A summer love story",
|
584 |
+
structure_dict=pop_structure
|
585 |
+
)
|
586 |
+
|
587 |
+
# 2. Generate emotional ballad
|
588 |
+
emotion_params = {
|
589 |
+
'primary_emotion': 'longing',
|
590 |
+
'intensity': 0.9,
|
591 |
+
'tone': 'melancholic',
|
592 |
+
'imagery_type': 'nature',
|
593 |
+
'word_choices': 'poetic'
|
594 |
+
}
|
595 |
+
|
596 |
+
ballad = generator.generate_with_emotion(
|
597 |
+
prompt="Lost love and memories",
|
598 |
+
emotion_params=emotion_params
|
599 |
+
)
|
600 |
+
|
601 |
+
# 3. Generate metaphorical lyrics
|
602 |
+
theme_params = {
|
603 |
+
'primary_theme': 'love',
|
604 |
+
'metaphor_source': 'ocean',
|
605 |
+
'imagery_type': 'visual',
|
606 |
+
'complexity_level': 'advanced'
|
607 |
+
}
|
608 |
+
|
609 |
+
metaphorical = generator.generate_with_metaphors(
|
610 |
+
prompt="Finding inner strength",
|
611 |
+
theme_params=theme_params
|
612 |
+
)
|
613 |
+
|
614 |
+
return {
|
615 |
+
'pop_song': pop_song,
|
616 |
+
'ballad': ballad,
|
617 |
+
'metaphorical': metaphorical
|
618 |
+
}
|
619 |
+
|
620 |
+
|
621 |
+
# Integration with Melody Generation
|
622 |
+
class SongIntegrator:
|
623 |
+
"""
|
624 |
+
Integrates lyrics and melody generation.
|
625 |
+
|
626 |
+
Features:
|
627 |
+
- Synchronizes lyrics with melody
|
628 |
+
- Adjusts rhythm to match syllables
|
629 |
+
- Ensures musical phrase alignment
|
630 |
+
"""
|
631 |
+
|
632 |
+
def __init__(self, lyrics_generator, melody_generator):
|
633 |
+
self.lyrics_generator = lyrics_generator
|
634 |
+
self.melody_generator = melody_generator
|
635 |
+
|
636 |
+
def generate_complete_song(self, prompt, style):
|
637 |
+
"""
|
638 |
+
Generate a complete song with matching lyrics and melody.
|
639 |
+
|
640 |
+
Args:
|
641 |
+
prompt (str): Song theme/topic
|
642 |
+
style (dict): Musical and lyrical style parameters
|
643 |
+
|
644 |
+
Returns:
|
645 |
+
dict: Complete song with melody and lyrics
|
646 |
+
"""
|
647 |
+
# Generate lyrics first
|
648 |
+
lyrics = self.lyrics_generator.generate_structured_song(
|
649 |
+
prompt=prompt,
|
650 |
+
structure_dict=self._get_structure_for_style(style)
|
651 |
+
)
|
652 |
+
|
653 |
+
# Generate matching melody
|
654 |
+
melody = self.melody_generator.generate_with_structure(
|
655 |
+
prompt=prompt,
|
656 |
+
form=self._extract_form_from_lyrics(lyrics)
|
657 |
+
)
|
658 |
+
|
659 |
+
# Align lyrics and melody
|
660 |
+
aligned_song = self._align_lyrics_and_melody(lyrics, melody)
|
661 |
+
|
662 |
+
return aligned_song
|
663 |
+
|
664 |
+
def _align_lyrics_and_melody(self, lyrics, melody):
|
665 |
+
"""Align lyrics with melody phrases."""
|
666 |
+
aligned = {}
|
667 |
+
|
668 |
+
for section in lyrics:
|
669 |
+
section_melody = melody[section]
|
670 |
+
section_lyrics = lyrics[section]
|
671 |
+
|
672 |
+
# Adjust melody note durations to match syllables
|
673 |
+
adjusted_melody = self._adjust_melody_to_lyrics(
|
674 |
+
section_melody,
|
675 |
+
self._count_syllables(section_lyrics)
|
676 |
+
)
|
677 |
+
|
678 |
+
aligned[section] = {
|
679 |
+
'lyrics': section_lyrics,
|
680 |
+
'melody': adjusted_melody
|
681 |
+
}
|
682 |
+
|
683 |
+
return aligned
|
684 |
+
|
685 |
+
def example_complete_song():
|
686 |
+
"""
|
687 |
+
Generate and demonstrate a complete song.
|
688 |
+
"""
|
689 |
+
# Initialize components
|
690 |
+
lyrics_gen = EnhancedLyricsGenerator(lyrics_model, tokenizer)
|
691 |
+
melody_gen = MelodyGenerator(melody_model)
|
692 |
+
integrator = SongIntegrator(lyrics_gen, melody_gen)
|
693 |
+
|
694 |
+
# Generate complete song
|
695 |
+
song = integrator.generate_complete_song(
|
696 |
+
prompt="A hopeful song about new beginnings",
|
697 |
+
style={
|
698 |
+
'genre': 'pop',
|
699 |
+
'mood': 'uplifting',
|
700 |
+
'tempo': 'moderate',
|
701 |
+
'complexity': 'medium'
|
702 |
+
}
|
703 |
+
)
|
704 |
+
|
705 |
+
# Evaluate the result
|
706 |
+
evaluator = EnhancedLyricsEvaluator(lyrics_model, tokenizer)
|
707 |
+
evaluation = evaluator.evaluate_comprehensive(
|
708 |
+
song['lyrics'],
|
709 |
+
target_style='pop'
|
710 |
+
)
|
711 |
+
|
712 |
+
return {
|
713 |
+
'song': song,
|
714 |
+
'evaluation': evaluation
|
715 |
+
}
|
716 |
+
|
717 |
+
if __name__ == "__main__":
|
718 |
+
# Run demonstrations
|
719 |
+
lyrics_examples = demonstrate_lyrics_generation()
|
720 |
+
complete_song = example_complete_song()
|
721 |
+
|
722 |
+
# Print results
|
723 |
+
print("Generated Lyrics Examples:")
|
724 |
+
print(json.dumps(lyrics_examples, indent=2))
|
725 |
+
|
726 |
+
print("\nComplete Song Generation:")
|
727 |
+
print(json.dumps(complete_song, indent=2))
|
728 |
+
|
729 |
+
|