hyejong commited on
Commit
fd91b40
·
verified ·
1 Parent(s): 392fbe7

Fix inference layout and remove packaging files

Browse files
.gitignore DELETED
@@ -1,34 +0,0 @@
1
- # Python
2
- __pycache__/
3
- *.py[cod]
4
- .pytest_cache/
5
- *.egg-info/
6
-
7
- # Virtual environment
8
- .venv/
9
- venv/
10
-
11
- # IDE
12
- .vscode/
13
- .idea/
14
-
15
- # Environment and credentials
16
- .env
17
- *.token
18
-
19
- # Hugging Face cache
20
- .hf_cache/
21
- .cache/
22
- huggingface/
23
-
24
- # Large/local data
25
- data/
26
- models/
27
- checkpoints/
28
- outputs/
29
- results/
30
- logs/
31
- *.log
32
-
33
- # OS
34
- .DS_Store
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
configuration_mistral.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ """Minimal local configuration for the GenomeOcean custom Mistral model."""
3
+
4
+ from transformers.configuration_utils import PretrainedConfig
5
+
6
+
7
+ class MistralConfig(PretrainedConfig):
8
+ model_type = "mistral"
9
+ keys_to_ignore_at_inference = ["past_key_values"]
10
+
11
+ def __init__(
12
+ self,
13
+ vocab_size=32000,
14
+ hidden_size=4096,
15
+ intermediate_size=14336,
16
+ num_hidden_layers=32,
17
+ num_attention_heads=32,
18
+ num_key_value_heads=8,
19
+ hidden_act="silu",
20
+ max_position_embeddings=4096 * 32,
21
+ initializer_range=0.02,
22
+ rms_norm_eps=1e-6,
23
+ use_cache=True,
24
+ pad_token_id=None,
25
+ bos_token_id=1,
26
+ eos_token_id=2,
27
+ tie_word_embeddings=False,
28
+ rope_theta=10000.0,
29
+ sliding_window=4096,
30
+ attention_dropout=0.0,
31
+ **kwargs,
32
+ ):
33
+ self.vocab_size = vocab_size
34
+ self.max_position_embeddings = max_position_embeddings
35
+ self.hidden_size = hidden_size
36
+ self.intermediate_size = intermediate_size
37
+ self.num_hidden_layers = num_hidden_layers
38
+ self.num_attention_heads = num_attention_heads
39
+ self.sliding_window = sliding_window
40
+ self.num_key_value_heads = num_key_value_heads or num_attention_heads
41
+ self.hidden_act = hidden_act
42
+ self.initializer_range = initializer_range
43
+ self.rms_norm_eps = rms_norm_eps
44
+ self.use_cache = use_cache
45
+ self.rope_theta = rope_theta
46
+ self.attention_dropout = attention_dropout
47
+ super().__init__(
48
+ pad_token_id=pad_token_id,
49
+ bos_token_id=bos_token_id,
50
+ eos_token_id=eos_token_id,
51
+ tie_word_embeddings=tie_word_embeddings,
52
+ **kwargs,
53
+ )
ensemble_manifest.json CHANGED
@@ -2,7 +2,6 @@
2
  "folds": [
3
  {
4
  "fold": "fold1",
5
- "source": "/mnt/taskmaster1/scratch/hyejong/01_gv_genomeocean/finetuning_go_sub_add/ft_models/train_100M_v1.2_5kb/fold1/checkpoint-44240",
6
  "files": [
7
  "config.json",
8
  "configuration_mistral.py",
@@ -15,7 +14,6 @@
15
  },
16
  {
17
  "fold": "fold2",
18
- "source": "/mnt/taskmaster1/scratch/hyejong/01_gv_genomeocean/finetuning_go_sub_add/ft_models/train_100M_v1.2_5kb/fold2/checkpoint-48620",
19
  "files": [
20
  "config.json",
21
  "configuration_mistral.py",
@@ -28,7 +26,6 @@
28
  },
29
  {
30
  "fold": "fold3",
31
- "source": "/mnt/taskmaster1/scratch/hyejong/01_gv_genomeocean/finetuning_go_sub_add/ft_models/train_100M_v1.2_5kb/fold3/checkpoint-46530",
32
  "files": [
33
  "config.json",
34
  "configuration_mistral.py",
@@ -41,7 +38,6 @@
41
  },
42
  {
43
  "fold": "fold4",
44
- "source": "/mnt/taskmaster1/scratch/hyejong/01_gv_genomeocean/finetuning_go_sub_add/ft_models/train_100M_v1.2_5kb/fold4/checkpoint-44195",
45
  "files": [
46
  "config.json",
47
  "configuration_mistral.py",
@@ -54,7 +50,6 @@
54
  },
55
  {
56
  "fold": "fold5",
57
- "source": "/mnt/taskmaster1/scratch/hyejong/01_gv_genomeocean/finetuning_go_sub_add/ft_models/train_100M_v1.2_5kb/fold5/checkpoint-46130",
58
  "files": [
59
  "config.json",
60
  "configuration_mistral.py",
 
2
  "folds": [
3
  {
4
  "fold": "fold1",
 
5
  "files": [
6
  "config.json",
7
  "configuration_mistral.py",
 
14
  },
15
  {
16
  "fold": "fold2",
 
17
  "files": [
18
  "config.json",
19
  "configuration_mistral.py",
 
26
  },
27
  {
28
  "fold": "fold3",
 
29
  "files": [
30
  "config.json",
31
  "configuration_mistral.py",
 
38
  },
39
  {
40
  "fold": "fold4",
 
41
  "files": [
42
  "config.json",
43
  "configuration_mistral.py",
 
50
  },
51
  {
52
  "fold": "fold5",
 
53
  "files": [
54
  "config.json",
55
  "configuration_mistral.py",
examples/example_ncldv_mirus.fna DELETED
@@ -1,26 +0,0 @@
1
- >synthetic_sub_example 5000 clean bases; N separators are removed by preprocessing
2
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
3
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
4
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
5
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
6
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
7
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
8
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
9
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
10
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
11
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
12
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
13
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
14
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
15
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
16
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
17
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
18
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
19
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
20
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
21
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
22
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
23
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
24
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
25
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
26
- CCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCCNCCCCCCCCCC
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fold1/config.json CHANGED
@@ -16,9 +16,17 @@
16
  "eos_token_id": null,
17
  "hidden_act": "silu",
18
  "hidden_size": 768,
 
 
 
 
19
  "initializer_range": 0.02,
20
  "intermediate_size": 3072,
21
  "is_causal": true,
 
 
 
 
22
  "max_position_embeddings": 32768,
23
  "model_type": "mistral",
24
  "num_attention_heads": 8,
 
16
  "eos_token_id": null,
17
  "hidden_act": "silu",
18
  "hidden_size": 768,
19
+ "id2label": {
20
+ "0": "NCLDV",
21
+ "1": "Mirus"
22
+ },
23
  "initializer_range": 0.02,
24
  "intermediate_size": 3072,
25
  "is_causal": true,
26
+ "label2id": {
27
+ "Mirus": 1,
28
+ "NCLDV": 0
29
+ },
30
  "max_position_embeddings": 32768,
31
  "model_type": "mistral",
32
  "num_attention_heads": 8,
fold2/config.json CHANGED
@@ -16,9 +16,17 @@
16
  "eos_token_id": null,
17
  "hidden_act": "silu",
18
  "hidden_size": 768,
 
 
 
 
19
  "initializer_range": 0.02,
20
  "intermediate_size": 3072,
21
  "is_causal": true,
 
 
 
 
22
  "max_position_embeddings": 32768,
23
  "model_type": "mistral",
24
  "num_attention_heads": 8,
 
16
  "eos_token_id": null,
17
  "hidden_act": "silu",
18
  "hidden_size": 768,
19
+ "id2label": {
20
+ "0": "NCLDV",
21
+ "1": "Mirus"
22
+ },
23
  "initializer_range": 0.02,
24
  "intermediate_size": 3072,
25
  "is_causal": true,
26
+ "label2id": {
27
+ "Mirus": 1,
28
+ "NCLDV": 0
29
+ },
30
  "max_position_embeddings": 32768,
31
  "model_type": "mistral",
32
  "num_attention_heads": 8,
fold3/config.json CHANGED
@@ -16,9 +16,17 @@
16
  "eos_token_id": null,
17
  "hidden_act": "silu",
18
  "hidden_size": 768,
 
 
 
 
19
  "initializer_range": 0.02,
20
  "intermediate_size": 3072,
21
  "is_causal": true,
 
 
 
 
22
  "max_position_embeddings": 32768,
23
  "model_type": "mistral",
24
  "num_attention_heads": 8,
 
16
  "eos_token_id": null,
17
  "hidden_act": "silu",
18
  "hidden_size": 768,
19
+ "id2label": {
20
+ "0": "NCLDV",
21
+ "1": "Mirus"
22
+ },
23
  "initializer_range": 0.02,
24
  "intermediate_size": 3072,
25
  "is_causal": true,
26
+ "label2id": {
27
+ "Mirus": 1,
28
+ "NCLDV": 0
29
+ },
30
  "max_position_embeddings": 32768,
31
  "model_type": "mistral",
32
  "num_attention_heads": 8,
fold4/config.json CHANGED
@@ -16,9 +16,17 @@
16
  "eos_token_id": null,
17
  "hidden_act": "silu",
18
  "hidden_size": 768,
 
 
 
 
19
  "initializer_range": 0.02,
20
  "intermediate_size": 3072,
21
  "is_causal": true,
 
 
 
 
22
  "max_position_embeddings": 32768,
23
  "model_type": "mistral",
24
  "num_attention_heads": 8,
 
16
  "eos_token_id": null,
17
  "hidden_act": "silu",
18
  "hidden_size": 768,
19
+ "id2label": {
20
+ "0": "NCLDV",
21
+ "1": "Mirus"
22
+ },
23
  "initializer_range": 0.02,
24
  "intermediate_size": 3072,
25
  "is_causal": true,
26
+ "label2id": {
27
+ "Mirus": 1,
28
+ "NCLDV": 0
29
+ },
30
  "max_position_embeddings": 32768,
31
  "model_type": "mistral",
32
  "num_attention_heads": 8,
fold5/config.json CHANGED
@@ -16,9 +16,17 @@
16
  "eos_token_id": null,
17
  "hidden_act": "silu",
18
  "hidden_size": 768,
 
 
 
 
19
  "initializer_range": 0.02,
20
  "intermediate_size": 3072,
21
  "is_causal": true,
 
 
 
 
22
  "max_position_embeddings": 32768,
23
  "model_type": "mistral",
24
  "num_attention_heads": 8,
 
16
  "eos_token_id": null,
17
  "hidden_act": "silu",
18
  "hidden_size": 768,
19
+ "id2label": {
20
+ "0": "NCLDV",
21
+ "1": "Mirus"
22
+ },
23
  "initializer_range": 0.02,
24
  "intermediate_size": 3072,
25
  "is_causal": true,
26
+ "label2id": {
27
+ "Mirus": 1,
28
+ "NCLDV": 0
29
+ },
30
  "max_position_embeddings": 32768,
31
  "model_type": "mistral",
32
  "num_attention_heads": 8,
modeling_mistral.py ADDED
@@ -0,0 +1,1617 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Copyright 2023 Mistral AI and the HuggingFace Inc. team. All rights reserved.
3
+ #
4
+ # This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
5
+ # and OPT implementations in this library. It has been modified from its
6
+ # original forms to accommodate minor architectural differences compared
7
+ # to GPT-NeoX and OPT used by the Meta AI team that trained the model.
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+ """ PyTorch Mistral model."""
21
+ import inspect
22
+ import math
23
+ import warnings
24
+ from typing import List, Optional, Tuple, Union
25
+ from dataclasses import dataclass
26
+
27
+ import torch
28
+ import torch.nn.functional as F
29
+ import torch.utils.checkpoint
30
+ from torch import nn
31
+ from torch.nn import BCEWithLogitsLoss, CrossEntropyLoss, MSELoss
32
+
33
+ from transformers.activations import ACT2FN
34
+ from transformers.cache_utils import Cache, DynamicCache
35
+ from transformers.modeling_attn_mask_utils import (
36
+ _prepare_4d_causal_attention_mask,
37
+ _prepare_4d_causal_attention_mask_for_sdpa,
38
+ _prepare_4d_attention_mask,
39
+ _prepare_4d_attention_mask_for_sdpa,
40
+ )
41
+ from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast, SequenceClassifierOutputWithPast, ModelOutput
42
+ from transformers.modeling_utils import PreTrainedModel
43
+ from transformers.utils import (
44
+ add_start_docstrings,
45
+ add_start_docstrings_to_model_forward,
46
+ is_flash_attn_2_available,
47
+ is_flash_attn_greater_or_equal_2_10,
48
+ logging,
49
+ replace_return_docstrings,
50
+ )
51
+ from .configuration_mistral import MistralConfig
52
+
53
+
54
+ if is_flash_attn_2_available():
55
+ from flash_attn import flash_attn_func, flash_attn_varlen_func
56
+ from flash_attn.bert_padding import index_first_axis, pad_input, unpad_input # noqa
57
+
58
+ _flash_supports_window_size = "window_size" in list(inspect.signature(flash_attn_func).parameters)
59
+ print("Using flast_attn 2")
60
+ print(f"flash_attn_func supports window_size: {_flash_supports_window_size}")
61
+
62
+
63
+
64
+ logger = logging.get_logger(__name__)
65
+
66
+ _CONFIG_FOR_DOC = "MistralConfig"
67
+
68
+
69
+ # Copied from transformers.models.llama.modeling_llama._get_unpad_data
70
+ def _get_unpad_data(attention_mask):
71
+ seqlens_in_batch = attention_mask.sum(dim=-1, dtype=torch.int32)
72
+ indices = torch.nonzero(attention_mask.flatten(), as_tuple=False).flatten()
73
+ max_seqlen_in_batch = seqlens_in_batch.max().item()
74
+ cu_seqlens = F.pad(torch.cumsum(seqlens_in_batch, dim=0, dtype=torch.int32), (1, 0))
75
+ return (
76
+ indices,
77
+ cu_seqlens,
78
+ max_seqlen_in_batch,
79
+ )
80
+
81
+
82
+ # Copied from transformers.models.llama.modeling_llama.LlamaRMSNorm with Llama->Mistral
83
+ class MistralRMSNorm(nn.Module):
84
+ def __init__(self, hidden_size, eps=1e-6):
85
+ """
86
+ MistralRMSNorm is equivalent to T5LayerNorm
87
+ """
88
+ super().__init__()
89
+ self.weight = nn.Parameter(torch.ones(hidden_size))
90
+ self.variance_epsilon = eps
91
+
92
+ def forward(self, hidden_states):
93
+ input_dtype = hidden_states.dtype
94
+ hidden_states = hidden_states.to(torch.float32)
95
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
96
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
97
+ return self.weight * hidden_states.to(input_dtype)
98
+
99
+
100
+ # copied from transformers.models.llama.modeling_llama.LlamaRotaryEmbedding with Llama->Mistral
101
+ # TODO @Arthur no longer copied from LLama after static cache
102
+ class MistralRotaryEmbedding(nn.Module):
103
+ def __init__(self, dim, max_position_embeddings=2048, base=10000, device=None):
104
+ super().__init__()
105
+
106
+ self.dim = dim
107
+ self.max_position_embeddings = max_position_embeddings
108
+ self.base = base
109
+ inv_freq = 1.0 / (self.base ** (torch.arange(0, self.dim, 2, dtype=torch.int64).float().to(device) / self.dim))
110
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
111
+
112
+ # Build here to make `torch.jit.trace` work.
113
+ self._set_cos_sin_cache(
114
+ seq_len=max_position_embeddings, device=self.inv_freq.device, dtype=torch.get_default_dtype()
115
+ )
116
+
117
+ def _set_cos_sin_cache(self, seq_len, device, dtype):
118
+ self.max_seq_len_cached = seq_len
119
+ t = torch.arange(self.max_seq_len_cached, device=device, dtype=torch.int64).type_as(self.inv_freq)
120
+
121
+ freqs = torch.outer(t, self.inv_freq)
122
+ # Different from paper, but it uses a different permutation in order to obtain the same calculation
123
+ emb = torch.cat((freqs, freqs), dim=-1)
124
+ self.register_buffer("cos_cached", emb.cos().to(dtype), persistent=False)
125
+ self.register_buffer("sin_cached", emb.sin().to(dtype), persistent=False)
126
+
127
+ def forward(self, x, seq_len=None):
128
+ # x: [bs, num_attention_heads, seq_len, head_size]
129
+ if seq_len > self.max_seq_len_cached:
130
+ self._set_cos_sin_cache(seq_len=seq_len, device=x.device, dtype=x.dtype)
131
+
132
+ return (
133
+ self.cos_cached[:seq_len].to(dtype=x.dtype),
134
+ self.sin_cached[:seq_len].to(dtype=x.dtype),
135
+ )
136
+
137
+
138
+ # Copied from transformers.models.llama.modeling_llama.rotate_half
139
+ def rotate_half(x):
140
+ """Rotates half the hidden dims of the input."""
141
+ x1 = x[..., : x.shape[-1] // 2]
142
+ x2 = x[..., x.shape[-1] // 2 :]
143
+ return torch.cat((-x2, x1), dim=-1)
144
+
145
+
146
+ # copied from transformers.models.llama.modeling_llama.apply_rotary_pos_emb
147
+ # TODO @Arthur no longer copied from LLama after static cache
148
+ def apply_rotary_pos_emb(q, k, cos, sin, position_ids, unsqueeze_dim=1):
149
+ """Applies Rotary Position Embedding to the query and key tensors.
150
+
151
+ Args:
152
+ q (`torch.Tensor`): The query tensor.
153
+ k (`torch.Tensor`): The key tensor.
154
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
155
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
156
+ position_ids (`torch.Tensor`):
157
+ The position indices of the tokens corresponding to the query and key tensors. For example, this can be
158
+ used to pass offsetted position ids when working with a KV-cache.
159
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
160
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
161
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
162
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
163
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
164
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
165
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
166
+ Returns:
167
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
168
+ """
169
+ cos = cos[position_ids].unsqueeze(unsqueeze_dim)
170
+ sin = sin[position_ids].unsqueeze(unsqueeze_dim)
171
+ q_embed = (q * cos) + (rotate_half(q) * sin)
172
+ k_embed = (k * cos) + (rotate_half(k) * sin)
173
+ return q_embed, k_embed
174
+
175
+
176
+ class MistralMLP(nn.Module):
177
+ def __init__(self, config):
178
+ super().__init__()
179
+ self.config = config
180
+ self.hidden_size = config.hidden_size
181
+ self.intermediate_size = config.intermediate_size
182
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
183
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
184
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
185
+ self.act_fn = ACT2FN[config.hidden_act]
186
+
187
+ def forward(self, x):
188
+ return self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
189
+
190
+
191
+ # Copied from transformers.models.llama.modeling_llama.repeat_kv
192
+ def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
193
+ """
194
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
195
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
196
+ """
197
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
198
+ if n_rep == 1:
199
+ return hidden_states
200
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
201
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
202
+
203
+
204
+ class MistralAttention(nn.Module):
205
+ """
206
+ Multi-headed attention from 'Attention Is All You Need' paper. Modified to use sliding window attention: Longformer
207
+ and "Generating Long Sequences with Sparse Transformers".
208
+ """
209
+
210
+ def __init__(self, config: MistralConfig, layer_idx: Optional[int] = None):
211
+ super().__init__()
212
+ self.config = config
213
+ self.layer_idx = layer_idx
214
+ if layer_idx is None:
215
+ logger.warning_once(
216
+ f"Instantiating {self.__class__.__name__} without passing a `layer_idx` is not recommended and will "
217
+ "lead to errors during the forward call if caching is used. Please make sure to provide a `layer_idx` "
218
+ "when creating this class."
219
+ )
220
+
221
+ self.hidden_size = config.hidden_size
222
+ self.num_heads = config.num_attention_heads
223
+ self.head_dim = self.hidden_size // self.num_heads
224
+ self.num_key_value_heads = config.num_key_value_heads
225
+ self.num_key_value_groups = self.num_heads // self.num_key_value_heads
226
+ self.max_position_embeddings = config.max_position_embeddings
227
+ self.rope_theta = config.rope_theta
228
+ self.is_causal = config.is_causal
229
+ self.attention_dropout = config.attention_dropout
230
+
231
+ if (self.head_dim * self.num_heads) != self.hidden_size:
232
+ raise ValueError(
233
+ f"hidden_size must be divisible by num_heads (got `hidden_size`: {self.hidden_size}"
234
+ f" and `num_heads`: {self.num_heads})."
235
+ )
236
+ self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
237
+ self.k_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
238
+ self.v_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
239
+ self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
240
+
241
+ self.rotary_emb = MistralRotaryEmbedding(
242
+ self.head_dim,
243
+ max_position_embeddings=self.max_position_embeddings,
244
+ base=self.rope_theta,
245
+ )
246
+
247
+ def _shape(self, tensor: torch.Tensor, seq_len: int, bsz: int):
248
+ return tensor.view(bsz, seq_len, self.num_heads, self.head_dim).transpose(1, 2).contiguous()
249
+
250
+ def forward(
251
+ self,
252
+ hidden_states: torch.Tensor,
253
+ attention_mask: Optional[torch.Tensor] = None,
254
+ position_ids: Optional[torch.LongTensor] = None,
255
+ past_key_value: Optional[Cache] = None,
256
+ output_attentions: bool = False,
257
+ use_cache: bool = False,
258
+ **kwargs,
259
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
260
+ if "padding_mask" in kwargs:
261
+ warnings.warn(
262
+ "Passing `padding_mask` is deprecated and will be removed in v4.37. Please make sure use `attention_mask` instead.`"
263
+ )
264
+ bsz, q_len, _ = hidden_states.size()
265
+
266
+ query_states = self.q_proj(hidden_states)
267
+ key_states = self.k_proj(hidden_states)
268
+ value_states = self.v_proj(hidden_states)
269
+
270
+ query_states = query_states.view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
271
+ key_states = key_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2)
272
+ value_states = value_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2)
273
+
274
+ kv_seq_len = key_states.shape[-2]
275
+ if past_key_value is not None:
276
+ if self.layer_idx is None:
277
+ raise ValueError(
278
+ f"The cache structure has changed since version v4.36. If you are using {self.__class__.__name__} "
279
+ "for auto-regressive decoding with k/v caching, please make sure to initialize the attention class "
280
+ "with a layer index."
281
+ )
282
+ kv_seq_len += past_key_value.get_seq_length(self.layer_idx)
283
+ cos, sin = self.rotary_emb(value_states, seq_len=kv_seq_len)
284
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin, position_ids)
285
+
286
+ if past_key_value is not None:
287
+ cache_kwargs = {"sin": sin, "cos": cos} # Specific to RoPE models
288
+ key_states, value_states = past_key_value.update(key_states, value_states, self.layer_idx, cache_kwargs)
289
+
290
+ # repeat k/v heads if n_kv_heads < n_heads
291
+ key_states = repeat_kv(key_states, self.num_key_value_groups)
292
+ value_states = repeat_kv(value_states, self.num_key_value_groups)
293
+
294
+ attn_weights = torch.matmul(query_states, key_states.transpose(2, 3)) / math.sqrt(self.head_dim)
295
+
296
+ if attn_weights.size() != (bsz, self.num_heads, q_len, kv_seq_len):
297
+ raise ValueError(
298
+ f"Attention weights should be of size {(bsz, self.num_heads, q_len, kv_seq_len)}, but is"
299
+ f" {attn_weights.size()}"
300
+ )
301
+
302
+ if attention_mask is not None:
303
+ if attention_mask.size() != (bsz, 1, q_len, kv_seq_len):
304
+ raise ValueError(
305
+ f"Attention mask should be of size {(bsz, 1, q_len, kv_seq_len)}, but is {attention_mask.size()}"
306
+ )
307
+
308
+ attn_weights = attn_weights + attention_mask
309
+
310
+ # upcast attention to fp32
311
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query_states.dtype)
312
+ attn_weights = nn.functional.dropout(attn_weights, p=self.attention_dropout, training=self.training)
313
+ attn_output = torch.matmul(attn_weights, value_states)
314
+
315
+ if attn_output.size() != (bsz, self.num_heads, q_len, self.head_dim):
316
+ raise ValueError(
317
+ f"`attn_output` should be of size {(bsz, self.num_heads, q_len, self.head_dim)}, but is"
318
+ f" {attn_output.size()}"
319
+ )
320
+
321
+ attn_output = attn_output.transpose(1, 2).contiguous()
322
+ attn_output = attn_output.reshape(bsz, q_len, self.hidden_size)
323
+
324
+ attn_output = self.o_proj(attn_output)
325
+
326
+ if not output_attentions:
327
+ attn_weights = None
328
+
329
+ return attn_output, attn_weights, past_key_value
330
+
331
+
332
+ class MistralFlashAttention2(MistralAttention):
333
+ """
334
+ Mistral flash attention module. This module inherits from `MistralAttention` as the weights of the module stays
335
+ untouched. The only required change would be on the forward pass where it needs to correctly call the public API of
336
+ flash attention and deal with padding tokens in case the input contains any of them.
337
+ """
338
+
339
+ # Copied from transformers.models.llama.modeling_llama.LlamaFlashAttention2.__init__
340
+ def __init__(self, *args, **kwargs):
341
+ super().__init__(*args, **kwargs)
342
+
343
+ # TODO: Should be removed once Flash Attention for RoCm is bumped to 2.1.
344
+ # flash_attn<2.1 generates top-left aligned causal mask, while what is needed here is bottom-right alignement, that was made default for flash_attn>=2.1. This attribute is used to handle this difference. Reference: https://github.com/Dao-AILab/flash-attention/releases/tag/v2.1.0.
345
+ # Beware that with flash_attn<2.1, using q_seqlen != k_seqlen (except for the case q_seqlen == 1) produces a wrong mask (top-left).
346
+ self._flash_attn_uses_top_left_mask = not is_flash_attn_greater_or_equal_2_10()
347
+
348
+ def forward(
349
+ self,
350
+ hidden_states: torch.Tensor,
351
+ attention_mask: Optional[torch.Tensor] = None,
352
+ position_ids: Optional[torch.LongTensor] = None,
353
+ past_key_value: Optional[Cache] = None,
354
+ output_attentions: bool = False,
355
+ use_cache: bool = False,
356
+ **kwargs,
357
+ ):
358
+ if "padding_mask" in kwargs:
359
+ warnings.warn(
360
+ "Passing `padding_mask` is deprecated and will be removed in v4.37. Please make sure use `attention_mask` instead.`"
361
+ )
362
+
363
+ # overwrite attention_mask with padding_mask
364
+ attention_mask = kwargs.pop("padding_mask")
365
+ bsz, q_len, _ = hidden_states.size()
366
+
367
+ query_states = self.q_proj(hidden_states)
368
+ key_states = self.k_proj(hidden_states)
369
+ value_states = self.v_proj(hidden_states)
370
+
371
+ query_states = query_states.view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
372
+ key_states = key_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2)
373
+ value_states = value_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2)
374
+
375
+ kv_seq_len = key_states.shape[-2]
376
+ if past_key_value is not None:
377
+ if self.layer_idx is None:
378
+ raise ValueError(
379
+ f"The cache structure has changed since version v4.36. If you are using {self.__class__.__name__} "
380
+ "for auto-regressive decoding with k/v caching, please make sure to initialize the attention class "
381
+ "with a layer index."
382
+ )
383
+ kv_seq_len += past_key_value.get_seq_length(self.layer_idx)
384
+
385
+ # Because the input can be padded, the absolute sequence length depends on the max position id.
386
+ rotary_seq_len = max(kv_seq_len, position_ids[:, -1].max().item()) + 1
387
+ cos, sin = self.rotary_emb(value_states, seq_len=rotary_seq_len)
388
+
389
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin, position_ids)
390
+
391
+ use_sliding_windows = (
392
+ _flash_supports_window_size
393
+ and getattr(self.config, "sliding_window", None) is not None
394
+ and kv_seq_len > self.config.sliding_window
395
+ )
396
+
397
+ if not _flash_supports_window_size:
398
+ logger.warning_once(
399
+ "The current flash attention version does not support sliding window attention, for a more memory efficient implementation"
400
+ " make sure to upgrade flash-attn library."
401
+ )
402
+
403
+ if past_key_value is not None:
404
+ # Activate slicing cache only if the config has a value `sliding_windows` attribute
405
+ cache_has_contents = past_key_value.get_seq_length(self.layer_idx) > 0
406
+ if (
407
+ getattr(self.config, "sliding_window", None) is not None
408
+ and kv_seq_len > self.config.sliding_window
409
+ and cache_has_contents
410
+ ):
411
+ slicing_tokens = 1 - self.config.sliding_window
412
+
413
+ past_key = past_key_value[self.layer_idx][0]
414
+ past_value = past_key_value[self.layer_idx][1]
415
+
416
+ past_key = past_key[:, :, slicing_tokens:, :].contiguous()
417
+ past_value = past_value[:, :, slicing_tokens:, :].contiguous()
418
+
419
+ if past_key.shape[-2] != self.config.sliding_window - 1:
420
+ raise ValueError(
421
+ f"past key must have a shape of (`batch_size, num_heads, self.config.sliding_window-1, head_dim`), got"
422
+ f" {past_key.shape}"
423
+ )
424
+
425
+ if attention_mask is not None:
426
+ attention_mask = attention_mask[:, slicing_tokens:]
427
+ attention_mask = torch.cat([attention_mask, torch.ones_like(attention_mask[:, -1:])], dim=-1)
428
+
429
+ cache_kwargs = {"sin": sin, "cos": cos} # Specific to RoPE models
430
+ key_states, value_states = past_key_value.update(key_states, value_states, self.layer_idx, cache_kwargs)
431
+
432
+ # repeat k/v heads if n_kv_heads < n_heads
433
+ key_states = repeat_kv(key_states, self.num_key_value_groups)
434
+ value_states = repeat_kv(value_states, self.num_key_value_groups)
435
+ dropout_rate = 0.0 if not self.training else self.attention_dropout
436
+
437
+ # In PEFT, usually we cast the layer norms in float32 for training stability reasons
438
+ # therefore the input hidden states gets silently casted in float32. Hence, we need
439
+ # cast them back in float16 just to be sure everything works as expected.
440
+ input_dtype = query_states.dtype
441
+ if input_dtype == torch.float32:
442
+ if torch.is_autocast_enabled():
443
+ target_dtype = torch.get_autocast_gpu_dtype()
444
+ # Handle the case where the model is quantized
445
+ elif hasattr(self.config, "_pre_quantization_dtype"):
446
+ target_dtype = self.config._pre_quantization_dtype
447
+ else:
448
+ target_dtype = self.q_proj.weight.dtype
449
+
450
+ logger.warning_once(
451
+ f"The input hidden states seems to be silently casted in float32, this might be related to"
452
+ f" the fact you have upcasted embedding or layer norm layers in float32. We will cast back the input in"
453
+ f" {target_dtype}."
454
+ )
455
+
456
+ query_states = query_states.to(target_dtype)
457
+ key_states = key_states.to(target_dtype)
458
+ value_states = value_states.to(target_dtype)
459
+
460
+ # Reashape to the expected shape for Flash Attention
461
+ query_states = query_states.transpose(1, 2)
462
+ key_states = key_states.transpose(1, 2)
463
+ value_states = value_states.transpose(1, 2)
464
+
465
+ attn_output = self._flash_attention_forward(
466
+ query_states,
467
+ key_states,
468
+ value_states,
469
+ attention_mask,
470
+ q_len,
471
+ dropout=dropout_rate,
472
+ use_sliding_windows=use_sliding_windows,
473
+ )
474
+
475
+ attn_output = attn_output.reshape(bsz, q_len, self.hidden_size).contiguous()
476
+ attn_output = self.o_proj(attn_output)
477
+
478
+ if not output_attentions:
479
+ attn_weights = None
480
+
481
+ return attn_output, attn_weights, past_key_value
482
+
483
+ def _flash_attention_forward(
484
+ self,
485
+ query_states,
486
+ key_states,
487
+ value_states,
488
+ attention_mask,
489
+ query_length,
490
+ dropout=0.0,
491
+ softmax_scale=None,
492
+ use_sliding_windows=False,
493
+ ):
494
+ """
495
+ Calls the forward method of Flash Attention - if the input hidden states contain at least one padding token
496
+ first unpad the input, then computes the attention scores and pad the final attention scores.
497
+
498
+ Args:
499
+ query_states (`torch.Tensor`):
500
+ Input query states to be passed to Flash Attention API
501
+ key_states (`torch.Tensor`):
502
+ Input key states to be passed to Flash Attention API
503
+ value_states (`torch.Tensor`):
504
+ Input value states to be passed to Flash Attention API
505
+ attention_mask (`torch.Tensor`):
506
+ The padding mask - corresponds to a tensor of size `(batch_size, seq_len)` where 0 stands for the
507
+ position of padding tokens and 1 for the position of non-padding tokens.
508
+ dropout (`int`, *optional*):
509
+ Attention dropout
510
+ softmax_scale (`float`, *optional*):
511
+ The scaling of QK^T before applying softmax. Default to 1 / sqrt(head_dim)
512
+ use_sliding_windows (`bool`, *optional*):
513
+ Whether to activate sliding window attention.
514
+ """
515
+ if not self._flash_attn_uses_top_left_mask:
516
+ causal = self.is_causal
517
+ else:
518
+ # TODO: Remove the `query_length != 1` check once Flash Attention for RoCm is bumped to 2.1. For details, please see the comment in LlamaFlashAttention2 __init__.
519
+ causal = self.is_causal and query_length != 1
520
+
521
+ # Contains at least one padding token in the sequence
522
+ if attention_mask is not None:
523
+ batch_size = query_states.shape[0]
524
+ query_states, key_states, value_states, indices_q, cu_seq_lens, max_seq_lens = self._upad_input(
525
+ query_states, key_states, value_states, attention_mask, query_length
526
+ )
527
+
528
+ cu_seqlens_q, cu_seqlens_k = cu_seq_lens
529
+ max_seqlen_in_batch_q, max_seqlen_in_batch_k = max_seq_lens
530
+
531
+ if not use_sliding_windows:
532
+ attn_output_unpad = flash_attn_varlen_func(
533
+ query_states,
534
+ key_states,
535
+ value_states,
536
+ cu_seqlens_q=cu_seqlens_q,
537
+ cu_seqlens_k=cu_seqlens_k,
538
+ max_seqlen_q=max_seqlen_in_batch_q,
539
+ max_seqlen_k=max_seqlen_in_batch_k,
540
+ dropout_p=dropout,
541
+ softmax_scale=softmax_scale,
542
+ causal=causal,
543
+ )
544
+ else:
545
+ attn_output_unpad = flash_attn_varlen_func(
546
+ query_states,
547
+ key_states,
548
+ value_states,
549
+ cu_seqlens_q=cu_seqlens_q,
550
+ cu_seqlens_k=cu_seqlens_k,
551
+ max_seqlen_q=max_seqlen_in_batch_q,
552
+ max_seqlen_k=max_seqlen_in_batch_k,
553
+ dropout_p=dropout,
554
+ softmax_scale=softmax_scale,
555
+ causal=causal,
556
+ window_size=(self.config.sliding_window, self.config.sliding_window),
557
+ )
558
+
559
+ attn_output = pad_input(attn_output_unpad, indices_q, batch_size, query_length)
560
+ else:
561
+ if not use_sliding_windows:
562
+ attn_output = flash_attn_func(
563
+ query_states,
564
+ key_states,
565
+ value_states,
566
+ dropout,
567
+ softmax_scale=softmax_scale,
568
+ causal=causal,
569
+ )
570
+ else:
571
+ attn_output = flash_attn_func(
572
+ query_states,
573
+ key_states,
574
+ value_states,
575
+ dropout,
576
+ softmax_scale=softmax_scale,
577
+ causal=causal,
578
+ window_size=(self.config.sliding_window, self.config.sliding_window),
579
+ )
580
+
581
+ return attn_output
582
+
583
+ def _upad_input(self, query_layer, key_layer, value_layer, attention_mask, query_length):
584
+ batch_size, kv_seq_len, num_heads, head_dim = key_layer.shape
585
+
586
+ # On the first iteration we need to properly re-create the padding mask
587
+ # by slicing it on the proper place
588
+ if kv_seq_len != attention_mask.shape[-1]:
589
+ attention_mask_num_tokens = attention_mask.shape[-1]
590
+ attention_mask = attention_mask[:, attention_mask_num_tokens - kv_seq_len :]
591
+
592
+ indices_k, cu_seqlens_k, max_seqlen_in_batch_k = _get_unpad_data(attention_mask)
593
+
594
+ key_layer = index_first_axis(key_layer.reshape(batch_size * kv_seq_len, num_heads, head_dim), indices_k)
595
+ value_layer = index_first_axis(value_layer.reshape(batch_size * kv_seq_len, num_heads, head_dim), indices_k)
596
+
597
+ if query_length == kv_seq_len:
598
+ query_layer = index_first_axis(
599
+ query_layer.reshape(batch_size * kv_seq_len, num_heads, head_dim), indices_k
600
+ )
601
+ cu_seqlens_q = cu_seqlens_k
602
+ max_seqlen_in_batch_q = max_seqlen_in_batch_k
603
+ indices_q = indices_k
604
+ elif query_length == 1:
605
+ max_seqlen_in_batch_q = 1
606
+ cu_seqlens_q = torch.arange(
607
+ batch_size + 1, dtype=torch.int32, device=query_layer.device
608
+ ) # There is a memcpy here, that is very bad.
609
+ indices_q = cu_seqlens_q[:-1]
610
+ query_layer = query_layer.squeeze(1)
611
+ else:
612
+ # The -q_len: slice assumes left padding.
613
+ attention_mask = attention_mask[:, -query_length:]
614
+ query_layer, indices_q, cu_seqlens_q, max_seqlen_in_batch_q = unpad_input(query_layer, attention_mask)
615
+
616
+ return (
617
+ query_layer,
618
+ key_layer,
619
+ value_layer,
620
+ indices_q,
621
+ (cu_seqlens_q, cu_seqlens_k),
622
+ (max_seqlen_in_batch_q, max_seqlen_in_batch_k),
623
+ )
624
+
625
+
626
+ # copied from transformers.models.llama.modeling_llama.LlamaSdpaAttention with Llama->Mistral
627
+ # TODO @Arthur no longer copied from LLama after static cache
628
+ class MistralSdpaAttention(MistralAttention):
629
+ """
630
+ Mistral attention module using torch.nn.functional.scaled_dot_product_attention. This module inherits from
631
+ `MistralAttention` as the weights of the module stays untouched. The only changes are on the forward pass to adapt to
632
+ SDPA API.
633
+ """
634
+
635
+ # Adapted from MistralAttention.forward
636
+ def forward(
637
+ self,
638
+ hidden_states: torch.Tensor,
639
+ attention_mask: Optional[torch.Tensor] = None,
640
+ position_ids: Optional[torch.LongTensor] = None,
641
+ past_key_value: Optional[Cache] = None,
642
+ output_attentions: bool = False,
643
+ use_cache: bool = False,
644
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
645
+ if output_attentions:
646
+ # TODO: Improve this warning with e.g. `model.config.attn_implementation = "manual"` once this is implemented.
647
+ logger.warning_once(
648
+ "MistralModel is using MistralSdpaAttention, but `torch.nn.functional.scaled_dot_product_attention` does not support `output_attentions=True`. Falling back to the manual attention implementation, "
649
+ 'but specifying the manual implementation will be required from Transformers version v5.0.0 onwards. This warning can be removed using the argument `attn_implementation="eager"` when loading the model.'
650
+ )
651
+ return super().forward(
652
+ hidden_states=hidden_states,
653
+ attention_mask=attention_mask,
654
+ position_ids=position_ids,
655
+ past_key_value=past_key_value,
656
+ output_attentions=output_attentions,
657
+ use_cache=use_cache,
658
+ )
659
+
660
+ bsz, q_len, _ = hidden_states.size()
661
+
662
+ query_states = self.q_proj(hidden_states)
663
+ key_states = self.k_proj(hidden_states)
664
+ value_states = self.v_proj(hidden_states)
665
+
666
+ query_states = query_states.view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
667
+ key_states = key_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2)
668
+ value_states = value_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2)
669
+
670
+ kv_seq_len = key_states.shape[-2]
671
+ if past_key_value is not None:
672
+ kv_seq_len += past_key_value.get_seq_length(self.layer_idx)
673
+ cos, sin = self.rotary_emb(value_states, seq_len=kv_seq_len)
674
+
675
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin, position_ids)
676
+
677
+ if past_key_value is not None:
678
+ cache_kwargs = {"sin": sin, "cos": cos} # Specific to RoPE models
679
+ key_states, value_states = past_key_value.update(key_states, value_states, self.layer_idx, cache_kwargs)
680
+
681
+ key_states = repeat_kv(key_states, self.num_key_value_groups)
682
+ value_states = repeat_kv(value_states, self.num_key_value_groups)
683
+
684
+ if attention_mask is not None:
685
+ if attention_mask.size() != (bsz, 1, q_len, kv_seq_len):
686
+ raise ValueError(
687
+ f"Attention mask should be of size {(bsz, 1, q_len, kv_seq_len)}, but is {attention_mask.size()}"
688
+ )
689
+
690
+ # SDPA with memory-efficient backend is currently (torch==2.1.2) bugged with non-contiguous inputs with custom attn_mask,
691
+ # Reference: https://github.com/pytorch/pytorch/issues/112577.
692
+ if query_states.device.type == "cuda" and attention_mask is not None:
693
+ query_states = query_states.contiguous()
694
+ key_states = key_states.contiguous()
695
+ value_states = value_states.contiguous()
696
+
697
+ attn_output = torch.nn.functional.scaled_dot_product_attention(
698
+ query_states,
699
+ key_states,
700
+ value_states,
701
+ attn_mask=attention_mask,
702
+ dropout_p=self.attention_dropout if self.training else 0.0,
703
+ # The q_len > 1 is necessary to match with AttentionMaskConverter.to_causal_4d that does not create a causal mask in case q_len == 1.
704
+ is_causal=self.is_causal and attention_mask is None and q_len > 1,
705
+ )
706
+
707
+ attn_output = attn_output.transpose(1, 2).contiguous()
708
+ attn_output = attn_output.view(bsz, q_len, self.hidden_size)
709
+
710
+ attn_output = self.o_proj(attn_output)
711
+
712
+ return attn_output, None, past_key_value
713
+
714
+
715
+ MISTRAL_ATTENTION_CLASSES = {
716
+ "eager": MistralAttention,
717
+ "flash_attention_2": MistralFlashAttention2,
718
+ "sdpa": MistralSdpaAttention,
719
+ }
720
+
721
+
722
+ class MistralDecoderLayer(nn.Module):
723
+ def __init__(self, config: MistralConfig, layer_idx: int):
724
+ super().__init__()
725
+ self.hidden_size = config.hidden_size
726
+
727
+ self.self_attn = MISTRAL_ATTENTION_CLASSES[config._attn_implementation](config, layer_idx)
728
+
729
+ self.mlp = MistralMLP(config)
730
+ self.input_layernorm = MistralRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
731
+ self.post_attention_layernorm = MistralRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
732
+
733
+ def forward(
734
+ self,
735
+ hidden_states: torch.Tensor,
736
+ attention_mask: Optional[torch.Tensor] = None,
737
+ position_ids: Optional[torch.LongTensor] = None,
738
+ past_key_value: Optional[Tuple[torch.Tensor]] = None,
739
+ output_attentions: Optional[bool] = False,
740
+ use_cache: Optional[bool] = False,
741
+ **kwargs,
742
+ ) -> Tuple[torch.FloatTensor, Optional[Tuple[torch.FloatTensor, torch.FloatTensor]]]:
743
+ if "padding_mask" in kwargs:
744
+ warnings.warn(
745
+ "Passing `padding_mask` is deprecated and will be removed in v4.37. Please make sure use `attention_mask` instead.`"
746
+ )
747
+ """
748
+ Args:
749
+ hidden_states (`torch.FloatTensor`): input to the layer of shape `(batch, seq_len, embed_dim)`
750
+ attention_mask (`torch.FloatTensor`, *optional*): attention mask of size
751
+ `(batch, sequence_length)` where padding elements are indicated by 0.
752
+ output_attentions (`bool`, *optional*):
753
+ Whether or not to return the attentions tensors of all attention layers. See `attentions` under
754
+ returned tensors for more detail.
755
+ use_cache (`bool`, *optional*):
756
+ If set to `True`, `past_key_values` key value states are returned and can be used to speed up decoding
757
+ (see `past_key_values`).
758
+ past_key_value (`Tuple(torch.FloatTensor)`, *optional*): cached past key and value projection states
759
+ """
760
+
761
+ residual = hidden_states
762
+
763
+ hidden_states = self.input_layernorm(hidden_states)
764
+
765
+ # Self Attention
766
+ hidden_states, self_attn_weights, present_key_value = self.self_attn(
767
+ hidden_states=hidden_states,
768
+ attention_mask=attention_mask,
769
+ position_ids=position_ids,
770
+ past_key_value=past_key_value,
771
+ output_attentions=output_attentions,
772
+ use_cache=use_cache,
773
+ )
774
+ hidden_states = residual + hidden_states
775
+
776
+ # Fully Connected
777
+ residual = hidden_states
778
+ hidden_states = self.post_attention_layernorm(hidden_states)
779
+ hidden_states = self.mlp(hidden_states)
780
+ hidden_states = residual + hidden_states
781
+
782
+ outputs = (hidden_states,)
783
+
784
+ if output_attentions:
785
+ outputs += (self_attn_weights,)
786
+
787
+ if use_cache:
788
+ outputs += (present_key_value,)
789
+
790
+ return outputs
791
+
792
+
793
+ MISTRAL_START_DOCSTRING = r"""
794
+ This model inherits from [`PreTrainedModel`]. Check the superclass documentation for the generic methods the
795
+ library implements for all its model (such as downloading or saving, resizing the input embeddings, pruning heads
796
+ etc.)
797
+
798
+ This model is also a PyTorch [torch.nn.Module](https://pytorch.org/docs/stable/nn.html#torch.nn.Module) subclass.
799
+ Use it as a regular PyTorch Module and refer to the PyTorch documentation for all matter related to general usage
800
+ and behavior.
801
+
802
+ Parameters:
803
+ config ([`MistralConfig`]):
804
+ Model configuration class with all the parameters of the model. Initializing with a config file does not
805
+ load the weights associated with the model, only the configuration. Check out the
806
+ [`~PreTrainedModel.from_pretrained`] method to load the model weights.
807
+ """
808
+
809
+
810
+ @add_start_docstrings(
811
+ "The bare Mistral Model outputting raw hidden-states without any specific head on top.",
812
+ MISTRAL_START_DOCSTRING,
813
+ )
814
+ class MistralPreTrainedModel(PreTrainedModel):
815
+ config_class = MistralConfig
816
+ base_model_prefix = "model"
817
+ supports_gradient_checkpointing = True
818
+ _no_split_modules = ["MistralDecoderLayer"]
819
+ _skip_keys_device_placement = "past_key_values"
820
+ _supports_flash_attn_2 = True
821
+ _supports_sdpa = True
822
+ _supports_cache_class = True
823
+
824
+ def _init_weights(self, module):
825
+ std = self.config.initializer_range
826
+ if isinstance(module, nn.Linear):
827
+ module.weight.data.normal_(mean=0.0, std=std)
828
+ if module.bias is not None:
829
+ module.bias.data.zero_()
830
+ elif isinstance(module, nn.Embedding):
831
+ module.weight.data.normal_(mean=0.0, std=std)
832
+ if module.padding_idx is not None:
833
+ module.weight.data[module.padding_idx].zero_()
834
+
835
+
836
+ MISTRAL_INPUTS_DOCSTRING = r"""
837
+ Args:
838
+ input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`):
839
+ Indices of input sequence tokens in the vocabulary. Padding will be ignored by default should you provide
840
+ it.
841
+
842
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
843
+ [`PreTrainedTokenizer.__call__`] for details.
844
+
845
+ [What are input IDs?](../glossary#input-ids)
846
+ attention_mask (`torch.Tensor` of shape `(batch_size, sequence_length)`, *optional*):
847
+ Mask to avoid performing attention on padding token indices. Mask values selected in `[0, 1]`:
848
+
849
+ - 1 for tokens that are **not masked**,
850
+ - 0 for tokens that are **masked**.
851
+
852
+ [What are attention masks?](../glossary#attention-mask)
853
+
854
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
855
+ [`PreTrainedTokenizer.__call__`] for details.
856
+
857
+ If `past_key_values` is used, optionally only the last `decoder_input_ids` have to be input (see
858
+ `past_key_values`).
859
+
860
+ If you want to change padding behavior, you should read [`modeling_opt._prepare_decoder_attention_mask`]
861
+ and modify to your needs. See diagram 1 in [the paper](https://arxiv.org/abs/1910.13461) for more
862
+ information on the default strategy.
863
+
864
+ - 1 indicates the head is **not masked**,
865
+ - 0 indicates the head is **masked**.
866
+ position_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
867
+ Indices of positions of each input sequence tokens in the position embeddings. Selected in the range `[0,
868
+ config.n_positions - 1]`.
869
+
870
+ [What are position IDs?](../glossary#position-ids)
871
+ past_key_values (`Cache` or `tuple(tuple(torch.FloatTensor))`, *optional*):
872
+ Pre-computed hidden-states (key and values in the self-attention blocks and in the cross-attention
873
+ blocks) that can be used to speed up sequential decoding. This typically consists in the `past_key_values`
874
+ returned by the model at a previous stage of decoding, when `use_cache=True` or `config.use_cache=True`.
875
+
876
+ Two formats are allowed:
877
+ - a [`~cache_utils.Cache`] instance;
878
+ - Tuple of `tuple(torch.FloatTensor)` of length `config.n_layers`, with each tuple having 2 tensors of
879
+ shape `(batch_size, num_heads, sequence_length, embed_size_per_head)`). This is also known as the legacy
880
+ cache format.
881
+
882
+ The model will output the same cache format that is fed as input. If no `past_key_values` are passed, the
883
+ legacy cache format will be returned.
884
+
885
+ If `past_key_values` are used, the user can optionally input only the last `input_ids` (those that don't
886
+ have their past key value states given to this model) of shape `(batch_size, 1)` instead of all `input_ids`
887
+ of shape `(batch_size, sequence_length)`.
888
+ inputs_embeds (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`, *optional*):
889
+ Optionally, instead of passing `input_ids` you can choose to directly pass an embedded representation. This
890
+ is useful if you want more control over how to convert `input_ids` indices into associated vectors than the
891
+ model's internal embedding lookup matrix.
892
+ use_cache (`bool`, *optional*):
893
+ If set to `True`, `past_key_values` key value states are returned and can be used to speed up decoding (see
894
+ `past_key_values`).
895
+ output_attentions (`bool`, *optional*):
896
+ Whether or not to return the attentions tensors of all attention layers. See `attentions` under returned
897
+ tensors for more detail.
898
+ output_hidden_states (`bool`, *optional*):
899
+ Whether or not to return the hidden states of all layers. See `hidden_states` under returned tensors for
900
+ more detail.
901
+ return_dict (`bool`, *optional*):
902
+ Whether or not to return a [`~utils.ModelOutput`] instead of a plain tuple.
903
+ """
904
+
905
+
906
+ @add_start_docstrings(
907
+ "The bare Mistral Model outputting raw hidden-states without any specific head on top.",
908
+ MISTRAL_START_DOCSTRING,
909
+ )
910
+ class MistralModel(MistralPreTrainedModel):
911
+ """
912
+ Transformer decoder consisting of *config.num_hidden_layers* layers. Each layer is a [`MistralDecoderLayer`]
913
+
914
+ Args:
915
+ config: MistralConfig
916
+ """
917
+
918
+ def __init__(self, config: MistralConfig):
919
+ super().__init__(config)
920
+ self.padding_idx = config.pad_token_id
921
+ self.vocab_size = config.vocab_size
922
+ self.is_causal = config.is_causal
923
+
924
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
925
+ self.layers = nn.ModuleList(
926
+ [MistralDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
927
+ )
928
+ self._attn_implementation = config._attn_implementation
929
+ self.norm = MistralRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
930
+
931
+ self.gradient_checkpointing = False
932
+ # Initialize weights and apply final processing
933
+ self.post_init()
934
+
935
+ def get_input_embeddings(self):
936
+ return self.embed_tokens
937
+
938
+ def set_input_embeddings(self, value):
939
+ self.embed_tokens = value
940
+
941
+ @add_start_docstrings_to_model_forward(MISTRAL_INPUTS_DOCSTRING)
942
+ def forward(
943
+ self,
944
+ input_ids: torch.LongTensor = None,
945
+ attention_mask: Optional[torch.Tensor] = None,
946
+ position_ids: Optional[torch.LongTensor] = None,
947
+ past_key_values: Optional[List[torch.FloatTensor]] = None,
948
+ inputs_embeds: Optional[torch.FloatTensor] = None,
949
+ use_cache: Optional[bool] = None,
950
+ output_attentions: Optional[bool] = None,
951
+ output_hidden_states: Optional[bool] = None,
952
+ return_dict: Optional[bool] = None,
953
+ ) -> Union[Tuple, BaseModelOutputWithPast]:
954
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
955
+ output_hidden_states = (
956
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
957
+ )
958
+ use_cache = use_cache if use_cache is not None else self.config.use_cache
959
+
960
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
961
+
962
+ # retrieve input_ids and inputs_embeds
963
+ if input_ids is not None and inputs_embeds is not None:
964
+ raise ValueError("You cannot specify both decoder_input_ids and decoder_inputs_embeds at the same time")
965
+ elif input_ids is not None:
966
+ batch_size, seq_length = input_ids.shape
967
+ elif inputs_embeds is not None:
968
+ batch_size, seq_length, _ = inputs_embeds.shape
969
+ else:
970
+ raise ValueError("You have to specify either decoder_input_ids or decoder_inputs_embeds")
971
+
972
+ if self.gradient_checkpointing and self.training:
973
+ if use_cache:
974
+ logger.warning_once(
975
+ "`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`..."
976
+ )
977
+ use_cache = False
978
+
979
+ past_key_values_length = 0
980
+
981
+ if use_cache:
982
+ use_legacy_cache = not isinstance(past_key_values, Cache)
983
+ if use_legacy_cache:
984
+ past_key_values = DynamicCache.from_legacy_cache(past_key_values)
985
+ past_key_values_length = past_key_values.get_seq_length()
986
+
987
+ if position_ids is None:
988
+ device = input_ids.device if input_ids is not None else inputs_embeds.device
989
+ position_ids = torch.arange(
990
+ past_key_values_length, seq_length + past_key_values_length, dtype=torch.long, device=device
991
+ )
992
+ position_ids = position_ids.unsqueeze(0).view(-1, seq_length)
993
+ else:
994
+ # Use position_ids' own last dim to avoid shape mismatch during generation
995
+ # (seq_length reflects original input length, but position_ids may be trimmed)
996
+ position_ids = position_ids.view(-1, position_ids.shape[-1]).long()
997
+
998
+ if inputs_embeds is None:
999
+ inputs_embeds = self.embed_tokens(input_ids)
1000
+
1001
+ if attention_mask is not None and self._attn_implementation == "flash_attention_2" and use_cache:
1002
+ is_padding_right = attention_mask[:, -1].sum().item() != batch_size
1003
+ if is_padding_right:
1004
+ raise ValueError(
1005
+ "You are attempting to perform batched generation with padding_side='right'"
1006
+ " this may lead to unexpected behaviour for Flash Attention version of Mistral. Make sure to "
1007
+ " call `tokenizer.padding_side = 'left'` before tokenizing the input. "
1008
+ )
1009
+
1010
+
1011
+ if self._attn_implementation == "flash_attention_2":
1012
+ # 2d mask is passed through the layers
1013
+ attention_mask = attention_mask if (attention_mask is not None and 0 in attention_mask) else None
1014
+ elif self._attn_implementation == "sdpa" and not output_attentions:
1015
+ # output_attentions=True can not be supported when using SDPA, and we fall back on
1016
+ # the manual implementation that requires a 4D causal mask in all cases.
1017
+ if self.is_causal:
1018
+ attention_mask = _prepare_4d_causal_attention_mask_for_sdpa(
1019
+ attention_mask,
1020
+ (batch_size, seq_length),
1021
+ inputs_embeds,
1022
+ past_key_values_length,
1023
+ )
1024
+ else:
1025
+ attention_mask = _prepare_4d_attention_mask_for_sdpa(
1026
+ attention_mask,
1027
+ dtype=inputs_embeds.dtype,
1028
+ )
1029
+ else:
1030
+ # 4d mask is passed through the layers
1031
+ if self.is_causal:
1032
+ attention_mask = _prepare_4d_causal_attention_mask(
1033
+ attention_mask,
1034
+ (batch_size, seq_length),
1035
+ inputs_embeds,
1036
+ past_key_values_length,
1037
+ sliding_window=self.config.sliding_window,
1038
+ )
1039
+ else:
1040
+ attention_mask = _prepare_4d_attention_mask(
1041
+ attention_mask,
1042
+ dtype=inputs_embeds.dtype,
1043
+ )
1044
+
1045
+ hidden_states = inputs_embeds
1046
+
1047
+ # decoder layers
1048
+ all_hidden_states = () if output_hidden_states else None
1049
+ all_self_attns = () if output_attentions else None
1050
+ next_decoder_cache = None
1051
+
1052
+ for decoder_layer in self.layers:
1053
+ if output_hidden_states:
1054
+ all_hidden_states += (hidden_states,)
1055
+
1056
+ if self.gradient_checkpointing and self.training:
1057
+ layer_outputs = self._gradient_checkpointing_func(
1058
+ decoder_layer.__call__,
1059
+ hidden_states,
1060
+ attention_mask,
1061
+ position_ids,
1062
+ past_key_values,
1063
+ output_attentions,
1064
+ use_cache,
1065
+ )
1066
+ else:
1067
+ layer_outputs = decoder_layer(
1068
+ hidden_states,
1069
+ attention_mask=attention_mask,
1070
+ position_ids=position_ids,
1071
+ past_key_value=past_key_values,
1072
+ output_attentions=output_attentions,
1073
+ use_cache=use_cache,
1074
+ )
1075
+
1076
+ hidden_states = layer_outputs[0]
1077
+
1078
+ if use_cache:
1079
+ next_decoder_cache = layer_outputs[2 if output_attentions else 1]
1080
+
1081
+ if output_attentions:
1082
+ all_self_attns += (layer_outputs[1],)
1083
+
1084
+ hidden_states = self.norm(hidden_states)
1085
+
1086
+ # add hidden states from the last decoder layer
1087
+ if output_hidden_states:
1088
+ all_hidden_states += (hidden_states,)
1089
+
1090
+ next_cache = None
1091
+ if use_cache:
1092
+ next_cache = next_decoder_cache.to_legacy_cache() if use_legacy_cache else next_decoder_cache
1093
+
1094
+ if not return_dict:
1095
+ return tuple(v for v in [hidden_states, next_cache, all_hidden_states, all_self_attns] if v is not None)
1096
+ return BaseModelOutputWithPast(
1097
+ last_hidden_state=hidden_states,
1098
+ past_key_values=next_cache,
1099
+ hidden_states=all_hidden_states,
1100
+ attentions=all_self_attns,
1101
+ )
1102
+
1103
+
1104
+ class MistralForCausalLM(MistralPreTrainedModel):
1105
+ _tied_weights_keys = ["lm_head.weight"]
1106
+
1107
+ def __init__(self, config):
1108
+ super().__init__(config)
1109
+ self.model = MistralModel(config)
1110
+ self.vocab_size = config.vocab_size
1111
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
1112
+
1113
+ # Initialize weights and apply final processing
1114
+ self.post_init()
1115
+
1116
+ def get_input_embeddings(self):
1117
+ return self.model.embed_tokens
1118
+
1119
+ def set_input_embeddings(self, value):
1120
+ self.model.embed_tokens = value
1121
+
1122
+ def get_output_embeddings(self):
1123
+ return self.lm_head
1124
+
1125
+ def set_output_embeddings(self, new_embeddings):
1126
+ self.lm_head = new_embeddings
1127
+
1128
+ def set_decoder(self, decoder):
1129
+ self.model = decoder
1130
+
1131
+ def get_decoder(self):
1132
+ return self.model
1133
+
1134
+ @add_start_docstrings_to_model_forward(MISTRAL_INPUTS_DOCSTRING)
1135
+ @replace_return_docstrings(output_type=CausalLMOutputWithPast, config_class=_CONFIG_FOR_DOC)
1136
+ def forward(
1137
+ self,
1138
+ input_ids: torch.LongTensor = None,
1139
+ attention_mask: Optional[torch.Tensor] = None,
1140
+ position_ids: Optional[torch.LongTensor] = None,
1141
+ past_key_values: Optional[List[torch.FloatTensor]] = None,
1142
+ inputs_embeds: Optional[torch.FloatTensor] = None,
1143
+ labels: Optional[torch.LongTensor] = None,
1144
+ use_cache: Optional[bool] = None,
1145
+ output_attentions: Optional[bool] = None,
1146
+ output_hidden_states: Optional[bool] = None,
1147
+ return_dict: Optional[bool] = None,
1148
+ ) -> Union[Tuple, CausalLMOutputWithPast]:
1149
+ r"""
1150
+ Args:
1151
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
1152
+ Labels for computing the masked language modeling loss. Indices should either be in `[0, ...,
1153
+ config.vocab_size]` or -100 (see `input_ids` docstring). Tokens with indices set to `-100` are ignored
1154
+ (masked), the loss is only computed for the tokens with labels in `[0, ..., config.vocab_size]`.
1155
+
1156
+ Returns:
1157
+
1158
+ Example:
1159
+
1160
+ ```python
1161
+ >>> from transformers import AutoTokenizer, MistralForCausalLM
1162
+
1163
+ >>> model = MistralForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1")
1164
+ >>> tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-v0.1")
1165
+
1166
+ >>> prompt = "Hey, are you conscious? Can you talk to me?"
1167
+ >>> inputs = tokenizer(prompt, return_tensors="pt")
1168
+
1169
+ >>> # Generate
1170
+ >>> generate_ids = model.generate(inputs.input_ids, max_length=30)
1171
+ >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
1172
+ "Hey, are you conscious? Can you talk to me?\nI'm not conscious, but I can talk to you."
1173
+ ```"""
1174
+
1175
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
1176
+ output_hidden_states = (
1177
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
1178
+ )
1179
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1180
+
1181
+ # decoder outputs consists of (dec_features, layer_state, dec_hidden, dec_attn)
1182
+ outputs = self.model(
1183
+ input_ids=input_ids,
1184
+ attention_mask=attention_mask,
1185
+ position_ids=position_ids,
1186
+ past_key_values=past_key_values,
1187
+ inputs_embeds=inputs_embeds,
1188
+ use_cache=use_cache,
1189
+ output_attentions=output_attentions,
1190
+ output_hidden_states=output_hidden_states,
1191
+ return_dict=return_dict,
1192
+ )
1193
+
1194
+ hidden_states = outputs[0]
1195
+ logits = self.lm_head(hidden_states)
1196
+ logits = logits.float()
1197
+
1198
+ loss = None
1199
+ if labels is not None:
1200
+ # Shift so that tokens < n predict n
1201
+ shift_logits = logits[..., :-1, :].contiguous()
1202
+ shift_labels = labels[..., 1:].contiguous()
1203
+ # Flatten the tokens
1204
+ loss_fct = CrossEntropyLoss()
1205
+ shift_logits = shift_logits.view(-1, self.config.vocab_size)
1206
+ shift_labels = shift_labels.view(-1)
1207
+ # Enable model parallelism
1208
+ shift_labels = shift_labels.to(shift_logits.device)
1209
+ loss = loss_fct(shift_logits, shift_labels)
1210
+
1211
+ if not return_dict:
1212
+ output = (logits,) + outputs[1:]
1213
+ return (loss,) + output if loss is not None else output
1214
+
1215
+ return CausalLMOutputWithPast(
1216
+ loss=loss,
1217
+ logits=logits,
1218
+ past_key_values=outputs.past_key_values,
1219
+ hidden_states=outputs.hidden_states,
1220
+ attentions=outputs.attentions,
1221
+ )
1222
+
1223
+ def prepare_inputs_for_generation(
1224
+ self, input_ids, past_key_values=None, attention_mask=None, inputs_embeds=None, **kwargs
1225
+ ):
1226
+ # Omit tokens covered by past_key_values
1227
+ if past_key_values is not None:
1228
+ if isinstance(past_key_values, Cache):
1229
+ cache_length = past_key_values.get_seq_length()
1230
+ past_length = past_key_values.get_seq_length()
1231
+ max_cache_length = past_key_values.get_max_cache_shape()
1232
+ else:
1233
+ cache_length = past_length = past_key_values[0][0].shape[2]
1234
+ max_cache_length = None
1235
+
1236
+ # Keep only the unprocessed tokens:
1237
+ # 1 - If the length of the attention_mask exceeds the length of input_ids, then we are in a setting where
1238
+ # some of the inputs are exclusively passed as part of the cache (e.g. when passing input_embeds as
1239
+ # input)
1240
+ if attention_mask is not None and attention_mask.shape[1] > input_ids.shape[1]:
1241
+ input_ids = input_ids[:, -(attention_mask.shape[1] - past_length) :]
1242
+ # 2 - If the past_length is smaller than input_ids', then input_ids holds all input tokens. We can discard
1243
+ # input_ids based on the past_length.
1244
+ elif past_length < input_ids.shape[1]:
1245
+ input_ids = input_ids[:, past_length:]
1246
+ # 3 - Otherwise (past_length >= input_ids.shape[1]), let's assume input_ids only has unprocessed tokens.
1247
+
1248
+ # If we are about to go beyond the maximum cache length, we need to crop the input attention mask.
1249
+ if (
1250
+ max_cache_length is not None
1251
+ and attention_mask is not None
1252
+ and cache_length + input_ids.shape[1] > max_cache_length
1253
+ ):
1254
+ attention_mask = attention_mask[:, -max_cache_length:]
1255
+
1256
+ position_ids = kwargs.get("position_ids", None)
1257
+ if attention_mask is not None and position_ids is None:
1258
+ # create position_ids on the fly for batch generation
1259
+ position_ids = attention_mask.long().cumsum(-1) - 1
1260
+ position_ids.masked_fill_(attention_mask == 0, 1)
1261
+ if past_key_values:
1262
+ position_ids = position_ids[:, -input_ids.shape[1] :]
1263
+
1264
+ # if `inputs_embeds` are passed, we only want to use them in the 1st generation step
1265
+ if inputs_embeds is not None and past_key_values is None:
1266
+ model_inputs = {"inputs_embeds": inputs_embeds}
1267
+ else:
1268
+ model_inputs = {"input_ids": input_ids}
1269
+
1270
+ model_inputs.update(
1271
+ {
1272
+ "position_ids": position_ids,
1273
+ "past_key_values": past_key_values,
1274
+ "use_cache": kwargs.get("use_cache"),
1275
+ "attention_mask": attention_mask,
1276
+ }
1277
+ )
1278
+ return model_inputs
1279
+
1280
+ @staticmethod
1281
+ def _reorder_cache(past_key_values, beam_idx):
1282
+ reordered_past = ()
1283
+ for layer_past in past_key_values:
1284
+ reordered_past += (
1285
+ tuple(past_state.index_select(0, beam_idx.to(past_state.device)) for past_state in layer_past),
1286
+ )
1287
+ return reordered_past
1288
+
1289
+
1290
+
1291
+
1292
+ @dataclass
1293
+ class MoEMaskedLMOutput(ModelOutput):
1294
+ """
1295
+ Base class for causal language model (or autoregressive) outputs as well as Mixture of Expert's router hidden
1296
+ states terms, to train a MoE model.
1297
+
1298
+ Args:
1299
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
1300
+ Language modeling loss (for next-token prediction).
1301
+ logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
1302
+ Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
1303
+ past_key_values (`tuple(tuple(torch.FloatTensor))`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
1304
+ Tuple of `tuple(torch.FloatTensor)` of length `config.n_layers`, with each tuple having 2 tensors of shape
1305
+ `(batch_size, num_heads, sequence_length, embed_size_per_head)`)
1306
+
1307
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
1308
+ `past_key_values` input) to speed up sequential decoding.
1309
+ hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
1310
+ Tuple of `torch.FloatTensor` (one for the output of the embeddings, if the model has an embedding layer, +
1311
+ one for the output of each layer) of shape `(batch_size, sequence_length, hidden_size)`.
1312
+
1313
+ Hidden-states of the model at the output of each layer plus the optional initial embedding outputs.
1314
+ attentions (`tuple(torch.FloatTensor)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
1315
+ Tuple of `torch.FloatTensor` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
1316
+ sequence_length)`.
1317
+
1318
+ Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
1319
+ heads.
1320
+ z_loss (`torch.FloatTensor`, *optional*, returned when `labels` is provided):
1321
+ z_loss for the sparse modules.
1322
+ aux_loss (`torch.FloatTensor`, *optional*, returned when `labels` is provided):
1323
+ aux_loss for the sparse modules.
1324
+ router_logits (`tuple(torch.FloatTensor)`, *optional*, returned when `output_router_logits=True` is passed or when `config.add_router_probs=True`):
1325
+ Tuple of `torch.FloatTensor` (one for each layer) of shape `(batch_size, sequence_length, num_experts)`.
1326
+
1327
+ Router logits of the encoder model, useful to compute the auxiliary loss and the z_loss for the sparse
1328
+ modules.
1329
+ """
1330
+
1331
+ loss: Optional[torch.FloatTensor] = None
1332
+ logits: torch.FloatTensor = None
1333
+ hidden_states: Optional[Tuple[torch.FloatTensor, ...]] = None
1334
+ attentions: Optional[Tuple[torch.FloatTensor, ...]] = None
1335
+ z_loss: torch.FloatTensor = None
1336
+ aux_loss: torch.FloatTensor = None
1337
+ router_logits: Optional[Tuple[torch.FloatTensor]] = None
1338
+
1339
+
1340
+
1341
+ class MistralPredictionHeadTransform(nn.Module):
1342
+ def __init__(self, config):
1343
+ super().__init__()
1344
+ self.dense = nn.Linear(config.hidden_size, config.hidden_size)
1345
+ if isinstance(config.hidden_act, str):
1346
+ self.transform_act_fn = ACT2FN[config.hidden_act]
1347
+ else:
1348
+ self.transform_act_fn = config.hidden_act
1349
+ self.norm = MistralRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
1350
+
1351
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
1352
+ hidden_states = self.dense(hidden_states)
1353
+ hidden_states = self.transform_act_fn(hidden_states)
1354
+ hidden_states = self.norm(hidden_states)
1355
+ return hidden_states
1356
+
1357
+
1358
+ class MistralLMPredictionHead(nn.Module):
1359
+ def __init__(self, config):
1360
+ super().__init__()
1361
+ self.transform = MistralPredictionHeadTransform(config)
1362
+
1363
+ # The output weights are the same as the input embeddings, but there is
1364
+ # an output-only bias for each token.
1365
+ self.decoder = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
1366
+
1367
+ self.bias = nn.Parameter(torch.zeros(config.vocab_size))
1368
+
1369
+ # Need a link between the two variables so that the bias is correctly resized with `resize_token_embeddings`
1370
+ self.decoder.bias = self.bias
1371
+
1372
+ def _tie_weights(self):
1373
+ self.decoder.bias = self.bias
1374
+
1375
+ def forward(self, hidden_states):
1376
+ hidden_states = self.transform(hidden_states)
1377
+ hidden_states = self.decoder(hidden_states)
1378
+ return hidden_states
1379
+
1380
+
1381
+ class MistralOnlyMLMHead(nn.Module):
1382
+ def __init__(self, config):
1383
+ super().__init__()
1384
+ self.predictions = MistralLMPredictionHead(config)
1385
+
1386
+ def forward(self, sequence_output: torch.Tensor) -> torch.Tensor:
1387
+ prediction_scores = self.predictions(sequence_output)
1388
+ return prediction_scores
1389
+
1390
+
1391
+
1392
+ class MistralForMaskedLM(MistralPreTrainedModel):
1393
+ _tied_weights_keys = ["predictions.decoder.bias", "cls.predictions.decoder.weight"]
1394
+
1395
+ def __init__(self, config):
1396
+ super().__init__(config)
1397
+
1398
+ if config.is_decoder:
1399
+ logger.warning(
1400
+ "If you want to use `MistralForMaskedLM` make sure `config.is_decoder=False` for "
1401
+ "bi-directional self-attention."
1402
+ )
1403
+
1404
+ self.model = MistralModel(config)
1405
+ self.cls = MistralOnlyMLMHead(config)
1406
+
1407
+ # Initialize weights and apply final processing
1408
+ self.post_init()
1409
+
1410
+ def get_output_embeddings(self):
1411
+ return self.cls.predictions.decoder
1412
+
1413
+ def set_output_embeddings(self, new_embeddings):
1414
+ self.cls.predictions.decoder = new_embeddings
1415
+ self.cls.predictions.bias = new_embeddings.bias
1416
+
1417
+ def forward(
1418
+ self,
1419
+ input_ids: Optional[torch.Tensor] = None,
1420
+ attention_mask: Optional[torch.Tensor] = None,
1421
+ token_type_ids: Optional[torch.Tensor] = None,
1422
+ position_ids: Optional[torch.Tensor] = None,
1423
+ inputs_embeds: Optional[torch.Tensor] = None,
1424
+ labels: Optional[torch.Tensor] = None,
1425
+ output_attentions: Optional[bool] = None,
1426
+ output_hidden_states: Optional[bool] = None,
1427
+ return_dict: Optional[bool] = None,
1428
+ ) -> Union[Tuple[torch.Tensor], MoEMaskedLMOutput]:
1429
+ r"""
1430
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
1431
+ Labels for computing the masked language modeling loss. Indices should be in `[-100, 0, ...,
1432
+ config.vocab_size]` (see `input_ids` docstring) Tokens with indices set to `-100` are ignored (masked), the
1433
+ loss is only computed for the tokens with labels in `[0, ..., config.vocab_size]`
1434
+ """
1435
+
1436
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1437
+
1438
+ outputs = self.model(
1439
+ input_ids,
1440
+ attention_mask=attention_mask,
1441
+ position_ids=position_ids,
1442
+ inputs_embeds=inputs_embeds,
1443
+ output_attentions=output_attentions,
1444
+ output_hidden_states=output_hidden_states,
1445
+ return_dict=return_dict,
1446
+ )
1447
+
1448
+
1449
+ sequence_output = outputs[0]
1450
+ logits = self.cls(sequence_output)
1451
+
1452
+ loss = None
1453
+ if labels is not None:
1454
+ loss_fct = CrossEntropyLoss() # -100 index = padding token
1455
+ loss = loss_fct(logits.view(-1, self.config.vocab_size), labels.view(-1))
1456
+
1457
+
1458
+ if not return_dict:
1459
+ output = (logits,) + outputs[2:]
1460
+ return ((loss,) + output) if loss is not None else output
1461
+
1462
+ return MoEMaskedLMOutput(
1463
+ loss=loss,
1464
+ logits=logits,
1465
+ hidden_states=outputs.hidden_states,
1466
+ attentions=outputs.attentions,
1467
+ )
1468
+
1469
+
1470
+
1471
+
1472
+ @add_start_docstrings(
1473
+ """
1474
+ The Mistral Model transformer with a sequence classification head on top (linear layer).
1475
+
1476
+ [`MistralForSequenceClassification`] uses the last token in order to do the classification, as other causal models
1477
+ (e.g. GPT-2) do.
1478
+
1479
+ Since it does classification on the last token, it requires to know the position of the last token. If a
1480
+ `pad_token_id` is defined in the configuration, it finds the last token that is not a padding token in each row. If
1481
+ no `pad_token_id` is defined, it simply takes the last value in each row of the batch. Since it cannot guess the
1482
+ padding tokens when `inputs_embeds` are passed instead of `input_ids`, it does the same (take the last value in
1483
+ each row of the batch).
1484
+ """,
1485
+ MISTRAL_START_DOCSTRING,
1486
+ )
1487
+ # Copied from transformers.models.llama.modeling_llama.LlamaForSequenceClassification with Llama->Mistral, LLAMA->MISTRAL
1488
+ class MistralForSequenceClassification(MistralPreTrainedModel):
1489
+ def __init__(self, config):
1490
+ super().__init__(config)
1491
+ self.num_labels = config.num_labels
1492
+ self.model = MistralModel(config)
1493
+ self.score = nn.Linear(config.hidden_size, self.num_labels, bias=False)
1494
+ self.dropout = nn.Dropout(config.classifier_dropout)
1495
+ self.is_causal = config.is_causal
1496
+
1497
+
1498
+ # Initialize weights and apply final processing
1499
+ self.post_init()
1500
+
1501
+ def get_input_embeddings(self):
1502
+ return self.model.embed_tokens
1503
+
1504
+ def set_input_embeddings(self, value):
1505
+ self.model.embed_tokens = value
1506
+
1507
+ @add_start_docstrings_to_model_forward(MISTRAL_INPUTS_DOCSTRING)
1508
+ def forward(
1509
+ self,
1510
+ input_ids: torch.LongTensor = None,
1511
+ attention_mask: Optional[torch.Tensor] = None,
1512
+ position_ids: Optional[torch.LongTensor] = None,
1513
+ past_key_values: Optional[List[torch.FloatTensor]] = None,
1514
+ inputs_embeds: Optional[torch.FloatTensor] = None,
1515
+ labels: Optional[torch.LongTensor] = None,
1516
+ use_cache: Optional[bool] = None,
1517
+ output_attentions: Optional[bool] = None,
1518
+ output_hidden_states: Optional[bool] = None,
1519
+ return_dict: Optional[bool] = None,
1520
+ ) -> Union[Tuple, SequenceClassifierOutputWithPast]:
1521
+ r"""
1522
+ labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
1523
+ Labels for computing the sequence classification/regression loss. Indices should be in `[0, ...,
1524
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
1525
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
1526
+ """
1527
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1528
+
1529
+ transformer_outputs = self.model(
1530
+ input_ids,
1531
+ attention_mask=attention_mask,
1532
+ position_ids=position_ids,
1533
+ past_key_values=past_key_values,
1534
+ inputs_embeds=inputs_embeds,
1535
+ use_cache=use_cache,
1536
+ output_attentions=output_attentions,
1537
+ output_hidden_states=output_hidden_states,
1538
+ return_dict=return_dict,
1539
+ )
1540
+ hidden_states = transformer_outputs[0]
1541
+ hidden_states = self.dropout(hidden_states)
1542
+ logits = self.score(hidden_states)
1543
+
1544
+ if input_ids is not None:
1545
+ batch_size = input_ids.shape[0]
1546
+ else:
1547
+ batch_size = inputs_embeds.shape[0]
1548
+
1549
+ if self.is_causal:
1550
+ if self.config.pad_token_id is None and batch_size != 1:
1551
+ raise ValueError("Cannot handle batch sizes > 1 if no padding token is defined.")
1552
+ if self.config.pad_token_id is None:
1553
+ sequence_lengths = -1
1554
+ else:
1555
+ if input_ids is not None:
1556
+ # if no pad token found, use modulo instead of reverse indexing for ONNX compatibility
1557
+ sequence_lengths = torch.eq(input_ids, self.config.pad_token_id).int().argmax(-1) - 1
1558
+ sequence_lengths = sequence_lengths % input_ids.shape[-1]
1559
+ sequence_lengths = sequence_lengths.to(logits.device)
1560
+ else:
1561
+ sequence_lengths = -1
1562
+
1563
+ pooled_logits = logits[torch.arange(batch_size, device=logits.device), sequence_lengths]
1564
+ else:
1565
+ pooled_logits = logits[:, 0]
1566
+
1567
+ loss = None
1568
+ if labels is not None:
1569
+ labels = labels.to(logits.device)
1570
+ if self.config.problem_type is None:
1571
+ if self.num_labels == 1:
1572
+ self.config.problem_type = "regression"
1573
+ elif self.num_labels > 1 and (labels.dtype == torch.long or labels.dtype == torch.int):
1574
+ self.config.problem_type = "single_label_classification"
1575
+ else:
1576
+ self.config.problem_type = "multi_label_classification"
1577
+
1578
+ if self.config.problem_type == "regression":
1579
+ loss_fct = MSELoss()
1580
+ if self.num_labels == 1:
1581
+ loss = loss_fct(pooled_logits.squeeze(), labels.squeeze())
1582
+ else:
1583
+ loss = loss_fct(pooled_logits, labels)
1584
+ elif self.config.problem_type == "single_label_classification":
1585
+ loss_fct = CrossEntropyLoss()
1586
+ loss = loss_fct(pooled_logits.view(-1, self.num_labels), labels.view(-1))
1587
+ elif self.config.problem_type == "multi_label_classification":
1588
+ loss_fct = BCEWithLogitsLoss()
1589
+ loss = loss_fct(pooled_logits, labels)
1590
+ if not return_dict:
1591
+ output = (pooled_logits,) + transformer_outputs[1:]
1592
+ return ((loss,) + output) if loss is not None else output
1593
+
1594
+ return SequenceClassifierOutputWithPast(
1595
+ loss=loss,
1596
+ logits=pooled_logits,
1597
+ past_key_values=transformer_outputs.past_key_values,
1598
+ hidden_states=transformer_outputs.hidden_states,
1599
+ attentions=transformer_outputs.attentions,
1600
+ )
1601
+
1602
+ # import torch
1603
+ # from safetensors import safe_open
1604
+ # from safetensors.torch import save_file
1605
+
1606
+ # tensors = {}
1607
+ # with safe_open("/root/MOE_DNA/trained_model/mistral_mlm_alldata_len5k_ep3/model.safetensors", framework="pt", device="cpu") as f:
1608
+ # # print(f.metadata())
1609
+
1610
+ # for key in f.keys():
1611
+ # tensors[key] = f.get_tensor(key)
1612
+
1613
+ # new_model = {}
1614
+ # for key in tensors.keys():
1615
+ # k = key.replace("Mistral", "model")
1616
+ # new_model[k] = tensors[key]
1617
+
pyproject.toml DELETED
@@ -1,28 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools>=69"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "genomeocean-sub-classifier"
7
- version = "0.1.0"
8
- description = "GenomeOcean NCLDV versus Mirus classifier for genomic FASTA files"
9
- requires-python = ">=3.11"
10
- dependencies = [
11
- "torch>=2.8,<2.9",
12
- "transformers==4.51.3",
13
- "huggingface-hub>=0.36,<1",
14
- "safetensors>=0.5,<1",
15
- "numpy>=2.2,<3",
16
- "pandas>=2.2,<3",
17
- ]
18
-
19
- [project.scripts]
20
- genomeocean-sub = "genomeocean_sub.cli:main"
21
-
22
- [project.optional-dependencies]
23
- dev = [
24
- "pytest>=8,<9",
25
- ]
26
-
27
- [tool.setuptools.packages.find]
28
- where = ["src"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/genomeocean_sub/__init__.py DELETED
@@ -1,3 +0,0 @@
1
- """User-facing inference package for the GenomeOcean sub classifier."""
2
-
3
- __version__ = "0.1.0"
 
 
 
 
src/genomeocean_sub/aggregation.py DELETED
@@ -1,113 +0,0 @@
1
- """Aggregate Sub predictions from chunks to contigs and files."""
2
-
3
- from __future__ import annotations
4
-
5
- from collections import Counter
6
- from typing import Iterable, Mapping
7
-
8
- import pandas as pd
9
-
10
-
11
- SUB_LABELS = {0: "NCLDV", 1: "Mirus"}
12
- PROBABILITY_COLUMNS = {0: "prob_ncldv", 1: "prob_mirus"}
13
- COUNT_COLUMNS = {0: "n_chunks_ncldv", 1: "n_chunks_mirus"}
14
-
15
-
16
- def _winner(values: Iterable[int], labels: Mapping[int, str]) -> tuple[int, int]:
17
- counts = Counter(int(value) for value in values)
18
- invalid = set(counts) - set(labels)
19
- if invalid:
20
- raise ValueError(f"Unexpected predicted labels: {sorted(invalid)}")
21
- winner = min(labels, key=lambda label: (-counts[label], label))
22
- return winner, counts[winner]
23
-
24
-
25
- def aggregate_chunks_to_contigs(chunk_results: pd.DataFrame) -> pd.DataFrame:
26
- output_columns = [
27
- "record_id",
28
- "source_file",
29
- "contig_id",
30
- "original_length",
31
- "clean_length",
32
- "n_chunks",
33
- "ignored_tail_bp",
34
- *COUNT_COLUMNS.values(),
35
- *PROBABILITY_COLUMNS.values(),
36
- "ensemble_size",
37
- "mean_chunk_ensemble_agreement",
38
- "mean_chunk_confidence_std",
39
- "predicted_label",
40
- "predicted_name",
41
- "predicted_votes",
42
- "confidence",
43
- ]
44
- if chunk_results.empty:
45
- return pd.DataFrame(columns=output_columns)
46
-
47
- group_columns = [
48
- "record_id",
49
- "source_file",
50
- "contig_id",
51
- "original_length",
52
- "clean_length",
53
- "n_chunks",
54
- "ignored_tail_bp",
55
- ]
56
- rows: list[dict] = []
57
- for group_key, group in chunk_results.groupby(group_columns, sort=False, dropna=False):
58
- row = dict(zip(group_columns, group_key))
59
- winner, votes = _winner(group["predicted_label"], SUB_LABELS)
60
- counts = Counter(group["predicted_label"].astype(int))
61
- for label, column in COUNT_COLUMNS.items():
62
- row[column] = int(counts[label])
63
- for _, column in PROBABILITY_COLUMNS.items():
64
- row[column] = float(group[column].mean())
65
- row["ensemble_size"] = int(
66
- group["ensemble_size"].max() if "ensemble_size" in group else 1
67
- )
68
- row["mean_chunk_ensemble_agreement"] = float(
69
- group["ensemble_agreement"].mean()
70
- if "ensemble_agreement" in group
71
- else 1.0
72
- )
73
- row["mean_chunk_confidence_std"] = float(
74
- group["confidence_std"].mean() if "confidence_std" in group else 0.0
75
- )
76
- row["predicted_label"] = winner
77
- row["predicted_name"] = SUB_LABELS[winner]
78
- row["predicted_votes"] = votes
79
- row["confidence"] = row[PROBABILITY_COLUMNS[winner]]
80
- rows.append(row)
81
- return pd.DataFrame(rows, columns=output_columns)
82
-
83
-
84
- def aggregate_contigs_to_files(contig_results: pd.DataFrame) -> pd.DataFrame:
85
- count_columns = {0: "n_contigs_ncldv", 1: "n_contigs_mirus"}
86
- output_columns = [
87
- "source_file",
88
- "n_contigs",
89
- *count_columns.values(),
90
- "predicted_label",
91
- "predicted_name",
92
- "predicted_votes",
93
- "confidence",
94
- ]
95
- if contig_results.empty:
96
- return pd.DataFrame(columns=output_columns)
97
-
98
- rows: list[dict] = []
99
- for source_file, group in contig_results.groupby("source_file", sort=False):
100
- winner, votes = _winner(group["predicted_label"], SUB_LABELS)
101
- counts = Counter(group["predicted_label"].astype(int))
102
- row = {
103
- "source_file": source_file,
104
- "n_contigs": len(group),
105
- "predicted_label": winner,
106
- "predicted_name": SUB_LABELS[winner],
107
- "predicted_votes": votes,
108
- "confidence": float(group.loc[group["predicted_label"] == winner, "confidence"].mean()),
109
- }
110
- for label, column in count_columns.items():
111
- row[column] = int(counts[label])
112
- rows.append(row)
113
- return pd.DataFrame(rows, columns=output_columns)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/genomeocean_sub/cli.py DELETED
@@ -1,85 +0,0 @@
1
- """Command-line interface for the Sub classifier."""
2
-
3
- from __future__ import annotations
4
-
5
- import argparse
6
- import os
7
-
8
- from .predict import SubPredictor
9
-
10
-
11
- def build_parser() -> argparse.ArgumentParser:
12
- parser = argparse.ArgumentParser(
13
- prog="genomeocean-sub",
14
- description="Classify FASTA contigs as NCLDV or Mirus.",
15
- )
16
- subparsers = parser.add_subparsers(dest="command", required=True)
17
- predict = subparsers.add_parser("predict", help="classify a FASTA file or directory")
18
- predict.add_argument("--input", required=True)
19
- predict.add_argument("--output-dir", required=True)
20
- predict.add_argument(
21
- "--model-id",
22
- action="append",
23
- default=None,
24
- help=(
25
- "Hugging Face model ID or local model path. Repeat this option for "
26
- "independent fold paths. A single value may also be set with "
27
- "GENOMEOCEAN_SUB_MODEL."
28
- ),
29
- )
30
- predict.add_argument(
31
- "--subfolder",
32
- action="append",
33
- default=None,
34
- help=(
35
- "Fold subfolder inside one shared --model-id, for example fold1. "
36
- "Repeat for all ensemble members."
37
- ),
38
- )
39
- predict.add_argument("--revision", default=None)
40
- predict.add_argument("--device", default="auto")
41
- predict.add_argument("--batch-size", type=int, default=8)
42
- predict.add_argument("--chunk-size", type=int, default=5000)
43
- predict.add_argument("--stride", type=int, default=5000)
44
- predict.add_argument("--max-length", type=int, default=1250)
45
- predict.add_argument("--cache-dir", default=None)
46
- predict.add_argument("--local-files-only", action="store_true")
47
- return parser
48
-
49
-
50
- def main() -> None:
51
- args = build_parser().parse_args()
52
- if args.command == "predict":
53
- model_ids = args.model_id
54
- if not model_ids:
55
- environment_model = os.environ.get("GENOMEOCEAN_SUB_MODEL")
56
- model_ids = [environment_model] if environment_model else None
57
- if not model_ids:
58
- raise SystemExit(
59
- "--model-id is required until a public default model has been published"
60
- )
61
- predictor = SubPredictor(
62
- model_ids,
63
- revision=args.revision,
64
- subfolders=args.subfolder,
65
- device=args.device,
66
- batch_size=args.batch_size,
67
- max_length=args.max_length,
68
- cache_dir=args.cache_dir,
69
- local_files_only=args.local_files_only,
70
- )
71
- bundle = predictor.predict_fasta(
72
- args.input,
73
- args.output_dir,
74
- chunk_size=args.chunk_size,
75
- stride=args.stride,
76
- )
77
- print(
78
- f"[OK] predicted {len(bundle.contig_results)} contigs; "
79
- f"ensemble models {len(predictor.model_specs)}; "
80
- f"skipped {len(bundle.skipped)}; outputs: {args.output_dir}"
81
- )
82
-
83
-
84
- if __name__ == "__main__":
85
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/genomeocean_sub/fasta.py DELETED
@@ -1,125 +0,0 @@
1
- """Small FASTA reader/writer for the independent Sub classifier."""
2
-
3
- from __future__ import annotations
4
-
5
- from dataclasses import dataclass
6
- import gzip
7
- from pathlib import Path
8
- from typing import Iterable, Iterator, TextIO
9
-
10
-
11
- FASTA_SUFFIXES = (".fa", ".fasta", ".fna", ".fa.gz", ".fasta.gz", ".fna.gz")
12
-
13
-
14
- @dataclass(frozen=True)
15
- class FastaRecord:
16
- source_file: str
17
- contig_id: str
18
- description: str
19
- sequence: str
20
-
21
- @property
22
- def record_id(self) -> str:
23
- return f"{self.source_file}::{self.contig_id}"
24
-
25
-
26
- def is_fasta_path(path: Path) -> bool:
27
- return path.is_file() and path.name.lower().endswith(FASTA_SUFFIXES)
28
-
29
-
30
- def discover_fasta_files(input_path: str | Path) -> list[Path]:
31
- path = Path(input_path).expanduser().resolve()
32
- if not path.exists():
33
- raise FileNotFoundError(f"Input does not exist: {path}")
34
- if path.is_file():
35
- if not is_fasta_path(path):
36
- raise ValueError(
37
- f"Unsupported FASTA extension: {path.name}. "
38
- f"Expected one of: {', '.join(FASTA_SUFFIXES)}"
39
- )
40
- return [path]
41
- files = sorted(candidate for candidate in path.rglob("*") if is_fasta_path(candidate))
42
- if not files:
43
- raise ValueError(f"No FASTA files found under: {path}")
44
- return files
45
-
46
-
47
- def _open_text(path: Path) -> TextIO:
48
- if path.name.lower().endswith(".gz"):
49
- return gzip.open(path, "rt")
50
- return path.open("rt")
51
-
52
-
53
- def read_fasta(path: str | Path, *, source_name: str | None = None) -> Iterator[FastaRecord]:
54
- fasta_path = Path(path)
55
- source_file = source_name or fasta_path.name
56
- seen_ids: set[str] = set()
57
- description: str | None = None
58
- sequence_parts: list[str] = []
59
-
60
- def build_record() -> FastaRecord:
61
- assert description is not None
62
- contig_id = description.split()[0]
63
- if contig_id in seen_ids:
64
- raise ValueError(f"Duplicate FASTA ID '{contig_id}' in {fasta_path}")
65
- seen_ids.add(contig_id)
66
- return FastaRecord(
67
- source_file=source_file,
68
- contig_id=contig_id,
69
- description=description,
70
- sequence="".join(sequence_parts),
71
- )
72
-
73
- with _open_text(fasta_path) as handle:
74
- for line_number, raw_line in enumerate(handle, start=1):
75
- line = raw_line.strip()
76
- if not line:
77
- continue
78
- if line.startswith(">"):
79
- if description is not None:
80
- yield build_record()
81
- description = line[1:].strip()
82
- if not description:
83
- raise ValueError(f"Empty FASTA header at {fasta_path}:{line_number}")
84
- sequence_parts = []
85
- else:
86
- if description is None:
87
- raise ValueError(
88
- f"Sequence appears before the first FASTA header at "
89
- f"{fasta_path}:{line_number}"
90
- )
91
- sequence_parts.append(line)
92
- if description is not None:
93
- yield build_record()
94
-
95
-
96
- def read_input_records(input_path: str | Path) -> list[FastaRecord]:
97
- input_root = Path(input_path).expanduser().resolve()
98
- files = discover_fasta_files(input_root)
99
- base = input_root if input_root.is_dir() else input_root.parent
100
- records: list[FastaRecord] = []
101
- for path in files:
102
- records.extend(
103
- read_fasta(path, source_name=path.relative_to(base).as_posix())
104
- )
105
- if not records:
106
- raise ValueError(f"No FASTA records found in: {input_root}")
107
- return records
108
-
109
-
110
- def write_fasta(
111
- records: Iterable[FastaRecord],
112
- output_path: str | Path,
113
- *,
114
- use_record_id: bool = False,
115
- line_width: int = 80,
116
- ) -> Path:
117
- path = Path(output_path)
118
- path.parent.mkdir(parents=True, exist_ok=True)
119
- with path.open("w") as handle:
120
- for record in records:
121
- header = record.record_id if use_record_id else record.description
122
- handle.write(f">{header}\n")
123
- for start in range(0, len(record.sequence), line_width):
124
- handle.write(record.sequence[start : start + line_width] + "\n")
125
- return path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/genomeocean_sub/predict.py DELETED
@@ -1,416 +0,0 @@
1
- """Hugging Face model loading and end-to-end NCLDV/Mirus classification."""
2
-
3
- from __future__ import annotations
4
-
5
- from dataclasses import asdict, dataclass
6
- import gc
7
- import json
8
- from pathlib import Path
9
- from typing import Sequence
10
-
11
- import pandas as pd
12
- import torch
13
- from transformers import AutoModelForSequenceClassification, AutoTokenizer
14
-
15
- from .aggregation import (
16
- PROBABILITY_COLUMNS,
17
- SUB_LABELS,
18
- aggregate_chunks_to_contigs,
19
- aggregate_contigs_to_files,
20
- )
21
- from .fasta import FastaRecord, read_input_records
22
- from .preprocessing import (
23
- DEFAULT_CHUNK_SIZE,
24
- DEFAULT_STRIDE,
25
- SequenceChunk,
26
- SkippedRecord,
27
- preprocess_records,
28
- )
29
-
30
- ENSEMBLE_METHOD = "mean_probabilities"
31
- SUB_CHUNK_RESULT_COLUMNS = [
32
- "record_id",
33
- "source_file",
34
- "contig_id",
35
- "original_length",
36
- "clean_length",
37
- "n_chunks",
38
- "ignored_tail_bp",
39
- "chunk_index",
40
- "chunk_start",
41
- "chunk_end",
42
- "predicted_label",
43
- "predicted_name",
44
- "confidence",
45
- "ensemble_size",
46
- "ensemble_votes",
47
- "ensemble_agreement",
48
- "confidence_std",
49
- *PROBABILITY_COLUMNS.values(),
50
- ]
51
- SKIPPED_RESULT_COLUMNS = [
52
- "record_id",
53
- "source_file",
54
- "contig_id",
55
- "original_length",
56
- "clean_length",
57
- "reason",
58
- ]
59
-
60
-
61
- @dataclass(frozen=True)
62
- class ModelSpec:
63
- """One Sub ensemble member stored locally or on Hugging Face."""
64
-
65
- model_id: str
66
- revision: str | None = None
67
- subfolder: str | None = None
68
- name: str | None = None
69
-
70
- def to_dict(self) -> dict:
71
- return asdict(self)
72
-
73
-
74
- @dataclass
75
- class SubPredictionBundle:
76
- records: list[FastaRecord]
77
- chunks: list[SequenceChunk]
78
- skipped: list[SkippedRecord]
79
- chunk_results: pd.DataFrame
80
- contig_results: pd.DataFrame
81
- file_results: pd.DataFrame
82
-
83
-
84
- def build_model_specs(
85
- model_ids: str | Sequence[str],
86
- *,
87
- revision: str | None = None,
88
- subfolders: Sequence[str] | None = None,
89
- ) -> list[ModelSpec]:
90
- ids = [model_ids] if isinstance(model_ids, str) else list(model_ids)
91
- ids = [str(value).strip() for value in ids if str(value).strip()]
92
- if not ids:
93
- raise ValueError("At least one model_id is required")
94
-
95
- folders = [str(value).strip() for value in (subfolders or []) if str(value).strip()]
96
- if folders:
97
- if len(ids) != 1:
98
- raise ValueError(
99
- "subfolders can only be used with one shared model_id; "
100
- "repeat model_id instead when every fold has a different path"
101
- )
102
- return [
103
- ModelSpec(
104
- model_id=ids[0],
105
- revision=revision,
106
- subfolder=folder,
107
- name=folder,
108
- )
109
- for folder in folders
110
- ]
111
-
112
- return [
113
- ModelSpec(
114
- model_id=model_id,
115
- revision=revision,
116
- name=f"model{index}",
117
- )
118
- for index, model_id in enumerate(ids, start=1)
119
- ]
120
-
121
-
122
- def resolve_device(requested: str) -> torch.device:
123
- if requested == "auto":
124
- if torch.cuda.is_available():
125
- return torch.device("cuda")
126
- if hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
127
- return torch.device("mps")
128
- return torch.device("cpu")
129
- device = torch.device(requested)
130
- if device.type == "cuda" and not torch.cuda.is_available():
131
- raise RuntimeError("CUDA was requested, but torch.cuda.is_available() is False")
132
- return device
133
-
134
-
135
- class ProbabilityEnsembleAccumulator:
136
- """Accumulate fold outputs and calculate a soft-voting prediction."""
137
-
138
- def __init__(self, n_items: int, n_labels: int):
139
- self.n_items = n_items
140
- self.n_labels = n_labels
141
- self.n_models = 0
142
- self._sum = torch.zeros((n_items, n_labels), dtype=torch.float64)
143
- self._sum_squares = torch.zeros((n_items, n_labels), dtype=torch.float64)
144
- self._votes = torch.zeros((n_items, n_labels), dtype=torch.int64)
145
-
146
- def add(self, probabilities: torch.Tensor) -> None:
147
- values = probabilities.detach().to(device="cpu", dtype=torch.float64)
148
- expected = (self.n_items, self.n_labels)
149
- if tuple(values.shape) != expected:
150
- raise ValueError(
151
- f"Model probabilities have shape {tuple(values.shape)}; expected {expected}"
152
- )
153
- if not torch.isfinite(values).all():
154
- raise ValueError("Model probabilities contain NaN or infinite values")
155
-
156
- self._sum += values
157
- self._sum_squares += values.square()
158
- predicted = values.argmax(dim=1)
159
- self._votes.scatter_add_(
160
- 1,
161
- predicted.unsqueeze(1),
162
- torch.ones((self.n_items, 1), dtype=torch.int64),
163
- )
164
- self.n_models += 1
165
-
166
- def finalize(
167
- self,
168
- ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
169
- if self.n_models == 0:
170
- raise ValueError("Cannot finalize an ensemble with no model predictions")
171
-
172
- mean = self._sum / self.n_models
173
- variance = (self._sum_squares / self.n_models - mean.square()).clamp_min(0)
174
- standard_deviation = variance.sqrt()
175
- predicted = mean.argmax(dim=1)
176
- selected_votes = self._votes.gather(1, predicted.unsqueeze(1)).squeeze(1)
177
- agreement = selected_votes.to(torch.float64) / self.n_models
178
- confidence_std = standard_deviation.gather(
179
- 1, predicted.unsqueeze(1)
180
- ).squeeze(1)
181
- return mean, predicted, selected_votes, agreement, confidence_std
182
-
183
-
184
- class SubPredictor:
185
- """Predict NCLDV/Mirus with one model or a sequential fold ensemble."""
186
-
187
- def __init__(
188
- self,
189
- model_id: str | Sequence[str],
190
- *,
191
- revision: str | None = None,
192
- subfolders: Sequence[str] | None = None,
193
- device: str = "auto",
194
- batch_size: int = 8,
195
- max_length: int = 1250,
196
- cache_dir: str | None = None,
197
- local_files_only: bool = False,
198
- ):
199
- if batch_size <= 0:
200
- raise ValueError("batch_size must be greater than zero")
201
- self.model_specs = build_model_specs(
202
- model_id,
203
- revision=revision,
204
- subfolders=subfolders,
205
- )
206
- self.device = resolve_device(device)
207
- self.batch_size = batch_size
208
- self.max_length = max_length
209
- self.cache_dir = cache_dir
210
- self.local_files_only = local_files_only
211
-
212
- def _load_kwargs(self, spec: ModelSpec) -> dict:
213
- values = {
214
- "revision": spec.revision,
215
- "cache_dir": self.cache_dir,
216
- "local_files_only": self.local_files_only,
217
- "trust_remote_code": True,
218
- }
219
- if spec.subfolder is not None:
220
- values["subfolder"] = spec.subfolder
221
- return values
222
-
223
- @staticmethod
224
- def _validate_local_model_path(spec: ModelSpec) -> None:
225
- path = Path(spec.model_id).expanduser()
226
- config_path = path / "config.json"
227
- if not path.is_dir() or not config_path.is_file():
228
- return
229
- try:
230
- config_text = config_path.read_text()
231
- except OSError:
232
- return
233
- if "DOEJGI/GenomeOcean-100M-v1.2" in config_text:
234
- raise RuntimeError(
235
- f"Model checkpoint {path} still references remote DOEJGI custom code. "
236
- "Use a prepared Hugging Face inference directory instead of the raw "
237
- "training checkpoint."
238
- )
239
-
240
- def _predict_one_model(
241
- self,
242
- spec: ModelSpec,
243
- chunks: Sequence[SequenceChunk],
244
- ) -> torch.Tensor:
245
- self._validate_local_model_path(spec)
246
- common = self._load_kwargs(spec)
247
- tokenizer = AutoTokenizer.from_pretrained(
248
- spec.model_id,
249
- model_max_length=self.max_length,
250
- use_fast=True,
251
- padding_side="right",
252
- **common,
253
- )
254
- model = AutoModelForSequenceClassification.from_pretrained(
255
- spec.model_id,
256
- **common,
257
- )
258
- model.to(self.device)
259
- model.eval()
260
-
261
- batches: list[torch.Tensor] = []
262
- for start in range(0, len(chunks), self.batch_size):
263
- batch = chunks[start : start + self.batch_size]
264
- encoded = tokenizer(
265
- [chunk.sequence for chunk in batch],
266
- padding=True,
267
- truncation=True,
268
- max_length=self.max_length,
269
- return_token_type_ids=False,
270
- return_tensors="pt",
271
- )
272
- encoded.pop("token_type_ids", None)
273
- encoded = {name: value.to(self.device) for name, value in encoded.items()}
274
- with torch.inference_mode():
275
- logits = model(**encoded).logits
276
- probabilities = torch.softmax(logits, dim=-1).detach().cpu()
277
- if probabilities.shape[1] != len(SUB_LABELS):
278
- raise RuntimeError(
279
- f"Sub model '{spec.name}' returned {probabilities.shape[1]} labels; "
280
- f"expected {len(SUB_LABELS)}"
281
- )
282
- batches.append(probabilities)
283
- return torch.cat(batches, dim=0)
284
-
285
- def _release_device_cache(self) -> None:
286
- gc.collect()
287
- if self.device.type == "cuda":
288
- torch.cuda.empty_cache()
289
- elif self.device.type == "mps" and hasattr(torch.mps, "empty_cache"):
290
- torch.mps.empty_cache()
291
-
292
- def predict_chunks(self, chunks: Sequence[SequenceChunk]) -> pd.DataFrame:
293
- if not chunks:
294
- return pd.DataFrame(columns=SUB_CHUNK_RESULT_COLUMNS)
295
-
296
- accumulator = ProbabilityEnsembleAccumulator(
297
- n_items=len(chunks),
298
- n_labels=len(SUB_LABELS),
299
- )
300
- for spec in self.model_specs:
301
- try:
302
- probabilities = self._predict_one_model(spec, chunks)
303
- accumulator.add(probabilities)
304
- del probabilities
305
- finally:
306
- self._release_device_cache()
307
-
308
- mean, predicted, votes, agreement, confidence_std = accumulator.finalize()
309
- rows: list[dict] = []
310
- for index, chunk in enumerate(chunks):
311
- label = int(predicted[index].item())
312
- probability_row = mean[index]
313
- row = chunk.to_dict()
314
- row.pop("sequence")
315
- row["predicted_label"] = label
316
- row["predicted_name"] = SUB_LABELS[label]
317
- row["confidence"] = float(probability_row[label].item())
318
- row["ensemble_size"] = accumulator.n_models
319
- row["ensemble_votes"] = int(votes[index].item())
320
- row["ensemble_agreement"] = float(agreement[index].item())
321
- row["confidence_std"] = float(confidence_std[index].item())
322
- for label_index, column in PROBABILITY_COLUMNS.items():
323
- row[column] = float(probability_row[label_index].item())
324
- rows.append(row)
325
- return pd.DataFrame(rows, columns=SUB_CHUNK_RESULT_COLUMNS)
326
-
327
- def predict_records(
328
- self,
329
- records: Sequence[FastaRecord],
330
- *,
331
- chunk_size: int = DEFAULT_CHUNK_SIZE,
332
- stride: int = DEFAULT_STRIDE,
333
- ) -> SubPredictionBundle:
334
- chunks, skipped = preprocess_records(
335
- records,
336
- chunk_size=chunk_size,
337
- stride=stride,
338
- )
339
- chunk_results = self.predict_chunks(chunks)
340
- contig_results = aggregate_chunks_to_contigs(chunk_results)
341
- return SubPredictionBundle(
342
- records=list(records),
343
- chunks=chunks,
344
- skipped=skipped,
345
- chunk_results=chunk_results,
346
- contig_results=contig_results,
347
- file_results=aggregate_contigs_to_files(contig_results),
348
- )
349
-
350
- def predict_fasta(
351
- self,
352
- input_path: str | Path,
353
- output_dir: str | Path,
354
- *,
355
- chunk_size: int = DEFAULT_CHUNK_SIZE,
356
- stride: int = DEFAULT_STRIDE,
357
- ) -> SubPredictionBundle:
358
- records = read_input_records(input_path)
359
- bundle = self.predict_records(records, chunk_size=chunk_size, stride=stride)
360
- write_sub_outputs(
361
- bundle,
362
- output_dir,
363
- metadata={
364
- "input": str(Path(input_path).expanduser().resolve()),
365
- "ensemble_method": ENSEMBLE_METHOD,
366
- "ensemble_size": len(self.model_specs),
367
- "models": [spec.to_dict() for spec in self.model_specs],
368
- "sequential_model_loading": True,
369
- "device": str(self.device),
370
- "batch_size": self.batch_size,
371
- "max_length": self.max_length,
372
- "chunk_size": chunk_size,
373
- "stride": stride,
374
- },
375
- )
376
- return bundle
377
-
378
-
379
- def write_sub_outputs(
380
- bundle: SubPredictionBundle,
381
- output_dir: str | Path,
382
- *,
383
- metadata: dict | None = None,
384
- ) -> dict[str, Path]:
385
- out = Path(output_dir)
386
- out.mkdir(parents=True, exist_ok=True)
387
- paths = {
388
- "chunks": out / "chunk_predictions.tsv",
389
- "contigs": out / "contig_predictions.tsv",
390
- "files": out / "file_predictions.tsv",
391
- "skipped": out / "skipped_records.tsv",
392
- "metadata": out / "run_metadata.json",
393
- }
394
- bundle.chunk_results.reindex(columns=SUB_CHUNK_RESULT_COLUMNS).to_csv(
395
- paths["chunks"], sep="\t", index=False
396
- )
397
- bundle.contig_results.to_csv(paths["contigs"], sep="\t", index=False)
398
- bundle.file_results.to_csv(paths["files"], sep="\t", index=False)
399
- pd.DataFrame(
400
- [item.to_dict() for item in bundle.skipped],
401
- columns=SKIPPED_RESULT_COLUMNS,
402
- ).to_csv(
403
- paths["skipped"],
404
- sep="\t",
405
- index=False,
406
- )
407
- run_metadata = {
408
- **(metadata or {}),
409
- "records": len(bundle.records),
410
- "chunks": len(bundle.chunks),
411
- "predicted_contigs": len(bundle.contig_results),
412
- "skipped_records": len(bundle.skipped),
413
- "outputs": {name: str(path) for name, path in paths.items() if name != "metadata"},
414
- }
415
- paths["metadata"].write_text(json.dumps(run_metadata, indent=2) + "\n")
416
- return paths
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/genomeocean_sub/preprocessing.py DELETED
@@ -1,119 +0,0 @@
1
- """Training-compatible DNA cleaning and fixed-size chunking for Sub."""
2
-
3
- from __future__ import annotations
4
-
5
- from dataclasses import asdict, dataclass
6
- import re
7
- from typing import Iterable
8
-
9
- from .fasta import FastaRecord
10
-
11
-
12
- DEFAULT_CHUNK_SIZE = 5000
13
- DEFAULT_STRIDE = 5000
14
-
15
-
16
- @dataclass(frozen=True)
17
- class SequenceChunk:
18
- record_id: str
19
- source_file: str
20
- contig_id: str
21
- original_length: int
22
- clean_length: int
23
- n_chunks: int
24
- ignored_tail_bp: int
25
- chunk_index: int
26
- chunk_start: int
27
- chunk_end: int
28
- sequence: str
29
-
30
- def to_dict(self) -> dict:
31
- return asdict(self)
32
-
33
-
34
- @dataclass(frozen=True)
35
- class SkippedRecord:
36
- record_id: str
37
- source_file: str
38
- contig_id: str
39
- original_length: int
40
- clean_length: int
41
- reason: str
42
-
43
- def to_dict(self) -> dict:
44
- return asdict(self)
45
-
46
-
47
- def clean_dna(sequence: str) -> str:
48
- cleaned = re.sub(r"[^ACGTNacgtn]", "", sequence).upper()
49
- return cleaned.replace("N", "")
50
-
51
-
52
- def make_chunk_spans(
53
- sequence_length: int,
54
- *,
55
- chunk_size: int = DEFAULT_CHUNK_SIZE,
56
- stride: int = DEFAULT_STRIDE,
57
- ) -> list[tuple[int, int]]:
58
- if chunk_size <= 0:
59
- raise ValueError("chunk_size must be greater than zero")
60
- if stride <= 0:
61
- raise ValueError("stride must be greater than zero")
62
- if sequence_length < chunk_size:
63
- return []
64
- return [
65
- (start, start + chunk_size)
66
- for start in range(0, sequence_length - chunk_size + 1, stride)
67
- ]
68
-
69
-
70
- def preprocess_records(
71
- records: Iterable[FastaRecord],
72
- *,
73
- chunk_size: int = DEFAULT_CHUNK_SIZE,
74
- stride: int = DEFAULT_STRIDE,
75
- ) -> tuple[list[SequenceChunk], list[SkippedRecord]]:
76
- chunks: list[SequenceChunk] = []
77
- skipped: list[SkippedRecord] = []
78
- for record in records:
79
- cleaned = clean_dna(record.sequence)
80
- spans = make_chunk_spans(
81
- len(cleaned),
82
- chunk_size=chunk_size,
83
- stride=stride,
84
- )
85
- if not spans:
86
- skipped.append(
87
- SkippedRecord(
88
- record_id=record.record_id,
89
- source_file=record.source_file,
90
- contig_id=record.contig_id,
91
- original_length=len(record.sequence),
92
- clean_length=len(cleaned),
93
- reason=(
94
- "empty_after_cleaning"
95
- if not cleaned
96
- else "shorter_than_chunk_size"
97
- ),
98
- )
99
- )
100
- continue
101
-
102
- ignored_tail_bp = max(0, len(cleaned) - max(end for _, end in spans))
103
- for chunk_index, (start, end) in enumerate(spans):
104
- chunks.append(
105
- SequenceChunk(
106
- record_id=record.record_id,
107
- source_file=record.source_file,
108
- contig_id=record.contig_id,
109
- original_length=len(record.sequence),
110
- clean_length=len(cleaned),
111
- n_chunks=len(spans),
112
- ignored_tail_bp=ignored_tail_bp,
113
- chunk_index=chunk_index,
114
- chunk_start=start,
115
- chunk_end=end,
116
- sequence=cleaned[start:end],
117
- )
118
- )
119
- return chunks, skipped
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/genomeocean_sub_classifier.egg-info/PKG-INFO DELETED
@@ -1,11 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: genomeocean-sub-classifier
3
- Version: 0.1.0
4
- Summary: GenomeOcean main classifier for genomic FASTA files
5
- Requires-Python: >=3.11
6
- Requires-Dist: torch<2.9,>=2.8
7
- Requires-Dist: transformers==4.51.3
8
- Requires-Dist: huggingface-hub<1,>=0.36
9
- Requires-Dist: safetensors<1,>=0.5
10
- Requires-Dist: numpy<3,>=2.2
11
- Requires-Dist: pandas<3,>=2.2
 
 
 
 
 
 
 
 
 
 
 
 
src/genomeocean_sub_classifier.egg-info/SOURCES.txt DELETED
@@ -1,16 +0,0 @@
1
- README.md
2
- pyproject.toml
3
- src/genomeocean_sub/__init__.py
4
- src/genomeocean_sub/aggregation.py
5
- src/genomeocean_sub/cli.py
6
- src/genomeocean_sub/fasta.py
7
- src/genomeocean_sub/predict.py
8
- src/genomeocean_sub/preprocessing.py
9
- src/genomeocean_sub_classifier.egg-info/PKG-INFO
10
- src/genomeocean_sub_classifier.egg-info/SOURCES.txt
11
- src/genomeocean_sub_classifier.egg-info/dependency_links.txt
12
- src/genomeocean_sub_classifier.egg-info/entry_points.txt
13
- src/genomeocean_sub_classifier.egg-info/requires.txt
14
- src/genomeocean_sub_classifier.egg-info/top_level.txt
15
- tests/test_preprocessing.py
16
- tests/test_smoke.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/genomeocean_sub_classifier.egg-info/dependency_links.txt DELETED
@@ -1 +0,0 @@
1
-
 
 
src/genomeocean_sub_classifier.egg-info/entry_points.txt DELETED
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- genomeocean-sub = genomeocean_sub.cli:main
 
 
 
src/genomeocean_sub_classifier.egg-info/requires.txt DELETED
@@ -1,6 +0,0 @@
1
- torch<2.9,>=2.8
2
- transformers==4.51.3
3
- huggingface-hub<1,>=0.36
4
- safetensors<1,>=0.5
5
- numpy<3,>=2.2
6
- pandas<3,>=2.2
 
 
 
 
 
 
 
src/genomeocean_sub_classifier.egg-info/top_level.txt DELETED
@@ -1 +0,0 @@
1
- genomeocean_sub
 
 
tests/test_preprocessing.py DELETED
@@ -1,28 +0,0 @@
1
- import unittest
2
-
3
- from genomeocean_sub.fasta import FastaRecord
4
- from genomeocean_sub.preprocessing import clean_dna, make_chunk_spans, preprocess_records
5
-
6
-
7
- class PreprocessingTests(unittest.TestCase):
8
- def test_clean_dna_matches_training_behavior(self):
9
- self.assertEqual(clean_dna("acgtn-RYSW"), "ACGT")
10
-
11
- def test_exact_5kb_produces_one_chunk(self):
12
- self.assertEqual(make_chunk_spans(5000), [(0, 5000)])
13
-
14
- def test_short_record_is_reported(self):
15
- record = FastaRecord("candidate.fna", "short", "short", "A" * 4999)
16
- chunks, skipped = preprocess_records([record])
17
- self.assertEqual(chunks, [])
18
- self.assertEqual(skipped[0].reason, "shorter_than_chunk_size")
19
-
20
- def test_main_and_sub_chunk_contract_is_5kb_non_overlapping(self):
21
- self.assertEqual(
22
- make_chunk_spans(10000),
23
- [(0, 5000), (5000, 10000)],
24
- )
25
-
26
-
27
- if __name__ == "__main__":
28
- unittest.main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/test_smoke.py DELETED
@@ -1,84 +0,0 @@
1
- import unittest
2
- from pathlib import Path
3
-
4
- import pandas as pd
5
- import torch
6
-
7
- from genomeocean_sub.aggregation import (
8
- aggregate_chunks_to_contigs,
9
- aggregate_contigs_to_files,
10
- )
11
- from genomeocean_sub.fasta import FastaRecord
12
- from genomeocean_sub.predict import (
13
- ModelSpec,
14
- ProbabilityEnsembleAccumulator,
15
- SubPredictor,
16
- build_model_specs,
17
- )
18
- from genomeocean_sub.preprocessing import preprocess_records
19
-
20
-
21
- class SmokeTests(unittest.TestCase):
22
- def test_fold_specs_and_probability_mean(self):
23
- specs = build_model_specs(
24
- ["/models/sub-fold1", "/models/sub-fold2"],
25
- )
26
- self.assertEqual([spec.model_id for spec in specs], ["/models/sub-fold1", "/models/sub-fold2"])
27
- self.assertEqual([spec.name for spec in specs], ["model1", "model2"])
28
-
29
- accumulator = ProbabilityEnsembleAccumulator(n_items=1, n_labels=2)
30
- accumulator.add(torch.tensor([[0.8, 0.2]]))
31
- accumulator.add(torch.tensor([[0.4, 0.6]]))
32
- mean, predicted, votes, agreement, _ = accumulator.finalize()
33
- self.assertTrue(
34
- torch.allclose(mean, torch.tensor([[0.6, 0.4]], dtype=torch.float64))
35
- )
36
- self.assertEqual(predicted.tolist(), [0])
37
- self.assertEqual(votes.tolist(), [1])
38
- self.assertEqual(agreement.tolist(), [0.5])
39
-
40
- def test_raw_training_checkpoint_gets_export_hint(self):
41
- checkpoint = (
42
- Path(__file__).resolve().parents[4]
43
- / "finetuning_go_sub_add"
44
- / "ft_models"
45
- / "train_100M_v1.2_5kb"
46
- / "fold1"
47
- / "checkpoint-44240"
48
- )
49
- if checkpoint.is_dir():
50
- with self.assertRaisesRegex(RuntimeError, "prepared Hugging Face inference"):
51
- SubPredictor._validate_local_model_path(ModelSpec(str(checkpoint)))
52
-
53
- def test_fake_mirus_predictions_aggregate_to_mirus(self):
54
- record = FastaRecord(
55
- "candidates.fna",
56
- "candidate_1",
57
- "candidate_1",
58
- "A" * 10000,
59
- )
60
- chunks, skipped = preprocess_records([record])
61
- self.assertEqual(skipped, [])
62
-
63
- rows = []
64
- for chunk in chunks:
65
- row = chunk.to_dict()
66
- row.pop("sequence")
67
- row.update(
68
- {
69
- "predicted_label": 1,
70
- "predicted_name": "Mirus",
71
- "confidence": 0.9,
72
- "prob_ncldv": 0.1,
73
- "prob_mirus": 0.9,
74
- }
75
- )
76
- rows.append(row)
77
- contigs = aggregate_chunks_to_contigs(pd.DataFrame(rows))
78
- files = aggregate_contigs_to_files(contigs)
79
- self.assertEqual(contigs.loc[0, "predicted_name"], "Mirus")
80
- self.assertEqual(files.loc[0, "predicted_name"], "Mirus")
81
-
82
-
83
- if __name__ == "__main__":
84
- unittest.main()