Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>Data Labeling Tic Tac Toe</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| </head> | |
| <body class="bg-gradient-to-br from-indigo-50 via-sky-50 to-teal-100 min-h-screen flex items-center justify-center p-4"> | |
| <div class="max-w-6xl w-full bg-white rounded-2xl shadow-xl p-6"> | |
| <h1 class="text-3xl font-extrabold mb-3 text-center text-indigo-800">Data Labeling Tic Tac Toe</h1> | |
| <p class="text-center mb-2 text-sm text-gray-600"> | |
| Reference: <a href="https://www.linkedin.com/pulse/data-labelling-michael-lively-ofhle/" class="text-indigo-600 hover:underline" target="_blank" rel="noopener noreferrer">Data Labelling: The Ghost in the Machine</a> | |
| </p> | |
| <p class="text-center mb-4 text-sm text-gray-500"> | |
| Answer correctly to claim a square. Miss the question, and your turn passes. | |
| </p> | |
| <div id="banner" class="hidden bg-indigo-100 border border-indigo-300 text-indigo-800 font-bold text-center py-2 rounded mb-4"></div> | |
| <div class="flex flex-col lg:flex-row items-center lg:items-start justify-center gap-5"> | |
| <!-- Game Board --> | |
| <div id="board" class="grid grid-cols-3 gap-2 border-4 border-indigo-300 rounded-xl p-3 bg-indigo-50"></div> | |
| <!-- Question Panel --> | |
| <div id="questionPanel" class="w-full lg:w-1/2 bg-gray-50 rounded-xl shadow p-5 min-h-80"> | |
| <h2 id="panelQuestion" class="text-xl font-semibold text-gray-800">Select a square to view the question</h2> | |
| <ul id="panelChoices" class="mt-4 space-y-2"></ul> | |
| <p id="panelHint" class="mt-3 text-sm text-gray-600 hidden"></p> | |
| <button id="hintBtn" class="mt-4 px-3 py-1 bg-gray-200 text-gray-700 rounded hover:bg-gray-300">Show Hint</button> | |
| </div> | |
| </div> | |
| <div class="flex flex-col sm:flex-row justify-between items-center gap-3 mt-6"> | |
| <p id="status" class="text-lg font-medium text-gray-800"></p> | |
| <button id="restartBtn" class="px-4 py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700">Restart</button> | |
| </div> | |
| </div> | |
| <script> | |
| const questions = [ | |
| { | |
| question: 'What is the main purpose of data labeling in supervised machine learning?', | |
| choices: [ | |
| 'To compress datasets before training', | |
| 'To give models labeled examples they can learn from', | |
| 'To remove all categorical data from a dataset', | |
| 'To increase the number of model parameters' | |
| ], | |
| answer: 2, | |
| hint: 'Labels provide the ground truth used during training.' | |
| }, | |
| { | |
| question: 'Which type of image labeling assigns a single class to an entire image?', | |
| choices: [ | |
| 'Image classification', | |
| 'Object detection', | |
| 'Semantic segmentation', | |
| 'Speaker identification' | |
| ], | |
| answer: 1, | |
| hint: 'The whole image receives one label.' | |
| }, | |
| { | |
| question: 'Which image labeling method uses bounding boxes around objects?', | |
| choices: [ | |
| 'Image classification', | |
| 'Object detection', | |
| 'Sentiment analysis', | |
| 'Label encoding' | |
| ], | |
| answer: 2, | |
| hint: 'The model must learn both what the object is and where it is.' | |
| }, | |
| { | |
| question: 'Semantic segmentation labels what part of an image?', | |
| choices: [ | |
| 'Only the image filename', | |
| 'Only the largest object', | |
| 'Every pixel with a class', | |
| 'Only the image metadata' | |
| ], | |
| answer: 3, | |
| hint: 'This is more detailed than a bounding box.' | |
| }, | |
| { | |
| question: 'Named Entity Recognition is used to label which kind of information?', | |
| choices: [ | |
| 'Names, dates, organizations, and locations in text', | |
| 'Pixels in an image', | |
| 'Temperature values in a spreadsheet', | |
| 'Audio volume levels' | |
| ], | |
| answer: 1, | |
| hint: 'NER is a text labeling task.' | |
| }, | |
| { | |
| question: 'What does Inter-Annotator Agreement measure?', | |
| choices: [ | |
| 'How fast a model trains', | |
| 'How much different annotators agree on labels', | |
| 'How many GPUs are used', | |
| 'How large a dataset is after compression' | |
| ], | |
| answer: 2, | |
| hint: 'It checks consistency between human labelers.' | |
| }, | |
| { | |
| question: 'Why is high Inter-Annotator Agreement important?', | |
| choices: [ | |
| 'It means the labels are likely more consistent and dependable', | |
| 'It guarantees the model will never fail', | |
| 'It removes the need for preprocessing', | |
| 'It makes numerical scaling unnecessary' | |
| ], | |
| answer: 1, | |
| hint: 'Models learn better from consistent labels.' | |
| }, | |
| { | |
| question: 'Which agreement metric is especially useful because it can handle missing labels and different numbers of annotators?', | |
| choices: [ | |
| 'Percent agreement', | |
| 'Krippendorff\'s Alpha', | |
| 'Min-max scaling', | |
| 'One-hot encoding' | |
| ], | |
| answer: 2, | |
| hint: 'It is one of the most robust agreement measures.' | |
| }, | |
| { | |
| question: 'Why can percent agreement be misleading as a labeling quality metric?', | |
| choices: [ | |
| 'It ignores agreement that may happen by chance', | |
| 'It only works on image files', | |
| 'It requires ratio data', | |
| 'It removes missing labels automatically' | |
| ], | |
| answer: 1, | |
| hint: 'Some annotators can agree randomly.' | |
| }, | |
| { | |
| question: 'Which data type has ordered values but no true zero point?', | |
| choices: [ | |
| 'Nominal data', | |
| 'Interval data', | |
| 'Binary data', | |
| 'Object data' | |
| ], | |
| answer: 2, | |
| hint: 'Temperature in Celsius is a common example.' | |
| }, | |
| { | |
| question: 'Which data type has a meaningful zero and supports meaningful ratios?', | |
| choices: [ | |
| 'Ratio data', | |
| 'Nominal data', | |
| 'Ordinal data', | |
| 'Interval data' | |
| ], | |
| answer: 1, | |
| hint: 'Height, weight, and revenue are examples.' | |
| }, | |
| { | |
| question: 'Movie ratings such as poor, fair, good, and excellent are examples of what data type?', | |
| choices: [ | |
| 'Nominal data', | |
| 'Ordinal data', | |
| 'Ratio data', | |
| 'Continuous data' | |
| ], | |
| answer: 2, | |
| hint: 'The categories have a meaningful order.' | |
| }, | |
| { | |
| question: 'One-hot encoding converts categorical data into what format?', | |
| choices: [ | |
| 'A binary vector', | |
| 'A text paragraph', | |
| 'A bounding box', | |
| 'A continuous audio signal' | |
| ], | |
| answer: 1, | |
| hint: 'Each category becomes a separate 0/1 indicator.' | |
| }, | |
| { | |
| question: 'Why is scaling numerical data important before modeling?', | |
| choices: [ | |
| 'It prevents large-range features from dominating the model', | |
| 'It removes all labels from the dataset', | |
| 'It changes text into images', | |
| 'It guarantees perfect accuracy' | |
| ], | |
| answer: 1, | |
| hint: 'Think about a feature ranging from 0 to 1 versus one ranging from 0 to 1000.' | |
| }, | |
| { | |
| question: 'Standardization usually transforms numerical data to have what?', | |
| choices: [ | |
| 'A mean of 0 and standard deviation of 1', | |
| 'Only values of true and false', | |
| 'Only text labels', | |
| 'A maximum value of exactly 100' | |
| ], | |
| answer: 1, | |
| hint: 'This is a common scaling technique.' | |
| }, | |
| { | |
| question: 'Which human factor can directly reduce labeling consistency over time?', | |
| choices: [ | |
| 'Fatigue and boredom', | |
| 'Higher screen resolution', | |
| 'More storage space', | |
| 'Faster network speed' | |
| ], | |
| answer: 1, | |
| hint: 'Human attention is limited.' | |
| }, | |
| { | |
| question: 'What is a major risk when labeling guidelines are unclear?', | |
| choices: [ | |
| 'Annotators may apply labels inconsistently', | |
| 'The dataset becomes too small to store', | |
| 'All labels become numerical automatically', | |
| 'The model trains without data' | |
| ], | |
| answer: 1, | |
| hint: 'Clear rules improve shared interpretation.' | |
| }, | |
| { | |
| question: 'What is the goal of training and calibration sessions for annotators?', | |
| choices: [ | |
| 'To align annotators on the same labeling standard', | |
| 'To increase file size', | |
| 'To remove quality control', | |
| 'To avoid checking disagreements' | |
| ], | |
| answer: 1, | |
| hint: 'Calibration improves consistency.' | |
| }, | |
| { | |
| question: 'What is the main tradeoff in the paradox of big data for labeling?', | |
| choices: [ | |
| 'More data can help generalization, but poor consistency can hurt precision', | |
| 'More data always guarantees better labels', | |
| 'Smaller data always removes bias', | |
| 'Bigger datasets do not need quality control' | |
| ], | |
| answer: 1, | |
| hint: 'Size does not automatically mean quality.' | |
| }, | |
| { | |
| question: 'Why do inconsistent labels confuse machine learning models?', | |
| choices: [ | |
| 'They give the model conflicting patterns to learn', | |
| 'They reduce monitor brightness', | |
| 'They make all features ratio data', | |
| 'They eliminate the need for testing' | |
| ], | |
| answer: 1, | |
| hint: 'The model learns from the labels it is given.' | |
| } | |
| ]; | |
| let currentPlayer, boardState, cellQuestions, selectedCell; | |
| const boardEl = document.getElementById('board'); | |
| const statusEl = document.getElementById('status'); | |
| const bannerEl = document.getElementById('banner'); | |
| const hintBtn = document.getElementById('hintBtn'); | |
| const panelQuestion = document.getElementById('panelQuestion'); | |
| const panelChoices = document.getElementById('panelChoices'); | |
| const panelHint = document.getElementById('panelHint'); | |
| const restartBtn = document.getElementById('restartBtn'); | |
| function shuffle(arr) { | |
| for (let i = arr.length - 1; i > 0; i--) { | |
| const j = Math.floor(Math.random() * (i + 1)); | |
| [arr[i], arr[j]] = [arr[j], arr[i]]; | |
| } | |
| return arr; | |
| } | |
| function buildBalancedPositions(nQuestions, kChoices) { | |
| const base = Math.floor(nQuestions / kChoices); | |
| const remainder = nQuestions % kChoices; | |
| const slots = []; | |
| for (let pos = 1; pos <= kChoices; pos++) { | |
| for (let c = 0; c < base; c++) slots.push(pos); | |
| } | |
| const extra = shuffle(Array.from({ length: kChoices }, (_, i) => i + 1)).slice(0, remainder); | |
| slots.push(...extra); | |
| return shuffle(slots); | |
| } | |
| function initGame() { | |
| currentPlayer = 'X'; | |
| boardState = Array(9).fill(''); | |
| selectedCell = null; | |
| bannerEl.classList.add('hidden'); | |
| statusEl.innerText = ''; | |
| const shuffledQs = shuffle([...questions]).slice(0, 9); | |
| const kChoices = 4; | |
| const targetPositions = buildBalancedPositions(shuffledQs.length, kChoices); | |
| cellQuestions = shuffledQs.map((q, idx) => { | |
| const originalCorrectIdx0 = q.answer - 1; | |
| const correctText = q.choices[originalCorrectIdx0]; | |
| const wrongs = q.choices.filter((_, i) => i !== originalCorrectIdx0); | |
| shuffle(wrongs); | |
| const targetPos1 = targetPositions[idx]; | |
| const targetPos0 = targetPos1 - 1; | |
| const newChoices = Array(kChoices).fill(null); | |
| newChoices[targetPos0] = correctText; | |
| let w = 0; | |
| for (let i = 0; i < kChoices; i++) { | |
| if (newChoices[i] === null) newChoices[i] = wrongs[w++]; | |
| } | |
| return { | |
| question: q.question, | |
| choices: newChoices, | |
| answer: targetPos1, | |
| hint: q.hint | |
| }; | |
| }); | |
| panelQuestion.innerText = 'Select a square to view the question'; | |
| panelChoices.innerHTML = ''; | |
| panelHint.classList.add('hidden'); | |
| panelHint.innerText = ''; | |
| renderBoard(); | |
| } | |
| function renderBoard() { | |
| boardEl.innerHTML = ''; | |
| boardState.forEach((mark, idx) => { | |
| const btn = document.createElement('button'); | |
| let cls = 'bg-white h-24 w-24 sm:h-28 sm:w-28 flex items-center justify-center text-4xl font-extrabold rounded-lg shadow hover:bg-indigo-100 transition'; | |
| if (mark === 'X') cls += ' text-blue-600'; | |
| if (mark === 'O') cls += ' text-red-600'; | |
| btn.className = cls; | |
| btn.innerText = mark; | |
| btn.disabled = mark !== ''; | |
| btn.addEventListener('click', () => openQuestion(idx)); | |
| boardEl.appendChild(btn); | |
| }); | |
| statusEl.innerText = `Current: ${currentPlayer}`; | |
| } | |
| function openQuestion(idx) { | |
| if (boardState[idx] !== '') return; | |
| selectedCell = idx; | |
| const q = cellQuestions[idx]; | |
| panelQuestion.innerText = q.question; | |
| panelChoices.innerHTML = ''; | |
| panelHint.classList.add('hidden'); | |
| panelHint.innerText = q.hint; | |
| q.choices.forEach((choiceText, i) => { | |
| const li = document.createElement('li'); | |
| const btn = document.createElement('button'); | |
| btn.innerText = choiceText; | |
| btn.className = 'w-full text-left px-4 py-2 bg-indigo-50 rounded hover:bg-indigo-100 border border-indigo-100'; | |
| btn.addEventListener('click', () => handleAnswer(i + 1)); | |
| li.appendChild(btn); | |
| panelChoices.appendChild(li); | |
| }); | |
| } | |
| function handleAnswer(choice) { | |
| if (selectedCell === null) return; | |
| const q = cellQuestions[selectedCell]; | |
| if (choice === q.answer) { | |
| boardState[selectedCell] = currentPlayer; | |
| panelChoices.innerHTML = ''; | |
| panelHint.classList.add('hidden'); | |
| panelQuestion.innerText = 'Correct! Select another open square.'; | |
| renderBoard(); | |
| if (checkWin()) return endGame(`${currentPlayer} wins!`); | |
| if (boardState.every(c => c)) return endGame('Stalemate!'); | |
| } else { | |
| alert('Incorrect! Turn missed.'); | |
| } | |
| currentPlayer = currentPlayer === 'X' ? 'O' : 'X'; | |
| statusEl.innerText = `Current: ${currentPlayer}`; | |
| selectedCell = null; | |
| } | |
| function checkWin() { | |
| const wins = [ | |
| [0,1,2], [3,4,5], [6,7,8], | |
| [0,3,6], [1,4,7], [2,5,8], | |
| [0,4,8], [2,4,6] | |
| ]; | |
| return wins.some(combo => combo.every(i => boardState[i] === currentPlayer)); | |
| } | |
| function endGame(msg) { | |
| bannerEl.innerText = `🎉 ${msg}`; | |
| bannerEl.classList.remove('hidden'); | |
| document.querySelectorAll('#board button').forEach(b => b.disabled = true); | |
| panelQuestion.innerText = msg; | |
| panelChoices.innerHTML = ''; | |
| panelHint.classList.add('hidden'); | |
| } | |
| hintBtn.addEventListener('click', () => { | |
| if (selectedCell !== null) panelHint.classList.toggle('hidden'); | |
| }); | |
| restartBtn.addEventListener('click', initGame); | |
| initGame(); | |
| </script> | |
| </body> | |
| </html> | |