xinyacs commited on
Commit
7781e94
·
verified ·
1 Parent(s): 1bef6ea

Upload folder using huggingface_hub

Browse files
Files changed (8) hide show
  1. README.md +213 -3
  2. config.json +41 -0
  3. infer.py +104 -0
  4. model.py +469 -0
  5. pytorch_model.bin +3 -0
  6. special_tokens_map.json +37 -0
  7. tokenizer.json +0 -0
  8. tokenizer_config.json +945 -0
README.md CHANGED
@@ -1,3 +1,213 @@
1
- ---
2
- license: apache-2.0
3
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ language:
4
+ - en
5
+ pipeline_tag: token-classification
6
+ tags:
7
+ - named-entity-recognition
8
+ - ner
9
+ - span-ner
10
+ - globalpointer
11
+ - pytorch
12
+ library_name: transformers
13
+ model_name: EcomBert_NER_V1
14
+ ---
15
+
16
+ # EcomBert_NER_V1
17
+
18
+ ## Model description
19
+
20
+ `EcomBert_NER_V1` is a span-based Named Entity Recognition (NER) model built on top of a BERT encoder with a GlobalPointer-style span classification head.
21
+
22
+ This repository exports and loads the model using a lightweight HuggingFace-style folder layout:
23
+
24
+ - `config.json`
25
+ - `pytorch_model.bin`
26
+ - tokenizer files saved by `transformers.AutoTokenizer.save_pretrained(...)`
27
+
28
+ **Parameter size**: ~0.4B parameters (as configured/reported for this model card).
29
+
30
+ ## Intended uses & limitations
31
+
32
+ ### Intended uses
33
+
34
+ - Extracting entity spans from short-to-medium English texts (e.g., product titles, user queries, support tickets).
35
+ - Offline batch inference and evaluation.
36
+
37
+ ### Limitations
38
+
39
+ - This is a span-scoring model: it predicts `(label, start, end)` spans. Overlapping spans are possible.
40
+ - Output quality depends heavily on:
41
+ - the training dataset schema and label definitions
42
+ - the decision threshold (`threshold`)
43
+ - tokenization behavior (subword boundaries)
44
+ - Long inputs will be truncated to `max_length`.
45
+
46
+ ## How to use
47
+
48
+ ### 1) Train and export
49
+
50
+ During training, the best checkpoint is exported to a HuggingFace-style directory (by default `checkpoints/hf_export`).
51
+
52
+ Example:
53
+
54
+ ```bash
55
+ python train.py \
56
+ --splits_dir ./data2/splits \
57
+ --output_dir checkpoints \
58
+ --model_name bert-base-chinese \
59
+ --hf_export_dir hf_export
60
+ ```
61
+
62
+ This produces:
63
+
64
+ - `checkpoints/hf_export/config.json`
65
+ - `checkpoints/hf_export/pytorch_model.bin`
66
+ - `checkpoints/hf_export/tokenizer.*`
67
+
68
+ ### 2) Inference (CLI)
69
+
70
+ ```bash
71
+ python infer.py \
72
+ --model_dir checkpoints/hf_export \
73
+ --text "Apple released a new iPhone in California."
74
+ ```
75
+
76
+ You can optionally override the threshold:
77
+
78
+ ```bash
79
+ python infer.py \
80
+ --model_dir checkpoints/hf_export \
81
+ --text "Apple released a new iPhone in California." \
82
+ --threshold 0.55
83
+ ```
84
+
85
+ ### 3) Inference (Python)
86
+
87
+ ```python
88
+ import torch
89
+ from transformers import AutoTokenizer
90
+ from model import EcomBertNER
91
+
92
+ model_dir = "checkpoints/hf_export"
93
+
94
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
95
+ model, cfg = EcomBertNER.from_pretrained(model_dir, device=device)
96
+
97
+ tokenizer = AutoTokenizer.from_pretrained(model_dir)
98
+ text = "Apple released a new iPhone in California."
99
+
100
+ enc = tokenizer(text, return_tensors="pt", return_offsets_mapping=True)
101
+ input_ids = enc["input_ids"].to(device)
102
+ attention_mask = enc["attention_mask"].to(device)
103
+
104
+ o = model(input_ids=input_ids, attention_mask=attention_mask)
105
+ logits = o["logits"][0] # (C, L, L)
106
+ probs = torch.sigmoid(logits)
107
+ threshold = float(cfg.get("threshold", 0.5))
108
+
109
+ hits = (probs > threshold).nonzero(as_tuple=False)
110
+ print(hits[:10])
111
+ ```
112
+
113
+ ## Few-shot examples
114
+
115
+ The model predicts spans over the following **23 labels**:
116
+
117
+ | Label | Description |
118
+ |---|---|
119
+ | `MAIN_PRODUCT` | Primary product being searched/described |
120
+ | `SUB_PRODUCT` | Secondary / accessory product |
121
+ | `BRAND` | Brand name |
122
+ | `MODEL` | Model number or name |
123
+ | `IP` | IP / licensed character / franchise |
124
+ | `MATERIAL` | Material composition |
125
+ | `COLOR` | Color attribute |
126
+ | `SHAPE` | Shape attribute |
127
+ | `PATTERN` | Pattern or print |
128
+ | `STYLE` | Style descriptor |
129
+ | `FUNCTION` | Function or use-case |
130
+ | `ATTRIBUTE` | Other product attribute |
131
+ | `COMPATIBILITY` | Compatible device / platform |
132
+ | `CROWD` | Target audience |
133
+ | `OCCASION` | Use occasion or scene |
134
+ | `LOCATION` | Geographic / location reference |
135
+ | `MEASUREMENT` | Size, dimension, capacity |
136
+ | `TIME` | Time reference |
137
+ | `QUANTITY` | Count or amount |
138
+ | `SALE` | Promotion or sale information |
139
+ | `SHOP` | Shop or seller name |
140
+ | `CONJ` | Conjunction linking entities |
141
+ | `PREP` | Preposition linking entities |
142
+
143
+ ---
144
+
145
+ ### Example 1
146
+
147
+ **Input**:
148
+
149
+ ```
150
+ "Nike running shoes for men, breathable mesh upper, size 42"
151
+ ```
152
+
153
+ **Expected entities**:
154
+
155
+ - `BRAND`: "Nike"
156
+ - `MAIN_PRODUCT`: "running shoes"
157
+ - `CROWD`: "men"
158
+ - `MATERIAL`: "breathable mesh"
159
+ - `MEASUREMENT`: "size 42"
160
+
161
+ ---
162
+
163
+ ### Example 2
164
+
165
+ **Input**:
166
+
167
+ ```
168
+ "iPhone 15 Pro compatible leather case, black, for outdoor use"
169
+ ```
170
+
171
+ **Expected entities**:
172
+
173
+ - `COMPATIBILITY`: "iPhone 15 Pro"
174
+ - `MAIN_PRODUCT`: "leather case"
175
+ - `MATERIAL`: "leather"
176
+ - `COLOR`: "black"
177
+ - `OCCASION`: "outdoor use"
178
+
179
+ ---
180
+
181
+ ### Example 3
182
+
183
+ **Input**:
184
+
185
+ ```
186
+ "Disney Mickey pattern kids cotton pajamas, 3-piece set, buy 2 get 1 free"
187
+ ```
188
+
189
+ **Expected entities**:
190
+
191
+ - `IP`: "Disney Mickey"
192
+ - `PATTERN`: "Mickey pattern"
193
+ - `CROWD`: "kids"
194
+ - `MATERIAL`: "cotton"
195
+ - `MAIN_PRODUCT`: "pajamas"
196
+ - `QUANTITY`: "3-piece set"
197
+ - `SALE`: "buy 2 get 1 free"
198
+
199
+ ## Training data
200
+
201
+ Not provided in this repository model card.
202
+
203
+ ## Evaluation
204
+
205
+ This repository includes `evaluate.py` for evaluating `.pt` checkpoints produced during training.
206
+
207
+ ## Environmental impact
208
+
209
+ Not measured.
210
+
211
+ ## Citation
212
+
213
+ If you use this work, consider citing your dataset and the BERT/Transformer literature relevant to your setup.
config.json ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "EcomBertNER"
4
+ ],
5
+ "model_name": "/home/jovyan/work/models/answerdotai/ModernBERT-large",
6
+ "num_labels": 23,
7
+ "head_size": 64,
8
+ "loss_type": "circle",
9
+ "use_rope": true,
10
+ "dropout": 0.1,
11
+ "circle_margin": 0.25,
12
+ "circle_gamma": 32.0,
13
+ "best_epoch": 5,
14
+ "best_f1": 0.7364,
15
+ "threshold": 0.45,
16
+ "label_list": [
17
+ "MAIN_PRODUCT",
18
+ "SUB_PRODUCT",
19
+ "BRAND",
20
+ "MODEL",
21
+ "IP",
22
+ "MATERIAL",
23
+ "COLOR",
24
+ "SHAPE",
25
+ "PATTERN",
26
+ "STYLE",
27
+ "FUNCTION",
28
+ "ATTRIBUTE",
29
+ "COMPATIBILITY",
30
+ "CROWD",
31
+ "OCCASION",
32
+ "LOCATION",
33
+ "MEASUREMENT",
34
+ "TIME",
35
+ "QUANTITY",
36
+ "SALE",
37
+ "SHOP",
38
+ "CONJ",
39
+ "PREP"
40
+ ]
41
+ }
infer.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """infer.py — load exported HF-style directory and run NER inference.
2
+
3
+ Usage:
4
+ python infer.py --model_dir checkpoints/hf_export --text "..."
5
+
6
+ Notes:
7
+ - This repo exports a lightweight HF-style folder:
8
+ config.json
9
+ pytorch_model.bin
10
+ tokenizer files (via transformers AutoTokenizer.save_pretrained)
11
+ - The model class is local (EcomBertNER in model.py).
12
+ """
13
+
14
+ import argparse
15
+ from pathlib import Path
16
+
17
+ import torch
18
+ from transformers import AutoTokenizer
19
+
20
+ from model import EcomBertNER
21
+
22
+
23
+ def parse_args():
24
+ p = argparse.ArgumentParser(description="Inference with exported HF-style NER model")
25
+ p.add_argument("--model_dir", type=str, required=True, help="Path to HF export dir")
26
+ p.add_argument("--text", type=str, required=True, help="Input text")
27
+ p.add_argument("--max_length", type=int, default=256)
28
+ p.add_argument("--threshold", type=float, default=None, help="Override threshold (default: config.json or 0.5)")
29
+ p.add_argument("--device", type=str, default=None, help="cuda / cpu; default auto")
30
+ p.add_argument("--cache_dir", type=str, default=None)
31
+ return p.parse_args()
32
+
33
+
34
+ @torch.no_grad()
35
+ def main():
36
+ args = parse_args()
37
+
38
+ model_dir = Path(args.model_dir)
39
+ if not model_dir.exists():
40
+ raise FileNotFoundError(f"model_dir not found: {model_dir}")
41
+
42
+ if args.device is None:
43
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
44
+ else:
45
+ device = torch.device(args.device)
46
+
47
+ model, cfg = EcomBertNER.from_pretrained(model_dir, device=device, cache_dir=args.cache_dir)
48
+
49
+ tokenizer = AutoTokenizer.from_pretrained(model_dir, cache_dir=args.cache_dir)
50
+
51
+ threshold = args.threshold
52
+ if threshold is None:
53
+ threshold = float(cfg.get("threshold", 0.5))
54
+
55
+ enc = tokenizer(
56
+ args.text,
57
+ max_length=args.max_length,
58
+ truncation=True,
59
+ padding=False,
60
+ return_tensors="pt",
61
+ return_offsets_mapping=True,
62
+ )
63
+
64
+ input_ids = enc["input_ids"].to(device)
65
+ attention_mask = enc["attention_mask"].to(device)
66
+ offsets = enc["offset_mapping"][0].tolist()
67
+
68
+ out = model(input_ids=input_ids, attention_mask=attention_mask)
69
+ logits = out["logits"][0] # (C, L, L)
70
+ probs = torch.sigmoid(logits)
71
+
72
+ label_list = cfg.get("label_list")
73
+ if not label_list:
74
+ label_list = [str(i) for i in range(int(cfg.get("num_labels", probs.size(0))))]
75
+
76
+ hits = (probs > threshold).nonzero(as_tuple=False)
77
+
78
+ results = []
79
+ for c, s, e in hits.tolist():
80
+ if s >= len(offsets) or e >= len(offsets):
81
+ continue
82
+ char_s = offsets[s][0]
83
+ char_e = offsets[e][1]
84
+ if char_s == char_e == 0:
85
+ continue
86
+ if char_s < 0 or char_e <= char_s:
87
+ continue
88
+ ent_text = args.text[char_s:char_e]
89
+ results.append({
90
+ "label": label_list[c] if c < len(label_list) else str(c),
91
+ "span": [char_s, char_e],
92
+ "text": ent_text,
93
+ "score": float(probs[c, s, e].item()),
94
+ })
95
+
96
+ results.sort(key=lambda x: (-x["score"], x["span"][0], x["span"][1]))
97
+
98
+ print(f"device={device} threshold={threshold}")
99
+ for r in results:
100
+ print(f"{r['label']}: {r['text']} span={r['span']} score={r['score']:.4f}")
101
+
102
+
103
+ if __name__ == "__main__":
104
+ main()
model.py ADDED
@@ -0,0 +1,469 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ model.py — GlobalPointer-based NER model on top of BERT
3
+
4
+ Changes vs previous version:
5
+ [FIX-1] Circle Loss: correct two-term formulation (Su Jianlin style),
6
+ with margin (m) and scale (gamma) params; no more logaddexp merging.
7
+ [FIX-2] Numerical safety: negated pos_logits no longer turns -1e9 → +1e9;
8
+ we apply the mask BEFORE negation.
9
+ [FIX-3] labels .float() cast inside forward (no silent runtime error / nan).
10
+ [FIX-4] valid_mask (bool, B×L) replaces attention_mask for span masking;
11
+ attention_mask is still passed to the encoder for self-attention.
12
+ [FIX-5] use_rope flag for GlobalPointer's span-level RoPE (independent of
13
+ BERT encoder internals).
14
+ """
15
+
16
+ import json
17
+ from pathlib import Path
18
+ import math
19
+ import torch
20
+ import torch.nn as nn
21
+ import torch.nn.functional as F
22
+ from transformers import AutoModel
23
+
24
+
25
+ # ════════════════════════════════════════════════════════════════════════════
26
+ # EfficientGlobalPointer head
27
+ # - shared q/k projection (hidden -> 2D)
28
+ # - per-label token bias (hidden -> 2C) as start/end bias
29
+ # - final logits: base_span + start_bias + end_bias
30
+ # ════════════════════════════════════════════════════════════════════════════
31
+
32
+ class EfficientGlobalPointer(nn.Module):
33
+ """
34
+ EfficientGlobalPointer span scorer (Su Jianlin style).
35
+
36
+ Differences vs standard GlobalPointer:
37
+ - q/k are shared across labels: hidden -> 2 * head_size
38
+ - label-specific bias per token: hidden -> 2 * num_labels
39
+ (start_bias and end_bias for each label)
40
+ - logits: (q @ k^T)/sqrt(D) expanded to C labels, then add biases
41
+
42
+ Output shape: (B, C, L, L)
43
+ """
44
+
45
+ def __init__(
46
+ self,
47
+ hidden_size: int,
48
+ num_labels: int,
49
+ head_size: int = 64,
50
+ use_rope: bool = True,
51
+ dropout: float = 0.1,
52
+ ):
53
+ super().__init__()
54
+ self.num_labels = num_labels
55
+ self.head_size = head_size
56
+ self.use_rope = use_rope
57
+
58
+ self.dropout = nn.Dropout(dropout)
59
+
60
+ # shared q/k: (H -> 2D)
61
+ self.dense_qk = nn.Linear(hidden_size, head_size * 2)
62
+
63
+ # label bias: (H -> 2C) => per token: start_bias + end_bias
64
+ self.dense_bias = nn.Linear(hidden_size, num_labels * 2)
65
+
66
+ if use_rope:
67
+ self.rope = RotaryEmbedding(head_size)
68
+
69
+ def forward(self, hidden: torch.Tensor) -> torch.Tensor:
70
+ """
71
+ hidden: (B, L, H)
72
+ returns logits: (B, C, L, L)
73
+ """
74
+ B, L, _ = hidden.shape
75
+ C = self.num_labels
76
+ D = self.head_size
77
+
78
+ hidden = self.dropout(hidden)
79
+
80
+ # ── shared q/k ───────────────────────────────────────────────────────
81
+ qk = self.dense_qk(hidden) # (B, L, 2D)
82
+ q, k = qk[..., :D], qk[..., D:] # each (B, L, D)
83
+
84
+ if self.use_rope:
85
+ emb = self.rope(L, hidden.device) # (L, D)
86
+ cos_ = emb.cos()[None, :, :] # (1, L, D)
87
+ sin_ = emb.sin()[None, :, :]
88
+ q = apply_rotary(q, cos_, sin_) # (B, L, D)
89
+ k = apply_rotary(k, cos_, sin_) # (B, L, D)
90
+
91
+ # base span score (shared across labels): (B, L, L)
92
+ base = torch.matmul(q, k.transpose(-1, -2)) / math.sqrt(D)
93
+
94
+ # ── per-label start/end bias ────────────────────────────────────────
95
+ bias = self.dense_bias(hidden) # (B, L, 2C)
96
+ bias = bias.view(B, L, C, 2) # (B, L, C, 2)
97
+
98
+ # start/end: (B, C, L)
99
+ start_bias = bias[..., 0].permute(0, 2, 1) # (B, C, L)
100
+ end_bias = bias[..., 1].permute(0, 2, 1) # (B, C, L)
101
+
102
+ # combine:
103
+ # base: (B, 1, L, L)
104
+ # start_bias: (B, C, L, 1)
105
+ # end_bias: (B, C, 1, L)
106
+ logits = (
107
+ base[:, None, :, :] +
108
+ start_bias[:, :, :, None] +
109
+ end_bias[:, :, None, :]
110
+ ) # (B, C, L, L)
111
+
112
+ return logits
113
+
114
+ # ════════════════════════════════════════════════════════════════════════════
115
+ # RoPE helper (span-level, applied to GlobalPointer q/k)
116
+ # ════════════════════════════════════════════════════════════════════════════
117
+
118
+ class RotaryEmbedding(nn.Module):
119
+ """Rotary Position Embedding for GlobalPointer span scoring."""
120
+
121
+ def __init__(self, dim: int):
122
+ super().__init__()
123
+ assert dim % 2 == 0, "RoPE dim must be even"
124
+ inv_freq = 1.0 / (10000 ** (torch.arange(0, dim, 2).float() / dim))
125
+ self.register_buffer("inv_freq", inv_freq)
126
+
127
+ def forward(self, seq_len: int, device: torch.device) -> torch.Tensor:
128
+ """Returns cos/sin interleaved tensor of shape (seq_len, dim)."""
129
+ t = torch.arange(seq_len, device=device).float()
130
+ freqs = torch.outer(t, self.inv_freq) # (L, dim/2)
131
+ emb = torch.cat([freqs, freqs], dim=-1) # (L, dim)
132
+ return emb # caller does cos/sin
133
+
134
+
135
+ def rotate_half(x: torch.Tensor) -> torch.Tensor:
136
+ half = x.shape[-1] // 2
137
+ x1, x2 = x[..., :half], x[..., half:]
138
+ return torch.cat([-x2, x1], dim=-1)
139
+
140
+
141
+ def apply_rotary(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
142
+ """x: (..., L, D) cos/sin: (L, D)"""
143
+ return x * cos + rotate_half(x) * sin
144
+
145
+
146
+ # ════════════════════════════════════════════════════════════════════════════
147
+ # Loss functions
148
+ # ════════════════════════════════════════════════════════════════════════════
149
+
150
+ def multilabel_circle_loss(
151
+ logits: torch.Tensor, # (B, C, L, L) raw scores
152
+ labels: torch.Tensor, # (B, C, L, L) float 0/1
153
+ mask2d: torch.Tensor, # (B, 1, L, L) bool — True = valid span position
154
+ margin: float = 0.25,
155
+ gamma: float = 32.0,
156
+ ) -> torch.Tensor:
157
+ """
158
+ Su Jianlin–style Circle Loss for multi-label span classification.
159
+
160
+ L = log(1 + Σ exp(γ·(s_neg + m))) + log(1 + Σ exp(−γ·(s_pos − m)))
161
+
162
+ Two independent logsumexp terms keep the original loss geometry intact.
163
+ Mask is applied BEFORE any sign flip to avoid ±1e9 explosions.
164
+
165
+ Args:
166
+ logits: raw span scores, shape (B, C, L, L)
167
+ labels: float tensor {0, 1}, same shape
168
+ mask2d: bool (B, 1, L, L) — True where span is valid (upper-tri + valid tokens)
169
+ margin: additive margin (default 0.25)
170
+ gamma: temperature / scale (default 32)
171
+ """
172
+ B, C, L, _ = logits.shape
173
+
174
+ # ── expand mask to (B, C, L, L) ─────────────────────────────────────────
175
+ mask = mask2d.expand(B, C, L, L) # broadcast over C
176
+
177
+ # ── positions that are valid positive / valid negative ───────────────────
178
+ pos_mask = mask & (labels > 0.5) # bool
179
+ neg_mask = mask & (labels < 0.5) # bool
180
+
181
+ # ── scale logits ─────────────────────────────────────────────────────────
182
+ s = logits * gamma # (B, C, L, L)
183
+
184
+ # ── negative term: log(1 + Σ exp(s_neg + γ·m)) ──────────────────────────
185
+ # Fill invalid & positive positions with -inf so they don't contribute
186
+ neg_scores = s.masked_fill(~neg_mask, float("-inf"))
187
+ # logsumexp over (L, L) for each (b, c)
188
+ neg_lse = torch.logsumexp(neg_scores.view(B, C, -1), dim=-1) # (B, C)
189
+ loss_neg = F.softplus(neg_lse + gamma * margin) # log(1+exp(...))
190
+
191
+ # ── positive term: log(1 + Σ exp(−(s_pos − γ·m))) ───────────────────────
192
+ # Fill invalid & negative positions with -inf (in the negated domain)
193
+ # To avoid -(-1e9) = +1e9: we mask FIRST, then negate.
194
+ pos_scores = s.masked_fill(~pos_mask, float("-inf"))
195
+ neg_pos_scores = (-pos_scores).masked_fill(~pos_mask, float("-inf"))
196
+ pos_lse = torch.logsumexp(neg_pos_scores.view(B, C, -1), dim=-1) # (B, C)
197
+ loss_pos = F.softplus(pos_lse + gamma * margin)
198
+
199
+ # ── average over labels (skip labels with no positive AND no negative) ───
200
+ loss = (loss_neg + loss_pos).mean()
201
+ return loss
202
+
203
+
204
+ def multilabel_bce_loss(
205
+ logits: torch.Tensor, # (B, C, L, L)
206
+ labels: torch.Tensor, # (B, C, L, L) float
207
+ mask2d: torch.Tensor, # (B, 1, L, L) bool
208
+ ) -> torch.Tensor:
209
+ mask = mask2d.expand_as(logits)
210
+ loss = F.binary_cross_entropy_with_logits(logits, labels, reduction="none")
211
+ loss = loss * mask.float()
212
+ return loss.sum() / mask.float().sum().clamp(min=1)
213
+
214
+
215
+ # ════════════════════════════════════════════════════════════════════════════
216
+ # GlobalPointer head
217
+ # ════════════��═══════════════════════════════════════════════════════════════
218
+
219
+ class GlobalPointer(nn.Module):
220
+ """
221
+ GlobalPointer span scorer.
222
+
223
+ Projects encoder hidden states to per-label (q, k) vectors and computes
224
+ an (L×L) score matrix per label. Optionally applies span-level RoPE.
225
+
226
+ Note: encoder internals (inside self-attention layers) are entirely
227
+ separate from this span-level RoPE — both can be active simultaneously.
228
+ """
229
+
230
+ def __init__(
231
+ self,
232
+ hidden_size: int,
233
+ num_labels: int,
234
+ head_size: int = 64,
235
+ use_rope: bool = True,
236
+ dropout: float = 0.1,
237
+ ):
238
+ super().__init__()
239
+ self.num_labels = num_labels
240
+ self.head_size = head_size
241
+ self.use_rope = use_rope
242
+
243
+ self.dropout = nn.Dropout(dropout)
244
+ # Project to 2 * num_labels * head_size (q and k for every label)
245
+ self.dense = nn.Linear(hidden_size, num_labels * head_size * 2)
246
+
247
+ if use_rope:
248
+ self.rope = RotaryEmbedding(head_size)
249
+
250
+ def forward(
251
+ self,
252
+ hidden: torch.Tensor, # (B, L, H)
253
+ ) -> torch.Tensor: # (B, C, L, L)
254
+ B, L, H = hidden.shape
255
+ C = self.num_labels
256
+ D = self.head_size
257
+
258
+ hidden = self.dropout(hidden)
259
+ proj = self.dense(hidden) # (B, L, C*D*2)
260
+ proj = proj.view(B, L, C, D * 2) # (B, L, C, D*2)
261
+ q, k = proj[..., :D], proj[..., D:] # each (B, L, C, D)
262
+
263
+ if self.use_rope:
264
+ emb = self.rope(L, hidden.device) # (L, D)
265
+ cos_ = emb.cos()[None, :, None, :] # (1, L, 1, D)
266
+ sin_ = emb.sin()[None, :, None, :]
267
+ q = apply_rotary(q, cos_, sin_)
268
+ k = apply_rotary(k, cos_, sin_)
269
+
270
+ # q: (B, L, C, D) → (B, C, L, D)
271
+ q = q.permute(0, 2, 1, 3)
272
+ k = k.permute(0, 2, 1, 3)
273
+
274
+ # Score matrix: (B, C, L, D) × (B, C, D, L) → (B, C, L, L)
275
+ logits = torch.matmul(q, k.transpose(-1, -2)) / math.sqrt(D)
276
+
277
+ return logits
278
+
279
+
280
+ # ════════════════════════════════════════════════════════════════════════════
281
+ # Full model
282
+ # ════════════════════════════════════════════════════════════════════════════
283
+
284
+ class EcomBertNER(nn.Module):
285
+ """
286
+ BERT encoder + GlobalPointer head for span-based NER.
287
+
288
+ forward() signature:
289
+ input_ids (B, L) — token ids
290
+ attention_mask (B, L) — passed to encoder (1=real, 0=pad)
291
+ labels (B, C, L, L) torch.bool, optional
292
+ valid_mask (B, L) torch.bool, optional — True = valid token
293
+ (excludes CLS/SEP/PAD; from dataset collate_fn)
294
+
295
+ If valid_mask is not provided, falls back to attention_mask.bool()
296
+ (slightly less precise — includes CLS/SEP as negative spans).
297
+ """
298
+
299
+ def __init__(
300
+ self,
301
+ model_name: str = "bert-base-chinese",
302
+ num_labels: int = 23,
303
+ head_size: int = 64,
304
+ loss_type: str = "circle", # "circle" | "bce"
305
+ use_rope: bool = True,
306
+ dropout: float = 0.1,
307
+ cache_dir: str = None,
308
+ # Circle Loss hyper-params (ignored for BCE)
309
+ circle_margin: float = 0.25,
310
+ circle_gamma: float = 32.0,
311
+ ):
312
+ super().__init__()
313
+ assert loss_type in ("circle", "bce"), \
314
+ f"loss_type must be 'circle' or 'bce', got {loss_type!r}"
315
+
316
+ self.loss_type = loss_type
317
+ self.circle_margin = circle_margin
318
+ self.circle_gamma = circle_gamma
319
+
320
+ self.encoder = AutoModel.from_pretrained(
321
+ model_name, cache_dir=cache_dir
322
+ )
323
+ hidden_size = self.encoder.config.hidden_size
324
+
325
+ self.global_pointer = EfficientGlobalPointer(
326
+ hidden_size = hidden_size,
327
+ num_labels = num_labels,
328
+ head_size = head_size,
329
+ use_rope = use_rope,
330
+ dropout = dropout,
331
+ )
332
+
333
+ self.model_name = model_name
334
+ self.num_labels = num_labels
335
+ self.head_size = head_size
336
+ self.use_rope = use_rope
337
+ self.dropout = dropout
338
+
339
+ # ── span validity mask ────────────────────────────────────────────────────
340
+
341
+ @staticmethod
342
+ def _build_span_mask(
343
+ valid_mask: torch.Tensor, # (B, L) bool
344
+ ) -> torch.Tensor:
345
+ """
346
+ Returns upper-triangular span mask (B, 1, L, L) where
347
+ mask[b,0,i,j] = True iff i<=j and both token i and j are valid.
348
+ """
349
+ # row mask (B, 1, L, 1) & col mask (B, 1, 1, L) → (B, 1, L, L)
350
+ row = valid_mask[:, None, :, None] # (B, 1, L, 1)
351
+ col = valid_mask[:, None, None, :] # (B, 1, 1, L)
352
+ pair_mask = row & col # (B, 1, L, L)
353
+
354
+ L = valid_mask.size(1)
355
+ upper_tri = torch.triu(
356
+ torch.ones(L, L, dtype=torch.bool, device=valid_mask.device)
357
+ ) # (L, L)
358
+
359
+ return pair_mask & upper_tri # (B, 1, L, L)
360
+
361
+ # ── forward ───────────────────────────────────────────────────────────────
362
+
363
+ def forward(
364
+ self,
365
+ input_ids: torch.Tensor, # (B, L)
366
+ attention_mask: torch.Tensor, # (B, L)
367
+ labels: torch.Tensor = None, # (B, C, L, L) bool
368
+ valid_mask: torch.Tensor = None, # (B, L) bool
369
+ ) -> dict:
370
+ # ── encoder ─────────────────────────────────────────────────────────
371
+ encoder_out = self.encoder(
372
+ input_ids = input_ids,
373
+ attention_mask = attention_mask,
374
+ )
375
+ hidden = encoder_out.last_hidden_state # (B, L, H)
376
+
377
+ # ── GlobalPointer logits ─────────────────────────────────────────────
378
+ logits = self.global_pointer(hidden) # (B, C, L, L)
379
+
380
+ # ── span validity mask ───────────────────────────────────────────────
381
+ # [FIX-4] prefer valid_mask (excludes CLS/SEP) over attention_mask
382
+ if valid_mask is None:
383
+ valid_mask = attention_mask.bool()
384
+
385
+ mask2d = self._build_span_mask(valid_mask) # (B, 1, L, L)
386
+
387
+ # Apply mask to logits for inference (fill invalid with -1e4)
388
+ logits_masked = logits.masked_fill(
389
+ ~mask2d.expand_as(logits), -1e4
390
+ )
391
+
392
+ # ── loss ─────────────────────────────────────────────────────────────
393
+ loss = None
394
+ if labels is not None:
395
+ # [FIX-3] ensure float regardless of bool input from dataset
396
+ labels_f = labels.float()
397
+
398
+ if self.loss_type == "circle":
399
+ loss = multilabel_circle_loss(
400
+ logits = logits, # raw (unmasked) scores
401
+ labels = labels_f,
402
+ mask2d = mask2d,
403
+ margin = self.circle_margin,
404
+ gamma = self.circle_gamma,
405
+ )
406
+ else:
407
+ loss = multilabel_bce_loss(
408
+ logits = logits,
409
+ labels = labels_f,
410
+ mask2d = mask2d,
411
+ )
412
+
413
+ return {
414
+ "loss": loss,
415
+ "logits": logits_masked, # (B, C, L, L)
416
+ }
417
+
418
+ def save_pretrained(self, save_directory: str | Path, *, extra_config: dict | None = None) -> None:
419
+ save_dir = Path(save_directory)
420
+ save_dir.mkdir(parents=True, exist_ok=True)
421
+
422
+ config = {
423
+ "architectures": [self.__class__.__name__],
424
+ "model_name": self.model_name,
425
+ "num_labels": self.num_labels,
426
+ "head_size": self.head_size,
427
+ "loss_type": self.loss_type,
428
+ "use_rope": self.use_rope,
429
+ "dropout": self.dropout,
430
+ "circle_margin": self.circle_margin,
431
+ "circle_gamma": self.circle_gamma,
432
+ }
433
+ if extra_config:
434
+ config.update(extra_config)
435
+
436
+ with open(save_dir / "config.json", "w", encoding="utf-8") as f:
437
+ json.dump(config, f, indent=2, ensure_ascii=False)
438
+
439
+ torch.save(self.state_dict(), save_dir / "pytorch_model.bin")
440
+
441
+ @classmethod
442
+ def from_pretrained(
443
+ cls,
444
+ model_dir: str | Path,
445
+ *,
446
+ device: torch.device | str | None = None,
447
+ cache_dir: str | None = None,
448
+ ) -> tuple["EcomBertNER", dict]:
449
+ model_dir = Path(model_dir)
450
+ with open(model_dir / "config.json", "r", encoding="utf-8") as f:
451
+ cfg = json.load(f)
452
+
453
+ model = cls(
454
+ model_name=cfg.get("model_name", "bert-base-chinese"),
455
+ num_labels=int(cfg.get("num_labels", 23)),
456
+ head_size=int(cfg.get("head_size", 64)),
457
+ loss_type=str(cfg.get("loss_type", "circle")),
458
+ use_rope=bool(cfg.get("use_rope", True)),
459
+ dropout=float(cfg.get("dropout", 0.1)),
460
+ cache_dir=cache_dir,
461
+ circle_margin=float(cfg.get("circle_margin", 0.25)),
462
+ circle_gamma=float(cfg.get("circle_gamma", 32.0)),
463
+ )
464
+ state = torch.load(model_dir / "pytorch_model.bin", map_location="cpu", weights_only=False)
465
+ model.load_state_dict(state)
466
+ if device is not None:
467
+ model.to(device)
468
+ model.eval()
469
+ return model, cfg
pytorch_model.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4f68f8b26a690304cb7dc1513c3107cc1919e2a0bd3b76b832b2695a89369fd7
3
+ size 1579917023
special_tokens_map.json ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cls_token": {
3
+ "content": "[CLS]",
4
+ "lstrip": false,
5
+ "normalized": false,
6
+ "rstrip": false,
7
+ "single_word": false
8
+ },
9
+ "mask_token": {
10
+ "content": "[MASK]",
11
+ "lstrip": true,
12
+ "normalized": false,
13
+ "rstrip": false,
14
+ "single_word": false
15
+ },
16
+ "pad_token": {
17
+ "content": "[PAD]",
18
+ "lstrip": false,
19
+ "normalized": false,
20
+ "rstrip": false,
21
+ "single_word": false
22
+ },
23
+ "sep_token": {
24
+ "content": "[SEP]",
25
+ "lstrip": false,
26
+ "normalized": false,
27
+ "rstrip": false,
28
+ "single_word": false
29
+ },
30
+ "unk_token": {
31
+ "content": "[UNK]",
32
+ "lstrip": false,
33
+ "normalized": false,
34
+ "rstrip": false,
35
+ "single_word": false
36
+ }
37
+ }
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,945 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "added_tokens_decoder": {
3
+ "0": {
4
+ "content": "|||IP_ADDRESS|||",
5
+ "lstrip": false,
6
+ "normalized": true,
7
+ "rstrip": false,
8
+ "single_word": false,
9
+ "special": false
10
+ },
11
+ "1": {
12
+ "content": "<|padding|>",
13
+ "lstrip": false,
14
+ "normalized": false,
15
+ "rstrip": false,
16
+ "single_word": false,
17
+ "special": true
18
+ },
19
+ "50254": {
20
+ "content": " ",
21
+ "lstrip": false,
22
+ "normalized": true,
23
+ "rstrip": false,
24
+ "single_word": false,
25
+ "special": false
26
+ },
27
+ "50255": {
28
+ "content": " ",
29
+ "lstrip": false,
30
+ "normalized": true,
31
+ "rstrip": false,
32
+ "single_word": false,
33
+ "special": false
34
+ },
35
+ "50256": {
36
+ "content": " ",
37
+ "lstrip": false,
38
+ "normalized": true,
39
+ "rstrip": false,
40
+ "single_word": false,
41
+ "special": false
42
+ },
43
+ "50257": {
44
+ "content": " ",
45
+ "lstrip": false,
46
+ "normalized": true,
47
+ "rstrip": false,
48
+ "single_word": false,
49
+ "special": false
50
+ },
51
+ "50258": {
52
+ "content": " ",
53
+ "lstrip": false,
54
+ "normalized": true,
55
+ "rstrip": false,
56
+ "single_word": false,
57
+ "special": false
58
+ },
59
+ "50259": {
60
+ "content": " ",
61
+ "lstrip": false,
62
+ "normalized": true,
63
+ "rstrip": false,
64
+ "single_word": false,
65
+ "special": false
66
+ },
67
+ "50260": {
68
+ "content": " ",
69
+ "lstrip": false,
70
+ "normalized": true,
71
+ "rstrip": false,
72
+ "single_word": false,
73
+ "special": false
74
+ },
75
+ "50261": {
76
+ "content": " ",
77
+ "lstrip": false,
78
+ "normalized": true,
79
+ "rstrip": false,
80
+ "single_word": false,
81
+ "special": false
82
+ },
83
+ "50262": {
84
+ "content": " ",
85
+ "lstrip": false,
86
+ "normalized": true,
87
+ "rstrip": false,
88
+ "single_word": false,
89
+ "special": false
90
+ },
91
+ "50263": {
92
+ "content": " ",
93
+ "lstrip": false,
94
+ "normalized": true,
95
+ "rstrip": false,
96
+ "single_word": false,
97
+ "special": false
98
+ },
99
+ "50264": {
100
+ "content": " ",
101
+ "lstrip": false,
102
+ "normalized": true,
103
+ "rstrip": false,
104
+ "single_word": false,
105
+ "special": false
106
+ },
107
+ "50265": {
108
+ "content": " ",
109
+ "lstrip": false,
110
+ "normalized": true,
111
+ "rstrip": false,
112
+ "single_word": false,
113
+ "special": false
114
+ },
115
+ "50266": {
116
+ "content": " ",
117
+ "lstrip": false,
118
+ "normalized": true,
119
+ "rstrip": false,
120
+ "single_word": false,
121
+ "special": false
122
+ },
123
+ "50267": {
124
+ "content": " ",
125
+ "lstrip": false,
126
+ "normalized": true,
127
+ "rstrip": false,
128
+ "single_word": false,
129
+ "special": false
130
+ },
131
+ "50268": {
132
+ "content": " ",
133
+ "lstrip": false,
134
+ "normalized": true,
135
+ "rstrip": false,
136
+ "single_word": false,
137
+ "special": false
138
+ },
139
+ "50269": {
140
+ "content": " ",
141
+ "lstrip": false,
142
+ "normalized": true,
143
+ "rstrip": false,
144
+ "single_word": false,
145
+ "special": false
146
+ },
147
+ "50270": {
148
+ "content": " ",
149
+ "lstrip": false,
150
+ "normalized": true,
151
+ "rstrip": false,
152
+ "single_word": false,
153
+ "special": false
154
+ },
155
+ "50271": {
156
+ "content": " ",
157
+ "lstrip": false,
158
+ "normalized": true,
159
+ "rstrip": false,
160
+ "single_word": false,
161
+ "special": false
162
+ },
163
+ "50272": {
164
+ "content": " ",
165
+ "lstrip": false,
166
+ "normalized": true,
167
+ "rstrip": false,
168
+ "single_word": false,
169
+ "special": false
170
+ },
171
+ "50273": {
172
+ "content": " ",
173
+ "lstrip": false,
174
+ "normalized": true,
175
+ "rstrip": false,
176
+ "single_word": false,
177
+ "special": false
178
+ },
179
+ "50274": {
180
+ "content": " ",
181
+ "lstrip": false,
182
+ "normalized": true,
183
+ "rstrip": false,
184
+ "single_word": false,
185
+ "special": false
186
+ },
187
+ "50275": {
188
+ "content": " ",
189
+ "lstrip": false,
190
+ "normalized": true,
191
+ "rstrip": false,
192
+ "single_word": false,
193
+ "special": false
194
+ },
195
+ "50276": {
196
+ "content": " ",
197
+ "lstrip": false,
198
+ "normalized": true,
199
+ "rstrip": false,
200
+ "single_word": false,
201
+ "special": false
202
+ },
203
+ "50277": {
204
+ "content": "|||EMAIL_ADDRESS|||",
205
+ "lstrip": false,
206
+ "normalized": true,
207
+ "rstrip": false,
208
+ "single_word": false,
209
+ "special": false
210
+ },
211
+ "50278": {
212
+ "content": "|||PHONE_NUMBER|||",
213
+ "lstrip": false,
214
+ "normalized": true,
215
+ "rstrip": false,
216
+ "single_word": false,
217
+ "special": false
218
+ },
219
+ "50279": {
220
+ "content": "<|endoftext|>",
221
+ "lstrip": false,
222
+ "normalized": false,
223
+ "rstrip": false,
224
+ "single_word": false,
225
+ "special": true
226
+ },
227
+ "50280": {
228
+ "content": "[UNK]",
229
+ "lstrip": false,
230
+ "normalized": false,
231
+ "rstrip": false,
232
+ "single_word": false,
233
+ "special": true
234
+ },
235
+ "50281": {
236
+ "content": "[CLS]",
237
+ "lstrip": false,
238
+ "normalized": false,
239
+ "rstrip": false,
240
+ "single_word": false,
241
+ "special": true
242
+ },
243
+ "50282": {
244
+ "content": "[SEP]",
245
+ "lstrip": false,
246
+ "normalized": false,
247
+ "rstrip": false,
248
+ "single_word": false,
249
+ "special": true
250
+ },
251
+ "50283": {
252
+ "content": "[PAD]",
253
+ "lstrip": false,
254
+ "normalized": false,
255
+ "rstrip": false,
256
+ "single_word": false,
257
+ "special": true
258
+ },
259
+ "50284": {
260
+ "content": "[MASK]",
261
+ "lstrip": true,
262
+ "normalized": false,
263
+ "rstrip": false,
264
+ "single_word": false,
265
+ "special": true
266
+ },
267
+ "50285": {
268
+ "content": "[unused0]",
269
+ "lstrip": false,
270
+ "normalized": true,
271
+ "rstrip": false,
272
+ "single_word": false,
273
+ "special": false
274
+ },
275
+ "50286": {
276
+ "content": "[unused1]",
277
+ "lstrip": false,
278
+ "normalized": true,
279
+ "rstrip": false,
280
+ "single_word": false,
281
+ "special": false
282
+ },
283
+ "50287": {
284
+ "content": "[unused2]",
285
+ "lstrip": false,
286
+ "normalized": true,
287
+ "rstrip": false,
288
+ "single_word": false,
289
+ "special": false
290
+ },
291
+ "50288": {
292
+ "content": "[unused3]",
293
+ "lstrip": false,
294
+ "normalized": true,
295
+ "rstrip": false,
296
+ "single_word": false,
297
+ "special": false
298
+ },
299
+ "50289": {
300
+ "content": "[unused4]",
301
+ "lstrip": false,
302
+ "normalized": true,
303
+ "rstrip": false,
304
+ "single_word": false,
305
+ "special": false
306
+ },
307
+ "50290": {
308
+ "content": "[unused5]",
309
+ "lstrip": false,
310
+ "normalized": true,
311
+ "rstrip": false,
312
+ "single_word": false,
313
+ "special": false
314
+ },
315
+ "50291": {
316
+ "content": "[unused6]",
317
+ "lstrip": false,
318
+ "normalized": true,
319
+ "rstrip": false,
320
+ "single_word": false,
321
+ "special": false
322
+ },
323
+ "50292": {
324
+ "content": "[unused7]",
325
+ "lstrip": false,
326
+ "normalized": true,
327
+ "rstrip": false,
328
+ "single_word": false,
329
+ "special": false
330
+ },
331
+ "50293": {
332
+ "content": "[unused8]",
333
+ "lstrip": false,
334
+ "normalized": true,
335
+ "rstrip": false,
336
+ "single_word": false,
337
+ "special": false
338
+ },
339
+ "50294": {
340
+ "content": "[unused9]",
341
+ "lstrip": false,
342
+ "normalized": true,
343
+ "rstrip": false,
344
+ "single_word": false,
345
+ "special": false
346
+ },
347
+ "50295": {
348
+ "content": "[unused10]",
349
+ "lstrip": false,
350
+ "normalized": true,
351
+ "rstrip": false,
352
+ "single_word": false,
353
+ "special": false
354
+ },
355
+ "50296": {
356
+ "content": "[unused11]",
357
+ "lstrip": false,
358
+ "normalized": true,
359
+ "rstrip": false,
360
+ "single_word": false,
361
+ "special": false
362
+ },
363
+ "50297": {
364
+ "content": "[unused12]",
365
+ "lstrip": false,
366
+ "normalized": true,
367
+ "rstrip": false,
368
+ "single_word": false,
369
+ "special": false
370
+ },
371
+ "50298": {
372
+ "content": "[unused13]",
373
+ "lstrip": false,
374
+ "normalized": true,
375
+ "rstrip": false,
376
+ "single_word": false,
377
+ "special": false
378
+ },
379
+ "50299": {
380
+ "content": "[unused14]",
381
+ "lstrip": false,
382
+ "normalized": true,
383
+ "rstrip": false,
384
+ "single_word": false,
385
+ "special": false
386
+ },
387
+ "50300": {
388
+ "content": "[unused15]",
389
+ "lstrip": false,
390
+ "normalized": true,
391
+ "rstrip": false,
392
+ "single_word": false,
393
+ "special": false
394
+ },
395
+ "50301": {
396
+ "content": "[unused16]",
397
+ "lstrip": false,
398
+ "normalized": true,
399
+ "rstrip": false,
400
+ "single_word": false,
401
+ "special": false
402
+ },
403
+ "50302": {
404
+ "content": "[unused17]",
405
+ "lstrip": false,
406
+ "normalized": true,
407
+ "rstrip": false,
408
+ "single_word": false,
409
+ "special": false
410
+ },
411
+ "50303": {
412
+ "content": "[unused18]",
413
+ "lstrip": false,
414
+ "normalized": true,
415
+ "rstrip": false,
416
+ "single_word": false,
417
+ "special": false
418
+ },
419
+ "50304": {
420
+ "content": "[unused19]",
421
+ "lstrip": false,
422
+ "normalized": true,
423
+ "rstrip": false,
424
+ "single_word": false,
425
+ "special": false
426
+ },
427
+ "50305": {
428
+ "content": "[unused20]",
429
+ "lstrip": false,
430
+ "normalized": true,
431
+ "rstrip": false,
432
+ "single_word": false,
433
+ "special": false
434
+ },
435
+ "50306": {
436
+ "content": "[unused21]",
437
+ "lstrip": false,
438
+ "normalized": true,
439
+ "rstrip": false,
440
+ "single_word": false,
441
+ "special": false
442
+ },
443
+ "50307": {
444
+ "content": "[unused22]",
445
+ "lstrip": false,
446
+ "normalized": true,
447
+ "rstrip": false,
448
+ "single_word": false,
449
+ "special": false
450
+ },
451
+ "50308": {
452
+ "content": "[unused23]",
453
+ "lstrip": false,
454
+ "normalized": true,
455
+ "rstrip": false,
456
+ "single_word": false,
457
+ "special": false
458
+ },
459
+ "50309": {
460
+ "content": "[unused24]",
461
+ "lstrip": false,
462
+ "normalized": true,
463
+ "rstrip": false,
464
+ "single_word": false,
465
+ "special": false
466
+ },
467
+ "50310": {
468
+ "content": "[unused25]",
469
+ "lstrip": false,
470
+ "normalized": true,
471
+ "rstrip": false,
472
+ "single_word": false,
473
+ "special": false
474
+ },
475
+ "50311": {
476
+ "content": "[unused26]",
477
+ "lstrip": false,
478
+ "normalized": true,
479
+ "rstrip": false,
480
+ "single_word": false,
481
+ "special": false
482
+ },
483
+ "50312": {
484
+ "content": "[unused27]",
485
+ "lstrip": false,
486
+ "normalized": true,
487
+ "rstrip": false,
488
+ "single_word": false,
489
+ "special": false
490
+ },
491
+ "50313": {
492
+ "content": "[unused28]",
493
+ "lstrip": false,
494
+ "normalized": true,
495
+ "rstrip": false,
496
+ "single_word": false,
497
+ "special": false
498
+ },
499
+ "50314": {
500
+ "content": "[unused29]",
501
+ "lstrip": false,
502
+ "normalized": true,
503
+ "rstrip": false,
504
+ "single_word": false,
505
+ "special": false
506
+ },
507
+ "50315": {
508
+ "content": "[unused30]",
509
+ "lstrip": false,
510
+ "normalized": true,
511
+ "rstrip": false,
512
+ "single_word": false,
513
+ "special": false
514
+ },
515
+ "50316": {
516
+ "content": "[unused31]",
517
+ "lstrip": false,
518
+ "normalized": true,
519
+ "rstrip": false,
520
+ "single_word": false,
521
+ "special": false
522
+ },
523
+ "50317": {
524
+ "content": "[unused32]",
525
+ "lstrip": false,
526
+ "normalized": true,
527
+ "rstrip": false,
528
+ "single_word": false,
529
+ "special": false
530
+ },
531
+ "50318": {
532
+ "content": "[unused33]",
533
+ "lstrip": false,
534
+ "normalized": true,
535
+ "rstrip": false,
536
+ "single_word": false,
537
+ "special": false
538
+ },
539
+ "50319": {
540
+ "content": "[unused34]",
541
+ "lstrip": false,
542
+ "normalized": true,
543
+ "rstrip": false,
544
+ "single_word": false,
545
+ "special": false
546
+ },
547
+ "50320": {
548
+ "content": "[unused35]",
549
+ "lstrip": false,
550
+ "normalized": true,
551
+ "rstrip": false,
552
+ "single_word": false,
553
+ "special": false
554
+ },
555
+ "50321": {
556
+ "content": "[unused36]",
557
+ "lstrip": false,
558
+ "normalized": true,
559
+ "rstrip": false,
560
+ "single_word": false,
561
+ "special": false
562
+ },
563
+ "50322": {
564
+ "content": "[unused37]",
565
+ "lstrip": false,
566
+ "normalized": true,
567
+ "rstrip": false,
568
+ "single_word": false,
569
+ "special": false
570
+ },
571
+ "50323": {
572
+ "content": "[unused38]",
573
+ "lstrip": false,
574
+ "normalized": true,
575
+ "rstrip": false,
576
+ "single_word": false,
577
+ "special": false
578
+ },
579
+ "50324": {
580
+ "content": "[unused39]",
581
+ "lstrip": false,
582
+ "normalized": true,
583
+ "rstrip": false,
584
+ "single_word": false,
585
+ "special": false
586
+ },
587
+ "50325": {
588
+ "content": "[unused40]",
589
+ "lstrip": false,
590
+ "normalized": true,
591
+ "rstrip": false,
592
+ "single_word": false,
593
+ "special": false
594
+ },
595
+ "50326": {
596
+ "content": "[unused41]",
597
+ "lstrip": false,
598
+ "normalized": true,
599
+ "rstrip": false,
600
+ "single_word": false,
601
+ "special": false
602
+ },
603
+ "50327": {
604
+ "content": "[unused42]",
605
+ "lstrip": false,
606
+ "normalized": true,
607
+ "rstrip": false,
608
+ "single_word": false,
609
+ "special": false
610
+ },
611
+ "50328": {
612
+ "content": "[unused43]",
613
+ "lstrip": false,
614
+ "normalized": true,
615
+ "rstrip": false,
616
+ "single_word": false,
617
+ "special": false
618
+ },
619
+ "50329": {
620
+ "content": "[unused44]",
621
+ "lstrip": false,
622
+ "normalized": true,
623
+ "rstrip": false,
624
+ "single_word": false,
625
+ "special": false
626
+ },
627
+ "50330": {
628
+ "content": "[unused45]",
629
+ "lstrip": false,
630
+ "normalized": true,
631
+ "rstrip": false,
632
+ "single_word": false,
633
+ "special": false
634
+ },
635
+ "50331": {
636
+ "content": "[unused46]",
637
+ "lstrip": false,
638
+ "normalized": true,
639
+ "rstrip": false,
640
+ "single_word": false,
641
+ "special": false
642
+ },
643
+ "50332": {
644
+ "content": "[unused47]",
645
+ "lstrip": false,
646
+ "normalized": true,
647
+ "rstrip": false,
648
+ "single_word": false,
649
+ "special": false
650
+ },
651
+ "50333": {
652
+ "content": "[unused48]",
653
+ "lstrip": false,
654
+ "normalized": true,
655
+ "rstrip": false,
656
+ "single_word": false,
657
+ "special": false
658
+ },
659
+ "50334": {
660
+ "content": "[unused49]",
661
+ "lstrip": false,
662
+ "normalized": true,
663
+ "rstrip": false,
664
+ "single_word": false,
665
+ "special": false
666
+ },
667
+ "50335": {
668
+ "content": "[unused50]",
669
+ "lstrip": false,
670
+ "normalized": true,
671
+ "rstrip": false,
672
+ "single_word": false,
673
+ "special": false
674
+ },
675
+ "50336": {
676
+ "content": "[unused51]",
677
+ "lstrip": false,
678
+ "normalized": true,
679
+ "rstrip": false,
680
+ "single_word": false,
681
+ "special": false
682
+ },
683
+ "50337": {
684
+ "content": "[unused52]",
685
+ "lstrip": false,
686
+ "normalized": true,
687
+ "rstrip": false,
688
+ "single_word": false,
689
+ "special": false
690
+ },
691
+ "50338": {
692
+ "content": "[unused53]",
693
+ "lstrip": false,
694
+ "normalized": true,
695
+ "rstrip": false,
696
+ "single_word": false,
697
+ "special": false
698
+ },
699
+ "50339": {
700
+ "content": "[unused54]",
701
+ "lstrip": false,
702
+ "normalized": true,
703
+ "rstrip": false,
704
+ "single_word": false,
705
+ "special": false
706
+ },
707
+ "50340": {
708
+ "content": "[unused55]",
709
+ "lstrip": false,
710
+ "normalized": true,
711
+ "rstrip": false,
712
+ "single_word": false,
713
+ "special": false
714
+ },
715
+ "50341": {
716
+ "content": "[unused56]",
717
+ "lstrip": false,
718
+ "normalized": true,
719
+ "rstrip": false,
720
+ "single_word": false,
721
+ "special": false
722
+ },
723
+ "50342": {
724
+ "content": "[unused57]",
725
+ "lstrip": false,
726
+ "normalized": true,
727
+ "rstrip": false,
728
+ "single_word": false,
729
+ "special": false
730
+ },
731
+ "50343": {
732
+ "content": "[unused58]",
733
+ "lstrip": false,
734
+ "normalized": true,
735
+ "rstrip": false,
736
+ "single_word": false,
737
+ "special": false
738
+ },
739
+ "50344": {
740
+ "content": "[unused59]",
741
+ "lstrip": false,
742
+ "normalized": true,
743
+ "rstrip": false,
744
+ "single_word": false,
745
+ "special": false
746
+ },
747
+ "50345": {
748
+ "content": "[unused60]",
749
+ "lstrip": false,
750
+ "normalized": true,
751
+ "rstrip": false,
752
+ "single_word": false,
753
+ "special": false
754
+ },
755
+ "50346": {
756
+ "content": "[unused61]",
757
+ "lstrip": false,
758
+ "normalized": true,
759
+ "rstrip": false,
760
+ "single_word": false,
761
+ "special": false
762
+ },
763
+ "50347": {
764
+ "content": "[unused62]",
765
+ "lstrip": false,
766
+ "normalized": true,
767
+ "rstrip": false,
768
+ "single_word": false,
769
+ "special": false
770
+ },
771
+ "50348": {
772
+ "content": "[unused63]",
773
+ "lstrip": false,
774
+ "normalized": true,
775
+ "rstrip": false,
776
+ "single_word": false,
777
+ "special": false
778
+ },
779
+ "50349": {
780
+ "content": "[unused64]",
781
+ "lstrip": false,
782
+ "normalized": true,
783
+ "rstrip": false,
784
+ "single_word": false,
785
+ "special": false
786
+ },
787
+ "50350": {
788
+ "content": "[unused65]",
789
+ "lstrip": false,
790
+ "normalized": true,
791
+ "rstrip": false,
792
+ "single_word": false,
793
+ "special": false
794
+ },
795
+ "50351": {
796
+ "content": "[unused66]",
797
+ "lstrip": false,
798
+ "normalized": true,
799
+ "rstrip": false,
800
+ "single_word": false,
801
+ "special": false
802
+ },
803
+ "50352": {
804
+ "content": "[unused67]",
805
+ "lstrip": false,
806
+ "normalized": true,
807
+ "rstrip": false,
808
+ "single_word": false,
809
+ "special": false
810
+ },
811
+ "50353": {
812
+ "content": "[unused68]",
813
+ "lstrip": false,
814
+ "normalized": true,
815
+ "rstrip": false,
816
+ "single_word": false,
817
+ "special": false
818
+ },
819
+ "50354": {
820
+ "content": "[unused69]",
821
+ "lstrip": false,
822
+ "normalized": true,
823
+ "rstrip": false,
824
+ "single_word": false,
825
+ "special": false
826
+ },
827
+ "50355": {
828
+ "content": "[unused70]",
829
+ "lstrip": false,
830
+ "normalized": true,
831
+ "rstrip": false,
832
+ "single_word": false,
833
+ "special": false
834
+ },
835
+ "50356": {
836
+ "content": "[unused71]",
837
+ "lstrip": false,
838
+ "normalized": true,
839
+ "rstrip": false,
840
+ "single_word": false,
841
+ "special": false
842
+ },
843
+ "50357": {
844
+ "content": "[unused72]",
845
+ "lstrip": false,
846
+ "normalized": true,
847
+ "rstrip": false,
848
+ "single_word": false,
849
+ "special": false
850
+ },
851
+ "50358": {
852
+ "content": "[unused73]",
853
+ "lstrip": false,
854
+ "normalized": true,
855
+ "rstrip": false,
856
+ "single_word": false,
857
+ "special": false
858
+ },
859
+ "50359": {
860
+ "content": "[unused74]",
861
+ "lstrip": false,
862
+ "normalized": true,
863
+ "rstrip": false,
864
+ "single_word": false,
865
+ "special": false
866
+ },
867
+ "50360": {
868
+ "content": "[unused75]",
869
+ "lstrip": false,
870
+ "normalized": true,
871
+ "rstrip": false,
872
+ "single_word": false,
873
+ "special": false
874
+ },
875
+ "50361": {
876
+ "content": "[unused76]",
877
+ "lstrip": false,
878
+ "normalized": true,
879
+ "rstrip": false,
880
+ "single_word": false,
881
+ "special": false
882
+ },
883
+ "50362": {
884
+ "content": "[unused77]",
885
+ "lstrip": false,
886
+ "normalized": true,
887
+ "rstrip": false,
888
+ "single_word": false,
889
+ "special": false
890
+ },
891
+ "50363": {
892
+ "content": "[unused78]",
893
+ "lstrip": false,
894
+ "normalized": true,
895
+ "rstrip": false,
896
+ "single_word": false,
897
+ "special": false
898
+ },
899
+ "50364": {
900
+ "content": "[unused79]",
901
+ "lstrip": false,
902
+ "normalized": true,
903
+ "rstrip": false,
904
+ "single_word": false,
905
+ "special": false
906
+ },
907
+ "50365": {
908
+ "content": "[unused80]",
909
+ "lstrip": false,
910
+ "normalized": true,
911
+ "rstrip": false,
912
+ "single_word": false,
913
+ "special": false
914
+ },
915
+ "50366": {
916
+ "content": "[unused81]",
917
+ "lstrip": false,
918
+ "normalized": true,
919
+ "rstrip": false,
920
+ "single_word": false,
921
+ "special": false
922
+ },
923
+ "50367": {
924
+ "content": "[unused82]",
925
+ "lstrip": false,
926
+ "normalized": true,
927
+ "rstrip": false,
928
+ "single_word": false,
929
+ "special": false
930
+ }
931
+ },
932
+ "clean_up_tokenization_spaces": true,
933
+ "cls_token": "[CLS]",
934
+ "extra_special_tokens": {},
935
+ "mask_token": "[MASK]",
936
+ "model_input_names": [
937
+ "input_ids",
938
+ "attention_mask"
939
+ ],
940
+ "model_max_length": 8192,
941
+ "pad_token": "[PAD]",
942
+ "sep_token": "[SEP]",
943
+ "tokenizer_class": "PreTrainedTokenizerFast",
944
+ "unk_token": "[UNK]"
945
+ }