Skip to content

Build and Deploy a Free Transformer Project: Resume to Job Description Matcher with Sentence Embeddings and Gradio

Build a resume to JD matcher with all MiniLM sentence embeddings and a missing keywords check, deploy it free on Hugging Face Spaces, and present it well on GitHub, your resume and in interviews.

Ucanly Team9 min read
Share
Build and Deploy a Free Transformer Project: Resume to Job Description Matcher with Sentence Embeddings and Gradio

Most fresher resumes list the same projects: a calculator, a to do app, a movie recommender copied from a tutorial. A small, working transformer project that solves a problem every student understands stands out far more, especially if anyone can open a link and try it. In this guide you will build a resume to job description matcher using sentence embeddings, add a simple missing keywords check, wrap it in a Gradio app and deploy it on Hugging Face Spaces at no cost. Then you will learn how to present it on GitHub, on your resume and in interviews.

You need a laptop, a free Hugging Face account and basic Python. No GPU is required.

What the Project Does and Why It Works

The app takes two inputs: the text of a resume and the text of a job description (JD). It returns two outputs:

  1. A semantic similarity score that measures how close the meaning of the resume is to the meaning of the JD.
  2. A list of keywords that appear in the JD but not in the resume, so the candidate knows what to add or learn.

The first part uses a sentence transformer. A sentence transformer converts a piece of text into a fixed length vector of numbers called an embedding. Texts with similar meaning end up with embeddings that point in similar directions. We compare the two embeddings using cosine similarity, which gives a value where higher means more similar.

This is better than plain word matching because embeddings understand that "built REST APIs with Flask" and "backend development in Python" are related, even though they share few words. The keyword check then complements it, because applicant tracking systems and recruiters often still look for exact terms.

We will use sentence-transformers/all-MiniLM-L6-v2, a small, popular, well documented model that is fast on CPU and good enough for this task.

Key takeaway: The project combines meaning (embeddings) with exact terms (keywords). Being able to explain why you need both is what turns a demo into a strong interview story.

Step 1: Set Up and Test the Core Logic

Start in a free Google Colab notebook or on your laptop. Install the two libraries:

pip install sentence-transformers gradio

Now test the similarity logic on its own before building any interface:

from sentence_transformers import SentenceTransformer, util

model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")

resume = "Final year CSE student. Built REST APIs with Flask and MySQL. Worked with pandas and scikit-learn."
jd = "Looking for a Python backend intern with experience in Flask, SQL databases and Docker."

embeddings = model.encode([resume, jd], convert_to_tensor=True)
score = util.cos_sim(embeddings[0], embeddings[1]).item()
print(f"Similarity: {score:.3f}")

model.encode returns one embedding per input text. util.cos_sim returns a small tensor of similarity values, and .item() converts the single value into a normal Python float. Swap in an unrelated JD, such as graphic design, and watch the score drop.

A note on long resumes

Like most transformer models, this one has a limited input length and silently truncates very long text. For a typical one page fresher resume this is usually fine. If you want to handle longer documents, split the resume into sentences or bullet points, encode each one, and compare every chunk with the JD. util.cos_sim(jd_embedding, chunk_embeddings) returns a matrix, so you can take the highest or average value.

Step 2: Add the Missing Keywords Check

Next, extract simple keywords from both texts and find what the JD has that the resume lacks. Keep it simple and transparent:

import re

STOPWORDS = {
    "the", "and", "for", "with", "you", "are", "our", "will", "have", "from",
    "this", "that", "your", "who", "can", "work", "team", "role", "experience",
    "looking", "good", "strong", "knowledge", "skills", "using", "must",
}

def extract_keywords(text):
    words = re.findall(r"[a-z][a-z0-9+#.]*", text.lower())
    cleaned = {w.strip(".") for w in words}
    return {w for w in cleaned if len(w) > 2 and w not in STOPWORDS}

missing = sorted(extract_keywords(jd) - extract_keywords(resume))
print(missing)

The regular expression keeps characters like +, # and . so that terms such as c++, c# and node.js survive. For the example above, the output includes docker, which tells the candidate exactly what is missing.

This approach ignores synonyms, drops very short terms like "Go" or "R", and uses a hand made stopword list. Knowing these limits is a strength in interviews.

Step 3: Wrap It in a Gradio App

Gradio lets you turn a Python function into a web interface in a few lines. Create a file named app.py:

import re
import gradio as gr
from sentence_transformers import SentenceTransformer, util

model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")

STOPWORDS = {
    "the", "and", "for", "with", "you", "are", "our", "will", "have", "from",
    "this", "that", "your", "who", "can", "work", "team", "role", "experience",
    "looking", "good", "strong", "knowledge", "skills", "using", "must",
}

def extract_keywords(text):
    words = re.findall(r"[a-z][a-z0-9+#.]*", text.lower())
    cleaned = {w.strip(".") for w in words}
    return {w for w in cleaned if len(w) > 2 and w not in STOPWORDS}

def match(resume_text, jd_text):
    if not resume_text.strip() or not jd_text.strip():
        return "Please paste both the resume and the job description.", ""
    embeddings = model.encode([resume_text, jd_text], convert_to_tensor=True)
    score = util.cos_sim(embeddings[0], embeddings[1]).item()
    missing = sorted(extract_keywords(jd_text) - extract_keywords(resume_text))
    missing_text = ", ".join(missing[:30]) if missing else "No obvious keywords missing."
    return f"{score * 100:.1f} / 100", missing_text

demo = gr.Interface(
    fn=match,
    inputs=[
        gr.Textbox(lines=12, label="Resume text"),
        gr.Textbox(lines=12, label="Job description"),
    ],
    outputs=[
        gr.Textbox(label="Semantic match score"),
        gr.Textbox(label="Keywords in the JD that are missing from the resume"),
    ],
    title="Resume to JD Matcher",
    description="Paste a resume and a job description to see how closely they match in meaning, plus keywords you may be missing.",
)

if __name__ == "__main__":
    demo.launch()

Run python app.py locally and open the address printed in the terminal. Test it with your own resume and a few real internship descriptions from a jobs board.

Treat the score as a relative signal, not a hiring verdict.

Step 4: Deploy Free on Hugging Face Spaces

Hugging Face Spaces hosts Gradio apps for free on basic CPU hardware. Free Spaces may go to sleep after a period of inactivity and wake up when someone visits, so the first load can be slow. Check the current free tier details on the Hugging Face Spaces page, since limits change over time.

Create a file named requirements.txt next to app.py:

sentence-transformers
gradio

Then deploy:

  1. Log in to Hugging Face and click New Space.
  2. Give it a clear name such as resume-jd-matcher, choose Gradio as the SDK and select the free CPU hardware option.
  3. Upload app.py and requirements.txt using the web interface, or clone the Space repository with Git and push the files.
  4. Wait for the build to finish. The Logs tab shows installation progress and any errors.

If you prefer Git:

git clone https://huggingface.co/spaces/YOUR_USERNAME/resume-jd-matcher
cd resume-jd-matcher
# copy app.py and requirements.txt here
git add app.py requirements.txt
git commit -m "Add resume to JD matcher"
git push

Most build failures come from a misspelled package name or model id, so read the logs carefully.

Key takeaway: A live link removes all doubt. An interviewer can try your project in thirty seconds instead of trusting a line on your resume.

Step 5: Present It on GitHub, Your Resume and in Interviews

On GitHub

Push the same code to a public GitHub repository and structure the README well:

  • One line summary: what the app does and who it helps.
  • Live demo link to your Space, placed at the very top.
  • Screenshot or short GIF of the app in action.
  • How it works: embeddings with all-MiniLM-L6-v2, cosine similarity with util.cos_sim, and the keyword set difference.
  • Example results: a small table showing scores for a matching JD and an unrelated JD.
  • Limitations: truncation of long text, no synonym handling in keywords, score is relative.
  • Future work: chunking, a skills dictionary, section level matching.
  • How to run locally: the two install and run commands.

Pin the repository on your GitHub profile so it is the first thing visitors see.

On your resume

Write one or two bullets that show action, tools and outcome without inflating anything:

Built and deployed a resume to job description matcher using sentence transformers (all-MiniLM-L6-v2) and cosine similarity, with a missing keywords check; served as a Gradio app on Hugging Face Spaces (live link).

Add the live link and the GitHub link. Before you send applications, run your own resume through an AI resume review to check clarity and formatting.

In interviews

Prepare clear answers to the questions you will likely face:

  • Why embeddings instead of keyword matching? Embeddings capture meaning, so related phrasing scores well even with different words.
  • Why cosine similarity? It compares the direction of vectors, which is what matters for semantic closeness, and it is not affected by text length in the same way raw dot products can be.
  • What are the limitations? Input truncation, a hand made stopword list, and a score that is relative rather than absolute.
  • How would you improve it? Chunk the resume, use a curated skills list, compare section by section, or try a larger embedding model and measure the difference on a small labelled set you create.
  • How would it scale? Precompute embeddings for many JDs and search them, which is the basis of semantic search.

Practise explaining the whole pipeline in under two minutes.

Four More Free Transformer Project Ideas

Once this project is live, these ideas follow the same build, deploy and present pattern:

  1. Hindi translation helper: use Helsinki-NLP/opus-mt-en-hi with the translation pipeline to translate English study notes or notices into Hindi.
  2. Lecture notes summariser: use facebook/bart-large-cnn with the summarization pipeline to condense long articles or transcripts. It is larger, so expect slower CPU responses.
  3. Placement FAQ semantic search: embed a list of college placement FAQs with all-MiniLM-L6-v2 and return the closest answers to a student's question.
  4. Review sentiment dashboard: use distilbert-base-uncased-finetuned-sst-2-english to classify canteen or app reviews as positive or negative and show simple counts.

Our courses cover the Python and machine learning foundations behind them.

What to Do Next

Build the core logic in Colab today, then move it into app.py and test it locally. Deploy it to Hugging Face Spaces, write a clear README with a live link, and add one honest bullet to your resume. Finally, rehearse the interview answers above until you can explain every design choice. One polished, deployed, well explained project will do more for your placement preparation than five half finished tutorials.

Talk it through with a counsellor

Leave your number and someone from our team will call you with a plan for the career you are working towards.

Get a free career consultation

Tell us where you're headed and a counsellor will call you with a plan for getting there.

We use these details to respond to your request. The box above is only for marketing. See our privacy policy.

Try the free aptitude test

Fifteen questions in fifteen minutes, with every answer explained. Then a roadmap built from your score.