File size: 5,710 Bytes
d7c8166
 
0225bda
 
f02a5fd
 
 
 
 
 
 
 
 
 
 
ae692d1
f02a5fd
 
 
 
 
 
 
d7c8166
dd8438e
0e07292
 
 
 
 
 
 
 
 
 
dd8438e
 
 
 
f02a5fd
dd8438e
0e07292
dd8438e
0e07292
f02a5fd
 
 
 
 
 
0e07292
dd8438e
f02a5fd
dd8438e
 
f02a5fd
0e07292
 
 
 
dd8438e
0e07292
 
 
dd8438e
0e07292
 
dd8438e
0e07292
dd8438e
 
 
f02a5fd
dd8438e
0e07292
 
dd8438e
 
0e07292
 
f02a5fd
dd8438e
 
 
 
0e07292
dd8438e
 
 
0e07292
 
 
 
 
 
f02a5fd
 
dd8438e
 
 
 
 
0e07292
dd8438e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f02a5fd
 
ae692d1
 
dd8438e
 
 
 
 
 
 
0e07292
dd8438e
 
 
0e07292
 
dd8438e
0e07292
 
d7c8166
dd8438e
d7c8166
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import gradio as gr

from predictor import predict

# πŸ“Œ CUSTOM CSS
css_code = """
#footer-container {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1000;
    background-color: var(--background-fill-primary);
    padding: var(--spacing-md);
    border-top: 1px solid var(--border-color-primary);
    text-align: center;
}

.gradio-container {
    padding-bottom: 70px !important;
}
"""


def update_inputs(mode: str):
    if mode == "Multimodal":
        return gr.Textbox(visible=True), gr.Image(visible=True)
    elif mode == "Text Only":
        return gr.Textbox(visible=True), gr.Image(visible=False)
    elif mode == "Image Only":
        return gr.Textbox(visible=False), gr.Image(visible=True)
    else:  # Default case
        return gr.Textbox(visible=True), gr.Image(visible=True)


# πŸ“Œ USER INTERFACE
with gr.Blocks(
    title="Multimodal Product Classification",
    theme=gr.themes.Ocean(),
    css=css_code,
) as demo:
    with gr.Tabs():
        # πŸ“Œ APP TAB
        with gr.TabItem("App"):
            gr.Markdown("""
                <div style="text-align: center;">
                    <h1>πŸ›οΈ Multimodal Product Classification</h1>
                </div>
                <br><br>
                """)

            with gr.Row(equal_height=True):
                # πŸ“Œ CLASSIFICATION INPUTS COLUMN
                with gr.Column():
                    with gr.Column():
                        gr.Markdown("## πŸ“ Classification Inputs")

                        mode_radio = gr.Radio(
                            choices=["Multimodal", "Text Only", "Image Only"],
                            value="Multimodal",
                            label="Choose Classification Mode:",
                        )

                        text_input = gr.Textbox(
                            label="Product Description:",
                            placeholder="e.g., Apple iPhone 15 Pro Max 256GB",
                        )

                        image_input = gr.Image(
                            label="Product Image",
                            type="filepath",
                            visible=True,
                            height=350,
                            width="100%",
                        )

                        classify_button = gr.Button(
                            "✨ Classify Product", variant="primary"
                        )

                # πŸ“Œ RESULTS COLUMN
                with gr.Column():
                    with gr.Column():
                        gr.Markdown("## πŸ“Š Results")

                        gr.Markdown(
                            """**πŸ’‘ How to use this app**

                            This app classifies a product based on its description and image.
                            - **Multimodal:** Uses both text and image for the most accurate prediction.
                            - **Text Only:** Uses only the product description.
                            - **Image Only:** Uses only the product image.
                            """
                        )

                        gr.HTML("<hr>")

                        output_label = gr.Label(
                            label="Predict category", num_top_classes=5
                        )

        # πŸ“Œ ABOUT TAB
        with gr.TabItem("About"):
            gr.Markdown("""
## About This Project

- This project is an image classification app powered by a Convolutional Neural Network (CNN).
- Simply upload an image, and the app predicts its category from over 1,000 classes using a pre-trained ResNet50 model.
- Originally developed as a multi-service ML system (FastAPI + Redis + Streamlit), this version has been adapted into a single Streamlit app for lightweight, cost-effective deployment on Hugging Face Spaces.

## Model & Description
- Model: ResNet50 (pre-trained on the ImageNet dataset with 1,000+ categories).
- Pipeline: Images are resized, normalized, and passed to the model.
- Output: The app displays the Top prediction with confidence score.
ResNet50 is widely used in both research and production, making it an excellent showcase of deep learning capabilities and transferable ML skills.
""")

        # πŸ“Œ MODEL TAB
        with gr.TabItem("Model"):
            gr.Markdown("""
## Original Architecture

- FastAPI β†’ REST API for image processing
- Redis β†’ Message broker for service communication
- Streamlit β†’ Interactive web UI
- TensorFlow β†’ Deep learning inference engine
- Locust β†’ Load testing & benchmarking
- Docker Compose β†’ Service orchestration

## Simplified Version
                        
- Streamlit only β†’ UI and model combined in a single app
- TensorFlow (ResNet50) β†’ Core prediction engine
- Docker β†’ Containerized for Hugging Face Spaces deployment
This evolution demonstrates the ability to design a scalable microservices system and also adapt it into a lightweight single-service solution for cost-effective demos.
""")

    # πŸ“Œ FOOTER
    # gr.HTML("<hr>")
    with gr.Row(elem_id="footer-container"):
        gr.HTML("""
<div>
        <b>Connect with me:</b> πŸ’Ό <a href="https://www.linkedin.com/in/alex-turpo/" target="_blank">LinkedIn</a> β€’ 
        🐱 <a href="https://github.com/iBrokeTheCode" target="_blank">GitHub</a> β€’ 
        πŸ€— <a href="https://huggingface.co/iBrokeTheCode" target="_blank">Hugging Face</a>
    </div>
""")

    # πŸ“Œ EVENT LISTENERS
    mode_radio.change(
        fn=update_inputs,
        inputs=mode_radio,
        outputs=[text_input, image_input],
    )

    classify_button.click(
        fn=predict, inputs=[mode_radio, text_input, image_input], outputs=output_label
    )


demo.launch()