<jupyter_start><jupyter_text>ModelThis notebook builds a Doc2Vec model for matching course descriptions to job descriptions. It trains the model on a corpus of course descriptions (from the Coursera catalog). Then it evaluates the model by testing it out with a sample set of job descriptions for which relevant courses have already been pre-selected. Finally, the model is "pickled" for use in an API.<jupyter_code># Import libraries

import numpy as np
import pandas as pd
import gensim
from scipy.spatial import distance
import pickle<jupyter_output><empty_output><jupyter_text>Model Building<jupyter_code># Read in the course data

course_df = pd.read_csv('../Data/Course_Data/Coursera_Catalog.csv')
print(course_df.shape)
course_df.head(2)
# Preprocess the course data as a training corpus

# Convert the course descriptions to a list
course_descriptions = course_df['description'].to_list()

# Define a function for basic preprocessing:
# tokenize, lowercase, de-punctuate, document tagging
def read_corpus(corpus):
    for i, doc in enumerate(corpus):
        tokens = gensim.utils.simple_preprocess(doc)
        yield gensim.models.doc2vec.TaggedDocument(tokens, [i])

# Run the function on the list of course descriptions
train_corpus = list(read_corpus(course_descriptions))
# Examine the first document in the corpus (tag at end)

print(train_corpus[0])
# Build a model

# Instantiate the model
model = gensim.models.doc2vec.Doc2Vec(vector_size=50,
                                      window=5,
                                      dm=0, # PV-DBOW
                                      dm_concat=1,
                                      min_count=2,
                                      epochs=10)

# Create a vocabulary for the model
model.build_vocab(train_corpus)

# Train the model on the corpus
model.train(train_corpus,
            total_examples=model.corpus_count,
            epochs=model.epochs)
# Self-assess the model

# Infer a vector for a document in the training corpus itself.
vector = model.infer_vector(train_corpus[0].words)

# Find which documents in the training corpus it is most similar to.
sims = model.docvecs.most_similar([vector])
sims<jupyter_output><empty_output><jupyter_text>Analysis: The model successfully finds that the queried document is most similar to itself in the training corpus, with a similarity score of over 96%.  Model Evaluation<jupyter_code># Read in the sample job descriptions

jobs_test_sample = pd.read_csv('../EDA/jobs_test_sample.csv')
jobs_test_sample
# Read in the sample courses relevant to the job descriptions

courses_test_sample = pd.read_csv('../EDA/courses_test_sample.csv')
courses_test_sample
# Split the sample courses by job title

ds_courses = courses_test_sample.loc[courses_test_sample['job_title'] == "Data Scientist"]
de_courses = courses_test_sample.loc[courses_test_sample['job_title'] == "Data Engineer"]
de_courses.reset_index(drop=True, inplace=True)
# Calculate the model's distance score for this sample data:
# A lower score is better because the model should recognize
# that these jobs and courses are similar to each other.

# Initiate a list of distances
distances = []

# Loop through the labeled jobs
for i in range(len(jobs_test_sample)):
    
    # Extract the information for the job
    job_title, job_desc, job_id = jobs_test_sample.iloc[i]
    
    # Preprocess the job description
    doc = gensim.utils.simple_preprocess(job_desc)
    
    # Convert the job description into a vector
    job_vec = model.infer_vector(doc)
    
    # Check the job title and loop through relevant courses
    if job_title == 'Data Scientist':
        
        for j in range(len(ds_courses)):
            
            # Extract the course id
            course_id = ds_courses.iloc[j,3]
            
            # Find the distance between the job and course
            d = distance.cosine(job_vec, model.docvecs[course_id])
            
            # Add the result to the distance list
            distances.append(d)
            
    if job_title == 'Data Engineer':
        
        for j in range(len(de_courses)):
            
            # Extract the course id
            course_id = de_courses.iloc[j,3]
            
            # Find the distance between the job and course
            d = distance.cosine(job_vec, model.docvecs[course_id])
            
            # Add the result to the distance list
            distances.append(d)

# Calculate the average distance for all job descriptions
sum_dist = 0
for d in distances:
    sum_dist += d

avg_dist = round(sum_dist / len(distances), 3)
min_dist = round(min(distances), 3)
max_dist = round(max(distances), 3)

print(f"Mean distance score: {avg_dist}")
print(f"Min distance score: {min_dist}")
print(f"Max distance score: {max_dist}")<jupyter_output>Mean distance score: 0.165
Min distance score: 0.059
Max distance score: 0.284<jupyter_text>Detailed Results<jupyter_code># Define a function to display the results for each labeled job

def display_recs(job_id):
    
    # Initialize a dictionary for the job
    job_dict = {}
    keys = ['job_id', 'job_title', 'course_id', 'course_name', 'selected', 'distance']
    for key in keys:
        job_dict[key] = []
    
    # Extract info about the job
    i = jobs_test_sample.loc[jobs_test_sample['Job_id'] == job_id].index.values[0]
    job_title = jobs_test_sample.iloc[i]['Job_title']
    job_desc = jobs_test_sample.iloc[i]['Job_Desc']
    
    # Preprocess the job description
    doc = gensim.utils.simple_preprocess(job_desc)
    
    # Convert the job description into a vector
    job_vec = model.infer_vector(doc)
    
    # Get top 5 courses from the model
    top_recs = model.docvecs.most_similar(positive=[job_vec], topn=5)
    
    # Loop through top 5 courses
    for rec in top_recs:
        
        # Add job info to the dictionary
        job_dict['job_id'].append(job_id)
        job_dict['job_title'].append(job_title)
        
        # Add course info to the dicitonary
        course_id = rec[0]
        job_dict['course_id'].append(course_id)
        course_name = course_df.loc[course_id, 'name']
        job_dict['course_name'].append(course_name)
        job_dict['selected'].append(0)
        
        # Add the distance score to the dictionary
        d = round((1-rec[1]), 3)
        job_dict['distance'].append(d)
    
    # Now loop through the courses pre-selected as relevant
    if job_title == 'Data Scientist':
        
        for i in range(len(ds_courses)):
            
            # Add job info to the dictionary
            job_dict['job_id'].append(job_id)
            job_dict['job_title'].append(job_title)
            
            # Add course info to the dictionary
            course_id = ds_courses.iloc[i,3]
            job_dict['course_id'].append(course_id)
            course_name = ds_courses.iloc[i,0]
            job_dict['course_name'].append(course_name)
            job_dict['selected'].append(1)
            
            # Find and add the distance score to the dictionary
            d = distance.cosine(job_vec, model.docvecs[course_id])
            d = round(d,3)
            job_dict['distance'].append(d)
            
    if job_title == 'Data Engineer':
        
        for i in range(len(de_courses)):
            
            # Add job info to the dictionary
            job_dict['job_id'].append(job_id)
            job_dict['job_title'].append(job_title)
            
            # Add course info to the dictionary
            course_id = de_courses.iloc[i,3]
            job_dict['course_id'].append(course_id)
            course_name = de_courses.iloc[i,0]
            job_dict['course_name'].append(course_name)
            job_dict['selected'].append(1)
            
            # Find and add the distance score to the dictionary
            d = distance.cosine(job_vec, model.docvecs[course_id])
            d = round(d,3)
            job_dict['distance'].append(d)
    
    # Convert the dictionary into a dataframe
    df = pd.DataFrame(job_dict)
    
    # Rank by distance
    df = df.sort_values('distance')
    
    return df
# Job 1
display_recs(901)
# Job 2
display_recs(910)
# Job 3
display_recs(916)
# Job 4
display_recs(920)
# Job 5
display_recs(938)
# Job 6
display_recs(935)
# Job 7
display_recs(1068)
# Job 8
display_recs(1089)
# Job 9
display_recs(1100)
# Job 10
display_recs(1105)<jupyter_output><empty_output><jupyter_text>Analysis:The architecture works, but the model's performance is not great. It is finding relevant courses, but almost definitely not the most relevant ones in the catalog for job seekers.  Pickle the Model<jupyter_code># Save the model for use in the API
# by serializing it with the pickle library.

pickle.dump(model, open('../model.p', 'wb'))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08560399636693915, 'fraction_numerical': 0.011012715712988192, 'mean_word_length': 2.81507145950628, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '69068', 'license': 'MIT', 'n_tokens_mistral': 2740, 'n_tokens_neox': 2642, 'n_words': 772, 'path': 'Data Modelling/Doc2Vec_Model.ipynb', 'repo_name': 'HarshCasper/MargSetu', 'size': '8808'}