Spaces:
Sleeping
Sleeping
Commit ·
e379153
1
Parent(s): 63c4006
modified for my resume
Browse files- app.py +89 -113
- milestones.md +0 -18
- templates/Arbab - Resume.tex +220 -0
app.py
CHANGED
|
@@ -1,13 +1,10 @@
|
|
| 1 |
"""
|
| 2 |
Resume Customizer Application.
|
| 3 |
-
This application customizes resumes
|
| 4 |
-
Works both for local development and Hugging Face Spaces deployment.
|
| 5 |
"""
|
| 6 |
|
| 7 |
import os
|
| 8 |
import gradio as gr
|
| 9 |
-
import tempfile
|
| 10 |
-
import shutil
|
| 11 |
import subprocess
|
| 12 |
from openai import OpenAI
|
| 13 |
import dotenv
|
|
@@ -16,170 +13,153 @@ import dotenv
|
|
| 16 |
dotenv.load_dotenv()
|
| 17 |
|
| 18 |
# Constants
|
| 19 |
-
DEFAULT_TEMPLATE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates", "
|
| 20 |
OUTPUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "outputs")
|
| 21 |
|
| 22 |
# Ensure output directory exists
|
| 23 |
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
| 24 |
|
| 25 |
def get_openai_client():
|
| 26 |
-
"""
|
| 27 |
-
|
| 28 |
-
Try to get it from both Hugging Face Spaces and local .env file.
|
| 29 |
-
|
| 30 |
-
Returns:
|
| 31 |
-
OpenAI: The OpenAI client
|
| 32 |
-
"""
|
| 33 |
-
# First try to get the API key from Hugging Face Spaces secrets
|
| 34 |
-
api_key = os.environ.get("OPENAI_API_KEY")
|
| 35 |
-
|
| 36 |
-
# If not found, try to get it from .env file
|
| 37 |
-
if not api_key:
|
| 38 |
-
api_key = os.getenv("OPENAI_API_KEY")
|
| 39 |
|
| 40 |
if not api_key:
|
| 41 |
-
raise ValueError("OPENAI_API_KEY environment variable is not set.
|
| 42 |
|
| 43 |
return OpenAI(api_key=api_key)
|
| 44 |
|
| 45 |
-
def
|
| 46 |
-
"""
|
| 47 |
-
Generate a 'Why Hire Me' section based on the job description using OpenAI.
|
| 48 |
-
|
| 49 |
-
Args:
|
| 50 |
-
job_description (str): The job description text
|
| 51 |
-
|
| 52 |
-
Returns:
|
| 53 |
-
str: LaTeX formatted 'Why Hire Me' section
|
| 54 |
-
"""
|
| 55 |
client = get_openai_client()
|
| 56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
# Create the prompt for OpenAI
|
| 58 |
prompt = f"""
|
| 59 |
-
|
| 60 |
-
The section should explain why the candidate is a good fit for the position.
|
| 61 |
-
Use LaTeX formatting with the '\\section*{{Why Hire Me}}' heading.
|
| 62 |
-
Keep it concise (3-5 paragraphs), professional, and highlight key qualifications that match the job.
|
| 63 |
-
Don't mention specific experience unless it's generic enough to apply to most professionals.
|
| 64 |
|
| 65 |
Job Description:
|
| 66 |
{job_description}
|
| 67 |
|
| 68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
"""
|
| 70 |
|
| 71 |
try:
|
| 72 |
response = client.chat.completions.create(
|
| 73 |
model="gpt-4o",
|
| 74 |
messages=[
|
| 75 |
-
{"role": "system", "content": "You are a professional resume writer who
|
| 76 |
{"role": "user", "content": prompt}
|
| 77 |
],
|
| 78 |
-
temperature=0.
|
| 79 |
-
max_tokens=
|
| 80 |
)
|
| 81 |
|
| 82 |
-
# Extract the generated
|
| 83 |
-
|
|
|
|
| 84 |
|
| 85 |
-
#
|
| 86 |
-
|
| 87 |
-
why_hire_me_text = "\\section*{Why Hire Me}\n" + why_hire_me_text
|
| 88 |
|
| 89 |
-
|
|
|
|
|
|
|
|
|
|
| 90 |
|
| 91 |
except Exception as e:
|
| 92 |
-
print(f"Error
|
| 93 |
-
|
| 94 |
-
return """\\section*{Why Hire Me}
|
| 95 |
-
Due to a technical issue, a personalized 'Why Hire Me' section could not be generated.
|
| 96 |
-
Please try again later or contact support for assistance."""
|
| 97 |
|
| 98 |
-
def
|
| 99 |
-
"""
|
| 100 |
-
Main function to customize resume based on job description.
|
| 101 |
-
|
| 102 |
-
Args:
|
| 103 |
-
job_description (str): The job description text
|
| 104 |
-
|
| 105 |
-
Returns:
|
| 106 |
-
tuple: (PDF path, Status message)
|
| 107 |
-
"""
|
| 108 |
try:
|
| 109 |
-
# Generate
|
| 110 |
-
|
| 111 |
|
| 112 |
-
#
|
| 113 |
-
with open(DEFAULT_TEMPLATE_PATH, "r") as f:
|
| 114 |
-
template_content = f.read()
|
| 115 |
-
|
| 116 |
-
# Replace the placeholder with the generated section
|
| 117 |
-
modified_content = template_content.replace("% WHY_HIRE_ME_SECTION", why_hire_me_section)
|
| 118 |
-
|
| 119 |
-
# Save the modified content to a new file
|
| 120 |
output_tex_path = os.path.join(OUTPUT_DIR, "customized_resume.tex")
|
| 121 |
with open(output_tex_path, "w") as f:
|
| 122 |
-
f.write(
|
| 123 |
|
| 124 |
-
# Convert to PDF
|
| 125 |
pdf_path = convert_to_pdf(output_tex_path)
|
| 126 |
if pdf_path:
|
| 127 |
-
return pdf_path,
|
| 128 |
else:
|
| 129 |
-
return None,
|
| 130 |
except Exception as e:
|
| 131 |
-
return None, f"Error
|
| 132 |
|
| 133 |
def convert_to_pdf(tex_path):
|
| 134 |
-
"""
|
| 135 |
-
Convert a LaTeX file to PDF using pdflatex.
|
| 136 |
-
|
| 137 |
-
Args:
|
| 138 |
-
tex_path (str): Path to the LaTeX file
|
| 139 |
-
|
| 140 |
-
Returns:
|
| 141 |
-
str: Path to the generated PDF file or None if conversion failed
|
| 142 |
-
"""
|
| 143 |
-
# Get the directory and filename
|
| 144 |
tex_dir = os.path.dirname(tex_path)
|
| 145 |
tex_filename = os.path.basename(tex_path)
|
| 146 |
|
| 147 |
-
# Change to the directory containing the tex file
|
| 148 |
original_dir = os.getcwd()
|
| 149 |
os.chdir(tex_dir)
|
| 150 |
|
| 151 |
try:
|
| 152 |
-
#
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
# Get the PDF path
|
| 164 |
-
pdf_filename = tex_filename.replace('.tex', '.pdf')
|
| 165 |
-
pdf_path = os.path.join(tex_dir, pdf_filename)
|
| 166 |
|
| 167 |
-
# Check
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
return None
|
| 172 |
except Exception as e:
|
| 173 |
-
|
|
|
|
| 174 |
finally:
|
| 175 |
-
# Change back to the original directory
|
| 176 |
os.chdir(original_dir)
|
| 177 |
|
| 178 |
# Define the Gradio interface
|
| 179 |
def create_interface():
|
| 180 |
with gr.Blocks(title="Resume Customizer") as app:
|
| 181 |
gr.Markdown("# Resume Customizer")
|
| 182 |
-
gr.Markdown("Enter a job description to generate a customized resume
|
| 183 |
|
| 184 |
with gr.Row():
|
| 185 |
with gr.Column():
|
|
@@ -196,7 +176,7 @@ def create_interface():
|
|
| 196 |
status_text = gr.Textbox(label="Status", interactive=False)
|
| 197 |
|
| 198 |
customize_btn.click(
|
| 199 |
-
fn=
|
| 200 |
inputs=[job_description],
|
| 201 |
outputs=[pdf_output, status_text]
|
| 202 |
)
|
|
@@ -205,11 +185,8 @@ def create_interface():
|
|
| 205 |
## How to Use
|
| 206 |
1. Paste a job description in the text area
|
| 207 |
2. Click "Customize Resume"
|
| 208 |
-
3. Wait for the AI to
|
| 209 |
4. Download the customized resume PDF
|
| 210 |
-
|
| 211 |
-
## Note
|
| 212 |
-
This app requires an OpenAI API key to be set up.
|
| 213 |
""")
|
| 214 |
|
| 215 |
return app
|
|
@@ -217,6 +194,5 @@ def create_interface():
|
|
| 217 |
# Create and launch the app
|
| 218 |
app = create_interface()
|
| 219 |
|
| 220 |
-
# For local development and Hugging Face Spaces compatibility
|
| 221 |
if __name__ == "__main__":
|
| 222 |
app.launch()
|
|
|
|
| 1 |
"""
|
| 2 |
Resume Customizer Application.
|
| 3 |
+
This application customizes resumes based on job descriptions.
|
|
|
|
| 4 |
"""
|
| 5 |
|
| 6 |
import os
|
| 7 |
import gradio as gr
|
|
|
|
|
|
|
| 8 |
import subprocess
|
| 9 |
from openai import OpenAI
|
| 10 |
import dotenv
|
|
|
|
| 13 |
dotenv.load_dotenv()
|
| 14 |
|
| 15 |
# Constants
|
| 16 |
+
DEFAULT_TEMPLATE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates", "Arbab - Resume.tex")
|
| 17 |
OUTPUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "outputs")
|
| 18 |
|
| 19 |
# Ensure output directory exists
|
| 20 |
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
| 21 |
|
| 22 |
def get_openai_client():
|
| 23 |
+
"""Get the OpenAI client with the API key from environment variables."""
|
| 24 |
+
api_key = os.environ.get("OPENAI_API_KEY") or os.getenv("OPENAI_API_KEY")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
if not api_key:
|
| 27 |
+
raise ValueError("OPENAI_API_KEY environment variable is not set.")
|
| 28 |
|
| 29 |
return OpenAI(api_key=api_key)
|
| 30 |
|
| 31 |
+
def customize_resume_with_llm(job_description):
|
| 32 |
+
"""Customize the entire resume based on the job description using OpenAI's LLM."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
client = get_openai_client()
|
| 34 |
|
| 35 |
+
# Read the original resume
|
| 36 |
+
with open(DEFAULT_TEMPLATE_PATH, "r") as f:
|
| 37 |
+
resume_content = f.read()
|
| 38 |
+
|
| 39 |
+
# Find the preamble end and content beginning
|
| 40 |
+
begin_document_index = resume_content.find("\\begin{document}")
|
| 41 |
+
if begin_document_index == -1:
|
| 42 |
+
raise ValueError("Could not find \\begin{document} in the resume template")
|
| 43 |
+
|
| 44 |
+
# Split the resume into preamble and content
|
| 45 |
+
preamble = resume_content[:begin_document_index + len("\\begin{document}")]
|
| 46 |
+
content = resume_content[begin_document_index + len("\\begin{document}"):]
|
| 47 |
+
|
| 48 |
+
# Find the end of the document
|
| 49 |
+
end_document_index = content.find("\\end{document}")
|
| 50 |
+
if end_document_index == -1:
|
| 51 |
+
raise ValueError("Could not find \\end{document} in the resume template")
|
| 52 |
+
|
| 53 |
+
# Extract just the content (without \end{document})
|
| 54 |
+
content = content[:end_document_index].strip()
|
| 55 |
+
|
| 56 |
# Create the prompt for OpenAI
|
| 57 |
prompt = f"""
|
| 58 |
+
You will rewrite a resume to tailor it for a specific job description.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
Job Description:
|
| 61 |
{job_description}
|
| 62 |
|
| 63 |
+
Original Resume Content:
|
| 64 |
+
{content}
|
| 65 |
+
|
| 66 |
+
Instructions:
|
| 67 |
+
1. Keep the SAME STRUCTURE and SECTION HEADINGS as the original resume.
|
| 68 |
+
2. Add a "Why Hire Me" section at the beginning, after the header/contact info but before Experience. Maximum 4 lines.
|
| 69 |
+
3. Customize/reword the Technical Skills, Experience, Projects, and other sections to emphasize relevant skills and experience.
|
| 70 |
+
4. Keep the same formatting, but highlight achievements that match the job description.
|
| 71 |
+
5. Do not invent new experiences or skills not in the original resume.
|
| 72 |
+
6. Maintain all section markers (like %-----------EXPERIENCE-----------------).
|
| 73 |
+
7. Return ONLY THE CONTENT between \begin{{document}} and \end{{document}} (do not include these tags).
|
| 74 |
+
8. Keep the header with name and contact information exactly the same.
|
| 75 |
+
whatever is related to the job description that you think should really catch the eye of the recruiter or the person who is going to look at the updated resume that is the hiring person , You should underline it in the LaTeX format. Remember not to overdo it and specifically do it with the terms which are exact match from the job description.
|
| 76 |
+
Basically we are preparing this resume for the person who will be hiring. So the final should be crisp and it should not be too divergent from the original resume and do not include additional information which does not already exist in my resume. So do not make stuff up. But you can make reasonable adjustments, for example if the job description mentions generative ai and my resume mentions large language models then you can also say generative ai in my resume. this is just one of the examples but keep it on the conservative side.
|
| 77 |
+
If the role is more applied and industry focused and does not mention about publications, you can skip publications and instead focus in why hire me section that i have multiple industry internships And most of my PhD projects have applied applications and give short and concise and crucial justification for that (you can remove the publications section). But if the role is more research focused you can focus on the research publication and mention that I have published in NeurIPS and WSCV and keep the final publication section as well .
|
| 78 |
+
FORMAT YOUR RESPONSE AS CLEAN LATEX CODE WITH NO EXPLANATIONS OR CODE BLOCKS.
|
| 79 |
"""
|
| 80 |
|
| 81 |
try:
|
| 82 |
response = client.chat.completions.create(
|
| 83 |
model="gpt-4o",
|
| 84 |
messages=[
|
| 85 |
+
{"role": "system", "content": "You are a professional resume writer who tailors resumes for job applications, while strictly maintaining the original LaTeX formatting and structure."},
|
| 86 |
{"role": "user", "content": prompt}
|
| 87 |
],
|
| 88 |
+
temperature=0.5,
|
| 89 |
+
max_tokens=2000
|
| 90 |
)
|
| 91 |
|
| 92 |
+
# Extract the generated content
|
| 93 |
+
customized_content = response.choices[0].message.content.strip()
|
| 94 |
+
print(customized_content)
|
| 95 |
|
| 96 |
+
# Clean any code block markers
|
| 97 |
+
customized_content = customized_content.replace("```latex", "").replace("```", "").strip()
|
|
|
|
| 98 |
|
| 99 |
+
# Reconstruct the full LaTeX document
|
| 100 |
+
customized_resume = f"{preamble}\n\n{customized_content}\n\n\\end{{document}}"
|
| 101 |
+
|
| 102 |
+
return customized_resume
|
| 103 |
|
| 104 |
except Exception as e:
|
| 105 |
+
print(f"Error customizing resume: {str(e)}")
|
| 106 |
+
return resume_content
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
+
def create_customized_resume(job_description):
|
| 109 |
+
"""Create a customized resume based on the job description."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
try:
|
| 111 |
+
# Generate customized resume content
|
| 112 |
+
customized_resume = customize_resume_with_llm(job_description)
|
| 113 |
|
| 114 |
+
# Save to file
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
output_tex_path = os.path.join(OUTPUT_DIR, "customized_resume.tex")
|
| 116 |
with open(output_tex_path, "w") as f:
|
| 117 |
+
f.write(customized_resume)
|
| 118 |
|
| 119 |
+
# Convert to PDF
|
| 120 |
pdf_path = convert_to_pdf(output_tex_path)
|
| 121 |
if pdf_path:
|
| 122 |
+
return pdf_path, "Resume successfully customized! Click to download."
|
| 123 |
else:
|
| 124 |
+
return None, "Failed to generate PDF. Check the logs for details."
|
| 125 |
except Exception as e:
|
| 126 |
+
return None, f"Error: {str(e)}"
|
| 127 |
|
| 128 |
def convert_to_pdf(tex_path):
|
| 129 |
+
"""Convert a LaTeX file to PDF using pdflatex."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
tex_dir = os.path.dirname(tex_path)
|
| 131 |
tex_filename = os.path.basename(tex_path)
|
| 132 |
|
|
|
|
| 133 |
original_dir = os.getcwd()
|
| 134 |
os.chdir(tex_dir)
|
| 135 |
|
| 136 |
try:
|
| 137 |
+
# Run pdflatex twice
|
| 138 |
+
for i in range(2):
|
| 139 |
+
result = subprocess.run(
|
| 140 |
+
['pdflatex', '-interaction=nonstopmode', tex_filename],
|
| 141 |
+
stdout=subprocess.PIPE,
|
| 142 |
+
stderr=subprocess.PIPE,
|
| 143 |
+
text=True
|
| 144 |
+
)
|
| 145 |
+
if result.returncode != 0:
|
| 146 |
+
print(f"pdflatex error: {result.stderr}")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
|
| 148 |
+
# Check output
|
| 149 |
+
pdf_path = os.path.join(tex_dir, tex_filename.replace('.tex', '.pdf'))
|
| 150 |
+
return os.path.abspath(pdf_path) if os.path.exists(pdf_path) else None
|
| 151 |
+
|
|
|
|
| 152 |
except Exception as e:
|
| 153 |
+
print(f"PDF conversion error: {str(e)}")
|
| 154 |
+
return None
|
| 155 |
finally:
|
|
|
|
| 156 |
os.chdir(original_dir)
|
| 157 |
|
| 158 |
# Define the Gradio interface
|
| 159 |
def create_interface():
|
| 160 |
with gr.Blocks(title="Resume Customizer") as app:
|
| 161 |
gr.Markdown("# Resume Customizer")
|
| 162 |
+
gr.Markdown("Enter a job description to generate a fully customized resume tailored to the position.")
|
| 163 |
|
| 164 |
with gr.Row():
|
| 165 |
with gr.Column():
|
|
|
|
| 176 |
status_text = gr.Textbox(label="Status", interactive=False)
|
| 177 |
|
| 178 |
customize_btn.click(
|
| 179 |
+
fn=create_customized_resume,
|
| 180 |
inputs=[job_description],
|
| 181 |
outputs=[pdf_output, status_text]
|
| 182 |
)
|
|
|
|
| 185 |
## How to Use
|
| 186 |
1. Paste a job description in the text area
|
| 187 |
2. Click "Customize Resume"
|
| 188 |
+
3. Wait for the AI to tailor your entire resume to match the job requirements
|
| 189 |
4. Download the customized resume PDF
|
|
|
|
|
|
|
|
|
|
| 190 |
""")
|
| 191 |
|
| 192 |
return app
|
|
|
|
| 194 |
# Create and launch the app
|
| 195 |
app = create_interface()
|
| 196 |
|
|
|
|
| 197 |
if __name__ == "__main__":
|
| 198 |
app.launch()
|
milestones.md
CHANGED
|
@@ -22,21 +22,3 @@ Note (when something is implemented write [done] in front of it)
|
|
| 22 |
- Configure deployment settings [done]
|
| 23 |
- Create simple deployment documentation [done]
|
| 24 |
- Document manual and terminal-based deployment steps [done]
|
| 25 |
-
|
| 26 |
-
## Milestone 5: Gradio UI Development
|
| 27 |
-
- Design simple UI with job description input
|
| 28 |
-
- Add file upload option for custom base resume (optional)
|
| 29 |
-
- Implement PDF preview and download functionality
|
| 30 |
-
- Style the interface for better user experience
|
| 31 |
-
|
| 32 |
-
## Milestone 6: Testing and Refinement
|
| 33 |
-
- Test with various job descriptions
|
| 34 |
-
- Refine LLM prompts for better customization
|
| 35 |
-
- Optimize performance and resource usage
|
| 36 |
-
- Fix bugs and edge cases
|
| 37 |
-
|
| 38 |
-
## Milestone 8: Documentation and Maintenance
|
| 39 |
-
- Complete user documentation
|
| 40 |
-
- Document code for future maintenance
|
| 41 |
-
- Create examples and usage instructions
|
| 42 |
-
- Plan for future enhancements
|
|
|
|
| 22 |
- Configure deployment settings [done]
|
| 23 |
- Create simple deployment documentation [done]
|
| 24 |
- Document manual and terminal-based deployment steps [done]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
templates/Arbab - Resume.tex
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
%-------------------------
|
| 2 |
+
% Resume in LateX
|
| 3 |
+
% Author : Sourabh Bajaj
|
| 4 |
+
% License : MIT
|
| 5 |
+
%------------------------
|
| 6 |
+
|
| 7 |
+
\documentclass[letterpaper,20pt]{article}
|
| 8 |
+
|
| 9 |
+
\usepackage{latexsym}
|
| 10 |
+
\usepackage[empty]{fullpage}
|
| 11 |
+
\usepackage{titlesec}
|
| 12 |
+
\usepackage{marvosym}
|
| 13 |
+
\usepackage[usenames,dvipsnames]{color}
|
| 14 |
+
\usepackage{verbatim}
|
| 15 |
+
\usepackage{enumitem}
|
| 16 |
+
\usepackage[hidelinks]{hyperref}
|
| 17 |
+
\usepackage{fancyhdr}
|
| 18 |
+
\usepackage[english]{babel}
|
| 19 |
+
\usepackage{tabularx}
|
| 20 |
+
\input{glyphtounicode}
|
| 21 |
+
|
| 22 |
+
\pagestyle{fancy}
|
| 23 |
+
\fancyhf{} % Clear all header and footer fields
|
| 24 |
+
\fancyfoot{}
|
| 25 |
+
\renewcommand{\headrulewidth}{0pt}
|
| 26 |
+
\renewcommand{\footrulewidth}{0pt}
|
| 27 |
+
|
| 28 |
+
% Adjust margins
|
| 29 |
+
\addtolength{\oddsidemargin}{-0.5in}
|
| 30 |
+
\addtolength{\evensidemargin}{-0.5in}
|
| 31 |
+
\addtolength{\textwidth}{1in}
|
| 32 |
+
\addtolength{\topmargin}{-.5in}
|
| 33 |
+
\addtolength{\textheight}{1.0in}
|
| 34 |
+
|
| 35 |
+
\urlstyle{same}
|
| 36 |
+
|
| 37 |
+
\raggedbottom
|
| 38 |
+
\raggedright
|
| 39 |
+
\setlength{\tabcolsep}{0in}
|
| 40 |
+
|
| 41 |
+
% Sections formatting
|
| 42 |
+
\titleformat{\section}{
|
| 43 |
+
\vspace{-4pt}\scshape\raggedright\large
|
| 44 |
+
}{}{0em}{}[\color{black}\titlerule \vspace{-5pt}]
|
| 45 |
+
|
| 46 |
+
% Ensure that generate PDF is machine readable/ATS parsable
|
| 47 |
+
\pdfgentounicode=1
|
| 48 |
+
|
| 49 |
+
%-------------------------
|
| 50 |
+
% Custom commands
|
| 51 |
+
\newcommand{\resumeItem}[2]{
|
| 52 |
+
\item\small{
|
| 53 |
+
\textbf{#1}{: #2 \vspace{-2pt}}
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
% Just in case someone needs a heading that does not need to be in a list
|
| 58 |
+
\newcommand{\resumeHeading}[4]{
|
| 59 |
+
\begin{tabular*}{0.99\textwidth}[t]{l@{\extracolsep{\fill}}r}
|
| 60 |
+
\textbf{#1} & #2 \\
|
| 61 |
+
\textit{\small#3} & \textit{\small #4} \\
|
| 62 |
+
\end{tabular*}\vspace{-5pt}
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
\newcommand{\resumeSubheading}[4]{
|
| 66 |
+
\vspace{-1pt}\item
|
| 67 |
+
\begin{tabular*}{0.97\textwidth}[t]{l@{\extracolsep{\fill}}r}
|
| 68 |
+
\textbf{#1} & #2 \\
|
| 69 |
+
\textit{\small#3} & \textit{\small #4} \\
|
| 70 |
+
\end{tabular*}\vspace{-5pt}
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
\newcommand{\resumeSubSubheading}[2]{
|
| 74 |
+
\begin{tabular*}{0.97\textwidth}{l@{\extracolsep{\fill}}r}
|
| 75 |
+
\textit{\small#1} & \textit{\small #2} \\
|
| 76 |
+
\end{tabular*}\vspace{-5pt}
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
\newcommand{\resumeSubItem}[2]{\resumeItem{#1}{#2}\vspace{-4pt}}
|
| 80 |
+
|
| 81 |
+
\renewcommand{\labelitemii}{$\circ$}
|
| 82 |
+
|
| 83 |
+
\newcommand{\resumeSubHeadingListStart}{\begin{itemize}[leftmargin=*]}
|
| 84 |
+
\newcommand{\resumeSubHeadingListEnd}{\end{itemize}}
|
| 85 |
+
\newcommand{\resumeItemListStart}{\begin{itemize}}
|
| 86 |
+
\newcommand{\resumeItemListEnd}{\end{itemize}\vspace{-5pt}}
|
| 87 |
+
|
| 88 |
+
%-------------------------------------------
|
| 89 |
+
%%%%%% CV STARTS HERE %%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
\begin{document}
|
| 93 |
+
|
| 94 |
+
%----------HEADING-----------------
|
| 95 |
+
\begin{tabular*}{\textwidth}{l@{\extracolsep{\fill}}r}
|
| 96 |
+
\textbf{\href{https://arbab-ml.github.io/}{\Large Muhammad Arbab Arshad}} & Email: \href{mailto:arbab.arshad97@gmail.com}{arbab.arshad97@gmail.com}\\
|
| 97 |
+
\href{https://www.linkedin.com/in/arbab-arshad/}{linkedin.com/in/arbab-arshad} & Mobile: \href{tel:+18483139857}{848-313-9857} \\
|
| 98 |
+
\end{tabular*}
|
| 99 |
+
|
| 100 |
+
\vspace{-5pt}
|
| 101 |
+
%--------SKILLS------------
|
| 102 |
+
\section{Technical Skills}
|
| 103 |
+
\resumeSubHeadingListStart
|
| 104 |
+
\resumeSubItem{Large Language Models}{Hugging Face, LangChain, vLLM (inference engine for LLMs), TorchTune (for LLM fine-tuning), MLflow (for model tracking), Retrieval Augmented Generation, LLM Quantization, Vision Language Models}
|
| 105 |
+
\resumeSubItem{Machine Learning \& AI}{ PyTorch, TensorFlow, CUDA, MLOps, Computer Vision, Distributed Systems, NLP}
|
| 106 |
+
\resumeSubItem{Programming Languages}{Python, Java, C++, JavaScript, R, SQL, Scala, Go}
|
| 107 |
+
\resumeSubItem{Development}{React, Node.js, Express, REST APIs, Gradio, MongoDB, PostgreSQL}
|
| 108 |
+
\resumeSubItem{Infrastructure \& Tools}{AWS, Docker, CI/CD, Terraform, Git, Linux, Agile, kanban, Jira, Bash }
|
| 109 |
+
\resumeSubHeadingListEnd
|
| 110 |
+
|
| 111 |
+
%-----------EXPERIENCE-----------------
|
| 112 |
+
\section{Experience}
|
| 113 |
+
\resumeSubHeadingListStart
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
\resumeSubheading
|
| 117 |
+
{Intel}{California, United States of America}
|
| 118 |
+
{Graduate Software Engineering Intern}{Feb 2025 -- Present}
|
| 119 |
+
\resumeItemListStart
|
| 120 |
+
\resumeItem{Computer Vision-based UI Automation via Large Language Models (LLMs)}
|
| 121 |
+
{Architected autonomous agent framework leveraging multimodal LLMs to generate cross-platform automation scripts, reducing manual testing workflows by 85\%.
|
| 122 |
+
Fine-tuned Llama 3.2 11B vision model using PyTorch TorchTune, improving computer navigation performance from 40\% to 90\% across diverse rendering applications.
|
| 123 |
+
Implemented real-time adaptive vision-to-code generation achieving 95\% validation accuracy for complex UI interactions.}
|
| 124 |
+
|
| 125 |
+
\resumeItem{Visual Anomaly Detection for Graphics Validation (Project VAAML)}
|
| 126 |
+
{Contributing to initiative addressing Intel's \$6.5M annual manual testing costs for hardware-game compatibility validation.
|
| 127 |
+
Developing real-time anomaly detection system using Vision-Language Models that has reached 87\% detection accuracy in preliminary testing across diverse rendering scenarios.}
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
\resumeItem{LLM Benchmarking \& Hardware Enablement}
|
| 131 |
+
{Implementing production deployment of cutting-edge LLMs (DeepSeek, Llama, Qwen, Janus) on Intel GPUs through OpenVINO and PyTorch optimizations as part of Intel's AI acceleration team.
|
| 132 |
+
Executing systematic performance analysis across Intel and NVIDIA hardware platforms, documenting throughput, latency, and memory utilization metrics for strategic product positioning.}
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
\resumeItemListEnd
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
\resumeSubheading
|
| 143 |
+
{AI Institute for Resilient Agriculture (AIIRA), Iowa State University}{Iowa, United States of America}
|
| 144 |
+
{Ph.D. Candidate and Research Assistant}{Jan 2023 -- Present}
|
| 145 |
+
\resumeItemListStart
|
| 146 |
+
\resumeItem{Agricultural LLM}
|
| 147 |
+
{Developed Large Language Model for precise agricultural recommendations using expert-verified data on 90 species.
|
| 148 |
+
Implemented Retrieval Augmented Generation with 82\% recall and 65\% precision.
|
| 149 |
+
Integrated multi-LLM support for enhanced performance. \href{https://AgLLMs.github.io}{(AgLLMs.github.io)}}
|
| 150 |
+
\resumeItem{LLM Evaluation}
|
| 151 |
+
{Developed 12-task agricultural benchmark to evaluate multimodal LLMs. Established baseline metrics, achieving up to 73.37\% F1 score with few-shot learning. \href{https://AgLLMs.github.io/AgEval}{(AgLLMs.github.io/AgEval)}}
|
| 152 |
+
\resumeItem{3D Modeling}
|
| 153 |
+
{Evaluated Neural Radiance Fields for detailed 3D plant reconstruction in various environments.
|
| 154 |
+
Achieved 74.6\% accuracy in challenging outdoor scenarios, demonstrating potential for complex modeling.
|
| 155 |
+
Developed optimization technique reducing training time by 50\% with minimal accuracy loss.}
|
| 156 |
+
\resumeItemListEnd
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
\resumeSubheading
|
| 160 |
+
{Kingland Systems}{Iowa, United States of America}
|
| 161 |
+
{Software Engineering Intern}{May 2023 -- Aug 2023}
|
| 162 |
+
\resumeItemListStart
|
| 163 |
+
\resumeItem{Cloud Deployment}
|
| 164 |
+
{Implemented auto-scaling AWS Fargate deployment and developed end-to-end stress testing pipeline using JMeter and Blazemeter, optimizing resource usage and enabling comprehensive load testing}
|
| 165 |
+
\resumeItem{CI/CD Enhancement}
|
| 166 |
+
{Customized GitLab CI/CD pipeline to seamlessly integrate testing, ensuring uninterrupted development workflow}
|
| 167 |
+
\resumeItem{Recognition}
|
| 168 |
+
{Received formal recognition in two sprint retrospectives for establishing comprehensive load testing baseline}
|
| 169 |
+
\resumeItemListEnd
|
| 170 |
+
|
| 171 |
+
\resumeSubheading
|
| 172 |
+
{Laboratory for Software Design, Iowa State University}{Iowa, United States of America}
|
| 173 |
+
{Research Assistant}{Jan 2022 -- Aug 2022}
|
| 174 |
+
\resumeItemListStart
|
| 175 |
+
\resumeItem{Program Repair}
|
| 176 |
+
{Executed 5 automated repair tools on GPU clusters for empirical study. Optimized parallel execution, reducing time by 16x across 40 clusters.}
|
| 177 |
+
\resumeItem{Research Impact}
|
| 178 |
+
{Publication received Distinguished Paper Award at 38th IEEE/ACM Conference on Automated Software Engineering.}
|
| 179 |
+
\resumeItemListEnd
|
| 180 |
+
\resumeSubHeadingListEnd
|
| 181 |
+
|
| 182 |
+
%-----------EDUCATION-----------------
|
| 183 |
+
\section{Education}
|
| 184 |
+
\resumeSubHeadingListStart
|
| 185 |
+
\resumeSubheading
|
| 186 |
+
{Iowa State University}{Iowa, United States of America}
|
| 187 |
+
{Doctor of Philosophy (Ph.D.) in Computer Science; GPA: 3.96}{Jan 2022 -- Aug 2025}
|
| 188 |
+
\resumeSubheading
|
| 189 |
+
{American University of Sharjah}{Sharjah, United Arab Emirates}
|
| 190 |
+
{Master of Science (M.S.) in Computer Engineering; GPA: 3.52}{Aug 2019 -- Aug 2021}
|
| 191 |
+
\resumeSubHeadingListEnd
|
| 192 |
+
%-----------PROJECTS-----------------
|
| 193 |
+
\section{Projects}
|
| 194 |
+
\resumeSubHeadingListStart
|
| 195 |
+
\resumeSubItem{MeditateGPT}
|
| 196 |
+
{Designed and developed an application for customized guided meditations using GPT-4 API, MERN Stack, Amazon Polly, and AWS S3.}
|
| 197 |
+
\resumeSubItem{Bat Behavior Analysis}
|
| 198 |
+
{Adapted unsupervised ML image clustering algorithms to audio data for bat behavior analysis, achieving 88.28\% accuracy in classifying bats.}
|
| 199 |
+
\resumeSubItem{Emotional Melody}
|
| 200 |
+
{Developed a text-to-audio generation system for poetry-to-melody using GANs, generating melodies with 68\% perceived similarity to real melodies.}
|
| 201 |
+
\resumeSubItem{Intrusion Detection}
|
| 202 |
+
{Utilized Amazon EI to remotely detect SSH and FTP brute-force attacks, achieving 99\% F1 score and 8x speed increase compared to local inference.}
|
| 203 |
+
\resumeSubHeadingListEnd
|
| 204 |
+
|
| 205 |
+
%--------SELECTED RESEARCH PUBLICATIONS------------
|
| 206 |
+
\section{Selected Research Publications}
|
| 207 |
+
\resumeSubHeadingListStart
|
| 208 |
+
|
| 209 |
+
\resumeSubItem{Leveraging Vision Language Models for Specialized Agricultural Tasks}{IEEE/CVF Winter Conference on Applications of Computer Vision (WACV), 2025.}
|
| 210 |
+
\resumeSubItem{Assisted Few-Shot Learning for Vision-Language Models in Agricultural Stress Phenotype Identification}{Advances in Neural Information Processing Systems (NeurIPS), Workshop on Adaptive Foundation Models: Evolving AI for Personalized and Efficient Learning, 2024.}
|
| 211 |
+
\resumeSubItem{Mutation-based Fault Localization of Deep Neural Networks}{38th IEEE/ACM International Conference on Automated Software Engineering (ASE), 2023.}
|
| 212 |
+
\resumeSubItem{Putting GPT-4o to the Sword: A Comprehensive Evaluation of Language, Vision, Speech, and Multimodal Proficiency}{Applied Sciences, 2024.}
|
| 213 |
+
\resumeSubItem{Comparison of deep learning algorithms for site detection of false data injection attacks in smart grids}{Energy Informatics, 2024.}
|
| 214 |
+
\resumeSubItem{Evaluating Neural Radiance Fields (NeRFs) for 3D Plant Geometry Reconstruction in Field Conditions}{Plant Phenomics, 2024.}
|
| 215 |
+
\resumeSubItem{Forecasting highly fluctuating electricity load using machine learning models based on multimillion observations}{Advanced Engineering Informatics, 2022.}
|
| 216 |
+
|
| 217 |
+
\resumeSubHeadingListEnd
|
| 218 |
+
|
| 219 |
+
%-------------------------------------------
|
| 220 |
+
\end{document}
|