MaliosDark commited on
Commit
1d35105
·
verified ·
1 Parent(s): 9acbec0

Add AGI module: sofia_multimodal.py

Browse files
Files changed (1) hide show
  1. sofia_multimodal.py +242 -0
sofia_multimodal.py ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ SOFIA Multi-modal AGI System
4
+ Combines text and image embeddings for advanced understanding
5
+ """
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ from transformers import CLIPModel, CLIPProcessor, AutoTokenizer, AutoModel
10
+ from PIL import Image
11
+ import requests
12
+ from io import BytesIO
13
+ import numpy as np
14
+ from typing import List, Union, Tuple
15
+ import logging
16
+
17
+ logging.basicConfig(level=logging.INFO)
18
+ logger = logging.getLogger(__name__)
19
+
20
+ class SOFIAMultiModal(nn.Module):
21
+ """
22
+ Multi-modal SOFIA model combining text and vision capabilities
23
+ """
24
+
25
+ def __init__(self, text_model_path: str = "./SOFIA-v2-lora", vision_model_name: str = "openai/clip-vit-base-patch32"):
26
+ super().__init__()
27
+
28
+ # Load text model (SOFIA)
29
+ self.text_tokenizer = AutoTokenizer.from_pretrained(text_model_path)
30
+ self.text_model = AutoModel.from_pretrained(text_model_path)
31
+
32
+ # Load vision model (CLIP)
33
+ self.vision_processor = CLIPProcessor.from_pretrained(vision_model_name)
34
+ self.vision_model = CLIPModel.from_pretrained(vision_model_name)
35
+
36
+ # Projection layers to align text and image embeddings
37
+ self.text_projection = nn.Linear(768, 512) # MPNet dim to CLIP dim
38
+ self.image_projection = nn.Linear(512, 512) # CLIP dim (already 512)
39
+
40
+ # Multi-modal fusion layer
41
+ self.fusion_layer = nn.Sequential(
42
+ nn.Linear(1024, 512),
43
+ nn.ReLU(),
44
+ nn.Linear(512, 512)
45
+ )
46
+
47
+ # Move to GPU if available
48
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
49
+ self.to(self.device)
50
+
51
+ def encode_text(self, texts: List[str]) -> torch.Tensor:
52
+ """Encode text inputs using SOFIA"""
53
+ inputs = self.text_tokenizer(texts, return_tensors="pt", padding=True, truncation=True, max_length=512)
54
+ inputs = {k: v.to(self.device) for k, v in inputs.items()}
55
+
56
+ with torch.no_grad():
57
+ outputs = self.text_model(**inputs)
58
+ embeddings = outputs.last_hidden_state.mean(dim=1) # Mean pooling
59
+
60
+ # Project to common space
61
+ embeddings = self.text_projection(embeddings)
62
+ return embeddings
63
+
64
+ def encode_image(self, images: Union[List[Image.Image], List[str]]) -> torch.Tensor:
65
+ """Encode image inputs using CLIP"""
66
+ # Handle URLs
67
+ processed_images = []
68
+ for img in images:
69
+ if isinstance(img, str):
70
+ # Load image from URL
71
+ response = requests.get(img)
72
+ img = Image.open(BytesIO(response.content))
73
+ processed_images.append(img)
74
+
75
+ inputs = self.vision_processor(images=processed_images, return_tensors="pt")
76
+ inputs = {k: v.to(self.device) for k, v in inputs.items()}
77
+
78
+ with torch.no_grad():
79
+ outputs = self.vision_model.get_image_features(**inputs)
80
+
81
+ # Project to common space
82
+ embeddings = self.image_projection(outputs)
83
+ return embeddings
84
+
85
+ def encode_multimodal(self, texts: List[str], images: Union[List[Image.Image], List[str], None] = None) -> torch.Tensor:
86
+ """Encode multi-modal inputs (text + optional images)"""
87
+ # Encode text
88
+ text_embeddings = self.encode_text(texts)
89
+
90
+ if images is None:
91
+ # Text-only mode
92
+ return text_embeddings
93
+
94
+ # Encode images
95
+ image_embeddings = self.encode_image(images)
96
+
97
+ # Concatenate and fuse
98
+ combined = torch.cat([text_embeddings, image_embeddings], dim=1)
99
+ fused_embeddings = self.fusion_layer(combined)
100
+
101
+ return fused_embeddings
102
+
103
+ def compute_similarity(self, query_embedding: torch.Tensor, target_embeddings: torch.Tensor) -> torch.Tensor:
104
+ """Compute cosine similarity between embeddings"""
105
+ # Normalize embeddings
106
+ query_norm = query_embedding / query_embedding.norm(dim=1, keepdim=True)
107
+ target_norm = target_embeddings / target_embeddings.norm(dim=1, keepdim=True)
108
+
109
+ # Cosine similarity
110
+ similarity = torch.mm(query_norm, target_norm.t())
111
+ return similarity
112
+
113
+ def search_similar(self, query: Union[str, Tuple[str, Union[Image.Image, str]]],
114
+ candidates: List[Union[str, Tuple[str, Union[Image.Image, str]]]],
115
+ top_k: int = 5) -> List[Tuple[int, float]]:
116
+ """
117
+ Search for most similar items to query
118
+
119
+ Args:
120
+ query: Either text string or (text, image) tuple
121
+ candidates: List of text strings or (text, image) tuples
122
+ top_k: Number of top results to return
123
+
124
+ Returns:
125
+ List of (index, similarity_score) tuples
126
+ """
127
+
128
+ # Encode query
129
+ if isinstance(query, str):
130
+ query_texts = [query]
131
+ query_images = None
132
+ else:
133
+ query_texts = [query[0]]
134
+ query_images = [query[1]]
135
+
136
+ query_embedding = self.encode_multimodal(query_texts, query_images)
137
+
138
+ # Encode candidates
139
+ candidate_texts = []
140
+ candidate_images = []
141
+
142
+ for candidate in candidates:
143
+ if isinstance(candidate, str):
144
+ candidate_texts.append(candidate)
145
+ candidate_images.append(None)
146
+ else:
147
+ candidate_texts.append(candidate[0])
148
+ candidate_images.append(candidate[1])
149
+
150
+ # Filter out None images
151
+ valid_images = [img for img in candidate_images if img is not None]
152
+ candidate_embeddings = self.encode_multimodal(candidate_texts, valid_images if valid_images else None)
153
+
154
+ # Compute similarities
155
+ similarities = self.compute_similarity(query_embedding, candidate_embeddings)
156
+
157
+ # Get top-k results
158
+ top_scores, top_indices = torch.topk(similarities[0], min(top_k, len(candidates)))
159
+
160
+ results = [(idx.item(), score.item()) for idx, score in zip(top_indices, top_scores)]
161
+ return results
162
+
163
+ class MultiModalSOFIA:
164
+ """
165
+ High-level interface for multi-modal SOFIA operations
166
+ """
167
+
168
+ def __init__(self, model_path: str = "./SOFIA-v2-lora"):
169
+ self.model = SOFIAMultiModal(model_path)
170
+ logger.info("Multi-modal SOFIA initialized")
171
+
172
+ def describe_image(self, image: Union[Image.Image, str], context: str = "") -> str:
173
+ """
174
+ Generate a textual description of an image, optionally with context
175
+ """
176
+ # This is a simplified implementation
177
+ # In a real system, this would use a captioning model
178
+ image_embedding = self.model.encode_image([image])
179
+
180
+ # For now, return a placeholder description
181
+ # TODO: Integrate with a proper image captioning model
182
+ return f"Image described with context: {context}"
183
+
184
+ def find_similar_images(self, query_image: Union[Image.Image, str],
185
+ image_database: List[Union[Image.Image, str]],
186
+ top_k: int = 5) -> List[Tuple[int, float]]:
187
+ """
188
+ Find images similar to a query image
189
+ """
190
+ # Convert to tuples for multi-modal search
191
+ query = ("", query_image) # Empty text, image only
192
+ candidates = [("image_" + str(i), img) for i, img in enumerate(image_database)]
193
+
194
+ results = self.model.search_similar(query, candidates, top_k)
195
+ return results
196
+
197
+ def search_visual_content(self, text_query: str,
198
+ image_results: List[Union[Image.Image, str]],
199
+ top_k: int = 5) -> List[Tuple[int, float]]:
200
+ """
201
+ Search for images that match a text description
202
+ """
203
+ query = (text_query, None) # Text only
204
+ candidates = [("image_" + str(i), img) for i, img in enumerate(image_results)]
205
+
206
+ results = self.model.search_similar(query, candidates, top_k)
207
+ return results
208
+
209
+ def multimodal_retrieval(self, query: Union[str, Tuple[str, Union[Image.Image, str]]],
210
+ documents: List[Union[str, Tuple[str, Union[Image.Image, str]]]],
211
+ top_k: int = 5) -> List[Tuple[int, float]]:
212
+ """
213
+ Perform retrieval across multi-modal documents
214
+ """
215
+ return self.model.search_similar(query, documents, top_k)
216
+
217
+
218
+ # Example usage and testing
219
+ if __name__ == "__main__":
220
+ # Initialize multi-modal SOFIA
221
+ mm_sofia = MultiModalSOFIA()
222
+
223
+ # Example 1: Text-to-image search
224
+ text_query = "a beautiful sunset over mountains"
225
+ sample_images = [
226
+ "https://picsum.photos/300/200?random=1",
227
+ "https://picsum.photos/300/200?random=2",
228
+ "https://picsum.photos/300/200?random=3"
229
+ ]
230
+
231
+ print("Searching for images matching:", text_query)
232
+ results = mm_sofia.search_visual_content(text_query, sample_images, top_k=2)
233
+ for idx, score in results:
234
+ print(f"Image {idx}: similarity = {score:.4f}")
235
+
236
+ # Example 2: Image-to-image similarity
237
+ print("\nFinding similar images...")
238
+ similar_results = mm_sofia.find_similar_images(sample_images[0], sample_images[1:], top_k=2)
239
+ for idx, score in similar_results:
240
+ print(f"Similar image {idx}: similarity = {score:.4f}")
241
+
242
+ print("Multi-modal SOFIA demo completed!")