{"cells": [{"cell_type": "markdown", "id": "4f6428bb", "metadata": {"id": "4f6428bb"}, "source": ["# \ud83e\udd16 ResumeBot: My Personalised Fine-Tuned LLM\n", "\n", "A **FLAN-T5-base** model (Google, pretrained) **fine-tuned on my resume**, with safeguards so it does not make things up.\n", "\n", "```\n", "Resume (built in) \u2192 300+ question\u2013answer pairs \u2192 Semantic retriever (finds the right resume section)\n", "\u2192 Fine-tune FLAN-T5 to answer from that section \u2192 Grounding check (no hallucination) \u2192 Gradio chat website\n", "```\n", "\n", "**How it avoids hallucination**\n", "1. **Retrieval:** every question is matched to the most relevant resume fact using sentence embeddings.\n", "2. **Fine-tuned generation:** the model is trained to answer *only* from the retrieved resume text.\n", "3. **Grounding check:** if an answer contains words that are not in the resume text, the bot returns the verified resume fact instead.\n", "4. **Out-of-scope guard:** questions that are not about the resume get a polite \"I only answer about Sudeep\" reply instead of a guess.\n", "\n", "**Run:** `Runtime \u2192 Change runtime type \u2192 T4 GPU`, then `Runtime \u2192 Run all`. The chat link appears at the bottom (about 5 minutes)."]}, {"cell_type": "markdown", "id": "cb9a4e27", "metadata": {"id": "cb9a4e27"}, "source": ["## 1 \u00b7 Setup"]}, {"cell_type": "code", "execution_count": 1, "id": "13b30281", "metadata": {"colab": {"base_uri": "https://localhost:8080/"}, "id": "13b30281", "executionInfo": {"status": "ok", "timestamp": 1790762392318, "user_tz": -330, "elapsed": 120038, "user": {"displayName": "sudeep shetty", "userId": "15639451297137763781"}}, "outputId": "f63ed019-4bc2-4677-caeb-9a493c90bf9f"}, "outputs": [{"output_type": "stream", "name": "stdout", "text": ["Device: CUDA | Base model: google/flan-t5-base\n"]}], "source": ["!pip install -q gradio sentence-transformers\n", "\n", "import re, random, inspect, torch, numpy as np, pandas as pd, matplotlib.pyplot as plt\n", "from datasets import Dataset, DatasetDict\n", "from sentence_transformers import SentenceTransformer\n", "from transformers import (AutoTokenizer, AutoModelForSeq2SeqLM, DataCollatorForSeq2Seq,\n", " Seq2SeqTrainingArguments, Seq2SeqTrainer, set_seed)\n", "set_seed(42); random.seed(42)\n", "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", "MODEL_NAME = \"google/flan-t5-base\" if device == \"cuda\" else \"google/flan-t5-small\"\n", "print(\"Device:\", device.upper(), \"| Base model:\", MODEL_NAME)"]}, {"cell_type": "markdown", "id": "f4dadc8a", "metadata": {"id": "f4dadc8a"}, "source": ["## 2 \u00b7 My resume and profile (built in)"]}, {"cell_type": "code", "execution_count": 2, "id": "d8f21dd8", "metadata": {"id": "d8f21dd8", "executionInfo": {"status": "ok", "timestamp": 1790762392319, "user_tz": -330, "elapsed": 44, "user": {"displayName": "sudeep shetty", "userId": "15639451297137763781"}}}, "outputs": [], "source": ["PROFILE = {\n", " \"name\": \"Sudeep\", \"role\": \"Data Analyst | Data Science\", \"location\": \"Bengaluru, Karnataka\",\n", " \"email\": \"shettysudeep27@gmail.com\",\n", " \"linkedin\": \"linkedin.com/in/sudeep-shetty-0aa23a1b0\", \"github\": \"github.com/sudeepshetty4247-stack\",\n", " \"education\": \"M.Sc. Data Science, Dayananda Sagar University (2025-2027)\",\n", " \"skills\": [\"Python\", \"SQL\", \"Power BI\", \"Excel\", \"Data Analysis\", \"Data Visualization\", \"Business Intelligence\"],\n", "}"]}, {"cell_type": "markdown", "id": "210bea38", "metadata": {"id": "210bea38"}, "source": ["## 3 \u00b7 Knowledge base: resume facts and questions\n", "Every fact below comes directly from the resume. Each fact has several ways of asking about it, and every question is\n", "automatically rewritten with *the candidate*, *Sudeep* and *he/his*, so the model learns many phrasings."]}, {"cell_type": "code", "execution_count": 3, "id": "c668450c", "metadata": {"colab": {"base_uri": "https://localhost:8080/", "height": 317}, "id": "c668450c", "executionInfo": {"status": "ok", "timestamp": 1790762392335, "user_tz": -330, "elapsed": 56, "user": {"displayName": "sudeep shetty", "userId": "15639451297137763781"}}, "outputId": "4bc7f7e6-999d-4294-8adf-2194f4fe2695"}, "outputs": [{"output_type": "stream", "name": "stdout", "text": ["28 resume facts -> 353 questions (train 327, unseen test 26)\n"]}, {"output_type": "execute_result", "data": {"text/plain": [" topic question\n", "150 BCA Which college did Sudeep graduate from?\n", "169 SSLC Which school did he attend?\n", "91 Career goals What are his career interests?\n", "299 Certifications What courses has the candidate completed?\n", "298 Certifications List his certifications.\n", "314 Achievements Has the candidate won any awards?\n", "329 Hobbies What does the candidate do in free time?\n", "286 Additional projects Has the candidate done any IoT project?"], "text/html": ["\n", "
\n", "
\n", "\n", "
\n", " \n", "
\n", "
\n", "
topic
\n", "
question
\n", "
\n", " \n", " \n", "
\n", "
150
\n", "
BCA
\n", "
Which college did Sudeep graduate from?
\n", "
\n", "
\n", "
169
\n", "
SSLC
\n", "
Which school did he attend?
\n", "
\n", "
\n", "
91
\n", "
Career goals
\n", "
What are his career interests?
\n", "
\n", "
\n", "
299
\n", "
Certifications
\n", "
What courses has the candidate completed?
\n", "
\n", "
\n", "
298
\n", "
Certifications
\n", "
List his certifications.
\n", "
\n", "
\n", "
314
\n", "
Achievements
\n", "
Has the candidate won any awards?
\n", "
\n", "
\n", "
329
\n", "
Hobbies
\n", "
What does the candidate do in free time?
\n", "
\n", "
\n", "
286
\n", "
Additional projects
\n", "
Has the candidate done any IoT project?
\n", "
\n", " \n", "
\n", "
\n", "
\n", "\n", "
\n", " \n", "\n", " \n", "\n", " \n", "
\n", "\n", "\n", "
\n", "
\n"], "application/vnd.google.colaboratory.intrinsic+json": {"type": "dataframe", "summary": "{\n \"name\": \"qa\",\n \"rows\": 8,\n \"fields\": [\n {\n \"column\": \"topic\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 7,\n \"samples\": [\n \"BCA\",\n \"SSLC\",\n \"Hobbies\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"question\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 8,\n \"samples\": [\n \"Which school did he attend?\",\n \"Has the candidate won any awards?\",\n \"Which college did Sudeep graduate from?\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}"}}, "metadata": {}, "execution_count": 3}], "source": ["FACTS = [\n", " (\"Name\", \"His name is Sudeep. He is a Data Analyst and M.Sc. Data Science student based in Bengaluru.\",\n", " [\"What is the candidate's name?\", \"Who is the candidate?\", \"Who is this resume about?\", \"Introduce the candidate.\", \"Who are you?\"]),\n", " (\"Role\", \"Sudeep is a Data Analyst with a focus on Data Science.\",\n", " [\"What is the candidate's professional title?\", \"What role is the candidate looking for?\", \"What job does the candidate want?\",\n", " \"What position is the candidate suited for?\", \"What does the candidate do?\"]),\n", " (\"Location\", \"Sudeep is based in Bengaluru, Karnataka.\",\n", " [\"Where is the candidate located?\", \"Where does the candidate live?\", \"Which city is the candidate from?\", \"Where is the candidate based?\"]),\n", " (\"Email\", \"You can email Sudeep at shettysudeep27@gmail.com.\",\n", " [\"What is the candidate's email?\", \"How can I contact the candidate?\", \"What is the candidate's email address?\", \"How do I reach the candidate?\"]),\n", " (\"LinkedIn & GitHub\", \"Sudeep's LinkedIn is linkedin.com/in/sudeep-shetty-0aa23a1b0 and his GitHub is github.com/sudeepshetty4247-stack.\",\n", " [\"What is the candidate's LinkedIn?\", \"What is the candidate's GitHub?\", \"Does the candidate have a GitHub profile?\", \"Where can I see the candidate's work online?\"]),\n", " (\"Profile\", \"Sudeep is an M.Sc. Data Science student with a strong foundation in data analytics, business intelligence and machine learning. He turns raw data into actionable insights using Power BI dashboards, SQL-based analysis and Python.\",\n", " [\"Tell me about the candidate.\", \"Give a short summary of the candidate.\", \"Describe the candidate's profile.\",\n", " \"What is the candidate's background?\", \"Summarize the candidate's resume.\"]),\n", " (\"Why hire\", \"Sudeep combines data analytics, business intelligence and machine learning skills with hands-on projects in Power BI, SQL, Python and AI, and he is passionate about solving business problems with data.\",\n", " [\"Why should we hire the candidate?\", \"What makes the candidate a good fit?\", \"Is the candidate suitable for a data analyst role?\",\n", " \"What are the candidate's strengths as a data analyst?\"]),\n", " (\"Career goals\", \"Sudeep is passionate about using data to solve business challenges and enable data-driven decision-making.\",\n", " [\"What are the candidate's career interests?\", \"What is the candidate passionate about?\", \"What are the candidate's career goals?\",\n", " \"What motivates the candidate?\"]),\n", " (\"Experience summary\", \"Sudeep has built analytical and AI-powered projects focused on decision support, predictive modeling and process optimization.\",\n", " [\"What kind of projects does the candidate build?\", \"What is the candidate's technical experience?\", \"What is the candidate experienced in?\"]),\n", " (\"Education\", \"Sudeep is pursuing an M.Sc. in Data Science at Dayananda Sagar University (2025-2027). He completed a Bachelor of Computer Applications (BCA) at Dr. B. B. Hegde First Grade College (2021-2024).\",\n", " [\"What is the candidate's educational qualification?\", \"What degrees does the candidate have?\", \"What is the candidate's education?\",\n", " \"What did the candidate study?\", \"What is the candidate's highest qualification?\"]),\n", " (\"Masters\", \"Sudeep is currently pursuing an M.Sc. in Data Science at Dayananda Sagar University, from 2025 to 2027.\",\n", " [\"What is the candidate studying now?\", \"Which university does the candidate attend?\", \"Is the candidate doing a master's degree?\",\n", " \"When will the candidate finish the M.Sc.?\", \"Which college is the candidate studying in now?\"]),\n", " (\"BCA\", \"Sudeep completed a Bachelor of Computer Applications (BCA) at Dr. B. B. Hegde First Grade College from 2021 to 2024.\",\n", " [\"What bachelor's degree does the candidate have?\", \"Where did the candidate do the BCA?\", \"When did the candidate finish graduation?\",\n", " \"Which college did the candidate graduate from?\", \"What is the candidate's undergraduate degree?\"]),\n", " (\"PUC\", \"Sudeep completed the Pre-University Course in Science at R N Shetty PU College, Kundapura, from 2019 to 2021.\",\n", " [\"Where did the candidate complete PUC?\", \"What did the candidate study in PU college?\", \"Which PU college did the candidate attend?\"]),\n", " (\"SSLC\", \"Sudeep completed SSLC at Sri Mookambika Public School in 2018-2019.\",\n", " [\"Where did the candidate complete SSLC?\", \"Which school did the candidate attend?\", \"When did the candidate finish school?\"]),\n", " (\"Technical skills\", \"Sudeep's technical skills are Python, SQL, Power BI, Excel, Data Analysis, Data Visualization and Business Intelligence.\",\n", " [\"What technical skills does the candidate have?\", \"What are the candidate's skills?\", \"What tools does the candidate know?\",\n", " \"List the candidate's technical skills.\", \"What software does the candidate use?\"]),\n", " (\"Python & SQL\", \"Sudeep knows Python and SQL. He built the Career AI Intelligence System with Python and PostgreSQL and does SQL-based analysis.\",\n", " [\"What programming languages does the candidate know?\", \"Does the candidate know Python?\", \"Does the candidate know SQL?\",\n", " \"What is the candidate's experience with Python?\", \"Can the candidate code?\"]),\n", " (\"Power BI\", \"Sudeep builds interactive dashboards in Power BI and Excel, using DAX and Power Query, for example the Smart Attendance Analytics System and the Prime Video Analytics Dashboard.\",\n", " [\"Does the candidate know Power BI?\", \"What data visualization tools does the candidate know?\", \"Does the candidate have experience with dashboards?\",\n", " \"Does the candidate know DAX?\", \"What BI tools does the candidate use?\"]),\n", " (\"Machine learning & AI\", \"Sudeep has a foundation in machine learning and has built AI projects such as the Career AI Intelligence System (NLP), AI Physio Fracture Detection System and an AI-Based Personalized Learning Strategy Recommendation System.\",\n", " [\"Does the candidate know machine learning?\", \"Has the candidate worked on AI?\", \"What AI projects has the candidate done?\", \"Does the candidate know NLP?\"]),\n", " (\"Soft skills\", \"Sudeep's soft skills are analytical thinking, problem solving, communication, critical thinking, teamwork and time management.\",\n", " [\"What soft skills does the candidate have?\", \"Is the candidate a team player?\", \"What are the candidate's strengths?\",\n", " \"Is the candidate good at communication?\", \"What personal qualities does the candidate have?\"]),\n", " (\"Projects\", \"Sudeep's main projects are the Career AI Intelligence System, the Smart Attendance Analytics System and the Prime Video Analytics Dashboard.\",\n", " [\"What projects has the candidate completed?\", \"List the candidate's projects.\", \"What has the candidate built?\",\n", " \"What are the candidate's main projects?\", \"Tell me about the candidate's projects.\"]),\n", " (\"Career AI project\", \"The Career AI Intelligence System is an AI-powered resume analysis platform for career prediction, ATS evaluation and skill-gap identification that generates personalized career roadmaps. It uses Python, PostgreSQL, React and NLP.\",\n", " [\"What is the Career AI Intelligence System?\", \"What is the candidate's best project?\", \"What is the candidate's most relevant project?\",\n", " \"Tell me about the career AI project.\", \"What technologies were used in the Career AI Intelligence System?\"]),\n", " (\"Attendance project\", \"The Smart Attendance Analytics System is a Power BI dashboard that tracks student performance, eligibility status and attendance deficits, with automated reporting and KPI monitoring. It uses Power BI, Excel, DAX and Power Query.\",\n", " [\"What is the Smart Attendance Analytics System?\", \"Tell me about the attendance project.\", \"What technologies were used in the attendance project?\",\n", " \"What does the attendance dashboard do?\"]),\n", " (\"Prime Video project\", \"The Prime Video Analytics Dashboard analyzed 9,651+ Prime Video titles to find trends in genres, ratings and release years. It was built with Power BI and Excel.\",\n", " [\"What is the Prime Video Analytics Dashboard?\", \"Tell me about the Prime Video project.\", \"How many titles were analyzed in the Prime Video project?\",\n", " \"What tools were used in the Prime Video project?\"]),\n", " (\"Additional projects\", \"Sudeep's additional projects are the AI-Based Personalized Learning Strategy Recommendation System, Strategic Data Analysis for AtliQ Hardware, Samtrupthi Online Waste Food Management System, Smart Dustbin Monitoring System, AI Physio Fracture Detection System and Cloud Computing for Energy Systems.\",\n", " [\"What other projects has the candidate done?\", \"What additional projects has the candidate worked on?\", \"Has the candidate done any healthcare project?\",\n", " \"Has the candidate done any IoT project?\", \"Has the candidate worked on cloud computing?\", \"What is the AtliQ Hardware project?\"]),\n", " (\"Certifications\", \"Sudeep's certifications are TechA Python Developer and Introduction to Data Mining from Infosys, Data Science Foundation and Prompt Engineering from Great Learning, and Certified Secure Computer User from EC-Council.\",\n", " [\"What certifications does the candidate have?\", \"List the candidate's certifications.\", \"What courses has the candidate completed?\",\n", " \"Does the candidate have any Infosys certificates?\", \"Does the candidate have a prompt engineering certificate?\", \"Is the candidate certified in cyber security?\"]),\n", " (\"Achievements\", \"Sudeep won the Best Presentation Award at an Intercollegiate IT Fest in 2024 and successfully completed Tritiya Sopan in Scouts and Guides.\",\n", " [\"What achievements does the candidate have?\", \"Has the candidate won any awards?\", \"What are the candidate's accomplishments?\",\n", " \"Did the candidate win any competition?\", \"Was the candidate in Scouts and Guides?\"]),\n", " (\"Hobbies\", \"Sudeep's hobbies are solving Rubik's Cubes, exploring emerging technologies and continuous learning.\",\n", " [\"What are the candidate's hobbies?\", \"What does the candidate do in free time?\", \"What are the candidate's interests outside work?\",\n", " \"Can the candidate solve a Rubik's Cube?\"]),\n", " (\"Work experience\", \"Sudeep's resume does not list any job or internship yet. His experience comes from his academic and personal projects.\",\n", " [\"What work experience does the candidate have?\", \"Has the candidate done any internships?\", \"Where has the candidate worked?\",\n", " \"Does the candidate have job experience?\", \"Which companies has the candidate worked for?\"]),\n", "]\n", "\n", "def variants(q):\n", " return list(dict.fromkeys([q,\n", " q.replace(\"the candidate's\", \"Sudeep's\").replace(\"the candidate\", \"Sudeep\"),\n", " q.replace(\"the candidate's\", \"his\").replace(\"the candidate\", \"he\")]))\n", "\n", "rows = []\n", "for fid, (topic, answer, questions) in enumerate(FACTS):\n", " for qi, q in enumerate(questions):\n", " for vi, v in enumerate(variants(q)):\n", " # hold out one reworded question per fact to test on questions the model never saw\n", " rows.append({\"fid\": fid, \"topic\": topic, \"question\": v, \"answer\": answer,\n", " \"split\": \"test\" if (qi == 1 and vi == 1) else \"train\"})\n", "qa = pd.DataFrame(rows)\n", "print(f\"{len(FACTS)} resume facts -> {len(qa)} questions (train {sum(qa.split=='train')}, unseen test {sum(qa.split=='test')})\")\n", "qa.sample(8, random_state=1)[[\"topic\", \"question\"]]"]}, {"cell_type": "markdown", "id": "2c40c0d4", "metadata": {"id": "2c40c0d4"}, "source": ["## 4 \u00b7 Semantic retriever\n", "A small sentence-embedding model (`all-MiniLM-L6-v2`) turns questions into vectors. A new question is matched to the\n", "most similar known questions, which tells us which resume fact it is about, even for wording never seen before."]}, {"cell_type": "code", "execution_count": 4, "id": "905e1a84", "metadata": {"colab": {"base_uri": "https://localhost:8080/", "height": 557, "referenced_widgets": ["9866c9cff1f74667b8a7a869152d7785", "eb4b2c3771364d7088e5c78cd2d67046", "6b817d909b0a449f8920776e1558eaef", "ffe87840f88b4ad6aae960d4c7cfe5c0", "6c6df9e95bb14a9f8db92a0296ccbebd", "4014794d8809420281b534a0ac0dccf3", "eb5c237f245742c49690114090f9f0cf", "744e1743e6a44b398f42edb565634a62", "71a4bff9b56141edb79673b32cc5d2b9", "f13464b22dc84b0cb4fbadb482768108", "90282570a35a425188e929729a90d6be", "ac56e415f3814066b44fd212a3cd8038", "93f0fb99ab0849e59ec710da3fbe081d", "f52cef13587146629c4a950878475f68", "e014b28de62a47ebbbbb15ad9ad2da85", "2dc8adbc3ac24371a6a14a57f1d91af4", "c6e10186f583435d91ba5e5e855e05da", "fcc3dffc1e02498a953dbbf0a3421330", "b014767cb14a4815b5ab383323a05450", "175027d6b8c74bc68af8ebb1d234cd47", "9239bee3509e4e11be47070c43d2d98f", "2f432b0caeac4f3aa1b53e4b551c4dc0", "fa3ffa577f9b4e1797858fbb853467da", "12135aad5d4a4c9cb33104ebc4555f5b", "47320391c7044537b4dc614c9e3883e3", "4c6ed5229b9f45d783ef638badc5fc49", "3c0c1cc1352b4d94a15c7086a32fbe5d", "d11241f8ce194a5b855b3c032042ba81", "04d6304bb117454e8b66c2e592951b52", "3e13337140a14094b3695eb68d3d20a8", "ca011a752b634980a55e83ebfcdb13b9", "6281a54808fc4b16a3c714bc51fa5f88", "d666c0773f4f46dea06dae17365c305d", "45a5ac2c6cf148269bb2fad003277509", "568cea035bee4ebeb4d82fb987e752ba", "3e1ab1f18a6e4578b9c7894e85ca94d4", "64e59331e3954a848d2b2ad7dfa6d420", "ec0b193215164a9392eae7c5f9423c02", "fbb4dffc20e34d829b9b66f41e503963", "5b030edb7a6341a8b7535f23503b0e13", "d596c709ced24dfaa2562aaa7998a9cc", "4461660154f3493caf86868accad68d1", "5a7e37b912754472b79d2e31c9f50f58", "63232811d08840278c479dcf92af1ebd", "1552fe0882b1450da22547852e18a433", "a2d5d30a33074cd7b16a5a0cc0382bc1", "46e8d34dade7402ca39d8ef878aeb68a", "dde8196e833944ab90aa4608af30200d", "ec74bdbe543f4fa1bb4fc1175c4a8266", "fea279f3e6a444a3adf0bc8d782c905d", "e92508f19b51456ea226f148a8ab0e05", "e0ce470959764c598267c8ba1c9dabcd", "bf2fa96de01a4715935c21f1bbe31e3a", "2e10dd767b2c494a83a180123fcf5aa3", "6b1beec05df349a18a81bbf8419f340e", "d313456d94a14bc79120a58b7cc4a7e8", "d74b8af7824a4cec99b4f281fe584fd1", "9c322bfa051e46f0871c1cfa6d74111d", "c14981d01e814b27ade28fd1e88cef5a", "5374d6aa8ec14436991f736acb206c77", "68d79fcd9ecc47608d3f333e4d87d74e", "bcfb7df6dbed4907a020c01432191f6f", "7df160fc2acf44589ac8761cadb16727", "b8ff9ef1464e41649d86fce20dda98ba", "0a487f967a534330b673e5dc45a53733", "e1b5b8c033b2408e8dd9a89a1ddf7212", "16c51ee5f8224b9d8b76fbc9ca43fd0c", "6aab9333c3f246c283f39eb6ca7a9640", "216bfc9a708b4be999c1f290c1f961e4", "d18ebbea4737462d8c437fbd41d739b3", "d97bd09a878540ff8a05b1836d19bc20", "0e2d70dc6b224a97bb7ec4461b409473", "8f8707eaec534141b097fc3a0d317c84", "0f935d78f89e4b799eaea00d5953c849", "082e2b0784114c5e9a8577960aeeddfc", "6ba0973ebbe94d13a8a555fc33a38cf9", "7514f7dbdfc149b392e5cb451b9ea655", "b7a748b9553a4c0aac05036135f7e187", "38036ef32e014fa2b9681dd010870eda", "421d997d3f8e4ed28508cecd12915626", "2a222291c06b4b19856f7d1db489fc6b", "03f434a225d149aa9aefd305580fd42e", "69849e3957254bc3b03d696c982b3a02", "e997e3bd7e67490d9d805d1e6dcca7f4", "2dbafd20d135475e9d72ecfbbb65ce85", "cc65e02809734980a47b1726515ab7e3", "945e48b0126548a4935977be5b4e1a0d", "e4a19de62b974472ab5e908052c81582", "0ec9cc1177af42c790f2b86302b1de81", "ca96008b4e674a7f8e90868dda1b2556", "1e2d1dc2d300406881e18b0b4df3a620", "5470571f54ac4308bb69f30a900bc27a", "8b4429bbc6aa4ca8853da5ba2a0b364c", "34b02e1ef43f44c5a0a28b4fd043e31e", "800d6c0023b44f5a87b012182d5944a9", "6c1caf654ce64d10a1439363a5da1e60", "4a6460a0a8864c04a0996b964330e92e", "0466844e19934fb99eabac1996240f87", "796cec9034424641a4e3c06f31534bf9", "0671180e87714c8ca19b2749c5166ea8", "614750bad8464374af06b5312582249f", "04e10aab082a41e59444c1c93987c2ea", "8ae9ec62cc4342c4a0cdd56f0d466b83", "4d4a4e34e9d842e891d38cd70b42971c", "371c680ab669434ab230d04e9a076435", "17da3a9b2f85431ba63815151aad8ca2", "bed92bc941904960b9c3b0cdd94445dc", "715429352d96429e9668a7372fc2d4d8", "5acb2045c30849f89ec7fefd9b98e96c", "999c4e23c325450ca21c71a56890cdef", "c923e85e56e145deb9e1cb884c561fdd", "10759cc694b84e23baf431ff425855d6", "9734bee08de64286a96dcbd5b7560935", "68be36a433d840e598dd6d0bab201d35", "c630187797d94c0f9d57565631d93faa", "f519024ebb844825a1f4e85baac02c8d", "2f494314cbe1457cbdd7da19ecd93612", "c240c6e225704e45acebc8ffae0bb9cb", "331828af5405442b9da936c6d3f7ce2e", "8caa4e541e6548448e24cf7221b8739b", "6d14464a38284e709a35be35c41ccfef", "05ede907891d40d4ab3c446fb2f8293c", "ecf75a299e3d44799bd9b392df11fed3", "85b1b44c8785480889ddd9fc4ad89a79", "298cece08ac146e5b37da4abae349b01", "66b8b347ba7644f5b40b0acaa8ab891e", "e98d25bcbe1b4797b5dd6cc179feb4ea", "6bb0f7ea5dce455895ea2803b84e8d78", "a958a51756084141979284d102cb6582", "db8e3a7d0cc847b19ea12e4c9b6f4c2e", "1263a98568964e4790f1d1635bb09604", "1f8d537384c24512b60668c175bb9915", "9bc88e333ea541a388c8e09312c562d3", "f9b9299ff7ca4c46949ef42e043f8c40", "eaa0b9f8dafe44c1bd5ac187a4144d42", "9646ae05ea044665aecc71d05afe4e09", "ff1c81b4416d4377972a1af9cb832aaf", "d40d4362c4b5473ea978c7dd69453594", "7c7b346ad52f4e92a9894bd7a5635370", "43bd1807e5cf4c1898d0408fb966f73f", "5437e36c1ffd457aafa07f25a9882a07", "c6ce256f9e53407fa8c0a01a378083de", "0226c3075bda470db403a34e6aedfc40"]}, "id": "905e1a84", "executionInfo": {"status": "ok", "timestamp": 1790762392793, "user_tz": -330, "elapsed": 456, "user": {"displayName": "sudeep shetty", "userId": "15639451297137763781"}}, "outputId": "6dfe6d38-eb27-4497-95e2-e9518d9540ad"}, "outputs": [{"output_type": "stream", "name": "stderr", "text": ["/usr/local/lib/python3.13/dist-packages/huggingface_hub/utils/_auth.py:138: UserWarning: \n", "Error while fetching `HF_TOKEN` secret value from your vault: 'Requesting secret HF_TOKEN timed out. Secrets can only be fetched when running from the Colab UI.'.\n", " warnings.warn(f\"\\nError while fetching `HF_TOKEN` secret value from your vault: '{str(e)}'.\")\n"]}, {"output_type": "display_data", "data": {"text/plain": ["modules.json: 0%| | 0.00/349 [00:00, ?B/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "9866c9cff1f74667b8a7a869152d7785"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["config_sentence_transformers.json: 0%| | 0.00/116 [00:00, ?B/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "ac56e415f3814066b44fd212a3cd8038"}}, "metadata": {}}, {"output_type": "stream", "name": "stderr", "text": ["Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.\n", "WARNING:huggingface_hub.utils._http:Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.\n"]}, {"output_type": "display_data", "data": {"text/plain": ["README.md: 0%| | 0.00/10.5k [00:00, ?B/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "fa3ffa577f9b4e1797858fbb853467da"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["sentence_bert_config.json: 0%| | 0.00/53.0 [00:00, ?B/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "45a5ac2c6cf148269bb2fad003277509"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["config.json: 0%| | 0.00/612 [00:00, ?B/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "1552fe0882b1450da22547852e18a433"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["model.safetensors: reconstructing file: 0%| | 0.00B / 90.9MB "], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "d313456d94a14bc79120a58b7cc4a7e8"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["model.safetensors: downloading bytes: | 0.00B "], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "16c51ee5f8224b9d8b76fbc9ca43fd0c"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["Loading weights: 0%| | 0/103 [00:00, ?it/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "b7a748b9553a4c0aac05036135f7e187"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["tokenizer_config.json: 0%| | 0.00/350 [00:00, ?B/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "0ec9cc1177af42c790f2b86302b1de81"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["vocab.txt: 0%| | 0.00/232k [00:00, ?B/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "0671180e87714c8ca19b2749c5166ea8"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["tokenizer.json: 0%| | 0.00/466k [00:00, ?B/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "c923e85e56e145deb9e1cb884c561fdd"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["special_tokens_map.json: 0%| | 0.00/112 [00:00, ?B/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "05ede907891d40d4ab3c446fb2f8293c"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["config.json: 0%| | 0.00/190 [00:00, ?B/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "9bc88e333ea541a388c8e09312c562d3"}}, "metadata": {}}, {"output_type": "stream", "name": "stdout", "text": ["Retriever accuracy on 26 unseen questions: 69%\n"]}], "source": ["embedder = SentenceTransformer(\"sentence-transformers/all-MiniLM-L6-v2\", device=device)\n", "train_qa = qa[qa.split == \"train\"].reset_index(drop=True)\n", "bank = train_qa.question.tolist() + [a for _, a, _ in FACTS] # known questions + the facts themselves\n", "bank_fid = train_qa.fid.tolist() + list(range(len(FACTS)))\n", "bank_vecs = embedder.encode(bank, normalize_embeddings=True, convert_to_numpy=True)\n", "\n", "def retrieve(question, k=2):\n", " sims = bank_vecs @ embedder.encode([question], normalize_embeddings=True, convert_to_numpy=True)[0]\n", " best = {}\n", " for i in np.argsort(-sims):\n", " best.setdefault(bank_fid[i], float(sims[i]))\n", " if len(best) == k: break\n", " fids = list(best)\n", " return fids, best[fids[0]] # fact ids (best first) and the top similarity score\n", "\n", "test = qa[qa.split == \"test\"]\n", "hits = [retrieve(q)[0][0] == f for q, f in zip(test.question, test.fid)]\n", "print(f\"Retriever accuracy on {len(test)} unseen questions: {np.mean(hits):.0%}\")"]}, {"cell_type": "markdown", "id": "85fdfbac", "metadata": {"id": "85fdfbac"}, "source": ["## 5 \u00b7 Training data and tokenization\n", "Input = retrieved resume facts + question. Target = the correct answer.\n", "The correct fact is always included, together with a second (distractor) fact, so the model learns to pick the right one."]}, {"cell_type": "code", "execution_count": 5, "id": "c0a6c0ca", "metadata": {"colab": {"base_uri": "https://localhost:8080/", "height": 612, "referenced_widgets": ["f09bae1f64084548bf9951170343720a", "d935eb8efd03440595057f3457fbe818", "70c21b8a9db1415487061fb2b930e516", "409463b9479b4e5c9321a5a2220c0b5c", "a716bb147c70485186566002956323a0", "2bf46025b9d949f7ab31c4ba562607d8", "2434a023ed624ffdb9633ad5b9dcba3f", "aeed263027964badb4cb087845dcd368", "f877657d105a40f9b0242d22b9dd4ec7", "1caa820c8a1b4459a9aa5ecb821f6fe3", "4a4874ed04c049cbbaa8262508ce3fa4", "52e3a5365ee04f8ba4384bcb62b76d47", "d91559cc312d427a9f66537b740cd55e", "6392a390d5c64c4f932d62f117a1d74b", "e86b1cc29e4541cd8dd2bc3701a91b9c", "5d8e8336fc034b339428026573080a5e", "c4ecf7aa5eb14831a8330d63a734d578", "6215580b0cc948688e9d3648cb61b741", "309ba14dd8a349c983cdf7fa2f0dfc8e", "86b26dd157be46d0bd6f02987af80021", "a12aceb039064ed1a0d8148fda377c68", "aca18478559c421aa2a8331447511fe4", "6a2e942d9c5f4b6d9cce82bc75fa499f", "0ed481068b75481193a0d816c5733054", "7bd1d789cd524816ae52ea59c27ed097", "ed558657eb0a4add8d769883c9d4bd53", "dfa9f1b5897c47b0b226d402bdbf113e", "3d34c1c4820a45a4b7f31f83a3290705", "115a8ae462504df6959a2eb805b11040", "3f90fdc2d289407d83e4f14b284af23c", "17d83789da1246c78f0ed8292eccb6ed", "e5d85b0602a54ac18d4264a79c0a6601", "f329bf7035784dedad4ef124250b68d0", "fa60f33823f44c3b9c5a469a338a8d69", "b0aafb7f846b4e738613ba6707125f88", "d6c937e369084a67a24652f48df3fe56", "2d8f9011c37d4523a956beebcd984e44", "2c9c94ebae104d7aadb3b16634b95592", "30e0cec2d34e4511b8d26fbaf744beb2", "94684244616a43ac9c997e07634221f3", "01aab09aca2f48b68dcc7b914dcc5b98", "6ba78bc1a8464036b3876427a57ed84b", "0947750e48124e5ba98713537799d70d", "e58c05b7cfa04c318cae0c026d78a701", "b0bf2a485392480a982c6dfdc9c1d730", "7e78e792550a42dc9f7aef410401d28d", "daaf77123b724a49b202de2e4a039e95", "2fb1a5acaf2d4f35aff3ce1eef11dd38", "36ea0ec3bc924cb6b20f68e4edcbcbc3", "e9c31c288c0a4a718bd13c3f82292ad1", "3de5047d4c2d4f3faa870c8dfd85ca1e", "cd1fe6c449814d4686f87aeca5c5c7e9", "20662133734840a7a5c1e23692a5a3f0", "31634ea4f2f14a1aaf26c0a6186acc4f", "adee0429e98f47f897f4be5dfa453525", "92d8d8c3ce154b2baf57312c1f0c5a1f", "4b08856ffd9145589a45b4c3c9b5b94c", "f5b1cc550dcb471a811b05cdde0a7625", "63160f3e6b134538927bd1ef64e45fbd", "836a02a814c14b44b20139f4cc0d3ecb", "e7092678769247c79431edde7d820fce", "4d883e4bacff4704a90e2c500e7b054b", "6de392a9133a4f1aa899f69f88e33abe", "ed8e3bd3e36242a9b44ec0607069340a", "590cbad13cb148b4a54837deab14927b", "b4b71cc1aa0e4751bc4bfd764dafe366", "f19c859cf5214e889ddafed53b931086", "b74401db16434f9e874889054cf76830", "b7f56b8b302f4be390fbcb8472ecdaee", "21d9ada20c094691a12cb14bb0bdde64", "8ed2124a089140038edbe691bea7834e", "89a4b78805334152b843411111f436e9", "2ff52c2f5bc64a819988d256ae5d0e72", "321923d490364d619de4561e26246886", "ea74ade9e4af486e94b11d6233809663", "8c1da7ba082142728ce12c767bb97f32", "1693a02700e04bc6ba0c8bd3e645639f", "87eb4335ce364aa6a1060a4a08534e22", "4688766ef5c943e3bb99f6eefee1005e", "32d8d89a6222406f816d258923ba1465", "f215c4c41c2943c497997a754ca1c9cd", "8c6f816e1e3d4ba3850b4e73572048db", "4f7f6772c8324d7ebc0326392190dc32", "0ad06f6b7a324a238a35aebe8487ec3e", "66796e8a6cc14a5fb229dbf570c8bb77", "9344b07db3e649ecb521b490980bf43e", "62eadbe40bc14df1ae2fe07def54f0e8", "9b9421c0c133440fbe29f291741d86c7", "24dcb947d3794a52810364b3beb5575e", "231e3a202c5b4e50a45d26f732002391", "abfdd302275541f986c0584db4cd5430", "b04141c4a9e6430eb7f38a4aafe35a80", "2e318c83e55a4c26bd51f755bba482b9", "3c2e674ec30942e98fa80cf22f89f0a4", "fb7875e0364048fa9ba20fdb4aef6c72", "a96feea202f4475ba5c80e30f8dab4de", "91c9b51ba8524e8685e9a17ec9daa3a0", "e1009e41bb1d45919261825e3e7e920e", "82227645348d4a118ad11ecf4f3a0e9f", "e3442400c9d445528ad040e98ced9f76", "0ddd70fa3a934125a28d9a37def0447f", "9b27f845754a46fda6aa82c16d754416", "9057c06b22344a1b9febfc411142b88a", "b728c8b3b6f641ce979aa16f0421e2d6", "0eab33de5b114ed084a30fb0810d357b", "c6ee0121d6bd41878314c30a66b6942b", "5f641ad4125448a2bd26fe5f606fcaf2", "f8e27d59c38f4b53b1d9f467cf639abb", "3ccc7d9a6d874a8890d24f3d4572e87b", "9d7cad2a3ca94caca069318353ea984b", "a48878013fa942aebd89b44e0f4fc28f", "fd4503e0d1a848c08f4a52d30c213c28", "fdea07775f0440ac9665ded716aaacbb", "126b72f8494542c29e878dc8894a97a0", "49feec745f034d2eaa900b01a3f93db5", "ddd0f6a33c27432fb60bbd8f2aa214b1", "fe649aaf00904014aed763ca362f3828", "c559333506334954bfc786f6eef0e66b", "a7c9d37ef3104c22bac7b056993d2f92", "a07ee19506ec45178b5b08607d1a38aa", "1ff7f3a2a7b5456791dbda28fd1b34a5", "8ff43826e5944628877ce41adca31ae7", "320eb99e89ee40e082524eca4930957c", "cdab09172bee4d22b51e34a0b97a0c6a", "7b6fc2b03de54e8585e54899ad58dd59", "e6d11234814b4bf287d6bb2b65585ec9", "7151f2ee629c45099e5f74dcba96c863", "4389a9a8def44ffbbce7c4cace20d1b0", "f8b17a90c8db4548a2b567cc4f1fa2c3", "bf28ff748d6341f6bbbd2fa2d770b736", "5eb1a1a7f2d34307a42e34e26ac3f2c4", "a2a01143c32d4666918663da3b051285"]}, "id": "c0a6c0ca", "executionInfo": {"status": "ok", "timestamp": 1790762393130, "user_tz": -330, "elapsed": 335, "user": {"displayName": "sudeep shetty", "userId": "15639451297137763781"}}, "outputId": "d6448062-eaa8-4468-bbcd-820f92699d30"}, "outputs": [{"output_type": "display_data", "data": {"text/plain": ["config.json: 0%| | 0.00/1.40k [00:00, ?B/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "f09bae1f64084548bf9951170343720a"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["tokenizer_config.json: 0%| | 0.00/2.54k [00:00, ?B/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "52e3a5365ee04f8ba4384bcb62b76d47"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["spiece.model: reconstructing file: 0%| | 0.00B / 792kB "], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "6a2e942d9c5f4b6d9cce82bc75fa499f"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["spiece.model: downloading bytes: | 0.00B "], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "fa60f33823f44c3b9c5a469a338a8d69"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["tokenizer.json: 0%| | 0.00/2.42M [00:00, ?B/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "b0bf2a485392480a982c6dfdc9c1d730"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["special_tokens_map.json: 0%| | 0.00/2.20k [00:00, ?B/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "92d8d8c3ce154b2baf57312c1f0c5a1f"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["model.safetensors: reconstructing file: 0%| | 0.00B / 990MB "], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "f19c859cf5214e889ddafed53b931086"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["model.safetensors: downloading bytes: | 0.00B "], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "87eb4335ce364aa6a1060a4a08534e22"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["Loading weights: 0%| | 0/282 [00:00, ?it/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "24dcb947d3794a52810364b3beb5575e"}}, "metadata": {}}, {"output_type": "stream", "name": "stderr", "text": ["[transformers] The tied weights mapping and config for this model specifies to tie shared.weight to lm_head.weight, but both are present in the checkpoints with different values, so we will NOT tie them. You should update the config with `tie_word_embeddings=False` to silence this warning.\n"]}, {"output_type": "display_data", "data": {"text/plain": ["generation_config.json: 0%| | 0.00/147 [00:00, ?B/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "e3442400c9d445528ad040e98ced9f76"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["Map: 0%| | 0/327 [00:00, ? examples/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "a48878013fa942aebd89b44e0f4fc28f"}}, "metadata": {}}, {"output_type": "display_data", "data": {"text/plain": ["Map: 0%| | 0/26 [00:00, ? examples/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "8ff43826e5944628877ce41adca31ae7"}}, "metadata": {}}, {"output_type": "stream", "name": "stdout", "text": ["DatasetDict({\n", " train: Dataset({\n", " features: ['input_ids', 'attention_mask', 'labels'],\n", " num_rows: 327\n", " })\n", " test: Dataset({\n", " features: ['input_ids', 'attention_mask', 'labels'],\n", " num_rows: 26\n", " })\n", "})\n"]}], "source": ["PROMPT = \"Resume information: {context}\\nQuestion: {question}\\nAnswer using only the resume information:\"\n", "\n", "def make_input(question, fid=None):\n", " fids, _ = retrieve(question)\n", " if fid is not None: # training: make sure the right fact is present, random order\n", " other = next((f for f in fids if f != fid), random.choice([i for i in range(len(FACTS)) if i != fid]))\n", " fids = [fid, other]; random.shuffle(fids)\n", " return PROMPT.format(context=\" \".join(FACTS[f][1] for f in fids), question=question)\n", "\n", "qa[\"input_text\"] = [make_input(q, f) for q, f in zip(qa.question, qa.fid)]\n", "\n", "tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)\n", "model = AutoModelForSeq2SeqLM.from_pretrained(MODEL_NAME)\n", "\n", "def tokenize(batch):\n", " enc = tokenizer(batch[\"input_text\"], max_length=256, truncation=True)\n", " enc[\"labels\"] = tokenizer(text_target=batch[\"answer\"], max_length=128, truncation=True)[\"input_ids\"]\n", " return enc\n", "\n", "data = DatasetDict({s: Dataset.from_pandas(qa[qa.split == s][[\"input_text\", \"answer\"]].reset_index(drop=True))\n", " for s in [\"train\", \"test\"]})\n", "tokenized = data.map(tokenize, batched=True, remove_columns=[\"input_text\", \"answer\"])\n", "print(tokenized)"]}, {"cell_type": "markdown", "id": "e09cd23b", "metadata": {"id": "e09cd23b"}, "source": ["## 6 \u00b7 Fine-tune FLAN-T5"]}, {"cell_type": "code", "execution_count": 6, "id": "384e2f86", "metadata": {"colab": {"base_uri": "https://localhost:8080/", "height": 782}, "id": "384e2f86", "executionInfo": {"status": "ok", "timestamp": 1790762592613, "user_tz": -330, "elapsed": 199481, "user": {"displayName": "sudeep shetty", "userId": "15639451297137763781"}}, "outputId": "7abb7e0e-6a87-4fe2-c423-1f393cbf872c"}, "outputs": [{"output_type": "display_data", "data": {"text/plain": [""], "text/html": ["\n", "
"], "image/png": "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E3d9DUKIoslNa0KICvPrr79afJTv6+vLRx99ZPHHf8OGDSQmJgLQv39/7r33XgAGDRrEzz//bE4yrl69ire3N/b29gC4u7vTpEkTWrVqhV6v57nnnquwuOfOnUv//v3p3bs3n376qbn822+/pUmTJvj7+7NixQoATp06BZiSusWLFwPg4OBAeHg4er0eV1dXHnvsMb744guys7P5+eefee2110qMIf/j7OzsbAwGA4qisHTpUvP+xYsX07ZtW8A0zGPcuHEA/PTTTwwdOpTevXvj6+trrt+uXbsSx5q6ubnRu3dv3NzczGWdO3cu0xRkwcHB5uTW29ubzZs3A7ffp6SkJNavXw+YktQxY8YA0LZtW/r378/KlSu5du0aERERPP744xw+fJjk5GSL12jevDn169enfv365jK9Xl+hY2lfeukl8z9dJ06cYNKkSRbXURq+vr4sXboUvV5Ply5dWLJkicU5FEVh7dq15vo//vijuU1jY2N5//33K+RahBCFScIrhKg0iYmJ/PXXXxZlBXuy7kyQ8xmNRmJiYujduzejR4/m3//+N9HR0XTo0AGdTkfz5s0ZOHAgEydOJCAg4K7j7Nq1KwCenp7mMg8PD5o0aQKYErl8+b1zV69e5fr16wBkZ2fTr18/q+c+duyY1fJly5bh7+9PYmIi06dP58CBA3z33Xd4enryySefkJiYaD6/k5OTOTEqGC+ges9gnz59zNteXl7m7fz36dSpU+axxnFxcdxzzz1Wz5P/Po0bN45t27ZZ7Fu4cGGlj2st6TpKo3v37uj1+iLPkZCQQFpaGlC4TXv06FGesIUQpSRDGoQQFWbEiBHk5OQQERGBk5MTRqORDz/80KJXq7TyP+J+7733+Omnn3jyySdp0aIFGo2GY8eO8cknnzBgwAByc3MBLG4kysvLM29fvXq1xNfK7+HUam//SnR1dbVaNz95K+t13Cn/prVHH33UfJMcYNGrm+/OhQ6q08IHBYeV2Nnd7kOpqPepLO7me6AirqMs56hObSiELZCEVwhRoezs7AgNDTV/PAzwzjvvmLebN29u3h4xYgRGo7HQIz09ndDQUHO9p59+mp9//pmYmBhSU1N54oknADhy5Ii5h7Pgx/L5Mx0oisLGjRsr5Tq9vb3NCU6dOnVITU0tdB15eXksXLiwxHMVTIiSkpIA8PHxMc/Hm56e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i4eHhwdgGm4wb948HnzwQRwdHWncuDHLly+3OP7w4cPcf//9ODo64uXlxfPPP09aWppFnW+++YY2bdqg1+sJCAhg7NixFvuvXr3Ko48+ipOTE82aNWPNmjWVe9FCCFHBJOEVQoga7J133uHxxx8nOjqaZ555hqeffppjx44BkJ6eTmhoKB4eHuzdu5dly5axadMmi4R23rx5vPzyyzz//PMcPnyYNWvW0LRpU4vXmDZtGk899RSHDh3ioYce4plnniEpKalKr1MIIe6Gxmg0GtUOQgghRGEjR47khx9+wGAwWJS/+eabvPnmm2g0Gl544QXmzZtn3te9e3c6duzIF198wVdffcUbb7zBxYsXcXZ2BmD9+vUMGjSIK1eu4OfnR926dRk1ahTTp0+3GoNGo+Htt9/mvffeA0xJdJ06dfj1119lLLEQosaQMbxCCFGN3XfffRYJLYCnp6d5u0ePHhb7evToQVRUFADHjh0jODjYnOwC9OrVC0VROH78OBqNhitXrvDAAw8UG0P79u3N287Ozri6upKQkFDeSxJCiConCa8QQlRjzs7OhYYYVBRHR8dS1bO3t7d4rtFoUBSlMkISQohKIWN4hRCiBtu1a1eh561atQKgVatWREdHk56ebt7/559/otVqadGiBS4uLjRs2JDNmzdXacxCCFHVpIdXCCGqsaysLOLi4izK7Ozs8Pb2BmDZsmV07tyZ3r178+OPP7Jnzx6+/vprAJ555hmmTJnCiBEjmDp1KomJiYwbN45nn30WPz8/AKZOncoLL7yAr68vDz74IKmpqfz555+MGzeuai9UCCEqkSS8QghRjUVERBAQEGBR1qJFC2JiYgDTDApLlizhpZdeIiAggJ9++onWrVsD4OTkxG+//caECRPo0qULTk5OPP7443z88cfmc40YMYLMzEw++eQTXn/9dby9vXniiSeq7gKFEKIKyCwNQghRQ2k0GlauXElYWJjaoQghRLUmY3iFEEIIIUStJgmvEEIIIYSo1WQMrxBC1FAyIk0IIUpHeniFEEIIIUStJgmvEEIIIYSo1SThFUIIIYQQtZokvEIIIYQQolaThFcIIYQQQtRqkvAKIYQQQohaTRJeIYQQQghRq0nCK4QQQggharX/B1sOnawxK5SHAAAAAElFTkSuQmCC\n"}, "metadata": {}}], "source": ["args = Seq2SeqTrainingArguments(\n", " output_dir=\"checkpoints\", learning_rate=3e-4, num_train_epochs=10,\n", " per_device_train_batch_size=8, per_device_eval_batch_size=8, weight_decay=0.01,\n", " eval_strategy=\"epoch\", logging_strategy=\"epoch\", save_strategy=\"no\", report_to=\"none\", fp16=False)\n", "\n", "trainer = Seq2SeqTrainer(model=model, args=args, train_dataset=tokenized[\"train\"], eval_dataset=tokenized[\"test\"],\n", " data_collator=DataCollatorForSeq2Seq(tokenizer, model=model))\n", "trainer.train()\n", "model.eval() # switch off training-mode randomness before answering\n", "\n", "logs = pd.DataFrame(trainer.state.log_history)\n", "tr, va = logs.dropna(subset=[\"loss\"]), logs.dropna(subset=[\"eval_loss\"])\n", "plt.figure(figsize=(8, 4))\n", "plt.plot(tr.epoch, tr.loss, \"o-\", lw=2, label=\"Training loss\")\n", "plt.plot(va.epoch, va.eval_loss, \"s-\", lw=2, label=\"Loss on unseen questions\")\n", "plt.xlabel(\"Epoch\"); plt.ylabel(\"Loss\"); plt.title(\"ResumeBot fine-tuning\", weight=\"bold\"); plt.grid(alpha=.3); plt.legend(); plt.savefig(\"loss_curve.png\", dpi=120, bbox_inches=\"tight\"); plt.show()"]}, {"cell_type": "markdown", "id": "ee0cdc60", "metadata": {"id": "ee0cdc60"}, "source": ["## 7 \u00b7 The ResumeBot answer function (with hallucination guards)"]}, {"cell_type": "code", "execution_count": 7, "id": "08df5f96", "metadata": {"colab": {"base_uri": "https://localhost:8080/", "height": 807}, "id": "08df5f96", "executionInfo": {"status": "ok", "timestamp": 1790762636662, "user_tz": -330, "elapsed": 44047, "user": {"displayName": "sudeep shetty", "userId": "15639451297137763781"}}, "outputId": "7343152d-e707-482b-e43f-223aa75dceeb"}, "outputs": [{"output_type": "execute_result", "data": {"text/plain": [" Question \\\n", "0 What is Sudeep's name? \n", "1 What are his technical skills? \n", "2 Which university is he studying at? \n", "3 Tell me about his best project. \n", "4 Does he know DAX? \n", "5 How many Prime Video titles did he analyze? \n", "6 Has Sudeep worked at any company? \n", "7 What certificates does he hold? \n", "8 Is he good at teamwork? \n", "9 Did he win any award? \n", "10 What is the capital of France? \n", "11 Who won the cricket world cup? \n", "\n", " ResumeBot answer \\\n", "0 His name is Sudeep. He is a Data Analyst and M.Sc. Data Science student based in Bengaluru. \n", "1 Sudeep's technical skills are Python, SQL, Power BI, Excel, Data Analysis, Data Visualization and Business Intelligence. \n", "2 Sudeep is currently pursuing an M.Sc. in Data Science at Dayananda Sagar University, from 2025 to 2027. \n", "3 The Career AI Intelligence System is an AI-powered resume analysis platform for career prediction, ATS evaluation and skill-gap identification that generates personalized career roadmaps. It uses Python, PostgreSQL, React and NLP. \n", "4 Sudeep builds interactive dashboards in Power BI and Excel, using DAX and Power Query, for example the Smart Attendance Analytics System and the Prime Video Analytics Dashboard. \n", "5 Prime Video Analytics Dashboard analyzed 9,651+ Prime Video titles to find trends in genres, ratings and release years. It was built with Power BI and Excel. \n", "6 Sudeep's resume does not list any job or internship yet. His experience comes from his academic and personal projects. \n", "7 Sudeep's certifications are TechA Python Developer and Introduction to Data Mining from Infosys, Data Science Foundation and Prompt Engineering from Great Learning, and Certified Secure Computer User from EC-Council. \n", "8 Sudeep's soft skills are analytical thinking, problem solving, communication, critical thinking, teamwork and time management. \n", "9 Sudeep won the Best Presentation Award at an Intercollegiate IT Fest in 2024 and successfully completed Tritiya Sopan in Scouts and Guides. \n", "10 I only answer questions about Sudeep's resume, so I won't guess about that. Try asking about his education, skills, projects, certifications or achievements. \n", "11 I only answer questions about Sudeep's resume, so I won't guess about that. Try asking about his education, skills, projects, certifications or achievements. \n", "\n", " Resume section \n", "0 Name \n", "1 Technical skills \n", "2 Masters \n", "3 Career AI project \n", "4 Power BI \n", "5 Prime Video project \n", "6 Work experience \n", "7 Certifications \n", "8 Soft skills \n", "9 Achievements \n", "10 Out of scope \n", "11 Out of scope "], "text/html": ["\n", "
\n", "
\n", "\n", "
\n", " \n", "
\n", "
\n", "
Question
\n", "
ResumeBot answer
\n", "
Resume section
\n", "
\n", " \n", " \n", "
\n", "
0
\n", "
What is Sudeep's name?
\n", "
His name is Sudeep. He is a Data Analyst and M.Sc. Data Science student based in Bengaluru.
\n", "
Name
\n", "
\n", "
\n", "
1
\n", "
What are his technical skills?
\n", "
Sudeep's technical skills are Python, SQL, Power BI, Excel, Data Analysis, Data Visualization and Business Intelligence.
\n", "
Technical skills
\n", "
\n", "
\n", "
2
\n", "
Which university is he studying at?
\n", "
Sudeep is currently pursuing an M.Sc. in Data Science at Dayananda Sagar University, from 2025 to 2027.
\n", "
Masters
\n", "
\n", "
\n", "
3
\n", "
Tell me about his best project.
\n", "
The Career AI Intelligence System is an AI-powered resume analysis platform for career prediction, ATS evaluation and skill-gap identification that generates personalized career roadmaps. It uses Python, PostgreSQL, React and NLP.
\n", "
Career AI project
\n", "
\n", "
\n", "
4
\n", "
Does he know DAX?
\n", "
Sudeep builds interactive dashboards in Power BI and Excel, using DAX and Power Query, for example the Smart Attendance Analytics System and the Prime Video Analytics Dashboard.
\n", "
Power BI
\n", "
\n", "
\n", "
5
\n", "
How many Prime Video titles did he analyze?
\n", "
Prime Video Analytics Dashboard analyzed 9,651+ Prime Video titles to find trends in genres, ratings and release years. It was built with Power BI and Excel.
\n", "
Prime Video project
\n", "
\n", "
\n", "
6
\n", "
Has Sudeep worked at any company?
\n", "
Sudeep's resume does not list any job or internship yet. His experience comes from his academic and personal projects.
\n", "
Work experience
\n", "
\n", "
\n", "
7
\n", "
What certificates does he hold?
\n", "
Sudeep's certifications are TechA Python Developer and Introduction to Data Mining from Infosys, Data Science Foundation and Prompt Engineering from Great Learning, and Certified Secure Computer User from EC-Council.
\n", "
Certifications
\n", "
\n", "
\n", "
8
\n", "
Is he good at teamwork?
\n", "
Sudeep's soft skills are analytical thinking, problem solving, communication, critical thinking, teamwork and time management.
\n", "
Soft skills
\n", "
\n", "
\n", "
9
\n", "
Did he win any award?
\n", "
Sudeep won the Best Presentation Award at an Intercollegiate IT Fest in 2024 and successfully completed Tritiya Sopan in Scouts and Guides.
\n", "
Achievements
\n", "
\n", "
\n", "
10
\n", "
What is the capital of France?
\n", "
I only answer questions about Sudeep's resume, so I won't guess about that. Try asking about his education, skills, projects, certifications or achievements.
\n", "
Out of scope
\n", "
\n", "
\n", "
11
\n", "
Who won the cricket world cup?
\n", "
I only answer questions about Sudeep's resume, so I won't guess about that. Try asking about his education, skills, projects, certifications or achievements.
\n", "
Out of scope
\n", "
\n", " \n", "
\n", "
\n", "
\n", "\n", "
\n", " \n", "\n", " \n", "\n", " \n", "
\n", "\n", "\n", "
\n", "
\n"], "application/vnd.google.colaboratory.intrinsic+json": {"type": "dataframe", "summary": "{\n \"name\": \"pd\",\n \"rows\": 12,\n \"fields\": [\n {\n \"column\": \"Question\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 12,\n \"samples\": [\n \"What is the capital of France?\",\n \"Did he win any award?\",\n \"What is Sudeep's name?\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"ResumeBot answer\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 11,\n \"samples\": [\n \"Prime Video Analytics Dashboard analyzed 9,651+ Prime Video titles to find trends in genres, ratings and release years. It was built with Power BI and Excel.\",\n \"His name is Sudeep. He is a Data Analyst and M.Sc. Data Science student based in Bengaluru.\",\n \"Sudeep won the Best Presentation Award at an Intercollegiate IT Fest in 2024 and successfully completed Tritiya Sopan in Scouts and Guides.\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Resume section\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 11,\n \"samples\": [\n \"Prime Video project\",\n \"Name\",\n \"Achievements\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}"}}, "metadata": {}, "execution_count": 7}], "source": ["PERSON_WORDS = {\"sudeep\", \"candidate\", \"he\", \"his\", \"him\", \"you\", \"your\", \"resume\", \"cv\", \"profile\"}\n", "STOP = set(\"a an the is are was were of in on at to for and or with by from as it this that his he sudeep s\".split())\n", "GREETING = \"Hi! \ud83d\udc4b I'm ResumeBot. Ask me anything about Sudeep: his education, skills, projects, certifications or achievements.\"\n", "OUT_OF_SCOPE = (\"I only answer questions about Sudeep's resume, so I won't guess about that. \"\n", " \"Try asking about his education, skills, projects, certifications or achievements.\")\n", "\n", "def words(text):\n", " return {w for w in re.findall(r\"[a-z0-9]+\", text.lower()) if w not in STOP}\n", "\n", "def generate(prompt):\n", " inputs = tokenizer(prompt, return_tensors=\"pt\", truncation=True, max_length=256).to(model.device)\n", " with torch.no_grad():\n", " out = model.generate(**inputs, max_new_tokens=128, num_beams=4, no_repeat_ngram_size=3, early_stopping=True)\n", " return tokenizer.decode(out[0], skip_special_tokens=True).strip()\n", "\n", "def answer(question):\n", " q = question.strip()\n", " ql = re.findall(r\"[a-z]+\", q.lower())\n", " if not q or set(ql) <= {\"hi\", \"hello\", \"hey\", \"hii\", \"good\", \"morning\", \"evening\", \"namaste\"}:\n", " return GREETING, \"Greeting\"\n", " if set(ql) & {\"thanks\", \"thank\", \"thankyou\"}:\n", " return \"You're welcome! Ask me anything else about Sudeep. \ud83d\ude0a\", \"Greeting\"\n", " fids, score = retrieve(q)\n", " about_him = bool(PERSON_WORDS & set(ql))\n", " if score < (0.35 if about_him else 0.55): # guard 1: not about the resume -> don't guess\n", " return OUT_OF_SCOPE, \"Out of scope\"\n", " context = \" \".join(FACTS[f][1] for f in fids)\n", " reply = generate(PROMPT.format(context=context, question=q))\n", " grounded = len(words(reply) - words(context)) <= 1 and len(reply) > 3\n", " if not grounded: # guard 2: answer must come from the resume text\n", " reply = FACTS[fids[0]][1]\n", " return reply, FACTS[fids[0]][0]\n", "\n", "checks = [\"What is Sudeep's name?\", \"What are his technical skills?\", \"Which university is he studying at?\",\n", " \"Tell me about his best project.\", \"Does he know DAX?\", \"How many Prime Video titles did he analyze?\",\n", " \"Has Sudeep worked at any company?\", \"What certificates does he hold?\", \"Is he good at teamwork?\",\n", " \"Did he win any award?\", \"What is the capital of France?\", \"Who won the cricket world cup?\"]\n", "pd.set_option(\"display.max_colwidth\", None)\n", "pd.DataFrame([(q, *answer(q)) for q in checks], columns=[\"Question\", \"ResumeBot answer\", \"Resume section\"])"]}, {"cell_type": "markdown", "id": "8d4bd7ec", "metadata": {"id": "8d4bd7ec"}, "source": ["## 8 \u00b7 Evaluation on questions the model never saw during training"]}, {"cell_type": "code", "execution_count": 8, "id": "4b88c8cf", "metadata": {"colab": {"base_uri": "https://localhost:8080/", "height": 901}, "id": "4b88c8cf", "executionInfo": {"status": "ok", "timestamp": 1790762706999, "user_tz": -330, "elapsed": 70362, "user": {"displayName": "sudeep shetty", "userId": "15639451297137763781"}}, "outputId": "86540008-4963-4dee-807c-bdc9cb5a682b"}, "outputs": [{"output_type": "stream", "name": "stdout", "text": ["Unseen test questions: 26\n", "Retriever picked the right resume fact: 69%\n", "Answer exactly matches the resume answer: 69%\n"]}, {"output_type": "execute_result", "data": {"text/plain": [" Unseen question \\\n", "0 Who is Sudeep? \n", "1 What role is Sudeep looking for? \n", "2 Where does Sudeep live? \n", "3 How can I contact Sudeep? \n", "4 What is Sudeep's GitHub? \n", "5 Give a short summary of Sudeep. \n", "6 What makes Sudeep a good fit? \n", "7 What is Sudeep passionate about? \n", "8 What is Sudeep's technical experience? \n", "9 What degrees does Sudeep have? \n", "\n", " ResumeBot answer \\\n", "0 His name is Sudeep. He is a Data Analyst and M.Sc. Data Science student based in Bengaluru. \n", "1 Sudeep is a Data Analyst with a focus on Data Science. \n", "2 Sudeep is based in Bengaluru, Karnataka. \n", "3 You can email Sudeep at shettysudeep27@gmail.com. \n", "4 Sudeep's GitHub is github.com/sudeepshetty4247-stack. \n", "5 Sudeep is an M.Sc. Data Science student with a strong foundation in data analytics, business intelligence and machine learning. He turns raw data into actionable insights using Power BI dashboards, SQL-based analysis and Python. \n", "6 Sudeep is a Data Analyst with a focus on Data Science. \n", "7 Sudeep is passionate about using data to solve business challenges and enable data-driven decision-making. \n", "8 Sudeep has built analytical and AI-powered projects focused on decision support, predictive modeling and process optimization. \n", "9 Sudeep's certifications are TechA Python Developer and Introduction to Data Mining from Infosys, Data Science Foundation and Prompt Engineering from Great Learning, and Certified Secure Computer User from EC-Council. \n", "\n", " Expected answer \n", "0 His name is Sudeep. He is a Data Analyst and M.Sc. Data Science student based in Bengaluru. \n", "1 Sudeep is a Data Analyst with a focus on Data Science. \n", "2 Sudeep is based in Bengaluru, Karnataka. \n", "3 You can email Sudeep at shettysudeep27@gmail.com. \n", "4 Sudeep's LinkedIn is linkedin.com/in/sudeep-shetty-0aa23a1b0 and his GitHub is github.com/sudeepshetty4247-stack. \n", "5 Sudeep is an M.Sc. Data Science student with a strong foundation in data analytics, business intelligence and machine learning. He turns raw data into actionable insights using Power BI dashboards, SQL-based analysis and Python. \n", "6 Sudeep combines data analytics, business intelligence and machine learning skills with hands-on projects in Power BI, SQL, Python and AI, and he is passionate about solving business problems with data. \n", "7 Sudeep is passionate about using data to solve business challenges and enable data-driven decision-making. \n", "8 Sudeep has built analytical and AI-powered projects focused on decision support, predictive modeling and process optimization. \n", "9 Sudeep is pursuing an M.Sc. in Data Science at Dayananda Sagar University (2025-2027). He completed a Bachelor of Computer Applications (BCA) at Dr. B. B. Hegde First Grade College (2021-2024). "], "text/html": ["\n", "
\n", "
\n", "\n", "
\n", " \n", "
\n", "
\n", "
Unseen question
\n", "
ResumeBot answer
\n", "
Expected answer
\n", "
\n", " \n", " \n", "
\n", "
0
\n", "
Who is Sudeep?
\n", "
His name is Sudeep. He is a Data Analyst and M.Sc. Data Science student based in Bengaluru.
\n", "
His name is Sudeep. He is a Data Analyst and M.Sc. Data Science student based in Bengaluru.
\n", "
\n", "
\n", "
1
\n", "
What role is Sudeep looking for?
\n", "
Sudeep is a Data Analyst with a focus on Data Science.
\n", "
Sudeep is a Data Analyst with a focus on Data Science.
\n", "
\n", "
\n", "
2
\n", "
Where does Sudeep live?
\n", "
Sudeep is based in Bengaluru, Karnataka.
\n", "
Sudeep is based in Bengaluru, Karnataka.
\n", "
\n", "
\n", "
3
\n", "
How can I contact Sudeep?
\n", "
You can email Sudeep at shettysudeep27@gmail.com.
\n", "
You can email Sudeep at shettysudeep27@gmail.com.
\n", "
\n", "
\n", "
4
\n", "
What is Sudeep's GitHub?
\n", "
Sudeep's GitHub is github.com/sudeepshetty4247-stack.
\n", "
Sudeep's LinkedIn is linkedin.com/in/sudeep-shetty-0aa23a1b0 and his GitHub is github.com/sudeepshetty4247-stack.
\n", "
\n", "
\n", "
5
\n", "
Give a short summary of Sudeep.
\n", "
Sudeep is an M.Sc. Data Science student with a strong foundation in data analytics, business intelligence and machine learning. He turns raw data into actionable insights using Power BI dashboards, SQL-based analysis and Python.
\n", "
Sudeep is an M.Sc. Data Science student with a strong foundation in data analytics, business intelligence and machine learning. He turns raw data into actionable insights using Power BI dashboards, SQL-based analysis and Python.
\n", "
\n", "
\n", "
6
\n", "
What makes Sudeep a good fit?
\n", "
Sudeep is a Data Analyst with a focus on Data Science.
\n", "
Sudeep combines data analytics, business intelligence and machine learning skills with hands-on projects in Power BI, SQL, Python and AI, and he is passionate about solving business problems with data.
\n", "
\n", "
\n", "
7
\n", "
What is Sudeep passionate about?
\n", "
Sudeep is passionate about using data to solve business challenges and enable data-driven decision-making.
\n", "
Sudeep is passionate about using data to solve business challenges and enable data-driven decision-making.
\n", "
\n", "
\n", "
8
\n", "
What is Sudeep's technical experience?
\n", "
Sudeep has built analytical and AI-powered projects focused on decision support, predictive modeling and process optimization.
\n", "
Sudeep has built analytical and AI-powered projects focused on decision support, predictive modeling and process optimization.
\n", "
\n", "
\n", "
9
\n", "
What degrees does Sudeep have?
\n", "
Sudeep's certifications are TechA Python Developer and Introduction to Data Mining from Infosys, Data Science Foundation and Prompt Engineering from Great Learning, and Certified Secure Computer User from EC-Council.
\n", "
Sudeep is pursuing an M.Sc. in Data Science at Dayananda Sagar University (2025-2027). He completed a Bachelor of Computer Applications (BCA) at Dr. B. B. Hegde First Grade College (2021-2024).
\n", "
\n", " \n", "
\n", "
\n", "
\n", "\n", "
\n", " \n", "\n", " \n", "\n", " \n", "
\n", "\n", "\n", "
\n", "
\n"], "application/vnd.google.colaboratory.intrinsic+json": {"type": "dataframe", "summary": "{\n \"name\": \"pd\",\n \"rows\": 10,\n \"fields\": [\n {\n \"column\": \"Unseen question\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 10,\n \"samples\": [\n \"What is Sudeep's technical experience?\",\n \"What role is Sudeep looking for?\",\n \"Give a short summary of Sudeep.\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"ResumeBot answer\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 9,\n \"samples\": [\n \"Sudeep has built analytical and AI-powered projects focused on decision support, predictive modeling and process optimization.\",\n \"Sudeep is a Data Analyst with a focus on Data Science.\",\n \"Sudeep is an M.Sc. Data Science student with a strong foundation in data analytics, business intelligence and machine learning. He turns raw data into actionable insights using Power BI dashboards, SQL-based analysis and Python.\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Expected answer\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 10,\n \"samples\": [\n \"Sudeep has built analytical and AI-powered projects focused on decision support, predictive modeling and process optimization.\",\n \"Sudeep is a Data Analyst with a focus on Data Science.\",\n \"Sudeep is an M.Sc. Data Science student with a strong foundation in data analytics, business intelligence and machine learning. He turns raw data into actionable insights using Power BI dashboards, SQL-based analysis and Python.\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}"}}, "metadata": {}, "execution_count": 8}], "source": ["results = [(q, answer(q)[0], a) for q, a in zip(test.question, test.answer)]\n", "exact = np.mean([r == a for _, r, a in results])\n", "print(f\"Unseen test questions: {len(results)}\")\n", "print(f\"Retriever picked the right resume fact: {np.mean(hits):.0%}\")\n", "print(f\"Answer exactly matches the resume answer: {exact:.0%}\")\n", "pd.DataFrame(results, columns=[\"Unseen question\", \"ResumeBot answer\", \"Expected answer\"]).head(10)"]}, {"cell_type": "markdown", "id": "dcfc2124", "metadata": {"id": "dcfc2124"}, "source": ["## 9 \u00b7 Save the fine-tuned model"]}, {"cell_type": "code", "execution_count": 9, "id": "75d2496f", "metadata": {"colab": {"base_uri": "https://localhost:8080/", "height": 66, "referenced_widgets": ["25c31b82f6244db4be530952f6aec685", "56217bfe09664c25865f889faeb2b84c", "953edc60db374e6c8f591d2777c04658", "c1329c0fc09747bf99e8e262424b949b", "00f2462ba85b49259e7dfae00fd6e9df", "b7624ed1a37e4e20b1844138371e6394", "cbe787661b0b46c988872d47243efebc", "df2273cc194b4c488a4ede50d8de727a", "b33c394cf64f42dcac1676eafb383b3a", "7ff9b75549d94aa2a91489f9959ba818", "7940ef26c93f4db8931b3317808d8c85"]}, "id": "75d2496f", "executionInfo": {"status": "ok", "timestamp": 1790762721956, "user_tz": -330, "elapsed": 14961, "user": {"displayName": "sudeep shetty", "userId": "15639451297137763781"}}, "outputId": "d7685f13-ea9d-44d4-dc24-616e634fc209"}, "outputs": [{"output_type": "display_data", "data": {"text/plain": ["Writing model shards: 0%| | 0/1 [00:00, ?it/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "25c31b82f6244db4be530952f6aec685"}}, "metadata": {}}, {"output_type": "stream", "name": "stdout", "text": ["Saved to ./my_resume_llm\n"]}], "source": ["model.save_pretrained(\"my_resume_llm\"); tokenizer.save_pretrained(\"my_resume_llm\")\n", "qa.to_csv(\"my_resume_llm/resume_qa_dataset.csv\", index=False)\n", "print(\"Saved to ./my_resume_llm\")"]}, {"cell_type": "markdown", "id": "5044ae18", "metadata": {"id": "5044ae18"}, "source": ["## 10 \u00b7 \ud83d\ude80 Launch the ResumeBot website\n", "A public **https://\u2026.gradio.live** link is printed below. Open it on any device. It works while this Colab tab is open."]}, {"cell_type": "code", "execution_count": 10, "id": "21a4328f", "metadata": {"colab": {"base_uri": "https://localhost:8080/", "height": 611}, "id": "21a4328f", "executionInfo": {"status": "ok", "timestamp": 1790762725614, "user_tz": -330, "elapsed": 3661, "user": {"displayName": "sudeep shetty", "userId": "15639451297137763781"}}, "outputId": "f083c43a-cc5f-453d-befb-4687c8494e4a"}, "outputs": [{"output_type": "stream", "name": "stdout", "text": ["Colab notebook detected. To show errors in colab notebook, set debug=True in launch()\n", "* Running on public URL: https://eacb2747b245f91104.gradio.live\n", "\n", "This share link is temporary and will last for up to 1 week (best effort). For free permanent hosting and GPU upgrades, run `gradio deploy` from the terminal in the working directory to deploy to Hugging Face Spaces (https://huggingface.co/spaces)\n"]}, {"output_type": "display_data", "data": {"text/plain": [""], "text/html": [""]}, "metadata": {}}, {"output_type": "execute_result", "data": {"text/plain": []}, "metadata": {}, "execution_count": 10}], "source": ["import gradio as gr\n", "\n", "CSS = \"\"\"\n", ".gradio-container {max-width: 1100px !important; margin: auto;}\n", "#hero {background: linear-gradient(135deg,#4f46e5 0%,#7c3aed 55%,#db2777 100%); border-radius: 18px; padding: 28px 32px; color: white;}\n", "#hero h1 {margin: 0; font-size: 34px; color: white;}\n", "#hero p {margin: 6px 0 0; opacity: .92; font-size: 16px; color: white;}\n", ".card {border: 1px solid var(--border-color-primary); border-radius: 16px; padding: 18px 20px; background: var(--block-background-fill);}\n", ".card h3 {margin: 0 0 8px; font-size: 15px; text-transform: uppercase; letter-spacing: .06em; opacity: .7;}\n", ".chip {display: inline-block; padding: 4px 10px; margin: 3px 4px 3px 0; border-radius: 999px; font-size: 13px;\n", " background: rgba(99,102,241,.12); border: 1px solid rgba(99,102,241,.35);}\n", ".meta {font-size: 14px; line-height: 1.8;}\n", "footer {display: none !important;}\n", "\"\"\"\n", "P = PROFILE\n", "HERO = f\"\"\"
\ud83e\udd16 ResumeBot
\n", "
Chat with my personal AI, a FLAN-T5 model fine-tuned on my resume. It answers only from verified resume facts.
\n", "1\ufe0f\u20e3 Finds the matching resume section 2\ufe0f\u20e3 Fine-tuned FLAN-T5 writes the answer 3\ufe0f\u20e3 Answer is checked against the resume
\"\"\"\n", "\n", "def chat(message, history):\n", " reply, source = answer(message)\n", " if source in (\"Greeting\",):\n", " return reply\n", " return f\"{reply}\\n\\n\ud83d\udcc4 Resume section: {source}\" if source != \"Out of scope\" else f\"{reply}\\n\\n\ud83d\udee1\ufe0f Out of scope\"\n", "\n", "theme = gr.themes.Soft(primary_hue=\"indigo\", secondary_hue=\"violet\", font=[gr.themes.GoogleFont(\"Inter\"), \"sans-serif\"])\n", "style = dict(theme=theme, css=CSS)\n", "new_gradio = \"css\" in inspect.signature(gr.Blocks.launch).parameters # Gradio 6 moved theme/css to launch()\n", "\n", "with gr.Blocks(title=\"ResumeBot | Sudeep\", **({} if new_gradio else style)) as demo:\n", " gr.HTML(HERO)\n", " with gr.Row(equal_height=False):\n", " with gr.Column(scale=1, min_width=280):\n", " gr.HTML(PROFILE_CARD)\n", " with gr.Column(scale=2):\n", " gr.ChatInterface(\n", " fn=chat,\n", " chatbot=gr.Chatbot(height=460, show_label=False, placeholder=\"Ask me about Sudeep \ud83d\udc47\"),\n", " textbox=gr.Textbox(placeholder=\"e.g. What projects has Sudeep built?\", show_label=False, scale=7),\n", " examples=[\"Tell me about Sudeep\", \"What are his technical skills?\", \"What is his best project?\",\n", " \"Which certifications does he have?\", \"Why should we hire him?\", \"Has he done any internships?\"],\n", " )\n", "\n", "demo.launch(share=True, **(style if new_gradio else {}))"]}, {"cell_type": "markdown", "id": "8f04507a", "metadata": {"id": "8f04507a"}, "source": ["## 11 \u00b7 \ud83d\udce6 Publish the whole project on Hugging Face\n", "Pushes everything to your Hugging Face account, with a description page (README) for each:\n", "\n", "| Repo | Contents |\n", "|---|---|\n", "| `your-name/ResumeBot` (model) | Fine-tuned model, **app.py** (full ResumeBot + website), knowledge base, requirements, notebook, loss chart, detailed model card |\n", "| `your-name/resumebot-dataset` (dataset) | All Q&A pairs + resume facts, with a dataset card |\n", "\n", "Get a token: **huggingface.co \u2192 profile picture \u2192 Settings \u2192 Access Tokens \u2192 Create new token \u2192 Write**. The token decides the account."]}, {"cell_type": "code", "execution_count": null, "id": "5e8dd295", "metadata": {"colab": {"base_uri": "https://localhost:8080/", "height": 84, "referenced_widgets": ["53c96a549b0040c3bc3949b983ac7012", "ef98135d3d1a4567ba5cd9f93396e947", "0234d8a8190f49c39844216432d8a787", "e36eeda35cc4408f90730a7780321cca", "6a0885e62a824d09841db2426150ca25", "5c82962a15974d18b5027602eb8d9e23", "6a71f20fdb364179950fb966323f7ec2", "a81b92d70460456e97ada8df54124d35", "e891bda86ed44b08832d275283d401cd", "6b1d641b82c6497a8c3fd51934a8f5dd", "aa4cdace00454ed6852697d794e815cf"]}, "id": "5e8dd295", "outputId": "daf69a8b-82c6-41bc-81f1-6a51e00f11f2"}, "outputs": [{"output_type": "stream", "name": "stdout", "text": ["Paste your Hugging Face WRITE token: \u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\n", "Hugging Face account: sudeep1610\n"]}, {"output_type": "display_data", "data": {"text/plain": ["Writing model shards: 0%| | 0/1 [00:00, ?it/s]"], "application/vnd.jupyter.widget-view+json": {"version_major": 2, "version_minor": 0, "model_id": "53c96a549b0040c3bc3949b983ac7012"}}, "metadata": {}}], "source": ["import os, json, shutil, getpass\n", "from huggingface_hub import HfApi\n", "\n", "api = HfApi(token=getpass.getpass(\"Paste your Hugging Face WRITE token: \").strip())\n", "user = api.whoami()[\"name\"]\n", "MODEL_REPO, DATA_REPO = f\"{user}/ResumeBot\", f\"{user}/resumebot-dataset\"\n", "print(\"Hugging Face account:\", user)\n", "\n", "# ---------- folders ----------\n", "for d in [\"hf_model\", \"hf_dataset\"]:\n", " shutil.rmtree(d, ignore_errors=True); os.makedirs(d)\n", "model.save_pretrained(\"hf_model\"); tokenizer.save_pretrained(\"hf_model\")\n", "\n", "# ---------- knowledge base used by app.py ----------\n", "json.dump({\"facts\": FACTS, \"profile\": PROFILE, \"prompt\": PROMPT, \"bank\": bank, \"bank_fid\": [int(f) for f in bank_fid]},\n", " open(\"hf_model/bot_config.json\", \"w\"), indent=1)\n", "shutil.copy(\"loss_curve.png\", \"hf_model/loss_curve.png\")\n", "open(\"hf_model/requirements.txt\", \"w\").write(\"torch\\ntransformers\\nsentence-transformers\\ngradio\\nhuggingface_hub\\nnumpy\\n\")\n", "\n", "# ---------- the notebook itself (works inside Google Colab) ----------\n", "try:\n", " from google.colab import _message\n", " json.dump(_message.blocking_request(\"get_ipynb\", timeout_sec=30)[\"ipynb\"], open(\"hf_model/ResumeBot_Pro.ipynb\", \"w\"))\n", " print(\"Notebook added.\")\n", "except Exception:\n", " print(\"Notebook not added automatically (upload it by hand later: repo \u2192 Files \u2192 Add file \u2192 Upload).\")\n", "\n", "# ---------- dataset files ----------\n", "qa[[\"topic\", \"question\", \"answer\", \"split\"]].to_csv(\"hf_dataset/resume_qa_dataset.csv\", index=False)\n", "json.dump([{\"topic\": t, \"answer\": a, \"questions\": q} for t, a, q in FACTS], open(\"hf_dataset/resume_facts.json\", \"w\"), indent=1)\n", "\n", "# ---------- results for the model card ----------\n", "R = dict(train_loss=tr.loss.iloc[-1], test_loss=va.eval_loss.iloc[-1], retrieval=np.mean(hits), exact=exact,\n", " n_facts=len(FACTS), n_qa=len(qa), n_train=int(sum(qa.split == \"train\")), n_test=int(sum(qa.split == \"test\")),\n", " epochs=int(args.num_train_epochs), lr=args.learning_rate, base=MODEL_NAME)\n", "print(R)"]}, {"cell_type": "code", "source": [], "metadata": {"id": "xHjPvfzbxsBo"}, "id": "xHjPvfzbxsBo", "execution_count": null, "outputs": []}, {"cell_type": "code", "source": [], "metadata": {"id": "MhbR8kzkxr0O"}, "id": "MhbR8kzkxr0O", "execution_count": null, "outputs": []}, {"cell_type": "markdown", "id": "52372c67", "metadata": {"id": "52372c67"}, "source": ["**Description pages (README cards)**"]}, {"cell_type": "code", "execution_count": null, "id": "55e4124d", "metadata": {"id": "55e4124d"}, "outputs": [], "source": ["open(\"hf_model/README.md\", \"w\").write(f\"\"\"---\n", "license: apache-2.0\n", "language: en\n", "base_model: {R['base']}\n", "datasets: [{DATA_REPO}]\n", "library_name: transformers\n", "tags: [flan-t5, fine-tuned, resume, chatbot, rag, retrieval-augmented-generation, gradio, personal-assistant]\n", "---\n", "\n", "# \ud83e\udd16 ResumeBot: a personalised LLM fine-tuned on my resume\n", "\n", "**ResumeBot** is a chatbot that answers questions about **{PROFILE['name']}** ({PROFILE['role']}).\n", "It is Google's pretrained **{R['base']}** model **fine-tuned on {R['n_qa']} question\u2013answer pairs built from my resume**,\n", "combined with a semantic retriever and two hallucination guards so it only says things that are actually on my resume.\n", "\n", "> *\"What are Sudeep's technical skills?\"* \u2192 *\"Sudeep's technical skills are Python, SQL, Power BI, Excel, Data Analysis,\n", "> Data Visualization and Business Intelligence.\"* \ud83d\udcc4 *Resume section: Technical skills*\n", "\n", "## How it works\n", "\n", "```\n", "Question\n", " \u2193\n", "Semantic retriever (all-MiniLM-L6-v2) finds the most relevant resume facts\n", " \u2193\n", "Out-of-scope guard: not about the resume? \u2192 polite refusal (no guessing)\n", " \u2193\n", "Fine-tuned FLAN-T5 writes the answer from those facts\n", " \u2193\n", "Grounding check: answer contains words not in the resume? \u2192 return the verified resume fact\n", " \u2193\n", "Answer + resume section (Gradio web app)\n", "```\n", "\n", "This approach is called **retrieval-augmented fine-tuning**: the model is fine-tuned to answer from retrieved resume text,\n", "which keeps answers accurate even for questions worded in new ways.\n", "\n", "## Training details\n", "\n", "| | |\n", "|---|---|\n", "| Base model | [`{R['base']}`](https://huggingface.co/{R['base']}) (pretrained by Google) |\n", "| Method | Supervised fine-tuning (seq2seq) with retrieved context |\n", "| Knowledge base | {R['n_facts']} resume facts |\n", "| Dataset | [{DATA_REPO}](https://huggingface.co/datasets/{DATA_REPO}): {R['n_qa']} Q&A pairs ({R['n_train']} train / {R['n_test']} unseen test) |\n", "| Epochs / learning rate / batch size | {R['epochs']} / {R['lr']} / 8 |\n", "| Hardware | Google Colab (free T4 GPU) |\n", "| Retriever | sentence-transformers/all-MiniLM-L6-v2 |\n", "\n", "## Results (on questions never seen during training)\n", "\n", "| Metric | Value |\n", "|---|---|\n", "| Final training loss | {R['train_loss']:.4f} |\n", "| Loss on unseen questions | {R['test_loss']:.4f} |\n", "| Retriever picked the correct resume fact | {R['retrieval']:.0%} |\n", "| Answer exactly matches the resume answer | {R['exact']:.0%} |\n", "\n", "\n", "\n", "## Hallucination guards\n", "1. **Retrieval:** the model only sees the resume facts relevant to the question.\n", "2. **Grounding check:** any answer containing information that is not in the retrieved resume text is replaced by the verified fact.\n", "3. **Out-of-scope guard:** questions unrelated to the resume (e.g. *\"What is the capital of France?\"*) get a polite refusal.\n", "\n", "## Use it\n", "\n", "**Run the full ResumeBot web app (Gradio):**\n", "```bash\n", "git clone https://huggingface.co/{MODEL_REPO}\n", "cd ResumeBot\n", "pip install -r requirements.txt\n", "python app.py\n", "```\n", "\n", "**Use only the fine-tuned model in Python:**\n", "```python\n", "from transformers import AutoTokenizer, AutoModelForSeq2SeqLM\n", "tok = AutoTokenizer.from_pretrained(\"{MODEL_REPO}\")\n", "model = AutoModelForSeq2SeqLM.from_pretrained(\"{MODEL_REPO}\")\n", "prompt = (\"Resume information: Sudeep's technical skills are Python, SQL, Power BI, Excel, Data Analysis, \"\n", " \"Data Visualization and Business Intelligence.\\\\nQuestion: What tools does he know?\\\\n\"\n", " \"Answer using only the resume information:\")\n", "out = model.generate(**tok(prompt, return_tensors=\"pt\"), max_new_tokens=128, num_beams=4)\n", "print(tok.decode(out[0], skip_special_tokens=True))\n", "```\n", "\n", "## Files\n", "\n", "| File | What it is |\n", "|---|---|\n", "| `model.safetensors`, `config.json`, `tokenizer*` | The fine-tuned FLAN-T5 model |\n", "| `app.py` | Complete ResumeBot: retriever + fine-tuned model + guards + Gradio website |\n", "| `bot_config.json` | Knowledge base: resume facts, profile, prompt, known questions |\n", "| `requirements.txt` | Python packages needed to run `app.py` |\n", "| `ResumeBot_Pro.ipynb` | Training notebook (Google Colab) |\n", "| `loss_curve.png` | Training / test loss chart |\n", "\n", "## Limitations\n", "- Knows only what is written in the resume; it cannot answer about anything else by design.\n", "- Small academic project: trained on a small dataset, so unusual phrasings may occasionally match the wrong section.\n", "\n", "## Author\n", "**{PROFILE['name']}**, {PROFILE['role']} \u00b7 {PROFILE['education']}\n", "[LinkedIn](https://{PROFILE['linkedin']}) \u00b7 [GitHub](https://{PROFILE['github']})\n", "\"\"\")\n", "\n", "open(\"hf_dataset/README.md\", \"w\").write(f\"\"\"---\n", "license: cc-by-4.0\n", "language: en\n", "task_categories: [question-answering, text2text-generation]\n", "tags: [resume, personal-assistant, chatbot, fine-tuning]\n", "size_categories: [n<1K]\n", "---\n", "\n", "# \ud83d\udcc4 ResumeBot dataset\n", "\n", "Question\u2013answer pairs about **{PROFILE['name']}'s** resume, used to fine-tune [{MODEL_REPO}](https://huggingface.co/{MODEL_REPO}).\n", "\n", "## Files\n", "\n", "| File | Rows | Description |\n", "|---|---|---|\n", "| `resume_qa_dataset.csv` | {R['n_qa']} | `topic`, `question`, `answer`, `split` (`train` / `test`) |\n", "| `resume_facts.json` | {R['n_facts']} | Each resume fact with its answer and the base questions written for it |\n", "\n", "## How it was built\n", "1. The resume was split into {R['n_facts']} facts (education, skills, each project, certifications, achievements, and more).\n", " Every answer is copied from the resume; nothing is invented.\n", "2. For each fact, several natural questions were written (e.g. *\"What are the candidate's skills?\"*, *\"What tools does the candidate know?\"*).\n", "3. Every question was automatically rewritten in three styles (*the candidate*, *Sudeep*, *he/his*).\n", "4. One reworded question per fact was held out as an **unseen test set** ({R['n_test']} questions).\n", "\n", "## Example\n", "\n", "| question | answer |\n", "|---|---|\n", "| What are Sudeep's technical skills? | Sudeep's technical skills are Python, SQL, Power BI, Excel, Data Analysis, Data Visualization and Business Intelligence. |\n", "| Where did he do the BCA? | Sudeep completed a Bachelor of Computer Applications (BCA) at Dr. B. B. Hegde First Grade College from 2021 to 2024. |\n", "\n", "## Load it\n", "```python\n", "from datasets import load_dataset\n", "ds = load_dataset(\"{DATA_REPO}\")\n", "print(ds)\n", "```\n", "\"\"\")\n", "print(\"README files written.\")"]}, {"cell_type": "markdown", "id": "3bca544c", "metadata": {"id": "3bca544c"}, "source": ["**The ResumeBot app (`app.py`)**: the complete bot + website as one file anyone can run."]}, {"cell_type": "code", "execution_count": null, "id": "b3d0c989", "metadata": {"id": "b3d0c989"}, "outputs": [], "source": ["%%writefile app.py\n", "\"\"\"ResumeBot: a FLAN-T5 model fine-tuned on Sudeep's resume, with retrieval and hallucination guards.\n", "Run: pip install -r requirements.txt && python app.py (then open the local link it prints)\"\"\"\n", "import os, re, json, inspect\n", "import numpy as np, torch, gradio as gr\n", "from huggingface_hub import hf_hub_download\n", "from sentence_transformers import SentenceTransformer\n", "from transformers import AutoTokenizer, AutoModelForSeq2SeqLM\n", "\n", "MODEL_REPO = \"YOUR_USERNAME/ResumeBot\" # the Hugging Face repo with the fine-tuned model\n", "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", "\n", "# ---- knowledge base (resume facts, profile, prompt, known questions) ----\n", "cfg_path = \"bot_config.json\" if os.path.exists(\"bot_config.json\") else hf_hub_download(MODEL_REPO, \"bot_config.json\")\n", "cfg = json.load(open(cfg_path))\n", "FACTS = [tuple(f) for f in cfg[\"facts\"]]\n", "PROFILE, PROMPT = cfg[\"profile\"], cfg[\"prompt\"]\n", "\n", "# ---- models ----\n", "tokenizer = AutoTokenizer.from_pretrained(MODEL_REPO)\n", "model = AutoModelForSeq2SeqLM.from_pretrained(MODEL_REPO).to(device).eval()\n", "embedder = SentenceTransformer(\"sentence-transformers/all-MiniLM-L6-v2\", device=device)\n", "bank, bank_fid = cfg[\"bank\"], cfg[\"bank_fid\"]\n", "bank_vecs = embedder.encode(bank, normalize_embeddings=True, convert_to_numpy=True)\n", "\n", "def retrieve(question, k=2):\n", " sims = bank_vecs @ embedder.encode([question], normalize_embeddings=True, convert_to_numpy=True)[0]\n", " best = {}\n", " for i in np.argsort(-sims):\n", " best.setdefault(bank_fid[i], float(sims[i]))\n", " if len(best) == k: break\n", " fids = list(best)\n", " return fids, best[fids[0]]\n", "\n", "# ---- answer function with hallucination guards ----\n", "PERSON_WORDS = {\"sudeep\", \"candidate\", \"he\", \"his\", \"him\", \"you\", \"your\", \"resume\", \"cv\", \"profile\"}\n", "STOP = set(\"a an the is are was were of in on at to for and or with by from as it this that his he sudeep s\".split())\n", "GREETING = \"Hi! \ud83d\udc4b I'm ResumeBot. Ask me anything about Sudeep: his education, skills, projects, certifications or achievements.\"\n", "OUT_OF_SCOPE = (\"I only answer questions about Sudeep's resume, so I won't guess about that. \"\n", " \"Try asking about his education, skills, projects, certifications or achievements.\")\n", "\n", "def words(text):\n", " return {w for w in re.findall(r\"[a-z0-9]+\", text.lower()) if w not in STOP}\n", "\n", "def generate(prompt):\n", " inputs = tokenizer(prompt, return_tensors=\"pt\", truncation=True, max_length=256).to(model.device)\n", " with torch.no_grad():\n", " out = model.generate(**inputs, max_new_tokens=128, num_beams=4, no_repeat_ngram_size=3, early_stopping=True)\n", " return tokenizer.decode(out[0], skip_special_tokens=True).strip()\n", "\n", "def answer(question):\n", " q = question.strip()\n", " ql = re.findall(r\"[a-z]+\", q.lower())\n", " if not q or set(ql) <= {\"hi\", \"hello\", \"hey\", \"hii\", \"good\", \"morning\", \"evening\", \"namaste\"}:\n", " return GREETING, \"Greeting\"\n", " if set(ql) & {\"thanks\", \"thank\", \"thankyou\"}:\n", " return \"You're welcome! Ask me anything else about Sudeep. \ud83d\ude0a\", \"Greeting\"\n", " fids, score = retrieve(q)\n", " about_him = bool(PERSON_WORDS & set(ql))\n", " if score < (0.35 if about_him else 0.55): # guard 1: not about the resume -> don't guess\n", " return OUT_OF_SCOPE, \"Out of scope\"\n", " context = \" \".join(FACTS[f][1] for f in fids)\n", " reply = generate(PROMPT.format(context=context, question=q))\n", " grounded = len(words(reply) - words(context)) <= 1 and len(reply) > 3\n", " if not grounded: # guard 2: answer must come from the resume text\n", " reply = FACTS[fids[0]][1]\n", " return reply, FACTS[fids[0]][0]\n", "\n", "# ---- Gradio website ----\n", "\n", "CSS = \"\"\"\n", ".gradio-container {max-width: 1100px !important; margin: auto;}\n", "#hero {background: linear-gradient(135deg,#4f46e5 0%,#7c3aed 55%,#db2777 100%); border-radius: 18px; padding: 28px 32px; color: white;}\n", "#hero h1 {margin: 0; font-size: 34px; color: white;}\n", "#hero p {margin: 6px 0 0; opacity: .92; font-size: 16px; color: white;}\n", ".card {border: 1px solid var(--border-color-primary); border-radius: 16px; padding: 18px 20px; background: var(--block-background-fill);}\n", ".card h3 {margin: 0 0 8px; font-size: 15px; text-transform: uppercase; letter-spacing: .06em; opacity: .7;}\n", ".chip {display: inline-block; padding: 4px 10px; margin: 3px 4px 3px 0; border-radius: 999px; font-size: 13px;\n", " background: rgba(99,102,241,.12); border: 1px solid rgba(99,102,241,.35);}\n", ".meta {font-size: 14px; line-height: 1.8;}\n", "footer {display: none !important;}\n", "\"\"\"\n", "P = PROFILE\n", "HERO = f\"\"\"
\ud83e\udd16 ResumeBot
\n", "
Chat with my personal AI, a FLAN-T5 model fine-tuned on my resume. It answers only from verified resume facts.
\n", "1\ufe0f\u20e3 Finds the matching resume section 2\ufe0f\u20e3 Fine-tuned FLAN-T5 writes the answer 3\ufe0f\u20e3 Answer is checked against the resume