🚀 UIClip
UIClipは、テキスト記述を元にユーザーインターフェイス(UI)のスクリーンショットのデザイン品質と関連性を定量化するために設計されたモデルです。このモデルは、自然言語によるデザイン提案の生成にも利用できます。
🚀 クイックスタート
UIClipは、テキスト記述を与えられたユーザーインターフェイス(UI)のスクリーンショットのデザイン品質と関連性を定量化するために設計されたモデルです。このモデルは、自然言語によるデザイン提案の生成にも利用できます(論文を参照)。このモデルは、UIST 2024で発表された論文「UIClip: A Data-driven Model for Assessing User Interface Design」(https://arxiv.org/abs/2404.12500)で説明されています。
ユーザーインターフェイス(UI)のデザインは、アプリケーションの使いやすさ、アクセシビリティ、美観を確保するために難しいが重要なタスクです。論文では、UIのスクリーンショットと自然言語の記述を元に、UIのデザイン品質と視覚的な関連性を評価する機械学習モデルであるUIClipを開発しました。UIClipを訓練するために、自動クローリング、合成拡張、および人間の評価を組み合わせて、UIの大規模データセットを構築し、記述ごとに整理し、デザイン品質でランク付けしました。このデータセットでの訓練を通じて、UIClipは、i)UIデザインの関連性と品質を表す数値スコアを割り当て、ii)デザイン提案を提供することで、良いデザインと悪いデザインの特性を暗黙的に学習します。12人のデザイナーによって評価されたUIに対するUIClipと他のベースラインの出力を比較した評価では、UIClipがグラウンドトゥルースのランキングと最も高い一致度を達成したことがわかりました。最後に、UIClipがUIデザイン品質の即時評価に依存する下流のアプリケーションをどのように促進できるかを示す3つの例を示します:i)UIコード生成、ii)UIデザインヒント生成、およびiii)品質を考慮したUI例検索。
- 開発者: BigLab
- モデルタイプ: CLIPスタイルのマルチモーダルデュアルエンコーダTransformer
- 言語 (NLP): 英語
- ライセンス: MIT
属性 |
详情 |
モデルタイプ |
CLIPスタイルのマルチモーダルデュアルエンコーダTransformer |
訓練データ |
biglab/jitteredwebsites-merged-224-paraphrased、biglab/jitteredwebsites-merged-224-paraphrased-paired、biglab/uiclip_human_data_hf |
ベースモデル |
openai/clip-vit-base-patch32、biglab/uiclip_jitteredwebsites-2-224-paraphrased_webpairs |
💻 使用例
基本的な使用法
import torch
from transformers import CLIPProcessor, CLIPModel
IMG_SIZE = 224
DEVICE = "cpu"
LOGIT_SCALE = 100
NORMALIZE_SCORING = True
model_path="uiclip_jitteredwebsites-2-224-paraphrased_webpairs_humanpairs"
processor_path="openai/clip-vit-base-patch32"
model = CLIPModel.from_pretrained(model_path)
model = model.eval()
model = model.to(DEVICE)
processor = CLIPProcessor.from_pretrained(processor_path)
def compute_quality_scores(input_list):
description_list = ["ui screenshot. well-designed. " + input_item[0] for input_item in input_list]
img_list = [input_item[1] for input_item in input_list]
text_embeddings_tensor = compute_description_embeddings(description_list)
img_embeddings_tensor = compute_image_embeddings(img_list)
text_embeddings_tensor /= text_embeddings_tensor.norm(dim=-1, keepdim=True)
img_embeddings_tensor /= img_embeddings_tensor.norm(dim=-1, keepdim=True)
if NORMALIZE_SCORING:
text_embeddings_tensor_poor = compute_description_embeddings([d.replace("well-designed. ", "poor design. ") for d in description_list])
text_embeddings_tensor_poor /= text_embeddings_tensor_poor.norm(dim=-1, keepdim=True)
text_embeddings_tensor_all = torch.stack((text_embeddings_tensor, text_embeddings_tensor_poor), dim=1)
else:
text_embeddings_tensor_all = text_embeddings_tensor.unsqueeze(1)
img_embeddings_tensor = img_embeddings_tensor.unsqueeze(1)
scores = (LOGIT_SCALE * img_embeddings_tensor @ text_embeddings_tensor_all.permute(0, 2, 1)).squeeze(1)
if NORMALIZE_SCORING:
scores = scores.softmax(dim=-1)
return scores[:, 0]
def compute_description_embeddings(descriptions):
inputs = processor(text=descriptions, return_tensors="pt", padding=True)
inputs['input_ids'] = inputs['input_ids'].to(DEVICE)
inputs['attention_mask'] = inputs['attention_mask'].to(DEVICE)
text_embedding = model.get_text_features(**inputs)
return text_embedding
def compute_image_embeddings(image_list):
windowed_batch = [slide_window_over_image(img, IMG_SIZE) for img in image_list]
inds = []
for imgi in range(len(windowed_batch)):
inds.append([imgi for _ in windowed_batch[imgi]])
processed_batch = [item for sublist in windowed_batch for item in sublist]
inputs = processor(images=processed_batch, return_tensors="pt")
inputs['pixel_values'] = inputs['pixel_values'].to(DEVICE)
with torch.no_grad():
image_features = model.get_image_features(**inputs)
processed_batch_inds = torch.tensor([item for sublist in inds for item in sublist]).long().to(image_features.device)
embed_list = []
for i in range(len(image_list)):
mask = processed_batch_inds == i
embed_list.append(image_features[mask].mean(dim=0))
image_embedding = torch.stack(embed_list, dim=0)
return image_embedding
def preresize_image(image, image_size):
aspect_ratio = image.width / image.height
if aspect_ratio > 1:
image = image.resize((int(aspect_ratio * image_size), image_size))
else:
image = image.resize((image_size, int(image_size / aspect_ratio)))
return image
def slide_window_over_image(input_image, img_size):
input_image = preresize_image(input_image, img_size)
width, height = input_image.size
square_size = min(width, height)
longer_dimension = max(width, height)
num_steps = (longer_dimension + square_size - 1) // square_size
if num_steps > 1:
step_size = (longer_dimension - square_size) // (num_steps - 1)
else:
step_size = square_size
cropped_images = []
for y in range(0, height - square_size + 1, step_size if height > width else square_size):
for x in range(0, width - square_size + 1, step_size if width > height else square_size):
left = x
upper = y
right = x + square_size
lower = y + square_size
cropped_image = input_image.crop((left, upper, right, lower))
cropped_images.append(cropped_image)
return cropped_images
prediction_scores = compute_quality_scores(list(zip(test_descriptions, test_images)))
📄 ライセンス
このモデルはMITライセンスの下で提供されています。