Training¶
Part of AssetEmbeddings. See also Installation, Data acquisition.
Three model families are supported:
- Recommender Systems (RS) — traditional methods like PCA, suitable for low-dimensional representations.
- Word2Vec (W2V) — shallow neural network that uses the order of investor holdings (sorted by weight from largest to smallest) to predict "masked" assets.
- BERT — Transformer model that generates context-aware embeddings, suitable for modeling ambiguous scenarios like "Is Apple a tech company or a fruit?"
All training entry points share the same five-flag CLI (also in the CLI reference):
uv run python -m asset_embeddings.scripts.train.<model> \
--config CONFIG [--log [LOG ...]] [--result_file RESULT_FILE] \
[--override KEY=VALUE ...] [--verbose]
| Flag | Type | Description |
|---|---|---|
--config, -c |
str |
Path to the JSON config file (examples below). Required. |
--log, -l |
str (0+) |
Log-file path(s). Optional. |
--result_file, -r |
str |
SQLite path for structured results. Optional. |
--override, -o |
str (1+) |
Override config fields, key=value with dot notation (e.g. train.max_epoches=10 optimizer.learning_rate=0.001). |
--verbose, -v |
flag | DEBUG console output (default INFO). |
With --result_file, each run also writes a per-run training summary (train_{bert,w2v,rs}_recordings) via the records system.
AssetRS¶
asset_embeddings.scripts.train.rs trains recommender-system-type embedding
models, corresponding to the first method in Asset Embeddings. It learns
low-dimensional asset embeddings from an investor-asset matrix using PCA.
Config example¶
{
"model_type": "RS_Binary",
"n_components": 4,
"whiten": false,
"data_path": "data/processed/MutualFundShareHoldings/finetune/2023Q2.csv",
"data_format": "csv",
"id_key": "InvestorID",
"portfolio_key": "Portfolio",
"proportion1_key": "Proportion1",
"proportion2_key": "Proportion2",
"save_folder": "checkpoints/finetune/AssetRS/RS_Binary/d4/2023Q2",
"save_name": "AssetRS_Binary_d4_2023Q2",
"save_format": ".pkl",
"seed": 42
}
Config fields¶
| Field | Type | Description |
|---|---|---|
model_type |
str |
RS embedding model type. Supported: RS_Binary, RS_Ranks, RS_Level0, RS_LevelMin. |
n_components |
int |
Embedding dimension (target reduced dimension). |
whiten |
bool |
Whether to whiten the dimensionality-reduction result. |
data_path |
str or list[str] |
Single file, directory, or list of file paths. |
data_format |
str |
Input data format: csv, json, binary. |
id_key |
str |
Investor ID key. |
portfolio_key |
str |
Portfolio key. |
proportion1_key |
str |
Proportion (relative to total shares) key. |
proportion2_key |
str |
Proportion (relative to tradable A-shares) key. |
save_folder |
str |
Save directory. |
save_name |
str |
Model save filename (no extension). |
save_format |
str |
Recommended .pkl. |
seed |
int |
Random seed. |
Note: RS uses proportion data when constructing the investor-asset matrix, so the source data must have been produced with the
--include_proportion Trueflag inasset_embeddings.scripts.data.sharehold.
AssetW2V¶
asset_embeddings.scripts.train.w2v trains asset-level Word2Vec models.
Holding sequences are treated as sentences and stock codes as words; low-dimensional
representations are learned through contextual co-occurrence. Implemented via
Gensim with Skip-Gram / CBOW, negative sampling, and high-frequency downsampling.
This corresponds to Asset Word2Vec in the paper, suitable for modeling asset co-occurrence structure and initializing more complex models (e.g. AssetBERT).
Config example¶
{
"pretrained_model": "checkpoints/pretrained/AssetW2V/d4/AssetW2V_d4_pretrained.model",
"embedding_dim": 4,
"epochs": 10,
"window": 5,
"min_count": 1,
"sg": 1,
"sample": 0.001,
"negative_sample": 10,
"seed": 42,
"data_path": "data/processed/MutualFundShareHoldings/finetune/2023Q2.csv",
"data_format": "csv",
"id_key": "InvestorID",
"portfolio_key": "Portfolio",
"workers": 1,
"save_folder": "checkpoints/finetune/AssetW2V/d4",
"save_name": "AssetW2V_d4_2023Q2",
"save_format": ".model"
}
Config fields¶
| Field | Type | Description |
|---|---|---|
pretrained_model |
str |
Optional pretrained model path (incremental training); null for from-scratch. |
embedding_dim |
int |
Word embedding dimension. |
epochs |
int |
Number of training epochs. |
window |
int |
Context window size. |
min_count |
int |
Ignore stocks appearing fewer times than this (recommended: 1). |
sg |
int |
Skip-Gram (1) or CBOW (0). |
sample |
float |
High-frequency downsampling threshold. |
negative_sample |
int |
Number of negatives per positive sample. |
seed |
int |
Random seed. |
data_path |
str or list[str] |
Input data path(s). |
data_format |
str |
csv, json, binary. |
id_key |
str |
Investor ID key. |
portfolio_key |
str |
Portfolio key. |
workers |
int |
Number of parallel threads. |
save_folder |
str |
Save directory. |
save_name |
str |
Save filename (no extension). |
save_format |
str |
.model or .txt. |
W2V does not consume proportion data, so the
proportion*_keyfields are not present.
AssetBERT¶
AssetBERT is a Transformer-based embedding model that learns context-aware representations (contextualized asset embeddings) of assets from holding sequences. It is the AssetBERT model of the paper (shortened to BERT in figures and tables).
We use AssetW2V embeddings to initialize BERT's embedding layer; the tokenizer vocabulary (excluding special tokens) is shared with AssetW2V.
Config example¶
{
"model": {
"model_type": "BERT",
"model_checkpoint": null,
"w2v_model": "checkpoints/finetune/AssetW2V/d4/2023Q2/AssetW2V_d4_2023Q2.model",
"embedding_file": null,
"vocab_size": 5236,
"hidden_size": 4,
"num_hidden_layers": 4,
"num_attention_heads": 2,
"intermediate_size": 512,
"max_position_embeddings": 64,
"type_vocab_size": 1,
"freeze_embedding": false,
"freeze_encoder": false
},
"tokenizer": {
"w2v_model": "checkpoints/finetune/AssetW2V/d4/2023Q2/AssetW2V_d4_2023Q2.model",
"vocab_file": null,
"pretrained_tokenizer_file": null,
"alias_file": null
},
"dataset": {
"data_path": "data/processed/MutualFundShareHoldings/finetune/2023Q2.csv",
"data_format": "csv",
"id_key": "InvestorID",
"portfolio_key": "Portfolio",
"proportion1_key": "Proportion1",
"proportion2_key": "Proportion2",
"include_proportion": false,
"max_length": 64,
"mask_prob": 0.15,
"mask_indices": null,
"num_repeats": 1,
"cache_size": 400
},
"dataloader": {
"batch_size": 32,
"shuffle": true,
"num_workers": 4,
"persistent_workers": true,
"pin_memory": true,
"drop_last": false
},
"optimizer": {
"optimizer_type": "PagedAdam8bit",
"optimizer_kwargs": null,
"learning_rate": 0.001,
"lr_scheduler_type": "cosine",
"lr_scheduler_warmup_steps": 0,
"lr_scheduler_train_steps": null,
"lr_scheduler_num_cycles": 0.5,
"lr_scheduler_power": 0
},
"accelerator": {
"mixed_precision": "auto",
"gradient_accumulation_steps": 1,
"accelerator_checkpoint": null,
"use_compile": false,
"compile_backend": "inductor",
"compile_mode": "reduce-overhead",
"compile_fullgraph": false,
"compile_dynamic": false,
"compile_cache_dir": null,
"allow_tf32": true
},
"train": {
"max_epoches": 30,
"validation_split": 0.1,
"test_split": 0.1,
"split_method": "random",
"best_metric": "val_loss",
"clip_grad_norm": null,
"clip_grad_value": null,
"detect_anomaly": false,
"check_per_step": 200,
"report_per_epoch": 1,
"calculate_contextualized_embeddings": true,
"save_per_epoch": 1,
"save_folder": "checkpoints/finetune/AssetBERT/d4/2023Q2",
"save_name": "AssetBERT_d4_2023Q2",
"save_format": ".safetensors",
"seed": 42,
"early_stop_enabled": false,
"early_stop_patience": 5,
"early_stop_min_delta": 0.0,
"early_stop_mode": "min",
"early_stop_relative": true,
"early_stop_metric": "val_loss"
}
}
Config fields¶
The config is in JSON format and groups fields into modules:
model: model structure and initialization.tokenizer: asset sequence encoding.dataset: dataset definition and preprocessing.dataloader: PyTorch DataLoader behavior.optimizer: optimizer and LR scheduler.accelerator: HuggingFace Accelerator, mixed precision,torch.compile.train: training control, validation/test split, early stopping.
| Module | Field | Type | Description |
|---|---|---|---|
model |
model_type |
str |
Fixed as "BERT". |
model_checkpoint |
str |
Pretrained path; null from scratch. |
|
w2v_model |
str |
Word2Vec model for vocab + embedding init; null for random. |
|
embedding_file |
str |
Embedding init CSV; null if not used. |
|
vocab_size |
int |
Asset vocabulary size. | |
hidden_size |
int |
BERT embedding dim. | |
num_hidden_layers |
int |
Number of Transformer encoder layers. | |
num_attention_heads |
int |
Number of attention heads; must divide hidden_size. |
|
intermediate_size |
int |
FFN hidden size, typically hidden_size * 4. |
|
max_position_embeddings |
int |
Max sequence length (max holdings per investor). | |
type_vocab_size |
int |
Type embedding dim. Set to 1. | |
freeze_embedding |
bool |
Freeze embedding-layer weights. | |
freeze_encoder |
bool |
Freeze encoder; fine-tune embeddings + LM head only. | |
tokenizer |
w2v_model |
str |
Word2Vec path for vocab/index mapping. |
vocab_file |
str |
HuggingFace-format vocab file. | |
embedding_file |
str |
Embedding-init CSV consistent with model.embedding_file. |
|
alias_file |
str |
Optional {ticker: name} alias mapping. |
|
dataset |
data_path |
str or list[str] |
Input path(s). |
data_format |
str |
csv / json / binary. |
|
id_key |
str |
Investor ID key. | |
portfolio_key |
str |
Portfolio key. | |
proportion1_key |
str |
Proportion (total shares) key. | |
proportion2_key |
str |
Proportion (tradable A-shares) key. | |
include_proportion |
bool |
Emit (id, proportion) tuples per item. Requires proportion*_key. |
|
max_length |
int |
Max holdings per investor; should match model.max_position_embeddings. |
|
mask_prob |
float |
MLM mask ratio. Mutually exclusive with mask_indices. |
|
mask_indices |
list[int] |
Fixed mask positions. Mutually exclusive with mask_prob. |
|
num_repeats |
int |
Times to repeat dataset per epoch. | |
cache_size |
int |
Streaming-loader cache size. | |
dataloader |
batch_size |
int |
Batch size. |
shuffle |
bool |
Whether to shuffle. | |
num_workers |
int |
Loader processes. | |
persistent_workers |
bool |
Reduce per-epoch worker restart. Default: true. |
|
pin_memory |
bool |
Pin to RAM for GPU transfer. Default: true. |
|
drop_last |
bool |
Drop last incomplete batch. Default: false. |
|
optimizer |
optimizer_type |
str |
Adam, AdamW, Adam8bit, PagedAdam8bit, AdamW8bit, PagedAdamW8bit, Lion, Lion8bit, PagedLion8bit, DAdaptation, SGDNesterov, SGD8bit, Adafactor. |
optimizer_kwargs |
dict |
Optional extras. | |
learning_rate |
float |
Initial LR. | |
lr_scheduler_type |
str |
constant, linear, cosine, cosine_with_restarts, polynomial, adafactor. |
|
lr_scheduler_warmup_steps |
int |
Warmup steps. | |
lr_scheduler_train_steps |
int |
Total steps; null auto-computes. |
|
lr_scheduler_num_cycles |
float |
For cosine schedulers. | |
lr_scheduler_power |
float |
For polynomial scheduler. | |
accelerator |
mixed_precision |
str |
'no', 'fp16', 'bf16', 'auto'. |
gradient_accumulation_steps |
int |
Grad accumulation. | |
accelerator_checkpoint |
str |
Accelerator checkpoint path. | |
use_compile |
bool |
Enable torch.compile. |
|
compile_backend |
str |
"inductor", "eager", "aot_eager". |
|
compile_mode |
str |
"default", "reduce-overhead", "max-autotune". |
|
compile_fullgraph |
bool |
Require full-graph compile. | |
compile_dynamic |
bool |
Enable dynamic shapes. | |
compile_cache_dir |
str |
Persistent compile cache. | |
allow_tf32 |
bool |
TF32 on Ampere/Ada. | |
train |
max_epoches |
int |
Max training epochs. |
validation_split |
float |
Validation split (null / 0 disables). |
|
test_split |
float |
Test split (null / 0 disables). validation + test < 1.0. |
|
split_method |
str |
"random" or "sequential". |
|
best_metric |
str |
"train_loss" or "val_loss". |
|
clip_grad_norm |
float |
L2 grad-clip. | |
clip_grad_value |
float |
Abs grad-clip. | |
detect_anomaly |
bool |
Anomaly detection (debugging). | |
check_per_step |
int |
Log frequency (steps). | |
report_per_epoch |
int |
Intermediate report frequency. | |
calculate_contextualized_embeddings |
bool |
Extract size-weighted contextualized embeddings post-training. Needs include_proportion=true. |
|
save_per_epoch |
int |
Save frequency. | |
keep_best_only |
bool |
Drop non-best checkpoints/embeddings at end. | |
save_folder |
str |
Save directory. | |
save_name |
str |
Filename (no extension). | |
save_format |
str |
Recommended .safetensors. |
|
seed |
int |
Random seed. | |
early_stop_enabled |
bool |
Enable early stopping. | |
early_stop_patience |
int |
Patience epochs. | |
early_stop_min_delta |
float |
Improvement threshold (rel if early_stop_relative=true). |
|
early_stop_mode |
str |
"min" / "max". |
|
early_stop_relative |
bool |
Relative vs absolute improvement. | |
early_stop_metric |
str |
"train_loss" / "val_loss". |
Static vs. contextualized embeddings¶
AssetBERT provides two complementary asset representations.
Static embeddings are read directly from the trained embedding layer. Each token maps to a fixed vector, analogous to standard BERT word embeddings, summarizing asset-level characteristics across all training data. Vocabulary matches AssetW2V (modulo BERT special tokens).
Contextualized embeddings are derived from BERT hidden states processing real investor holding sequences. Following Gabaix et al. (2024), we compute the size-weighted average
over investors \(i\) for each asset \(a\), where:
- \(x_{i,a}\) is the BERT hidden state for asset \(a\) in investor \(i\)'s sequence,
- \(S_{i,a}\) is the investor's shareholding proportion relative to tradable
A-shares (the
proportion2_keyfield).
To enable contextualized extraction:
- Set
dataset.include_proportion=true. - Set
train.calculate_contextualized_embeddings=true. - Configure both
proportion1_keyandproportion2_key.
Both kinds of embeddings are saved as CSV with Token plus Embed_1...Embed_N
columns. Naming distinguishes them:
- Static:
{save_name}_best_embedding.csv - Contextualized:
{save_name}_best_contextual_embedding.csv