ginka-generator/ginka/utils.py

123 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import torch
import numpy as np
# 工具函数:密度常量、剩余密度计算、掩码生成、邻接检测
MAP_W = 13 # 地图宽度
MAP_H = 13 # 地图高度
DENSITY_DIM = 5 # [wall, door, monster, entrance, resource]
MAP_SIZE = MAP_W * MAP_H # 地图大小
WALL_DENSITY_IDX = 0
DOOR_DENSITY_IDX = 1
MONSTER_DENSITY_IDX = 2
ENTRANCE_DENSITY_IDX = 3
RESOURCE_DENSITY_IDX = 4
def print_memory(device, tag=""):
if torch.cuda.is_available():
print(f"{tag} | 当前显存: {torch.cuda.memory_allocated(device) / 1024**2:.2f} MB, 最大显存: {torch.cuda.max_memory_allocated(device) / 1024**2:.2f} MB")
else:
print("当前设备不支持 cuda.")
def compute_remaining(
current: torch.Tensor,
target_density: torch.Tensor,
stage: int
) -> torch.Tensor:
remain = torch.zeros(current.size(0), DENSITY_DIM, device=current.device)
visible_wall = (current == 1).sum(dim=1).float() / MAP_SIZE
visible_door = ((current == 2) | (current == 6)).sum(dim=1).float() / MAP_SIZE
visible_monster = (current == 4).sum(dim=1).float() / MAP_SIZE
visible_entrance = (current == 5).sum(dim=1).float() / MAP_SIZE
visible_resource = (current == 3).sum(dim=1).float() / MAP_SIZE
if stage == 1:
remain[:, WALL_DENSITY_IDX] = (
target_density[:, WALL_DENSITY_IDX] - visible_wall
).clamp(min=0.0, max=1.0)
elif stage == 2:
remain[:, DOOR_DENSITY_IDX] = (
target_density[:, DOOR_DENSITY_IDX] - visible_door
).clamp(min=0.0, max=1.0)
remain[:, MONSTER_DENSITY_IDX] = (
target_density[:, MONSTER_DENSITY_IDX] - visible_monster
).clamp(min=0.0, max=1.0)
remain[:, ENTRANCE_DENSITY_IDX] = (
target_density[:, ENTRANCE_DENSITY_IDX] - visible_entrance
).clamp(min=0.0, max=1.0)
elif stage == 3:
remain[:, RESOURCE_DENSITY_IDX] = (
target_density[:, RESOURCE_DENSITY_IDX] - visible_resource
).clamp(min=0.0, max=1.0)
return remain
def rect_mask(
ratio: float, h_range: tuple[int, int] = (2, 7),
w_range: tuple[int, int] = (2, 7)
) -> np.ndarray:
# 纯矩形分块掩码,反复放置随机矩形直到掩码格数达标
target = int(MAP_SIZE * ratio)
mask = np.zeros((MAP_H, MAP_W), dtype=bool)
while mask.sum() < target:
bh = np.random.randint(h_range[0], h_range[1])
bw = np.random.randint(w_range[0], w_range[1])
x = np.random.randint(0, MAP_H - bh + 1)
y = np.random.randint(0, MAP_W - bw + 1)
mask[x:x + bh, y:y + bw] = True
return mask
def compute_adjacency_mask(flat_state: torch.Tensor) -> torch.Tensor:
# 返回与输入同形状的 bool tensorTrue 表示该位置与任意墙壁 4-邻接
# 支持 [MAP_SIZE] 和 [B, MAP_SIZE] 两种输入
was_1d = flat_state.dim() == 1
if was_1d:
flat_state = flat_state.unsqueeze(0)
state_2d = flat_state.reshape(flat_state.size(0), MAP_H, MAP_W)
wall = (state_2d == 1)
adj = torch.zeros_like(wall, dtype=torch.bool)
adj[:, :, 1:] |= wall[:, :, :-1]
adj[:, :, :-1] |= wall[:, :, 1:]
adj[:, 1:, :] |= wall[:, :-1, :]
adj[:, :-1, :] |= wall[:, 1:, :]
result = adj.reshape(flat_state.size(0), MAP_SIZE)
if was_1d:
result = result.squeeze(0)
return result
def summarize_codebook_hits(code_hits):
# code_hits 为 tuple of 3 tensors各量器不同 K
# 返回各阶段独立统计 + 汇总统计
names = ["q1(stage1)", "q2(stage2)", "q3(stage3)"]
result = {}
for hits, name in zip(code_hits, names):
total = hits.sum()
if total.item() <= 0:
result[name] = {"ppl": 0.0, "usage": 0.0, "active": 0, "K": int(hits.numel())}
continue
probs = hits / total
ppl = torch.exp(
-(probs * torch.log(probs.clamp_min(1e-10))).sum()
).item()
active = int((hits > 0).sum().item())
usage = active / hits.numel()
result[name] = {"ppl": ppl, "usage": usage, "active": active, "K": int(hits.numel())}
# 汇总统计
combined = torch.cat([h.flatten() for h in code_hits], dim=0)
total_hits = combined.sum()
if total_hits.item() > 0:
probs = combined / total_hits
result["combined"] = {
"ppl": float(torch.exp(
-(probs * torch.log(probs.clamp_min(1e-10))).sum()
).item()),
"active": int((combined > 0).sum().item()),
"K": int(combined.numel()),
}
else:
result["combined"] = {"ppl": 0.0, "active": 0, "K": int(combined.numel())}
return result