Blokus / app.py
aracape's picture
Update app.py
eca13df verified
Raw
History Blame Contribute Delete
4.09 kB
import gradio as gr
import numpy as np
import torch
import json
from huggingface_hub import hf_hub_download
import torch.nn as nn
class BlokusTransformer(nn.Module):
def __init__(self,
d_max=20, # maximum board dimension
embed_dim=128, # dimension of the transformer embeddings
num_heads=4,
mlp_dim=256, # dimension of feedforward layer in TransformerEncoderLayer
num_layers=4, # number of transformer layers
dropout=0.1):
super().__init__()
self.input_proj = nn.Linear(5, embed_dim)
self.pos_embed = nn.Parameter(torch.zeros(d_max * d_max, embed_dim))
nn.init.trunc_normal_(self.pos_embed, std=0.02)
self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
encoder_layer = nn.TransformerEncoderLayer(d_model=embed_dim,
nhead=num_heads,
dim_feedforward=mlp_dim,
dropout=dropout,
batch_first=True)
self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)
self.policy_head = nn.Linear(embed_dim, 1)
self.value_head = nn.Linear(embed_dim, 4)
def forward(self, x):
"""
board_tensor: shape [batch_size, d, input_dim]
each cell has input_dim=5 features (4 occupancy bits + 1 legal bit)
Returns:
policy_logits: [batch_size, d, d]
value: [batch_size, 4]
"""
batch_size, d, _ = x.shape
# Project to embedding dim: [batch_size, d*d, embed_dim]
x = self.input_proj(x)
# Add positional embeddings
pos_embed_slice = self.pos_embed[:d, :].unsqueeze(0) # [1, d, embed_dim]
x = x + pos_embed_slice
init_cls_tokens = self.cls_token.expand(batch_size, -1, -1)
x = torch.cat([init_cls_tokens, x], dim=1)
x = self.transformer(x) # [batch_size, 1 + d, embed_dim]
board_tokens = x[:, 1:, :]
cls_tokens = x[:, 0, :]
policy_logits = self.policy_head(board_tokens).view(batch_size, d) # [batch_size, d]
value_logits = self.value_head(cls_tokens) # [batch_size, 4]
return policy_logits, value_logits
# Download your model from the Hub
model_path = hf_hub_download(
repo_id="aracape/Blokus-Transformer",
filename="20x20.pt"
)
# Load your model
config = {
"d_max": 20,
"embed_dim": 128,
"num_heads": 4,
"mlp_dim": 256,
"num_layers": 4,
"dropout": 0.1
}
model = BlokusTransformer(**config)
model.load_state_dict(torch.load(model_path, weights_only=True, map_location='cpu'))
model.eval()
def predict(input_data):
"""
Process input and return prediction
"""
try:
# Parse input if it's JSON string
if isinstance(input_data, str):
input_data = json.loads(input_data)
# Convert input
input_np = np.array(input_data, dtype=np.float32)
if input_np.size == 20 * 20 * 5:
# Reshape and add batch dimension
input_tensor = torch.from_numpy(input_np.reshape(1, 20 * 20, 5))
else:
return "Error: Input must be 20x20x5 = 2000 elements", "Error: Invalid input size"
# Run inference
with torch.no_grad():
policies, values = model(input_tensor)
policy = policies.squeeze().tolist()
value = values.squeeze().tolist()
return policy, value
except Exception as e:
return f"Error: {str(e)}"
# Create Gradio interface
iface = gr.Interface(
fn=predict,
inputs=gr.Textbox(label="Input (JSON format)", placeholder='{"data": [1, 2, 3, 4]}'),
outputs=gr.Textbox(label="Prediction"),
title="My PyTorch Model API"
)
# Launch with API access enabled
iface.launch(share=True)