用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/cxcscmu/SkillLearnBench --skill run2-greedy-matching命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
| name | run2_greedy_matching |
| description | Greedy one-to-one matching of cluster centroids to expert points with maximum distance constraint |
Input:
Goal: Find best one-to-one matching between clusters and experts.
Constraints:
The greedy algorithm matches pairs in order of increasing distance, skipping pairs where either endpoint is already matched.
import numpy as np
from scipy.spatial.distance import cdist
def greedy_match(cluster_centroids, expert_points, max_distance=100):
"""
Greedily match cluster centroids to expert points.
Algorithm:
1. Compute all pairwise Euclidean distances (standard, not custom)
2. Create list of valid pairs (distance ≤ max_distance)
3. Sort by distance (ascending)
4. Iterate through sorted pairs:
- If both endpoints unmatched: create match, mark as matched
- Otherwise: skip (one endpoint already matched)
5. Return all created matches and their distances
Parameters:
- cluster_centroids: (n_clusters, 2) array of [x, y]
- expert_points: (n_experts, 2) array of [x, y]
- max_distance: float, maximum Euclidean distance for valid match (default: 100)
Returns:
- matches: list of (centroid_idx, expert_idx) tuples
- match_distances: list of distances for each match
"""
# Handle empty inputs
if len(cluster_centroids) == 0 or len(expert_points) == 0:
return [], []
# Compute pairwise STANDARD Euclidean distances (not custom weighted)
distances = cdist(cluster_centroids, expert_points, metric='euclidean')
# Track which endpoints are already matched
unmatched_centroids = set(range(len(cluster_centroids)))
unmatched_experts = set(range(len(expert_points)))
# Create list of valid candidate pairs
valid_pairs = []
for i in range(len(cluster_centroids)):
for j in range(len(expert_points)):
if distances[i, j] <= max_distance:
valid_pairs.append((distances[i, j], i, j))
# Sort by distance (ascending)
valid_pairs.sort()
# Greedy matching
matches = []
match_distances = []
for dist, centroid_idx, expert_idx in valid_pairs:
# Only match if both endpoints are still unmatched
if centroid_idx in unmatched_centroids and expert_idx in unmatched_experts:
matches.append((centroid_idx, expert_idx))
match_distances.append(dist)
unmatched_centroids.remove(centroid_idx)
unmatched_experts.remove(expert_idx)
return matches, match_distances
The 100-pixel threshold:
# Example: Rejecting distant matches
if distance > 100:
# Skip this pair, do not consider for matching
continue
def get_cluster_centroids(points, labels):
"""
Compute centroid for each cluster from DBSCAN labels.
Parameters:
- points: (n_points, 2) array of [x, y] coordinates
- labels: DBSCAN labels (integer, -1 for noise points)
Returns:
- centroids: (n_clusters, 2) array of cluster centroids (excluding noise)
"""
unique_labels = set(labels)
# Remove noise label (-1)
if -1 in unique_labels:
unique_labels.remove(-1)
centroids = []
for label in sorted(unique_labels):
# Get all points with this label
cluster_points = points[labels == label]
# Compute mean coordinate
centroid = cluster_points.mean(axis=0)
centroids.append(centroid)
return np.array(centroids) if centroids else np.empty((0, 2))
from sklearn.cluster import DBSCAN
def cluster_and_match(image_points, expert_points, distance_matrix, epsilon, min_samples):
"""
Complete workflow: DBSCAN → centroids → matching.
"""
# Run DBSCAN with custom distance matrix
clusterer = DBSCAN(eps=epsilon, min_samples=min_samples, metric='precomputed')
labels = clusterer.fit_predict(distance_matrix)
# Compute centroids
centroids = get_cluster_centroids(image_points, labels)
# Match centroids to experts (using standard Euclidean)
matches, match_distances = greedy_match(centroids, expert_points, max_distance=100)
return matches, match_distances
if len(centroids) == 0:
matches = []
match_distances = []
# → Results in: F1 = 0.0, delta = NaN
if len(expert_points) == 0:
matches = []
# → Results in: F1 = 0.0 (recall = 0), delta = NaN
valid_pairs = [] # Empty after filtering by max_distance
matches = []
# → Results in: F1 = 0.0, delta = NaN
if len(centroids) == 1 and len(expert_points) == 1:
distance = euclidean_distance(centroids[0], expert_points[0])
if distance <= 100:
matches = [(0, 0)]
match_distances = [distance]
else:
matches = []
match_distances = []
def verify_matching(matches, n_clusters, n_experts):
"""
Sanity checks for matching result.
"""
# Check 1: No duplicate centroids
centroid_indices = [m[0] for m in matches]
assert len(centroid_indices) == len(set(centroid_indices)), \
"Duplicate centroid in matches"
# Check 2: No duplicate experts
expert_indices = [m[1] for m in matches]
assert len(expert_indices) == len(set(expert_indices)), \
"Duplicate expert in matches"
# Check 3: All indices in valid range
assert all(0 <= i < n_clusters for i in centroid_indices), \
"Centroid index out of range"
assert all(0 <= j < n_experts for j in expert_indices), \
"Expert index out of range"
# Check 4: Matches ≤ min(clusters, experts)
assert len(matches) <= min(n_clusters, n_experts), \
"Too many matches"
print(f"✓ Matching verified: {len(matches)} matches")
Critical: Always use standard Euclidean distance for matching, NOT the custom weighted distance:
# ✓ Correct: Standard Euclidean
distances = cdist(centroids, experts, metric='euclidean')
# ✗ Wrong: Using custom weighted distance would bias matching
# based on hyperparameter choice, not actual spatial accuracy
The custom distance is only for clustering (DBSCAN), not for evaluation or matching.
基于 SOC 职业分类