SMA-RAG/visu_new.html

643 lines
23 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Distanzmetriken Visualisierung</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
background: #f5f5f5;
padding: 20px;
line-height: 1.6;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.header {
text-align: center;
margin-bottom: 40px;
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
margin-bottom: 10px;
}
.subtitle {
color: #666;
}
.highlight-box {
background: #fff3cd;
border-left: 4px solid #ffc107;
padding: 20px;
margin: 20px 0;
border-radius: 8px;
}
.highlight-box h3 {
color: #856404;
margin-bottom: 10px;
}
.highlight-box p {
color: #856404;
font-size: 14px;
}
.controls {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 30px;
}
.controls h2 {
margin-bottom: 20px;
color: #333;
}
.control-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 40px;
}
.vector-controls {
padding: 20px;
border-radius: 8px;
}
.vector-controls.v1 {
background: #e3f2fd;
border-left: 4px solid #2196F3;
}
.vector-controls.v2 {
background: #ffebee;
border-left: 4px solid #f44336;
}
.vector-controls h3 {
margin-bottom: 20px;
}
.control-group {
margin-bottom: 20px;
}
.control-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: #555;
}
input[type="range"] {
width: 100%;
height: 6px;
border-radius: 3px;
background: #ddd;
outline: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #2196F3;
cursor: pointer;
}
.viz-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-bottom: 30px;
}
.viz-card {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.viz-card h3 {
margin-bottom: 20px;
font-size: 24px;
}
.viz-card.euclidean h3 {
color: #9c27b0;
}
.viz-card.cosine h3 {
color: #4caf50;
}
.svg-container {
background: #fafafa;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 10px;
margin-bottom: 20px;
}
.result-box {
padding: 20px;
border-radius: 8px;
margin-top: 20px;
}
.result-box.euclidean {
background: #f3e5f5;
border-left: 4px solid #9c27b0;
}
.result-box.cosine {
background: #e8f5e9;
border-left: 4px solid #4caf50;
}
.formula {
font-family: 'Courier New', monospace;
font-size: 14px;
margin-bottom: 10px;
padding: 10px;
background: rgba(0,0,0,0.05);
border-radius: 4px;
}
.result-value {
font-size: 24px;
font-weight: bold;
margin: 10px 0;
}
.result-box.euclidean .result-value {
color: #9c27b0;
}
.result-box.cosine .result-value {
color: #4caf50;
}
.interpretation {
margin-top: 15px;
padding: 15px;
background: white;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
}
.interpretation.similar {
color: #2e7d32;
border: 2px solid #4caf50;
}
.interpretation.dissimilar {
color: #c62828;
border: 2px solid #f44336;
}
.interpretation.medium {
color: #f57c00;
border: 2px solid #ff9800;
}
.description {
font-size: 14px;
color: #666;
margin-top: 10px;
}
.comparison {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.comparison h2 {
margin-bottom: 20px;
color: #333;
}
.comparison-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
}
.comparison-box {
padding: 20px;
border-radius: 8px;
}
.comparison-box.euclidean {
background: #f3e5f5;
border-left: 4px solid #9c27b0;
}
.comparison-box.cosine {
background: #e8f5e9;
border-left: 4px solid #4caf50;
}
.comparison-box h3 {
margin-bottom: 15px;
}
.comparison-box ul {
list-style: none;
padding-left: 0;
}
.comparison-box li {
padding: 5px 0;
font-size: 14px;
}
.quick-test {
background: #e3f2fd;
padding: 20px;
border-radius: 10px;
margin-bottom: 30px;
border-left: 4px solid #2196F3;
}
.quick-test h3 {
color: #1565c0;
margin-bottom: 15px;
}
.test-buttons {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.test-btn {
padding: 10px 20px;
background: #2196F3;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s;
}
.test-btn:hover {
background: #1976d2;
}
@media (max-width: 968px) {
.control-grid, .viz-grid, .comparison-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Distanzmetriken für Vektorvergleiche</h1>
<p class="subtitle">Interaktive Visualisierung der mathematischen Konzepte</p>
</div>
<div class="highlight-box">
<h3>Der entscheidende Unterschied!</h3>
<p><strong>Teste die Szenarien unten:</strong> Wenn beide Vektoren die gleiche Richtung haben (gleicher Winkel),
aber unterschiedlich lang sind, zeigt Kosinus perfekte Ähnlichkeit (1.0), während Euklidisch eine große Distanz (= wenig bis mittlere Ähnlichkeit) gemessen wird.</p>
</div>
<div class="quick-test">
<h3>Schnell-Test:</h3>
<div class="test-buttons">
<button class="test-btn" onclick="testScenario('same-direction')">
Gleiche Richtung, unterschiedliche Länge
</button>
<button class="test-btn" onclick="testScenario('different-direction')">
Unterschiedliche Richtung, gleiche Länge
</button>
<button class="test-btn" onclick="testScenario('orthogonal')">
Orthogonal (90° Unterschied)
</button>
<button class="test-btn" onclick="testScenario('opposite')">
Fast entgegengesetzt
</button>
</div>
</div>
<div class="controls">
<h2>Vektoreinstellungen</h2>
<div class="control-grid">
<div class="vector-controls v1">
<h3>Vektor 1 (Blau)</h3>
<div class="control-group">
<label>Winkel: <span id="angle1-value">30</span>°</label>
<input type="range" id="angle1" min="0" max="180" value="30">
</div>
<div class="control-group">
<label>Länge: <span id="length1-value">80</span></label>
<input type="range" id="length1" min="20" max="100" value="80">
</div>
</div>
<div class="vector-controls v2">
<h3>Vektor 2 (Rot)</h3>
<div class="control-group">
<label>Winkel: <span id="angle2-value">60</span>°</label>
<input type="range" id="angle2" min="0" max="180" value="60">
</div>
<div class="control-group">
<label>Länge: <span id="length2-value">60</span></label>
<input type="range" id="length2" min="20" max="100" value="60">
</div>
</div>
</div>
</div>
<div class="viz-grid">
<div class="viz-card euclidean">
<h3>Euklidische Distanz</h3>
<div class="svg-container">
<svg id="euclidean-svg" width="100%" height="300" viewBox="-20 -220 260 260"></svg>
</div>
<div class="result-box euclidean">
<div class="formula">d = √[(x₂-x₁)² + (y₂-y₁)²]</div>
<div class="result-value">Distanz: <span id="euclidean-result">0.00</span></div>
<div class="interpretation" id="euclidean-interpretation"></div>
<div class="description">
Misst die <strong>direkte räumliche Entfernung</strong> zwischen den Vektorendpunkten.
Berücksichtigt sowohl Richtung als auch Länge der Vektoren.
</div>
</div>
</div>
<div class="viz-card cosine">
<h3>Kosinus-Ähnlichkeit</h3>
<div class="svg-container">
<svg id="cosine-svg" width="100%" height="300" viewBox="-20 -220 260 260"></svg>
</div>
<div class="result-box cosine">
<div class="formula">cos(θ) = (v1 · v2) / (||v1|| × ||v2||)</div>
<div class="result-value">Ähnlichkeit: <span id="cosine-result">0.0000</span></div>
<div class="interpretation" id="cosine-interpretation"></div>
<div class="description">
Misst nur den <strong>Winkel zwischen Vektoren</strong>, ignoriert die Länge komplett!
Perfekt für Text-Embeddings: "Hund" und "großer Hund" haben gleiche Semantik (Richtung),
auch wenn unterschiedlich lang.
</div>
<div class="description" style="margin-top: 10px; font-size: 12px;">
1.0 = gleiche Richtung | 0.0 = orthogonal (90°) | -1.0 = entgegengesetzt (180°)
</div>
</div>
</div>
</div>
<div class="comparison">
<h2>Wann welche Metrik verwenden?</h2>
<div class="comparison-grid">
<div class="comparison-box euclidean">
<h3>Euklidische Distanz</h3>
<ul>
<li>✓ Geografische Koordinaten</li>
<li>✓ Physikalische Messungen</li>
<li>✓ Pixelwerte in Bildern</li>
<li>✓ Wenn absolute Position wichtig ist</li>
<li>✓ Wenn Magnitude (Länge) relevant ist</li>
<li>✗ Nicht ideal für hochdimensionale Daten</li>
</ul>
</div>
<div class="comparison-box cosine">
<h3>Kosinus-Ähnlichkeit</h3>
<ul>
<li>✓ Text-Embeddings (Semantik zählt, nicht Länge)</li>
<li>✓ Dokumentenähnlichkeit</li>
<li>✓ Empfehlungssysteme</li>
<li>✓ Wenn nur Richtung wichtig ist</li>
<li>✓ Robust gegenüber Skalierung</li>
<li>✓ Ideal für normalisierte Daten</li>
</ul>
</div>
</div>
</div>
</div>
<script>
const scale = 2;
function toRadians(deg) {
return deg * Math.PI / 180;
}
function testScenario(scenario) {
const scenarios = {
'same-direction': { a1: 45, l1: 90, a2: 45, l2: 30 },
'different-direction': { a1: 30, l1: 70, a2: 90, l2: 70 },
'orthogonal': { a1: 0, l1: 70, a2: 90, l2: 70 },
'opposite': { a1: 20, l1: 70, a2: 160, l2: 70 }
};
const s = scenarios[scenario];
document.getElementById('angle1').value = s.a1;
document.getElementById('length1').value = s.l1;
document.getElementById('angle2').value = s.a2;
document.getElementById('length2').value = s.l2;
update();
}
function getEuclideanInterpretation(dist, maxDist = 200) {
const normalized = dist / maxDist;
if (normalized < 0.2) {
return { class: 'similar', text: '✓ Sehr ähnlich (kleine Distanz)' };
} else if (normalized < 0.5) {
return { class: 'medium', text: '~ Mittlere Ähnlichkeit' };
} else {
return { class: 'dissimilar', text: '✗ Sehr unterschiedlich (große Distanz)' };
}
}
function getCosineInterpretation(similarity) {
if (similarity > 0.9) {
return { class: 'similar', text: '✓ Sehr ähnlich (fast gleiche Richtung)' };
} else if (similarity > 0.5) {
return { class: 'medium', text: '~ Mittlere Ähnlichkeit' };
} else if (similarity > 0) {
return { class: 'dissimilar', text: '✗ Unterschiedlich (großer Winkel)' };
} else if (similarity > -0.5) {
return { class: 'dissimilar', text: '✗ Sehr unterschiedlich (> 90°)' };
} else {
return { class: 'dissimilar', text: '✗✗ Fast entgegengesetzt!' };
}
}
function drawEuclideanViz(v1x, v1y, v2x, v2y) {
const svg = document.getElementById('euclidean-svg');
svg.innerHTML = `
<!-- Koordinatensystem -->
<line x1="0" y1="0" x2="220" y2="0" stroke="#ddd" stroke-width="1" />
<line x1="0" y1="0" x2="0" y2="-220" stroke="#ddd" stroke-width="1" />
<!-- Gitter -->
${[50, 100, 150, 200].map(i => `
<line x1="${i}" y1="0" x2="${i}" y2="-200" stroke="#f0f0f0" stroke-width="0.5" />
<line x1="0" y1="${-i}" x2="200" y2="${-i}" stroke="#f0f0f0" stroke-width="0.5" />
`).join('')}
<!-- Distanzlinie -->
<line x1="${v1x * scale}" y1="${-v1y * scale}"
x2="${v2x * scale}" y2="${-v2y * scale}"
stroke="purple" stroke-width="3" stroke-dasharray="5,5" />
<!-- Vektor 1 -->
<defs>
<marker id="arrowBlue" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
<path d="M0,0 L0,6 L9,3 z" fill="blue" />
</marker>
<marker id="arrowRed" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
<path d="M0,0 L0,6 L9,3 z" fill="red" />
</marker>
</defs>
<line x1="0" y1="0" x2="${v1x * scale}" y2="${-v1y * scale}"
stroke="blue" stroke-width="3" marker-end="url(#arrowBlue)" />
<!-- Vektor 2 -->
<line x1="0" y1="0" x2="${v2x * scale}" y2="${-v2y * scale}"
stroke="red" stroke-width="3" marker-end="url(#arrowRed)" />
<!-- Endpunkte -->
<circle cx="${v1x * scale}" cy="${-v1y * scale}" r="4" fill="blue" />
<circle cx="${v2x * scale}" cy="${-v2y * scale}" r="4" fill="red" />
<!-- Labels -->
<text x="${v1x * scale + 10}" y="${-v1y * scale}" fill="blue" font-size="14" font-weight="bold">v1</text>
<text x="${v2x * scale + 10}" y="${-v2y * scale}" fill="red" font-size="14" font-weight="bold">v2</text>
<text x="${(v1x + v2x) * scale / 2}" y="${-(v1y + v2y) * scale / 2 - 10}" fill="purple" font-size="12" font-weight="bold">d</text>
`;
}
function drawCosineViz(v1x, v1y, v2x, v2y, angle1, angle2) {
const svg = document.getElementById('cosine-svg');
const angleDiff = Math.abs(angle2 - angle1);
// Winkelbogen berechnen
const arcRadius = 30;
const startAngle = toRadians(Math.min(angle1, angle2));
const endAngle = toRadians(Math.max(angle1, angle2));
const largeArc = angleDiff > 180 ? 1 : 0;
const sweep = 1;
svg.innerHTML = `
<!-- Koordinatensystem -->
<line x1="0" y1="0" x2="220" y2="0" stroke="#ddd" stroke-width="1" />
<line x1="0" y1="0" x2="0" y2="-220" stroke="#ddd" stroke-width="1" />
<!-- Gitter -->
${[50, 100, 150, 200].map(i => `
<line x1="${i}" y1="0" x2="${i}" y2="-200" stroke="#f0f0f0" stroke-width="0.5" />
<line x1="0" y1="${-i}" x2="200" y2="${-i}" stroke="#f0f0f0" stroke-width="0.5" />
`).join('')}
<!-- Winkelbogen -->
<path d="M ${arcRadius * Math.cos(startAngle)} ${-arcRadius * Math.sin(startAngle)}
A ${arcRadius} ${arcRadius} 0 ${largeArc} ${sweep}
${arcRadius * Math.cos(endAngle)} ${-arcRadius * Math.sin(endAngle)}"
fill="none" stroke="green" stroke-width="2" />
<!-- Vektoren -->
<defs>
<marker id="arrowBlue2" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
<path d="M0,0 L0,6 L9,3 z" fill="blue" />
</marker>
<marker id="arrowRed2" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
<path d="M0,0 L0,6 L9,3 z" fill="red" />
</marker>
</defs>
<line x1="0" y1="0" x2="${v1x * scale}" y2="${-v1y * scale}"
stroke="blue" stroke-width="3" marker-end="url(#arrowBlue2)" />
<line x1="0" y1="0" x2="${v2x * scale}" y2="${-v2y * scale}"
stroke="red" stroke-width="3" marker-end="url(#arrowRed2)" />
<!-- Labels -->
<text x="${v1x * scale + 10}" y="${-v1y * scale}" fill="blue" font-size="14" font-weight="bold">v1</text>
<text x="${v2x * scale + 10}" y="${-v2y * scale}" fill="red" font-size="14" font-weight="bold">v2</text>
<text x="45" y="-45" fill="green" font-size="12" font-weight="bold">θ = ${angleDiff.toFixed(1)}°</text>
`;
}
function update() {
const angle1 = parseFloat(document.getElementById('angle1').value);
const length1 = parseFloat(document.getElementById('length1').value);
const angle2 = parseFloat(document.getElementById('angle2').value);
const length2 = parseFloat(document.getElementById('length2').value);
// Update labels
document.getElementById('angle1-value').textContent = angle1;
document.getElementById('length1-value').textContent = length1;
document.getElementById('angle2-value').textContent = angle2;
document.getElementById('length2-value').textContent = length2;
// Berechne kartesische Koordinaten
const v1x = length1 * Math.cos(toRadians(angle1));
const v1y = length1 * Math.sin(toRadians(angle1));
const v2x = length2 * Math.cos(toRadians(angle2));
const v2y = length2 * Math.sin(toRadians(angle2));
// Euklidische Distanz
const euclidean = Math.sqrt(Math.pow(v2x - v1x, 2) + Math.pow(v2y - v1y, 2));
document.getElementById('euclidean-result').textContent = euclidean.toFixed(2);
const eucInterpret = getEuclideanInterpretation(euclidean);
const eucInterpretEl = document.getElementById('euclidean-interpretation');
eucInterpretEl.textContent = eucInterpret.text;
eucInterpretEl.className = 'interpretation ' + eucInterpret.class;
// Kosinus-Ähnlichkeit
const dotProduct = v1x * v2x + v1y * v2y;
const norm1 = Math.sqrt(v1x * v1x + v1y * v1y);
const norm2 = Math.sqrt(v2x * v2x + v2y * v2y);
const cosine = dotProduct / (norm1 * norm2);
document.getElementById('cosine-result').textContent = cosine.toFixed(4);
const cosInterpret = getCosineInterpretation(cosine);
const cosInterpretEl = document.getElementById('cosine-interpretation');
cosInterpretEl.textContent = cosInterpret.text;
cosInterpretEl.className = 'interpretation ' + cosInterpret.class;
// Zeichne Visualisierungen
drawEuclideanViz(v1x, v1y, v2x, v2y);
drawCosineViz(v1x, v1y, v2x, v2y, angle1, angle2);
}
// Event Listeners
document.getElementById('angle1').addEventListener('input', update);
document.getElementById('length1').addEventListener('input', update);
document.getElementById('angle2').addEventListener('input', update);
document.getElementById('length2').addEventListener('input', update);
// Initial draw
update();
</script>
</body>
</html>