| """ |
| Ekalavya Multi-Modal - Vision + Audio + Video + Text |
| Complete multi-modal AI that can see, hear, read, and understand |
| """ |
| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
| from typing import Optional, Tuple, List, Union |
| import math |
|
|
|
|
| class VisionEncoder(nn.Module): |
| """ |
| Vision Encoder - Processes images and video frames |
| Converts images to embeddings that can be understood by the language model |
| """ |
| def __init__(self, image_size=224, patch_size=16, embed_dim=1024): |
| super().__init__() |
| self.image_size = image_size |
| self.patch_size = patch_size |
| self.num_patches = (image_size // patch_size) ** 2 |
| |
| |
| self.patch_embed = nn.Conv2d( |
| in_channels=3, |
| out_channels=embed_dim, |
| kernel_size=patch_size, |
| stride=patch_size |
| ) |
| |
| |
| self.pos_embed = nn.Parameter(torch.zeros(1, self.num_patches + 1, embed_dim)) |
| self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) |
| |
| |
| self.layers = nn.ModuleList([ |
| VisionTransformerBlock(embed_dim) for _ in range(12) |
| ]) |
| |
| self.norm = nn.LayerNorm(embed_dim) |
| |
| def forward(self, x): |
| """ |
| Args: |
| x: Image tensor [batch, channels, height, width] |
| Returns: |
| Image embeddings [batch, num_patches+1, embed_dim] |
| """ |
| batch_size = x.shape[0] |
| |
| |
| x = self.patch_embed(x) |
| x = x.flatten(2).transpose(1, 2) |
| |
| |
| cls_tokens = self.cls_token.expand(batch_size, -1, -1) |
| x = torch.cat([cls_tokens, x], dim=1) |
| |
| |
| x = x + self.pos_embed |
| |
| |
| for layer in self.layers: |
| x = layer(x) |
| |
| x = self.norm(x) |
| return x |
|
|
|
|
| class VisionTransformerBlock(nn.Module): |
| """Transformer block for vision encoder""" |
| def __init__(self, dim, num_heads=16, mlp_ratio=4.0): |
| super().__init__() |
| self.norm1 = nn.LayerNorm(dim) |
| self.attn = nn.MultiheadAttention(dim, num_heads, batch_first=True) |
| self.norm2 = nn.LayerNorm(dim) |
| self.mlp = nn.Sequential( |
| nn.Linear(dim, int(dim * mlp_ratio)), |
| nn.GELU(), |
| nn.Linear(int(dim * mlp_ratio), dim) |
| ) |
| |
| def forward(self, x): |
| x = x + self.attn(self.norm1(x), self.norm1(x), self.norm1(x))[0] |
| x = x + self.mlp(self.norm2(x)) |
| return x |
|
|
|
|
| class AudioEncoder(nn.Module): |
| """ |
| Audio Encoder - Processes speech and audio |
| Converts audio waveforms to embeddings |
| """ |
| def __init__(self, embed_dim=1024, sample_rate=16000): |
| super().__init__() |
| self.sample_rate = sample_rate |
| |
| |
| self.mel_transform = nn.Sequential( |
| nn.Conv1d(1, 64, kernel_size=400, stride=160), |
| nn.GELU(), |
| nn.Conv1d(64, 128, kernel_size=3, stride=2), |
| nn.GELU(), |
| nn.Conv1d(128, 256, kernel_size=3, stride=2), |
| nn.GELU() |
| ) |
| |
| |
| self.proj = nn.Linear(256, embed_dim) |
| |
| |
| self.layers = nn.ModuleList([ |
| AudioTransformerBlock(embed_dim) for _ in range(6) |
| ]) |
| |
| self.norm = nn.LayerNorm(embed_dim) |
| |
| def forward(self, x): |
| """ |
| Args: |
| x: Audio waveform [batch, samples] |
| Returns: |
| Audio embeddings [batch, time_steps, embed_dim] |
| """ |
| |
| x = x.unsqueeze(1) |
| |
| |
| x = self.mel_transform(x) |
| x = x.transpose(1, 2) |
| |
| |
| x = self.proj(x) |
| |
| |
| for layer in self.layers: |
| x = layer(x) |
| |
| x = self.norm(x) |
| return x |
|
|
|
|
| class AudioTransformerBlock(nn.Module): |
| """Transformer block for audio encoder""" |
| def __init__(self, dim, num_heads=16, mlp_ratio=4.0): |
| super().__init__() |
| self.norm1 = nn.LayerNorm(dim) |
| self.attn = nn.MultiheadAttention(dim, num_heads, batch_first=True) |
| self.norm2 = nn.LayerNorm(dim) |
| self.mlp = nn.Sequential( |
| nn.Linear(dim, int(dim * mlp_ratio)), |
| nn.GELU(), |
| nn.Linear(int(dim * mlp_ratio), dim) |
| ) |
| |
| def forward(self, x): |
| x = x + self.attn(self.norm1(x), self.norm1(x), self.norm1(x))[0] |
| x = x + self.mlp(self.norm2(x)) |
| return x |
|
|
|
|
| class MultiModalProjector(nn.Module): |
| """ |
| Projects vision and audio embeddings to language model space |
| """ |
| def __init__(self, vision_dim=1024, audio_dim=1024, llm_dim=2048): |
| super().__init__() |
| self.vision_proj = nn.Sequential( |
| nn.Linear(vision_dim, llm_dim), |
| nn.GELU(), |
| nn.Linear(llm_dim, llm_dim) |
| ) |
| self.audio_proj = nn.Sequential( |
| nn.Linear(audio_dim, llm_dim), |
| nn.GELU(), |
| nn.Linear(llm_dim, llm_dim) |
| ) |
| |
| def forward(self, vision_embeds=None, audio_embeds=None): |
| """ |
| Project multi-modal embeddings to LLM space |
| """ |
| projected = {} |
| if vision_embeds is not None: |
| projected['vision'] = self.vision_proj(vision_embeds) |
| if audio_embeds is not None: |
| projected['audio'] = self.audio_proj(audio_embeds) |
| return projected |
|
|
|
|
| class EkalavyaMultiModal(nn.Module): |
| """ |
| Ekalavya Multi-Modal - Complete multi-modal AI |
| |
| Can process: |
| - Text (language model) |
| - Images (vision encoder) |
| - Videos (multiple frames) |
| - Audio/Voice (audio encoder) |
| |
| All modalities are fused and understood together |
| """ |
| def __init__( |
| self, |
| llm_dim=2048, |
| vision_dim=1024, |
| audio_dim=1024, |
| image_size=224, |
| patch_size=16 |
| ): |
| super().__init__() |
| |
| |
| self.vision_encoder = VisionEncoder(image_size, patch_size, vision_dim) |
| |
| |
| self.audio_encoder = AudioEncoder(audio_dim) |
| |
| |
| self.projector = MultiModalProjector(vision_dim, audio_dim, llm_dim) |
| |
| |
| self.llm_dim = llm_dim |
| self.text_embed = nn.Embedding(32000, llm_dim) |
| |
| |
| self.fusion = nn.ModuleList([ |
| FusionLayer(llm_dim) for _ in range(8) |
| ]) |
| |
| |
| self.output_head = nn.Linear(llm_dim, 32000) |
| |
| def forward( |
| self, |
| text_tokens=None, |
| image=None, |
| audio=None, |
| video_frames=None |
| ): |
| """ |
| Process multi-modal input |
| |
| Args: |
| text_tokens: Text token IDs [batch, seq_len] |
| image: Image tensor [batch, 3, height, width] |
| audio: Audio waveform [batch, samples] |
| video_frames: Video frames [batch, num_frames, 3, height, width] |
| |
| Returns: |
| Fused multi-modal embeddings |
| """ |
| batch_size = text_tokens.shape[0] if text_tokens is not None else 1 |
| embeddings = [] |
| |
| |
| if text_tokens is not None: |
| text_embeds = self.text_embed(text_tokens) |
| embeddings.append(text_embeds) |
| |
| |
| if image is not None: |
| vision_embeds = self.vision_encoder(image) |
| vision_projected = self.projector(vision_embeds=vision_embeds)['vision'] |
| embeddings.append(vision_projected) |
| |
| |
| if video_frames is not None: |
| num_frames = video_frames.shape[1] |
| frame_embeds = [] |
| for i in range(num_frames): |
| frame = video_frames[:, i] |
| frame_embed = self.vision_encoder(frame) |
| frame_embeds.append(frame_embed) |
| |
| video_embeds = torch.stack(frame_embeds, dim=1).mean(dim=1) |
| video_projected = self.projector(vision_embeds=video_embeds)['vision'] |
| embeddings.append(video_projected) |
| |
| |
| if audio is not None: |
| audio_embeds = self.audio_encoder(audio) |
| audio_projected = self.projector(audio_embeds=audio_embeds)['audio'] |
| embeddings.append(audio_projected) |
| |
| |
| if embeddings: |
| combined = torch.cat(embeddings, dim=1) |
| |
| |
| for layer in self.fusion: |
| combined = layer(combined) |
| |
| |
| logits = self.output_head(combined) |
| return logits |
| |
| return None |
| |
| def generate_response(self, text_prompt, image=None, audio=None, video=None): |
| """ |
| Generate response from multi-modal input |
| """ |
| |
| logits = self.forward( |
| text_tokens=text_prompt, |
| image=image, |
| audio=audio, |
| video_frames=video |
| ) |
| |
| if logits is not None: |
| |
| next_token_logits = logits[:, -1, :] |
| next_token = torch.argmax(next_token_logits, dim=-1) |
| return next_token |
| |
| return None |
|
|
|
|
| class FusionLayer(nn.Module): |
| """Fusion layer for combining multi-modal embeddings""" |
| def __init__(self, dim, num_heads=16): |
| super().__init__() |
| self.norm = nn.LayerNorm(dim) |
| self.attn = nn.MultiheadAttention(dim, num_heads, batch_first=True) |
| self.mlp = nn.Sequential( |
| nn.Linear(dim, dim * 4), |
| nn.GELU(), |
| nn.Linear(dim * 4, dim) |
| ) |
| |
| def forward(self, x): |
| x = x + self.attn(self.norm(x), self.norm(x), self.norm(x))[0] |
| x = x + self.mlp(self.norm(x)) |
| return x |
|
|
|
|
| |
|
|
| def process_image(image_path_or_tensor): |
| """ |
| Process image for the model |
| """ |
| import torchvision.transforms as T |
| |
| if isinstance(image_path_or_tensor, str): |
| from PIL import Image |
| image = Image.open(image_path_or_tensor).convert('RGB') |
| else: |
| image = image_path_or_tensor |
| |
| |
| transform = T.Compose([ |
| T.Resize((224, 224)), |
| T.ToTensor(), |
| T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) |
| ]) |
| |
| if isinstance(image, torch.Tensor): |
| return image |
| return transform(image).unsqueeze(0) |
|
|
|
|
| def process_audio(audio_path_or_tensor, sample_rate=16000): |
| """ |
| Process audio for the model |
| """ |
| if isinstance(audio_path_or_tensor, str): |
| import torchaudio |
| waveform, sr = torchaudio.load(audio_path_or_tensor) |
| if sr != sample_rate: |
| resampler = torchaudio.transforms.Resample(sr, sample_rate) |
| waveform = resampler(waveform) |
| waveform = waveform.mean(dim=0) |
| else: |
| waveform = audio_path_or_tensor |
| |
| return waveform.unsqueeze(0) |
|
|
|
|
| def process_video(video_path, num_frames=8): |
| """ |
| Extract frames from video |
| """ |
| import cv2 |
| import numpy as np |
| |
| cap = cv2.VideoCapture(video_path) |
| total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) |
| |
| |
| frame_indices = np.linspace(0, total_frames-1, num_frames, dtype=int) |
| |
| frames = [] |
| for idx in frame_indices: |
| cap.set(cv2.CAP_PROP_POS_FRAMES, idx) |
| ret, frame = cap.read() |
| if ret: |
| frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) |
| frames.append(process_image(frame)) |
| |
| cap.release() |
| |
| return torch.cat(frames, dim=0).unsqueeze(0) |
|
|
|
|
| if __name__ == '__main__': |
| print("="*70) |
| print("EKALAVYA MYTHOS MULTI-MODAL") |
| print("="*70) |
| print("\n🎯 Capabilities:") |
| print(" ✅ Image Understanding") |
| print(" ✅ Video Analysis") |
| print(" ✅ Voice/Audio Processing") |
| print(" ✅ Text Generation") |
| print(" ✅ Multi-modal Fusion") |
| |
| |
| model = EkalavyaMultiModal() |
| print(f"\n📊 Model Parameters: {sum(p.numel() for p in model.parameters()):,}") |
| |
| |
| batch_size = 2 |
| text_tokens = torch.randint(0, 32000, (batch_size, 10)) |
| image = torch.randn(batch_size, 3, 224, 224) |
| audio = torch.randn(batch_size, 16000) |
| |
| print("\n🧪 Testing multi-modal processing...") |
| output = model(text_tokens=text_tokens, image=image, audio=audio) |
| print(f"✅ Output shape: {output.shape}") |
| |
| print("\n" + "="*70) |
| print("✅ Multi-modal model ready!") |
| print("="*70) |
|
|