簡介
賣家上傳了設計,但不想手動標記它。人工智慧自動標記會自動按風格、主題、調色板、季節對設計進行分類,使設計更容易找到並更準確地推薦。
1. 標籤架構
Design Upload
│
▼
┌─────────────────────────────────────────┐
│ AI Auto-Tagger │
│ │
│ ┌──────────┐ ┌──────────┐ ┌────────┐ │
│ │ CLIP │ │ Color │ │ Custom │ │
│ │ Zero-shot │ │ Analyzer │ │ Model │ │
│ └─────┬────┘ └─────┬────┘ └───┬────┘ │
│ └──────┬──────┘ ────┘ │
│ ▼ │
│ Tag Aggregator │
│ Confidence Filter │
└─────────────────┬───────────────────────┘
│
▼
┌─────────────────┐
│ Tags: │
│ style: minimalist│
│ theme: nature │
│ colors: green │
│ season: spring │
│ audience: unisex │
└─────────────────┘
2. CLIP 零樣本分類
import torch
import clip
from PIL import Image
class CLIPTagger:
"""Zero-shot design classification with CLIP"""
# Tag taxonomy
TAXONOMIES = {
"style": [
"minimalist", "vintage", "retro", "modern",
"grunge", "kawaii", "street", "classic",
"abstract", "geometric", "illustrative",
"typography-focused", "photographic",
],
"theme": [
"nature", "animals", "music", "sports",
"gaming", "food", "travel", "humor",
"motivational", "holiday", "cultural",
"pop culture", "sci-fi", "horror",
],
"audience": [
"men", "women", "unisex", "kids", "teens",
],
"season": [
"spring", "summer", "autumn", "winter",
"all-season",
],
"mood": [
"fun", "serious", "edgy", "cute",
"elegant", "bold", "calm", "energetic",
],
}
def __init__(self):
self.device = "cuda" if torch.cuda.is_available() else "cpu"
self.model, self.preprocess = clip.load(
"ViT-L/14", device=self.device
)
def tag_design(
self,
image: Image.Image,
confidence_threshold: float = 0.15,
) -> dict[str, list[TagResult]]:
"""
Tag design sử dụng CLIP zero-shot
Returns tags per category, sorted by confidence
"""
image_input = self.preprocess(image).unsqueeze(0).to(self.device)
results = {}
with torch.no_grad():
image_features = self.model.encode_image(image_input)
image_features /= image_features.norm(dim=-1, keepdim=True)
for category, labels in self.TAXONOMIES.items():
# Create text prompts
prompts = [
f"a {label} style t-shirt design"
for label in labels
]
text_tokens = clip.tokenize(prompts).to(self.device)
text_features = self.model.encode_text(text_tokens)
text_features /= text_features.norm(dim=-1, keepdim=True)
# Calculate similarity
similarity = (
image_features @ text_features.T
).softmax(dim=-1)[0]
# Filter by threshold
tags = []
for i, (label, score) in enumerate(
zip(labels, similarity.cpu().numpy())
):
if score >= confidence_threshold:
tags.append(TagResult(
label=label,
confidence=float(score),
))
# Sort by confidence
tags.sort(key=lambda t: t.confidence, reverse=True)
results[category] = tags
return results
3. 色彩分析
from sklearn.cluster import KMeans
from collections import Counter
class ColorAnalyzer:
"""Extract và classify color palette"""
COLOR_NAMES = {
(255, 0, 0): "red",
(0, 255, 0): "green",
(0, 0, 255): "blue",
(255, 255, 0): "yellow",
(255, 165, 0): "orange",
(128, 0, 128): "purple",
(255, 192, 203): "pink",
(0, 0, 0): "black",
(255, 255, 255): "white",
(128, 128, 128): "gray",
(139, 69, 19): "brown",
(0, 128, 128): "teal",
(0, 255, 255): "cyan",
(255, 215, 0): "gold",
(192, 192, 192): "silver",
}
def extract_palette(
self, image: Image.Image, n_colors: int = 5
) -> list[ColorInfo]:
"""Extract dominant colors bằng K-means clustering"""
# Resize for speed
img_small = image.resize((150, 150)).convert("RGB")
pixels = np.array(img_small).reshape(-1, 3)
# Remove near-white/transparent pixels
mask = np.all(pixels < 250, axis=1)
pixels = pixels[mask]
if len(pixels) < n_colors:
return []
# K-means clustering
kmeans = KMeans(
n_clusters=n_colors, random_state=42, n_init=10
)
kmeans.fit(pixels)
# Get cluster sizes
labels = kmeans.labels_
counts = Counter(labels)
total = len(labels)
colors = []
for i, center in enumerate(kmeans.cluster_centers_):
rgb = tuple(int(c) for c in center)
name = self._closest_color_name(rgb)
percentage = counts[i] / total
colors.append(ColorInfo(
rgb=rgb,
hex=f"#{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}",
name=name,
percentage=percentage,
))
colors.sort(key=lambda c: c.percentage, reverse=True)
return colors
def _closest_color_name(self, rgb: tuple) -> str:
"""Find closest named color"""
min_dist = float("inf")
closest = "unknown"
for ref_rgb, name in self.COLOR_NAMES.items():
dist = sum((a - b) ** 2 for a, b in zip(rgb, ref_rgb))
if dist < min_dist:
min_dist = dist
closest = name
return closest
4.自訂多標籤分類器
import torch
import torch.nn as nn
from torchvision import models, transforms
class DesignClassifier(nn.Module):
"""Custom multi-label classifier trained trên design dataset"""
def __init__(self, num_labels: int = 50):
super().__init__()
# ResNet50 backbone
backbone = models.resnet50(pretrained=True)
self.features = nn.Sequential(
*list(backbone.children())[:-1]
)
# Multi-label head
self.classifier = nn.Sequential(
nn.Flatten(),
nn.Linear(2048, 512),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(512, num_labels),
nn.Sigmoid(), # Multi-label: independent probabilities
)
def forward(self, x):
features = self.features(x)
return self.classifier(features)
class CustomTagger:
"""Wrapper cho custom classifier"""
def __init__(self, model_path: str, label_map: dict):
self.model = DesignClassifier(num_labels=len(label_map))
self.model.load_state_dict(torch.load(model_path))
self.model.eval()
self.label_map = label_map
self.transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225],
),
])
def predict(
self,
image: Image.Image,
threshold: float = 0.5,
) -> list[TagResult]:
input_tensor = self.transform(image).unsqueeze(0)
with torch.no_grad():
probs = self.model(input_tensor)[0]
tags = []
for idx, prob in enumerate(probs.numpy()):
if prob >= threshold:
tags.append(TagResult(
label=self.label_map[idx],
confidence=float(prob),
))
return sorted(tags, key=lambda t: t.confidence, reverse=True)
5. 統一標記管道
class AutoTagPipeline:
"""Pipeline gom kết quả từ nhiều taggers"""
def __init__(self):
self.clip_tagger = CLIPTagger()
self.color_analyzer = ColorAnalyzer()
self.custom_tagger = CustomTagger(
"models/design_classifier.pt",
label_map=LABEL_MAP,
)
async def tag_design(
self, image: Image.Image
) -> DesignTags:
# CLIP zero-shot tags
clip_tags = self.clip_tagger.tag_design(image)
# Color palette
colors = self.color_analyzer.extract_palette(image)
# Custom model tags
custom_tags = self.custom_tagger.predict(image)
# Merge and deduplicate
return DesignTags(
style=clip_tags.get("style", []),
theme=clip_tags.get("theme", []),
audience=clip_tags.get("audience", []),
season=clip_tags.get("season", []),
mood=clip_tags.get("mood", []),
colors=[c.name for c in colors[:3]],
color_palette=colors,
custom_tags=custom_tags,
)
總結
人工智慧自動標記:
- CLIP 零樣本 — 根據 5 種分類法對設計進行分類,無需訓練數據
- 顏色分析——K-means聚類,最接近命名顏色匹配
- 自訂分類器 - ResNet50 多標籤,在設計資料集上進行訓練
- Pipeline — 合併 3 個標記器的結果
下一篇文章:AI 產品產生 — 自動產生標題、描述和模型。