Pro-Coder commited on
Commit
6b52e57
·
verified ·
1 Parent(s): 888b377

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +96 -7
  2. app.py +205 -0
  3. requirements.txt +5 -0
README.md CHANGED
@@ -1,14 +1,103 @@
1
  ---
2
- title: Skin Ai
3
- emoji: 🌍
4
- colorFrom: purple
5
- colorTo: indigo
6
  sdk: gradio
7
- sdk_version: 6.22.0
8
- python_version: '3.12'
9
  app_file: app.py
10
  pinned: false
11
  license: apache-2.0
12
  ---
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Skin Disease Detection Demo
3
+ emoji: 🩺
4
+ colorFrom: blue
5
+ colorTo: pink
6
  sdk: gradio
7
+ sdk_version: 4.44.0
 
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
11
  ---
12
 
13
+ # 🩺 Skin Disease Detection — Demo
14
+
15
+ A demo Space where users can:
16
+ - **Upload a skin image** (or click a bundled test image) and get an AI classifier's
17
+ prediction across common skin-lesion categories, plus a plain-language explanation
18
+ from an LLM.
19
+ - **Chat with an assistant** about skin-health topics in general terms.
20
+
21
+ > ⚠️ **This is an educational demo, not a medical device.** It does not diagnose
22
+ > anyone. Always see a licensed dermatologist or doctor for real concerns.
23
+
24
+ ## How it works
25
+
26
+ | Component | What it does | Default model |
27
+ |---|---|---|
28
+ | Image classifier | Predicts a skin-lesion category from a photo | [`Anwarkh1/Skin_Cancer-Image_Classification`](https://huggingface.co/Anwarkh1/Skin_Cancer-Image_Classification) (ViT, trained on a HAM10000-derived dataset) |
29
+ | Chat / explanation | Turns predictions into plain language, answers general questions | `HuggingFaceH4/zephyr-7b-beta` via the Hugging Face Inference API |
30
+
31
+ Both model IDs are configurable via Space variables — see below.
32
+
33
+ ## 🚀 Deploy in 3 steps
34
+
35
+ 1. **Create a new Space**
36
+ Go to [huggingface.co/new-space](https://huggingface.co/new-space) →
37
+ choose **Gradio** as the SDK → CPU basic hardware is fine for the classifier.
38
+
39
+ 2. **Upload these files**
40
+ Upload `app.py`, `requirements.txt`, this `README.md`, and the `examples/`
41
+ folder (if you generated test images — see below) to the Space repo,
42
+ either via the web UI ("Add file") or:
43
+ ```bash
44
+ git clone https://huggingface.co/spaces/<your-username>/<your-space-name>
45
+ cp -r app.py requirements.txt README.md examples <your-space-name>/
46
+ cd <your-space-name>
47
+ git add . && git commit -m "Initial commit" && git push
48
+ ```
49
+
50
+ 3. **Add an `HF_TOKEN` secret (to enable the chatbot)**
51
+ In your Space → **Settings → Variables and secrets** → **New secret**:
52
+ - Name: `HF_TOKEN`
53
+ - Value: a Hugging Face access token (create one at
54
+ [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens),
55
+ "Read" scope is enough)
56
+
57
+ Without this secret, image classification still works — only the
58
+ LLM-generated explanations and the chat tab need the token.
59
+
60
+ That's it — the Space will build and the app will be live.
61
+
62
+ ## 🖼️ Adding test images (optional but recommended)
63
+
64
+ So users have something to click without needing their own photo, generate a
65
+ few sample images once, locally, before you push:
66
+
67
+ ```bash
68
+ pip install datasets pillow
69
+ python scripts/download_examples.py
70
+ ```
71
+
72
+ This saves a handful of JPEGs into `examples/`. Include that folder when you
73
+ push to your Space. You can also just drop in your own sample `.jpg`/`.png`
74
+ files instead.
75
+
76
+ ## ⚙️ Configuration
77
+
78
+ Set these as Space **variables** (not secret, unless noted) to customize:
79
+
80
+ - `IMAGE_MODEL_ID` — any Hugging Face image-classification model compatible
81
+ with `transformers.pipeline("image-classification", ...)`.
82
+ - `CHAT_MODEL_ID` — any chat-completion-capable model available via the HF
83
+ Inference API.
84
+ - `HF_TOKEN` *(secret)* — required for the chat tab and the LLM explanations.
85
+
86
+ ## 🩹 Limitations & responsible use
87
+
88
+ - The bundled classifier was trained on a research dataset (HAM10000-derived
89
+ dermatoscopic images) and has real, published accuracy limits — treat its
90
+ output as a talking point, not a result.
91
+ - Performance depends heavily on image quality, lighting, and skin tone
92
+ representation in the training data.
93
+ - This app must not be used as a substitute for professional medical
94
+ evaluation, and the chatbot is instructed to avoid giving diagnoses,
95
+ treatments, or dosages.
96
+
97
+ ## Local development
98
+
99
+ ```bash
100
+ pip install -r requirements.txt
101
+ export HF_TOKEN=your_token_here # optional, for chat
102
+ python app.py
103
+ ```
app.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Skin Disease Detection — Demo
3
+ ------------------------------
4
+ A Gradio app for Hugging Face Spaces with two tabs:
5
+ 1. Image Diagnosis — upload (or pick a test image) and get a classifier
6
+ prediction plus a plain-language, LLM-generated explanation.
7
+ 2. Chat Assistant — a cautious chatbot that discusses skin-health topics
8
+ in general terms.
9
+
10
+ IMPORTANT: This is an educational / demo tool only. It is NOT a medical
11
+ device and does NOT provide real diagnoses. See DISCLAIMER below.
12
+ """
13
+
14
+ import os
15
+
16
+ import gradio as gr
17
+ from transformers import pipeline
18
+ from huggingface_hub import InferenceClient
19
+
20
+ # ---------------------------------------------------------------------------
21
+ # Config — override any of these via Space "Variables and secrets"
22
+ # ---------------------------------------------------------------------------
23
+ IMAGE_MODEL_ID = os.environ.get("IMAGE_MODEL_ID", "Anwarkh1/Skin_Cancer-Image_Classification")
24
+ CHAT_MODEL_ID = os.environ.get("CHAT_MODEL_ID", "HuggingFaceH4/zephyr-7b-beta")
25
+ HF_TOKEN = os.environ.get("HF_TOKEN") # add as a Space SECRET to enable the chatbot
26
+
27
+ DISCLAIMER = (
28
+ "⚠️ **Demo only — not a medical device.** This tool does not provide a real "
29
+ "diagnosis. Always consult a licensed dermatologist or doctor about any skin concern, "
30
+ "especially a spot that is new, changing, bleeding, or growing."
31
+ )
32
+
33
+ # Friendly descriptions for the HAM10000-style label set used by the default model
34
+ CLASS_INFO = {
35
+ "actinic keratoses": "A rough, scaly patch caused by sun damage; can occasionally progress to skin cancer if untreated.",
36
+ "basal cell carcinoma": "The most common type of skin cancer; usually slow-growing and rarely spreads.",
37
+ "benign keratosis-like lesions": "Non-cancerous growths such as seborrheic keratoses or solar lentigines.",
38
+ "dermatofibroma": "A common benign skin nodule, often on the legs; usually harmless.",
39
+ "melanocytic nevi": "Ordinary moles; almost always benign.",
40
+ "melanoma": "The most serious type of skin cancer; early evaluation matters.",
41
+ "vascular lesions": "Blood-vessel-related marks such as angiomas; usually benign.",
42
+ }
43
+
44
+ SYSTEM_PROMPT = (
45
+ "You are a friendly, cautious skin-health assistant inside a DEMO application. "
46
+ "You are NOT a doctor and must never claim to diagnose anyone. "
47
+ "You can explain general skin-condition concepts, explain what classifier labels mean, "
48
+ "and encourage users to see a licensed dermatologist for anything concerning. "
49
+ "Never give a definitive diagnosis, never prescribe treatment or dosages. "
50
+ "If a user describes an urgent symptom (rapid growth, bleeding, severe pain, sudden "
51
+ "changes to a mole), tell them to seek in-person medical care promptly. "
52
+ "Keep answers concise — 3 to 6 sentences."
53
+ )
54
+
55
+ # ---------------------------------------------------------------------------
56
+ # Lazy-loaded models (so the Space boots fast and only loads on first use)
57
+ # ---------------------------------------------------------------------------
58
+ _classifier = None
59
+
60
+
61
+ def get_classifier():
62
+ global _classifier
63
+ if _classifier is None:
64
+ _classifier = pipeline("image-classification", model=IMAGE_MODEL_ID)
65
+ return _classifier
66
+
67
+
68
+ def get_chat_client():
69
+ if not HF_TOKEN:
70
+ return None
71
+ return InferenceClient(model=CHAT_MODEL_ID, token=HF_TOKEN)
72
+
73
+
74
+ # ---------------------------------------------------------------------------
75
+ # Image diagnosis tab
76
+ # ---------------------------------------------------------------------------
77
+ def diagnose_image(image):
78
+ """Run the image classifier and, if a chat model is configured, ask it
79
+ for a short plain-language explanation of the top prediction."""
80
+ if image is None:
81
+ return "Please upload an image or pick one of the test images first."
82
+
83
+ try:
84
+ clf = get_classifier()
85
+ preds = clf(image, top_k=5)
86
+ except Exception as e:
87
+ return f"**Model error:** {e}\n\nMake sure `IMAGE_MODEL_ID` is a valid image-classification model."
88
+
89
+ lines = ["### 🔬 Classifier prediction (demo only — not a real diagnosis)\n"]
90
+ for p in preds:
91
+ label = p["label"]
92
+ score = p["score"] * 100
93
+ info = CLASS_INFO.get(label.lower(), "")
94
+ info_txt = f" \n _{info}_" if info else ""
95
+ lines.append(f"- **{label}** — {score:.1f}%{info_txt}")
96
+
97
+ client = get_chat_client()
98
+ if client is not None:
99
+ top = preds[0]["label"]
100
+ others = ", ".join(p["label"] for p in preds[1:3])
101
+ prompt = (
102
+ f"An image classifier in a DEMO app predicted the top possible skin-lesion "
103
+ f"category as '{top}' ({preds[0]['score']*100:.1f}% confidence), with other "
104
+ f"candidates including {others}. In 3-4 short, plain-language sentences, explain "
105
+ f"generally what the top category means, make clear this is not a diagnosis, and "
106
+ f"suggest seeing a dermatologist, especially if the spot is new, changing, or bleeding."
107
+ )
108
+ try:
109
+ resp = client.chat_completion(
110
+ messages=[
111
+ {"role": "system", "content": SYSTEM_PROMPT},
112
+ {"role": "user", "content": prompt},
113
+ ],
114
+ max_tokens=250,
115
+ )
116
+ explanation = resp.choices[0].message.content
117
+ lines.append(f"\n### 🤖 Assistant explanation\n{explanation}")
118
+ except Exception as e:
119
+ lines.append(f"\n_(Chat explanation unavailable: {e})_")
120
+ else:
121
+ lines.append(
122
+ "\n_Add an `HF_TOKEN` secret to this Space to also get a plain-language "
123
+ "explanation from the chat model here._"
124
+ )
125
+
126
+ lines.append(f"\n---\n{DISCLAIMER}")
127
+ return "\n".join(lines)
128
+
129
+
130
+ # ---------------------------------------------------------------------------
131
+ # Chat tab
132
+ # ---------------------------------------------------------------------------
133
+ def chat_respond(message, history):
134
+ client = get_chat_client()
135
+ if client is None:
136
+ return (
137
+ "⚠️ Chat isn't configured yet. Add an `HF_TOKEN` secret to this Space "
138
+ "(Settings → Variables and secrets) to enable the chatbot."
139
+ )
140
+
141
+ messages = [{"role": "system", "content": SYSTEM_PROMPT}]
142
+ for turn in history:
143
+ # supports both the legacy (user, bot) tuple format and the
144
+ # newer {"role": ..., "content": ...} messages format
145
+ if isinstance(turn, dict):
146
+ messages.append(turn)
147
+ else:
148
+ user_msg, bot_msg = turn
149
+ messages.append({"role": "user", "content": user_msg})
150
+ if bot_msg:
151
+ messages.append({"role": "assistant", "content": bot_msg})
152
+ messages.append({"role": "user", "content": message})
153
+
154
+ try:
155
+ resp = client.chat_completion(messages=messages, max_tokens=400)
156
+ return resp.choices[0].message.content
157
+ except Exception as e:
158
+ return f"Sorry, I hit an error talking to the model: {e}"
159
+
160
+
161
+ # ---------------------------------------------------------------------------
162
+ # UI
163
+ # ---------------------------------------------------------------------------
164
+ EXAMPLES_DIR = "examples"
165
+ example_images = []
166
+ if os.path.isdir(EXAMPLES_DIR):
167
+ example_images = [
168
+ os.path.join(EXAMPLES_DIR, f)
169
+ for f in sorted(os.listdir(EXAMPLES_DIR))
170
+ if f.lower().endswith((".jpg", ".jpeg", ".png"))
171
+ ]
172
+
173
+ with gr.Blocks(title="Skin Disease Detection — Demo") as demo:
174
+ gr.Markdown("# 🩺 Skin Disease Detection — Demo")
175
+ gr.Markdown(DISCLAIMER)
176
+
177
+ with gr.Tab("📷 Image Diagnosis"):
178
+ with gr.Row():
179
+ with gr.Column():
180
+ img_in = gr.Image(type="pil", label="Upload a skin image")
181
+ if example_images:
182
+ gr.Examples(examples=example_images, inputs=img_in, label="Or try a test image")
183
+ else:
184
+ gr.Markdown(
185
+ "_No bundled test images found. Run `scripts/download_examples.py` "
186
+ "before deploying, or just upload your own image._"
187
+ )
188
+ analyze_btn = gr.Button("Analyze image", variant="primary")
189
+ with gr.Column():
190
+ result_md = gr.Markdown()
191
+ analyze_btn.click(diagnose_image, inputs=img_in, outputs=result_md)
192
+
193
+ with gr.Tab("💬 Chat Assistant"):
194
+ gr.ChatInterface(
195
+ fn=chat_respond,
196
+ description="Ask general questions about skin conditions. " + DISCLAIMER,
197
+ )
198
+
199
+ gr.Markdown(
200
+ "---\nBuilt with 🤗 Transformers + Gradio. "
201
+ f"Image model: `{IMAGE_MODEL_ID}` · Chat model: `{CHAT_MODEL_ID}`"
202
+ )
203
+
204
+ if __name__ == "__main__":
205
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio>=4.44.0
2
+ transformers>=4.42.0
3
+ torch>=2.2.0
4
+ pillow>=10.0.0
5
+ huggingface_hub>=0.24.0