สร้างระบบสรุปวิดีโอแบบ Local ด้วย SmolVLM2-2.2B: ประมวลผลบนเครื่องส่วนตัวได้อย่างมีประสิทธิภาพ

Local Video Summarization Pipeline: Processing Frames with SmolVLM2-2.2B

บทนำ (Introduction)

เครื่องมือทำความเข้าใจวิดีโอในปัจจุบันมักแบ่งเป็นสองขั้ว ขั้วแรกคือการใช้งานผ่าน Cloud API ซึ่งมีค่าใช้จ่ายสูงและต้องอัปโหลดข้อมูลส่วนตัวขึ้นเซิร์ฟเวอร์ผู้อื่น ขั้วที่สองคือโมเดลแบบรันในเครื่อง (Local) แต่กลับต้องการคลัสเตอร์ GPU ขนาดมหึมา เช่น โมเดลระดับ 70B+ ที่ต้องใช้ A100 หลายตัวและประมวลผลนานหลายนาทีต่อคลิป ซึ่งไม่ตอบโจทย์นักพัฒนาที่ต้องการสรุปการประชุม การบรรยาย หรือฟุตเทจวงจรปิดที่มีความยาวทั้งวันบนเครื่องเวิร์กสเตชันทั่วไป

SmolVLM2-2.2B-Instruct ซึ่งเปิดตัวโดย Hugging Face เมื่อวันที่ 20 กุมภาพันธ์ 2025 ได้เข้ามาเปลี่ยนขีดจำกัดนี้ โดยสามารถรันได้บน GPU RAM เพียง 5.2 GB เช่น RTX 3060, MacBook Pro M2 หรือแม้แต่ Google Colab ระดับ T4 ฟรี ผลการทดสอบจาก Video-MME ซึ่งเป็นมาตรฐานวัดความเข้าใจวิดีโอยาว ยืนยันว่าโมเดลรุ่นนี้มีประสิทธิภาพเหนือกว่าโมเดลขนาด 2B ทุกรุ่นที่มีอยู่ การรวมพลังระหว่างฮาร์ดแวร์ระดับผู้บริโภคกับผลลัพธ์ที่ใช้งานได้จริงจึงเป็นหัวใจหลักของบทความนี้

ในโปรเจกต์นี้ เราจะสร้างระบบประมวลผลในเครื่องที่รับไฟล์วิดีโอ ดึงเฟรมตามช่วงเวลา วิเคราะห์เฟรมแบบเป็นชุด (batch) ด้วย SmolVLM2-2.2B และแสดงผลสรุปในรูปแบบ JSON ที่มีโครงสร้างชัดเจน ประกอบด้วยรายละเอียดแต่ละเฟรม ช่วงเวลาสำคัญ (timestamp) รายการสิ่งที่ต้องทำ (action items) และสรุปจบ โดยระบบนี้รองรับทั้งบันทึกการประชุม การบรรยาย และฟุตเทจด้านความปลอดภัยโดยไม่ต้องแก้โค้ดเพิ่ม

SmolVLM2-2.2B

หัวใจที่ทำให้ SmolVLM2-2.2B รันบน RTX 3060 ได้อย่างลื่นไหลและเหนือกว่าโมเดลใหญ่ๆ ในงานวิดีโอ คือการออกแบบวิธีแปลงรูปภาพเป็นโทเค็น (tokenized)

โมเดล Vision-Language ส่วนใหญ่มักใช้โทเค็นจำนวนมาก เช่น Qwen2-VL ที่ใช้ถึง 16,000 โทเค็นต่อหนึ่งภาพ หากป้อน 50 เฟรมจะกินทรัพยากรสูงถึง 800,000 โทเค็น ซึ่งเกินความจุของ GPU ทั่วไป SmolVLM2 จึงหันมาใช้กลยุทธ์ pixel shuffle strategy เพื่อบีบอัดภาพขนาด 384x384 ให้เหลือเพียง 81 โทเค็น ทำให้วิดีโอ 50 เฟรมใช้โทเค็นเพียง 4,050 โทเค็นเท่านั้น ส่งผลให้ SmolVLM2 มีความเร็วในการประมวลผลขั้นต้นเร็วกว่าเดิม 3.3-4.5 เท่า และสร้างข้อความเร็วกว่า Qwen2-VL-2B ถึง 7.5-16 เท่า

โมเดลนี้มี 3 ขนาด รุ่น 256M และ 500M เน้นอุปกรณ์พกพาและ Edge โดยรุ่น 256M สามารถรันบนมือถือได้ แต่สำหรับงานสรุปวิดีโอ รุ่น 2.2B คือตัวเลือกที่ดีที่สุดเพราะมีคะแนนทดสอบวิดีโอที่เสถียร (Video-MME 52.1, MLVU 55.2) เมื่อเทียบกับรุ่น 500M

SmolVLM2 มองวิดีโอเป็นลำดับภาพต่อกัน ระบบอ้างอิงอย่างเป็นทางการ จะสุ่มดึงได้สูงสุด 50 เฟรม และส่งเข้าไปประมวลผลในหน้าแชทเดียว วิธีนี้ทำให้ได้คะแนนจาก CinePile ที่ 27.14% ซึ่งอยู่ในระดับเดียวกับโมเดลที่มีขนาดใหญ่กว่ามากอย่าง Video-LLaVA (7B)

การตั้งค่าสภาพแวดล้อม (Setting Up the Environment)

ข้อกำหนดด้านฮาร์ดแวร์:

คุณสมบัติขั้นต่ำแนะนำ
GPU VRAM6 GB (RTX 3060)12–16 GB (RTX 4080)
Apple SiliconM2 8 GB (เส้นทาง MPS)M2 Pro / M3 16 GB
System RAM16 GB32 GB
Diskพื้นที่ว่าง 10 GB20 GB+ SSD
ColabT4 (ระดับฟรี)A100 (Colab Pro)

แพ็กเกจ Python:

# ต้องการ Python 3.10 ขึ้นไป
python --version
 
python -m venv smolvlm2-env
source smolvlm2-env/bin/activate       # macOS / Linux
smolvlm2-env\Scripts\activate          # Windows
 
# ติดตั้งจากสาขาเสถียร SmolVLM-2 -- จำเป็นสำหรับการรองรับ SmolVLM2
pip install git+https://github.com/huggingface/[email protected]
 
# การพึ่งพาหลัก (Core dependencies)
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
pip install \
  opencv-python \
  Pillow \
  numpy \
  num2words \
  accelerate
 
# Flash Attention 2 สำหรับ CUDA -- เร็วขึ้นอย่างมากบน GPU NVIDIA
# ข้ามขั้นตอนนี้บน Apple Silicon และ CPU -- ใช้ได้เฉพาะ CUDA เท่านั้น
pip install flash-attn --no-build-isolation
 
# decord -- จำเป็นสำหรับเส้นทางการนำเข้าวิดีโอแบบดั้งเดิมของ SmolVLM2
pip install decord

หมายเหตุ: แพ็กเกจ num2words มีความสำคัญในการแปลงตัวเลขเป็นคำเขียนเพื่อให้ตรงกับรูปแบบที่โมเดลถูกฝึกมา การข้ามขั้นตอนนี้อาจทำให้เกิดข้อผิดพลาดในการโหลดโมเดลได้ ตามที่ อธิบายไว้ในบทแนะนำนี้

การตรวจสอบอุปกรณ์:

# device_check.py
# รัน: python device_check.py
 
import torch
 
def detect_device():
    if torch.cuda.is_available():
        name  = torch.cuda.get_device_name(0)
        vram  = torch.cuda.get_device_properties(0).total_memory / 1e9
        print(f"CUDA: {name} ({vram:.1f} GB VRAM)")
        return "cuda", torch.bfloat16, "flash_attention_2"
    elif hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
        print("ตรวจพบ Apple Silicon MPS")
        return "mps", torch.float16, "eager"
    else:
        print("ใช้ CPU (ช้า -- พิจารณาใช้ Colab T4)")
        return "cpu", torch.float32, "eager"
 
if __name__ == "__main__":
    device, dtype, attn = detect_device()
    print(f"อุปกรณ์: {device} | dtype: {dtype} | attn: {attn}")

รันด้วยคำสั่ง:

python device_check.py

การสร้างรากฐานของระบบประมวลผล (Building the Foundation of the Pipeline)

ขั้นตอนแรกคือการแปลงวิดีโอเป็นรายการรูปภาพ PIL พร้อมระบุเวลา โดยมี 2 โหมดหลัก: Uniform sampling (สุ่มแบบสม่ำเสมอ) เหมาะสำหรับงานประชุมที่ไม่ต้องการพลาดเนื้อหาส่วนใดส่วนหนึ่ง และ Keyframe sampling (สุ่มตามการเปลี่ยนฉาก) เหมาะสำหรับงานเฝ้าระวังเพื่อเน้นจังหวะที่แตกต่างในวิดีโอ

# frame_extractor.py
import cv2
import numpy as np
from PIL import Image
 
class FrameExtractor:
    MAX_FRAMES = 50
 
def __init__(self, max_frames: int = MAX_FRAMES):
        self.max_frames = max_frames
 
def uniform_sample(self, video_path: str) -> list[tuple[float, Image.Image]]:
        cap = cv2.VideoCapture(video_path)
        if not cap.isOpened():
            raise IOError(f"ไม่สามารถเปิดวิดีโอได้: {video_path}")
        total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
        fps          = cap.get(cv2.CAP_PROP_FPS) or 30.0
        n_extract    = min(self.max_frames, total_frames)
        indices = np.linspace(0, total_frames - 1, n_extract, dtype=int)
        results = []
        for idx in indices:
            cap.set(cv2.CAP_PROP_POS_FRAMES, int(idx))
            ret, frame = cap.read()
            if not ret: continue
            timestamp = round(idx / fps, 2)
            rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            results.append((timestamp, Image.fromarray(rgb)))
        cap.release()
        return results
 
def keyframe_sample(self, video_path: str, diff_threshold: float = 30.0) -> list[tuple[float, Image.Image]]:
        cap = cv2.VideoCapture(video_path)
        fps = cap.get(cv2.CAP_PROP_FPS) or 30.0
        results, prev_gray, idx = [], None, 0
        while len(results) < self.max_frames:
            ret, frame = cap.read()
            if not ret: break
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            if prev_gray is None:
                rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                results.append((round(idx / fps, 2), Image.fromarray(rgb)))
            else:
                diff = np.mean(np.abs(gray.astype(float) - prev_gray.astype(float)))
                if diff > diff_threshold:
                    rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                    results.append((round(idx / fps, 2), Image.fromarray(rgb)))
            prev_gray = gray
            idx += 1
        cap.release()
        return results

การโหลด SmolVLM2 และการประมวลผล

สำหรับการประมวลผลบน CUDA การเปิดใช้ Flash Attention 2 จะช่วยเพิ่มความเร็วในการรับข้อมูลภาพหลายภาพได้อย่างดี

# smolvlm2_loader.py
import sys
import torch
from PIL import Image
from transformers import AutoProcessor, AutoModelForImageTextToText
 
MODEL_ID = "HuggingFaceTB/SmolVLM2-2.2B-Instruct"
 
def load_model():
    # ระบบเลือก device อัตโนมัติ (CUDA/MPS/CPU)
    device = "cuda" if torch.cuda.is_available() else ("mps" if hasattr(torch.backends, "mps") and torch.backends.mps.is_available() else "cpu")
    dtype = torch.bfloat16 if device == "cuda" else (torch.float16 if device == "mps" else torch.float32)
    attn = "flash_attention_2" if device == "cuda" else "eager"
 
processor = AutoProcessor.from_pretrained(MODEL_ID)
    model = AutoModelForImageTextToText.from_pretrained(MODEL_ID, torch_dtype=dtype, _attn_implementation=attn).to(device)
    return model, processor
 
def describe_frame(model, processor, frame, prompt="Describe this frame.", max_new_tokens=256):
    messages = [{"role": "user", "content": [{"type": "image"}, {"type": "text", "text": prompt}]}]
    input_text = processor.apply_chat_template(messages, add_generation_prompt=True)
    inputs = processor(images=[frame], text=input_text, return_tensors="pt").to(model.device)
    with torch.no_grad():
        output_ids = model.generate(**inputs, max_new_tokens=max_new_tokens, do_sample=False)
    new_tokens = output_ids[0][inputs["input_ids"].shape[-1]:]
    return processor.decode(new_tokens, skip_special_tokens=True).strip()

การสร้างโปรเจกต์ใช้งานจริง (ระบบสรุปบันทึกการประชุม)

ระบบนี้ใช้กลยุทธ์ 2 รอบ: รอบแรกให้โมเดลอธิบายแต่ละเฟรมเพื่อความแม่นยำ และรอบที่สองรวบรวมคำอธิบายเหล่านั้นมาสรุปเป็นรายงาน JSON พร้อมรายการสิ่งที่ต้องทำ (action items)

# video_summarizer.py จัดการประมวลผลทั้งหมดเป็นขั้นตอน
# (ดูโค้ดการทำงานในส่วน Building the Foundation และ Loader ด้านบน)

การคำนวณ VRAM สำหรับ Batch Processing เพื่อให้รันบน GPU ทั่วไปได้โดยไม่เกิดข้อผิดพลาด OOM (Out-of-memory) การคำนวณ batch size ที่เหมาะสมจึงสำคัญมาก:

# vram_calculator.py
def compute_batch_size(vram_gb: float) -> int:
    MODEL_GB = 4.5
    HEADROOM = 0.8
    MB_PER_TOKEN = 0.5 / 1024
    usable_gb = vram_gb * HEADROOM
    inference_budget = max(0.0, usable_gb - MODEL_GB)
    frames = int(inference_budget / (81 * MB_PER_TOKEN))
    return max(1, min(frames, 50))

ผลลัพธ์โดยประมาณ:

  • 6 GB VRAM → batch_size = 16
  • 8 GB VRAM → batch_size = 30
  • 12 GB+ VRAM → batch_size = 50

บทสรุป (Conclusion)

SmolVLM2-2.2B คือจุดสมดุลที่ยอดเยี่ยมระหว่างขนาดและความสามารถ โดยนักพัฒนาสามารถนำไปประยุกต์ใช้กับวิดีโอได้หลากหลายประเภทผ่านไลบรารี transformers มาตรฐาน การประมวลผล 2 รอบช่วยให้สรุปเนื้อหาได้ลุ่มลึกและแม่นยำ เหมาะอย่างยิ่งสำหรับการสร้างเครื่องมือสรุปวิดีโอแบบรันในเครื่องที่ให้ความเป็นส่วนตัวสูงและประหยัดค่าใช้จ่าย

Source: KDnuggets
ดูแลงานแปลและเรียบเรียงโดย SirilukP

ความคิดเห็น (0)

เข้าสู่ระบบเพื่อร่วมแสดงความเห็น

สมัครสมาชิก

มาเป็นคนแรกที่แสดงความเห็นกันเลยโบร