dwmk commited on
Commit
1b8b8df
·
verified ·
1 Parent(s): 5d76435

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +280 -100
app.py CHANGED
@@ -5,169 +5,349 @@ import torch
5
  import torch.nn as nn
6
  import torch.nn.functional as F
7
  from sklearn.feature_extraction.text import TfidfVectorizer
8
- from sklearn.ensemble import RandomForestClassifier
 
 
 
9
  from sklearn.preprocessing import LabelEncoder
10
  import kagglehub
11
- import time
12
- import random
13
 
14
- # --- 1. ARCHITECTURE (Core logic from social_messages_benchmarks_ft_distil.py) ---
 
 
 
15
 
16
  class EpisodicMemory:
17
- """Mimics Hippocampal retention and retrieval"""
18
  def __init__(self, capacity=2000):
19
- self.memory_x, self.memory_y = [], []
 
20
  self.capacity = capacity
21
-
22
  def store(self, x, y):
23
- curr_x, curr_y = x.detach().cpu(), y.detach().cpu()
 
 
24
  for i in range(curr_x.size(0)):
25
  if len(self.memory_x) >= self.capacity:
26
- self.memory_x.pop(0); self.memory_y.pop(0)
27
- self.memory_x.append(curr_x[i]); self.memory_y.append(curr_y[i])
28
-
 
 
29
  def retrieve(self, query_x, k=5):
30
- if not self.memory_x: return None
 
31
  mem_tensor = torch.stack(self.memory_x).to(query_x.device)
32
  distances = torch.cdist(query_x, mem_tensor)
33
  top_k_indices = torch.topk(distances, k, largest=False).indices
34
- return torch.stack([torch.stack([self.memory_y[idx] for idx in s_idx]) for s_idx in top_k_indices]).to(query_x.device)
 
 
 
 
35
 
36
  class H3MOS(nn.Module):
37
- """The H3MOS architecture using Executive Core and Hippocampus"""
38
  def __init__(self, input_dim, hidden_dim, output_dim):
39
  super().__init__()
 
40
  self.executive = nn.Sequential(
41
- nn.Linear(input_dim, hidden_dim),
42
- nn.LayerNorm(hidden_dim),
 
 
 
43
  nn.GELU()
44
  )
 
45
  self.motor = nn.Linear(hidden_dim, output_dim)
46
- self.hippocampus = EpisodicMemory()
47
-
 
48
  def forward(self, x, training_mode=False):
49
  z = self.executive(x)
50
  raw_logits = self.motor(z)
51
- if training_mode: return raw_logits
 
 
 
 
 
52
  past_labels = self.hippocampus.retrieve(x, k=5)
53
- if past_labels is None: return raw_logits
 
 
54
  mem_votes = torch.zeros_like(raw_logits)
55
  for i in range(x.size(0)):
56
  votes = torch.bincount(past_labels[i], minlength=raw_logits.size(1)).float()
57
  mem_votes[i] = votes
58
- return (0.8 * raw_logits) + (0.2 * F.softmax(mem_votes, dim=1) * 5.0)
59
 
60
- # --- 2. DATA LOAD & TRAINING ---
 
 
 
 
 
61
 
62
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
63
- print(f"Initializing models on {device}...")
64
 
65
- path = kagglehub.dataset_download('dewanmukto/social-messages-and-emoji-reactions')
66
- df = pd.read_csv(path+"/messages_emojis.csv").dropna(subset=['content'])
 
 
 
 
 
 
67
 
68
- # Sentiment/Intent Mappings from benchmark
69
  sent_map = {'❤️':'Pos', '👍':'Pos', '😂':'Pos', '💯':'Pos', '😢':'Neg', '😭':'Neg', '😮':'Neu'}
70
  intent_map = {'❤️':'Emotion', '👍':'Agreement', '😂':'Emotion', '😮':'Surprise'}
71
 
72
- tfidf = TfidfVectorizer(max_features=500, stop_words='english')
 
73
  X_sparse = tfidf.fit_transform(df['content'])
74
  X_dense = torch.FloatTensor(X_sparse.toarray()).to(device)
75
 
76
- model_zoo = {}
 
 
 
77
  encoders = {}
78
 
79
- for task in ['emoji', 'sentiment', 'intent']:
80
- y_labels = df['emoji'].values if task == 'emoji' else df['emoji'].apply(lambda x: sent_map.get(x, 'Neutral') if task == 'sentiment' else intent_map.get(x, 'Other')).values
 
 
 
 
 
 
 
 
 
81
  le = LabelEncoder()
82
- y_enc = torch.LongTensor(le.fit_transform(y_labels)).to(device)
83
  encoders[task] = le
84
 
85
- h3 = H3MOS(X_dense.shape[1], 64, len(le.classes_)).to(device)
86
- opt = torch.optim.Adam(h3.parameters(), lr=0.01)
87
- for _ in range(20):
88
- opt.zero_grad(); F.cross_entropy(h3(X_dense, True), y_enc).backward(); opt.step()
 
 
89
 
90
- rf = RandomForestClassifier(n_estimators=20).fit(X_sparse, y_labels)
91
- model_zoo[task] = {"DISTIL-H3MOS": h3, "RandomForest": rf}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
- # --- 3. UI LOGIC ---
 
 
 
 
 
94
 
95
- CSS = """
96
- .reaction-btn {
97
- background: #f0f2f5; border: 1px solid #ddd; border-radius: 15px;
98
- padding: 2px 8px; font-size: 14px; cursor: pointer; margin-top: 5px;
99
- }
100
- .bot-header { display: flex; align-items: center; margin-bottom: 5px; }
101
- .bot-avatar { width: 28px; height: 28px; border-radius: 50%; margin-right: 8px; border: 1px solid #eee; }
102
- .bot-name { font-weight: bold; font-size: 0.9em; color: #555; }
103
- """
104
 
105
- def get_avatar_url(name):
106
- return f"https://api.dicebear.com/7.x/adventurer/svg?seed={name}"
107
 
108
- def predict(text):
 
109
  vec_s = tfidf.transform([text])
110
  vec_t = torch.FloatTensor(vec_s.toarray()).to(device)
111
- res = {}
112
- for task in ['emoji', 'sentiment', 'intent']:
113
- with torch.no_grad():
114
- h3_idx = torch.argmax(model_zoo[task]["DISTIL-H3MOS"](vec_t)).item()
115
- h3_p = encoders[task].inverse_transform([h3_idx])[0]
116
- rf_p = model_zoo[task]["RandomForest"].predict(vec_s)[0]
117
- res[task] = {"DISTIL-H3MOS": h3_p, "RandomForest": rf_p}
118
- return res
119
-
120
- def chat_interface(message, history):
121
- if not message:
122
- yield "", history
123
- return
124
 
125
- preds = predict(message)
126
 
127
- # Emoji Reaction Logic
128
- h3_emoji = preds['emoji']['DISTIL-H3MOS']
129
- rf_emoji = preds['emoji']['RandomForest']
130
- details = f"DISTIL-H3MOS: {h3_emoji} | RandomForest: {rf_emoji}"
131
- reaction_html = f"<button class='reaction-btn' title='{details}'>{h3_emoji} 🤖</button>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
- # 1. Add User Message and CLEAR textbox by yielding (" ", history)
134
- history.append({"role": "user", "content": f"{message}<br>{reaction_html}"})
135
- yield "", history
 
 
 
 
136
 
137
- # 2. Simulate Group Members
138
- bots = ["DISTIL-H3MOS", "RandomForest"]
139
- random.shuffle(bots)
140
 
141
- for bot in bots:
142
- time.sleep(random.uniform(0.6, 1.2))
143
- sent = preds['sentiment'][bot]
144
- intent = preds['intent'][bot]
145
- avatar = get_avatar_url(bot)
146
 
147
- bot_content = f"""
148
- <div class="bot-header">
149
- <img src="{avatar}" class="bot-avatar">
150
- <span class="bot-name">{bot}</span>
151
- </div>
152
- <div style="padding-left: 36px;">
153
- <b>Sentiment:</b> {sent}<br>
154
- <b>Intent:</b> {intent}
 
 
 
 
155
  </div>
156
  """
157
- history.append({"role": "assistant", "content": bot_content})
158
- # Always yield a value for both [textbox, chatbot]
159
- yield "", history
 
 
160
 
161
- with gr.Blocks() as demo:
162
- gr.Markdown("### 📱 Model Group Chat")
163
- chatbot = gr.Chatbot(elem_id="chat-window", avatar_images=(get_avatar_url("User"), None), height=500)
 
 
 
 
 
 
 
 
 
 
164
 
165
  with gr.Row():
166
- txt = gr.Textbox(placeholder="Type a message...", show_label=False, scale=4)
167
- btn = gr.Button("Send", variant="primary")
 
 
 
 
 
168
 
169
- # The fix: Ensure inputs/outputs match the yield count
170
- txt.submit(chat_interface, [txt, chatbot], [txt, chatbot])
171
- btn.click(chat_interface, [txt, chatbot], [txt, chatbot])
172
 
173
- demo.launch(css=CSS)
 
 
5
  import torch.nn as nn
6
  import torch.nn.functional as F
7
  from sklearn.feature_extraction.text import TfidfVectorizer
8
+ from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
9
+ from sklearn.svm import SVC
10
+ from sklearn.naive_bayes import MultinomialNB
11
+ from sklearn.linear_model import LogisticRegression
12
  from sklearn.preprocessing import LabelEncoder
13
  import kagglehub
14
+ import warnings
 
15
 
16
+ # Suppress sklearn warnings for cleaner logs
17
+ warnings.filterwarnings("ignore")
18
+
19
+ # --- 1. ARCHITECTURE: H3MOS (Hippocampal Memory & Executive Core) ---
20
 
21
  class EpisodicMemory:
22
+ """Mimics Hippocampal retention and retrieval of recent experiences."""
23
  def __init__(self, capacity=2000):
24
+ self.memory_x = []
25
+ self.memory_y = []
26
  self.capacity = capacity
27
+
28
  def store(self, x, y):
29
+ # Store on CPU to save GPU VRAM
30
+ curr_x = x.detach().cpu()
31
+ curr_y = y.detach().cpu()
32
  for i in range(curr_x.size(0)):
33
  if len(self.memory_x) >= self.capacity:
34
+ self.memory_x.pop(0)
35
+ self.memory_y.pop(0)
36
+ self.memory_x.append(curr_x[i])
37
+ self.memory_y.append(curr_y[i])
38
+
39
  def retrieve(self, query_x, k=5):
40
+ if not self.memory_x:
41
+ return None
42
  mem_tensor = torch.stack(self.memory_x).to(query_x.device)
43
  distances = torch.cdist(query_x, mem_tensor)
44
  top_k_indices = torch.topk(distances, k, largest=False).indices
45
+
46
+ # Gather labels
47
+ retrieved_y = [torch.stack([self.memory_y[idx] for idx in sample_indices])
48
+ for sample_indices in top_k_indices]
49
+ return torch.stack(retrieved_y).to(query_x.device)
50
 
51
  class H3MOS(nn.Module):
 
52
  def __init__(self, input_dim, hidden_dim, output_dim):
53
  super().__init__()
54
+ # Executive Core
55
  self.executive = nn.Sequential(
56
+ nn.Linear(input_dim, hidden_dim),
57
+ nn.LayerNorm(hidden_dim),
58
+ nn.GELU(),
59
+ nn.Dropout(0.2),
60
+ nn.Linear(hidden_dim, hidden_dim),
61
  nn.GELU()
62
  )
63
+ # Motor Policy
64
  self.motor = nn.Linear(hidden_dim, output_dim)
65
+ # Hippocampus
66
+ self.hippocampus = EpisodicMemory(capacity=2000)
67
+
68
  def forward(self, x, training_mode=False):
69
  z = self.executive(x)
70
  raw_logits = self.motor(z)
71
+
72
+ # Fast Path (Training or Empty Memory)
73
+ if training_mode or len(self.hippocampus.memory_x) < 10:
74
+ return raw_logits
75
+
76
+ # Memory Retrieval & Integration
77
  past_labels = self.hippocampus.retrieve(x, k=5)
78
+ if past_labels is None:
79
+ return raw_logits
80
+
81
  mem_votes = torch.zeros_like(raw_logits)
82
  for i in range(x.size(0)):
83
  votes = torch.bincount(past_labels[i], minlength=raw_logits.size(1)).float()
84
  mem_votes[i] = votes
 
85
 
86
+ mem_probs = F.softmax(mem_votes, dim=1)
87
+
88
+ # Dynamic Gating: 80% Neural, 20% Memory
89
+ return (0.8 * raw_logits) + (0.2 * mem_probs * 5.0)
90
+
91
+ # --- 2. DATA SETUP & TRAINING PIPELINE ---
92
 
93
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
94
+ print(f"🚀 Initializing System on {device}...")
95
 
96
+ # Load Data
97
+ try:
98
+ path = kagglehub.dataset_download('dewanmukto/social-messages-and-emoji-reactions')
99
+ df = pd.read_csv(path+"/messages_emojis.csv").dropna(subset=['content'])
100
+ except Exception as e:
101
+ print("Error loading data:", e)
102
+ # Fallback dummy data if kaggle fails (for testing)
103
+ df = pd.DataFrame({'content': ['test'], 'emoji': ['👍']})
104
 
105
+ # Mappings
106
  sent_map = {'❤️':'Pos', '👍':'Pos', '😂':'Pos', '💯':'Pos', '😢':'Neg', '😭':'Neg', '😮':'Neu'}
107
  intent_map = {'❤️':'Emotion', '👍':'Agreement', '😂':'Emotion', '😮':'Surprise'}
108
 
109
+ # Vectorization
110
+ tfidf = TfidfVectorizer(max_features=600, stop_words='english')
111
  X_sparse = tfidf.fit_transform(df['content'])
112
  X_dense = torch.FloatTensor(X_sparse.toarray()).to(device)
113
 
114
+ # Model Zoo Containers
115
+ tasks = ['emoji', 'sentiment', 'intent']
116
+ model_names = ['DISTIL', 'RandomForest', 'SVM', 'NaiveBayes', 'LogReg', 'GradBoost']
117
+ zoo = {task: {} for task in tasks}
118
  encoders = {}
119
 
120
+ print("🧠 Training Models... (This may take a moment)")
121
+
122
+ for task in tasks:
123
+ # Prepare Labels
124
+ if task == 'emoji':
125
+ raw_y = df['emoji'].values
126
+ elif task == 'sentiment':
127
+ raw_y = df['emoji'].apply(lambda x: sent_map.get(x, 'Neutral')).values
128
+ else:
129
+ raw_y = df['emoji'].apply(lambda x: intent_map.get(x, 'Other')).values
130
+
131
  le = LabelEncoder()
132
+ y_nums = le.fit_transform(raw_y)
133
  encoders[task] = le
134
 
135
+ # 1. Train DISTIL-H3MOS (PyTorch)
136
+ y_tensor = torch.LongTensor(y_nums).to(device)
137
+ output_dim = len(le.classes_)
138
+
139
+ model = H3MOS(X_dense.shape[1], 64, output_dim).to(device)
140
+ optimizer = torch.optim.AdamW(model.parameters(), lr=0.01)
141
 
142
+ model.train()
143
+ # Short training loop for demo speed
144
+ for epoch in range(25):
145
+ optimizer.zero_grad()
146
+ out = model(X_dense, training_mode=True)
147
+ loss = F.cross_entropy(out, y_tensor)
148
+ loss.backward()
149
+ optimizer.step()
150
+ # Populate memory occasionally
151
+ if epoch % 5 == 0:
152
+ with torch.no_grad():
153
+ idx = torch.randperm(X_dense.size(0))[:50]
154
+ model.hippocampus.store(X_dense[idx], y_tensor[idx])
155
+
156
+ model.eval()
157
+ zoo[task]['DISTIL'] = model
158
 
159
+ # 2. Train Sklearn Models
160
+ zoo[task]['RandomForest'] = RandomForestClassifier(n_estimators=50).fit(X_sparse, y_nums)
161
+ zoo[task]['SVM'] = SVC(kernel='linear').fit(X_sparse, y_nums)
162
+ zoo[task]['NaiveBayes'] = MultinomialNB().fit(X_sparse, y_nums)
163
+ zoo[task]['LogReg'] = LogisticRegression(max_iter=500).fit(X_sparse, y_nums)
164
+ zoo[task]['GradBoost'] = GradientBoostingClassifier(n_estimators=30).fit(X_sparse, y_nums)
165
 
166
+ print("✅ Training Complete.")
 
 
 
 
 
 
 
 
167
 
168
+ # --- 3. INFERENCE LOGIC ---
 
169
 
170
+ def get_predictions(text):
171
+ """Runs all models on the text."""
172
  vec_s = tfidf.transform([text])
173
  vec_t = torch.FloatTensor(vec_s.toarray()).to(device)
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
+ results = {name: {} for name in model_names}
176
 
177
+ for task in tasks:
178
+ le = encoders[task]
179
+
180
+ for name in model_names:
181
+ if name == 'DISTIL':
182
+ with torch.no_grad():
183
+ logits = zoo[task][name](vec_t)
184
+ pred_idx = torch.argmax(logits, dim=1).item()
185
+ pred_label = le.inverse_transform([pred_idx])[0]
186
+ else:
187
+ pred_idx = zoo[task][name].predict(vec_s)[0]
188
+ pred_label = le.inverse_transform([pred_idx])[0]
189
+
190
+ results[name][task] = pred_label
191
+
192
+ return results
193
+
194
+ # --- 4. UI STYLING & INTERFACE ---
195
+
196
+ def get_avatar_url(seed):
197
+ return f"https://api.dicebear.com/7.x/bottts/svg?seed={seed}&backgroundColor=transparent"
198
+
199
+ CSS = """
200
+ .chat-window { font-family: 'Segoe UI', sans-serif; }
201
+
202
+ /* User Message Styling */
203
+ .user-reactions {
204
+ margin-top: 8px;
205
+ padding-top: 6px;
206
+ border-top: 1px solid rgba(255,255,255,0.3);
207
+ font-size: 1.2em;
208
+ letter-spacing: 4px;
209
+ text-align: right;
210
+ opacity: 0.9;
211
+ }
212
+
213
+ /* Bot Reply Container */
214
+ .model-scroll-container {
215
+ display: flex;
216
+ gap: 12px;
217
+ overflow-x: auto;
218
+ padding: 10px 4px;
219
+ scrollbar-width: thin;
220
+ }
221
+
222
+ .model-card {
223
+ background: white;
224
+ min-width: 140px;
225
+ border-radius: 12px;
226
+ padding: 12px;
227
+ box-shadow: 0 4px 12px rgba(0,0,0,0.08);
228
+ display: flex;
229
+ flex-direction: column;
230
+ align-items: center;
231
+ border: 1px solid #eee;
232
+ transition: transform 0.2s;
233
+ }
234
+ .model-card:hover { transform: translateY(-3px); }
235
+
236
+ .card-avatar {
237
+ width: 45px;
238
+ height: 45px;
239
+ border-radius: 50%;
240
+ margin-bottom: 8px;
241
+ border: 2px solid #f0f2f5;
242
+ background: #f9f9f9;
243
+ }
244
+
245
+ .card-name {
246
+ font-size: 11px;
247
+ font-weight: 700;
248
+ text-transform: uppercase;
249
+ color: #888;
250
+ margin-bottom: 4px;
251
+ }
252
+
253
+ .card-emoji {
254
+ font-size: 28px;
255
+ margin: 4px 0;
256
+ }
257
+
258
+ .card-badge {
259
+ font-size: 10px;
260
+ padding: 2px 8px;
261
+ border-radius: 10px;
262
+ margin-top: 4px;
263
+ font-weight: 600;
264
+ }
265
+
266
+ .bg-Pos { background-color: #e6fffa; color: #2c7a7b; }
267
+ .bg-Neg { background-color: #fff5f5; color: #c53030; }
268
+ .bg-Neu { background-color: #f7fafc; color: #4a5568; }
269
+
270
+ .intent-row {
271
+ font-size: 10px;
272
+ color: #666;
273
+ margin-top: 6px;
274
+ border-top: 1px dashed #eee;
275
+ padding-top: 4px;
276
+ width: 100%;
277
+ text-align: center;
278
+ }
279
+ """
280
+
281
+ def chat_logic(message, history):
282
+ if not message:
283
+ return "", history
284
+
285
+ preds = get_predictions(message)
286
+
287
+ # 1. Create User Message HTML (with Emoji Reaction Bar)
288
+ # Order: DISTIL, RF, SVM, NB, LR, GB
289
+ reaction_string = "".join([preds[m]['emoji'] for m in model_names])
290
 
291
+ user_html = f"""
292
+ <div>
293
+ {message}
294
+ <div class="user-reactions" title="Consensus: {reaction_string}">{reaction_string}</div>
295
+ </div>
296
+ """
297
+ history.append({"role": "user", "content": user_html})
298
 
299
+ # 2. Create Single Bot Reply HTML (Horizontal Scroll Cards)
300
+ cards_html = '<div class="model-scroll-container">'
 
301
 
302
+ for name in model_names:
303
+ p = preds[name]
 
 
 
304
 
305
+ # Color coding for sentiment
306
+ sent_cls = "bg-Neu"
307
+ if "Pos" in p['sentiment']: sent_cls = "bg-Pos"
308
+ elif "Neg" in p['sentiment']: sent_cls = "bg-Neg"
309
+
310
+ cards_html += f"""
311
+ <div class="model-card">
312
+ <img src="{get_avatar_url(name)}" class="card-avatar">
313
+ <div class="card-name">{name}</div>
314
+ <div class="card-emoji">{p['emoji']}</div>
315
+ <div class="card-badge {sent_cls}">{p['sentiment']}</div>
316
+ <div class="intent-row">{p['intent']}</div>
317
  </div>
318
  """
319
+ cards_html += "</div>"
320
+
321
+ history.append({"role": "assistant", "content": cards_html})
322
+
323
+ return "", history
324
 
325
+ # --- 5. LAUNCH APP ---
326
+
327
+ with gr.Blocks(css=CSS, title="Social Benchmarks AI") as demo:
328
+ gr.Markdown("### 🤖 Multi-Model Social Benchmarks")
329
+ gr.Markdown("Type a message to see how 6 different AI architectures interpret it.")
330
+
331
+ chatbot = gr.Chatbot(
332
+ elem_id="chat-window",
333
+ type="messages",
334
+ avatar_images=(None, "https://api.dicebear.com/7.x/bottts/svg?seed=Admin"),
335
+ height=600,
336
+ render_markdown=False # Important to render our custom HTML
337
+ )
338
 
339
  with gr.Row():
340
+ txt = gr.Textbox(
341
+ placeholder="Type a social message (e.g., 'I cant believe you did that!')",
342
+ scale=4,
343
+ show_label=False,
344
+ container=False
345
+ )
346
+ btn = gr.Button("Analyze", variant="primary", scale=1)
347
 
348
+ # Event bindings
349
+ txt.submit(chat_logic, [txt, chatbot], [txt, chatbot])
350
+ btn.click(chat_logic, [txt, chatbot], [txt, chatbot])
351
 
352
+ if __name__ == "__main__":
353
+ demo.launch()