ProfRom commited on
Commit
96cd726
·
verified ·
1 Parent(s): f5d065f

Meslie - Final submission

Browse files
Files changed (1) hide show
  1. app.py +132 -50
app.py CHANGED
@@ -1,60 +1,142 @@
1
  import gradio as gr
 
2
  import torch
3
- from transformers import (
4
- BlipProcessor,
5
- BlipForConditionalGeneration,
6
- AutoTokenizer,
7
- AutoModelForSeq2SeqLM
 
 
 
8
  )
9
 
10
- # Image captioning model
11
- caption_processor = BlipProcessor.from_pretrained("Salesforce/blip-image-captioning-base")
12
- caption_model = BlipForConditionalGeneration.from_pretrained("Salesforce/blip-image-captioning-base")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
- # English-to-Spanish translation model
15
- translation_tokenizer = AutoTokenizer.from_pretrained("Helsinki-NLP/opus-mt-en-es")
16
- translation_model = AutoModelForSeq2SeqLM.from_pretrained("Helsinki-NLP/opus-mt-en-es")
 
 
 
 
 
 
 
17
 
18
- def analyze_image(image):
19
- if image is None:
20
- return "Please upload an image first.", ""
21
 
 
22
  try:
23
- # Generate English caption
24
- inputs = caption_processor(image, return_tensors="pt")
25
- caption_ids = caption_model.generate(**inputs, max_new_tokens=40)
26
- english_caption = caption_processor.decode(caption_ids[0], skip_special_tokens=True)
27
-
28
- # Translate caption to Spanish
29
- translation_inputs = translation_tokenizer(
30
- english_caption,
31
- return_tensors="pt",
32
- padding=True
33
- )
34
- translation_ids = translation_model.generate(**translation_inputs, max_new_tokens=60)
35
- spanish_caption = translation_tokenizer.decode(
36
- translation_ids[0],
37
- skip_special_tokens=True
38
- )
39
-
40
- return english_caption, spanish_caption
41
-
42
- except Exception as e:
43
- return f"Error: {str(e)}", ""
44
-
45
- demo = gr.Interface(
46
- fn=analyze_image,
47
- inputs=gr.Image(type="pil", label="Upload an Image"),
48
- outputs=[
49
- gr.Textbox(label="English Image Caption"),
50
- gr.Textbox(label="Spanish Translation")
51
- ],
52
- title="Multimodal Image Captioning and Spanish Translation Tool",
53
- description=(
54
- "This app uses two AI models. First, it uses a vision-language model to describe "
55
- "an uploaded image in English. Then, it uses a language translation model to translate "
56
- "that caption into Spanish."
57
  )
58
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
- demo.launch()
 
 
1
  import gradio as gr
2
+ from transformers import pipeline, AutoTokenizer, AutoModelForSeq2SeqLM
3
  import torch
4
+ import pypdf
5
+ import re
6
+
7
+ # NER Model: Standard setup with aggregation
8
+ skill_pipe = pipeline(
9
+ "token-classification",
10
+ model="ihk/skillner",
11
+ aggregation_strategy="simple"
12
  )
13
 
14
+ # LLM: Flan-T5-Large
15
+ model_id = "google/flan-t5-large"
16
+ tokenizer = AutoTokenizer.from_pretrained(model_id)
17
+ model = AutoModelForSeq2SeqLM.from_pretrained(model_id)
18
+
19
+ def extract_text_from_pdf(file_path):
20
+ text = ""
21
+ try:
22
+ with open(file_path, "rb") as f:
23
+ reader = pypdf.PdfReader(f)
24
+ for page in reader.pages:
25
+ content = page.extract_text()
26
+ if content:
27
+ text += content + "\n"
28
+ except Exception:
29
+ return ""
30
+ return text
31
 
32
+ def analyze_profile(file_obj, text_input):
33
+ resume_text = ""
34
+ if file_obj is not None:
35
+ if file_obj.name.endswith('.pdf'):
36
+ resume_text = extract_text_from_pdf(file_obj.name)
37
+ else:
38
+ with open(file_obj.name, "r", encoding="utf-8") as f:
39
+ resume_text = f.read()
40
+ else:
41
+ resume_text = text_input
42
 
43
+ if not resume_text.strip():
44
+ return "Please provide a resume.", "N/A"
 
45
 
46
+ # Step 1: Skill Extraction & Cleaning
47
  try:
48
+ results = skill_pipe(resume_text[:2000])
49
+ raw_skills = [res['word'].strip() for res in results if 'SKILL' in res['entity_group']]
50
+
51
+ clean_skills = []
52
+ for s in raw_skills:
53
+ s = s.replace("##", "")
54
+ # Removing non-alphabetic noise
55
+ s = re.sub(r'^[^a-zA-Z]+|[^a-zA-Z]+$', '', s)
56
+ if len(s) > 2:
57
+ clean_skills.append(s)
58
+
59
+ skills = sorted(list(set(clean_skills)))
60
+ except Exception:
61
+ return "Extraction failed.", "N/A"
62
+
63
+ if not skills:
64
+ return "No skills detected.", "Try a more detailed resume."
65
+
66
+ skills_str = ", ".join(skills)
67
+
68
+ # Step 2: Few-Shot Prompting
69
+
70
+ few_shot_prompt = (
71
+ "Example:\n"
72
+ "Skills: Python, SQL, AWS, Spark\n"
73
+ "Certifications:\n"
74
+ "1. AWS Certified Solutions Architect - Professional\n"
75
+ "2. Databricks Certified Data Engineer Professional\n"
76
+ "3. Google Professional Data Engineer\n\n"
77
+ f"Actual Task:\n"
78
+ f"Skills: {skills_str}\n"
79
+ "Certifications:\n"
80
+ "1."
 
81
  )
82
+
83
+ inputs = tokenizer(few_shot_prompt, return_tensors="pt", truncation=True, max_length=512)
84
+
85
+
86
+ outputs = model.generate(
87
+ **inputs,
88
+ max_new_tokens=80,
89
+ num_beams=4, # Beam search is logical/less random
90
+ repetition_penalty=1.2, # Lower penalty avoids hallucinations
91
+ no_repeat_ngram_size=2, # Prevents word-looping
92
+ early_stopping=True
93
+ )
94
+
95
+ roadmap = tokenizer.decode(outputs[0], skip_special_tokens=True)
96
+
97
+ # Cleaning the output to ensure it starts with our '1.'
98
+ if not roadmap.startswith("1."):
99
+ roadmap = "1. " + roadmap
100
+
101
+ return skills_str, roadmap
102
+
103
+ # Documentation
104
+ arch_doc = """
105
+ ### System Architecture
106
+ This system utilizes a three-stage pipeline for document intelligence. I integrated pypdf for raw document ingestion, followed by a JobBERT model fine-tuned for professional skill extraction. For the final reasoning layer, I used Flan-T5-Large to perform a gap analysis and generate an upskilling roadmap.
107
+ """
108
+
109
+ eval_doc = """
110
+ ### Challenges & Solutions
111
+ Fragmentation: To fix sub-word tokenization (e.g., "Py" + "##spark"), I implemented an Aggregation Strategy and custom Regex cleaning.
112
+ Hallucinations: Early testing showed model "parrotting." I solved this using Few-Shot In-Context Learning and Beam Search (num_beams = 4‬) to ensure factual certification names.
113
+ """
114
+
115
+ ethics_doc = """
116
+ ### Responsible AI & Ethics
117
+ Bias Mitigation: The model is "feature-restricted"—the LLM only sees technical skills, blinding it to demographic data like names or gender.
118
+ Data Privacy: This application uses transient processing; all document data exists only in volatile memory and is discarded immediately after the session ends.
119
+ """
120
+
121
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
122
+ gr.Markdown("# Advanced Resume Intelligence")
123
+
124
+ with gr.Row():
125
+ with gr.Column():
126
+ file_in = gr.File(label="Upload Resume (PDF/TXT)", file_types=[".pdf", ".txt"])
127
+ txt_in = gr.Textbox(label="Or Paste Text", lines=5)
128
+ btn = gr.Button("Analyze Profile", variant="primary")
129
+
130
+ with gr.Column():
131
+ skill_out = gr.Textbox(label="Identified Skills")
132
+ advice_out = gr.Textbox(label="AI Training Roadmap")
133
+
134
+ btn.click(fn=analyze_profile, inputs=[file_in, txt_in], outputs=[skill_out, advice_out])
135
+
136
+ with gr.Accordion("Project Documentation", open=False):
137
+ gr.Markdown(arch_doc)
138
+ gr.Markdown(eval_doc)
139
+ gr.Markdown(ethics_doc)
140
 
141
+ if __name__ == "__main__":
142
+ demo.launch()