--- license: apache-2.0 language: - en library_name: sentence-transformers pipeline_tag: sentence-similarity base_model: sentence-transformers/all-MiniLM-L6-v2 datasets: - FronkonGames/steam-games-dataset tags: - transformers.js - onnx - sentence-transformers - sentence-similarity - feature-extraction - recommendation - video-games - steam --- # gamespace.1 gamespace.1 embeds Steam games into a 128-dimensional space where games that play alike sit close together. It powers the [GameMerge](https://huggingface.co/spaces/GulkoA/gamemerge) recommender, which ranks every game on Steam by cosine similarity to the games you like. It is [all-MiniLM-L6-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2), fine-tuned on 138,920 Steam games, with a trained projection from 384 to 128 dimensions. Output vectors are L2-normalised, so a dot product is the cosine similarity. ## Usage The model reads one short text document per game, built from store metadata. Use `docs.py` from this repo to build documents in exactly the format the model was trained on. ```python from huggingface_hub import hf_hub_download from sentence_transformers import SentenceTransformer import importlib.util model = SentenceTransformer("GulkoA/gamespace.1") spec = importlib.util.spec_from_file_location("docs", hf_hub_download("GulkoA/gamespace.1", "docs.py")) docs = importlib.util.module_from_spec(spec); spec.loader.exec_module(docs) game = { "name": "Hollow Knight", "genres": ["Action", "Adventure", "Indie"], "tags": ["Metroidvania", "Souls-like", "Platformer", "Difficult", "Atmospheric"], # most-voted first "categories": ["Single-player"], "developers": ["Team Cherry"], "short": "Forge your own path in Hollow Knight! An epic action adventure through a vast ruined kingdom of insects and heroes.", "about": "", } vec = model.encode(docs.build_doc(game)) # shape (128,), unit length ``` The resulting document looks like this: ``` Hollow Knight. Genres: Action, Adventure, Indie. Tags: Metroidvania, Souls-like, ... Modes: Single-player. Developer: Team Cherry. Forge your own path... ``` ### In the browser `onnx/model_quantized.onnx` (22 MB, per-channel int8, cosine 0.995 to the PyTorch model) runs with [transformers.js](https://huggingface.co/docs/transformers.js). Apply the 128x384 projection in `dense.bin` (float32 weights, row-major, followed by the 128 biases) to the mean-pooled output: ```js import { pipeline } from "@huggingface/transformers"; const extractor = await pipeline("feature-extraction", "GulkoA/gamespace.1", { dtype: "q8" }); const w = new Float32Array(await (await fetch("https://huggingface.co/GulkoA/gamespace.1/resolve/main/dense.bin")).arrayBuffer()); const { data: x } = await extractor("cozy farming game with fishing", { pooling: "mean", normalize: false }); const y = Array.from({ length: 128 }, (_, i) => w[128 * 384 + i] + x.reduce((s, xj, j) => s + w[i * 384 + j] * xj, 0)); const norm = Math.hypot(...y); const vec = y.map((v) => v / norm); // 128-d, unit length ``` Use `dtype: "fp32"` with `onnx/model.onnx` for output identical to PyTorch. Missing fields are simply left out. Games with no user tags still embed well from their description, since training deliberately hid tags half the time. ## Training - **Data.** The [Steam Games Dataset](https://huggingface.co/datasets/FronkonGames/steam-games-dataset) has 147,352 apps. Soundtracks, demos, playtests, dedicated servers, SDKs, and empty records were removed, leaving 138,920 games. 3,000 tagged games were held out from all training. - **Objective.** Contrastive training uses `CachedMultipleNegativesRankingLoss` with 128 in-batch negatives and 140,000 positive pairs: - 70,000 pairs are two randomly degraded views of the same game. Each view may drop the tag list, the description, or part of each. - 70,000 pairs are two different games whose vote-weighted user-tag profiles have a cosine of at least 0.6. - **Projection.** A `Dense(384→128)` layer, initialised from the PCA basis of the base model's embeddings, is trained end to end. - **Settings.** One epoch, learning rate 3e-5, 128-token documents, fp16, on one RTX 3050 Ti laptop GPU in about 20 minutes. - **Excluded signals.** Review counts, price, playtime, and owner counts are never shown to the model. Similarity reflects what a game is, not how popular it is. ## Evaluation For each held-out game, we retrieve its 10 nearest neighbours from a fixed pool of 30,000 tagged games. The score is the mean cosine between the query's vote-weighted, IDF-scaled tag profile and those of its neighbours. "Description only" embeds the query with its tags removed. | Model | With tags | Description only | |---|---|---| | Random pairs | 0.076 | 0.076 | | all-MiniLM-L6-v2, no fine-tuning | 0.230 | 0.208 | | TSDAE denoising autoencoder, then contrastive | 0.493 | 0.370 | | **gamespace.1, contrastive only** | **0.503** | **0.376** | | Ceiling, matching on true tags | 0.621 | n/a | A TSDAE denoising-autoencoder stage before contrastive training scored the same within noise. A side-by-side look at the neighbours of well-known games slightly favoured the model without it, so gamespace.1 skips that stage. ## Example neighbours | Game | Nearest games | |---|---| | DARK SOULS III | Dark Souls II: Scholar of the First Sin, Dark Souls Remastered, Elden Ring, Lies of P | | Stardew Valley | Staxel, Dinkum, Coral Island, Sun Haven | | Factorio | FOUNDRY, Satisfactory, shapez, Techtonica | | Euro Truck Simulator 2 | American Truck Simulator, Truck & Logistics Simulator, The Bus | ## Limitations - Similarity is about content, so obscure, low-quality, or unreleased games rank alongside well-known ones. - Tags are user-generated and English-centric. Descriptions in other languages embed less reliably. - The catalog snapshot ends in September 2026. ## License The model is licensed under Apache 2.0, the same as the base model. The training data is MIT-licensed.