Files
test-repo/docs/.vitepress/theme/components/appendix/vlm-intro/ProjectorDemo.vue
T
sanbuphy d35211071a style: update border-radius and padding values across components
- standardize border-radius from 8px to 6px for consistent styling
- adjust padding values from 1rem to 0.75rem for better visual hierarchy
- remove redundant overflow-y properties for cleaner code
2026-02-14 20:23:34 +08:00

255 lines
5.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
ProjectorDemo.vue
投射器Projector原理演示
-->
<template>
<div class="projector-demo">
<div class="mode-switch">
<button :class="{ active: mode === 'linear' }" @click="mode = 'linear'">
Linear (LLaVA)
</button>
<button :class="{ active: mode === 'qformer' }" @click="mode = 'qformer'">
Q-Former (BLIP-2)
</button>
</div>
<div class="pipeline">
<!-- Input: Visual Tokens -->
<div class="stage">
<div class="label">Visual Tokens (ViT)</div>
<div class="token-container input">
<div v-for="n in 16" :key="n" class="token visual"></div>
</div>
<div class="count">
{{ mode === 'linear' ? '256 Tokens' : '256 Tokens' }}
</div>
</div>
<!-- Process: The Projector -->
<div class="stage connector">
<div class="arrow-line"></div>
<div class="projector-box" :class="mode">
<div class="title">
{{ mode === 'linear' ? 'Linear Layer' : 'Q-Former' }}
</div>
<div class="desc">
{{ mode === 'linear' ? '直接映射 (1:1)' : '查询提取 (N:M)' }}
</div>
<div class="animation-dots" v-if="mode === 'qformer'">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</div>
<div class="arrow-line"></div>
</div>
<!-- Output: LLM Tokens -->
<div class="stage">
<div class="label">LLM Tokens</div>
<div class="token-container output">
<div
v-for="n in mode === 'linear' ? 16 : 4"
:key="n"
class="token llm"
></div>
</div>
<div class="count">
{{
mode === 'linear'
? '256 Tokens (保留全部细节)'
: '32 Tokens (只保留关键信息)'
}}
</div>
</div>
</div>
<div class="explanation">
<div v-if="mode === 'linear'">
<strong>Linear Projector:</strong>
简单高效它像一个直译器保留了所有的视觉信息虽然 Token
数量多计算量大但对细节的把控更好
</div>
<div v-else>
<strong>Q-Former:</strong>
精细优雅它使用一组查询向量主动去图像中提取与文本相关的信息大大压缩了
Token 数量 LLM 跑得更快
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const mode = ref('linear')
</script>
<style scoped>
.projector-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
padding: 20px;
background: var(--vp-c-bg-soft);
margin: 20px 0;
}
.mode-switch {
display: flex;
justify-content: center;
gap: 10px;
margin-bottom: 30px;
}
.mode-switch button {
padding: 6px 16px;
border-radius: 20px;
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg);
cursor: pointer;
transition: all 0.2s;
}
.mode-switch button.active {
background: var(--vp-c-brand);
color: white;
border-color: var(--vp-c-brand);
}
.pipeline {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
.stage {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
flex: 1;
}
.label {
font-size: 0.8em;
color: var(--vp-c-text-2);
font-weight: 600;
}
.token-container {
display: grid;
gap: 4px;
padding: 10px;
background: var(--vp-c-bg);
border-radius: 6px;
border: 1px solid var(--vp-c-divider);
}
.token-container.input {
grid-template-columns: repeat(4, 1fr);
}
.token-container.output {
grid-template-columns: repeat(4, 1fr);
}
.token {
width: 12px;
height: 12px;
border-radius: 2px;
}
.token.visual {
background-color: #3b82f6;
}
.token.llm {
background-color: #10b981;
}
.connector {
flex: 0.5;
display: flex;
flex-direction: row;
align-items: center;
}
.projector-box {
background: var(--vp-c-bg-mute);
border: 2px solid var(--vp-c-brand);
border-radius: 6px;
padding: 10px;
text-align: center;
min-width: 100px;
transition: all 0.3s;
}
.projector-box.qformer {
border-color: #8b5cf6;
background: rgba(139, 92, 246, 0.1);
}
.title {
font-weight: bold;
font-size: 0.9em;
}
.desc {
font-size: 0.7em;
color: var(--vp-c-text-2);
}
.count {
font-size: 0.8em;
color: var(--vp-c-text-3);
}
.explanation {
margin-top: 20px;
padding: 12px;
background: var(--vp-c-bg-mute);
border-radius: 6px;
font-size: 0.9em;
line-height: 1.6;
}
.arrow-line {
height: 2px;
background: var(--vp-c-divider);
flex-grow: 1;
}
.animation-dots {
display: flex;
justify-content: center;
gap: 4px;
margin-top: 4px;
}
.dot {
width: 4px;
height: 4px;
border-radius: 50%;
background: #8b5cf6;
animation: pulse 1s infinite;
}
.dot:nth-child(2) {
animation-delay: 0.2s;
}
.dot:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes pulse {
0%,
100% {
opacity: 0.3;
}
50% {
opacity: 1;
}
}
</style>