Feature Extraction
Transformers
Safetensors
English
trace
dialogue
task-oriented-dialogue
contextual-representations
turn-embeddings
custom_code
Instructions to use jumafernandez/trace-deep-ar with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use jumafernandez/trace-deep-ar with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="jumafernandez/trace-deep-ar", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("jumafernandez/trace-deep-ar", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download modeling_trace.py from jumafernandez/trace-deep-ar: direct link, hf CLI and curl.
- Browser
- Download file 12.8 kB
-
https://huggingface.co/jumafernandez/trace-deep-ar/resolve/main/modeling_trace.py
- Command line
-
hf download hf://jumafernandez/trace-deep-ar/modeling_trace.py
-
curl -L -o modeling_trace.py https://huggingface.co/jumafernandez/trace-deep-ar/resolve/main/modeling_trace.py
12.8 kB
| """TRACE and TRACE-Dialog in the transformers format. | |
| TRACE is a BERT-style encoder whose inputs are not tokens but the embeddings of the turns of a dialogue, produced by | |
| a base turn encoder (Dialog2Flow by default). It returns a contextual representation ``h_t`` of each turn; in the | |
| autoregressive mode each turn attends only to past turns, so ``h_t`` can be computed during the conversation. | |
| TRACE-Dialog adds learned [CLS] and [SEP] vectors in the input space and returns ``h_CLS``, a representation of the | |
| whole dialogue. | |
| The layers follow ``modeling_bert.py`` with three differences: continuous inputs projected to the hidden size, | |
| speaker embeddings instead of token types, and positions that index turns. | |
| """ | |
| from __future__ import annotations | |
| import numpy as np | |
| import torch | |
| from torch import nn | |
| from transformers import PreTrainedModel | |
| from .configuration_trace import TraceConfig | |
| CLS_ID, SEP_ID = 1, 2 | |
| ACT2FN = {"gelu": nn.GELU(), "relu": nn.ReLU(), "tanh": nn.Tanh()} | |
| def eager_attention_forward(module, query, key, value, attention_mask, scaling, dropout=0.0): | |
| attn_weights = torch.matmul(query, key.transpose(2, 3)) * scaling | |
| if attention_mask is not None: | |
| attn_weights = attn_weights + attention_mask | |
| attn_weights = nn.functional.softmax(attn_weights, dim=-1) | |
| attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training) | |
| attn_output = torch.matmul(attn_weights, value) | |
| return attn_output.transpose(1, 2).contiguous(), attn_weights | |
| class TraceSelfAttention(nn.Module): | |
| def __init__(self, config: TraceConfig): | |
| super().__init__() | |
| self.attention_head_size = config.hidden_size // config.num_attention_heads | |
| self.scaling = self.attention_head_size ** -0.5 | |
| self.query = nn.Linear(config.hidden_size, config.hidden_size) | |
| self.key = nn.Linear(config.hidden_size, config.hidden_size) | |
| self.value = nn.Linear(config.hidden_size, config.hidden_size) | |
| self.dropout = nn.Dropout(config.attention_probs_dropout_prob) | |
| def forward(self, hidden_states, attention_mask=None): | |
| input_shape = hidden_states.shape[:-1] | |
| shape = (*input_shape, -1, self.attention_head_size) | |
| q = self.query(hidden_states).view(shape).transpose(1, 2) | |
| k = self.key(hidden_states).view(shape).transpose(1, 2) | |
| v = self.value(hidden_states).view(shape).transpose(1, 2) | |
| out, _ = eager_attention_forward(self, q, k, v, attention_mask, self.scaling, | |
| dropout=self.dropout.p if self.training else 0.0) | |
| return out.reshape(*input_shape, -1).contiguous() | |
| class TraceSelfOutput(nn.Module): | |
| def __init__(self, config: TraceConfig): | |
| super().__init__() | |
| self.dense = nn.Linear(config.hidden_size, config.hidden_size) | |
| self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) | |
| self.dropout = nn.Dropout(config.hidden_dropout_prob) | |
| def forward(self, hidden_states, input_tensor): | |
| return self.LayerNorm(self.dropout(self.dense(hidden_states)) + input_tensor) | |
| class TraceAttention(nn.Module): | |
| def __init__(self, config: TraceConfig): | |
| super().__init__() | |
| self.self = TraceSelfAttention(config) | |
| self.output = TraceSelfOutput(config) | |
| def forward(self, hidden_states, attention_mask=None): | |
| return self.output(self.self(hidden_states, attention_mask), hidden_states) | |
| class TraceIntermediate(nn.Module): | |
| def __init__(self, config: TraceConfig): | |
| super().__init__() | |
| self.dense = nn.Linear(config.hidden_size, config.intermediate_size) | |
| self.intermediate_act_fn = ACT2FN[config.hidden_act] | |
| def forward(self, hidden_states): | |
| return self.intermediate_act_fn(self.dense(hidden_states)) | |
| class TraceOutput(nn.Module): | |
| def __init__(self, config: TraceConfig): | |
| super().__init__() | |
| self.dense = nn.Linear(config.intermediate_size, config.hidden_size) | |
| self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) | |
| self.dropout = nn.Dropout(config.hidden_dropout_prob) | |
| def forward(self, hidden_states, input_tensor): | |
| return self.LayerNorm(self.dropout(self.dense(hidden_states)) + input_tensor) | |
| class TraceLayer(nn.Module): | |
| def __init__(self, config: TraceConfig): | |
| super().__init__() | |
| self.attention = TraceAttention(config) | |
| self.intermediate = TraceIntermediate(config) | |
| self.output = TraceOutput(config) | |
| def forward(self, hidden_states, attention_mask=None): | |
| attention_output = self.attention(hidden_states, attention_mask) | |
| return self.output(self.intermediate(attention_output), attention_output) | |
| class TraceEncoder(nn.Module): | |
| def __init__(self, config: TraceConfig): | |
| super().__init__() | |
| self.layer = nn.ModuleList([TraceLayer(config) for _ in range(config.num_hidden_layers)]) | |
| def forward(self, hidden_states, attention_mask=None): | |
| for layer in self.layer: | |
| hidden_states = layer(hidden_states, attention_mask) | |
| return hidden_states | |
| class TraceEmbeddings(nn.Module): | |
| """Turn embeddings projected to the hidden size, plus speaker and position embeddings.""" | |
| def __init__(self, config: TraceConfig): | |
| super().__init__() | |
| self.input_proj = (nn.Linear(config.input_dim, config.hidden_size) | |
| if config.input_dim != config.hidden_size else nn.Identity()) | |
| self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size) | |
| self.speaker_embeddings = (nn.Embedding(config.num_speakers, config.hidden_size) | |
| if config.use_speaker_embeddings else None) | |
| self.num_speakers = config.num_speakers | |
| self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) | |
| self.dropout = nn.Dropout(config.hidden_dropout_prob) | |
| def forward(self, inputs_embeds, speaker_ids=None): | |
| embeddings = self.input_proj(inputs_embeds) | |
| if self.speaker_embeddings is not None and speaker_ids is not None: | |
| embeddings = embeddings + self.speaker_embeddings(speaker_ids.clamp(min=0, max=self.num_speakers - 1)) | |
| positions = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device).unsqueeze(0) | |
| return self.dropout(self.LayerNorm(embeddings + self.position_embeddings(positions))) | |
| class TraceBody(nn.Module): | |
| """Embeddings and encoder (the counterpart of ``BertModel`` without pooler).""" | |
| def __init__(self, config: TraceConfig): | |
| super().__init__() | |
| self.config = config | |
| self.embeddings = TraceEmbeddings(config) | |
| self.encoder = TraceEncoder(config) | |
| def extended_mask(self, attention_mask, seq_len, dtype): | |
| allowed = attention_mask[:, None, None, :].bool() | |
| if self.config.is_causal: | |
| causal = torch.tril(torch.ones(seq_len, seq_len, dtype=torch.bool, device=attention_mask.device)) | |
| allowed = allowed & causal[None, None] | |
| mask = torch.zeros(allowed.shape, dtype=dtype, device=attention_mask.device) | |
| return mask.masked_fill(~allowed, torch.finfo(dtype).min) | |
| def forward(self, inputs_embeds, attention_mask, speaker_ids=None): | |
| hidden = self.embeddings(inputs_embeds, speaker_ids) | |
| return self.encoder(hidden, self.extended_mask(attention_mask, inputs_embeds.shape[1], hidden.dtype)) | |
| class TracePreTrainedModel(PreTrainedModel): | |
| config_class = TraceConfig | |
| base_model_prefix = "bert" | |
| def _init_weights(self, module): | |
| std = self.config.initializer_range | |
| if isinstance(module, nn.Linear): | |
| module.weight.data.normal_(mean=0.0, std=std) | |
| if module.bias is not None: | |
| module.bias.data.zero_() | |
| elif isinstance(module, nn.Embedding): | |
| module.weight.data.normal_(mean=0.0, std=std) | |
| elif isinstance(module, nn.LayerNorm): | |
| module.bias.data.zero_() | |
| module.weight.data.fill_(1.0) | |
| # ------------------------------------------------------------------ helpers shared by both models | |
| def speaker_ids(self, speakers: list[str]) -> list[int]: | |
| unknown = self.config.num_speakers - 1 | |
| return [min(self.config.speaker_map.get(str(s).strip().lower(), unknown), unknown) for s in speakers] | |
| def base_embeddings(self, texts: list[str], base_encoder=None) -> np.ndarray: | |
| """Turn embeddings with the base encoder the model was trained on (loaded on first use).""" | |
| if base_encoder is None: | |
| if getattr(self, "_base", None) is None: | |
| from sentence_transformers import SentenceTransformer | |
| self._base = SentenceTransformer(self.config.base_encoder, device=str(self.device)) | |
| base_encoder = self._base | |
| return np.asarray(base_encoder.encode(texts, batch_size=64, show_progress_bar=False), dtype=np.float32) | |
| class TraceModel(TracePreTrainedModel): | |
| """TRACE: contextual representation ``h_t`` of every turn of a dialogue.""" | |
| def __init__(self, config: TraceConfig): | |
| super().__init__(config) | |
| self.bert = TraceBody(config) | |
| self.output_proj = (nn.Linear(config.hidden_size, config.output_dim) | |
| if config.hidden_size != config.output_dim else nn.Identity()) | |
| self.post_init() | |
| def forward(self, inputs_embeds, attention_mask, speaker_ids=None): | |
| if inputs_embeds.shape[1] > self.config.max_turns: | |
| raise ValueError(f"{inputs_embeds.shape[1]} turns exceed max_turns ({self.config.max_turns})") | |
| return self.output_proj(self.bert(inputs_embeds, attention_mask, speaker_ids)) | |
| def encode_dialogue(self, turns: list[str], speakers: list[str], embeddings: np.ndarray | None = None, | |
| base_encoder=None) -> np.ndarray: | |
| """``h_t`` of every turn, shape ``(n, output_dim)``. Dialogues longer than ``max_turns`` are split into | |
| consecutive windows. Pass ``embeddings`` to reuse precomputed base turn embeddings.""" | |
| self.eval() | |
| e = embeddings if embeddings is not None else self.base_embeddings(turns, base_encoder) | |
| spk = self.speaker_ids(speakers) | |
| out = [] | |
| for start in range(0, len(turns), self.config.max_turns): | |
| end = start + self.config.max_turns | |
| x = torch.as_tensor(e[start:end], dtype=self.dtype, device=self.device)[None] | |
| s = torch.as_tensor(spk[start:end], device=self.device)[None] | |
| att = torch.ones(x.shape[:2], dtype=torch.long, device=self.device) | |
| out.append(self(x, att, s if self.config.use_speaker_embeddings else None)[0].float().cpu().numpy()) | |
| return np.concatenate(out) | |
| class TraceDialogModel(TraceModel): | |
| """TRACE-Dialog: representation ``h_CLS`` of a whole dialogue, from [CLS] turns [SEP].""" | |
| def __init__(self, config: TraceConfig): | |
| super().__init__(config) | |
| self.special_embeddings = nn.Embedding(2, config.input_dim) | |
| self.post_init() | |
| def forward(self, inputs_embeds, attention_mask, speaker_ids=None, special_ids=None): | |
| if special_ids is not None: | |
| x = inputs_embeds.clone() | |
| x[special_ids == CLS_ID] = self.special_embeddings.weight[0].to(x.dtype) | |
| x[special_ids == SEP_ID] = self.special_embeddings.weight[1].to(x.dtype) | |
| inputs_embeds = x | |
| return super().forward(inputs_embeds, attention_mask, speaker_ids) | |
| def encode_dialogue(self, turns: list[str], speakers: list[str], embeddings: np.ndarray | None = None, | |
| base_encoder=None) -> np.ndarray: | |
| """``h_CLS`` of the dialogue, shape ``(output_dim,)``. Only the first ``max_turns - 2`` turns are used.""" | |
| self.eval() | |
| e = embeddings if embeddings is not None else self.base_embeddings(turns, base_encoder) | |
| n = min(len(turns), self.config.max_turns - 2) | |
| x = torch.zeros(1, n + 2, self.config.input_dim, dtype=self.dtype, device=self.device) | |
| x[0, 1:n + 1] = torch.as_tensor(e[:n], dtype=self.dtype, device=self.device) | |
| spk = torch.full((1, n + 2), self.config.num_speakers - 1, dtype=torch.long, device=self.device) | |
| spk[0, 1:n + 1] = torch.as_tensor(self.speaker_ids(speakers[:n]), device=self.device) | |
| special = torch.zeros(1, n + 2, dtype=torch.long, device=self.device) | |
| special[0, 0], special[0, n + 1] = CLS_ID, SEP_ID | |
| att = torch.ones(1, n + 2, dtype=torch.long, device=self.device) | |
| h = self(x, att, spk if self.config.use_speaker_embeddings else None, special) | |
| return h[0, 0].float().cpu().numpy() | |