dashakoryakovskaya commited on
Commit
939957c
·
verified ·
1 Parent(s): b48ab23

Upload 4 files

Browse files
text/architectures.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding: utf-8
2
+ import math
3
+ import torch
4
+ import torch.nn as nn
5
+ import torch.nn.functional as F
6
+ import numpy as np
7
+ from .help_layers import TransformerEncoderLayer, CustomMambaBlock
8
+
9
+ device = "cuda" if torch.cuda.is_available() else "cpu"
10
+
11
+
12
+ class EmotionMamba(nn.Module):
13
+ def __init__(self, input_dim_emotion=1024, input_dim_personality=1024, hidden_dim=128, out_features=512, mamba_layer_number=2, positional_encoding=True, num_transformer_heads=4, transformer_dropout=0.1, tr_layer_number=1, dropout=0.1, num_emotions=7, num_traits=5):
14
+ super().__init__()
15
+
16
+ self.hidden_dim = hidden_dim
17
+
18
+ self.emo_proj = nn.Sequential(
19
+ nn.Linear(input_dim_emotion, hidden_dim),
20
+ nn.LayerNorm(hidden_dim),
21
+ nn.Dropout(dropout)
22
+ )
23
+
24
+ self.emotion_encoder = nn.ModuleList([
25
+ CustomMambaBlock(hidden_dim, hidden_dim, dropout=dropout)
26
+ for _ in range(mamba_layer_number)
27
+ ])
28
+
29
+
30
+ self.emotion_fc_out = nn.Sequential(
31
+ nn.Linear(hidden_dim, out_features),
32
+ nn.LayerNorm(out_features),
33
+ nn.GELU(),
34
+ nn.Dropout(dropout),
35
+ nn.Linear(out_features, num_emotions)
36
+ )
37
+
38
+
39
+ def forward(self, emotion_input=None, personality_input=None, return_features=False):
40
+ emo = self.emo_proj(emotion_input) # (B, T, hidden_dim)
41
+ for layer in self.emotion_encoder:
42
+ emo = layer(emo)
43
+ out_emo = self.emotion_fc_out(emo.mean(dim=1)) # (B, num_emotions)
44
+ if return_features:
45
+ return {
46
+ 'emotion_logits': out_emo,
47
+ 'last_encoder_features': emo,
48
+ }
49
+ else:
50
+ return {'emotion_logits': out_emo}
51
+
52
+
53
+ class PersonalityMamba(nn.Module):
54
+ def __init__(self, input_dim_emotion=1024, input_dim_personality=1024, hidden_dim=128, out_features=512, mamba_layer_number=2, per_activation="sigmoid", positional_encoding=True, num_transformer_heads=4, tr_layer_number=1, dropout=0.1, num_emotions=7, num_traits=5):
55
+ super().__init__()
56
+
57
+ self.hidden_dim = hidden_dim
58
+
59
+ self.per_proj = nn.Sequential(
60
+ nn.Linear(input_dim_personality, hidden_dim),
61
+ nn.LayerNorm(hidden_dim),
62
+ nn.Dropout(dropout)
63
+ )
64
+
65
+ self.personality_encoder = nn.ModuleList([
66
+ CustomMambaBlock(hidden_dim, hidden_dim, dropout=dropout)
67
+ for _ in range(mamba_layer_number)
68
+ ])
69
+
70
+ self.personality_fc_out = nn.Sequential(
71
+ nn.Linear(hidden_dim, out_features),
72
+ nn.LayerNorm(out_features),
73
+ nn.GELU(),
74
+ nn.Dropout(dropout),
75
+ nn.Linear(out_features, num_traits)
76
+ )
77
+
78
+ if per_activation == "sigmoid":
79
+ self.activation = nn.Sigmoid()
80
+ elif per_activation == "relu":
81
+ self.activation = nn.ReLU()
82
+
83
+ def forward(self, emotion_input=None, personality_input=None, return_features=False):
84
+ per = self.per_proj(personality_input)
85
+
86
+ for layer in self.personality_encoder:
87
+ per = layer(per)
88
+
89
+ out_per = self.personality_fc_out(per.mean(dim=1))
90
+
91
+ if return_features:
92
+ return {
93
+ 'personality_scores': self.activation(out_per),
94
+ 'last_encoder_features': per,
95
+ }
96
+ else:
97
+ return {'personality_scores': self.activation(out_per)}
98
+
99
+
100
+ class FusionTransformer(nn.Module):
101
+ def __init__(self, emo_model, per_model, hidden_dim=128, out_features=512, per_activation="sigmoid", positional_encoding=True, num_transformer_heads=4, tr_layer_number=1, dropout=0.1, num_emotions=7, num_traits=5):
102
+ super().__init__()
103
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
104
+
105
+ self.hidden_dim = hidden_dim
106
+
107
+ self.emo_model = emo_model
108
+ self.per_model = per_model
109
+
110
+ for param in self.emo_model.parameters():
111
+ param.requires_grad = False
112
+
113
+ for param in self.per_model.parameters():
114
+ param.requires_grad = False
115
+
116
+ self.emo_proj = nn.Sequential(
117
+ nn.Linear(self.emo_model.hidden_dim, hidden_dim),
118
+ nn.LayerNorm(hidden_dim),
119
+ nn.Dropout(dropout)
120
+ )
121
+
122
+ self.per_proj = nn.Sequential(
123
+ nn.Linear(self.per_model.hidden_dim, hidden_dim),
124
+ nn.LayerNorm(hidden_dim),
125
+ nn.Dropout(dropout)
126
+ )
127
+
128
+ self.emotion_to_personality_attn = nn.ModuleList([
129
+ TransformerEncoderLayer(
130
+ input_dim=hidden_dim,
131
+ num_heads=num_transformer_heads,
132
+ dropout=dropout,
133
+ positional_encoding=positional_encoding
134
+ ) for _ in range(tr_layer_number)
135
+ ])
136
+
137
+ self.personality_to_emotion_attn = nn.ModuleList([
138
+ TransformerEncoderLayer(
139
+ input_dim=hidden_dim,
140
+ num_heads=num_transformer_heads,
141
+ dropout=dropout,
142
+ positional_encoding=positional_encoding
143
+ ) for _ in range(tr_layer_number)
144
+ ])
145
+
146
+ self.emotion_personality_fc_out = nn.Sequential(
147
+ nn.Linear(hidden_dim*2, out_features),
148
+ nn.LayerNorm(out_features),
149
+ nn.SiLU(),
150
+ nn.Dropout(dropout),
151
+ nn.Linear(out_features, num_emotions)
152
+ )
153
+
154
+ self.personality_emotion_fc_out = nn.Sequential(
155
+ nn.Linear(hidden_dim*2, out_features),
156
+ nn.LayerNorm(out_features),
157
+ nn.SiLU(),
158
+ nn.Dropout(dropout),
159
+ nn.Linear(out_features, num_traits)
160
+ )
161
+
162
+ if per_activation == "sigmoid":
163
+ self.activation = nn.Sigmoid()
164
+ elif per_activation == "relu":
165
+ self.activation = nn.ReLU()
166
+
167
+ def forward(self, emotion_input=None, personality_input=None, return_features=False):
168
+ emo_features = self.emo_model(emotion_input=emotion_input, return_features=True)
169
+ per_features = self.per_model(personality_input=personality_input, return_features=True)
170
+
171
+ emo_emd = self.emo_proj(emo_features['last_encoder_features'])
172
+ per_emd = self.per_proj(per_features['last_encoder_features'])
173
+
174
+ # padding
175
+ max_len = max(emo_emd.shape[1], per_emd.shape[1])
176
+ emo_emd = emo_emd.cpu().detach().numpy()
177
+ per_emd = per_emd.cpu().detach().numpy()
178
+ emo_emd = np.pad(emo_emd[:, :max_len, :], ((0, 0), (0, max(0, max_len - emo_emd.shape[1])), (0, 0)), "constant")
179
+ per_emd = np.pad(per_emd[:, :max_len, :], ((0, 0), (0, max(0, max_len - per_emd.shape[1])), (0, 0)), "constant")
180
+ emo_emd = torch.tensor(emo_emd, device=self.device)
181
+ per_emd = torch.tensor(per_emd, device=self.device)
182
+
183
+ for layer in self.emotion_to_personality_attn:
184
+ emo_emd += layer(emo_emd, per_emd, per_emd)
185
+
186
+ for layer in self.personality_to_emotion_attn:
187
+ per_emd += layer(per_emd, emo_emd, emo_emd)
188
+
189
+ fused = torch.cat([emo_emd, per_emd], dim=-1)
190
+ emotion_logits = self.emotion_personality_fc_out(fused.mean(dim=1))
191
+ personality_scores = self.personality_emotion_fc_out(fused.mean(dim=1))
192
+
193
+ if return_features:
194
+ return {
195
+ 'emotion_logits': (emotion_logits+emo_features['emotion_logits'])/2,
196
+ 'personality_scores': (self.activation(personality_scores)+per_features['personality_scores'])/2,
197
+ 'last_emo_encoder_features': emo_emd,
198
+ 'last_per_encoder_features': per_emd,
199
+ }
200
+ else:
201
+ return {'emotion_logits': (emotion_logits+emo_features['emotion_logits'])/2,
202
+ 'personality_scores': (self.activation(personality_scores)+per_features['personality_scores'])/2,}
text/feature_extractor.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding: utf-8
2
+ import torch
3
+ from transformers import AutoTokenizer, AutoModel
4
+ from .model_loader import load_fusion_model
5
+
6
+
7
+ class PretrainedTextEmbeddingExtractor:
8
+ """
9
+ jinaai/jina-embeddings-v → последовательный эмбеддинг (B, T, 1024) →
10
+ Fusion-модель → логиты эмоций, оценки Big-5 и последние признаки.
11
+ """
12
+
13
+ def __init__(
14
+ self,
15
+ device: str = "cuda",
16
+ model_name: str = "jinaai/jina-embeddings-v3",
17
+ fusion_ckpt: str = "modalities/text/checkpoints_models/Transformer_jina_fusion.pt",
18
+ emo_ckpt: str = "modalities/text/checkpoints_models/Mamba_jina_emotion.pt",
19
+ per_ckpt: str = "modalities/text/checkpoints_models/Mamba_jina_personality.pt",
20
+ ):
21
+ self.device = torch.device(device)
22
+
23
+ self.tok = AutoTokenizer.from_pretrained(model_name, code_revision='da863dd04a4e5dce6814c6625adfba87b83838aa', trust_remote_code=True)
24
+ self.enc = AutoModel.from_pretrained(model_name, code_revision='da863dd04a4e5dce6814c6625adfba87b83838aa', trust_remote_code=True).to(self.device).eval()
25
+
26
+ self.fusion, _ = load_fusion_model(
27
+ fusion_ckpt, emo_ckpt, per_ckpt, device=self.device
28
+ )
29
+
30
+ @torch.no_grad()
31
+ def extract(self, texts: list[str] | str) -> dict:
32
+ if isinstance(texts, str):
33
+ texts = [texts]
34
+
35
+ batch = self.tok(texts, padding=True, truncation=True,
36
+ return_tensors="pt").to(self.device)
37
+
38
+ hidden = self.enc(**batch).last_hidden_state # (B, T, 1024)
39
+
40
+ out = self.fusion(
41
+ emotion_input=hidden.float(),
42
+ personality_input=hidden.float(),
43
+ return_features=True,
44
+ )
45
+
46
+ return {
47
+ "emotion_logits": out["emotion_logits"].cpu(),
48
+ "personality_scores": out["personality_scores"].cpu(),
49
+ "last_emo_encoder_features": out["last_emo_encoder_features"].cpu(),
50
+ "last_per_encoder_features": out["last_per_encoder_features"].cpu(),
51
+ }
text/help_layers.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding: utf-8
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+ import torch.nn.init as init
6
+ import numpy as np
7
+ import math
8
+
9
+
10
+ class PositionWiseFeedForward(nn.Module):
11
+ def __init__(self, input_dim, hidden_dim, dropout=0.1):
12
+ super().__init__()
13
+ self.layer_1 = nn.Linear(input_dim, hidden_dim)
14
+ self.layer_2 = nn.Linear(hidden_dim, input_dim)
15
+ self.dropout = nn.Dropout(dropout)
16
+
17
+ def forward(self, x):
18
+ x = self.layer_1(x)
19
+ x = F.gelu(x)
20
+ x = self.dropout(x)
21
+ return self.layer_2(x)
22
+
23
+
24
+ class AddAndNorm(nn.Module):
25
+ def __init__(self, input_dim, dropout=0.1):
26
+ super().__init__()
27
+ self.norm = nn.LayerNorm(input_dim)
28
+ self.dropout = nn.Dropout(dropout)
29
+
30
+ def forward(self, x, residual):
31
+ return self.norm(x + self.dropout(residual))
32
+
33
+
34
+ class PositionalEncoding(nn.Module):
35
+ def __init__(self, d_model, dropout=0.1, max_len=5000):
36
+ super().__init__()
37
+ self.dropout = nn.Dropout(p=dropout)
38
+
39
+ position = torch.arange(max_len).unsqueeze(1)
40
+ div_term = torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model))
41
+ pe = torch.zeros(max_len, d_model)
42
+ pe[:, 0::2] = torch.sin(position * div_term)
43
+ pe[:, 1::2] = torch.cos(position * div_term)
44
+
45
+ self.register_buffer("pe", pe)
46
+
47
+ def forward(self, x):
48
+ x = x + self.pe[: x.size(1)].detach() # Отключаем градиенты
49
+ return self.dropout(x)
50
+
51
+
52
+ class TransformerEncoderLayer(nn.Module):
53
+ def __init__(self, input_dim, num_heads, dropout=0.1, positional_encoding=False):
54
+ super().__init__()
55
+ self.input_dim = input_dim
56
+ self.self_attention = nn.MultiheadAttention(input_dim, num_heads, dropout=dropout, batch_first=True)
57
+ self.feed_forward = PositionWiseFeedForward(input_dim, input_dim, dropout=dropout)
58
+ self.add_norm_after_attention = AddAndNorm(input_dim, dropout=dropout)
59
+ self.add_norm_after_ff = AddAndNorm(input_dim, dropout=dropout)
60
+ self.positional_encoding = PositionalEncoding(input_dim) if positional_encoding else None
61
+
62
+ def forward(self, query, key, value):
63
+ if self.positional_encoding:
64
+ key = self.positional_encoding(key)
65
+ value = self.positional_encoding(value)
66
+ query = self.positional_encoding(query)
67
+
68
+ attn_output, _ = self.self_attention(query, key, value, need_weights=False)
69
+
70
+ x = self.add_norm_after_attention(attn_output, query)
71
+
72
+ ff_output = self.feed_forward(x)
73
+ x = self.add_norm_after_ff(ff_output, x)
74
+
75
+ return x
76
+
77
+ class CustomMambaBlock(nn.Module):
78
+ def __init__(self, d_input, d_model, dropout=0.1):
79
+ super().__init__()
80
+ self.in_proj = nn.Linear(d_input, d_model)
81
+ self.s_B = nn.Linear(d_model, d_model)
82
+ self.s_C = nn.Linear(d_model, d_model)
83
+ self.out_proj = nn.Linear(d_model, d_input)
84
+ self.norm = nn.LayerNorm(d_input)
85
+ self.dropout = nn.Dropout(dropout)
86
+ self.activation = nn.ReLU()
87
+
88
+ def forward(self, x):
89
+ x_in = x
90
+ x = self.in_proj(x)
91
+ B = self.s_B(x)
92
+ C = self.s_C(x)
93
+ x = x + B + C
94
+ x = self.activation(x)
95
+ x = self.out_proj(x)
96
+ x = self.dropout(x)
97
+ x = self.norm(x + x_in)
98
+ return x
text/model_loader.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding: utf-8
2
+ import torch
3
+ from .architectures import (
4
+ EmotionMamba,
5
+ PersonalityMamba,
6
+ FusionTransformer,
7
+ )
8
+
9
+
10
+ def load_pretrained_emotion_encoder(checkpoint_path, device):
11
+ emotion_model = EmotionMamba(
12
+ input_dim_emotion=1024,
13
+ input_dim_personality=1024,
14
+ hidden_dim=256,
15
+ out_features=128,
16
+ mamba_layer_number=2,
17
+ dropout=0.1
18
+ ).to(device)
19
+
20
+ checkpoint = torch.load(checkpoint_path, map_location=device)
21
+ state_dict = checkpoint["model_state_dict"] if "model_state_dict" in checkpoint else checkpoint
22
+ emotion_model.load_state_dict(state_dict)
23
+
24
+ def extract_features(inputs, lengths):
25
+ features = emotion_model.emo_proj(inputs)
26
+ for block in emotion_model.emotion_encoder:
27
+ features = block(features)
28
+ return features
29
+
30
+ emotion_model.extract_features = extract_features
31
+ emotion_model.eval()
32
+ return emotion_model
33
+
34
+ def load_pretrained_personality_encoder(checkpoint_path, device):
35
+ personality_model = PersonalityMamba(
36
+ input_dim_emotion=1024,
37
+ input_dim_personality=1024,
38
+ hidden_dim=64,
39
+ out_features=256,
40
+ mamba_layer_number=3,
41
+ dropout=0.1).to(device)
42
+
43
+ checkpoint = torch.load(checkpoint_path, map_location=device)
44
+ personality_model.load_state_dict(checkpoint)
45
+
46
+ def extract_features(inputs, lengths):
47
+ features = personality_model.per_proj(inputs)
48
+ for block in personality_model.personality_encoder:
49
+ features = block(features, features, features)
50
+ return features
51
+
52
+ personality_model.extract_features = extract_features
53
+ personality_model.eval()
54
+ return personality_model
55
+
56
+ def load_fusion_model(
57
+ fusion_checkpoint_path: str,
58
+ emotion_encoder_checkpoint: str,
59
+ personality_encoder_checkpoint: str,
60
+ device: str = "cpu",
61
+ ):
62
+ device = torch.device(device)
63
+
64
+ emotion_encoder = load_pretrained_emotion_encoder(emotion_encoder_checkpoint, device)
65
+ personality_encoder = load_pretrained_personality_encoder(personality_encoder_checkpoint, device)
66
+
67
+ checkpoint = torch.load(fusion_checkpoint_path, map_location=device)
68
+
69
+ fusion_model = FusionTransformer(
70
+ emo_model=emotion_encoder,
71
+ per_model=personality_encoder,
72
+ hidden_dim=128,
73
+ out_features=64,
74
+ tr_layer_number=3,
75
+ num_transformer_heads=16,
76
+ dropout=0.1
77
+ ).to(device)
78
+ fusion_model.load_state_dict(checkpoint)
79
+ fusion_model.eval()
80
+ return fusion_model, device