Nada Salim commited on
Commit
becb4f7
·
0 Parent(s):

Clean space deploy

Browse files
.gitattributes ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ *.pt filter=lfs diff=lfs merge=lfs -text
2
+ *.pth filter=lfs diff=lfs merge=lfs -text
3
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Neural Image Captioning
3
+ emoji: 🖼️
4
+ colorFrom: indigo
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 4.19.0
8
+ app_file: app/gradio_app.py
9
+ pinned: false
10
+ ---
11
+
12
+ # 🖼️ Neural Image Caption Generation
13
+
14
+ ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
15
+ ![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)
16
+ ![PyTorch](https://img.shields.io/badge/PyTorch-EE4C2C?style=flat&logo=pytorch&logoColor=white)
17
+
18
+ ## Overview
19
+
20
+ End-to-end image captioning system using deep learning, combining computer vision (ResNet-50) with NLP (LSTM) to generate natural language descriptions of images.
21
+
22
+ ## Architecture
23
+
24
+ ```mermaid
25
+ graph LR
26
+ A[Input Image] -->|Resize & Normalize| B[ResNet-50 Frozen]
27
+ B -->|2048-d| C[Linear Projection]
28
+ C -->|256-d| D[LSTM Decoder]
29
+ D -->|Step-by-step| E[Caption]
30
+ ```
31
+
32
+ This model follows the "Show and Tell" architecture by utilizing a pre-trained CNN to extract image features and passing those features as the initial state or input to an LSTM which decodes them into a natural language sequence.
33
+
34
+ ## Dataset
35
+
36
+ We use the **Flickr8k** dataset:
37
+
38
+ - **8,092** images in total
39
+ - **5** captions per image
40
+ - **Splits**:
41
+ - Train: 6,000 images
42
+ - Val: 1,000 images
43
+ - Test: 1,000 images
44
+ - **Preprocessing**: Lowercase conversion, punctuation removal, and frequency thresholding.
45
+
46
+ ## Project Structure
47
+
48
+ ```text
49
+ .
50
+ ├── app/
51
+ │ ├── app.py (Streamlit App)
52
+ │ └── gradio_app.py (Gradio App)
53
+ ├── checkpoints/
54
+ ├── scripts/
55
+ │ ├── extract_features.py
56
+ │ ├── train.py
57
+ │ └── evaluate.py
58
+ ├── src/
59
+ │ ├── data/
60
+ │ ├── evaluation/
61
+ │ ├── inference/
62
+ │ ├── models/
63
+ │ └── utils/
64
+ ├── tests/
65
+ │ ├── integration/
66
+ │ └── unit/
67
+ ├── Dockerfile
68
+ ├── Dockerfile.gpu
69
+ ├── docker-compose.yml
70
+ ├── requirements.txt
71
+ └── README.md
72
+ ```
73
+
74
+ ## Installation
75
+
76
+ ```bash
77
+ git clone <repo-url>
78
+ cd Task62
79
+ pip install -r requirements.txt
80
+ make setup
81
+ ```
82
+
83
+ ## Quick Start
84
+
85
+ 1. Download the Flickr8k dataset from Kaggle.
86
+ 2. Run data preparation: `python setup_data.py`
87
+ 3. Extract features: `python scripts/extract_features.py`
88
+ 4. Train the model: `python scripts/train.py`
89
+ 5. Evaluate: `python scripts/evaluate.py`
90
+
91
+ ## Training
92
+
93
+ | Hyperparameter | Value |
94
+ | -------------- | ----- |
95
+ | Embed Dim | 256 |
96
+ | Hidden Dim | 512 |
97
+ | Batch Size | 64 |
98
+ | Learning Rate | 1e-3 |
99
+
100
+ Features include early stopping, LR scheduling, gradient clipping, and automated checkpointing.
101
+
102
+ ## Evaluation
103
+
104
+ | Metric | Expected Score |
105
+ | ------- | -------------- |
106
+ | BLEU-1 | ~60.0 |
107
+ | BLEU-4 | ~20.0 |
108
+ | ROUGE-L | ~45.0 |
109
+ | METEOR | ~22.0 |
110
+
111
+ _Qualitative examples showing generated captions will be displayed here._
112
+
113
+ ## Web Interfaces
114
+
115
+ ### Streamlit App
116
+
117
+ Start the Streamlit interface to upload and test your own images:
118
+
119
+ ```bash
120
+ streamlit run app/app.py
121
+ # or
122
+ make app
123
+ ```
124
+
125
+ Available at: `http://localhost:8501`
126
+
127
+ ### Gradio App
128
+
129
+ Start the Gradio interface:
130
+
131
+ ```bash
132
+ python app/gradio_app.py
133
+ # or
134
+ make gradio
135
+ ```
136
+
137
+ Available at: `http://localhost:7860`
138
+
139
+ ## Docker
140
+
141
+ Run both the Streamlit UI and Gradio UI inside Docker containers:
142
+
143
+ ```bash
144
+ # Build the image
145
+ docker build -t image-captioning .
146
+
147
+ # Run both services
148
+ docker-compose up
149
+
150
+ # Services:
151
+ # Streamlit UI → http://localhost:8501
152
+ # Gradio UI → http://localhost:7860
153
+ ```
154
+
155
+ ## Model & Deployments
156
+
157
+ ### 🤗 Hugging Face Spaces
158
+
159
+ Try the model live on Hugging Face Spaces: [Image Captioning Demo](https://huggingface.co/spaces/Nada0salim/image-captioning)
160
+
161
+ ### 🐳 Docker Hub
162
+
163
+ Container image available at: [Docker Hub Repository](https://hub.docker.com/repository/docker/nada123456aa/image-caption/general)
164
+
165
+ ## Testing
166
+
167
+ Run the test suite using pytest:
168
+
169
+ ```bash
170
+ pytest tests/ -v --cov=src
171
+ ```
172
+
173
+ ## Technologies
174
+
175
+ - Python 3.10+
176
+ - PyTorch & torchvision (ResNet-50)
177
+ - Streamlit & Gradio (Web UIs)
178
+ - Docker & Docker Compose
179
+ - NLTK, rouge-score
180
+ - HuggingFace Hub
181
+
182
+ ## License
183
+
184
+ MIT
app/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ """Streamlit web application package."""
app/app.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import gradio as gr
4
+ import torch
5
+ from PIL import Image
6
+ import nltk
7
+
8
+ # Download NLTK data required for metrics/vocabulary on Hugging Face Spaces
9
+ try:
10
+ nltk.data.find('tokenizers/punkt')
11
+ except LookupError:
12
+ nltk.download('punkt', quiet=True)
13
+ nltk.download('wordnet', quiet=True)
14
+ nltk.download('omw-1.4', quiet=True)
15
+ nltk.download('punkt_tab', quiet=True)
16
+
17
+ # Add project root to path to allow importing from src
18
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
19
+
20
+ from src.inference.pipeline import CaptionPipeline
21
+
22
+ # Global pipeline instance
23
+ pipeline = None
24
+
25
+ def load_model():
26
+ global pipeline
27
+ if pipeline is None:
28
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
29
+ try:
30
+ pipeline = CaptionPipeline.from_checkpoint('checkpoints', device=device)
31
+ return f"✅ Model loaded successfully on **{device}**."
32
+ except Exception as e:
33
+ return f"❌ Failed to load model: {e}\n\nMake sure you have trained the model and `checkpoints/` exists."
34
+ return f"✅ Model ready."
35
+
36
+ def generate_caption(image, strategy, beam_size, max_length, temperature):
37
+ if image is None:
38
+ return "⚠️ Please upload an image first."
39
+ if pipeline is None:
40
+ return "❌ Model not loaded properly."
41
+
42
+ try:
43
+ # Generate caption using the pipeline
44
+ result = pipeline.generate(
45
+ image,
46
+ strategy="beam" if strategy == "Beam Search" else "greedy",
47
+ beam_size=int(beam_size),
48
+ max_length=int(max_length),
49
+ temperature=float(temperature)
50
+ )
51
+ print(f"Generated caption: {result['caption']}")
52
+ return f"Caption: {result['caption']}\nConfidence Score: {result['score']:.4f}"
53
+ except Exception as e:
54
+ print(f"Error: {e}")
55
+ return f"Error generating caption: {e}"
56
+
57
+ # Build Gradio Interface using Blocks
58
+ with gr.Blocks(title="Neural Image Captioning", theme=gr.themes.Soft()) as interface:
59
+ gr.Markdown("# 🖼️ Neural Image Captioning")
60
+ gr.Markdown("Upload an image to generate a description using a CNN-LSTM model trained on Flickr8k.")
61
+
62
+ status_text = gr.Markdown(load_model())
63
+
64
+ with gr.Row():
65
+ with gr.Column(scale=1):
66
+ image_input = gr.Image(type="pil", label="Upload Image")
67
+
68
+ with gr.Accordion("Advanced Settings", open=False):
69
+ strategy_input = gr.Radio(["Beam Search", "Greedy"], value="Beam Search", label="Decoding Strategy")
70
+ beam_size_input = gr.Slider(1, 10, value=3, step=1, label="Beam Size (for Beam Search)")
71
+ max_len_input = gr.Slider(5, 50, value=20, step=1, label="Max Caption Length")
72
+ temp_input = gr.Slider(0.1, 2.0, value=1.0, step=0.1, label="Temperature")
73
+
74
+ submit_btn = gr.Button("Generate Caption", variant="primary")
75
+
76
+ with gr.Column(scale=1):
77
+ output_text = gr.Textbox(label="Generated Output", lines=4)
78
+
79
+ submit_btn.click(
80
+ fn=generate_caption,
81
+ inputs=[image_input, strategy_input, beam_size_input, max_len_input, temp_input],
82
+ outputs=output_text
83
+ )
84
+
85
+ if __name__ == "__main__":
86
+ # Run the Gradio app (defaults to port 7860, binds to 127.0.0.1 locally so the link works on Windows)
87
+ interface.launch(server_port=7860, share=False)
checkpoints/best_model.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:398bc16b8f91a41ca017bb24dd3217b3cebf557c7dedf4b90edfdbacfa521dc3
3
+ size 152183294
checkpoints/config.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "data": {
3
+ "images_dir": "data/raw/Images",
4
+ "captions_file": "data/raw/captions.txt",
5
+ "features_dir": "data/processed",
6
+ "vocab_path": "data/processed/vocab.json",
7
+ "splits_path": "data/processed/splits.json",
8
+ "min_word_freq": 3,
9
+ "max_caption_len": 35
10
+ },
11
+ "model": {
12
+ "encoder": "resnet50",
13
+ "embed_dim": 256,
14
+ "hidden_dim": 512,
15
+ "num_layers": 1,
16
+ "dropout": 0.5
17
+ },
18
+ "training": {
19
+ "batch_size": 64,
20
+ "num_epochs": 30,
21
+ "learning_rate": 0.001,
22
+ "weight_decay": 0.0001,
23
+ "patience": 5,
24
+ "teacher_forcing": 1.0,
25
+ "grad_clip": 5.0,
26
+ "device": "cuda",
27
+ "num_workers": 2
28
+ },
29
+ "checkpoint": {
30
+ "save_dir": "checkpoints",
31
+ "save_best": true
32
+ }
33
+ }
checkpoints/model.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1425b59931c738faecd6142f98d6f8c5614a56b1b39d44597cc3885f2ebf9fe1
3
+ size 113609091
checkpoints/training_history.png ADDED
checkpoints/vocab.json ADDED
The diff for this file is too large to render. See raw diff
 
configs/inference_config.yaml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ model:
2
+ checkpoint_dir: "checkpoints"
3
+ device: "cuda"
4
+
5
+ inference:
6
+ strategy: "beam"
7
+ beam_size: 3
8
+ max_length: 20
9
+ temperature: 1.0
10
+
11
+ hub:
12
+ repo_id: "username/flickr8k-image-captioning"
configs/train_config.yaml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ data:
2
+ images_dir: "data/raw/Images"
3
+ captions_file: "data/raw/captions.txt"
4
+ features_dir: "data/processed"
5
+ vocab_path: "data/processed/vocab.json"
6
+ splits_path: "data/processed/splits.json"
7
+ min_word_freq: 3
8
+ max_caption_len: 35
9
+
10
+ model:
11
+ encoder: "resnet50"
12
+ embed_dim: 256
13
+ hidden_dim: 512
14
+ num_layers: 1
15
+ dropout: 0.5
16
+
17
+ training:
18
+ batch_size: 64
19
+ num_epochs: 30
20
+ learning_rate: 0.001
21
+ weight_decay: 0.0001
22
+ patience: 5
23
+ teacher_forcing: 1.0
24
+ grad_clip: 5.0
25
+ device: "cuda"
26
+ num_workers: 2
27
+
28
+ checkpoint:
29
+ save_dir: "checkpoints"
30
+ save_best: true
requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ torch>=2.1.0
2
+ torchvision>=0.16.0
3
+ safetensors>=0.4.1
4
+ huggingface-hub>=0.20.0
5
+ Pillow>=10.1.0
6
+ numpy>=1.24.3
7
+ nltk>=3.8.1
8
+ rouge-score>=0.1.2
9
+ streamlit>=1.30.0
10
+ pyyaml>=6.0.1
11
+ tqdm>=4.66.0
12
+ matplotlib>=3.7.0
13
+ gradio>=4.0.0