Chuyển đến nội dung chính

レッスン 12: スタイル分析エンジン — ユーザー入力から美的センスを分析する

オンボーディング フローの構築: ユーザーが 5 ~ 10 枚の写真をアップロード → AI がカラー パレット、タイポグラフィーの好み、パターン スタイル、美しさ (サイバーパンク、ミニマル、ヴィンテージ、ゲーム) を分析します。クリップ + クラスタリング。

🧠 AI と ML — レッスン 11 レッスン 12: スタイル分析エンジン — 分析 ユーザー入力による美的センス

AI の活用: ファッションとプリント オン デマンド向けの AI プラットフォームの構築

パート 4: AI のパーソナライゼーションとレコメンデーション

xdev.asia

はじめに

このプラットフォームは単にランダムなデザインを生成するだけではありません。AI は各ユーザーの美的嗜好を理解し、時間の経過とともにより適切なデザインを作成する必要があります。この記事では、オンボーディング データから好みを分析する AI モジュールである スタイル分析エンジン を構築します。


1. オンボーディングの流れ

User đăng ký platform
    │
    ├── Upload 5–10 ảnh design yêu thích
    │   (áo thun, artwork, meme, illustration...)
    │
    ├── AI phân tích:
    │   ├── Color palette preference
    │   ├── Typography style
    │   ├── Pattern/layout preference
    │   └── Aesthetic category
    │
    └── Tạo User Style Profile
        ├── Style vector (CLIP embedding)
        ├── Dominant colors
        ├── Preferred categories
        └── Confidence score

2. クリップベースのスタイルプロファイリング

class StyleProfiler:
    """Tạo style profile từ ảnh user upload"""

    def __init__(self):
        self.clip_model = CLIPModel.from_pretrained(
            "openai/clip-vit-large-patch14-336"
        )
        self.clip_processor = CLIPProcessor.from_pretrained(
            "openai/clip-vit-large-patch14-336"
        )
        self.color_extractor = ColorExtractor()

    def create_profile(
        self, images: list[Image.Image]
    ) -> StyleProfile:
        # 1. Extract CLIP embeddings
        embeddings = [
            self._get_embedding(img) for img in images
        ]

        # 2. Compute average style vector
        style_vector = torch.stack(embeddings).mean(dim=0)
        style_vector = style_vector / style_vector.norm()

        # 3. Classify aesthetics
        aesthetics = self._classify_aesthetics(images)

        # 4. Extract color preferences
        color_prefs = self._aggregate_colors(images)

        # 5. Detect patterns
        patterns = self._detect_patterns(images)

        return StyleProfile(
            style_vector=style_vector,
            aesthetics=aesthetics,
            color_preferences=color_prefs,
            pattern_preferences=patterns,
            confidence=self._calculate_confidence(embeddings),
        )

    def _classify_aesthetics(
        self, images: list[Image.Image]
    ) -> dict:
        """Classify aesthetic style across all images"""
        all_styles = {}

        for img in images:
            styles = self.style_analyzer.classify_style(img)
            for style, score in styles.items():
                if style not in all_styles:
                    all_styles[style] = []
                all_styles[style].append(score)

        # Average scores
        avg_styles = {
            style: sum(scores) / len(scores)
            for style, scores in all_styles.items()
        }

        # Return top 3
        sorted_styles = sorted(
            avg_styles.items(), key=lambda x: x[1], reverse=True
        )
        return dict(sorted_styles[:3])

    def _aggregate_colors(
        self, images: list[Image.Image]
    ) -> list[dict]:
        """Tổng hợp color palette từ tất cả ảnh"""
        all_colors = []
        for img in images:
            colors = self.color_extractor.extract_palette(img, n_colors=3)
            all_colors.extend(colors)

        # Cluster lại để tìm dominant colors
        from sklearn.cluster import KMeans
        import numpy as np

        rgb_values = np.array([c["rgb"] for c in all_colors])
        kmeans = KMeans(n_clusters=5, n_init=10, random_state=42)
        kmeans.fit(rgb_values)

        dominant = []
        for center in kmeans.cluster_centers_:
            rgb = tuple(center.astype(int))
            dominant.append({
                "rgb": rgb,
                "hex": "#{:02x}{:02x}{:02x}".format(*rgb),
            })

        return dominant

    def _calculate_confidence(
        self, embeddings: list[torch.Tensor]
    ) -> float:
        """
        Confidence = mức độ consistent của style

        Nếu tất cả ảnh cùng style → confidence cao
        Nếu ảnh rất đa dạng → confidence thấp
        """
        if len(embeddings) < 2:
            return 0.5

        # Pairwise cosine similarity
        similarities = []
        for i in range(len(embeddings)):
            for j in range(i + 1, len(embeddings)):
                sim = torch.cosine_similarity(
                    embeddings[i], embeddings[j], dim=0
                )
                similarities.append(sim.item())

        avg_similarity = sum(similarities) / len(similarities)
        # Normalize to 0-1 range
        return min(max(avg_similarity, 0), 1)

3. スタイルプロファイルのストレージ

@dataclass
class StyleProfile:
    style_vector: torch.Tensor      # CLIP embedding (768-dim)
    aesthetics: dict                 # {"cyberpunk": 0.7, "neon": 0.5}
    color_preferences: list[dict]   # Top 5 colors
    pattern_preferences: list[str]  # ["geometric", "typography"]
    confidence: float               # 0-1

    def to_storage(self) -> dict:
        """Serialize cho database storage"""
        return {
            "style_vector": self.style_vector.tolist(),
            "aesthetics": self.aesthetics,
            "color_preferences": self.color_preferences,
            "pattern_preferences": self.pattern_preferences,
            "confidence": self.confidence,
        }

# Storage: Vector DB (Qdrant/Pinecone)
async def store_profile(
    user_id: str, profile: StyleProfile
):
    # Store style vector trong Vector DB
    await vector_db.upsert(
        collection="user_styles",
        points=[{
            "id": user_id,
            "vector": profile.style_vector.tolist(),
            "payload": {
                "aesthetics": profile.aesthetics,
                "colors": profile.color_preferences,
                "confidence": profile.confidence,
            },
        }],
    )

4. 類似ユーザーの発見

class SimilarUsersFinder:
    """Tìm users có gu tương tự (cho collaborative filtering)"""

    async def find_similar(
        self, user_id: str, top_k: int = 20
    ) -> list[dict]:
        # Get user's style vector
        user_profile = await vector_db.get(
            collection="user_styles", id=user_id
        )

        # Search similar vectors
        results = await vector_db.search(
            collection="user_styles",
            query_vector=user_profile.vector,
            limit=top_k + 1,  # +1 because self is included
        )

        # Exclude self
        similar = [
            r for r in results if r.id != user_id
        ]

        return similar[:top_k]

概要

スタイル分析エンジン:

  1. オンボーディング — ユーザーが 5 ~ 10 枚の写真をアップロード → AI 分析
  2. CLIP プロファイリング — 美的センスを表す 768 次元のベクトル スタイルを作成します。
  3. 美的分類 — サイバーパンク、ミニマル、ヴィンテージ、ゲームを検出します...
  4. 色の集約 — 好みに基づいて主要な色を合成します
  5. 信頼スコアリング — スタイルの好みの明確さを評価します
  6. 類似ユーザー — 類似性ベクトルを介して類似ユーザーを検索します

次の記事: 行動学習 — AI は時間の経過とともに使用行動から学習します。