File size: 13,202 Bytes
42d18ff
4ed2091
42d18ff
4ed2091
 
 
 
6b94d96
4ed2091
6b94d96
 
4ed2091
 
 
 
 
 
 
dd541ca
4ed2091
 
6b94d96
dd541ca
6b94d96
 
4ed2091
 
 
 
 
6b94d96
dd541ca
 
 
4ed2091
 
 
 
dd541ca
6b94d96
4ed2091
 
 
 
 
 
 
 
6b94d96
4ed2091
 
 
 
6b94d96
 
 
 
 
 
4ed2091
 
6b94d96
 
4ed2091
 
 
 
 
6b94d96
 
4ed2091
 
6b94d96
 
 
4ed2091
 
dd541ca
4ed2091
6b94d96
4ed2091
dd541ca
 
 
 
4ed2091
6b94d96
4ed2091
 
 
 
 
 
 
6b94d96
4ed2091
 
dd541ca
4ed2091
 
6b94d96
4ed2091
dd541ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d9f9dd4
dd541ca
 
 
 
 
 
 
 
 
 
 
 
 
d9f9dd4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2c18e92
 
 
 
 
 
 
 
 
 
 
4ed2091
42d18ff
 
4ed2091
 
 
 
 
 
 
 
 
d9f9dd4
 
 
dd541ca
4ed2091
2c18e92
 
 
 
 
 
4ed2091
dd541ca
d9f9dd4
dd541ca
2c18e92
dd541ca
 
d9f9dd4
dd541ca
 
 
 
d9f9dd4
 
dd541ca
 
d9f9dd4
dd541ca
 
4ed2091
dd541ca
42d18ff
dd541ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42d18ff
 
 
 
dd541ca
d9f9dd4
 
 
42d18ff
 
 
 
 
 
 
 
 
 
dd541ca
d9f9dd4
 
 
4ed2091
 
 
 
 
6b94d96
4ed2091
 
 
6b94d96
4ed2091
 
 
6b94d96
4ed2091
 
 
6b94d96
4ed2091
 
 
6b94d96
4ed2091
 
6b94d96
 
4ed2091
dd541ca
 
 
 
2c18e92
 
 
dd541ca
 
2c18e92
dd541ca
 
 
 
 
2c18e92
 
 
 
 
dd541ca
 
 
 
42d18ff
2c18e92
 
 
 
 
 
 
 
 
 
 
42d18ff
 
4ed2091
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Moody Lyrics</title>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Lora:wght@400;700&family=Playfair+Display:wght@400;700&display=swap');
        body {
            font-family: 'Lora', serif;
            background: linear-gradient(135deg, #f3ec78, #af4261);
            color: #333;
            margin: 0;
            padding: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            transition: background 1s ease-in-out; /* Smooth gradient transition */
        }
        .container {
            background: rgba(255, 255, 255, 0.9);
            padding: 30px;
            border-radius: 20px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
            max-width: 700px;
            width: 100%;
            text-align: center;
            overflow-y: auto;
            max-height: 90vh;
            transition: background-color 0.5s ease-in-out;
            display: flex;
            flex-direction: column;
            gap: 20px;
        }
        h1 {
            color: #2c3e50;
            font-size: 2.5em;
            margin-bottom: 20px;
            font-family: 'Playfair Display', serif;
        }
        form {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 20px;
        }
        input[type="text"] {
            padding: 15px;
            width: 80%;
            border: 2px solid #2c3e50;
            border-radius: 10px;
            font-size: 1em;
            transition: border-color 0.3s;
            font-family: 'Lora', serif;
        }
        input[type="text"]:focus {
            border-color: #2980b9;
            outline: none;
        }
        button {
            padding: 15px 40px;
            background-color: #af4261;
            color: #fff;
            border: none;
            border-radius: 10px;
            font-size: 1.2em;
            cursor: pointer;
            transition: background-color 0.3s, transform 0.3s;
            font-family: 'Lora', serif;
        }
        button:hover {
            background-color: #f3ec78;
            color: #af4261;
            transform: translateY(-2px);
        }
        #mood-result {
            margin-top: 20px;
            font-size: 1.5em;
            color: #2980b9;
        }
        #mood-chart {
            max-width: 100%;
            height: 300px;
        }
        pre {
            background: rgba(244, 244, 249, 0.9);
            padding: 20px;
            border-radius: 10px;
            white-space: pre-wrap;
            word-wrap: break-word;
            margin-top: 20px;
            font-family: 'Courier New', Courier, monospace;
            text-align: left;
            transition: background-color 0.5s ease-in-out;
        }
        #results-container {
            margin-top: 20px;
            padding: 20px;
            border-radius: 10px;
            transition: background-color 0.5s ease-in-out;
        }
        .footer {
            margin-top: 20px;
            font-size: 0.9em;
            color: #555;
            text-align: center;
        }
        #more-info {
            display: none;
            max-height: 0;
            overflow: hidden;
            transition: max-height 0.5s ease-out, opacity 0.5s ease-out;
            opacity: 0;
            text-align: left;
            font-size: 0.9em;
            color: #333;
            padding: 20px;
            border-radius: 10px;
            background: rgba(244, 244, 249, 0.9);
        }
        #more-info.show {
            display: block;
            max-height: 500px; /* Adjust as needed */
            opacity: 1;
        }
        #more-info-link {
            color: #333; /* Match the rest of the text color */
            cursor: pointer;
            text-decoration: underline;
        }
        .spinner {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100px;
            margin-top: 20px;
        }
        .spinner-border {
            width: 3rem;
            height: 3rem;
            border: 0.3em solid rgba(0, 0, 0, 0.1);
            border-radius: 50%;
            border-top-color: #af4261;
            animation: spin 0.75s linear infinite;
        }
        @keyframes spin {
            to { transform: rotate(360deg); }
        }
        #accuracy-test {
            display: none;
            margin-top: 20px;
            text-align: center;
        }
        #accuracy-test button {
            padding: 10px 20px;
            margin: 0 10px;
            font-size: 1em;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Moody Lyrics</h1>
        <form id="mood-form">
            <input type="text" id="title" placeholder="Song Title" required>
            <input type="text" id="artist" placeholder="Artist" required>
            <button type="submit">Predict Mood</button>
        </form>
        <div id="results-container">
            <h2 id="mood-result"></h2>
            <div id="spinner" class="spinner" style="display: none;">
                <div class="spinner-border" role="status"></div>
            </div>
            <canvas id="mood-chart"></canvas>
            <pre id="lyrics"></pre>
            <!-- Feedback and Accuracy Test -->
            <div id="accuracy-test">
                <p>Did the model get the mood right?</p>
                <button id="correct" title="Yes">Yes</button>
                <button id="incorrect" title="No">No</button>
            </div>
        </div>
        <div class="footer">
            This app analyzes song lyrics to predict their mood. Enter a song title and artist, and get mood predictions along with the lyrics. Deployed using Hugging Face Spaces. The model may be inaccurate in some cases.
            <br><br>
            <a href="#" id="more-info-link">Learn more.</a>
            <div id="more-info">
                <p><strong>How it Works:</strong></p>
                <p>This web app utilizes a fine-tuned BERT model to analyze the mood of song lyrics. Upon entering a song title and artist, the app retrieves the song's lyrics from Genius, processes them through the BERT model, and predicts the mood. The model's accuracy is 93% on its test set.</p>
                <p><strong>Technology:</strong></p>
                <ul>
                    <li><strong>Model:</strong> BERT (Bidirectional Encoder Representations from Transformers) fine-tuned on song lyrics to classify moods.</li>
                    <li><strong>Framework:</strong> PyTorch and Hugging Face Transformers library for model training and prediction.</li>
                    <li><strong>Frontend:</strong> HTML, CSS, and JavaScript.</li>
                    <li><strong>Backend:</strong> Flask.</li>
                    <li><strong>Deployment:</strong> The web app is hosted and deployed using Hugging Face Spaces, which provides a simple interface for serving machine learning models.</li>
                </ul>
                <p>For the source code, visit: <a href="https://github.com/ggopalai/moody-lyrics/tree/master" target="_blank">GitHub Repository</a></p>
            </div>
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script>
        // Initialize the chart with zero probabilities
        const ctx = document.getElementById('mood-chart').getContext('2d');
        let moodChart = new Chart(ctx, {
            type: 'bar',
            data: {
                labels: ['Angry', 'Happy', 'Relaxed', 'Sad'],
                datasets: [{
                    label: 'Mood Probabilities',
                    data: [0, 0, 0, 0], // Initial data
                    backgroundColor: [
                        'rgba(255, 99, 132, 0.2)',
                        'rgba(255, 159, 64, 0.2)',
                        'rgba(75, 192, 192, 0.2)',
                        'rgba(153, 102, 255, 0.2)'
                    ],
                    borderColor: [
                        'rgba(255, 99, 132, 1)',
                        'rgba(255, 159, 64, 1)',
                        'rgba(75, 192, 192, 1)',
                        'rgba(153, 102, 255, 1)'
                    ],
                    borderWidth: 1
                }]
            },
            options: {
                scales: {
                    y: {
                        beginAtZero: true,
                        min: 0,
                        max: 1,
                        ticks: {
                            stepSize: 0.1,
                            callback: function(value) { return value.toFixed(1); } // Format y-axis labels
                        }
                    }
                }
            }
        });

        document.getElementById('mood-form').addEventListener('submit', async function(event) {
            event.preventDefault();
            const title = document.getElementById('title').value;
            const artist = document.getElementById('artist').value;

            // Show the spinner
            document.getElementById('spinner').style.display = 'flex';

            const response = await fetch('/predict', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ title, artist }),
            });
            const data = await response.json();
            document.getElementById('mood-result').innerText = `Predicted Mood: ${data.mood}`;
            document.getElementById('lyrics').innerText = data.lyrics;

            // Hide the spinner
            document.getElementById('spinner').style.display = 'none';

            // Scroll to the top of the container to ensure the form remains visible
            document.querySelector('.container').scrollTop = 0;

            // Change background color based on the predicted mood
            const resultsContainer = document.getElementById('results-container');
            const body = document.body;
            switch (data.mood.toLowerCase()) {
                case 'happy':
                    resultsContainer.style.backgroundColor = '#fff9c4'; // light yellow
                    body.style.background = 'linear-gradient(135deg, #fff9c4, #f39c12)'; // gradient for happy
                    break;
                case 'angry':
                    resultsContainer.style.backgroundColor = '#ffcdd2'; // light red
                    body.style.background = 'linear-gradient(135deg, #ffcdd2, #e74c3c)'; // gradient for angry
                    break;
                case 'relaxed':
                    resultsContainer.style.backgroundColor = '#c8e6c9'; // light green
                    body.style.background = 'linear-gradient(135deg, #c8e6c9, #27ae60)'; // gradient for relaxed
                    break;
                case 'sad':
                    resultsContainer.style.backgroundColor = '#bbdefb'; // light blue
                    body.style.background = 'linear-gradient(135deg, #bbdefb, #3498db)'; // gradient for sad
                    break;
                default:
                    resultsContainer.style.backgroundColor = 'rgba(244, 244, 249, 0.9)'; // default background color
                    body.style.background = 'linear-gradient(135deg, #f3ec78, #af4261)'; // default gradient
            }

            // Update chart with new data
            moodChart.data.datasets[0].data = data.probabilities || [0, 0, 0, 0];
            moodChart.update();

            // Show feedback section
            document.getElementById('accuracy-test').style.display = 'block';
        });

        // Toggle detailed description with smooth effect and scroll to it
        document.getElementById('more-info-link').addEventListener('click', function(event) {
            event.preventDefault();
            const moreInfo = document.getElementById('more-info');
            if (moreInfo.style.display === 'none' || !moreInfo.style.display) {
                moreInfo.style.display = 'block';
                setTimeout(() => {
                    moreInfo.classList.add('show');
                    // Scroll to the more-info section smoothly
                    moreInfo.scrollIntoView({ behavior: 'smooth', block: 'start' });
                }, 10);
            } else {
                moreInfo.classList.remove('show');
                setTimeout(() => moreInfo.style.display = 'none', 500);
            }
        });

        // Handle accuracy test
        document.getElementById('correct').addEventListener('click', function() {
            alert('Thank you for the feedback!');
            // Send correct feedback to the server if needed
        });

        document.getElementById('incorrect').addEventListener('click', function() {
            alert('Thank you for the feedback!');
            // Send incorrect feedback to the server if needed
        });
    </script>
</body>
</html>