diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..fe3e998bd00c888d95b1da70915f980e56261a2f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,21 @@ +node_modules +build +dist +venv +env +__pycache__ +*.pyc +*.pyo +*.pyd +.Python +.git +.gitignore +.dockerignore +Dockerfile +README.md +backend/polyline_generation.log +backend/db.json +backend/db_backup.json +.env +*.pem +*.local diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..60f92f3340dbcc849ca3bf8242573056adc61c53 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +.env + +# Temporary and local files +__pycache__/ +hf_https_clone/ +temp_zip_check/ +backend/backend_logs.txt +backend/data/*.db +backend/data/*.sql +nl_main_deployment.zip + +# Large files and models +**/*.xlsx +**/*.pth +**/*.zip +backend/models/ +backend/nlp/*.xlsx +backend/data/*.json +!backend/data/youtube_links.json +!backend/data/youtube_transcripts.json +!backend/nlp/nlp_resources.json +!backend/data/db.json +!extracted_data.json diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..dedbaa41f69b9188814d90942989178e18c3e071 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,45 @@ +# Stage 1: Build the React Frontend +FROM node:18-alpine as build-frontend +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build + +# Stage 2: Setup the Python Backend +FROM python:3.9-slim + +# Create a non-root user for Hugging Face (UID 1000) +RUN useradd -m -u 1000 user + +# Install system dependencies first (cached layer) +USER root +RUN apt-get update && apt-get install -y --no-install-recommends gcc python3-dev \ + && rm -rf /var/lib/apt/lists/* + +USER user +ENV PATH="/home/user/.local/bin:$PATH" +WORKDIR /home/user/app + +# Install PyTorch CPU-only FIRST (largest package, separate cache layer) +# This avoids downloading the ~800MB CUDA version +RUN pip install --no-cache-dir torch --index-url https://download.pytorch.org/whl/cpu + +# Install remaining Python dependencies +COPY --chown=user backend/requirements.txt backend/ +# Remove torch from requirements since we installed it above +RUN grep -v '^torch' backend/requirements.txt > /tmp/req_notorch.txt && pip install --no-cache-dir --upgrade -r /tmp/req_notorch.txt && pip install --no-cache-dir gunicorn + +# Copy Backend Code +COPY --chown=user backend/ backend/ +COPY --chown=user Navigators/ Navigators/ + +# Copy Frontend Build from Stage 1 +COPY --chown=user --from=build-frontend /app/dist ./dist + +# Environment variables +ENV PYTHONUNBUFFERED=1 +ENV PORT=7860 + +# Run the application +CMD gunicorn -b 0.0.0.0:$PORT backend.nlp_api:app diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4ee007617ee4083e8295cb209431134b0cf0bd52 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ + +--- +title: NL Main +emoji: ๐Ÿง  +colorFrom: blue +colorTo: indigo +sdk: docker +app_port: 7860 +--- + +# NLP Learning Grid + +Advanced agentic NLP learning platform with a 3D grid interface, DQN-based pathfinding, and interactive AI learning assistant. + +## Deployment Options + +### Hugging Face Spaces +This repository is configured for direct deployment to Hugging Face Spaces using Docker. +- Port: 7860 (Auto-configured in Dockerfile) + +### Fly.io +Deploy using the included `fly.toml`: +```bash +fly launch +``` + +### Render.com +Use the `render.yaml` for one-click deployment. + +### Manual +```bash +docker build -t nlp-learning-grid . +docker run -p 5000:7860 nlp-learning-grid +``` diff --git a/backend/Inspiration/app.py b/backend/Inspiration/app.py new file mode 100644 index 0000000000000000000000000000000000000000..0a5bbf107860c3e1c5aa504204b77473358cde05 --- /dev/null +++ b/backend/Inspiration/app.py @@ -0,0 +1,600 @@ +# This Flask backend manages user authentication (signup/login), course enrollment, teacher/TA assignment, +# and provides course/learner data. It handles requests for course details, module/topic information, learner positions, +# and quiz interactions (submission, fetching questions/logs, recording attempts, creation). +# Data is often fetched from or updated in the database based on user actions and IDs. +import datetime +from utils import is_valid_id +from dbModels import User, db, Course, Question, UserQuiz +from init import app, DBcreated +import pandas as pd +from flask import make_response,jsonify, request +from repository import add_learner_from_user, add_teacher_from_user, create_Course, update_position, login,signup,teacher_course,teacher_course_unassigned,assign_teacher_course,unassign_teacher_course, learner_course_enrolled,generate_data,learner_course_unenrolled,enrolled_learner_data,enrolled_learners_by_course,calculate_all_module_centroids,add_enroll,update_by_quiz,learner_polyline_enrolled,get_suitable_position,change_resource_position,update_position_resource,update_summary_grade, quiz_adder_from_json,ta_course,ta_course_teached,ta_course_unteached, user_enrolled_courses, user_recom_courses +from datetime import datetime, timedelta,timezone +from flask import Flask +import modelsRoutes # to expose routes + +# Read data from Excel file +excel_file = 'DM_Resource_Plot.xlsx' +df = pd.read_excel(excel_file) +excel_file = 'DM_learner_plot.xlsx' +df_learner = pd.read_excel(excel_file) + +# Assuming your Excel file has columns 'x', 'y', and 'video_url' +scatterplot_data = df[['index', 'name', 'x', 'y', 'video_url', 'module','module_id','submodule_id']].to_dict(orient='records') + +# Convert the scatterplot_data into a DataFrame +df_scatter = pd.DataFrame(scatterplot_data) + +# Group by 'module_id' and calculate the mean of 'x' and 'y' +module_data_df = df_scatter.groupby('module_id').agg({'x': 'mean', 'y': 'mean','module': 'first' }).reset_index() + +# Convert the result to a list of dictionaries with 'module_id', 'x', and 'y' +module_data = module_data_df.to_dict(orient='records') +topic_data_df=pd.read_excel('DM/DM_topics.xlsx') +topic_data=topic_data_df[['name','description']].to_dict(orient='records') + + +learner_data = df_learner[['index', 'resource_name', 'x', 'y', 'description']].to_dict(orient='records') + +if DBcreated: + # print("creating the course") + # create_Course("Discreate Mathematics", + # "this is the description of DM", None, None) + print("Generating Data") + generate_data() + + +@app.route('/ids/') +def get_ids(user_id): + """ + Fetches learner, teacher, and TA IDs for a given user. + + Args: + user_id (int): ID of the user. + + Returns: + JSON: Dictionary containing 'learner_id', 'teacher_id', and 'ta_id'. + """ + with app.app_context(): + user = User.query.get(user_id) + return ({ + 'learner_id': user.learner_id, + 'teacher_id': user.teacher_id, + 'ta_id': user.ta_id, + }) + +@app.route('/data') +def get_data(): + """ + Returns resource scatterplot data (x, y, video URLs, module info). + + Returns: + JSON: List of resource data dictionaries. + """ + return jsonify(scatterplot_data) + +@app.route('/moduleData/') +def get_module_data(id): + """ + Calculates and returns module centroid data for a given course. + + Args: + id (int): Course ID. + + Returns: + JSON: List of module data with centroid positions. + """ + moudle = calculate_all_module_centroids(id) + return jsonify(moudle) + +@app.route('/topicData') +def get_topic_data(): + """ + Fetches topic names and descriptions. + + Returns: + JSON: List of topic dictionaries with 'name' and 'description'. + """ + return jsonify(topic_data) + +@app.route('/new_positions') +def get_new_data(): + """ + Returns learner plot data including positions and descriptions. + + Returns: + JSON: List of learner data dictionaries. + """ + return jsonify(learner_data) + +@app.route("/signup", methods=['POST']) +def signup_user(): + """ + Registers a new user. + + Request JSON: + - name (str): Full name of the user. + - username (str): Username for login. + - password (str): Password for the account. + + Returns: + JSON: Created user details if successful, else error message. + """ + data = request.get_json() + name = data["name"] + username = data["username"] + password = data["password"] + print("user signup: ", name, password, username) + + user = signup(name, username, password) + if user: + return jsonify(user), 201 + else: + return jsonify({"msg":"Error Creating user"}), 400 + +@app.route("/login", methods=['POST']) +def login_user(): + """ + Logs in an existing user. + + Request JSON: + - username (str): Username of the user. + - password (str): Password of the user. + + Returns: + JSON: User details if login is successful, else 401 status. + """ + data = request.get_json() + username = data["username"] + password = data["password"] + print("user login request:", username, password) + + user = login(username, password) + response = make_response(jsonify(user)) + if user: + response.status_code = 200 + else: + response.status_code = 401 + + return response + +@app.route("/teacher/courses/", methods=['GET']) +def get_teacher_course(id): + """ + Fetches all courses assigned to a teacher. + + Args: + id (int): Teacher ID. + + Returns: + JSON: List of courses assigned to the teacher. + """ + return teacher_course(id) + +@app.route("/teacher/courses/unassigned/", methods=['GET']) +def get_teacher_course_unassigned(id): + """ + Fetches all unassigned courses for a teacher. + + Args: + id (int): Teacher ID. + + Returns: + JSON: List of unassigned courses available for assignment. + """ + return teacher_course_unassigned(id) + +@app.route("/teacher/courses/assign", methods=['POST']) +def assign_teacher(): + """ + Assigns a teacher to a course. If teacher doesn't exist, creates one. + + Request JSON: + - user_id (int): User ID. + - teacher_id (int): Teacher ID (may be invalid if new teacher). + - course_id (int): Course ID. + + Returns: + JSON: Assignment result (success or failure). + """ + data = request.get_json() + user_id = data["user_id"] + teacher_id = data["teacher_id"] + course_id = data["course_id"] + print(f"user_id: {user_id}, teacher_id: {teacher_id}, course_id: {course_id}") + if not is_valid_id(teacher_id): + print("adding new teacher...") + teacher_id = add_teacher_from_user(user_id)["id"] + if teacher_id and course_id: + result = assign_teacher_course(teacher_id, course_id) + response = make_response(jsonify(result)) + if result: + response.status_code = 200 + else : + response.status_code = 500 + else: + response = make_response(None) + response.status_code = 400 + return response + +@app.route("/teacher/courses/unassign", methods=['POST']) +def unassign_teacher(): + """ + Unassigns a teacher from a course. + + Request JSON: + - teacher_id (int): Teacher ID. + - course_id (int): Course ID. + + Returns: + JSON: Result of unassignment (success/failure). + """ + data = request.get_json() + teacher_id = data["teacher_id"] + course_id = data["course_id"] + print(teacher_id,course_id) + if teacher_id and course_id : + result = unassign_teacher_course(teacher_id, course_id) + return jsonify(result), 200 if result else jsonify(result), 400 + +@app.route("/enrolledCourses/", methods=['GET']) +def get_enrolled_course(id): + """ + Fetches list of courses a user is enrolled in, including role. + + Args: + id (int): User ID. + + Returns: + JSON: List of enrolled courses with role info. + """ + return user_enrolled_courses(id) + +@app.route("/taTeachedCourses/",methods=['GET']) +def get_teached_course(id): + """ + Fetches courses taught by a TA. + + Args: + id (int): TA ID. + + Returns: + JSON: List of courses taught by the TA. + """ + return ta_course_teached(id) + +@app.route("/enrolledLearner//", methods=['GET']) +def get_enrolled_learner(id,id2): + """ + Fetches enrollment data for a learner in a specific course. + + Args: + id (int): Learner ID. + id2 (int): Course ID. + + Returns: + JSON: Learner enrollment details. + """ + return enrolled_learner_data(id,id2) + +@app.route("/enrolledLearnersByCourse/", methods=['GET']) +def get_enrolled_learners(id): + """ + Fetches all learners enrolled in a given course. + + Args: + id (int): Course ID. + + Returns: + JSON: List of enrolled learners. + """ + return enrolled_learners_by_course(id) + +@app.route("/recomCourses/", methods=['GET']) +def get_recom_course(id): + """ + Fetches list of courses recommended for a user. + + Args: + id (int): User ID. + + Returns: + JSON: List of recommended courses for enrollment. + """ + return user_recom_courses(id) + +@app.route("/ta/recomCourses/",methods=['GET']) +def get_ta_recom_course(id): + """ + Fetches list of courses recommended for a TA to teach. + + Args: + id (int): TA ID. + + Returns: + JSON: List of teachable recommended courses. + """ + return ta_course_unteached(id) + +@app.route("/enrolledPolylines/", methods=['GET']) +def get_enrolled_polyline(id): + """ + Fetches polyline data for resources a learner is enrolled in. + + Args: + id (int): Learner ID. + + Returns: + JSON: Polyline positions of enrolled resources. + """ + return learner_polyline_enrolled(id) + +@app.route("/submitsummary", methods=['POST']) +def get_new_postion(): + """ + Updates learner's position after submitting a summary. + + Request JSON: + - summary (str): Learner's summary. + - enroll_id (int): Enrollment ID. + + Returns: + JSON: Updated position and contribution ID. + """ + data = request.get_json() + summary = data["summary"] + enrollId = data["enroll_id"] + pos,contribution_id = update_position(summary, enrollId) + return jsonify({"position": pos, "contribution_id": contribution_id}), 200 + +@app.route("/changeSummaryGrade", methods=['POST']) +def get_new_learner_postion(): + """ + Updates the grade of a learner's summary. + + Request JSON: + - contribution_id (int): Contribution ID. + - grade (float): Grade to assign. + + Returns: + JSON: Updated learner position. + """ + data = request.get_json() + contributionId = data["contribution_id"] + grade = data["grade"] + pos = update_summary_grade(contributionId,grade) + return jsonify(pos), 200 + +@app.route("/watchResource", methods=['POST']) +def get_updated_postion(): + """ + Updates learner's position after watching a resource. + + Request JSON: + - enroll_id (int): Enrollment ID. + - resource_id (int): Resource ID. + + Returns: + JSON: Updated learner position. + """ + data = request.get_json() + enrollId = data["enroll_id"] + resourceId = data["resource_id"] + pos = update_position_resource(enrollId,resourceId) + return jsonify(pos), 200 + +@app.route("/suitableResourcePosition", methods=['POST']) +def suitable_postion(): + """ + Suggests a suitable position for a learner based on resource. + + Request JSON: + - pos (float): Initial position. + - resource_id (int): Resource ID. + + Returns: + JSON: Adjusted suitable position. + """ + data = request.get_json() + initial_pos = data["pos"] + resourceId = data["resource_id"] + pos = get_suitable_position(initial_pos,resourceId) + return jsonify(pos), 200 + +@app.route("/changeResourcePosition", methods=['POST']) +def change_postion(): + """ + Changes position of a resource. + + Request JSON: + - pos (float): New position. + - resource_id (int): Resource ID. + + Returns: + JSON: Empty success response. + """ + data = request.get_json() + pos = data["pos"] + resourceId = data["resource_id"] + change_resource_position(pos,resourceId) + return jsonify({}), 200 + +@app.route("/submitquiz", methods=['POST']) +def update_by_quiz_route(): + """ + Updates learner's position based on quiz performance. + + Request JSON: + - enroll_id (int): Enrollment ID. + - course_id (int): Course ID. + - to_consider (list): Boolean array of questions to consider. + - question_polyline (list): Polyline array for each question. + + Returns: + JSON: Updated learner position. + """ + data = request.get_json() + enrollId = data["enroll_id"] + courseId = data["course_id"] + to_consider = data["to_consider"] + question_polyline = data["question_polyline"] + + pos = update_by_quiz(enrollId, courseId, to_consider, question_polyline, position_scaler = 1) + + return jsonify(pos), 200 + +@app.route('/quiz_questions/', methods=['GET']) +def get_quiz_questions(quiz_id): + """ + Fetches all questions for a given quiz. + + Args: + quiz_id (int): Quiz ID. + + Returns: + JSON: List of question dictionaries with options and correct answer. + """ + questions = Question.query.filter_by(quiz_id=quiz_id).all() + questions_data = [] + for question in questions: + questions_data.append({ + 'id': question.id, + 'quiz_id': question.quiz_id, + 'question_text': question.question_text, + 'option_a': question.option_a, + 'option_b': question.option_b, + 'option_c': question.option_c, + 'option_d': question.option_d, + 'correct_answer': question.correct_answer, + 'polyline': question.polyline + }) + + return jsonify(questions_data), 200 + +@app.route('/fetch_quiz_log/', methods=['GET']) +def fetch_quiz_log(user_id): + """ + Fetches all quiz attempts made by a user. + + Args: + user_id (int): User ID. + + Returns: + JSON: List of quiz attempt logs with score and attempt date. + """ + user_quizzes = UserQuiz.query.filter_by(user_id=user_id).all() + user_quiz_data = [] + + for user_quiz in user_quizzes: + user_quiz_data.append({ + 'id': user_quiz.id, + 'quiz_id': user_quiz.quiz_id, + 'user_id': user_quiz.user_id, + 'score': user_quiz.score, + 'completion_date': user_quiz.attempt_date, + }) + + return jsonify(user_quiz_data), 200 + +@app.route('/record_quiz_attempt', methods=['POST']) +def record_quiz_attempt(): + """ + Records a quiz attempt for a user. + + Request JSON: + - user_id (int): User ID. + - quiz_id (int): Quiz ID. + - score (float): Score obtained. + - status (str): Status (e.g., completed, pending). + - attempt_date (str, optional): ISO timestamp of attempt. + + Returns: + JSON: Created quiz attempt record if successful. + """ + try: + data = request.get_json() + attempt_date = None + if 'attempt_date' in data: + attempt_date_utc = datetime.fromisoformat(data['attempt_date'].replace("Z", "+00:00")) + ist_timezone = timezone(timedelta(hours=5, minutes=30)) + attempt_date = attempt_date_utc.astimezone(ist_timezone) + + new_user_quiz = UserQuiz( + user_id=data['user_id'], + quiz_id=data['quiz_id'], + score=data.get('score'), + status=data['status'], + attempt_date=attempt_date + ) + + db.session.add(new_user_quiz) + db.session.commit() + return jsonify(new_user_quiz.to_dict()), 201 + except Exception as e: + print("Error in record_quiz_attempt:", e) + return jsonify({"error": "Failed to record quiz attempt"}), 500 + +@app.route('/createquiz', methods=['POST']) +def create_quiz(): + """ + Creates a new quiz and its associated questions. + + Request JSON: + - quiz details and questions in structured format. + + Returns: + JSON: Success message with x, y coordinates from quiz creation. + """ + try: + data = request.get_json() + if not data: + return jsonify({"error": "No data provided"}), 400 + + x, y = quiz_adder_from_json(data) + return jsonify({"message": "Quiz and questions added successfully!", "x": x, "y": y}), 201 + + except Exception as e: + print("Unexpected error in create_quiz:", e) + return jsonify({"error": str(e)}), 500 + +@app.route('/enrolls', methods=['POST']) +def create_enroll(): + """ + Enrolls a learner into a course. Creates learner profile if missing. + + Request JSON: + - user_id (int): User ID. + - learner_id (int): Learner ID (optional if new). + - course_id (int): Course ID. + + Returns: + JSON: Enrollment confirmation. + """ + data = request.get_json() + user_id=data['user_id'] + learner_id=data['learner_id'] + course_id=data['course_id'] + if not is_valid_id(learner_id): + learner_id = add_learner_from_user(user_id)['id'] + return add_enroll(learner_id, course_id) + +@app.route('/coursename/', methods=['GET']) +def get_course_name(course_id): + """ + Fetches the name of a course by its ID. + + Args: + course_id (int): ID of the course. + + Returns: + JSON: A dictionary containing the course name or an error message. + """ + course = Course.query.get(course_id) + if course: + return jsonify({'course_id': course.id, 'name': course.name}) + else: + return jsonify({'error': 'Course not found'}), 404 + + +if __name__ == '__main__': + app.run(host="0.0.0.0", debug=True) diff --git a/backend/Inspiration/model_library.py b/backend/Inspiration/model_library.py new file mode 100644 index 0000000000000000000000000000000000000000..ac856009a63fa8b90857d09a081526817e3a6c58 --- /dev/null +++ b/backend/Inspiration/model_library.py @@ -0,0 +1,597 @@ +import pandas as pd +from bs4 import BeautifulSoup +import re +import nltk +from nltk.stem import WordNetLemmatizer, PorterStemmer +from nltk.corpus import stopwords +from sentence_transformers import SentenceTransformer +import numpy as np +from transformers import BertModel, BertTokenizer, BertForMaskedLM +import torch +from dbModels import db, Resource, Course, Topic, app, Enroll, Learner +import math +from keybert import KeyBERT +from utils import get_cos_sim +from statistics import mean +# from memory_profiler import profile +import gc + +nltk.download('stopwords') +nltk.download('wordnet') +stop_words = set(stopwords.words('english')) + + +def utils_preprocess_text(text: str, flg_stemm: bool = False, flg_lemm: bool = True, lst_stopwords: list = None) -> str: + """ + Preprocess text by removing HTML tags, punctuations, numbers, stopwords, and applying stemming/lemmatization. + + Parameters: + text (str): The text to preprocess. + flg_stemm (bool): Flag to apply stemming. Default is False. + flg_lemm (bool): Flag to apply lemmatization. Default is True. + lst_stopwords (list): List of stopwords to remove. Default is None. + + Returns: + str: The preprocessed text. + """ + + # Remove HTML + soup = BeautifulSoup(text, 'lxml') + text = soup.get_text() + + # Remove punctuations and numbers + text = re.sub('[^a-zA-Z]', ' ', text) + + # Single character removal + text = re.sub(r"\s+[a-zA-Z]\s+", ' ', text) + + # Remove multiple spaces + text = re.sub(r'\s+', ' ', text) + + # Tokenize text + lst_text = text.split() + + # Remove stopwords + if lst_stopwords is not None: + lst_text = [word for word in lst_text if word not in lst_stopwords] + + # Apply stemming + if flg_stemm: + ps = PorterStemmer() + lst_text = [ps.stem(word) for word in lst_text] + + # Apply lemmatization + if flg_lemm: + lem = WordNetLemmatizer() + lst_text = [lem.lemmatize(word) for word in lst_text] + + text = " ".join(lst_text) + return text + + +def apply_preprocessing(df: pd.DataFrame): + """ + Apply text preprocessing to the 'description' column of the DataFrame. + + Parameters: + df (pd.DataFrame): DataFrame containing topics. + """ + stop_words = set(stopwords.words('english')) # Define stopwords + df['clean_text'] = df['description'].apply(lambda x: x.lower()) + df['clean_text'] = df['clean_text'].apply(lambda x: utils_preprocess_text( + x, flg_stemm=False, flg_lemm=True, lst_stopwords=stop_words)) + df['tokens'] = df['clean_text'].apply(lambda x: x.split()) + +# @profile +def create_topic_embeddings(topics: pd.DataFrame) -> list: + """ + Create embeddings for each topic using a pre-trained BERT model. + + Parameters: + topics (pd.DataFrame): DataFrame containing topics. + + Returns: + list: List of topic embeddings. + """ + model = SentenceTransformer('all-MiniLM-L6-v2') + topic_embeddings = [] + + for i in range(len(topics)): + embedding = model.encode( + topics.loc[i, "description"], convert_to_tensor=True) + # Store embeddings as numpy arrays + topic_embeddings.append(embedding.cpu().numpy()) + # Clear the embedding tensor and force garbage collection after each iteration + del embedding + gc.collect() + + return topic_embeddings + +# @profile +def create_topic_polylines(topics: pd.DataFrame, topic_embeddings: list) -> pd.DataFrame: + """ + Create a DataFrame containing topic names, module numbers, and cosine similarity polylines. + + Parameters: + topics (pd.DataFrame): DataFrame containing topics. + topic_embeddings (list): List of topic embeddings. + + Returns: + pd.DataFrame: DataFrame with topic polylines. + """ + topic_names = topics["name"].tolist() # Topic keyphrases + length = len(topics) + topic_modules = [] # Topic module number + for i in range(12): + topic_modules.append(1) + for i in range(8): + topic_modules.append(2) + nowl = len(topic_modules) + for i in range(length-nowl): + topic_modules.append(3) + # Topic embedding - mean of topic embeddings of individual words of a keyphrase + + top_poly = [] + top_module = [] + topic = [] + + # Going through each topic and computing the cosine similarity between it's embedding and all other topic's embeddings + for i in range(len(topic_names)): + polyline = [0]*len(topic_names) + for j in range(len(topic_names)): + cos_sim = 0 + if topic_names[i] == topic_names[j]: + cos_sim = 1 + else: + topic1_vector = topic_embeddings[i] + topic2_vector = topic_embeddings[j] + + # scaling cosine similarity value from [-1,1] to [0,1] + cos_sim = (get_cos_sim( + (topic1_vector), (topic2_vector)) + 1) / 2 + + polyline[j] = cos_sim # format 1 + # polyline.append((j, cos_sim)) #format 2 + + topic.append(topic_names[i]) + top_module.append(topic_modules[i]) + top_poly.append(polyline) + + polyline_dict = {"topic": topic, + "module": top_module, "polyline": top_poly} + # converting the topic polyline to a dataframe + topic_polylines = pd.DataFrame(polyline_dict) + return topic_polylines + + +# +# +# Resources functions start +# +# +# @profile + +def create_summary_embeddings(summary) -> list: + model = SentenceTransformer('all-MiniLM-L6-v2') + summary_embeddings_list = [] + + # Encode the summary and convert to numpy array, then wrap in an additional list to match the format + embedding = model.encode(summary, convert_to_tensor=True).cpu().numpy() + summary_embeddings_list.append([embedding.tolist()]) # Add extra list wrapping to match format + + # Clear the embedding tensor and force garbage collection after each iteration + del embedding + gc.collect() + + return summary_embeddings_list + +def create_resource_embeddings(keywords): + model = SentenceTransformer('all-MiniLM-L6-v2') + keybert_embeddings_list = [] + for i in keywords: + # Encode the keyword + embedding = model.encode(i) + + # Convert to list and wrap to match expected structure [[float, ...]] + embeddings = [embedding.tolist()] + keybert_embeddings_list.append(embeddings) + + return keybert_embeddings_list + +# @profile +def create_resource_polylines(topicembedding, keybert_embeddings_list, beta): + all_polylines = [] + topic_embeddings = topicembedding + for embeddings in keybert_embeddings_list: + single_file_polyline = [] + for i in range(len(embeddings)): + docVector = embeddings[i] + polyline = [] + for j in range(len(topic_embeddings)): + wordVector = topic_embeddings[j] + # find the cosine similarity between resource embeddings and the topic embeddings + cos_sim = (get_cos_sim(wordVector, docVector) + 1) / 2 + polyline.append({'x': j, 'y': cos_sim}) + single_file_polyline.append(polyline) + all_polylines.append(single_file_polyline) + new_polylines = [] + + for single_file_polyline in all_polylines: + templ = [0]*len(topicembedding) + for i in range(len(topicembedding)): + temp = 0 + # between the multiple polylines for each doc find the average and set that as the final polyline + for j in range(len(single_file_polyline)): + temp += single_file_polyline[j][i]['y'] + templ[i] = temp / len(single_file_polyline) + new_polylines.append(templ) + + polylines = [] + temporary_list = [] + learning_objects = [] + for i in range(len(new_polylines)): + polyline = new_polylines[i] + pol = {} + temporary_dict = {} + for j in range(len(polyline)): + pol[j] = polyline[j] + hd1 = np.array([v for v in pol.values()]) + hd1.tolist() + temporary_dict["polyline"] = hd1 + temporary_dict["ID"] = "r" + learning_objects.append(temporary_dict) + temporary_list.append(hd1) + polylines.extend(temporary_list) + + beta = beta # beta funtion to get more variance when plotting the polyline + polyline2 = polylines.copy() + beta_polylines = [] + for line in polyline2: + v2 = [] + mean_val = np.average(line) + len_arr = len(line) + for j in line: + j = j + beta*(j - mean_val) + if j > 1: + j = 1 + if j < 0: + j = 0 + v2.append(j) + beta_polylines.append(v2) + + return beta_polylines + +def create_beta_polylines(resource_polylines,beta): + beta = beta # beta funtion to get more variance when plotting the polyline + polyline2 = resource_polylines.copy() + beta_polylines = [] + for line in polyline2: + v2 = [] + mean_val = np.average(line) + len_arr = len(line) + for j in line: + j = j + beta*(j - mean_val) + if j > 1: + j = 1 + if j < 0: + j = 0 + v2.append(j) + beta_polylines.append(v2) + + return beta_polylines + +def create_beta_polyline(polyline, beta): + # Apply beta transformation to a single polyline + beta_polyline = [] + mean_val = np.average(polyline) + + for j in polyline: + j = j + beta * (j - mean_val) + # Clamping the values between 0 and 1 + if j > 1: + j = 1 + if j < 0: + j = 0 + beta_polyline.append(j) + + return beta_polyline + +# +# +# +# learners functions +# +# +# + + +def create_embeddings_centroid_list(l): + new_keybert_embeddings_list = [] + for i in l: + # find the centroid of the embeddings for a doc + index_averages = [sum(x) / len(x) for x in zip(*i)] + new_keybert_embeddings_list.append(index_averages) + return new_keybert_embeddings_list + + +def rad_plot_axes(num: int, x_max: float, y_max: float): + """ + Generate radial plot axes. + + Parameters: + num (int): Number of axes. + x_max (float): Maximum x-coordinate. + y_max (float): Maximum y-coordinate. + + Returns: + tuple: A tuple containing the lengths of the axes and the angle theta. + """ + empt_arr = [] # Temporary container for y-coordinate calculations + xstop = [] # List to store x-coordinate of the axes endpoints + ystop = [] # List to store y-coordinate of the axes endpoints + tlen = [] # List to store the length of axes + ttempl = [] # Temporary container for reversed lengths + theta = ((np.pi) / (num - 1)) / 2 # Calculate theta + b = 0 + + while (b * theta) <= (np.arctan(y_max / x_max)): + y_val = x_max * math.tan(b * theta) + empt_arr.append(y_val) + ystop.append(y_val) + ttemp = math.sqrt((x_max ** 2) + (y_val ** 2)) + tlen.append(ttemp) + if (b * theta) != np.arctan(y_max / x_max): + ttempl.append(ttemp) + b += 1 + + while b < num: + ystop.append(y_max) + b += 1 + + tlen.extend(list(reversed(ttempl))) + xstop = list(reversed(ystop)) + + # Plotting is commented out for modularity; can be enabled as needed + # for d in range(num): + # x_values = [0, xstop[d]] + # y_values = [0, ystop[d]] + # plt.plot(x_values, y_values, label=f'Axis {d+1}', alpha=1, linewidth=0.2) + + return tlen, theta + +# @profile +def rad_plot_poly(num: int, hd_point: list, tlen: list, theta: float) -> list: + """ + Plot the polyline and calculate their centroids. + + Parameters: + num (int): Number of points. + hd_point (list): List of polyline points. + tlen (list): Length of the axes. + theta (float): Angle theta. + + Returns: + list: List of centroid coordinates. + """ + coordinates = [] + + for pnt in hd_point: + x_values = [] + y_values = [] + for p in range(num): + rlen = pnt[p] * tlen[p] + x_values.append(rlen * math.cos(p * theta)) + y_values.append(rlen * math.sin(p * theta)) + + # Plotting is commented out for modularity; can be enabled as needed + # plt.plot(x_values, y_values, label='Polyline', alpha=0.6, linewidth=0.5) + + average_x = sum(x_values) / num + average_y = sum(y_values) / num + coordinates.append([average_x, average_y]) + + # Print statement for debugging + print("Red - Resources ") + + return coordinates + + +def push_topics_to_db(topics: pd.DataFrame, topic_embeddings: list, topic_polylines: pd.DataFrame, course_id: str): + """ + Push topics, their embeddings, and polylines to the database. + + Parameters: + topics (pd.DataFrame): DataFrame containing topics. + topic_embeddings (list): List of topic embeddings. + topic_polylines (pd.DataFrame): DataFrame with topic polylines. + course_id (str): Unique identifier for the course. + """ + # Print the lengths of topics, polylines, and embeddings for debugging + print(len(topics), len(topic_polylines), len(topic_embeddings)) + + # Determine the feature length from the polyline data + feature_length = len(topic_polylines["polyline"][0]) + + # Generate radial plot axes and plot polylines to get centroid coordinates + tlen, theta = rad_plot_axes(feature_length, 1, 1) + centroid_list = rad_plot_poly( + feature_length, topic_polylines["polyline"], tlen, theta) + + # List to hold all topic objects + all_topics = [] + + with app.app_context(): + for i in range(len(topics)): + topic = Topic( + name=topics["name"][i], + description=topics["description"][i], + keywords=topics["tokens"][i], + polyline=topic_polylines["polyline"][i], + x_coordinate=centroid_list[i][0], + y_coordinate=centroid_list[i][1], + course_id=course_id, + embedding=topic_embeddings[i].tolist() + ) + all_topics.append(topic) + + # Add all topics to the session and commit to the database + db.session.add_all(all_topics) + db.session.commit() + + print("Added topics to DB") + + +def get_cord_from_polyline(polylines): + x_max = y_max = 1 + tlen, ttempl = [], [] + b = 0 + theta = ((np.pi) / (len(polylines[0]) - 1)) / 2 + while (b * theta) <= (np.arctan(y_max / x_max)): + y_val = x_max * math.tan(b * theta) + ttemp = math.sqrt((x_max ** 2) + (y_val ** 2)) + tlen.append(ttemp) + if (b * theta) != np.arctan(y_max / x_max): + ttempl.append(ttemp) + b += 1 + + while b < len(polylines[0]): + b += 1 + + tlen.extend(list(reversed(ttempl))) + print(tlen) + coordinates = [] + + for polyline in polylines: + x_values = [] + y_values = [] + for p in range(len(polyline)): + rlen = polyline[p] * tlen[p] + x_values.append(rlen * math.cos(p * theta)) + y_values.append(rlen * math.sin(p * theta)) + coordinates.append([mean(x_values), mean(y_values)]) + return coordinates + + +def pushResourcesToDB(resources, resourceembedding, resource_polylines, course_id): + print(len(resources), len(resource_polylines), len(resourceembedding)) + beta_polylines=create_beta_polylines(resource_polylines,8) + feature_length = len(resource_polylines[0]) + (tlen, theta) = rad_plot_axes(feature_length, 1, 1) + centroid_list = rad_plot_poly( + feature_length, beta_polylines, tlen, theta) + allresources = [] + with app.app_context(): + for i in range(len(resources)): + new_resource = Resource( + name=resources["name"][i], + description=resources["description"][i], + keywords=resources['tokens'][i], + polyline=resource_polylines[i], + x_coordinate=centroid_list[i][0], + y_coordinate=centroid_list[i][1], + course_id=course_id, + type=1, + module_id=resources["module_id"][i], + submodule_id=resources["submodule_id"][i], + module=resources["module"][i], + index=resources["index"][i], + # embedding=resourceembedding[i], + link=resources['links'][i], + beta=8 + ) + # print(new_resource.to_dict()) + allresources.append(new_resource) + # db.session.add(new_resource) + # db.session.commit() + db.session.add_all(allresources) + db.session.commit() + print("added resources to the DB") + # breakpoint() + + +# find the keywords for all the documents and store it in a list +def create_keywords_list(content_list,num_keywords=10): + kw_model = KeyBERT(model='all-mpnet-base-v2') + all_keywords_list = [] + all_weight_list = [] + for i in range(len(content_list)): + keywords = kw_model.extract_keywords(content_list[i], keyphrase_ngram_range=( + 1, 2), stop_words='english', highlight=False, top_n=num_keywords) + keywords_list = list(dict(keywords).keys()) + cs_list = list(dict(keywords).values()) + weight = sum(cs_list)/len(cs_list) + all_keywords_list.append(keywords_list) + all_weight_list.append(weight) + return all_keywords_list, all_weight_list + + +# Load pre-trained BERT model and tokenizer +def create_embeddings_list(l): + model_name = 'bert-base-uncased' + tokenizer = BertTokenizer.from_pretrained(model_name) + new_model = BertModel.from_pretrained(model_name) + + keybert_embeddings_list = [] + for i in l: + + # Tokenize the keywords and convert them into token IDs + tokenized_inputs = tokenizer( + i, padding=True, truncation=True, return_tensors="pt") + + # Obtain the embeddings from the BERT model + with torch.no_grad(): + outputs = new_model(**tokenized_inputs) + + # Extract the embeddings corresponding to the [CLS] token + embeddings = outputs.last_hidden_state[:, 0, :].numpy() + embeddings = embeddings.tolist() + keybert_embeddings_list.append(embeddings) + return keybert_embeddings_list + + +def create_polyline(l, course_id): + all_polylines = [] + embeddings = db.session.query( + Topic.embedding).filter_by(course_id=course_id).all() + topic_embeddings = embeddings + for keybert_embeddings in l: + docVector = keybert_embeddings + polyline = [] + for j in range(len(topic_embeddings)): + wordVector = topic_embeddings[j] + # find cosine similarity between the learner embeddings and the topic embeddings + cos_sim = (get_cos_sim(wordVector, docVector) + 1) / 2 + polyline.append(cos_sim) + all_polylines.append(polyline) + return all_polylines + + + +def pushQuizToResourceInDB( + name, description, keywords, polyline, + x_coordinate, y_coordinate, course_id, + module_id, submodule_id, module, index, + link,beta, type=1): + + new_resource = Resource( + name=name, + description=description, + keywords=keywords, + polyline=polyline, + x_coordinate=x_coordinate, + y_coordinate=y_coordinate, + course_id=course_id, + type=type, + module_id=module_id, + submodule_id=submodule_id, + module=module, + index=index, + link=link, + beta=beta + ) + + with app.app_context(): + db.session.add(new_resource) + db.session.commit() + + print("Quiz resource added to the DB") diff --git a/backend/Inspiration/modelsRoutes.py b/backend/Inspiration/modelsRoutes.py new file mode 100644 index 0000000000000000000000000000000000000000..8c0bc708778eef2ca7bf911d5cd51ac07a1d486f --- /dev/null +++ b/backend/Inspiration/modelsRoutes.py @@ -0,0 +1,1457 @@ +import gc +import json +import math +import os +import re +from datetime import datetime +import numpy as np +import pandas as pd +import pdfplumber +import torch +from flask import jsonify, request, send_from_directory +from utils import is_valid_id +from repository import add_ta_from_user +from dbModels import TAT, Activity, Contribution, Course, Enroll, Learner, Module, Question, Quiz, Resource, Topic, UserQuiz, db, Description,ExitPoint +from init import app +from keybert import KeyBERT +from sentence_transformers import SentenceTransformer +from sqlalchemy import text +from sqlalchemy.sql import func +from transformers import BertModel, BertTokenizer +from werkzeug.utils import secure_filename +from youtube_transcript_api import YouTubeTranscriptApi + + +UPLOAD_FOLDER_NAME = "uploads" +UPLOAD_FOLDER = os.path.join(os.getcwd(), UPLOAD_FOLDER_NAME) # Save files in a 'uploads' folder in the project directory +os.makedirs(UPLOAD_FOLDER, exist_ok=True) # Create the folder if it doesn't exist + +app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER + +def get_cos_sim(a: np.ndarray, b: np.ndarray) -> float: + """ + Calculate the cosine similarity between two vectors. + + Parameters: + a (np.ndarray): First vector. + b (np.ndarray): Second vector. + + Returns: + float: Cosine similarity between the two vectors. + """ + dot_product = np.dot(a, b) + norm_a = np.linalg.norm(a) + norm_b = np.linalg.norm(b) + return dot_product / (norm_a * norm_b) + + +# Routes + +# Learners + + +@app.route('/learners', methods=['GET']) +def get_learners(): + learners = Learner.query.all() + return jsonify([learner.to_dict() for learner in learners]) + + +@app.route('/learners', methods=['POST']) +def create_learner(): + data = request.get_json() + new_learner = Learner( + name=data['name'], + cgpa=data['cgpa'], + username=data['username'], + password=data['password'] + ) + db.session.add(new_learner) + db.session.commit() + return jsonify(new_learner.to_dict()), 201 + +# Courses + + +@app.route('/course/', methods=['GET']) +def get_courses(id): + course = Course.query.filter_by(id=id).first() + return (course.to_dict()) + + +@app.route('/courses', methods=['POST']) +def create_course(): + data = request.get_json() + new_course = Course( + name=data['name'], + description=data['description'], + initial_position=[0]*data['topic_count'] + ) + db.session.add(new_course) + db.session.commit() + return jsonify(new_course.to_dict()), 201 + +# Resources + +@app.route("/resource-types", methods=['GET']) +def get_resource_types(): + return jsonify([ + { "type" : "0", "name" : "PDF"}, + { "type" : "1", "name" : "Youtube Video"}, + # { "type" : "2", "name" : "Quiz"}, :) + ]) + +@app.route('/resources/', methods=['GET']) +def get_resources(id): + resources = Resource.query.filter_by(course_id=id) + # print("resources are ") + # print([resource.to_dict() for resource in resources]) + return jsonify([resource.to_dict() for resource in resources]) + +@app.route('/specific_resource/', methods=['GET']) +def get_specifc_resource(id): + resource = Resource.query.filter_by(id=id).first() + return jsonify(resource.to_dict()) + +@app.route('/resources', methods=['POST']) +def create_resource(): + data = request.get_json() + new_resource = Resource( + name=data['name'], + description=data['description'], + keywords=data['keywords'], + polyline=data['polyline'], + x_coordinate=data['x_coordinate'], + y_coordinate=data['y_coordinate'], + course_id=data['course_id'], + type=data['type'] + # , + # embedding=data['embedding'] + ) + db.session.add(new_resource) + db.session.commit() + return jsonify(new_resource.to_dict()), 201 + +# Topics + + +@app.route('/topics/', methods=['GET']) +def get_topics(id): + topics = Topic.query.filter_by(course_id=id) + return jsonify([topic.to_dict() for topic in topics]) + + +@app.route('/topics', methods=['POST']) +def create_topic(): + data = request.get_json() + new_topic = Topic( + name=data['name'], + description=data['description'], + keywords=data['keywords'], + polyline=data['polyline'], + x_coordinate=data['x_coordinate'], + y_coordinate=data['y_coordinate'], + course_id=data['course_id'], + embedding=data['embedding'] + ) + db.session.add(new_topic) + db.session.commit() + return jsonify(new_topic.to_dict()), 201 + +# Enrolls + + +@app.route('/enrolls/', methods=['GET']) +def get_enroll(id): + enroll = Enroll.query.get(id) + return jsonify(enroll.to_dict()) + +@app.route('/teach/',methods=['GET']) +def get_teach(id): + teach = TAT.query.get(id) + return jsonify(teach.to_dict()) + + +# Activities + + +@app.route('/activities/', methods=['GET']) +def get_activities(id): + activities = Activity.query.filter_by(enroll_id=id) + return jsonify([activity.to_dict() for activity in activities]) + + +@app.route('/activities', methods=['POST']) +def create_activity(): + data = request.get_json() + if data.get('resource_id') is not None: + new_activity = Activity( + time=datetime.strptime(data['time'], '%Y-%m-%d %H:%M:%S'), + # type_id=data['type_id'], + type=data['type'], + name=data['name'], + link=data['link'], + enroll_id=data['enroll_id'], + resource_id=data['resource_id'], + x_coordinate=data['x_coordinate'], + y_coordinate=data['y_coordinate'], + ) + else: + new_activity = Activity( + time=datetime.strptime(data['time'], '%Y-%m-%d %H:%M:%S'), + # type_id=data['type_id'], + type=data['type'], + name=data['name'], + link=data['link'], + enroll_id=data['enroll_id'], + contribution_id=data['contribution_id'], + x_coordinate=data['x_coordinate'], + y_coordinate=data['y_coordinate'], + ) + db.session.add(new_activity) + db.session.commit() + return jsonify(new_activity.to_dict()), 201 + +# Contributions + + +@app.route('/contributions/', methods=['GET']) +def get_contributions(id): + contributions = Contribution.query.filter_by(enroll_id=id) + + return jsonify([contribution.to_dict() for contribution in contributions]) + +@app.route('/contributions/view/', methods=['GET']) +def get_contribution_view(id): + contribution = Contribution.query.filter_by(id=id).first() + + return jsonify(contribution.to_dict()) + + +@app.route('/contributions', methods=['POST']) +def create_contribution(): + data = request.get_json() + new_contribution = Contribution( + enroll_id=data['enroll_id'], + submitted_on=datetime.strptime( + data['submitted_on'], '%Y-%m-%d %H:%M:%S'), + file_path=data['file_path'], + description=data['description'], + prev_polyline=data['prev_polyline'], + polyline=data['polyline'], + x_coordinate=data['x_coordinate'], + y_coordinate=data['y_coordinate'], + embedding=data['embedding'] + ) + db.session.add(new_contribution) + db.session.commit() + return jsonify(new_contribution.to_dict()), 201 + +# Quizzes + + +@app.route('/quizzes', methods=['GET']) +def get_quizzes(): + quizzes = Quiz.query.all() + return jsonify([quiz.to_dict() for quiz in quizzes]) + + +# Questions + + +@app.route('/questions', methods=['GET']) +def get_questions(): + quiz_id = request.args.get('quiz_id') + questions = Question.query.filter_by(quiz_id=quiz_id).all() + return jsonify([question.to_dict() for question in questions]) + + +@app.route('/questions', methods=['POST']) +def create_question(): + data = request.get_json() + new_question = Question( + quiz_id=data['quiz_id'], + question_text=data['question_text'], + option_a=data.get('option_a'), + option_b=data.get('option_b'), + option_c=data.get('option_c'), + option_d=data.get('option_d'), + correct_answer=data['correct_answer'] + ) + db.session.add(new_question) + db.session.commit() + return jsonify(new_question.to_dict()), 201 + +# UserQuiz : log of quizzes attempted by various users + + +@app.route('/user_quizzes', methods=['GET']) +def get_user_quizzes(): + user_id = request.args.get('user_id') + user_quizzes = UserQuiz.query.filter_by(user_id=user_id).all() + return jsonify([user_quiz.to_dict() for user_quiz in user_quizzes]) + + +@app.route('/user_quizzes', methods=['POST']) +def create_user_quiz(): + data = request.get_json() + new_user_quiz = UserQuiz( + user_id=data['user_id'], + quiz_id=data['quiz_id'], + score=data.get('score'), + status=data['status'], + attempt_date=datetime.strptime( + data['attempt_date'], '%Y-%m-%d %H:%M:%S') if 'attempt_date' in data else None + ) + db.session.add(new_user_quiz) + db.session.commit() + return jsonify(new_user_quiz.to_dict()), 201 + +# ------------------------------------------------------Teacher APIs + +@app.route('/course_module_mappings/', methods=['GET']) +def course_module_mappings(course_id): + try: + # Query for ModName_ModID mapping + mod_name_mod_id_query = ( + db.session.query(Resource.module, Resource.module_id) + .filter(Resource.course_id == course_id) + .distinct() + .all() + ) + ModName_ModID = {row.module: row.module_id for row in mod_name_mod_id_query} + + # Query for ModID_SubModCount mapping + mod_id_submod_count_query = ( + db.session.query(Resource.module_id, func.count(Resource.submodule_id)) + .filter(Resource.course_id == course_id) + .group_by(Resource.module_id) + .all() + ) + ModID_SubModCount = {row.module_id: row[1] for row in mod_id_submod_count_query} + + # Return the mappings as JSON + return jsonify({ + "ModName_ModID": ModName_ModID, + "ModID_SubModCount": ModID_SubModCount + }) + except Exception as e: + return jsonify({"error": str(e)}), 500 + +# Function to create topic embeddings +def create_topic_embeddings(topics: pd.DataFrame) -> list: + model = SentenceTransformer('bert-base-nli-mean-tokens') + topic_embeddings = [] + + for i in range(len(topics)): + embedding = model.encode(topics.loc[i, "description"], convert_to_tensor=True) + topic_embeddings.append(embedding.cpu().numpy().tolist()) # Ensure list for JSON compatibility + del embedding + gc.collect() + + return topic_embeddings + +# Function to create polylines from embeddings +def create_topic_polylines(topics: pd.DataFrame, topic_embeddings: list) -> pd.DataFrame: + topic_names = topics["name"].tolist() + length = len(topics) + topic_modules = [1] * 12 + [2] * 8 + [3] * (length - 20) + + top_poly = [] + top_module = [] + topic = [] + + for i in range(len(topic_names)): + polyline = [0] * len(topic_names) + for j in range(len(topic_names)): + cos_sim = 0 + if topic_names[i] == topic_names[j]: + cos_sim = 1 + else: + topic1_vector = topic_embeddings[i] + topic2_vector = topic_embeddings[j] + cos_sim = (get_cos_sim(topic1_vector, topic2_vector) + 1) / 2 + polyline[j] = cos_sim + + topic.append(topic_names[i]) + top_module.append(topic_modules[i]) + top_poly.append(polyline) + + polyline_dict = {"topic": topic, "module": top_module, "polyline": top_poly} + topic_polylines = pd.DataFrame(polyline_dict) + return topic_polylines + +# Function to create a list of keywords from the topic descriptions +def create_keywords_list(content_list, num_keywords=10): + kw_model = KeyBERT(model='all-mpnet-base-v2') + all_keywords_list = [] + all_weight_list = [] + + for i in range(len(content_list)): + keywords = kw_model.extract_keywords(content_list[i], keyphrase_ngram_range=(1, 2), stop_words='english', highlight=False, top_n=num_keywords) + keywords_list = list(dict(keywords).keys()) + cs_list = list(dict(keywords).values()) + + + + # Ensure safe handling of cs_list + if isinstance(cs_list, (list, np.ndarray)) and len(cs_list) > 0: + weight = np.mean(cs_list) # Use NumPy's mean for safer handling + else: + weight = 0 # Default weight if cs_list is empty or invalid + + all_keywords_list.append(keywords_list) + all_weight_list.append(weight) + + return all_keywords_list, all_weight_list + + +def rad_plot_axes(num: int, x_max: float, y_max: float): + """ + Generate radial plot axes. + + Parameters: + num (int): Number of axes. + x_max (float): Maximum x-coordinate. + y_max (float): Maximum y-coordinate. + + Returns: + tuple: A tuple containing the lengths of the axes and the angle theta. + """ + empt_arr = [] # Temporary container for y-coordinate calculations + xstop = [] # List to store x-coordinate of the axes endpoints + ystop = [] # List to store y-coordinate of the axes endpoints + tlen = [] # List to store the length of axes + ttempl = [] # Temporary container for reversed lengths + theta = ((np.pi) / (num - 1)) / 2 # Calculate theta + b = 0 + + while (b * theta) <= (np.arctan(y_max / x_max)): + y_val = x_max * math.tan(b * theta) + empt_arr.append(y_val) + ystop.append(y_val) + ttemp = math.sqrt((x_max ** 2) + (y_val ** 2)) + tlen.append(ttemp) + if (b * theta) != np.arctan(y_max / x_max): + ttempl.append(ttemp) + b += 1 + + while b < num: + ystop.append(y_max) + b += 1 + + tlen.extend(list(reversed(ttempl))) + xstop = list(reversed(ystop)) + + # Plotting is commented out for modularity; can be enabled as needed + # for d in range(num): + # x_values = [0, xstop[d]] + # y_values = [0, ystop[d]] + # plt.plot(x_values, y_values, label=f'Axis {d+1}', alpha=1, linewidth=0.2) + + return tlen, theta + + +def rad_plot_poly(num: int, hd_point: list, tlen: list, theta: float) -> list: + """ + Plot the polyline and calculate their centroids. + + Parameters: + num (int): Number of points. + hd_point (list): List of polyline points. + tlen (list): Length of the axes. + theta (float): Angle theta. + + Returns: + list: List of centroid coordinates. + """ + coordinates = [] + + for pnt in hd_point: + x_values = [] + y_values = [] + for p in range(num): + rlen = pnt[p] * tlen[p] + x_values.append(rlen * math.cos(p * theta)) + y_values.append(rlen * math.sin(p * theta)) + + # Plotting is commented out for modularity; can be enabled as needed + # plt.plot(x_values, y_values, label='Polyline', alpha=0.6, linewidth=0.5) + + average_x = sum(x_values) / num + average_y = sum(y_values) / num + coordinates.append([average_x, average_y]) + + # Print statement for debugging + print("Red - Resources ") + + return coordinates + + + +@app.route('/add-module', methods=['POST']) +def add_modules(): + try: + data = request.get_json() + course_id = data.get('course_id') + name = data.get('name') + + if not course_id or not name: + return jsonify({"error": "course_id and name are required"}), 400 + + new_module = Module( + name=name, + course_id=course_id, + ) + + db.session.add(new_module) + db.session.commit() + return jsonify(new_module.to_dict()), 201 + + except Exception as e: + app.logger.error(f"Error in add_modules: {str(e)}", exc_info=True) + return jsonify({"error": "Server error", "details": str(e)}), 500 + + +# Route for creating topics related to a newly created course +@app.route('/new-course-topics', methods=['POST']) +def create_new_topics_for_new_course(): + try: + data = request.get_json() + + # Validate input + course_id = data.get('course_id') + topics_data = data.get('topics') + + if not course_id or not topics_data or not isinstance(topics_data, list): + return jsonify({"error": "Invalid or missing 'course_id' or 'topics'"}), 400 + + topics = pd.DataFrame(topics_data) + + # Generate embeddings, polylines, and keywords + topic_embeddings = create_topic_embeddings(topics) + topic_polylines = create_topic_polylines(topics, topic_embeddings) + keywords, weights = create_keywords_list(topics["description"].tolist()) + + # Ensure polylines exist before calculating centroids + if not topic_polylines.empty and "polyline" in topic_polylines: + feature_length = len(topic_polylines["polyline"][0]) + tlen, theta = rad_plot_axes(feature_length, 1, 1) + centroid_list = rad_plot_poly(feature_length, topic_polylines["polyline"], tlen, theta) + else: + return jsonify({"error": "Failed to generate topic polylines"}), 500 + + # Check if generated lists match topic count + if len(topic_embeddings) != len(topics) or len(topic_polylines) != len(topics) or len(centroid_list) != len(topics): + return jsonify({"error": "Mismatch in topic processing results"}), 500 + + # Insert topics into the database + for i in range(len(topics)): + new_topic = Topic( + name=topics.loc[i, 'name'], + description=topics.loc[i, 'description'], + module_id=topics.loc[i, 'module_id'], + keywords=keywords[i] if i < len(keywords) else None, + polyline=topic_polylines.loc[i, 'polyline'] if i < len(topic_polylines) else None, + course_id=course_id, + x_coordinate=centroid_list[i][0] if i < len(centroid_list) else None, + y_coordinate=centroid_list[i][1] if i < len(centroid_list) else None, + embedding=topic_embeddings[i] if i < len(topic_embeddings) else None + ) + db.session.add(new_topic) + + db.session.commit() + + return jsonify({"message": "Topics created successfully"}), 201 + + except Exception as e: + app.logger.error(f"Error in create_new_topics_for_new_course: {str(e)}", exc_info=True) + return jsonify({"error": "Server error", "details": str(e)}), 500 + +def extract_transcript(video_id): + try: + transcript = YouTubeTranscriptApi.get_transcript(video_id) + transcript_text = "" + for i in transcript: + transcript_text += i['text'] + " " + return transcript_text.strip() + except Exception as e: + return f"Error: {str(e)}" + + +def get_youtube_video_id(url): + # Regular expression pattern to match video ID + pattern = r"(?:v=|\/)([0-9A-Za-z_-]{11}).*" + match = re.search(pattern, url) + if match: + return match.group(1) + else: + return "Invalid YouTube URL" + + +def create_resource_embeddings(keywords): + + if not keywords: + print("ERROR: Received empty keywords list!") + return [] + + model_name = 'bert-base-uncased' + try: + tokenizer = BertTokenizer.from_pretrained(model_name) + model = BertModel.from_pretrained(model_name) + except Exception as e: + print(f"ERROR: Failed to load BERT model -> {e}") + return [] + + model.eval() + keybert_embeddings_list = [] + + for keyword in keywords: + if not isinstance(keyword, str) or not keyword.strip(): + print(f"WARNING: Skipping invalid keyword -> {keyword}") + continue + + try: + tokenized_inputs = tokenizer(keyword, padding=True, truncation=True, return_tensors="pt") + with torch.no_grad(): + outputs = model(**tokenized_inputs) + + embeddings = outputs.last_hidden_state[:, 0, :].detach().cpu().numpy() + + # Ensure embeddings are always lists of lists + if isinstance(embeddings, np.ndarray): + embeddings = embeddings.tolist() + + if isinstance(embeddings, list) and isinstance(embeddings[0], list): + keybert_embeddings_list.append(embeddings[0]) + else: + print(f"ERROR: Unexpected embedding format for '{keyword}', skipping.") + continue + + except Exception as e: + print(f"ERROR: Failed to generate embedding for '{keyword}' -> {e}") + continue + + return keybert_embeddings_list + +def get_topic_embedding(topic_id): + topic = Topic.query.filter_by(id=topic_id).first() + + if topic and topic.embedding is not None: + + if isinstance(topic.embedding, np.ndarray): + topic_embeddings = topic.embedding.tolist() # Convert NumPy array to list + elif isinstance(topic.embedding, list): + topic_embeddings = topic.embedding # Already a list + else: + print("ERROR: Unexpected topic embedding format!") + return None # Handle unexpected data formats + + # ๐Ÿ”น Ensure `topic_embeddings` is a list of vectors + if isinstance(topic_embeddings, list): + if all(isinstance(vec, (list, np.ndarray)) for vec in topic_embeddings): + return topic_embeddings # โœ… Correct format + elif isinstance(topic_embeddings[0], (float, int)): + # ๐Ÿ”น If it's a single vector (not wrapped in a list), wrap it + print("WARNING: Detected single topic embedding, wrapping it in a list.") + return [topic_embeddings] + + + return None + + + return None # If topic is not found or embedding is None + + + +def create_resource_polylines(topic_embeddings, keybert_embeddings_list, beta): + if not keybert_embeddings_list or not topic_embeddings: + print("ERROR: Empty embeddings provided") + return [] + + all_polylines = [] + + for i, docVector in enumerate(keybert_embeddings_list): + if not isinstance(docVector, list): + print(f"ERROR: Skipping invalid embedding at index {i} -> Expected list, got {type(docVector)}") + continue + + polyline = [] + for idx, wordVector in enumerate(topic_embeddings): + if not isinstance(wordVector, list): + print(f"ERROR: Invalid topic embedding at [{idx}] -> Expected list, got {type(wordVector)}") + continue + + cos_sim = get_cos_sim(wordVector, docVector) + cos_sim = (cos_sim + 1) / 2 if cos_sim is not None else 0 # Normalize and handle None + polyline.append({'x': idx, 'y': cos_sim}) + + all_polylines.append(polyline) + + if not all_polylines: + print("WARNING: No polylines were generated") + return [] + + # Averaging polylines across multiple keywords + new_polylines = [] + for polyline in all_polylines: + averaged_polyline = [sum(p['y'] for p in polyline) / len(polyline) for _ in range(len(topic_embeddings))] + new_polylines.append(averaged_polyline) + + beta = float(beta) + beta_polylines = [[max(0, min(val + beta * (val - np.mean(polyline)), 1)) for val in polyline] for polyline in new_polylines] + + + return beta_polylines + + +@app.route('/new-resources-topics', methods=['POST']) +def create_new_resources_for_new_course(): + try: + data = request.get_json() + + name = data.get('name') + course_id = data.get('course_id') + module_id = data.get('module_id') + res_type = data.get('type') + link = data.get('link') + module = data.get('module') + + if not all([course_id, module_id, res_type, link]): + return jsonify({"error": "Missing required fields"}), 400 + + video_id = get_youtube_video_id(link) + if video_id == "Invalid YouTube URL": + return jsonify({"error": "Invalid YouTube URL"}), 400 + + transcript = extract_transcript(video_id) + if transcript.startswith("Error:"): + return jsonify({"error": f"Failed to fetch transcript: {transcript}"}), 400 + + try: + keywords, weights = create_keywords_list([transcript]) + if keywords and isinstance(keywords[0], list): + keywords = [word for sublist in keywords for word in sublist] + + except Exception as e: + return jsonify({"error": f"Failed to extract keywords: {str(e)}"}), 400 + + if not keywords: + return jsonify({"error": "No keywords extracted"}), 400 + + resource_embeddings = create_resource_embeddings(keywords) + if not resource_embeddings: + return jsonify({"error": "Failed to generate resource embeddings"}), 400 + + topic_embeddings = get_topic_embedding(module_id) + + # Debugging + if topic_embeddings is None: + return jsonify({"error": "Topic embeddings not found"}), 400 + + if isinstance(topic_embeddings, np.ndarray) and topic_embeddings.ndim == 1: + topic_embeddings = [topic_embeddings] + + # If only one topic embedding is available, duplicate it + if len(topic_embeddings) == 1: + print("WARNING: Only one topic embedding found, duplicating it.") + topic_embeddings.append(topic_embeddings[0]) + + if len(topic_embeddings) < 2: + return jsonify({"error": "Insufficient topic embeddings"}), 400 + + resource_polylines = create_resource_polylines(topic_embeddings, resource_embeddings, 8) + if not resource_polylines: + return jsonify({"error": "Generated polylines are empty"}), 400 + + # Ensure num_axes is valid + num_axes = len(topic_embeddings) + x_max, y_max = 1.0, 1.0 + + # Compute axes lengths and angle safely + tlen, theta = rad_plot_axes(num_axes, x_max, y_max) + + # Compute x, y coordinates + centroids = rad_plot_poly(num_axes, resource_polylines, tlen, theta) + + max_id = db.session.query(db.func.max(Resource.id)).scalar() or 0 + new_resources = [] + + if centroids: + x_coordinate, y_coordinate = centroids[0] # Use only the first centroid + new_resource = Resource( + id=max_id + 1, + name=name, + description=None, + keywords=keywords, + polyline=resource_polylines, + x_coordinate=x_coordinate, + y_coordinate=y_coordinate, + course_id=course_id, + module_id=module_id, + submodule_id=None, + type=res_type, + link=link, + index=max_id + 1, + module=module, + beta=8 + ) + + db.session.add(new_resource) + db.session.commit() + + return jsonify({"message": "Resource created successfully"}), 201 + else: + return jsonify({"error": "No valid centroid found"}), 400 + + except Exception as e: + db.session.rollback() + app.logger.error(f"Error occurred: {e}") + return jsonify({"error": "Server error", "details": str(e)}), 500 + + +def allowed_file(filename): + ALLOWED_EXTENSIONS = {"pdf"} + return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS + +@app.route('/uploads/') +def send_file(filename: str): + filename = secure_filename(filename) + filepath = os.path.join(app.config["UPLOAD_FOLDER"], filename) + if allowed_file(filename): + if os.path.exists(filepath): + return send_from_directory(UPLOAD_FOLDER_NAME, filename) + else: + return jsonify({'error': 'file not found'}), 404 + return jsonify({'error': 'invalid filename'}), 400 + +@app.route("/upload-pdf-resource", methods=["POST"]) +def upload_pdf_resource(): + try: + if "pdf_file" not in request.files: + return jsonify({"error": "No PDF file provided"}), 400 + + pdf_file = request.files["pdf_file"] + if pdf_file.filename == "": + return jsonify({"error": "No selected file"}), 400 + + if not allowed_file(pdf_file.filename): + return jsonify({"error": "Invalid file type"}), 400 + + # Secure filename and save file + filename = secure_filename(pdf_file.filename) + filepath = os.path.join(app.config["UPLOAD_FOLDER"], filename) + pdf_file.save(filepath) + + # Extract metadata + name = request.form.get("name") + course_id = request.form.get("course_id") + module_id = request.form.get("module_id") + res_type = request.form.get("type") + module = request.form.get("module") + + if not all([name, course_id, module_id, res_type]): + return jsonify({"error": "Missing required fields"}), 400 + + # Extract text from PDF + extracted_text = "" + with pdfplumber.open(filepath) as pdf: + for page in pdf.pages: + extracted_text += page.extract_text() or "" + + if not extracted_text.strip(): + return jsonify({"error": "Failed to extract text from PDF"}), 400 + + # Generate keywords + try: + keywords, _ = create_keywords_list([extracted_text]) + if keywords and isinstance(keywords[0], list): + keywords = [word for sublist in keywords for word in sublist] + except Exception as e: + return jsonify({"error": f"Failed to extract keywords: {str(e)}"}), 400 + + if not keywords: + return jsonify({"error": "No keywords extracted"}), 400 + + # Generate embeddings + resource_embeddings = create_resource_embeddings(keywords) + if not resource_embeddings: + return jsonify({"error": "Failed to generate resource embeddings"}), 400 + + topic_embeddings = get_topic_embedding(module_id) + if topic_embeddings is None: + return jsonify({"error": "Topic embeddings not found"}), 400 + + if isinstance(topic_embeddings, np.ndarray) and topic_embeddings.ndim == 1: + topic_embeddings = [topic_embeddings] + + if len(topic_embeddings) == 1: + topic_embeddings.append(topic_embeddings[0]) + + if len(topic_embeddings) < 2: + return jsonify({"error": "Insufficient topic embeddings"}), 400 + + # Generate resource polylines + resource_polylines = create_resource_polylines(topic_embeddings, resource_embeddings, 8) + if not resource_polylines: + return jsonify({"error": "Generated polylines are empty"}), 400 + + num_axes = len(topic_embeddings) + x_max, y_max = 1.0, 1.0 + tlen, theta = rad_plot_axes(num_axes, x_max, y_max) + centroids = rad_plot_poly(num_axes, resource_polylines, tlen, theta) + + max_id = db.session.query(db.func.max(Resource.id)).scalar() or 0 + if centroids: + x_coordinate, y_coordinate = centroids[0] + + new_resource = Resource( + id=max_id + 1, + name=name, + description=None, + keywords=keywords, + polyline=resource_polylines, + x_coordinate=x_coordinate, + y_coordinate=y_coordinate, + course_id=course_id, + module_id=module_id, + submodule_id=None, + type=res_type, + link='/' + UPLOAD_FOLDER_NAME + '/' + filename, + index=max_id + 1, + module=module, + beta=8 + ) + + db.session.add(new_resource) + db.session.commit() + + return jsonify({"message": "PDF Resource uploaded successfully"}), 201 + else: + return jsonify({"error": "No valid centroid found"}), 400 + + except Exception as e: + db.session.rollback() + app.logger.error(f"Error occurred: {e}") + return jsonify({"error": "Server error", "details": str(e)}), 500 + + +def convert_to_lists(data): + if isinstance(data, np.ndarray): + return data.tolist() + elif isinstance(data, list): + return [convert_to_lists(item) for item in data] + elif isinstance(data, dict): + return {key: convert_to_lists(value) for key, value in data.items()} + else: + return data + +@app.route('/ta-gi-summary',methods=['POST']) +def changed_by_summary(): + try: + print("[REQUEST RECEIVED] Processing new TA description request...") + + # Parse request data + data = request.get_json() + #user_id = data.get('user_id') + ta_id = data.get('ta_id') + course_id = data.get('course_id') + description = data.get('description') + + # if not isinstance(ta_id, int): + # print("adding new ta...") + # ta_id = add_ta_from_user(user_id)["id"] + + # Validate inputs + if not all([ta_id, course_id, description]): + print("p1") + print(ta_id) + print(description) + print(course_id) + return jsonify({"error": "Missing required fields"}), 400 + + # Generate keywords + all_keywords_list, _ = create_keywords_list([description]) + if not all_keywords_list: + print("p2") + return jsonify({"error": "Keyword extraction failed"}), 400 + if isinstance(all_keywords_list[0], list): + all_keywords_list = [word for sublist in all_keywords_list for word in sublist] + + # Generate embeddings + learner_embeddings = create_resource_embeddings(all_keywords_list) + if not learner_embeddings: + print("p3") + return jsonify({"error": "Embedding generation failed"}), 400 + + # Fetch topic embeddings for the given course_id + raw_embeddings = db.session.query(Topic.embedding).filter_by(course_id=course_id).all() + + # Convert embeddings safely + topic_embeddings = [] + for embed in raw_embeddings: + try: + emb_value = json.loads(embed[0]) if isinstance(embed[0], str) else embed[0] + if isinstance(emb_value, list): + topic_embeddings.append(emb_value) + else: + print(f"ERROR: Invalid topic embedding -> Expected list, got {type(emb_value)}") + except json.JSONDecodeError: + print("ERROR: Failed to parse embedding JSON") + + # Validate topic embeddings + if not topic_embeddings: + print("p4") + return jsonify({"error": "No valid topic embeddings found for this course"}), 400 + + # Generate polylines + learner_polylines = create_resource_polylines(topic_embeddings, learner_embeddings, 8) + if not learner_polylines: + print("p5") + return jsonify({"error": "Polyline generation failed"}), 400 + + # Convert to description polyline + description_polyline_list = [item for sublist in convert_to_lists(learner_polylines[0]) for item in ( + sublist if isinstance(sublist, list) else [sublist])] + + # Compute axes and centroid + feature_length = len(learner_polylines[0]) + tlen, theta = rad_plot_axes(feature_length, 1, 1) + centroid_list = rad_plot_poly(feature_length, [description_polyline_list], tlen, theta) + + # Extract centroid coordinates + x_coordinate, y_coordinate = centroid_list[0] + + # Insert description into description table + insert_description_query = text(""" + INSERT INTO description (ta_id, description,course_id) + VALUES (:ta_id, :description, :course_id) + """) + + db.session.execute(insert_description_query, { + "ta_id": ta_id, + "description": json.dumps(description), + "course_id": course_id + }) + + print(f"Saving to tat: x={x_coordinate}, y={y_coordinate}") + + # ๐Ÿ”น Save TA position in tat table + # update_query = text(""" + # INSERT INTO tat (ta_id, course_id, x_coordinate, y_coordinate, polyline) + # VALUES (:ta_id, :course_id, :x_coordinate, :y_coordinate, :polyline) + # """) + + # db.session.execute(update_query, { + # "ta_id": ta_id, + # "course_id": course_id, + # "x_coordinate": float(x_coordinate), + # "y_coordinate": float(y_coordinate), + # "polyline": json.dumps(learner_polylines) + # }) + + max_learner_id = db.session.query(db.func.max(Learner.id)).scalar() + new_learner_id = (max_learner_id ) if max_learner_id else 1 # Start from 1 if table is empty + + # ๐Ÿ”น Fetch all resource indexes for this course_id + resource_indexes = db.session.query(Resource.index).filter_by(course_id=course_id).all() + resource_index_list = [idx[0] for idx in resource_indexes if idx[0] is not None] # Extract values from tuples + + # ๐Ÿ”น Insert into enroll table with learner_id as max+1 and ta_id as given + enroll_insert_query = text(""" + INSERT INTO enroll (learner_id, course_id, x_coordinate, y_coordinate, polyline, ta_id, accessible_resources) + VALUES (:learner_id, :course_id, :x_coordinate, :y_coordinate, :polyline, :ta_id, :accessible_resources) + """) + + db.session.execute(enroll_insert_query, { + "learner_id": new_learner_id, + "course_id": course_id, + "x_coordinate": float(x_coordinate), + "y_coordinate": float(y_coordinate), + "polyline": json.dumps(learner_polylines), + "ta_id": ta_id, + "accessible_resources": json.dumps(resource_index_list) # Store indexes as JSON array + }) + + db.session.commit() + print("[SUCCESS] TA position stored successfully!") + return jsonify({"message": "TA position stored successfully"}), 201 + + except ValueError as ve: + print(f"[ERROR] Validation error: {str(ve)}") + return jsonify({"error": "Validation Error", "details": str(ve)}), 400 + + except Exception as e: + db.session.rollback() + print(f"[ERROR] Server error: {str(e)}") + return jsonify({"error": "Internal Server Error", "details": str(e)}), 500 + +@app.route('/ta-ch-description', methods=['POST']) +def changed_ta_position(): + try: + print("[REQUEST RECEIVED] Processing new TA description request...") + + # Parse request data + data = request.get_json() + user_id = data.get('user_id') + ta_id = data.get('ta_id') + course_id = data.get('course_id') + description = data.get('description') + + if not is_valid_id(ta_id): + print("adding new ta...") + ta_id = add_ta_from_user(user_id)["id"] + + # Validate inputs + if not all([ta_id, course_id, description]): + return jsonify({"error": "Missing required fields"}), 400 + + # Generate keywords + all_keywords_list, _ = create_keywords_list([description]) + if not all_keywords_list: + return jsonify({"error": "Keyword extraction failed"}), 400 + if isinstance(all_keywords_list[0], list): + all_keywords_list = [word for sublist in all_keywords_list for word in sublist] + + # Generate embeddings + learner_embeddings = create_resource_embeddings(all_keywords_list) + if not learner_embeddings: + return jsonify({"error": "Embedding generation failed"}), 400 + + # Fetch topic embeddings for the given course_id + raw_embeddings = db.session.query(Topic.embedding).filter_by(course_id=course_id).all() + + # Convert embeddings safely + topic_embeddings = [] + for embed in raw_embeddings: + try: + emb_value = json.loads(embed[0]) if isinstance(embed[0], str) else embed[0] + if isinstance(emb_value, list): + topic_embeddings.append(emb_value) + else: + print(f"ERROR: Invalid topic embedding -> Expected list, got {type(emb_value)}") + except json.JSONDecodeError: + print("ERROR: Failed to parse embedding JSON") + + if not topic_embeddings: + return jsonify({"error": "No valid topic embeddings found for this course"}), 400 + + # Generate polylines + learner_polylines = create_resource_polylines(topic_embeddings, learner_embeddings, 8) + if not learner_polylines: + return jsonify({"error": "Polyline generation failed"}), 400 + + description_polyline_list = [item for sublist in convert_to_lists(learner_polylines[0]) for item in ( + sublist if isinstance(sublist, list) else [sublist])] + + feature_length = len(learner_polylines[0]) + tlen, theta = rad_plot_axes(feature_length, 1, 1) + centroid_list = rad_plot_poly(feature_length, [description_polyline_list], tlen, theta) + + x_coordinate, y_coordinate = centroid_list[0] + + # ๐Ÿ”น Insert into description table + insert_description_query = text(""" + INSERT INTO description (ta_id, description) + VALUES (:ta_id, :description) + """) + db.session.execute(insert_description_query, { + "ta_id": ta_id, + "description": json.dumps(description) + }) + + print(f"Saving to tat: x={x_coordinate}, y={y_coordinate}") + + # ๐Ÿ”น Insert into tat table + update_query = text(""" + INSERT INTO tat (ta_id, course_id, x_coordinate, y_coordinate, polyline) + VALUES (:ta_id, :course_id, :x_coordinate, :y_coordinate, :polyline) + """) + db.session.execute(update_query, { + "ta_id": ta_id, + "course_id": course_id, + "x_coordinate": float(x_coordinate), + "y_coordinate": float(y_coordinate), + "polyline": json.dumps(learner_polylines) + }) + + # ๐Ÿ”น Fetch user info to add to learner table + user_data_query = text(""" + SELECT registered_date, name, username, password FROM user WHERE id = :user_id + """) + user_data_result = db.session.execute(user_data_query, {"user_id": user_id}).fetchone() + if not user_data_result: + return jsonify({"error": "User not found"}), 404 + + registered_date, name, username, password = user_data_result + + # ๐Ÿ”น Insert into learner table + insert_learner_query = text(""" + INSERT INTO learner (registered_date, name, cgpa, username, password, ta_id) + VALUES (:registered_date, :name, 4, :username, :password, :ta_id) + """) + db.session.execute(insert_learner_query, { + "registered_date": registered_date, + "name": name, + "cgpa": 4, + "username": username, + "password": password, + "ta_id": ta_id + }) + + # ๐Ÿ”น Get newly inserted learner_id + new_learner_id = db.session.query(db.func.max(Learner.id)).scalar() + + # ๐Ÿ”น Fetch accessible resources for course + resource_indexes = db.session.query(Resource.index).filter_by(course_id=course_id).all() + resource_index_list = [idx[0] for idx in resource_indexes if idx[0] is not None] + + # ๐Ÿ”น Insert into enroll table + enroll_insert_query = text(""" + INSERT INTO enroll (learner_id, course_id, x_coordinate, y_coordinate, polyline, ta_id, accessible_resources) + VALUES (:learner_id, :course_id, :x_coordinate, :y_coordinate, :polyline, :ta_id, :accessible_resources) + """) + db.session.execute(enroll_insert_query, { + "learner_id": new_learner_id, + "course_id": course_id, + "x_coordinate": float(x_coordinate), + "y_coordinate": float(y_coordinate), + "polyline": json.dumps(learner_polylines), + "ta_id": ta_id, + "accessible_resources": json.dumps(resource_index_list) + }) + + db.session.commit() + print("[SUCCESS] TA and Learner information stored successfully!") + return jsonify({"message": "TA and Learner data stored successfully"}), 201 + + except ValueError as ve: + print(f"[ERROR] Validation error: {str(ve)}") + return jsonify({"error": "Validation Error", "details": str(ve)}), 400 + + except Exception as e: + db.session.rollback() + print(f"[ERROR] Server error: {str(e)}") + return jsonify({"error": "Internal Server Error", "details": str(e)}), 500 + + +@app.route('/teacher-exit-points', methods=['POST']) +def teacher_exit_points(): + try: + print("[REQUEST RECEIVED] Processing teacher exit point...") + + # Parse request data + data = request.get_json() + course_id = data.get('course_id') + description = data.get('description') + + if not all([course_id, description]): + return jsonify({"error": "Missing required fields"}), 400 + + # ๐Ÿ”น Step 1: Generate keywords + all_keywords_list, _ = create_keywords_list([description]) + if not all_keywords_list: + return jsonify({"error": "Keyword extraction failed"}), 400 + if isinstance(all_keywords_list[0], list): + all_keywords_list = [word for sublist in all_keywords_list for word in sublist] + + # ๐Ÿ”น Step 2: Generate learner embeddings + learner_embeddings = create_resource_embeddings(all_keywords_list) + if not learner_embeddings: + return jsonify({"error": "Embedding generation failed"}), 400 + + # ๐Ÿ”น Step 3: Fetch topic embeddings for the given course_id + raw_embeddings = db.session.query(Topic.embedding).filter_by(course_id=course_id).all() + + topic_embeddings = [] + for embed in raw_embeddings: + try: + emb_value = json.loads(embed[0]) if isinstance(embed[0], str) else embed[0] + if isinstance(emb_value, list): + topic_embeddings.append(emb_value) + else: + print(f"ERROR: Invalid topic embedding -> Expected list, got {type(emb_value)}") + except json.JSONDecodeError: + print("ERROR: Failed to parse embedding JSON") + + if not topic_embeddings: + return jsonify({"error": "No valid topic embeddings found for this course"}), 400 + + # ๐Ÿ”น Step 4: Generate polylines + learner_polylines = create_resource_polylines(topic_embeddings, learner_embeddings, 8) + if not learner_polylines: + return jsonify({"error": "Polyline generation failed"}), 400 + + description_polyline_list = [item for sublist in convert_to_lists(learner_polylines[0]) for item in ( + sublist if isinstance(sublist, list) else [sublist])] + + # ๐Ÿ”น Step 5: Get (x, y) coordinates + feature_length = len(learner_polylines[0]) + tlen, theta = rad_plot_axes(feature_length, 1, 1) + centroid_list = rad_plot_poly(feature_length, [description_polyline_list], tlen, theta) + x_coordinate, y_coordinate = centroid_list[0] + + print(f"[EXIT POINT] x: {x_coordinate}, y: {y_coordinate}") + + # ๐Ÿ”น Step 6: Insert into exit_point table + insert_exit_point_query = text(""" + INSERT INTO exit_point (id, course_id, description, polyline, x, y) + VALUES (:id, :course_id, :description, :polyline, :x, :y) + """) + + # You may want to dynamically generate `id`, e.g., as max(id)+1 + new_id_query = text("SELECT COALESCE(MAX(id), 0) + 1 FROM exit_point") + new_id = db.session.execute(new_id_query).scalar() + + + db.session.execute(insert_exit_point_query, { + "id": new_id, + "course_id": course_id, + "description": json.dumps(description), + "polyline": json.dumps(learner_polylines), + "x": float(x_coordinate), + "y": float(y_coordinate) + }) + + db.session.commit() + print("[SUCCESS] Exit point saved successfully.") + return jsonify({"message": "Exit point stored successfully."}), 201 + + except Exception as e: + db.session.rollback() + print(f"[ERROR] Server error: {str(e)}") + return jsonify({"error": "Internal Server Error", "details": str(e)}), 500 + + +@app.route('/summaries//', methods=['GET']) +def get_summaries(ta_id, course_id): + try: + # Fetch summaries for the given TA and course + summaries = db.session.query(Description).filter_by(ta_id=ta_id, course_id=course_id).all() + if not summaries: + return jsonify({"message": "No summaries found"}), 404 + + # Convert to list of dictionaries + summaries_list = [{"description": summary.description} for summary in summaries] + return jsonify(summaries_list), 200 + + except Exception as e: + app.logger.error(f"Error fetching summaries: {str(e)}", exc_info=True) + return jsonify({"error": "Server error", "details": str(e)}), 500 + +@app.route('/contributions/insert-summary-coordinates//', methods=['POST']) +def insert_summary_coordinates_per_contribution(enroll_id, course_id): + try: + print(f"[REQUEST RECEIVED] Inserting summary coordinates for enroll_id: {enroll_id}, course_id: {course_id}") + + # Fetch summary contributions for enroll_id + contributions = Contribution.query.filter( + Contribution.enroll_id == enroll_id, + Contribution.description['summary'].as_boolean() == True + ).order_by(Contribution.submitted_on.asc()).all() + + if not contributions: + return jsonify({"error": "No summary contributions found for the given enroll_id"}), 404 + + # Fetch topic embeddings for course + raw_embeddings = db.session.query(Topic.embedding).filter_by(course_id=course_id).all() + topic_embeddings = [] + for embed in raw_embeddings: + try: + emb_value = json.loads(embed[0]) if isinstance(embed[0], str) else embed[0] + if isinstance(emb_value, list): + topic_embeddings.append(emb_value) + except json.JSONDecodeError: + print("ERROR: Failed to parse embedding JSON") + + if not topic_embeddings: + return jsonify({"error": "No valid topic embeddings found for this course"}), 400 + + inserted_count = 0 + + for contrib in contributions: + content = contrib.contribution_content + if not content or content.strip() == "": + continue + + # Extract keywords + all_keywords_list, _ = create_keywords_list([content]) + if not all_keywords_list: + print(f"Keyword extraction failed for contribution id: {contrib.id}") + continue + + if isinstance(all_keywords_list[0], list): + all_keywords_list = [word for sublist in all_keywords_list for word in sublist] + + # Generate embeddings + learner_embeddings = create_resource_embeddings(all_keywords_list) + if not learner_embeddings: + print(f"Embedding generation failed for contribution id: {contrib.id}") + continue + + # Generate polyline + learner_polylines = create_resource_polylines(topic_embeddings, learner_embeddings, 8) + if not learner_polylines: + print(f"Polyline generation failed for contribution id: {contrib.id}") + continue + + # Prepare polyline list for centroid calculation + description_polyline_list = [ + item for sublist in convert_to_lists(learner_polylines[0]) + for item in (sublist if isinstance(sublist, list) else [sublist]) + ] + + # Compute (x,y) + feature_length = len(learner_polylines[0]) + tlen, theta = rad_plot_axes(feature_length, 1, 1) + centroid_list = rad_plot_poly(feature_length, [description_polyline_list], tlen, theta) + x_coordinate, y_coordinate = centroid_list[0] + + # Get new id for summary_coordinates + new_id_query = text("SELECT COALESCE(MAX(id), 0) + 1 FROM summary_coordinates") + new_id = db.session.execute(new_id_query).scalar() + + # Insert into summary_coordinates table + insert_query = text(""" + INSERT INTO summary_coordinates (id, enroll_id, course_id, summary, polyline, x_coordinate, y_coordinate) + VALUES (:id, :enroll_id, :course_id, :summary, :polyline, :x, :y) + """) + + db.session.execute(insert_query, { + "id": new_id, + "enroll_id": enroll_id, + "course_id": course_id, + "summary": json.dumps(content), + "polyline": json.dumps(learner_polylines), + "x": float(x_coordinate), + "y": float(y_coordinate) + }) + + inserted_count += 1 + + db.session.commit() + print(f"[SUCCESS] Inserted {inserted_count} summary coordinate entries.") + return jsonify({"message": f"Inserted {inserted_count} summary coordinate entries."}), 201 + + except Exception as e: + db.session.rollback() + print(f"[ERROR] Server error: {str(e)}") + return jsonify({"error": "Internal Server Error", "details": str(e)}), 500 + +@app.route('/exit-points/', methods=['GET']) +def get_exit_coordinates(course_id): + exit_points = ExitPoint.query.filter_by(course_id=course_id).all() + + if not exit_points: + return jsonify([]), 200 # Return empty list if no data found + + coordinates = [ + [float(point.x), float(point.y)] + for point in exit_points + if point.x is not None and point.y is not None + ] + + return jsonify(coordinates), 200 + + +if __name__ == '__main__': + app.run(host="0.0.0.0", debug=True) diff --git a/backend/__init__.py b/backend/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6f5d8b34b81f8ed30a33f4cf7cd6f40aee421ba2 --- /dev/null +++ b/backend/__init__.py @@ -0,0 +1,32 @@ +""" +Flask Application Initialization +Sets up the Flask app with necessary configurations +""" + +from flask import Flask, send_from_directory +from flask_cors import CORS +import os + +# Initialize Flask app +# Serves static files from the build directory (../dist) +app = Flask(__name__, static_folder='../dist', static_url_path='') + +# Enable CORS +CORS(app) + +# Serve React App (Catch-all route) +@app.route('/', defaults={'path': ''}) +@app.route('/') +def serve(path): + # Don't interfere with API routes + if path.startswith('api'): + return {"error": "Not found"}, 404 + + if path != "" and os.path.exists(os.path.join(app.static_folder, path)): + return send_from_directory(app.static_folder, path) + + # Return index.html for SPA routing + return send_from_directory(app.static_folder, 'index.html') + +# Flag to indicate if database was just created +DBcreated = False diff --git a/backend/app.py b/backend/app.py new file mode 100644 index 0000000000000000000000000000000000000000..a35be05ab75502994f72b1797ddf2c5dfbd5924c --- /dev/null +++ b/backend/app.py @@ -0,0 +1,18 @@ +# NLP Learning Grid Backend +# Provides API for grid-based NLP learning system +from init import app + +# Import NLP API routes +try: + import nlp_api +except Exception as e: + print(f"Warning: Could not import nlp_api: {e}") + +@app.route('/') +def index(): + return "NLP Learning Grid Backend is Running!" + +if __name__ == '__main__': + # Run on port 5000 by default + app.run(debug=True, port=5000, host='0.0.0.0') + diff --git a/backend/cert.pem b/backend/cert.pem new file mode 100644 index 0000000000000000000000000000000000000000..595dd695e0b6665acfb49861b062a8f2aa96d662 --- /dev/null +++ b/backend/cert.pem @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIGGjCCBAKgAwIBAgITMXqbiYTBzImh5lOL+GycBWSRCzANBgkqhkiG9w0BAQsF +ADCBnDELMAkGA1UEBhMCSU4xEjAQBgNVBAgMCUthcm5hdGFrYTESMBAGA1UEBwwJ +QmVuZ2FsdXJ1MQwwCgYDVQQKDANXU0wxGzAZBgNVBAsMEk5hdmlnYXRlZF9sZWFy +bmluZzELMAkGA1UEAwwCTkwxLTArBgkqhkiG9w0BCQEWHmd1cnVyYWpvdGFnZXJp +bWFpbDJAZ21haWwuY29tbTAeFw0yNDA3MDcxODU4NDZaFw0yNTA3MDcxODU4NDZa +MIGcMQswCQYDVQQGEwJJTjESMBAGA1UECAwJS2FybmF0YWthMRIwEAYDVQQHDAlC +ZW5nYWx1cnUxDDAKBgNVBAoMA1dTTDEbMBkGA1UECwwSTmF2aWdhdGVkX2xlYXJu +aW5nMQswCQYDVQQDDAJOTDEtMCsGCSqGSIb3DQEJARYeZ3VydXJham90YWdlcmlt +YWlsMkBnbWFpbC5jb21tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +s7lkfW+Wu3ecT9qsW0Omh2j+nzaVD/lN9CREhF7FCqTG0D+lesOvkKqX3Mbi/ZeB +eukOwjiaz18UFRTebYifMv9+aG5PZuByJyrMNOQ7mLpS63qCKZqbdqMkQryT3hHF +Y2A7L4ugUEDu8n22E3wNKF0ZnBz56XBHZb/LwnG2yUJJe9EFscEvnWjz4WqwkRgx +otzlotUN4e6K/wRMehftLLmYa3aBcjALlGLFW0PD04X6Urrbd1xicLdTUAoDqpTu +X/Q2HKZDvUl1nTZSXFrBGjA+a4DtpIQeCMMD561ApLD6iiDKA8cV+2wreBiVgC9K +WN2RjYCGwkxH/swWSzAUixH+cCQmWH+6m64nk6qS32OfhJQJrLd/0uOetetui6sD +82a866CGdtw1mbl1CjtpnEzCdlFvupfNmmbz71eQcSKaq4L60mpGrIoRGokHGFIh +Ns2XojrHUGG5gWSSnDFwHTAVUaG1Ni3oaem59j8oFBwaiL+ouqpB7T7EompPR2VR +YFio4jNKb0q0Tjl9Gqjc6eoZWa8aSlEjOWJqGtyKmfnUDr1cfz6qUxY4FZAh23dl +ZyGt1ho67YLvCq45p4F498i+55tTLUNZyvA6Q6d2lxTKiTg69th+0oGFNWN9pOGf +/P9If7q25mGwpJWFv3dV5p744PyyBu8IfsriY/HkYZ0CAwEAAaNTMFEwHQYDVR0O +BBYEFGeJsHds9rt1dl1exT5/7VSZlZoFMB8GA1UdIwQYMBaAFGeJsHds9rt1dl1e +xT5/7VSZlZoFMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAHp6 +hTNHKkWTGDVQJaFiFYdOg/FuqfpoJYx/0aveVNJiWMaOM0/RR0p55Vz/mI1l8Pb8 +D4zBeJM2lfRSS6/3i5R+sMZVlTIBtlwiyTwBMvfl/x3IW0+bNuWPK+bk7LEf6WcK +KhxKMmyEBPLEz333p5zOqo3rO/lEiT1darQOy+voXdyEXsjahLXQ1QfucdMlKPCZ +16NSaOUTwnWRgkuJk/OySo2VUMIKA45KXJNi5PueKs3UQPZV/jP8AKaj+RNFq9fB +P6SvJAjl39xbFLSOE4hmGP9yggKseQpd4Jw8KH5rpKoja/dAUIH1gmkcqWDHC1eF +MJvpVPz9nArh1xb/Bo+HgCqkGUoGM5073+NaHXLuDHj7PGAtpT2vE8Cts8j6ntNt +kz1FCATI5R0TgFKvqdUREqSiDmoFyDmIs/EU4qmCPqyUVpIm5xGm+BISO8lvUc09 +MIFDqc7Kfct/yu1WS4t48sJNK3nxmCQwXsog5GD+rXVkNVDQ3l6PsUTtIZ5Qv+cu +Cg6UWjg+myUKZjG7FLfHR/L6ND3JSOBURhDVDarFeA4yltELtKXIEJctQtesyT+W +P/CMBory0u8I2uBoCnAOOcc5BuhikmV4uYChOzkgHGSVulZnOsiP+eJb/SKrsxgX +7+kXCwnK5wq9bhuihakMVhwU6fBMFkdj+ABC+fGF +-----END CERTIFICATE----- diff --git a/backend/data/db.json b/backend/data/db.json new file mode 100644 index 0000000000000000000000000000000000000000..92df7fd50d74874f3a1a6dfcafe24d3d4576efef --- /dev/null +++ b/backend/data/db.json @@ -0,0 +1,512 @@ +{ + "users": [], + "learning_sessions": { + "default": { + "position": { + "x": 3, + "y": 13 + }, + "level": 2, + "totalReward": 200, + "visitedResources": [ + "4", + "3", + "2", + "1" + ], + "notifications": [ + { + "id": "notif_1775177177", + "type": "success", + "message": "Level up! You are now Stage 2", + "timestamp": 1775177177230, + "read": false + }, + { + "id": "notif_1775174735", + "type": "success", + "message": "Level up! You are now Stage 2", + "timestamp": 1775174735824, + "read": true + }, + { + "id": "notif_1775174142", + "type": "success", + "message": "Level up! You are now Stage 2", + "timestamp": 1775174142894, + "read": true + }, + { + "id": "notif_1775173994", + "type": "success", + "message": "Level up! You are now Stage 2", + "timestamp": 1775173994762, + "read": true + }, + { + "id": "notif_1775173639", + "type": "success", + "message": "Level up! You are now Stage 2", + "timestamp": 1775173639779, + "read": true + }, + { + "id": "notif_1775172872", + "type": "success", + "message": "Level up! You are now Stage 2", + "timestamp": 1775172872637, + "read": true + }, + { + "id": "notif_1775172411", + "type": "success", + "message": "Level up! You are now Stage 2", + "timestamp": 1775172411178, + "read": true + } + ] + } + }, + "polylines": { + "polyline_20260403_032235": { + "id": "polyline_20260403_032235", + "name": "hye3dg", + "path": [ + { + "x": 2, + "y": 17 + }, + { + "x": 0, + "y": 17 + } + ], + "color": "rgba(121, 114, 255, 0.4)", + "isActive": true, + "summary": "hcioasuhioc", + "keywords_found": [], + "module_scores": [ + 0.036593932658433914, + 0.051530614495277405, + 0.1929866373538971, + 0.1, + 0.045435961335897446, + 0.05906526371836662, + 0.00729989493265748, + 0.08250045776367188, + 0.027184199541807175, + 0.061868976801633835, + 0.05629986152052879, + 0.06286735832691193, + 0.019927512854337692, + 0.06329352408647537, + 0.07700204849243164, + 0.06306321918964386, + 0.03309740498661995, + 0.0 + ], + "strengths": [ + "Tutorial: Introduction to huggingface", + "Fine tuning LLM" + ], + "dominant_topics": [], + "ai_analysis": "Learning profile enriched by modules like Basics. Level 1 achieved with 100 points.", + "assimilation_position": { + "x": 2, + "y": 17 + }, + "next_recommendation": { + "id": "8", + "title": "Incontext Learning", + "position": { + "x": 3, + "y": 13 + }, + "module": "Incontext Learning", + "reason": "dqn" + } + }, + "polyline_20260403_032347": { + "id": "polyline_20260403_032347", + "name": "xas", + "path": [ + { + "x": 2, + "y": 17 + }, + { + "x": 0, + "y": 17 + } + ], + "color": "rgba(153, 107, 255, 0.4)", + "isActive": true, + "summary": "xss", + "keywords_found": [], + "module_scores": [ + 0.0, + 0.0, + 0.15388113260269165, + 0.1306712031364441, + 0.0, + 0.0, + 0.015578164719045162, + 0.006550716236233711, + 0.03091040439903736, + 0.0, + 0.017347849905490875, + 0.06170199066400528, + 0.009152302518486977, + 0.024282503873109818, + 0.022633565589785576, + 0.05440949276089668, + 0.047033052891492844, + 0.0 + ], + "strengths": [ + "Tutorial: Introduction to huggingface", + "Fine tuning LLM" + ], + "dominant_topics": [], + "ai_analysis": "Learning profile enriched by modules like Basics. Level 1 achieved with 100 points.", + "assimilation_position": { + "x": 3, + "y": 13 + }, + "next_recommendation": { + "id": "8", + "title": "Incontext Learning", + "position": { + "x": 3, + "y": 13 + }, + "module": "Incontext Learning", + "reason": "dqn" + } + }, + "polyline_20260403_033404": { + "id": "polyline_20260403_033404", + "name": "heyyy", + "path": [ + { + "x": 3, + "y": 18 + }, + { + "x": 3, + "y": 17 + }, + { + "x": 2, + "y": 17 + }, + { + "x": 0, + "y": 17 + } + ], + "color": "rgba(180, 160, 255, 0.4)", + "isActive": true, + "summary": "heyyyy", + "keywords_found": [], + "module_scores": [ + 0.10115420073270798, + 0.1, + 0.18399392068386078, + 0.19090811908245087, + 0.040012165904045105, + 0.03171403333544731, + 0.004172520712018013, + 0.0, + 0.0, + 0.0, + 0.013521099463105202, + 0.0, + 0.06345637887716293, + 0.02790343202650547, + 0.057048387825489044, + 0.09004955738782883, + 0.0, + 0.0 + ], + "strengths": [ + "Pre training objectives", + "Pre trained models", + "Tutorial: Introduction to huggingface", + "Fine tuning LLM" + ], + "dominant_topics": [], + "ai_analysis": "Learning profile enriched by modules like Basics. Level 2 achieved with 200 points.", + "assimilation_position": { + "x": 3, + "y": 15 + }, + "next_recommendation": { + "id": "8", + "title": "Incontext Learning", + "position": { + "x": 3, + "y": 13 + }, + "module": "Incontext Learning", + "reason": "dqn" + } + }, + "polyline_20260403_042600": { + "id": "polyline_20260403_042600", + "name": "heyyy", + "path": [ + { + "x": 3, + "y": 18 + }, + { + "x": 3, + "y": 17 + }, + { + "x": 2, + "y": 17 + }, + { + "x": 0, + "y": 17 + } + ], + "color": "rgba(154, 164, 255, 0.4)", + "isActive": true, + "summary": "heyyyy", + "keywords_found": [], + "module_scores": [ + 0.10115420073270798, + 0.1, + 0.18399392068386078, + 0.19090811908245087, + 0.040012165904045105, + 0.03171403333544731, + 0.004172520712018013, + 0.0, + 0.0, + 0.0, + 0.013521099463105202, + 0.0, + 0.06345637887716293, + 0.02790343202650547, + 0.057048387825489044, + 0.09004955738782883, + 0.0, + 0.0 + ], + "strengths": [ + "Pre training objectives", + "Pre trained models", + "Tutorial: Introduction to huggingface", + "Fine tuning LLM" + ], + "dominant_topics": [], + "ai_analysis": "Learning profile enriched by modules like Basics. Stage 2 achieved with 200 points.", + "assimilation_position": { + "x": 3, + "y": 13 + }, + "next_recommendation": { + "id": "8", + "title": "Incontext Learning", + "position": { + "x": 3, + "y": 13 + }, + "module": "Incontext Learning", + "reason": "dqn" + } + }, + "polyline_20260403_051831": { + "id": "polyline_20260403_051831", + "name": "HBOIDCUHGDOHQWE", + "path": [ + { + "x": 3, + "y": 18 + }, + { + "x": 3, + "y": 17 + }, + { + "x": 2, + "y": 17 + }, + { + "x": 0, + "y": 17 + } + ], + "color": "rgba(173, 194, 255, 0.4)", + "isActive": true, + "summary": "BGOAIUXDASHGXCIOAHXIO", + "keywords_found": [], + "module_scores": [ + 0.12643486261367798, + 0.1, + 0.12446118891239166, + 0.1, + 0.0, + 0.010079984553158283, + 0.0, + 0.06248624622821808, + 0.0, + 0.02448994107544422, + 0.0, + 0.0, + 0.0, + 0.038079071789979935, + 0.05198737978935242, + 0.06710068136453629, + 0.05336645245552063, + 0.0 + ], + "strengths": [ + "Pre training objectives", + "Pre trained models", + "Tutorial: Introduction to huggingface", + "Fine tuning LLM" + ], + "dominant_topics": [], + "ai_analysis": "Learning profile enriched by modules like Basics. Stage 2 achieved with 200 points.", + "assimilation_position": { + "x": 3, + "y": 13 + }, + "next_recommendation": { + "id": "8", + "title": "Incontext Learning", + "position": { + "x": 3, + "y": 13 + }, + "module": "Incontext Learning", + "reason": "dqn" + } + } + }, + "notes": {}, + "bookmarks": {}, + "summaries": [ + { + "id": "summary_default_20260403_032235", + "title": "hye3dg", + "summary": "hcioasuhioc", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 2, + "currentLevel": 1, + "strengths": [ + "Tutorial: Introduction to huggingface", + "Fine tuning LLM" + ], + "recommendations": [ + "Pre training objectives", + "Pre trained models", + "Instruction tuning" + ], + "ai_analysis": "Learning profile enriched by modules like Basics. Level 1 achieved with 100 points.", + "avgDifficulty": 2.0, + "totalReward": 100, + "xp_earned": 0 + }, + { + "id": "summary_default_20260403_032347", + "title": "xas", + "summary": "xss", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 2, + "currentLevel": 1, + "strengths": [ + "Tutorial: Introduction to huggingface", + "Fine tuning LLM" + ], + "recommendations": [ + "Pre training objectives", + "Pre trained models", + "Instruction tuning" + ], + "ai_analysis": "Learning profile enriched by modules like Basics. Level 1 achieved with 100 points.", + "avgDifficulty": 2.0, + "totalReward": 100, + "xp_earned": 0 + }, + { + "id": "summary_default_20260403_033404", + "title": "heyyy", + "summary": "heyyyy", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 4, + "currentLevel": 1, + "strengths": [ + "Pre training objectives", + "Pre trained models", + "Tutorial: Introduction to huggingface", + "Fine tuning LLM" + ], + "recommendations": [ + "Instruction tuning", + "Prompt based learning", + "Parameter efficient fine tuning" + ], + "ai_analysis": "Learning profile enriched by modules like Basics. Level 2 achieved with 200 points.", + "avgDifficulty": 2.0, + "totalReward": 200, + "xp_earned": 0 + }, + { + "id": "summary_default_20260403_042600", + "title": "heyyy", + "summary": "heyyyy", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 4, + "currentLevel": 2, + "strengths": [ + "Pre training objectives", + "Pre trained models", + "Tutorial: Introduction to huggingface", + "Fine tuning LLM" + ], + "recommendations": [ + "Instruction tuning", + "Prompt based learning", + "Parameter efficient fine tuning" + ], + "ai_analysis": "Learning profile enriched by modules like Basics. Stage 2 achieved with 200 points.", + "avgDifficulty": 2.0, + "totalReward": 200, + "xp_earned": 0 + }, + { + "id": "summary_default_20260403_051831", + "title": "HBOIDCUHGDOHQWE", + "summary": "BGOAIUXDASHGXCIOAHXIO", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 4, + "currentLevel": 2, + "strengths": [ + "Pre training objectives", + "Pre trained models", + "Tutorial: Introduction to huggingface", + "Fine tuning LLM" + ], + "recommendations": [ + "Instruction tuning", + "Prompt based learning", + "Parameter efficient fine tuning" + ], + "ai_analysis": "Learning profile enriched by modules like Basics. Stage 2 achieved with 200 points.", + "avgDifficulty": 2.0, + "totalReward": 200, + "xp_earned": 0 + } + ] +} \ No newline at end of file diff --git a/backend/data/youtube_links.json b/backend/data/youtube_links.json new file mode 100644 index 0000000000000000000000000000000000000000..71a89bf8a15c32c609e9e03d9f4e2396e0c3c1e8 --- /dev/null +++ b/backend/data/youtube_links.json @@ -0,0 +1,20 @@ +{ + "Pre training objectives": "https://www.youtube.com/watch?v=WZOqXkld9mk&list=PLp6ek2hDcoNDDRINFiWGDlPKUwW-g1Hjk&index=19", + "Pre trained models": "https://www.youtube.com/watch?v=NB_4dkfJf_w&list=PLp6ek2hDcoNDDRINFiWGDlPKUwW-g1Hjk&index=20", + "Tutorial: Introduction to huggingface": "https://www.youtube.com/watch?v=dHqggovEOwk&list=PLp6ek2hDcoNDDRINFiWGDlPKUwW-g1Hjk&index=21", + "Fine tuning LLM": "https://www.youtube.com/watch?v=eC6Hd1hFvos", + "Instruction tuning": "https://www.youtube.com/watch?v=TPDiqPnJDTs&list=PLp6ek2hDcoNDDRINFiWGDlPKUwW-g1Hjk&index=22", + "Prompt based learning": "https://www.youtube.com/watch?v=sXPggianwos&list=PLp6ek2hDcoNDDRINFiWGDlPKUwW-g1Hjk&index=23", + "Parameter efficient fine tuning": "https://www.youtube.com/watch?v=S0l-qUniC54&list=PLp6ek2hDcoNDDRINFiWGDlPKUwW-g1Hjk&index=30", + "Incontext Learning": "https://www.youtube.com/watch?v=eyNLkiQ89KI&t=378s", + "Prompting methods": "https://www.youtube.com/watch?v=6BXqKzOwObo&list=PLp6ek2hDcoNDDRINFiWGDlPKUwW-g1Hjk&index=24", + "Retrieval Methods": "https://www.youtube.com/watch?v=oJvFO2QwuvI", + "Retrieval Augmented Generation": "https://www.youtube.com/watch?v=mE7IDf2SmJg", + "Quantization": "https://www.youtube.com/watch?v=Kx5x3HYBDls&list=PLp6ek2hDcoNDDRINFiWGDlPKUwW-g1Hjk&index=31", + "Mixture of Experts Model": "https://www.youtube.com/watch?v=U8J32Z3qV8s", + "Agentic AI": "https://www.youtube.com/watch?v=kJLiOGle3Lw", + "Multimodal LLMs": "https://www.youtube.com/watch?v=cYfKQ6YG9Qo", + "Vision Language Models": "https://www.youtube.com/watch?v=rUQUv4u7jFs", + "Policy learning using DQN": "https://www.youtube.com/watch?v=0BsWItDlpxA&list=PLp6ek2hDcoNDDRINFiWGDlPKUwW-g1Hjk&index=26", + "RLHF": "https://www.youtube.com/watch?v=2MBJOuVq380" +} \ No newline at end of file diff --git a/backend/data/youtube_transcripts.json b/backend/data/youtube_transcripts.json new file mode 100644 index 0000000000000000000000000000000000000000..4e555ca4f9fc9d349c3c05579f5ebaca4ae3f2d4 --- /dev/null +++ b/backend/data/youtube_transcripts.json @@ -0,0 +1,20 @@ +{ + "Pre training objectives": "Hello everyone, welcome to this course! So, in this lecture, we are going to talk about pre-training strategies. Last lecture, we covered transformers and different blocks of a transformer model, right? We also talked about what to wear for glove models that are pre-trained. what embedding methods, right. We will see the overall arena of pre-training with transformers. And then we will also see how the paradigm of pre-training actually changed. From pre-trained word embeddings to pre-trained language models.\n\nLet us look at the pretraining strategy. So, while discussing this, let us start with this famous quote. which we already mentioned earlier in the distributional semantics chapter. In the word embedding chapter, it says that \"you shall know a word by the company it keeps,\" right? So, the same person modified the quote later on, and then he said. That the complete meaning of a word is always contextual. Okay, this is always contextual, and no study of meaning can occur apart from a complete context.\n\nCan be taken seriously, okay? And this is essentially the foundation behind building the pre-training strategies, okay. This is an example; I record the record right. If you look at these two, the two positions of the word \"record\" are right. The meanings are completely different. Now if you use a word-to-way kind of method or a glove-type method which basically produces pre-trained embeddings, right? You will get the same embedding for these two positions of the record, right? But the meanings are different.\n\nOkay. Let's take another example. A bat flew out of the cave. He hit the ball with the bat. Again, here are the two words bat, the two positions of the word bat. The two contexts of the word \"bat\" are different, right? If you use the same embedding to represent this, You will not be able to do the proper processing of it, right? So what will we do here? We will produce embeddings that are going to be contextualized. Meaning, depending on the context, the embedding will change. For example, here let us say that for the word \"bat,\" you will get one embedding.\n\nFor the word \"bat\" here, you get another embedding that is different, okay? How do you do this? The first approach that was proposed is not a transformer-based approach. What was proposed in 2018 is called ELMO. Deeply contextualized word representation. This was done mostly by the LNAI team and some folks from this university as well. One of them is a very good friend of mine. So, the idea behind the ELMO method is as follows. So ELMO is a non-transformer-based approach. There is no concept of a transformer.\n\nTransformers were introduced in 2017, around the same time it was introduced, in 2018. So ELMO stands for embeddings from language models. What does it do? It essentially relies on RNNs, which can be LSTM or GRU. And what it does is that it essentially processes a sequence, right? When you process a sequence using RNN, each of the hidden states Which correspond to basically the to", + "Pre trained models": "Hello, everyone. Welcome back. So we were discussing different pre-training strategies, and in the last lecture, we discussed. You know, pre-training and encoder-only models, specifically, we discussed the BERT model, right? So, in today's class, we will focus on two other pre-training strategies. One is encoder-decoder-only models, right? Models like T5 and BART. So B-A-R-T is another model, right? This is not B-E-R-T; this is B-A-R-T, BART. And at the same time, we will also discuss decoder-only models, right?\n\nwhich includes ChatGPT, all GPT series models, LLaMA models, and Decoder-only models are essentially very popular. So, the encoder-only model, as I discussed, was discussed in the last class. We saw that a new pre-training strategy called masked language model was introduced. This was used in BERT, where you essentially mask some of the tokens in the input. And you ask the model to predict those masked tokens, right? This is a self-supervised approach. You don't need any labeled data for training, right?\n\nAnd being an encoder model, it essentially means, The BERT model can essentially look at all the tokens present in the input. It is not an autoregressive model, is it? So the entire input is exposed to the model. And you basically perform self-attention. Through self-attention, you look at all the tokens present in the input. And based on that, you predict the mass of the tokens. On the other hand, the encoder-decoder model that we discussed here, As we discussed in today's class, we have this encoder component and the decoder component.\n\nAnd we will see how you can best use both parts of the encoder. And the decoder part during pre-training. And then we will also discuss the decoder-only model. Now, in the decoder-only model, as the name suggests, the decoder part, If you remember in the transformer class we discussed, it's an autoregressive model. autoregressive component in the sense that when you predict a word at the t-th location, You only have access to tokens until the t minus 1th location, right? You don't have access to tokens after the location, right?\n\nWe will see how you understand this autoregressive style of pre-training essentially. helps the decoder-only model learn things correctly This set of models is very popular these days, as people realize. Over the times that an encoder-only model is not the right solution for. For a generative task, right? Because a generative task requires an autoregressive setup, right? Whereas in an encoder-decoder model, right, as you require. Both the encoder part and the decoder part require a lot of memory.\n\nIt requires a lot of parameters, right? You can do all these generative tasks through the encoder-decoder model together, right? But the decoder model makes more sense both in terms of parameters. As well as the setup, right? The autoregressive model setup itself is suitable for, you know, next token generation. And, parameter-wise, you need half of the parameters. than the encoder d", + "Tutorial: Introduction to huggingface": "Hi, everyone. In this tutorial, we'll be discussing a kind of introduction to the HuggingFace library. So Hugging Face is a very useful library when we work with basically transformer-based models. Mostly the transformer-based model. All these models, open-source models, and data sets are available on something called the Hugging Face Hub. So we will see how we can use this Hugging Face library to load the models. To fine-tune them using some toy examples, how do I load the data sets? And how to use it for inference.\n\nSo let's get started. So here... That package in Hugging Face, which deals with transformer-based models, is called Transformers. And there's a package called Datasets that deals with all the open-source datasets. So we need to first install them. Let's first look at the whole pipeline: the whole flow of how we process an input. So, first, we have this raw text. This course is amazing. And then we have a tokenization algorithm that breaks the text into tokens. And map it to some numbers correctly.\n\nSo these numbers represent a token, right? So this code is amazing; suppose we apply some tokenization algorithm, say like byte pair encoding. Or something, or a sentence piece tokenization, something like that. And it maps it to a sequence of tokens, and tokens are represented as numbers, right? So these token numbers are basically a kind of dictionary mapping. Please provide the sentence that needs correction. So 101, say, this tells us this is a token, and its number is 101. Something like that.\n\nSo we then have a list of input IDs. And then these are passed on to the model. So when the model receives the input IDs, which are a list of token numbers, What it does is go to its embedding matrix and do a lookup. So the embedding dimension of the token is already stored. In the embedding matrix of the pre-trained model. If it is not pre-trained and we are looking to train it from scratch, Then initialization is either some random initialization or some informed initialization like Xavier initialization or something like that.\n\nAnd then, when you train the model, these embeddings are also updated. Embeddings of the tokens are also updated. So for now, let us assume that the model has been pre-trained. So once we pass the input IDs, the model maps the token IDs to their token embeddings. And then the token embeddings are passed on to the model's position encoding. is added in the case of transformers, regardless of the model architecture present Whether it's an encoder-decoder model or a decoder-only model. Whether it's an encoder-only model, it is processed accordingly.\n\nWe have already seen in our lecture, I guess, one or two weeks ago. That's how you implement transformers from scratch using PyTorch, right? There we saw how a transformer layer is implemented within the model. We saw every component of it: multi-reduction positional encoding and layer normalization. Encoder block, decoder block; we saw all that. So now we are putting all o", + "Fine tuning LLM": "hey everyone I'm Shaw and this is the fifth video in the larger series on how to use large language models in practice in the previous video we talked about prompt engineering which is concerned with using large language models out of the box while prompt engineering is a very powerful approach and can handle a lot of llm use cases in practice for some applications prompt engineering just doesn't cut it and for those cases we can go one step further and fine-tune a existing large language model for a\n\nspecific use case so Navy's question is what is model fine tuning the way I like to Define it is taking a pre-trained model and training at least one internal model parameter and here I mean the internal weights or biases inside the neural network what this typically looks like is taking a pre-trained existing model like gpt3 and fine-tuning it for a particular use case for example chatgypt to use an analogy here gpt3 is like a raw diamond right out of the earth it's a diamond but it's a bit rough around\n\nthe edges fine tuning is taking this raw diamond and transforming it into something a bit more practical something that you can put on a diamond ring for example so the process of taking the raw base model of gpt3 and transforming it into the fine-tuned model of gbt 3.5 turbo for example is what gives us applications like chat GPT or any of the other incredible applications of large language models we're seeing these days to get a more concrete sense of the difference between a base model link\n\ngpt3 and a fine-tuned model let's look at this particular example we have to keep in mind that these Foundation large language models like gpg3 llama 2 or whatever your favorite large language model is these models are strictly trained to do word prediction given a sequence of words predicting the next word so when you train one of these launch language models on huge Corpus of text and documents and web pages what it essentially becomes is a document completer what that translates to in practice is if you plug into a lot of\n\nthese base models like gpt3 the prompt tell me how to find tune a model a typical completion might look something like this where it's just listing out questions like you might see in a Google search or maybe like a homework assignment or something here when I prompted gpt3 to tell me how to fine-tune a model the completion was as follows how can I control the complexity of a model how do I know when my model is done how do I test a model well this might be reasonable for gpt3 to do based on the data that it was trained on this\n\nisn't something that's very practical now let's look at the fine-tuned model completion so now we have text DaVinci zero zero three which is just one of the many fine-tuned models based on gpt3 coming from open AI we give it the same prompt tell me how to fine tune a model and this is the completion fine-tuning a model involves a adjusting the parameters of a pre-trained model in order to make it better suited f", + "Instruction tuning": "Hello everyone, today we will discuss instruction fine-tuning or instruction tuning. which is one of the key advancements in recent language modeling research. which enables us to have a conversation with language models easily Or, simply put, we can chat with language models. So, first, a quick review. So in previous weeks, we have learned about decoder-based language models. So such models are trained on vast amounts of text from the internet. Using the next word prediction task. As a result of this, these models learn to encode a great deal of information.\n\nAbout the world. They also have the ability to understand language to some extent. So these models are very powerful. They're pretty amazing, but we'll see in the upcoming slides. That they have some major limitations. One note: these pre-trained language models are also known as base models. So I'll be using the term \"base models\" throughout the lecture. So whenever I mention base models, it simply refers to pre-trained language models. For example, let's say we have been given the following prompt.\n\nWhat is the national flower of India? We have prompted the language model with this question. Now, what can happen is that the language model can generate the following response: What is the national animal of India? What is the national bird of India? So this response is nothing but the continuation of the prompt. And this is the result of the next word prediction that is happening. after the prompt. So here we see that the response contains questions that are quite common. which we can come across such questions on the web, where we see\n\nThere is a web page on general knowledge questions about India. Such questions are very common. However, this is not the desired response. Because when we asked this question of the language model, we were expecting an answer. That is, the national flower of India is the lotus. This was the desired outcome. However, since the language model is just predicting the next word, So the response to a question could be that it may. or may not follow the question. May or may not. Might follow the instructions, might not follow the instructions.\n\nBecause, as I said, it's simply just doing next-word prediction at this point. So the key takeaway from this slide is that next-word prediction, which is what is governing this response generation, That does not necessarily ensure that the model understands or follows instructions. So the reason we need instruction tuning is that. We want to teach the language models how to follow and understand instructions. So multitask learning is another very important paradigm in the natural language processing literature.\n\nSo in classical multitask learning, what we do is combine multiple tasks. We train the model, the language model, on multiple tasks with the intention. that these models will have a positive influence on one another And thereby, the final outcome will be improved across all the tasks. So here, if we take a look at th", + "Prompt based learning": "Welcome back. So in today's lecture, we are going to talk about prompts, right? And you know when it comes to chat GPT kind of models, Large language models, the first question that comes to our mind is how to write a prompt, right. What is going to be the optimal prompt for a specific model, right? So in this lecture, we will discuss different types of prompting techniques, right? how prompts affect the accuracy of the models, right? And how, with the scaling and the increasing size of the models,\n\nhow it basically affects the accuracy and how a specific prompt will be responsible for it Producing accuracy across different types of models. We'll also discuss prompt sensitivity, right? We'll see that most of these models are highly sensitive to simple perturbations of prompts. Right? And that would affect the accuracy; that would affect other aspects of the model. And how can we quantify them, right? So far, we have discussed different pretraining strategies. We have seen encoder-only models, haven't we?\n\nWe have seen models like BERT, which is a pure encoder-only model, right? And these models are pre-trained with an objective called masked language modeling, right, MLM. We have seen models like GPT, which is a purely decoder-only model. And these models are trained using an autoregressive setup, right, or causal language modeling. This is called causal language modeling or an autoregressive setup. There are other models like T5 and BART, and they are encoder-decoder models. Okay, which are also trained with a kind of autoregressive setup.\n\nWhere your input will be fed to the encoder. and decoder will predict the next word Our decoder will predict if your input is part of, and if your input has noise. the decoder model will be able to essentially denoise your input, right? And you know the board model came out around 2018. The first GPT paper was written in 2018, GPT-1, and then GPT-2 came in 2019. GPT-2 was released in 2019 and then GPT-3 in 2020. Right, and the GPT-3 paper showed that the model doesn't need any fine-tuning. We just need to write prompts, and the model will be able to.\n\nunderstand your prompt, okay. We will discuss all these topics in this lecture. Okay, so I strongly suggest that you guys read this wonderful survey paper. This is a survey paper written by Graham Neubig and his team from CMU. And this is, you know, this rightly and nicely reflects the, you know, Different prompting strategies and the kind of evolution of the overall. You know, prompt, you know, prompt as a kind of aspect overall, how it evolves, right? How do we quantify different components of a prompt, and so on and so forth?\n\nIt's a very nice survey paper that I strongly recommend you read. Okay, so you know there have been, kind of, we have seen. We have witnessed that there has been a kind of war, right? Among all these giant industries, such as Meta, OpenAI, and Google, They have been building larger and larger models with more and more parameters. and so", + "Parameter efficient fine tuning": "# Summary: Parameter-Efficient Fine-Tuning (PEFT) for Large Language Models\n\nThis lecture by Dinesh Raghu from IBM Research covers efficient methods for fine-tuning Large Language Models (LLMs) without updating all parameters.\n\n## Key Concepts\n\n**Why PEFT is Needed:**\n- Full fine-tuning requires 12-20ร— model size in memory for optimizer states, gradients, and activations\n- Storage overhead: each task requires saving a full model checkpoint (e.g., 350GB)\n- In-context learning (prompting) has limitations: lower accuracy than fine-tuning, sensitivity to prompt wording, and high inference costs\n\n**Main PEFT Techniques:**\n\n1. **Prompt Tuning (Soft Prompting)**\n - Reserves special trainable tokens in the input while freezing all model weights\n - Extremely parameter-efficient (~0.1% of model parameters)\n - Enables multi-task serving: different soft prompts can be swapped for different tasks on the same base model\n - Performance approaches full fine-tuning for large models (11B+ parameters)\n\n2. **Prefix Tuning**\n - Adds trainable parameters at every transformer layer, not just the input\n - Uses a bottleneck MLP architecture to prevent training instability\n - Achieves comparable performance to full fine-tuning with only 0.1% trainable parameters\n\n3. **Adapters**\n - Inserts new trainable layers (bottleneck architecture) within each transformer block\n - Down-projects hidden dimensions, applies nonlinearity, then up-projects\n - Achieves good performance with ~3.6% of parameters\n - Drawback: inference latency overhead due to added layers\n\n4. **LoRA (Low-Rank Adaptation)**\n - Most popular PEFT method based on intrinsic dimensionality theory\n - Decomposes weight updates into low-rank matrices: ฮ”W = BA\n - Only modifies query, key, value, and output projection matrices\n - Advantages: no inference latency, can be merged back into base weights\n - Variants: QLoRA (memory-efficient), DyLoRA (dynamic rank selection), LoRA+\n\n**Key Benefits of PEFT:**\n- Reduced memory and compute requirements (can use older GPUs)\n- Faster convergence due to smaller parameter space\n- Less overfitting and catastrophic forgetting\n- Better out-of-domain generalization\n- Minimal storage per task\n\nThe lecture emphasizes that PEFT bridges the gap between inefficient in-context learning and computationally prohibitive full fine-tuning, making LLM adaptation accessible and practical.\n\n[1](https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/attachments/52028987/a8512587-2c43-4219-84a6-bc60a1065297/paste.txt)", + "Incontext Learning": "welcome everyone this is the first screencast in our series on in context learning this series is a kind of companion to the one that we did on information retrieval the two series come together to help you with homework 2 and bake off two which is focused on few shot open domain question answering with frozen retrievers and Frozen large language models to start this series I thought we would just reflect a bit on the origins of the idea of in context learning which is really a story of how NLP got to this\n\nstrange and exciting and chaotic moment for the field and maybe also for the society more broadly all credit to the Chomsky bot for bringing us to this moment I'm only joking the the Chomsky bot is a very simple pattern-based language model it's been around since the 90s I believe and with very simple mechanisms it produces Pros that is roughly in the style of the political philosopher and sometimes linguist Noam Chomsky it produces prose that Delights and maybe informs us and the underlying mechanisms are very\n\nsimple and I think that's a nice reminder about what all of these large language models might be doing even in the present day but I'm only joking although it's only partly a joke I think when we think about precedence for in context learning it is worth mentioning that in the pre-deep learning era engram based language models very sparse large language models were often truly massive for example brands at all 2007 use a 300 billion parameter language model trained on 2 trillion tokens of text to help\n\nwith machine translation that is a very large and very powerful mechanism with a different character from the large language models of today but it is nonetheless worth noting that they played an important role in a lot of different fields way back when I think for in context learning as we know it now the earliest paper as far as I know is the DECA NLP paper this is McCann adult 2018. they do multitask training with task instructions that are natural language questions and that does seem that like the origin of the idea\n\nthat with freeform natural language instructions we could essentially end up with artifacts that could do multiple things guided solely by text and then it's worth noting also that in the GPT paper Radford at all 2018 you can find buried in there some tentative proposals to do prompt-based uh experiments with that model but the real origins of the ideas again as far as I know are Radford at all 2019 this is the gpt2 paper and let me just show you some Snippets from this paper it's really inspiring\n\nhow much they did they say at the start we demonstrate language models can perform Downstream tasks in a zero shot setting without any parameter or architecture modification so there you see this idea of using Frozen models prompting them and seeing if they will produce interesting behaviors they looked at a bunch of different tasks for summarization they say to induce summarization Behavior we add the text tldr after the art", + "Prompting methods": "welcome everyone this is the first screencast in our series on in context learning this series is a kind of companion to the one that we did on information retrieval the two series come together to help you with homework 2 and bake off two which is focused on few shot open domain question answering with frozen retrievers and Frozen large language models to start this series I thought we would just reflect a bit on the origins of the idea of in context learning which is really a story of how NLP got to this\n\nstrange and exciting and chaotic moment for the field and maybe also for the society more broadly all credit to the Chomsky bot for bringing us to this moment I'm only joking the the Chomsky bot is a very simple pattern-based language model it's been around since the 90s I believe and with very simple mechanisms it produces Pros that is roughly in the style of the political philosopher and sometimes linguist Noam Chomsky it produces prose that Delights and maybe informs us and the underlying mechanisms are very\n\nsimple and I think that's a nice reminder about what all of these large language models might be doing even in the present day but I'm only joking although it's only partly a joke I think when we think about precedence for in context learning it is worth mentioning that in the pre-deep learning era engram based language models very sparse large language models were often truly massive for example brands at all 2007 use a 300 billion parameter language model trained on 2 trillion tokens of text to help\n\nwith machine translation that is a very large and very powerful mechanism with a different character from the large language models of today but it is nonetheless worth noting that they played an important role in a lot of different fields way back when I think for in context learning as we know it now the earliest paper as far as I know is the DECA NLP paper this is McCann adult 2018. they do multitask training with task instructions that are natural language questions and that does seem that like the origin of the idea\n\nthat with freeform natural language instructions we could essentially end up with artifacts that could do multiple things guided solely by text and then it's worth noting also that in the GPT paper Radford at all 2018 you can find buried in there some tentative proposals to do prompt-based uh experiments with that model but the real origins of the ideas again as far as I know are Radford at all 2019 this is the gpt2 paper and let me just show you some Snippets from this paper it's really inspiring\n\nhow much they did they say at the start we demonstrate language models can perform Downstream tasks in a zero shot setting without any parameter or architecture modification so there you see this idea of using Frozen models prompting them and seeing if they will produce interesting behaviors they looked at a bunch of different tasks for summarization they say to induce summarization Behavior we add the text tldr after the art", + "Retrieval Methods": "All right, welcome everyone to the Comet YouTube channel. We are doing a series of guest speakers where we dive into some very fun technical topics related to building and scaling Genai systems. And today I have with us Lena. She is a absolute expert in building rags. I've had some good conversations with her as we prepared for this session about her approaches. And I'm really excited to just go through really some of the best practices around like optimizing retrieval for LMS. We're going to deep\n\ndive into some rag techniques, some advanced stuff here. Um, before we dive in, I want to pass it over to Lena to introduce herself. Thank you Claire for the introduction and uh, hi everyone who is watching the recording. I'm so happy to be here. Uh, my name is Lennena. I'm a founder of pars labs and chatbot and I've been working in this space of chatbot development conversational AI agents since um I think eight years now. Uh my career started in linguistics. I studied theoretical linguistics then AI. I\n\nworked as an NLP research engineer and full stack developer and uh yeah now running a small agency with a team of five now and doing also a lot of um public speaking and sharing my knowledge online with people um running courses on AI automation and things like that. Uh so hopefully I will have something interesting to share uh with you today. >> All right well let's dig into some deep technical topics. Um, I wanted to dive into the advanced techniques for improving rags. I think there's a few\n\nideas that you had here. Um, and if you want to share screen, uh, feel free. We can, uh, pull up some slides here. But let's dive into some of these more advanced techniques in regards to just improving your RAG systems. >> Uh, yes. Um, what should we start with? >> Let's see. I'm thinking about optimizing the retrieval quality. Um, >> okay. Let me share my screen and then we'll just see where it leads us. So I prepared a lot of different diagrams uh with different techniques\n\n>> um in a random order. So you are welcome. >> Thank you. I love this. This is a gold mine of information. >> Uh yeah. So let's start with um maybe uh this um I classified it as improving a retrieval quality trick. So uh in the past we used intent detection when we were building chatbots. So instead of generating anything for anything we had to make data sets for okay this type of question is about salary this type of question is I want to talk to customer support. We had all\n\nthose categories and um I don't see that often being used yet with newer teams who just joined this whole chatbot development. So I want to bring it back. Uh so instead of just generating the response using LLM, you get the user question. So what's the salary? Then you predict an intent and you get an an answer that your SMMES have written. So the answer that's in your database and it says the salary for this position is salary. And then you paste this information uh to the prompt and then\n\nyou generate an answer o", + "Retrieval Augmented Generation": "### Lecture Summary: Retrieval-Augmented Generation (RAG) and Contextualization of Language Models\n\nWelcome everyone. Today weโ€™ll discuss **retrieval augmentation** โ€” one of the most active areas in modern NLP. Our speaker, Adella, is CEO of Contextual AI, an enterprise LLM company, an adjunct professor at Stanford, and former head of research at Hugging Face and Facebook AI. His research focuses on machine learning and NLP, especially on language understanding, generation, and evaluation.\n\n---\n\n### 1. The Age of Language Models\n\nWe live in the era of large language models (LLMs). However, **language models** are not a recent inventionโ€”neural language modeling dates back to the 1990s. The idea is simple: given a sequence of tokens, predict the next token. Early versions (e.g., Bengio et al., 2003) already had embeddings and similar formulations to todayโ€™s models. What changed is scale.\n\nThe main breakthrough of **ChatGPT** wasnโ€™t the model architectureโ€”it was the **user interface**. Previously, using a language model required strange prompt engineering. ChatGPT solved this through *instruction tuning* and *reinforcement learning from human feedback (RLHF)*, allowing people to simply โ€œaskโ€ the model naturally.\n\n---\n\n### 2. Problems with Pure Language Models\n\nEven with good interfaces, LLMs have serious issues:\n\n* **Hallucination:** Models generate incorrect facts with high confidence.\n* **Attribution:** We donโ€™t know *why* a model produced an answer.\n* **Staleness:** Models become outdated quickly.\n* **Editing:** We canโ€™t easily revise or delete knowledge (e.g., for GDPR compliance).\n* **Customization:** Hard to adapt models to specific domains or private data.\n\nThese limitations make LLMs unreliable for enterprise or high-accuracy applications.\n\n---\n\n### 3. Enter Retrieval-Augmented Generation (RAG)\n\nThe solution many have turned to is **RAG** โ€” connecting a generator (LLM) with an **external retriever** or **memory**. Instead of relying solely on parameters, the model can look up information dynamically.\n\nThink of **closed-book vs open-book exams**:\n\n* Closed-book โ†’ memorize everything (parametric LMs).\n* Open-book โ†’ look up relevant facts when needed (RAG).\n\nThis architecture has two main parts:\n\n1. **Retriever:** Fetches relevant documents or passages from an external database.\n2. **Generator:** Takes the query and retrieved context to produce an answer.\n\nThis approach gives **updatable, grounded, and customizable** models that hallucinate less and can cite their sources.\n\n---\n\n### 4. Retrieval Foundations\n\nEarly retrieval methods used **sparse retrieval** (e.g., TF-IDF, BM25).\nBM25 scores documents by term frequency and inverse document frequency, emphasizing distinctive words. Itโ€™s fast and efficient on CPUs but fails with synonyms or paraphrases.\n\n**Dense retrieval** (e.g., DPR, ORQA) replaces sparse counts with **embedding-based similarity** using models like BERT. Each document and query is encoded into a dense vector; retrieval is done v", + "Quantization": "Alright, hello everyone. So, today we will be talking about quantization, pruning, and distillation. So, these all fall under the same umbrella, and maybe once I go into the introduction, It will be clear why we are discussing these three topics together. So, like we discussed last time, the reason why we wanted to do theft was Because of how the model sizes have been increasing over time. And this is, again, a recap of that. Over time, the size of the model is increasing exponentially. It is not just the size but also the performance of these models.\n\nare also increasing over time. So, here we see that these are the test laws for various language modeling tasks. on different datasets and you see that they are decreasing considerably Based on the number of parameters that are in the large language model. So now, what is the flip side of having such large models during inference? So, the first thing, as we discussed last time, is that the bigger the model is, You will have to buy new hardware to support them. In a sort of way, it is not very friendly to keep buying hardware.\n\nAnd when the model keeps growing over time. So, this also puts a cap on how many organizations can actually run these LLM inferences. So, the GPU requirement is going to get larger and One of the biggest problems in deployment is latency. So, the larger the model is, the more time it will take to come back. with a completion for a given prompt. Now, let's say that you have a chatbot that has been deployed. whose backbone is an LLM, then having to wait for 30 or 40 seconds, It seems really difficult at this point in time when we are so used to.\n\nGetting replies very quickly. And the way the LLM field is progressing now, People are not just using a single LLM call per sort of response. For example, there is now a new paradigm called agentic behavior. where once a language model responded, You ask the same language model or a different language model to reflect on the output. or even sometimes execute the output, for example, if the model generates a code, you execute the code, Get the outputs and then make the model reflect on the output.\n\nAnd then decide whether there is an execution error; should it redo things. And finally, you give back the answer. So now, if you have to make multiple LLM inferences, to just get back with one output. Then that's going to increase latency even more. So, latency is one of the biggest concerns. Third is the inference cost. If you have an application deployed that uses LLMs, Of course, you'll be worried about how much money. You're going to spend to serve a single user. And the money that a single user is going to pay you.\n\nshould be more than what you invest for that user. So, if LLM is for slightly more improvement in accuracy, If you have to spend a lot more, then it does not seem commercially viable. So, of course, the inference cost is going to be one of the biggest dimensions. And finally, sustainability and environmental concerns. So, yo", + "Mixture of Experts Model": "## **Scaling Transformers Through Sparsity**\n\n### **1. Motivation**\n\nThe driving idea behind this research is **scaling**. In the deep learning community, performance has been shown to improve predictably with model size and compute, as outlined in *OpenAIโ€™s 2020 paper โ€œScaling Laws for Neural Language Models.โ€*\nThese scaling laws hold across several orders of magnitude, demonstrating that **larger models are more sample-efficient**: for a fixed compute budget, it is better to train a **larger model for fewer steps** than a smaller one for longer.\n\nSo far, most scaling has relied on **dense models**, where every parameter participates in every forward pass. But this is expensive and inefficient.\nTodayโ€™s discussion explores a new axis of scaling: **sparsity** โ€” models where different inputs activate **different subsets of weights**, performing **adaptive computation** depending on the input.\n\n---\n\n### **2. What Is Sparsity?**\n\nSparsity here doesnโ€™t mean pruning or zeroing weights, but **conditional computation**:\neach input token is processed by a subset of the network โ€” โ€œexpertsโ€ โ€” chosen dynamically.\n\nThis idea dates back to 1991โ€™s *Adaptive Mixtures of Local Experts*, revived in modern NLP by Noam Shazeer and colleagues at Google with **Mixture of Experts (MoE)** for LSTMs.\nThe architecture has:\n\n* Several **experts**, each a small feed-forward network.\n* A **router (gating network)** that predicts which experts to send each token to, using a softmax distribution.\n* The output is a weighted mixture of the selected experts.\n\nMoE proved successful in machine translation but had issues like communication cost and training instability.\nThe **Switch Transformer** simplified this by sending each token to **only its top-1 expert**, reducing both cost and complexity.\n\n---\n\n### **3. The Switch Transformer**\n\nA Switch Transformer modifies the Transformer block by replacing some feed-forward layers with **Switch Layers**:\n\n* A **router** decides which expert each token goes to.\n* Each token goes to one expert (top-1 routing).\n* The same amount of computation is done overall, but different tokens use different weight matrices.\n\nThis makes computation adaptive while keeping the FLOPs (floating-point operations) roughly constant.\n\n---\n\n### **4. Key Improvements for Sparse Training**\n\nSparse models can be unstable to train. The team made several innovations to stabilize and optimize performance:\n\n#### (a) **Selective Precision**\n\n* Training in low precision (bfloat16) improves speed but can cause divergence due to numerical instability, especially in routers (softmax/exponentiation).\n* Casting router computations to **float32** (while keeping others in bfloat16) solved this without meaningful speed loss.\n\n#### (b) **Initialization Scaling**\n\n* Default initializations made training unstable.\n* Simply reducing the initialization scale significantly improved convergence and performance.\n\n#### (c) **Expert Dropout Regularization**\n\n* Sparse models with many param", + "Agentic AI": "my name is inso so uh today uh we like to uh go over agent AI aent language model as a progression of language model usage so here is the outline of the today's talk uh we'll go over uh the overview of language model and how we use and then and the common limitations and then um some of the method that improves towards this common limitation and then we'll transition it to uh what is the Agent B language model and its design patterns so uh language model is a machine learning model that predicts the\n\nnext coming word given the input text as in this example if the input is the the students open there then um language model uh can predict what's the most likely word coming uh next as a next word so if the language model is trained with the large corpers it is a predict it is generating the probability of next coming word in this example uh as you could see books and laptops have a high higher probability than other other words in the vocabulary so um the so the completion of this whole sentence\n\ncould be uh the students open their books and then if you want to keep generating um the what's coming next then we can uh uh turn them in as an input and then uh um put it into the language model and then language model continuously generating the next coming word then how uh these uh language models are trained largely uh two two parts pre-training part and then posttraining part and then um first pre-training portion is the one that language models are trained with lot with large copers uh text are collected\n\nfrom internet or books or different type of text publicly available text and then trained with the next token or next word prediction objectives so once the models uh is finished in this pre-training stage models are fairly good at predicting um any words coming uh next as a word um given the inputs um however um this type uh the pre-train model uh itself is um is not easy to use so the hence the posttraining uh steps are coming and then these uh post trining stage um uh would includes uh instruction following training as well\n\nas reinforcement learning with human feedback and what this uh training stage uh means is uh um we could prepare a data set such a way that the specific instruction or question and then the answers or the uh generated output that is what the uh user would expect or uh more uh more uh um uh related to the questions and answers so that's how uh the models are trained so that it's easier to use and then also it'll respond to a specific uh and then uh once this done and then uh additional training uh method uh is uh\n\num uh aligning to Humane preference by using uh reinforcement learning with human feedback which uhu U is using human uh preference to align the model by using uh rewards schemes and um let's take a quick look really quick look on the instruction data set this is the template that uh we uh uh we would use to train the model in instruction following training phase as you can see uh there's a specific uh instructions w", + "Multimodal LLMs": "um Hello thank you all for joining CS uh 25 Transformers today uh for today's talk we have Ming ding a research scientist at jeu AI based in Beijing he obtained his bachelor's and doctoral degrees at tingua University and he does research on multimodal generative models and pre-training Technologies um he has LED or participated in the research work Works about multimodal generative models such as Cog View and Cog video and multimodal understanding models such as Cog uh VM and Cog agent uh for today's\n\nattendance the attendance form is up on the course website and if you have any questions ask them through slido s l d o and for the code you just have to input cs25 um thank you Ming for today's talk and I'm going to pass it off to you thank you for the instructors of cs25 to is very happy to gave a talk in Stanford University about multimodality in training and uh uh actually I have checked the uh all the previous talks in cs25 and uh they are really diverse topics someone share the intuition in\n\ntheir research about PR training someone shared recent Works about maybe M Moe and some other technical uh actually I'm working in a uh large language model company in China and our company working on print training and uh maybe there's lots of different area uh from a large langage model and multi modality model and generative model diffusion and uh tatto Speech something like that so uh I Le all the multimodality model research in J AI so I will uh share lots of different topics in in in this talk um some some of them\n\nmay be not very familiar to you so uh yeah it's okay but you can uh get more information of the the area uh yeah I will talk about several aspects of Transformers and I will generally follow the history of large language model and say why are we here it's about large language model introduction and history uh and how did we get here it's about the some practical techniques for training large lar langage models and what are we working on it's about the last one year uh the real language\n\nmodels and other techniques in the uh papers of all the uh V language model community and finally I will talk about the some possible and valuable direction for research in multimodality okay okay well uh I will share three moments uh I think the most important three moments in the development of language model uh the first moment is called Bo moment actually I get I got into the area at this moment it's very uh honored that I'm the first among the first group of people who publish paper on the next year ACL when\n\nB came out and at that time since is is we don't really know what is language modeling so at that time nearly all the people talking about uh how can we get a better self-supervised method for an option uh at that time a common opinion is mask language model is just for is is good at understanding the the TX and GPT the auto regressive model is better for tax generations and T5 maybe can U can do the B but is redundant and that's true but uh uh n", + "Vision Language Models": "## **Visionโ€“Language Models (VLMs)**\n\n### **1. Introduction and Motivation**\n\nVisionโ€“language models (VLMs) are systems that **learn jointly from images and text**, enabling understanding and reasoning across both modalities. These models can describe images, answer visual questions, classify objects in an open vocabulary, and perform grounding or retrieval tasks โ€” all using a shared understanding between vision and language.\n\nThe talk is divided into three parts:\n\n1. **Foundations and Early Models** โ€“ how it started (around 2021)\n2. **Cross-Modal Models** โ€“ modern multimodal transformers\n3. **Applications and Outlook** โ€“ where VLMs are being used and whatโ€™s next\n\nThe goal is to trace how we moved from basic dual-encoder models like **CLIP** to modern multimodal systems such as **Gemini**, and how this shift is transforming research and real-world applications.\n\n---\n\n## **2. What Is a Visionโ€“Language Model?**\n\nA VLM jointly processes both **images and text**.\nInput: image(s) + text(s)\nOutput: typically text (caption, answer, label, etc.)\n\nWhile some models can also *generate* images, this talk focuses on those that produce **text outputs**.\n\nTo design a VLM, we must decide:\n\n* How to **encode** images and text (shared vs separate architectures)\n* When and how to **fuse** the modalities\n* What **losses** to use (contrastive, captioning, etc.)\n* Whether to train from **scratch** or **fine-tune** pretrained models\n* What kind of **data** to use: paired (imageโ€“text), interleaved, or unpaired\n\n---\n\n## **3. Dual-Encoder Models: The Beginning**\n\n### **3.1 The Idea**\n\nThe simplest form of VLMs are **dual encoders**:\n\n* An **image encoder** and a **text encoder**, each processing its own modality\n* The two encoders only interact **at the loss level** โ€” their final embeddings are compared to learn alignment.\n\nThis structure laid the foundation for large-scale models like **CLIP (OpenAI)** and **ALIGN (Google)**, both published in early 2021.\n\n### **3.2 CLIP: Connecting Images and Text**\n\n**CLIP (Contrastive Languageโ€“Image Pretraining)** became the turning point for multimodal learning.\n\n**Training setup:**\n\n* 400 million **imageโ€“text pairs** scraped from the web.\n* Train two encoders (ViT for images, Transformer for text) from scratch.\n* Use a **contrastive loss** to bring matching imageโ€“text pairs closer and push others apart.\n\nThis simple recipe led to highly transferable representations and *open-vocabulary* capabilities โ€” allowing classification without retraining on new classes.\n\n---\n\n## **4. Contrastive Learning in CLIP**\n\n### **4.1 Principle**\n\nContrastive learning teaches the model to:\n\n* **Maximize similarity** between two *positive* samples (e.g., an image and its true caption)\n* **Minimize similarity** with *negative* samples (other imageโ€“text pairs in the batch)\n\nIn formula form, the **InfoNCE loss** compares one positive pair to all others using a softmax over cosine similarities.\n\n### **4.2 Implementation Details**\n\n* **Normalize** embeddings", + "Policy learning using DQN": "Hello everyone, welcome back to the course on LLMs. So we were discussing the alignments of large language models. And in the last class, we discussed how you know. Human-generated feedback can be injected into this model for further refining. So, specifically, we discussed that we have a policy model. The policy model is the LLM that you want to refine. that you want to fine-tune. And then this policy model will generate certain output. This output will be scored by a reward model. This reward model is another LLM.\n\nSo, it will produce a reward and based on this reward, we will further refine this policy model. We also discussed that only reward maximization is not enough. Because what happens if we only maximize the reward? What would end up happening is that the policy model would start hacking the reward model. meaning that it will start producing such responses for which The reward model will produce higher reward values. But the responses may not be realistic. For example, the policy model will start producing a lot of emoticons.\n\nIt will start producing sentences that are verbose, lengthy, and so on. Not to the point, and so on and so forth, which you do not want. Therefore, to address this reward hacking loophole, what you do? you have another component in the objective function. Which do you want to minimize, and what is this component? This component is basically the divergence scale between the old policy. And the updated policy, the old LLM, and the updated LLM. So, you do not want the updated LLM to be too far. from the starting LLM.\n\nSo, we discussed maximizing the reward, which is the expected reward. that you obtain given a policy. So, the policy model is ฮธ. So, this is parameterized by ฮธ. You essentially sample, given a prompt x. this policy model will generate a y and for this y, You have this reward. So, for example, x is the prompt that it is. Where is the Taj Mahal located, and let us say that y is the response? Let us say the response is, \"The Taj Mahal is located in Uttar Pradesh.\" The Taj Mahal is located in France and so on and so forth.\n\nand based on that you give high reward or low reward. And this second term, this term, is essentially the KL divergence between The updated policy model and the reference policy model. So, pi theta and pi ref. So, the reference LLM policy model is the one. from which you started your reward maximization process. These two components will be combined. So, you want to maximize rewards and minimize scale divergence. And there is this beta scaling factor. Now, this lambda is essentially responsible for scaling these two components.\n\nIf you want to give more weight to scale divergence, You increase the value of lambda, and so on and so forth. So this is what we discussed. So now the question is or given this regularized reward maximization Why is this a regularized reward? Because this component... You can think of this as a regularizer. This is your objective, and this is your regularizer. ", + "RLHF": "Incredibly powerful and a ton of people were just able to download this and use this on their own and that was transformative on how people viewed machine learning as a technology that interfaced with people's lives and we at hug and face kind of see this as a theme that's going to continue to accelerate as time goes on and there's kind of a lot of questions on Where is this going and kind of how do these tools actually work and one of the big things that has come up in recent years is that these machine learning models can fall short which is they're not perfect and they have some really interesting failure modes so on the left you could see a snippet from chat GPT which uh if you've used chat gbt there's these filters that are built in and essentially if you ask it to say like how do I make a bomb it's going to say I can't do this because I'm a robot I don't know how to do this and this seems harmful but what people have done is that they have figured out how to jailbreak this this agent in a way which is you kind of tell it I have a certain I'm a playwriter how do I do this and you're a character in my play what happens and there's all sorts of huge issues around this where we're trying to make sure these models are safe but there's a long history of failure and challenges with interfacing in society and a like fair and safe Manner and on the right are two a little bit older examples where there's Tay which is a chatbot from Microsoft that was trying to learn in the real world and by interacting with humans and being trained on a large variety of data without any grounding and what values are it quickly became hateful and was turned off and then a large history of it field studying bias in machine learning algorithms and data sets where the by the data and the algorithm often reflect biases of their designers and where the data was created from so it's kind of a question of like how do we actually use machine learning models where we have the goals of mitigating these issues and something that we're going to come and talk to in this talk is is reinforcement learning a lot so I'm just going to kind of get the lingo out of the way for some people that might not be familiar with deeprl essentially reinforcement learning is a mathematical framework when you hear RL you should think about this is kind of like a set of math problems that we're looking at that are constrained and in this framework we can study a lot of different interactions in the world so some terminology that we'll revisit again and again is that there's an agent interacting with an environment and the agent interacts with the environment by taking an action and then the environment returns two things called the state and the reward the reward is the objective that we want to optimize and the state is just kind of a representation of the world at that current time index and the agent uses something called a policy to map from that state to an action and the beauty of this is that " +} \ No newline at end of file diff --git a/backend/database.py b/backend/database.py new file mode 100644 index 0000000000000000000000000000000000000000..7762085cd9804801e32e04638b49099888a88358 --- /dev/null +++ b/backend/database.py @@ -0,0 +1,150 @@ +import json +import os +from datetime import datetime + +# HF Native Persistence: Check if /data volume is mounted +IF_HF_PERSISTENT = os.path.exists('/data') +if IF_HF_PERSISTENT: + DB_FILE = '/data/db.json' + print(f"๐Ÿ“ก HF Native Persistence: Active. Storing data in {DB_FILE}") +else: + DB_FILE = os.path.join(os.path.dirname(__file__), 'data', 'db.json') + print(f"๐Ÿ’ป Local Persistence: Active. Storing data in {DB_FILE}") + +# Ensure data directory exists +os.makedirs(os.path.dirname(DB_FILE), exist_ok=True) + +def init_db(): + if not os.path.exists(DB_FILE): + data = { + "users": [], + "learning_sessions": {}, + "polylines": {}, + "summaries": [], + "bookmarks": {}, # session_id -> list of resource_ids + "notes": {}, # session_id -> list of note objects + "lectures": [] # list of lecture objects + } + save_db(data) + +def reset_db(): + if os.path.exists(DB_FILE): + os.remove(DB_FILE) + init_db() + +def load_db(): + if not os.path.exists(DB_FILE): + init_db() + + try: + with open(DB_FILE, 'r') as f: + content = f.read().strip() + if not content: + init_db() + with open(DB_FILE, 'r') as f2: + db = json.load(f2) + else: + db = json.loads(content) + + if "bookmarks" not in db: + db["bookmarks"] = {} + save_db(db) + return db + except (json.JSONDecodeError, FileNotFoundError): + init_db() + with open(DB_FILE, 'r') as f: + return json.load(f) + +def save_db(data): + # Ensure directory exists (in case /data was just mounted) + os.makedirs(os.path.dirname(DB_FILE), exist_ok=True) + with open(DB_FILE, 'w') as f: + json.dump(data, f, indent=4) + +def get_session(session_id): + db = load_db() + if session_id not in db["learning_sessions"]: + db["learning_sessions"][session_id] = { + 'position': {'x': 10, 'y': 10}, + 'level': 0, + 'totalReward': 0, + 'visitedResources': [], + 'notifications': [ + { + 'id': 'initial', + 'type': 'info', + 'message': 'Welcome back to the Intelligence Hub. Neural Sync complete.', + 'timestamp': int(datetime.now().timestamp() * 1000), + 'read': False + } + ] + } + save_db(db) + return db["learning_sessions"][session_id] + +def update_session(session_id, session_data): + db = load_db() + db["learning_sessions"][session_id] = session_data + save_db(db) + +def save_summary(summary_data): + db = load_db() + if "summaries" not in db: + db["summaries"] = [] + db["summaries"].append(summary_data) + save_db(db) + +def save_polyline(polyline_id, polyline_data): + db = load_db() + db["polylines"][polyline_id] = polyline_data + save_db(db) + +def get_polylines(): + db = load_db() + return db["polylines"] + +def get_bookmarks(session_id): + db = load_db() + return db["bookmarks"].get(session_id, []) + +def add_bookmark(session_id, resource_id): + db = load_db() + if session_id not in db["bookmarks"]: + db["bookmarks"][session_id] = [] + if resource_id not in db["bookmarks"][session_id]: + db["bookmarks"][session_id].append(resource_id) + save_db(db) + +def remove_bookmark(session_id, resource_id): + db = load_db() + if session_id in db["bookmarks"] and resource_id in db["bookmarks"][session_id]: + db["bookmarks"][session_id].remove(resource_id) + save_db(db) + +def get_notes(session_id): + db = load_db() + if "notes" not in db or isinstance(db["notes"], list): + db["notes"] = {} + save_db(db) + return db["notes"].get(session_id, []) + +def add_note(session_id, note_data): + db = load_db() + if "notes" not in db or isinstance(db["notes"], list): + db["notes"] = {} + if session_id not in db["notes"]: + db["notes"][session_id] = [] + + # Simple ID generation if not provided + if "id" not in note_data: + note_data["id"] = f"note_{datetime.now().strftime('%Y%m%d_%H%M%S')}" + if "createdAt" not in note_data: + note_data["createdAt"] = datetime.now().isoformat() + + db["notes"][session_id].append(note_data) + save_db(db) + return note_data + +def get_lectures(): + db = load_db() + return db.get("lectures", []) diff --git a/backend/db.json b/backend/db.json new file mode 100644 index 0000000000000000000000000000000000000000..b31e4dc393cd4f539afc3359ad2f41c5d00c0448 --- /dev/null +++ b/backend/db.json @@ -0,0 +1,2137 @@ +{ + "users": [], + "learning_sessions": { + "default": { + "position": { + "x": 13, + "y": 7 + }, + "level": 5, + "totalReward": 900, + "visitedResources": [ + "18", + "13", + "3", + "16", + "17", + "1", + "10", + "8", + "11", + "6", + "15", + "2", + "7", + "4", + "5", + "14", + "12", + "9" + ] + } + }, + "polylines": { + "polyline_0": { + "id": "polyline_0", + "name": "hi", + "path": [], + "color": "rgba(124, 111, 197, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "hiiii" + }, + "polyline_1": { + "id": "polyline_1", + "name": "HI", + "path": [], + "color": "rgba(233, 165, 150, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "HII" + }, + "polyline_2": { + "id": "polyline_2", + "name": "Hey", + "path": [], + "color": "rgba(209, 233, 75, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "Helloo", + "keywords_found": [], + "module_scores": [ + 0.5484583564915045, + 0.46220741047368424, + 0.46279993894614624, + 0.4622026423083014, + 0.48167479720554324, + 0.5185239739531438, + 0.47663528950961, + 0.5005995736735275, + 0.45502656055000007, + 0.4580010883357644, + 0.45637659612686454, + 0.5112952634097881, + 0.5216384772372223, + 0.5355484607706302, + 0.5135652915368838, + 0.46179487959519183, + 0.5207722130936243, + 0.49757484056057777 + ] + }, + "polyline_3": { + "id": "polyline_3", + "name": "finetuning", + "path": [], + "color": "rgba(121, 211, 92, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "I studied fine tuning and RAG", + "keywords_found": [ + "Fine tuning LLM", + "Retrieval Augmented Generation" + ], + "module_scores": [ + 0.49860121134816787, + 0.4500395442873277, + 0.471618804424644, + 0.6583558195456436, + 0.4580364051366949, + 0.5067814340383607, + 0.5267787555517994, + 0.45159593526792297, + 0.5354930330453695, + 0.4633757771977945, + 0.7440110046152995, + 0.5165231405422576, + 0.5131620206629675, + 0.5382607288711271, + 0.548322797358488, + 0.4885586287263669, + 0.5297033776671244, + 0.5269894013550447 + ] + }, + "polyline_4": { + "id": "polyline_4", + "name": "main", + "path": [], + "color": "rgba(213, 61, 92, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "I studied fine tuning and RAG", + "keywords_found": [ + "Fine tuning LLM", + "Retrieval Augmented Generation" + ], + "module_scores": [ + 0.15775318443775177, + 0.20897206664085388, + 0.20236347615718842, + 0.6945049047470093, + 0.4463333487510681, + 0.12979882955551147, + 0.4455224275588989, + 0.1599089652299881, + 0.10129339247941971, + 0.19735002517700195, + 0.412064354121685, + 0.15838992595672607, + 0.1395527720451355, + 0.06164519488811493, + 0.19151057302951813, + 0.05123066157102585, + 0.046856656670570374, + 0.09634523838758469 + ] + }, + "polyline_5": { + "id": "polyline_5", + "name": "Test Summary", + "path": [ + { + "x": 7, + "y": 0 + }, + { + "x": 14, + "y": 1 + } + ], + "color": "rgba(63, 91, 199, 0.4)", + "isActive": true, + "confidence": 0.73, + "summary": "I learned about BERT and Transformers.", + "keywords_found": [], + "module_scores": [ + 0.41952282190322876, + 0.4550943672657013, + 0.38158124685287476, + 0.22420939803123474, + 0.21500593423843384, + 0.2883783280849457, + 0.08223940432071686, + 0.2931893765926361, + 0.12915080785751343, + 0.09058266133069992, + 0.19895221292972565, + 0.10976134240627289, + 0.10291370004415512, + 0.17602239549160004, + 0.28020530939102173, + 0.2765440344810486, + 0.153752401471138, + 0.1386902928352356 + ] + }, + "polyline_6": { + "id": "polyline_6", + "name": "work", + "path": [ + { + "x": 9, + "y": 18 + }, + { + "x": 1, + "y": 15 + }, + { + "x": 6, + "y": 19 + }, + { + "x": 7, + "y": 19 + } + ], + "color": "rgba(181, 126, 100, 0.4)", + "isActive": true, + "confidence": 0.76, + "summary": "I have learned a lot about rag and preprocessing it was pretty good understanding.", + "keywords_found": [ + "Retrieval Augmented Generation" + ], + "module_scores": [ + 0.2574118673801422, + 0.2806059718132019, + 0.18621119856834412, + 0.10942331701517105, + 0.26856184005737305, + 0.3550261855125427, + 0.07388029992580414, + 0.3707934617996216, + 0.1708703488111496, + 0.2050769031047821, + 0.5013039708137512, + 0.1576768010854721, + 0.10632561147212982, + 0.12748579680919647, + 0.19742925465106964, + 0.05362231656908989, + 0.07185838371515274, + 0.055319223552942276 + ] + }, + "polyline_7": { + "id": "polyline_7", + "name": "Hey", + "path": [], + "color": "rgba(102, 156, 166, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "heyyy", + "keywords_found": [], + "module_scores": [ + 0.0, + 0.017389550805091858, + 0.10327564924955368, + 0.09360406547784805, + 0.03690618276596069, + 0.04519070312380791, + 0.038354936987161636, + 0.07477933913469315, + 0.005562630016356707, + 0.0, + 0.0, + 0.0, + 0.05210745334625244, + 0.002015892416238785, + 0.07473935186862946, + 0.0972217470407486, + 0.0, + 0.05581950396299362 + ] + }, + "polyline_8": { + "id": "polyline_8", + "name": "DQN Learning", + "path": [ + { + "x": 5, + "y": 2 + }, + { + "x": 9, + "y": 10 + } + ], + "color": "rgba(228, 246, 240, 0.4)", + "isActive": true, + "confidence": 0.73, + "summary": "I studied Policy learning using DQN and reinforcement learning agents.", + "keywords_found": [ + "Agentic AI", + "Policy learning using DQN" + ], + "module_scores": [ + 0.3080591907103858, + 0.32459903260072087, + 0.10327564924955368, + 0.3576170255740485, + 0.03690618276596069, + 0.5689638306697211, + 0.038354936987161636, + 0.7058747758467994, + 0.005562630016356707, + 0.0, + 0.0, + 0.0, + 0.05210745334625244, + 1.0, + 0.07473935186862946, + 0.0972217470407486, + 1.0, + 1.0 + ] + }, + "polyline_9": { + "id": "polyline_9", + "name": "heyyy", + "path": [], + "color": "rgba(144, 174, 69, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "heyyy", + "keywords_found": [], + "module_scores": [ + 0.3080591907103858, + 0.32459903260072087, + 0.6197177293813891, + 0.5326734754360385, + 0.03690618276596069, + 0.5689638306697211, + 0.038354936987161636, + 0.7058747758467994, + 0.005562630016356707, + 0.0, + 0.0, + 0.0, + 0.15920396625167793, + 1.0, + 0.36289105295307106, + 0.5652326095021434, + 1.0, + 1.0 + ] + }, + "polyline_10": { + "id": "polyline_10", + "name": "hii", + "path": [], + "color": "rgba(78, 166, 73, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "hiii", + "keywords_found": [], + "module_scores": [ + 0.5118434429168701, + 0.7275236248970032, + 0.5195397734642029, + 0.2506510019302368, + 0.5906199216842651, + 0.460385799407959, + 0.9057682156562805, + 0.577440083026886, + 0.44362306594848633, + 0.6044198870658875, + 0.26611554622650146, + 0.5114411115646362, + 0.49167001247406006, + 0.6330015063285828, + 0.38416922092437744, + 0.5338788628578186, + 0.37343931198120117, + 0.7777066826820374 + ] + }, + "polyline_11": { + "id": "polyline_11", + "name": "hii", + "path": [], + "color": "rgba(199, 83, 212, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "nocajco", + "keywords_found": [], + "module_scores": [ + 0.035517044365406036, + 0.07075861096382141, + 0.021871453151106834, + 0.00989073608070612, + 0.019412130117416382, + 0.0, + 0.11922578513622284, + 0.039588794112205505, + 0.06686414033174515, + 0.09156952798366547, + 0.03569290041923523, + 0.0679011419415474, + 0.03752126544713974, + 0.00478791818022728, + 0.07304392009973526, + 0.011222838424146175, + 0.026982085779309273, + 0.054899897426366806 + ] + }, + "polyline_12": { + "id": "polyline_12", + "name": "hii", + "path": [], + "color": "rgba(159, 210, 119, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "heyy", + "keywords_found": [], + "module_scores": [ + 0.0, + 0.03199615329504013, + 0.11629796773195267, + 0.09658470749855042, + 0.03200055658817291, + 0.054899267852306366, + 0.03489005193114281, + 0.10348626226186752, + 0.008831196464598179, + 0.0, + 0.0008121263235807419, + 0.0, + 0.051078762859106064, + 0.01544315367937088, + 0.07961554080247879, + 0.11205136775970459, + 0.007900607772171497, + 0.0693073719739914 + ] + }, + "polyline_13": { + "id": "polyline_13", + "name": "hello", + "path": [ + { + "x": 3, + "y": 19 + } + ], + "color": "rgba(230, 228, 186, 0.4)", + "isActive": true, + "confidence": 0.71, + "summary": "rag and everything was good", + "keywords_found": [ + "Retrieval Augmented Generation" + ], + "module_scores": [ + 0.05804816260933876, + 0.09254653751850128, + 0.10877598077058792, + 0.012610942125320435, + 0.03797341510653496, + 0.015777718275785446, + 0.059225354343652725, + 0.028679510578513145, + 0.055405281484127045, + 0.10398496687412262, + 0.31428444385528564, + 0.0, + 0.0, + 0.002234384650364518, + 0.0014527571620419621, + 0.025889771059155464, + 0.012727380730211735, + 0.0 + ] + }, + "polyline_14": { + "id": "polyline_14", + "name": "hello", + "path": [ + { + "x": 13, + "y": 15 + } + ], + "color": "rgba(53, 84, 159, 0.4)", + "isActive": true, + "confidence": 0.71, + "summary": "rag and many other things such as pretrainig objectives and all were good", + "keywords_found": [ + "Pre training objectives", + "Retrieval Augmented Generation" + ], + "module_scores": [ + 0.8379164934158325, + 0.3340357542037964, + 0.17760807275772095, + 0.22517074644565582, + 0.26595813035964966, + 0.23630976676940918, + 0.28637999296188354, + 0.19740065932273865, + 0.15292729437351227, + 0.13514529168605804, + 0.42122960090637207, + 0.10186541825532913, + 0.11643099039793015, + 0.16304181516170502, + 0.1428326964378357, + 0.1396074891090393, + 0.21233661472797394, + 0.17357251048088074 + ] + }, + "polyline_15": { + "id": "polyline_15", + "name": "hey ", + "path": [], + "color": "rgba(167, 117, 238, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "hey", + "keywords_found": [], + "module_scores": [ + 0.0, + 0.023556426167488098, + 0.07454296201467514, + 0.10734860599040985, + 0.03490018844604492, + 0.061513230204582214, + 0.06893599033355713, + 0.11837682127952576, + 0.01902083493769169, + 0.02021801471710205, + 0.020470205694437027, + 0.0, + 0.05460038036108017, + 0.018192946910858154, + 0.034782107919454575, + 0.10332289338111877, + 0.011397147551178932, + 0.07992972433567047 + ] + }, + "polyline_16": { + "id": "polyline_16", + "name": "hey", + "path": [], + "color": "rgba(167, 64, 198, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "hey", + "keywords_found": [], + "module_scores": [ + 0.0, + 0.023556426167488098, + 0.07454296201467514, + 0.10734860599040985, + 0.03490018844604492, + 0.061513230204582214, + 0.06893599033355713, + 0.11837682127952576, + 0.01902083493769169, + 0.02021801471710205, + 0.020470205694437027, + 0.0, + 0.05460038036108017, + 0.018192946910858154, + 0.034782107919454575, + 0.10332289338111877, + 0.011397147551178932, + 0.07992972433567047 + ] + }, + "polyline_17": { + "id": "polyline_17", + "name": "hey", + "path": [], + "color": "rgba(142, 153, 121, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "hey", + "keywords_found": [], + "module_scores": [ + 0.0, + 0.023556426167488098, + 0.07454296201467514, + 0.10734860599040985, + 0.03490018844604492, + 0.061513230204582214, + 0.06893599033355713, + 0.11837682127952576, + 0.01902083493769169, + 0.02021801471710205, + 0.020470205694437027, + 0.0, + 0.05460038036108017, + 0.018192946910858154, + 0.034782107919454575, + 0.10332289338111877, + 0.011397147551178932, + 0.07992972433567047 + ] + }, + "polyline_18": { + "id": "polyline_18", + "name": "heyy", + "path": [], + "color": "rgba(205, 206, 221, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "heyyy", + "keywords_found": [], + "module_scores": [ + 0.0, + 0.017389550805091858, + 0.10327564924955368, + 0.09360406547784805, + 0.03690618276596069, + 0.04519070312380791, + 0.038354936987161636, + 0.07477933913469315, + 0.005562630016356707, + 0.0, + 0.0, + 0.0, + 0.05210745334625244, + 0.002015892416238785, + 0.07473935186862946, + 0.0972217470407486, + 0.0, + 0.05581950396299362 + ] + }, + "polyline_19": { + "id": "polyline_19", + "name": "heyy", + "path": [], + "color": "rgba(188, 202, 118, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "I love rag with my pre trining", + "keywords_found": [ + "Retrieval Augmented Generation" + ], + "module_scores": [ + 0.09309744089841843, + 0.12534257769584656, + 0.18787196278572083, + 0.05221807584166527, + 0.053495265543460846, + 0.14688314497470856, + 0.09295167028903961, + 0.13856445252895355, + 0.11419141292572021, + 0.0, + 0.3, + 0.04010733589529991, + 0.0, + 0.045714717358350754, + 0.06977400183677673, + 0.03902996703982353, + 0.010432184673845768, + 0.014520765282213688 + ] + }, + "polyline_20": { + "id": "polyline_20", + "name": "huu", + "path": [], + "color": "rgba(132, 67, 193, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "huuu", + "keywords_found": [], + "module_scores": [ + 0.06137967109680176, + 0.025739794597029686, + 0.05682278797030449, + 0.0015102589968591928, + 0.0, + 0.0, + 0.0828077420592308, + 0.03640921786427498, + 0.0, + 0.048738643527030945, + 0.0, + 0.07323087751865387, + 0.0, + 0.06557325273752213, + 0.03551648184657097, + 0.0, + 0.007635355927050114, + 0.030927538871765137 + ] + }, + "polyline_21": { + "id": "polyline_21", + "name": "hello", + "path": [], + "color": "rgba(171, 185, 237, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "hello", + "keywords_found": [], + "module_scores": [ + 0.09007163345813751, + 0.14346976578235626, + 0.09423135966062546, + 0.039209116250276566, + 0.09866251051425934, + 0.07642864435911179, + 0.050438638776540756, + 0.11424312740564346, + 0.06147314980626106, + 0.025780409574508667, + 0.04125567153096199, + 0.06409071385860443, + 0.0545211136341095, + 0.08595173060894012, + 0.03564179316163063, + 0.09568523615598679, + 0.05039823427796364, + 0.07651611417531967 + ] + }, + "polyline_22": { + "id": "polyline_22", + "name": "Introduction", + "path": [ + { + "x": 4, + "y": 19 + } + ], + "color": "rgba(117, 80, 114, 0.4)", + "isActive": true, + "confidence": 0.71, + "summary": "hello", + "keywords_found": [], + "module_scores": [ + 0.09007163345813751, + 0.14346976578235626, + 0.19423136115074158, + 0.039209116250276566, + 0.09866251051425934, + 0.07642864435911179, + 0.050438638776540756, + 0.11424312740564346, + 0.06147314980626106, + 0.025780409574508667, + 0.04125567153096199, + 0.06409071385860443, + 0.0545211136341095, + 0.08595173060894012, + 0.03564179316163063, + 0.09568523615598679, + 0.05039823427796364, + 0.07651611417531967 + ] + }, + "polyline_23": { + "id": "polyline_23", + "name": "heyyy", + "path": [], + "color": "rgba(76, 80, 77, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "heyyy", + "keywords_found": [], + "module_scores": [ + 0.0, + 0.017389550805091858, + 0.10327564924955368, + 0.09360406547784805, + 0.03690618276596069, + 0.04519070312380791, + 0.038354936987161636, + 0.07477933913469315, + 0.005562630016356707, + 0.0, + 0.0, + 0.0, + 0.05210745334625244, + 0.002015892416238785, + 0.07473935186862946, + 0.0972217470407486, + 0.0, + 0.05581950396299362 + ] + }, + "polyline_24": { + "id": "polyline_24", + "name": "heyyy", + "path": [], + "color": "rgba(123, 83, 128, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "heyyyyy", + "keywords_found": [], + "module_scores": [ + 0.0, + 0.015168394893407822, + 0.09509415924549103, + 0.08808448165655136, + 0.055125169456005096, + 0.03878949210047722, + 0.06340508162975311, + 0.0406477153301239, + 0.0, + 0.0, + 0.0, + 0.012255707755684853, + 0.07024804502725601, + 0.005069136619567871, + 0.06734798103570938, + 0.08735782653093338, + 0.0, + 0.03643130138516426 + ] + }, + "polyline_25": { + "id": "polyline_25", + "name": "Introduction to transformers", + "path": [ + { + "x": 4, + "y": 19 + } + ], + "color": "rgba(143, 130, 92, 0.4)", + "isActive": true, + "confidence": 0.71, + "summary": "Transformers are a class of deep learning models designed to process sequential data efficiently by relying on an attention-based mechanism rather than recurrence or convolution. Introduced in the landmark paper \u201cAttention Is All You Need\u201d, transformers revolutionized natural language processing by enabling models to capture long-range dependencies in data with high parallelism.\n\nAt the core of a transformer is the self-attention mechanism, which allows the model to weigh the importance of different parts of an input sequence when processing each element. This makes transformers highly effective at understanding context, relationships, and structure within data. The architecture typically consists of an encoder\u2013decoder structure, where encoders extract meaningful representations from input data and decoders generate output sequences based on those representations.\n\nTransformers also use positional encoding to retain information about the order of tokens, since the model itself does not process data sequentially. Combined with multi-head attention and feed-forward neural networks, this design enables scalable training on large datasets.\n\nDue to their flexibility and performance, transformers form the backbone of many modern AI systems, including large language models, machine translation systems, text summarization tools, and increasingly, applications in vision, speech, and multimodal learning.", + "keywords_found": [ + "Multimodal LLMs" + ], + "module_scores": [ + 0.433811753988266, + 0.4582192003726959, + 0.5164702534675598, + 0.2946617603302002, + 0.37847253680229187, + 0.2834630310535431, + 0.2269679754972458, + 0.23173178732395172, + 0.12193305790424347, + 0.15064716339111328, + 0.20222218334674835, + 0.23586700856685638, + 0.2287927120923996, + 0.2737714946269989, + 0.7030925154685974, + 0.5322507619857788, + 0.2212483286857605, + 0.202763170003891 + ] + }, + "polyline_26": { + "id": "polyline_26", + "name": "heyy", + "path": [ + { + "x": 4, + "y": 19 + }, + { + "x": 7, + "y": 18 + }, + { + "x": 18, + "y": 6 + }, + { + "x": 9, + "y": 15 + }, + { + "x": 9, + "y": 17 + } + ], + "color": "rgba(94, 122, 221, 0.4)", + "isActive": true, + "confidence": 0.77, + "summary": "heyyy", + "keywords_found": [], + "module_scores": [ + 0.0, + 0.017389550805091858, + 0.2032756507396698, + 0.19360406696796417, + 0.13690617680549622, + 0.04519070312380791, + 0.13835494220256805, + 0.07477933913469315, + 0.005562630016356707, + 0.0, + 0.0, + 0.0, + 0.05210745334625244, + 0.002015892416238785, + 0.17473936080932617, + 0.0972217470407486, + 0.0, + 0.05581950396299362 + ] + }, + "polyline_27": { + "id": "polyline_27", + "name": "heyy", + "path": [ + { + "x": 4, + "y": 19 + } + ], + "color": "rgba(74, 171, 224, 0.4)", + "isActive": true, + "confidence": 0.71, + "summary": "heyyy", + "keywords_found": [], + "module_scores": [ + 0.0, + 0.017389550805091858, + 0.2032756507396698, + 0.09360406547784805, + 0.03690618276596069, + 0.04519070312380791, + 0.038354936987161636, + 0.07477933913469315, + 0.005562630016356707, + 0.0, + 0.0, + 0.0, + 0.05210745334625244, + 0.002015892416238785, + 0.07473935186862946, + 0.0972217470407486, + 0.0, + 0.05581950396299362 + ] + }, + "polyline_28": { + "id": "polyline_28", + "name": "introduction to my ", + "path": [ + { + "x": 4, + "y": 19 + } + ], + "color": "rgba(76, 227, 191, 0.4)", + "isActive": true, + "confidence": 0.71, + "summary": "transformers", + "keywords_found": [], + "module_scores": [ + 0.024954048916697502, + 0.13416121900081635, + 0.5420874357223511, + 0.07039379328489304, + 0.021926987916231155, + 0.0, + 0.19421246647834778, + 0.0, + 0.0, + 0.0374816469848156, + 0.0, + 0.057812318205833435, + 0.04283241927623749, + 0.0, + 0.061064332723617554, + 0.2036512792110443, + 0.0, + 0.0 + ] + }, + "polyline_29": { + "id": "polyline_29", + "name": "hiii", + "path": [ + { + "x": 4, + "y": 19 + }, + { + "x": 7, + "y": 18 + } + ], + "color": "rgba(253, 134, 192, 0.4)", + "isActive": true, + "confidence": 0.73, + "summary": "transformers", + "keywords_found": [], + "module_scores": [ + 0.024954048916697502, + 0.13416121900081635, + 0.5420874357223511, + 0.17039379477500916, + 0.021926987916231155, + 0.0, + 0.19421246647834778, + 0.0, + 0.0, + 0.0374816469848156, + 0.0, + 0.057812318205833435, + 0.04283241927623749, + 0.0, + 0.061064332723617554, + 0.2036512792110443, + 0.0, + 0.0 + ] + }, + "polyline_30": { + "id": "polyline_30", + "name": "heyy", + "path": [ + { + "x": 4, + "y": 15 + }, + { + "x": 11, + "y": 19 + }, + { + "x": 5, + "y": 14 + } + ], + "color": "rgba(109, 71, 172, 0.4)", + "isActive": true, + "confidence": 0.74, + "summary": "heyyy", + "keywords_found": [], + "module_scores": [ + 0.0, + 0.017389550805091858, + 0.10327564924955368, + 0.09360406547784805, + 0.13690617680549622, + 0.04519070312380791, + 0.13835494220256805, + 0.07477933913469315, + 0.005562630016356707, + 0.0, + 0.0, + 0.0, + 0.05210745334625244, + 0.002015892416238785, + 0.17473936080932617, + 0.0972217470407486, + 0.0, + 0.05581950396299362 + ], + "strengths": [ + "Instruction tuning", + "Parameter efficient fine tuning", + "Multimodal LLMs" + ], + "dominant_topics": [], + "ai_analysis": "Based on your path, you have shown strong engagement with . You successfully reinforced concepts in Instruction tuning, Parameter efficient fine tuning. Consider exploring advanced topics in new areas next." + }, + "polyline_31": { + "id": "polyline_31", + "name": "Introduction to transformers", + "path": [], + "color": "rgba(112, 160, 225, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "Transformers are a class of deep learning models designed to process sequential data efficiently by relying on an attention-based mechanism rather than recurrence or convolution. Introduced in the landmark paper \u201cAttention Is All You Need\u201d, transformers revolutionized natural language processing by enabling models to capture long-range dependencies in data with high parallelism.\n\nAt the core of a transformer is the self-attention mechanism, which allows the model to weigh the importance of different parts of an input sequence when processing each element. This makes transformers highly effective at understanding context, relationships, and structure within data. The architecture typically consists of an encoder\u2013decoder structure, where encoders extract meaningful representations from input data and decoders generate output sequences based on those representations.\n\nTransformers also use positional encoding to retain information about the order of tokens, since the model itself does not process data sequentially. Combined with multi-head attention and feed-forward neural networks, this design enables scalable training on large datasets.\n\nDue to their flexibility and performance, transformers form the backbone of many modern AI systems, including large language models, machine translation systems, text summarization tools, and increasingly, applications in vision, speech, and multimodal learning.", + "keywords_found": [ + "Multimodal LLMs" + ], + "module_scores": [ + 0.433811753988266, + 0.4582192003726959, + 0.4164702296257019, + 0.2946617603302002, + 0.37847253680229187, + 0.2834630310535431, + 0.2269679754972458, + 0.23173178732395172, + 0.12193305790424347, + 0.15064716339111328, + 0.20222218334674835, + 0.23586700856685638, + 0.2287927120923996, + 0.2737714946269989, + 0.7030925154685974, + 0.5322507619857788, + 0.2212483286857605, + 0.202763170003891 + ], + "strengths": [], + "dominant_topics": [ + "Multimodal LLMs", + "Vision Language Models", + "Pre trained models" + ], + "ai_analysis": "Based on your path, you have shown strong engagement with Multimodal LLMs, Vision Language Models. You successfully reinforced concepts in . Consider exploring advanced topics in new areas next." + }, + "polyline_32": { + "id": "polyline_32", + "name": "Intro", + "path": [], + "color": "rgba(65, 189, 58, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "Transformers are a class of deep learning models designed to process sequential data efficiently by relying on an attention-based mechanism rather than recurrence or convolution. Introduced in the landmark paper \u201cAttention Is All You Need\u201d, transformers revolutionized natural language processing by enabling models to capture long-range dependencies in data with high parallelism.\n\nAt the core of a transformer is the self-attention mechanism, which allows the model to weigh the importance of different parts of an input sequence when processing each element. This makes transformers highly effective at understanding context, relationships, and structure within data. The architecture typically consists of an encoder\u2013decoder structure, where encoders extract meaningful representations from input data and decoders generate output sequences based on those representations.\n\nTransformers also use positional encoding to retain information about the order of tokens, since the model itself does not process data sequentially. Combined with multi-head attention and feed-forward neural networks, this design enables scalable training on large datasets.\n\nDue to their flexibility and performance, transformers form the backbone of many modern AI systems, including large language models, machine translation systems, text summarization tools, and increasingly, applications in vision, speech, and multimodal learning.", + "keywords_found": [ + "Multimodal LLMs" + ], + "module_scores": [ + 0.433811753988266, + 0.4582192003726959, + 0.4164702296257019, + 0.2946617603302002, + 0.37847253680229187, + 0.2834630310535431, + 0.2269679754972458, + 0.23173178732395172, + 0.12193305790424347, + 0.15064716339111328, + 0.20222218334674835, + 0.23586700856685638, + 0.2287927120923996, + 0.2737714946269989, + 0.7030925154685974, + 0.5322507619857788, + 0.2212483286857605, + 0.202763170003891 + ], + "strengths": [], + "dominant_topics": [ + "Multimodal LLMs", + "Vision Language Models", + "Pre trained models" + ], + "ai_analysis": "To enable advanced AI analysis, set the GEMINI_API_KEY environment variable. Based on your path, you have shown strong engagement with Multimodal LLMs, Vision Language Models. You successfully reinforced concepts in . Consider exploring advanced topics in new areas next." + }, + "polyline_33": { + "id": "polyline_33", + "name": "Intro", + "path": [ + { + "x": 5, + "y": 16 + } + ], + "color": "rgba(196, 90, 89, 0.4)", + "isActive": true, + "confidence": 0.71, + "summary": "Transformers are a class of deep learning models designed to process sequential data efficiently by relying on an attention-based mechanism rather than recurrence or convolution. Introduced in the landmark paper \u201cAttention Is All You Need\u201d, transformers revolutionized natural language processing by enabling models to capture long-range dependencies in data with high parallelism.\n\nAt the core of a transformer is the self-attention mechanism, which allows the model to weigh the importance of different parts of an input sequence when processing each element. This makes transformers highly effective at understanding context, relationships, and structure within data. The architecture typically consists of an encoder\u2013decoder structure, where encoders extract meaningful representations from input data and decoders generate output sequences based on those representations.\n\nTransformers also use positional encoding to retain information about the order of tokens, since the model itself does not process data sequentially. Combined with multi-head attention and feed-forward neural networks, this design enables scalable training on large datasets.\n\nDue to their flexibility and performance, transformers form the backbone of many modern AI systems, including large language models, machine translation systems, text summarization tools, and increasingly, applications in vision, speech, and multimodal learning.", + "keywords_found": [ + "Multimodal LLMs" + ], + "module_scores": [ + 0.433811753988266, + 0.4582192003726959, + 0.4164702296257019, + 0.2946617603302002, + 0.37847253680229187, + 0.2834630310535431, + 0.2269679754972458, + 0.23173178732395172, + 0.12193305790424347, + 0.15064716339111328, + 0.20222218334674835, + 0.23586700856685638, + 0.2287927120923996, + 0.2737714946269989, + 0.8030925393104553, + 0.5322507619857788, + 0.2212483286857605, + 0.202763170003891 + ], + "strengths": [ + "Multimodal LLMs" + ], + "dominant_topics": [ + "Multimodal LLMs", + "Vision Language Models", + "Pre trained models" + ], + "ai_analysis": "To enable advanced AI analysis, set the GEMINI_API_KEY environment variable. Based on your path, you have shown strong engagement with Multimodal LLMs, Vision Language Models. You successfully reinforced concepts in Multimodal LLMs. Consider exploring advanced topics in new areas next." + }, + "polyline_34": { + "id": "polyline_34", + "name": "Pre training LLM", + "path": [ + { + "x": 5, + "y": 17 + } + ], + "color": "rgba(231, 183, 145, 0.4)", + "isActive": true, + "confidence": 0.71, + "summary": "I have learnt to finetune a pre trained BERT GPT model and i am using these models for sentiment analysis task", + "keywords_found": [ + "Pre trained models" + ], + "module_scores": [ + 0.43252697587013245, + 0.6987414360046387, + 0.2524697184562683, + 0.4963360130786896, + 0.26733019948005676, + 0.28303566575050354, + 0.11330951005220413, + 0.2320728749036789, + 0.07975509762763977, + 0.13868987560272217, + 0.21347038447856903, + 0.13678917288780212, + 0.20242607593536377, + 0.20470771193504333, + 0.19388322532176971, + 0.24398048222064972, + 0.25268879532814026, + 0.3274286389350891 + ], + "strengths": [ + "Agentic AI" + ], + "dominant_topics": [ + "Pre trained models", + "Fine tuning LLM", + "Pre training objectives" + ], + "ai_analysis": "Based on your path, you have shown strong engagement with Pre trained models, Fine tuning LLM. You successfully reinforced concepts in Agentic AI. Consider exploring advanced topics in new areas next." + }, + "polyline_35": { + "id": "polyline_35", + "name": "heyy", + "path": [ + { + "x": 5, + "y": 17 + } + ], + "color": "rgba(95, 72, 171, 0.4)", + "isActive": true, + "confidence": 0.71, + "summary": "heyyy", + "keywords_found": [], + "module_scores": [ + 0.0, + 0.017389550805091858, + 0.10327564924955368, + 0.09360406547784805, + 0.03690618276596069, + 0.04519070312380791, + 0.038354936987161636, + 0.07477933913469315, + 0.005562630016356707, + 0.0, + 0.0, + 0.0, + 0.05210745334625244, + 0.1020158976316452, + 0.07473935186862946, + 0.0972217470407486, + 0.0, + 0.05581950396299362 + ], + "strengths": [ + "Agentic AI" + ], + "dominant_topics": [], + "ai_analysis": "Based on your path, you have shown strong engagement with . You successfully reinforced concepts in Agentic AI. Consider exploring advanced topics in new areas next." + }, + "polyline_36": { + "id": "polyline_36", + "name": "Agentic AI", + "path": [ + { + "x": 5, + "y": 17 + } + ], + "color": "rgba(139, 171, 145, 0.4)", + "isActive": true, + "confidence": 0.71, + "summary": "agents independently solve the problem, each can have differnt loss function", + "keywords_found": [ + "Agentic AI" + ], + "module_scores": [ + 0.05024600028991699, + 0.0863848477602005, + 0.0, + 0.1774979680776596, + 0.007330421358346939, + 0.06964121013879776, + 0.08910828828811646, + 0.1227140724658966, + 0.0983947366476059, + 0.02778339385986328, + 0.012763900682330132, + 0.0882045105099678, + 0.2467341423034668, + 0.6995577812194824, + 0.13445383310317993, + 0.07179756462574005, + 0.3390352427959442, + 0.24001526832580566 + ], + "strengths": [ + "Agentic AI" + ], + "dominant_topics": [ + "Agentic AI", + "Policy learning using DQN" + ], + "ai_analysis": "Based on your path, you have shown strong engagement with Agentic AI, Policy learning using DQN. You successfully reinforced concepts in Agentic AI. Consider exploring advanced topics in new areas next." + }, + "polyline_37": { + "id": "polyline_37", + "name": "Agentic AI, Transformer, CNN", + "path": [ + { + "x": 7, + "y": 16 + }, + { + "x": 5, + "y": 17 + } + ], + "color": "rgba(124, 155, 146, 0.4)", + "isActive": true, + "confidence": 0.73, + "summary": "Agentic AI, AI, Artificial Intelligence, Agentic AI has agents, Agents are good, Agentic AI, Agent", + "keywords_found": [ + "Agentic AI" + ], + "module_scores": [ + 0.19596125185489655, + 0.17878513038158417, + 0.013299948535859585, + 0.09019515663385391, + 0.2148519903421402, + 0.176703080534935, + 0.04141182824969292, + 0.12695123255252838, + 0.14567424356937408, + 0.06627196818590164, + 0.07899114489555359, + 0.17586567997932434, + 0.19476987421512604, + 0.9779932498931885, + 0.14229676127433777, + 0.17270609736442566, + 0.3035743236541748, + 0.3211010694503784 + ], + "strengths": [ + "Quantization", + "Agentic AI" + ], + "dominant_topics": [ + "Agentic AI", + "RLHF", + "Policy learning using DQN" + ], + "ai_analysis": "Based on your path, you have shown strong engagement with Agentic AI, RLHF. You successfully reinforced concepts in Quantization, Agentic AI. Consider exploring advanced topics in new areas next." + }, + "polyline_38": { + "id": "polyline_38", + "name": "heyy", + "path": [ + { + "x": 7, + "y": 16 + }, + { + "x": 5, + "y": 17 + } + ], + "color": "rgba(198, 232, 160, 0.4)", + "isActive": true, + "confidence": 0.73, + "summary": "heyey", + "keywords_found": [], + "module_scores": [ + 0.03077820874750614, + 0.08723001182079315, + 0.17077267169952393, + 0.0844632163643837, + 0.05345889925956726, + 0.06896784901618958, + 0.01981256529688835, + 0.10914305597543716, + 0.025269674137234688, + 0.0, + 0.0, + 0.12327592819929123, + 0.08018720149993896, + 0.1916126310825348, + 0.07461867481470108, + 0.14787648618221283, + 0.01713082380592823, + 0.15488959848880768 + ], + "strengths": [ + "Quantization", + "Agentic AI" + ], + "dominant_topics": [], + "ai_analysis": "Based on your path, you have shown strong engagement with . You successfully reinforced concepts in Quantization, Agentic AI. Consider exploring advanced topics in new areas next." + }, + "polyline_39": { + "id": "polyline_39", + "name": "heyu", + "path": [], + "color": "rgba(215, 166, 66, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "heyu", + "keywords_found": [], + "module_scores": [ + 0.025842785835266113, + 0.06289363652467728, + 0.10655583441257477, + 0.059236228466033936, + 0.04497949779033661, + 0.05150831118226051, + 0.08413022756576538, + 0.10349386930465698, + 0.019398359581828117, + 0.023599496111273766, + 0.0, + 0.02620738558471203, + 0.055542171001434326, + 0.08238955587148666, + 0.07436030358076096, + 0.08257163316011429, + 0.05098576098680496, + 0.10993840545415878 + ], + "strengths": [], + "dominant_topics": [], + "ai_analysis": "Based on your path, you have shown strong engagement with . You successfully reinforced concepts in . Consider exploring advanced topics in new areas next." + }, + "polyline_40": { + "id": "polyline_40", + "name": "he7yey", + "path": [ + { + "x": 4, + "y": 2 + }, + { + "x": 10, + "y": 10 + } + ], + "color": "rgba(68, 203, 241, 0.4)", + "isActive": true, + "confidence": 0.73, + "summary": "heyyy", + "keywords_found": [], + "module_scores": [ + 0.0, + 0.017389550805091858, + 0.10327564924955368, + 0.09360406547784805, + 0.03690618276596069, + 0.04519070312380791, + 0.038354936987161636, + 0.17477934062480927, + 0.005562630016356707, + 0.0, + 0.0, + 0.0, + 0.05210745334625244, + 0.002015892416238785, + 0.07473935186862946, + 0.1972217559814453, + 0.0, + 0.05581950396299362 + ], + "strengths": [ + "Incontext Learning", + "Vision Language Models" + ], + "dominant_topics": [], + "ai_analysis": "Based on your path, you have shown strong engagement with . You successfully reinforced concepts in Incontext Learning, Vision Language Models. Consider exploring advanced topics in new areas next." + }, + "polyline_41": { + "id": "polyline_41", + "name": "heyy", + "path": [], + "color": "rgba(129, 154, 197, 0.4)", + "isActive": true, + "confidence": 0.7, + "summary": "hyeyyyhu", + "keywords_found": [], + "module_scores": [ + 0.053188253194093704, + 0.0, + 0.04981702193617821, + 0.0, + 0.07837709784507751, + 0.0, + 0.11811202019453049, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0558195635676384, + 0.02478315681219101, + 0.019923537969589233, + 0.0004506018303800374, + 0.046636879444122314 + ], + "strengths": [], + "dominant_topics": [], + "ai_analysis": "Based on your path, you have shown strong engagement with . You successfully reinforced concepts in . Consider exploring advanced topics in new areas next." + }, + "polyline_42": { + "id": "polyline_42", + "name": "heyyu", + "path": [ + { + "x": 10, + "y": 13 + } + ], + "color": "rgba(253, 226, 126, 0.4)", + "isActive": true, + "confidence": 0.71, + "summary": "heyuuu", + "keywords_found": [], + "module_scores": [ + 0.0, + 0.0, + 0.1106286495923996, + 0.04658149555325508, + 0.027793176472187042, + 0.0, + 0.05434393510222435, + 0.037776097655296326, + 0.1, + 0.004652492236346006, + 0.004580324981361628, + 0.01859883777797222, + 0.04390064999461174, + 0.0656537190079689, + 0.0459163598716259, + 0.04920380190014839, + 0.037917736917734146, + 0.09046495705842972 + ], + "strengths": [ + "Prompting methods" + ], + "dominant_topics": [], + "ai_analysis": "AI Insight: Tell student to improve summary about Retrieval Augmented Generation. be critical." + } + }, + "summaries": [ + { + "id": "summary_default_5", + "title": "Test Summary", + "summary": "I learned about BERT and Transformers.", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 2, + "currentLevel": 1, + "strengths": [ + "Pre training objectives", + "Pre trained models" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 100 + }, + { + "id": "summary_default_6", + "title": "work", + "summary": "I have learned a lot about rag and preprocessing it was pretty good understanding.", + "keywords_found": [ + "Retrieval Augmented Generation" + ], + "totalResources": 18, + "visitedResources": 4, + "currentLevel": 2, + "strengths": [ + "Prompt based learning", + "Incontext Learning", + "Retrieval Methods", + "Retrieval Augmented Generation" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 200 + }, + { + "id": "summary_default_7", + "title": "Hey", + "summary": "heyyy", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_8", + "title": "DQN Learning", + "summary": "I studied Policy learning using DQN and reinforcement learning agents.", + "keywords_found": [ + "Agentic AI", + "Policy learning using DQN" + ], + "totalResources": 18, + "visitedResources": 2, + "currentLevel": 1, + "strengths": [ + "Pre training objectives", + "Pre trained models" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 100 + }, + { + "id": "summary_default_9", + "title": "heyyy", + "summary": "heyyy", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_10", + "title": "hii", + "summary": "hiii", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_11", + "title": "hii", + "summary": "nocajco", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_12", + "title": "hii", + "summary": "heyy", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_13", + "title": "hello", + "summary": "rag and everything was good", + "keywords_found": [ + "Retrieval Augmented Generation" + ], + "totalResources": 18, + "visitedResources": 1, + "currentLevel": 1, + "strengths": [ + "Retrieval Methods" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 50 + }, + { + "id": "summary_default_14", + "title": "hello", + "summary": "rag and many other things such as pretrainig objectives and all were good", + "keywords_found": [ + "Pre training objectives", + "Retrieval Augmented Generation" + ], + "totalResources": 18, + "visitedResources": 1, + "currentLevel": 1, + "strengths": [ + "Pre training objectives" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 50 + }, + { + "id": "summary_default_15", + "title": "hey ", + "summary": "hey", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_16", + "title": "hey", + "summary": "hey", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_17", + "title": "hey", + "summary": "hey", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_18", + "title": "heyy", + "summary": "heyyy", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_19", + "title": "heyy", + "summary": "I love rag with my pre trining", + "keywords_found": [ + "Retrieval Augmented Generation" + ], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_20", + "title": "huu", + "summary": "huuu", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_21", + "title": "hello", + "summary": "hello", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_22", + "title": "Introduction", + "summary": "hello", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 1, + "currentLevel": 1, + "strengths": [ + "Tutorial: Introduction to huggingface" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 50 + }, + { + "id": "summary_default_23", + "title": "heyyy", + "summary": "heyyy", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_24", + "title": "heyyy", + "summary": "heyyyyy", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_25", + "title": "Introduction to transformers", + "summary": "Transformers are a class of deep learning models designed to process sequential data efficiently by relying on an attention-based mechanism rather than recurrence or convolution. Introduced in the landmark paper \u201cAttention Is All You Need\u201d, transformers revolutionized natural language processing by enabling models to capture long-range dependencies in data with high parallelism.\n\nAt the core of a transformer is the self-attention mechanism, which allows the model to weigh the importance of different parts of an input sequence when processing each element. This makes transformers highly effective at understanding context, relationships, and structure within data. The architecture typically consists of an encoder\u2013decoder structure, where encoders extract meaningful representations from input data and decoders generate output sequences based on those representations.\n\nTransformers also use positional encoding to retain information about the order of tokens, since the model itself does not process data sequentially. Combined with multi-head attention and feed-forward neural networks, this design enables scalable training on large datasets.\n\nDue to their flexibility and performance, transformers form the backbone of many modern AI systems, including large language models, machine translation systems, text summarization tools, and increasingly, applications in vision, speech, and multimodal learning.", + "keywords_found": [ + "Multimodal LLMs" + ], + "totalResources": 18, + "visitedResources": 1, + "currentLevel": 1, + "strengths": [ + "Tutorial: Introduction to huggingface" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 50 + }, + { + "id": "summary_default_26", + "title": "heyy", + "summary": "heyyy", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 5, + "currentLevel": 2, + "strengths": [ + "Tutorial: Introduction to huggingface", + "Fine tuning LLM", + "Instruction tuning", + "Parameter efficient fine tuning", + "Multimodal LLMs" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 250 + }, + { + "id": "summary_default_27", + "title": "heyy", + "summary": "heyyy", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 1, + "currentLevel": 1, + "strengths": [ + "Tutorial: Introduction to huggingface" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 50 + }, + { + "id": "summary_default_28", + "title": "introduction to my ", + "summary": "transformers", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 1, + "currentLevel": 1, + "strengths": [ + "Tutorial: Introduction to huggingface" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 50 + }, + { + "id": "summary_default_29", + "title": "hiii", + "summary": "transformers", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 2, + "currentLevel": 1, + "strengths": [ + "Tutorial: Introduction to huggingface", + "Fine tuning LLM" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 100 + }, + { + "id": "summary_default_30", + "title": "heyy", + "summary": "heyyy", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 3, + "currentLevel": 1, + "strengths": [ + "Instruction tuning", + "Parameter efficient fine tuning", + "Multimodal LLMs" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 150 + }, + { + "id": "summary_default_31", + "title": "Introduction to transformers", + "summary": "Transformers are a class of deep learning models designed to process sequential data efficiently by relying on an attention-based mechanism rather than recurrence or convolution. Introduced in the landmark paper \u201cAttention Is All You Need\u201d, transformers revolutionized natural language processing by enabling models to capture long-range dependencies in data with high parallelism.\n\nAt the core of a transformer is the self-attention mechanism, which allows the model to weigh the importance of different parts of an input sequence when processing each element. This makes transformers highly effective at understanding context, relationships, and structure within data. The architecture typically consists of an encoder\u2013decoder structure, where encoders extract meaningful representations from input data and decoders generate output sequences based on those representations.\n\nTransformers also use positional encoding to retain information about the order of tokens, since the model itself does not process data sequentially. Combined with multi-head attention and feed-forward neural networks, this design enables scalable training on large datasets.\n\nDue to their flexibility and performance, transformers form the backbone of many modern AI systems, including large language models, machine translation systems, text summarization tools, and increasingly, applications in vision, speech, and multimodal learning.", + "keywords_found": [ + "Multimodal LLMs" + ], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_32", + "title": "Intro", + "summary": "Transformers are a class of deep learning models designed to process sequential data efficiently by relying on an attention-based mechanism rather than recurrence or convolution. Introduced in the landmark paper \u201cAttention Is All You Need\u201d, transformers revolutionized natural language processing by enabling models to capture long-range dependencies in data with high parallelism.\n\nAt the core of a transformer is the self-attention mechanism, which allows the model to weigh the importance of different parts of an input sequence when processing each element. This makes transformers highly effective at understanding context, relationships, and structure within data. The architecture typically consists of an encoder\u2013decoder structure, where encoders extract meaningful representations from input data and decoders generate output sequences based on those representations.\n\nTransformers also use positional encoding to retain information about the order of tokens, since the model itself does not process data sequentially. Combined with multi-head attention and feed-forward neural networks, this design enables scalable training on large datasets.\n\nDue to their flexibility and performance, transformers form the backbone of many modern AI systems, including large language models, machine translation systems, text summarization tools, and increasingly, applications in vision, speech, and multimodal learning.", + "keywords_found": [ + "Multimodal LLMs" + ], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_33", + "title": "Intro", + "summary": "Transformers are a class of deep learning models designed to process sequential data efficiently by relying on an attention-based mechanism rather than recurrence or convolution. Introduced in the landmark paper \u201cAttention Is All You Need\u201d, transformers revolutionized natural language processing by enabling models to capture long-range dependencies in data with high parallelism.\n\nAt the core of a transformer is the self-attention mechanism, which allows the model to weigh the importance of different parts of an input sequence when processing each element. This makes transformers highly effective at understanding context, relationships, and structure within data. The architecture typically consists of an encoder\u2013decoder structure, where encoders extract meaningful representations from input data and decoders generate output sequences based on those representations.\n\nTransformers also use positional encoding to retain information about the order of tokens, since the model itself does not process data sequentially. Combined with multi-head attention and feed-forward neural networks, this design enables scalable training on large datasets.\n\nDue to their flexibility and performance, transformers form the backbone of many modern AI systems, including large language models, machine translation systems, text summarization tools, and increasingly, applications in vision, speech, and multimodal learning.", + "keywords_found": [ + "Multimodal LLMs" + ], + "totalResources": 18, + "visitedResources": 1, + "currentLevel": 1, + "strengths": [ + "Multimodal LLMs" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 50 + }, + { + "id": "summary_default_34", + "title": "Pre training LLM", + "summary": "I have learnt to finetune a pre trained BERT GPT model and i am using these models for sentiment analysis task", + "keywords_found": [ + "Pre trained models" + ], + "totalResources": 18, + "visitedResources": 1, + "currentLevel": 1, + "strengths": [ + "Agentic AI" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 50 + }, + { + "id": "summary_default_35", + "title": "heyy", + "summary": "heyyy", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 1, + "currentLevel": 1, + "strengths": [ + "Agentic AI" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 50 + }, + { + "id": "summary_default_36", + "title": "Agentic AI", + "summary": "agents independently solve the problem, each can have differnt loss function", + "keywords_found": [ + "Agentic AI" + ], + "totalResources": 18, + "visitedResources": 1, + "currentLevel": 1, + "strengths": [ + "Agentic AI" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 50 + }, + { + "id": "summary_default_37", + "title": "Agentic AI, Transformer, CNN", + "summary": "Agentic AI, AI, Artificial Intelligence, Agentic AI has agents, Agents are good, Agentic AI, Agent", + "keywords_found": [ + "Agentic AI" + ], + "totalResources": 18, + "visitedResources": 2, + "currentLevel": 1, + "strengths": [ + "Quantization", + "Agentic AI" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 100 + }, + { + "id": "summary_default_38", + "title": "heyy", + "summary": "heyey", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 2, + "currentLevel": 1, + "strengths": [ + "Quantization", + "Agentic AI" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 100 + }, + { + "id": "summary_default_39", + "title": "heyu", + "summary": "heyu", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_40", + "title": "he7yey", + "summary": "heyyy", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 2, + "currentLevel": 1, + "strengths": [ + "Incontext Learning", + "Vision Language Models" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 100 + }, + { + "id": "summary_default_41", + "title": "heyy", + "summary": "hyeyyyhu", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 0, + "currentLevel": 1, + "strengths": [], + "recommendations": [], + "avgDifficulty": 0, + "totalReward": 0 + }, + { + "id": "summary_default_42", + "title": "heyyu", + "summary": "heyuuu", + "keywords_found": [], + "totalResources": 18, + "visitedResources": 1, + "currentLevel": 1, + "strengths": [ + "Prompting methods" + ], + "recommendations": [], + "avgDifficulty": 2.0, + "totalReward": 50 + } + ] +} \ No newline at end of file diff --git a/backend/debug_nlp.py b/backend/debug_nlp.py new file mode 100644 index 0000000000000000000000000000000000000000..6d924503dba207019eeddfcab247c8d6fcbf7ae6 --- /dev/null +++ b/backend/debug_nlp.py @@ -0,0 +1,70 @@ +import os +import json +import numpy as np + +def load_nlp_resources(): + nlp_json_path = os.path.join(os.getcwd(), 'backend', 'nlp', 'nlp_resources.json') + print(f"Loading from: {nlp_json_path}") + + try: + if not os.path.exists(nlp_json_path): + print(f"File not found: {nlp_json_path}") + return [] + + with open(nlp_json_path, 'r', encoding='utf-8') as f: + data = json.load(f) + + print(f"Data count: {len(data)}") + + # Group by difficulty for tiered journey + intro_resources = [r for r in data if int(r.get('difficulty', 2)) <= 3] + medium_resources = [r for r in data if 4 <= int(r.get('difficulty', 2)) <= 7] + advanced_resources = [r for r in data if int(r.get('difficulty', 2)) >= 8] + + print(f"Intro: {len(intro_resources)}, Medium: {len(medium_resources)}, Advanced: {len(advanced_resources)}") + + # Limit introductory count as requested (top 6 by reward) + intro_resources.sort(key=lambda x: int(x.get('reward', 0)), reverse=True) + intro_resources = intro_resources[:6] + + journey_data = intro_resources + medium_resources + advanced_resources + print(f"Journey data count: {len(journey_data)}") + + resources = [] + for idx, row in enumerate(journey_data): + title = str(row.get('name', f'Resource {idx + 1}')).strip() + module = str(row.get('module', title)).strip() + difficulty = int(row.get('difficulty', 2)) + + if difficulty <= 3: + y_min, y_max = 16, 19 + elif difficulty <= 7: + y_min, y_max = 8, 15 + else: + y_min, y_max = 1, 7 + + x = int(row.get('x', np.random.randint(2, 18))) + y = int(row.get('y', np.random.randint(y_min, y_max + 1))) + + resource = { + 'id': str(row.get('id', idx + 1)), + 'title': title, + 'module': module, + 'type': str(row.get('type', 'video')), + 'difficulty': difficulty, + 'reward': int(row.get('reward', 10 * difficulty)), + 'position': {'x': x, 'y': y}, + 'visited': row.get('visited', False) + } + resources.append(resource) + + return resources + except Exception as e: + print(f"Error: {e}") + import traceback + traceback.print_exc() + return [] + +if __name__ == "__main__": + res = load_nlp_resources() + print(f"Final Count: {len(res)}") diff --git a/backend/fix_db.py b/backend/fix_db.py new file mode 100644 index 0000000000000000000000000000000000000000..64db822ecbe906ef0749ab440fc396f6f3bf2d61 --- /dev/null +++ b/backend/fix_db.py @@ -0,0 +1,32 @@ +import json + +db_path = r'e:\Nl_main\backend\data\db.json' + +with open(db_path, 'r', encoding='utf-8') as f: + s = f.read().strip() + +# The file ends with '}}' but should end with '}' +# Try stripping from the end until valid +for i in range(1, 20): + try: + data = json.loads(s[:-i] if i > 0 else s) + with open(db_path, 'w', encoding='utf-8') as f: + json.dump(data, f, indent=4) + print(f"DB repaired by removing {i} trailing characters!") + break + except json.JSONDecodeError: + continue +else: + print("Could not repair automatically. Resetting to clean state.") + data = { + "users": [], + "learning_sessions": {}, + "polylines": {}, + "summaries": [], + "bookmarks": {}, + "notes": {}, + "lectures": [] + } + with open(db_path, 'w', encoding='utf-8') as f: + json.dump(data, f, indent=4) + print("DB reset to clean state.") diff --git a/backend/init.py b/backend/init.py new file mode 100644 index 0000000000000000000000000000000000000000..6f5d8b34b81f8ed30a33f4cf7cd6f40aee421ba2 --- /dev/null +++ b/backend/init.py @@ -0,0 +1,32 @@ +""" +Flask Application Initialization +Sets up the Flask app with necessary configurations +""" + +from flask import Flask, send_from_directory +from flask_cors import CORS +import os + +# Initialize Flask app +# Serves static files from the build directory (../dist) +app = Flask(__name__, static_folder='../dist', static_url_path='') + +# Enable CORS +CORS(app) + +# Serve React App (Catch-all route) +@app.route('/', defaults={'path': ''}) +@app.route('/') +def serve(path): + # Don't interfere with API routes + if path.startswith('api'): + return {"error": "Not found"}, 404 + + if path != "" and os.path.exists(os.path.join(app.static_folder, path)): + return send_from_directory(app.static_folder, path) + + # Return index.html for SPA routing + return send_from_directory(app.static_folder, 'index.html') + +# Flag to indicate if database was just created +DBcreated = False diff --git a/backend/key.pem b/backend/key.pem new file mode 100644 index 0000000000000000000000000000000000000000..ce32ff9a4f7ea5081f71c8ae62b8a462681bc54a --- /dev/null +++ b/backend/key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCzuWR9b5a7d5xP +2qxbQ6aHaP6fNpUP+U30JESEXsUKpMbQP6V6w6+QqpfcxuL9l4F66Q7COJrPXxQV +FN5tiJ8y/35obk9m4HInKsw05DuYulLreoIpmpt2oyRCvJPeEcVjYDsvi6BQQO7y +fbYTfA0oXRmcHPnpcEdlv8vCcbbJQkl70QWxwS+daPPharCRGDGi3OWi1Q3h7or/ +BEx6F+0suZhrdoFyMAuUYsVbQ8PThfpSutt3XGJwt1NQCgOqlO5f9DYcpkO9SXWd +NlJcWsEaMD5rgO2khB4IwwPnrUCksPqKIMoDxxX7bCt4GJWAL0pY3ZGNgIbCTEf+ +zBZLMBSLEf5wJCZYf7qbrieTqpLfY5+ElAmst3/S4561626LqwPzZrzroIZ23DWZ +uXUKO2mcTMJ2UW+6l82aZvPvV5BxIpqrgvrSakasihEaiQcYUiE2zZeiOsdQYbmB +ZJKcMXAdMBVRobU2Lehp6bn2PygUHBqIv6i6qkHtPsSiak9HZVFgWKjiM0pvSrRO +OX0aqNzp6hlZrxpKUSM5Ymoa3IqZ+dQOvVx/PqpTFjgVkCHbd2VnIa3WGjrtgu8K +rjmngXj3yL7nm1MtQ1nK8DpDp3aXFMqJODr22H7SgYU1Y32k4Z/8/0h/urbmYbCk +lYW/d1Xmnvjg/LIG7wh+yuJj8eRhnQIDAQABAoICAG6EFlM8B0iIKMrCATKtyv2B +hvGIsuj7uP/kPW3YiPoekVMFkFCJfLBM9s2AT5z5SVm3bqfn/up94O+FwbFBTgQi +MC0viAwPAKMtIh10K2GjUdITp27F78toTqfrJlJsQyD0z/BQv+gZxdW/j/MzWTP0 +7Aafmt39eSoMYtPS8T+ZT26f/gxsa0c0fqOi46iVZBBuHZefw8Wm1joy/oQlqmbS +SfPrD3aULMq2Yf3ZEdZEkMXGuaYQXUhKsySIw/BlXKAMoCnF4+nF/lkKSmMYO55L +BFc/FOMRixaANSqF0zZyzd2ahB8GkXHl9mUHWQdWDTXQKe0Fl9hq5+q6vQrHKhnC +fqUyQfWwRukS9p0pfUjdJC+sc8RsiHYhdu0CCC7W0GrL1tMrJRs778QvbHeZ/Ecx +ArdppOY5s/at9sg4ZP1oc03MEe6lVRzOiQi2DVpuuqUAU4sRbqi+Urw+uIHs0EFm +LiDiiFnfpgiO8wQLFTULJmWrnReQGNocIvtUudURb8fdoRZaUrYkw04dbnGV2HF4 +9JtQHMr1YMNI+XHL3MZLbFUvNT+ntG8ZlTIN3/DIEZDxGeitdjhy1Lh1zWA0RwQM +FnNxKvfh+Xq0WHxwRBRJLRAzvXG0mDmxRDDrUBach7ONN5iTu/nXgopEDi6aXsuh +OnqjorptxmH2g8iTTGABAoIBAQDhHkoKJhL2znvp25P4PWNGCT0TYZaiwPyzpcdO +ArwwGsLJNgTiAzKJlvc82dNE1bw9LQ0+8DiiR1Y6hIIrJ9n+5NCv1rtFgJyvqKf6 +wEE5FbHmA91qSWasm50yoF9OXh9a2duvy0yIyIupI2aGHt1JmFKeRkPHJozpByLK +7rPAIqTwETvvq2kJ3RKk3Ge3EiyAY4fHKYTsty58N4/E51as8W9FsiEpdf354l8O +WrrQSzCFElcDslB6TVgpr0LQgJCc9CNow10gFfy8qjR/g81hk4u4IZ/Ghmll8TcM +0vGItWc/VJ0rslHD0m/noVGYtSrN4EGD0Iqg85aV3LU3Zk4BAoIBAQDMYPOBcr3P +EvDs4LGp4ehlKKHnvwaAzl7NOYHUwVvFno1brkrRxXf/tw1qLinjHWIzJQwYzDGz +Fx9+2qpw2pdO5uF16l5LcsgZYuM6G4obUzNsUAf3IO0OJ5tcyCFcNWXx/62jZAce +tY3GPpMwyF5U6dNtauFR1q475FPYDuzCwPR+ncX0FTnjejDG4k/5ElKVMq4f/jfz +GQaYDw3dyiAoZ/E850f4dz46uP1nuHMOMu4nC5dvKzI7MGAzWIb7x9fxnS9TeiHS +tVSbD3uFwbgB0rDCQTLFx9awaiFbG2eSjj8fjGGIqXpLt4V2+sxJnjsXq5EGtSIM +Khml7jxDzIudAoIBAQCr8sDG47IUhj8rryZktC8BrcjS5vVyN0pNxdBoC7rGW4pw +uiwcniiZ44/REOhIaaEwcRru+fmoKij45YGviO7/vLKRvMiqG8SSf5Ze9z5aZfHO +KSGUL6CXMv1tUAW8DDSS5ljg+73ntJbIiFulEiogLpLchfr7QLzcuTCgJJdzqIKn +Q+Kt/BVtDUlSV2947A7FukBZpaL8VCbThnTwB2uzCW7eo2KlEpT9qyTECBUtSGE1 +I38HOcoywKUYod+89pZ+7BOnz6FwAptbV/5lb70OTO6ppDUwJDaZQRe1WeJXc4f7 +XZv3ZmsB0djb6eIzbB3XxvUDw+Q/cA+TRFO+jwwBAoIBAQCChpydnKkqWZ9yuSHe +LD/ecy/LsM03cBVNnh+TwBWbRWJkHhD8nc/AiajQmkD4Imj33v41zBsllGvx+TcH +MiOWKY/uXcrxBpJS3DvgfXwbbWyXDRm5M9cRxivL+qiLmjLMdgzwH88IRs0PwiEA +88aDFn6ID81tFd1atLzzbLy6uL6tKpglBXVeGUiTjC+lE5WYYbChEButbtsuO9cQ +Qal+BLwluFPXlFgy6rg0CLG6bL56Q0Xoor7G3GNkB3LV1FDOpyN1fbeiOVqaWS37 +Q7f/ug5XUYpalAFB09YPuJXSHHSl8mLMA+jt+HwNe6kCj0KQIGtTzFS9ThnpRhhI ++JZlAoIBAHoNxnRlxZOsRAUemoL0yMhMhZ2KyoU5QPol85GpPYzpXVjNwY1gktzm +Y6ARz4mS099bRIPR+W32+JfNY2ZRTRcvfDNWmQCcaCzaxKXATdYJVFnHwP11CFOj +LvX8knNBicD36J2eH5ndPkuJYg81fiM8pt88pGimowxqevCLebktuk/My/zmkdvj +u1uwuHi5QmvQhAS9PDsMWE7mUqu5kr4qneijbFZaekcPMeqiJo5jEB6SHtyP4LIG +xGLBVEpJcZqh04/IPUU/w9F1omAgm8620VlzK5MbHmb6pBIPJIO8FeR9tipZOSbf +UfP81uklgtHf9yAd3zWU3GHdnqoMc4s= +-----END PRIVATE KEY----- diff --git a/backend/navigator.py b/backend/navigator.py new file mode 100644 index 0000000000000000000000000000000000000000..6856829140a3c02351cb3312e1619849f36994b9 --- /dev/null +++ b/backend/navigator.py @@ -0,0 +1,228 @@ +""" +Navigator โ€” DQN-based Next Resource Recommender +================================================ +Loads the pre-trained DQN model from Navigators/dqn_model.pth and uses it +to recommend the next best resource for a student to visit. + +Model architecture (inferred from .pth weights): + fc1: Linear(18, 128) โ€” input is a 18-dim state vector + fc2: Linear(128, 128) โ€” hidden layer + fc3: Linear(128, 18) โ€” output is Q-values over 18 topic modules + (ReLU activations between layers) + +State vector (18-dim): one value per topic module, representing the student's + assimilation score (from the polyline) for that module. + +Output: index of the module (0-17) with the highest Q-value among unvisited. + The unvisited resource from that module with the highest reward is returned. +""" + +import os +import numpy as np + +# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +# Model Definition โ€” must match training architecture +# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +_MODEL_PATH = os.path.join(os.path.dirname(__file__), '..', 'Navigators', 'dqn_model.pth') + +_dqn_net = None +_dqn_mode = "unavailable" + +try: + import torch + import torch.nn as nn + + class DQNNet(nn.Module): + def __init__(self, input_dim=18, hidden_dim=128, output_dim=18): + super().__init__() + self.fc1 = nn.Linear(input_dim, hidden_dim) + self.fc2 = nn.Linear(hidden_dim, hidden_dim) + self.fc3 = nn.Linear(hidden_dim, output_dim) + + def forward(self, x): + x = torch.relu(self.fc1(x)) + x = torch.relu(self.fc2(x)) + return self.fc3(x) + + _net = DQNNet(input_dim=18, hidden_dim=128, output_dim=18) + state_dict = torch.load(_MODEL_PATH, map_location='cpu', weights_only=False) + _net.load_state_dict(state_dict) + _net.eval() + _dqn_net = _net + _dqn_mode = "dqn" + print("DQN Navigator loaded successfully") + +except Exception as e: + print(f"DQN Navigator fallback mode (could not load model): {e}") + _dqn_mode = "fallback" + + +# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +# Topic-to-module index mapping (matches nlp_api.py order) +# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +ORDERED_MODULES = [ + "Pre training objectives", + "Pre trained models", + "Tutorial: Introduction to huggingface", + "Fine tuning LLM", + "Instruction tuning", + "Prompt based learning", + "Parameter efficient fine tuning", + "Incontext Learning", + "Prompting methods", + "Retrieval Methods", + "Retrieval Augmented Generation", + "Quantization", + "Mixture of Experts Model", + "Agentic AI", + "Multimodal LLMs", + "Vision Language Models", + "Policy learning using DQN", + "RLHF", +] + + +def recommend_next(visited_ids: list, module_scores: list, nlp_resources: list) -> dict: + """ + Recommend the next best resource using combined DQN + sequential progression. + + The DQN Q-values alone have minimal differentiation, so we combine them + with sequential module ordering for sensible recommendations: + - 70% weight on sequential progression (next module by S.No) + - 30% weight on DQN Q-value ranking + """ + visited_set = set(str(v).strip() for v in visited_ids) + unvisited = [r for r in nlp_resources if str(r['id']).strip() not in visited_set] + + print(f"\n[NAV DEBUG] --- recommend_next called ---") + print(f"[NAV DEBUG] Total resources: {len(nlp_resources)}, Visited: {len(visited_ids)}, Unvisited: {len(unvisited)}") + + if not unvisited: + print("[NAV DEBUG] No unvisited resources remaining!") + return {"resource": None, "module": None, "reason": _dqn_mode, "q_values": []} + + # โ”€โ”€ Build state vector โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + state = list(module_scores) if module_scores else [] + if len(state) < 18: + state.extend([0.5] * (18 - len(state))) + state = state[:18] + state_arr = np.array(state, dtype=np.float32) + + # โ”€โ”€ Group unvisited resources by module โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + module_to_resources = {} + for r in unvisited: + m = r.get('module', '') + if m not in module_to_resources: + module_to_resources[m] = [] + module_to_resources[m].append(r) + + print(f"[NAV DEBUG] Unvisited modules ({len(module_to_resources)}): {list(module_to_resources.keys())}") + + q_values = [] + reason = _dqn_mode + + # โ”€โ”€ Compute sequential scores (which module should come next by S.No) โ”€โ”€ + # Find the highest visited module index to determine progression + visited_module_indices = set() + for r in nlp_resources: + if str(r['id']).strip() in visited_set: + m = r.get('module', '') + if m in ORDERED_MODULES: + visited_module_indices.add(ORDERED_MODULES.index(m)) + + max_visited_idx = max(visited_module_indices) if visited_module_indices else -1 + print(f"[NAV DEBUG] Highest visited module index: {max_visited_idx} ({ORDERED_MODULES[max_visited_idx] if max_visited_idx >= 0 else 'none'})") + + # Sequential score: modules right after the last visited get highest score + sequential_scores = {} + for module_name in module_to_resources: + if module_name in ORDERED_MODULES: + idx = ORDERED_MODULES.index(module_name) + # Distance from next expected module (max_visited_idx + 1) + distance = abs(idx - (max_visited_idx + 1)) + # Score: closer to next = higher score (normalize to 0-1) + # Use asinh to soften the penalty for distance so DQN can override more easily + sequential_scores[module_name] = 1.0 / (1.0 + distance * 0.5) + + # โ”€โ”€ DQN scores (normalized to 0-1 range) โ”€โ”€ + dqn_scores = {} + if _dqn_net is not None: + try: + import torch + with torch.no_grad(): + t = torch.tensor(state_arr).unsqueeze(0) + qs = _dqn_net(t).squeeze(0).tolist() + q_values = qs + + # Normalize Q-values to 0-1 for the modules that have unvisited resources + relevant_qs = [] + for module_name in module_to_resources: + if module_name in ORDERED_MODULES: + idx = ORDERED_MODULES.index(module_name) + relevant_qs.append(qs[idx]) + + if relevant_qs: + q_min = min(relevant_qs) + q_range = max(relevant_qs) - q_min + if q_range > 0.01: # Meaningful differentiation + for module_name in module_to_resources: + if module_name in ORDERED_MODULES: + idx = ORDERED_MODULES.index(module_name) + dqn_scores[module_name] = (qs[idx] - q_min) / q_range + else: + # Q-values are too clustered, DQN can't differentiate + print(f"[NAV DEBUG] Q-values too clustered (range={q_range:.4f}), ignoring DQN scores") + for module_name in module_to_resources: + dqn_scores[module_name] = 0.5 # neutral + + reason = "dqn" + except Exception as e: + print(f"[NAV DEBUG] DQN inference error: {e}") + reason = "fallback" + + # โ”€โ”€ Combined scoring (DQN-forward approach) โ”€โ”€ + WEIGHT_SEQUENTIAL = 0.05 + WEIGHT_DQN = 0.95 + + best_module = None + best_score = float('-inf') + + print(f"[NAV DEBUG] Module scores (seq={WEIGHT_SEQUENTIAL}, dqn={WEIGHT_DQN}):") + for module_name in module_to_resources: + seq = sequential_scores.get(module_name, 0.0) + dqn = dqn_scores.get(module_name, 0.5) + combined = WEIGHT_SEQUENTIAL * seq + WEIGHT_DQN * dqn + + idx_str = "" + if module_name in ORDERED_MODULES: + idx_str = f" (idx={ORDERED_MODULES.index(module_name)})" + + print(f"[NAV DEBUG] '{module_name}'{idx_str}: seq={seq:.3f}, dqn={dqn:.3f}, combined={combined:.3f}") + + if combined > best_score: + best_score = combined + best_module = module_name + + if best_module and best_module in module_to_resources: + candidates = module_to_resources[best_module] + candidates.sort(key=lambda r: -r['reward']) + chosen = candidates[0] + print(f"[NAV DEBUG] โœ“ Chose '{best_module}' โ†’ '{chosen['title']}' (id={chosen['id']}, score={best_score:.3f})") + return { + "resource": chosen, + "module": best_module, + "reason": reason, + "q_values": q_values + } + + # โ”€โ”€ Fallback: next sequential unvisited resource โ”€โ”€ + unvisited_sorted = sorted(unvisited, key=lambda r: int(r['id'])) + best = unvisited_sorted[0] + print(f"[NAV DEBUG] Fallback: '{best['title']}' (id={best['id']})") + return { + "resource": best, + "module": best.get('module', ''), + "reason": "fallback", + "q_values": q_values + } + diff --git a/backend/nlp/nlp_resources.json b/backend/nlp/nlp_resources.json new file mode 100644 index 0000000000000000000000000000000000000000..eedc1aad82515a1019b3190b05f1d84a715665cb --- /dev/null +++ b/backend/nlp/nlp_resources.json @@ -0,0 +1,176 @@ +[ + { + "name": "Pre training objectives", + "description": "### Summary \nThis lecture provides a comprehensive overview of pre-training strategies in natural language processing (NLP), focusing on how pre-trained language models have evolved from static word embeddings like GloVe to dynamic, contextualized embeddings and fully pre-trained transformer models. It begins with the motivation behind contextual embeddings, highlighting the limitations of static embeddings that assign the same vector to polysemous words regardless of their context. The lecture then introduces ELMo, a pioneering method that uses bi-directional recurrent neural networks (RNNs) to generate context-sensitive embeddings by processing sequences from both directions. The working of ELMo is explained in detail, including how it trains with a language model objective and fine-tunes with task-specific layers.\n\nFollowing this, the lecture transitions to transformer-based pre-training approaches, with a detailed explanation of BERT (Bidirectional Encoder Representations from Transformers). BERT uses an encoder-only transformer and introduces the masked language modeling (MLM) objective, where tokens are randomly masked and the model learns to predict these masked tokens using the surrounding context. The lecture highlights the importance of the CLS token for classification tasks and the next sentence prediction (NSP) task used in BERT pre-training to model sentence relationships. It explains BERT\u2019s architecture, training data, and how it is fine-tuned for downstream tasks like question answering by adding classification heads that identify answer spans within passages.\n\nThe lecture concludes by comparing BERT to other models, noting its state-of-the-art status until 2020 and mentioning subsequent developments such as RoBERTa and TinyBERT that improved or compressed the original model. The speaker encourages further reading and exploration of these models and indicates that upcoming lectures will cover other pre-training paradigms like GPT and T5.\n\n### Highlights \n- [00:01:36] The meaning of words is inherently contextual, underscoring the need for contextualized embeddings. \n- [00:02:58] ELMo introduces deep contextualized word representations using bi-directional RNNs, preceding transformers. \n- [00:12:24] ELMo embeddings are fine-tuned for downstream tasks with frozen language model layers and trainable task-specific layers. \n- [00:24:26] BERT uses a masked language modeling (MLM) objective, masking 15% of tokens to enable bidirectional context learning. \n- [00:29:11] Next sentence prediction (NSP) in BERT helps model sentence relationships by classifying whether two sentences follow each other. \n- [00:40:57] For question answering, BERT is fine-tuned to predict start and end tokens of answer spans within passages. \n- [00:47:05] BERT set new benchmarks in NLP tasks, inspiring follow-up models like RoBERTa and TinyBERT that improve or compress BERT. \n\n### Key Insights \n- [00:01:36] **Contextual Meaning is Essential:** Traditional word embeddings like GloVe assign a single vector per word, ignoring polysemy. This leads to ambiguity when the same word has different meanings (e.g., \u201crecord\u201d as a noun vs. verb). This foundational insight drives the design of contextual embeddings that adjust representations based on surrounding words, enhancing downstream task performance. \n- [00:02:58] **ELMo\u2019s Bi-directional RNN Approach:** ELMo demonstrated that contextual embeddings could be generated by training language models with forward and backward RNNs. This approach captures both left and right context, enabling dynamic embeddings that vary with sentence structure. Unlike transformers, ELMo relies on recurrent architectures, which limits parallelization but was groundbreaking for its time. \n- [00:12:24] **Layer Freezing and Fine-tuning in ELMo:** The separation of pre-trained language model layers (frozen) and task-specific layers (trainable) in ELMo highlights a modular transfer learning strategy. Freezing the base embeddings preserves general language understanding, while fine-tuning task layers adapts to specific tasks without catastrophic forgetting. This concept laid the groundwork for later fine-tuning strategies in transformer models. \n- [00:24:26] **Masked Language Modeling Enables Deep Bidirectional Learning:** BERT masks 15% of tokens and trains the model to predict these masked tokens, forcing it to use both left and right context simultaneously. This contrasts with traditional left-to-right or right-to-left language models, enabling richer bidirectional representations. The masking strategy balances context preservation with learning difficulty, avoiding excessive loss of contextual information. \n- [00:29:11] **Next Sentence Prediction Enhances Sentence-Level Understanding:** In addition to token-level MLM, BERT\u2019s NSP task trains the model to understand relationships between sentences, crucial for tasks like question answering and natural language inference. This dual-objective pre-training strengthens BERT\u2019s generalization on sentence-pair tasks. \n- [00:40:57] **Fine-Tuning BERT for Question Answering via Span Prediction:** BERT uses two classification heads to predict the start and end positions of an answer span within a passage. This approach leverages BERT\u2019s contextual embeddings to accurately localize answers, demonstrating how pre-trained models can be adapted with minimal architectural changes for diverse downstream tasks. \n- [00:47:05] **Progress Beyond BERT:** While BERT was revolutionary, subsequent models like RoBERTa showed that more extensive training and data lead to better performance, while TinyBERT and others focus on efficiency. This evolution illustrates the dynamic nature of NLP research, balancing accuracy with computational resource constraints. \n\nThis lecture thoroughly covers the transition from static embeddings to dynamic, contextual language models, illustrating how pre-training strategies have dramatically improved NLP capabilities. The detailed explanations of ELMo and BERT provide foundational knowledge essential for understanding modern NLP architectures and their applications in various tasks.", + "links": "https://example.com/resource/1", + "module": "Pre training objectives", + "module_id": 1, + "submodule_id": 1, + "index": 1, + "transcript": "Hello everyone, welcome to this course! So, in this lecture, we are going to talk about pre-training strategies. Last lecture, we covered transformers and different blocks of a transformer model, right? We also talked about what to wear for glove models that are pre-trained. what embedding methods, right. We will see the overall arena of pre-training with transformers. And then we will also see how the paradigm of pre-training actually changed. From pre-trained word embeddings to pre-trained language models.\n\nLet us look at the pretraining strategy. So, while discussing this, let us start with this famous quote. which we already mentioned earlier in the distributional semantics chapter. In the word embedding chapter, it says that \"you shall know a word by the company it keeps,\" right? So, the same person modified the quote later on, and then he said. That the complete meaning of a word is always contextual. Okay, this is always contextual, and no study of meaning can occur apart from a complete context.\n\nCan be taken seriously, okay? And this is essentially the foundation behind building the pre-training strategies, okay. This is an example; I record the record right. If you look at these two, the two positions of the word \"record\" are right. The meanings are completely different. Now if you use a word-to-way kind of method or a glove-type method which basically produces pre-trained embeddings, right? You will get the same embedding for these two positions of the record, right? But the meanings are different.\n\nOkay. Let's take another example. A bat flew out of the cave. He hit the ball with the bat. Again, here are the two words bat, the two positions of the word bat. The two contexts of the word \"bat\" are different, right? If you use the same embedding to represent this, You will not be able to do the proper processing of it, right? So what will we do here? We will produce embeddings that are going to be contextualized. Meaning, depending on the context, the embedding will change. For example, here let us say that for the word \"bat,\" you will get one embedding.\n\nFor the word \"bat\" here, you get another embedding that is different, okay? How do you do this? The first approach that was proposed is not a transformer-based approach. What was proposed in 2018 is called ELMO. Deeply contextualized word representation. This was done mostly by the LNAI team and some folks from this university as well. One of them is a very good friend of mine. So, the idea behind the ELMO method is as follows. So ELMO is a non-transformer-based approach. There is no concept of a transformer.\n\nTransformers were introduced in 2017, around the same time it was introduced, in 2018. So ELMO stands for embeddings from language models. What does it do? It essentially relies on RNNs, which can be LSTM or GRU. And what it does is that it essentially processes a sequence, right? When you process a sequence using RNN, each of the hidden states Which correspond to basically the tokens, right? These hidden states will basically be used as embeddings, right? Let's say I am going to school. This is my RNN, okay?\n\nIn an RNN, what do we learn? We learn the weight matrix ( W ). Right there, how many matrices are there in general? Generally, there are three matrices; one is a matrix between layers, right? What another is this that corresponds to the input? And the other is this U, which basically projects it to the output space, right? For the time being, you should just assume that there is only one matrix, which is wh. Okay, so you have a fixed-length RNN, right? And then what you do is pass one training instance, okay?\n\nAnd you train this RNN using the language model objective, next word prediction, right? You pass one sequence; you predict the first position of the sequence. Second position, third position, fourth position, and so on, right? You do the backdrop and then you update WH, right? Then again, you pass another sequence; you update this, and so on and so forth. Now think about it: what is happening here? So the claim is that the hidden state will act as an embedding. The hidden state here, let's say this hidden state will act as a\n\nEmbedding for the word \"to.\" Now you may wonder how it would be possible because There are so many sequences; there is only one RNN. So let's think of a dictionary where every row indicates. an embedding of a particular token. Let's assume that this is a WAD level token. Not a character-level WAD-level tokenization. So there is an entry I, and this is that vector. Entry J, this is a vector, and so on and so forth. And let us assume that this matrix is initially randomly initialized, okay?\n\nNow, when you start this RNN, you need to feed the input correctly. This input is fetched from the dictionary. So when the input is \"I,\" you basically fetch the corresponding embedding vector. From the dictionary, you feed it as input. Right to start with, then you train the model. Let's say you take one instance; you train the model. your hidden states are updated right, The hidden state corresponding to the word I is also updated, right? So when the training process is done, you take that hidden state.\n\nand you replace the previous embedding of i by this hidden state done. Another instance, another training sentence comes in; you use the same RNN, okay? Let's say she is eating a pizza, okay? So what do you do first? How do you initialize this? You take the embedding of her from the dictionary, right? Eating and so on and so forth, right? You already have this weight matrix, which came from the previous iteration; you again update it. You update the hidden states, and the corresponding dictionary is updated.\n\nYou keep doing this thing with all these instances. Now you can say that, you know, I will train the model using batches. That is also possible. You take a batch, and then you do this update, or let's say, per instance. You basically update this RNN. Okay. So this is a language model objective-based RNN model. There are no tasks involved so far. Now, so this is, now what you do, so this is your first RNN, which is a language model of the TBH RNN. When all these things are done, you freeze this RNN.\n\nWhen you freeze this, what does it mean? It means all the embeddings are frozen, right? And now let's say you are interested in doing some tasks. Okay. And let's say you have some task-specific data. You have some task-specific data that you want to use to train the model further. Some sort of fine-tuning. So on top of this, you apply another RNN which will be responsible for doing this task. Now, let's say the task is sentiment analysis. This is sentiment analysis. So this is frozen.\n\n\"It is frozen means that the hidden states are coming from this, the first layer.\" So given the task-specific input, all the token embeddings are coming from this layer. which are frozen, right? And here in the second layer, you have another weight matrix, W'H. which corresponds to that layer. And you have the task, you produce some output, and there's some error, right? You backpropagate and update the weights. The task-specific weights are updated. So during the inference time, let's see what is given a sentence, right?\n\nWhat do you do first? You're given a sentence, then you retrieve it first. Now, remember, there are two layers. Don't forget that there are two layers. The first layer is a language model layer. The second layer is a task-specific layer, right? During the inference time, what do you do? During the inference time, you feed here, right? You basically feed here. The bottom layer, the corresponding weight wh, is already stored. It has already been learned correctly. So let's say you are feeding a new sentence.\n\nLet's say Akshay is a great cricket player, okay? So what you do is, this is my first layer, okay? So you feed the input vector of Akshay from the dictionary, right? You multiply this by the weight wh. Right, it will, and then Akshay is then. You also have this embedding for the word \"is.\" The previous hidden state will basically be used to generate the next hidden state. Right, and so on and so forth, you roll out. Okay, when you roll out, now let's look at the current hidden states now,\n\nNow, these hidden states\u2014the current hidden states of the bottom layer\u2014are very different. from the hidden states which are stored earlier, Because these are multiplied by, these are basically functions of the weight matrix, right? and this is contextual. Why is this contextual? Because the word \"great\" is now dependent on the previous words, which are \"Akshay is.\" Okay, maybe the word \"great\" was used in some other context. But in this context, it is a function of the previous words.\n\nClear. So the bottom layer will produce some hidden states, right? Now these hidden states will be moved to the to the task specific layer. And the task-specific layer, we already know the corresponding weight. which was learned earlier, that will be used to predict the task. Okay. Now there are some changes here and there. What I mentioned is a very high-level overview of this. Let's look at the changes, but the bottom-line stories should be clear, right? There are two layers. One layer is responsible for the language model objective.\n\nThe top layer is responsible for tasks, isn't it? Here you store the hidden states as embeddings of the tokens, right? And then the embeddings will be changed when you apply them to a specific context. It will change, okay. RNN is not a very good approach. I mean, a unidirectional RNN is not a very good approach. Because it only scans from left to right. As you understand, for a language model objective, why do we need to just scan? Because I want to process the entire sentence. So, along with the left-to-right RNN, I will also use a right-to-left RNN.\n\nThis will process the entire sentence. Now left to right RNN will process, will basically predict the next word From left to right. Right-to-left RNN will basically predict the word from right to left. Okay. Now, I mean, there are two ways to essentially do this. Let's say you have access to only a left-to-right RNN, Not an R, right to left RNN. What can we do? You can just rotate the sentence right as I am going to school. I am going to school and then you run a left to right RNN. Okay. In fact, what they did was use a stacked RNN.\n\nThere are two layers. Now, this RNN that I'm talking about is a language model RNN. The bottom RNN that I mentioned earlier. So the bottom RNN was essentially a stacked RNN. There are two layers, right? You see here, this is one RNN called the forward language model. There are two layers: layer 1 and layer 2, right? And this is a backward LM backward RNN; here also there are two layers. Layer 1 and layer 2 are right; a cute puppy and a cute puppy are okay. Left to right and right to left. What is going to be my final hidden state that I am going to store?\n\nInto the dictionary, the final hidden state corresponding to the word \"cute\" is right. It can be either the concatenation of this and this, or it can be. Let's say only the top layers; this plus this plus means concatenation. Right there can be multiple ways to combine this. Let's look at it here. So you have a left-to-right RNN, okay? Look at this equation very carefully. So this is a language model objective. So you are given a sequence of tokens t1, t2. Dot dot dot, tk minus 1, you are predicting tk.\n\nWhat is this? This is essentially the LSTM; I mean the RNN parameter, which is right. the all the other parameters meaning the let us say we, Right, all the other parameters in the RNN will remain the same. What is the parameter corresponding to the input, right? In this case, this is theta x. This will remain the same in the forward LSTM and the backward LSTM. Okay, all the other parameters, what is this theta S? What is this theta S? Theta S is the softmax parameter. This will also remain the same in both forward and backward.\n\nSo the only parameter that is going to change is the WH, okay? So then you essentially add this to look at here. This is, I think, a better way to present it. So for every token K, you get one representation from the forward layer. And one representation from the backward layer. Either you just concatenate them, or you don't. So what they argued is that instead of taking this embedding, This embedding and this embedding, instead of taking four embeddings for a particular token, We will only use the top layers, not the bottom layers.\n\nWhy are bottom layers not that interesting? So after the top layers, we have this U matrix and the softmax. So when you do backprop right, this layer will be affected more. compared to the bottom layer. So, task-specific weight is just above the top layer. I will only use the top two layers, and the corresponding vectors will be fetched. and concatenated okay. In fact, what they said is that instead of doing this, You can also add some parameters. So along with this, which is the right combination of backward and forward,\n\nYou add a parameter that is specific to this layer. Look at your layer j, which ranges from zero to n. And there are two parameters. One is this gamma was this is right. Now this is corresponds to a specific layer. And this gamma corresponds to a specific task. What does it mean? It means that when you are on top of this language model layer, When you apply a task-specific layer, right? I mentioned earlier that the language model was going to be frozen. Right? What they said is that instead of freezing it completely,\n\nYou add some parameters that will be dependent on the task. Okay, this hidden state will not be updated. But the parameters corresponding to the hidden states will be updated. It means during the inference time, when you feed a specific instance. for that specific task, you basically fetch is s and gamma using because these parameters are already learned. So now, your hidden states are little bit dependent on the parameters of the task. Now, if you want, you can discard this dependency. You can just remove these class-specific parameters and only use the hidden state, right?\n\nBut if you feel that it's okay, you know some dependencies should be there. you can use this right. Some other properties of this ELMO use token label representation. This token is essentially a character label token, not a word-level token. Okay. So for that, they use CNN, right? Uh, two zero four eight characters, CNN, conv layer. And then, you know, some linear projections and so on and so forth. Because kettlebell encoding always helps you overcome the UNK kind of problem, right? On the token problem.\n\nOkay. Let's look at the results very quickly. So ELMO was one of the first large-scale models, okay, million-size models. I think it's 90 million or something like that; I forget the exact size. It was tested on different tasks, including question answering tasks. Squad question answering task, the SQuAD dataset was proposed by Stanford. Again in 2017 and 2018, during that time, we'll talk about it later. Another task was the natural language inference task, semantic role-leveling task,\n\nCo-reference resolution task, named entity recognition task, sentiment analysis task. SST-5 is a very famous dataset. And if you look at the results, there is always an increase. In fact, in some cases, the increase is quite significant compared to the other models. Though this is the previous SOTA output, the right state of the. art model and this is ELMO. In fact, you can use ELMO to initiate the transformer, right? In a transformer, you also need to feed embeddings, right? Instead of using Watt2vec, you can essentially use ELMo's representation, okay?\n\nSo this is all about ELMO. So ELMO is yet another contextual word embedding technique. Dynamic, unlike what it means to be a glove, which is static, right? Context-independent. So we have been talking about pre-trained word vectors. In pre-trained word vectors, your embedding layer is frozen, correct? And your task-specific layer is essentially something that you are interested in, okay? And this part is pre-trained. The model is not pre-trained; the entire model is not pre-trained; only the inputs are pre-trained, right?\n\nNow, from pre-trained word vectors, we are moving to pre-trained models. In the pre-trained model, all the parameters are pre-trained, correct? All the parameters in a transformer model are the parameters. Corresponding to the attentions, right? Parameters corresponding to the feedforward network are all pre-trained. Right then, for a specific task, you can fine-tune it, okay? So let's look at the three kinds of pretrainings. That we are interested in right depends on which part of the transformer.\n\nWe are interested, right? Transformer: there is an encoder block and a decoder block. So one type of pretraining only considers the decoder part of the transformer. So all these GPT series models are only decoder-only models. decoder based models, right. They do not consider the encoder part; therefore, this cross attention is not present. The other type of models is encoder-only models where we only look at the In the encoder part of it, we forget the decoder part and then we pretend it's an encoder model.\n\nBERT, ALBERT, and ROBERTA are all encoder-only models. And then the normal structure, which is the encoder-decoder model, The actual transformer model can also be used for pre-training. So GPT comes here. The BERT series model comes here. And T5, another model is called BART, B-A-R-T. These are all basically encoder-decoder models. So let's take a look at this BERT model proposed by Google. So BERT stands for Bidirectional Encoder Representations from Transformers. And this is the paper. So now we have only the encoder part of the Transformer model.\n\nAnd now we are creating this encoder component. The beauty of the encoder part is that there is no masked self-attention. This is unmasked self-attention, meaning all the tokens, Each of the tokens can attend to all the other tokens in the sequence. So it is some sort of bi-detection in nature because you can scan. From left to right, right to left, and whatever. So in BERT, when you pre-train, what we do is essentially now. For pre-training, what is my objective? Because I don't have any task-specific data.\n\nI only have access to the raw corpus, right? Raw corpus; raw sentences. What is going to be my objective? So in this paper, they came up with an objective called the masked language model, MLM. Now this is a self supervised approach. Not an unsupervised approach because there is some gradient flow. In a self-supervised approach, you don't need any labeled data. Right? So what you do in BERT in the encoder part is that you feed a sentence, right? And you mask some of the tokens in the sentence.\n\nAnd let's say, for the time being, the tokens are words. You mask some of the tokens and let the model predict those tokens. Okay. Now there are so many questions. For example, which tokens should I mask, and how many tokens should I mask, right? And so on and so forth. So, these are all empirical questions. And what they showed is that 50% masking is sufficient. So you mask 15% of the tokens present in the training set, right? But when you mask it, you mask it in three different ways. Okay, out of this 15%, 80% of the cases, right?\n\nYou basically just mask that token, right? So what does it mean to mask a token? So they introduced a new token called \"mask,\" right? A new token called mask. when you feed, you replace a token by mask. And at that position corresponding to the final output, you predict What is going to be the actual word in place of \"mask\"? So 80% of the time you mask some of the tokens, 10% times you replace that token by another token. Let's say the sentence is like going to the park.\n\nYou replace the word \"park\" with \"store\" and You let the model predict the word \"park.\" This is some sort of corruption. You perform some sort of corruption and let the model predict the actual word. And 10% of the time you don't make any changes. But still, you let the model predict it. Your input is like going to the park; right? You let the model predict toward the park. Why is this important? Why is this important? This is important because if you only teach the model to predict the masked token,\n\nThe model will basically start forgetting to predict the actual tokens. So sometimes you also need to, you know, tell the model that, look, I mean, you are learning this mask output, but you also need to understand. what is there in the other tokens. Okay. So why 15%? Why not 50%? If you mask 50% of the tokens, right? Most of the context will be destroyed. Your context; you also need some context, right? The context will be gone, won't it? So they empirically tested that 15% was a good number.\n\n15% of a 1 billion or a 10 billion size token is good enough. This is my sentence. Let's say, for example, going to the park; you fit this. You replace this with a mask, and you want the model to predict something like going to the park. So your error is only computed here. not at the other tokens, other token positions. This is very important, isn't it? Now this is not all about what I'm coming to; there are other parts to it. So mask language modeling is clear, clear. So one task is this masked language prediction, okay?\n\nMask language model. So they actually use two tasks. One is this mask language prediction, and the second is next sentence prediction. What is the next sentence prediction? You give two sentences, right? You have this corpus, you know. Which sentences are actually there side by side, you know, the two consecutive sentences? You take two sentences, w1 and w2, and you let the model predict. Whether W2 will follow W1 or not. So what do you do for the training data? You take those pairs of sentences that actually appear side by side, right?\n\nAs your positive samples and your negative sample, you do random shuffling, right? And you basically take those pairs that do not appear side by side. I enjoy reading the book. I finished the novel. These two sentences appear side by side. So the label is yes; otherwise, the label is no. So these are two tasks, and we will do them simultaneously. Meaning, let us say the input is \"I enjoyed reading the mask.\" Okay, how do I separate these two sequences? This is sequence one; sequence two, the novel was great, right?\n\nWe separate them using another token called the SEP separator. This separator is a new token, like a mask token. And again, you may have a separator here. So you introduced two new tokens. Along with this, we introduce another token called the CLS token. And this is prepended with the sequence. This stands for classification. Okay, so you feed this entire thing to the encoder block, right? What is the need for the CLS token? Now, let me first tell you what will happen after this. So the model will predict the vectors right at the end of the right vector.\n\ncorresponding to the CLS vector corresponding to I, and so on and so forth. It will also try to predict this masked word on top of this CLS. I will use a task-specific head, meaning basically a linear layer view, right? which will project it into two classes: yes or no. Okay, if two sequences are sequential, then it will predict yes; otherwise, no. There are two losses; one loss is here, and another loss is here. mask and those two losses will be back-promoted. Now my question is, why do we need this CLS token?\n\nCan't we just take the embeddings of all these tokens and then concatenate them? And then pass it through a linear layer followed by a non-linearity. Okay, what is saying that we can't take the concatenation because? We don't essentially know this one, the actual masked token, right? Okay, that's fine. Then we just ignore the masked token and somehow combine the other tokens. So the other tokens are good enough. Only 15% are masked. Why is this CLS token? So remember, the CLS token also acts as a normal token.\n\nMeaning, CLS also attends to all the other tokens. In the self-attention part, it attends to all the other tokens. So CLS has information about all the tokens, doesn't it? But the CLS token is a token that doesn't indicate any meaning present in the sentence. Because this is not a part of the sentence at all. You may argue, \"Why do I need a classification layer on top of CLS?\" I can have a classification layer on top of me, on top of read. They have also attended to all the other tokens, right?\n\nBut they themselves have some meanings. A CLS is a token that is not present in the sequence at all. So it is not biased. That token is not biased. The CLS token is not biased toward any of the other tokens present in the sequence. Whereas the token I is biased toward itself. Could it be high, right? In fact, there is another plus point, you know, to using CLS for the classification. How many weights are you incorporating here? Apart from the, you know, the normal encoder weights, Self-attention feedforward weights are already there in the encoder.\n\nFor this classification, how many weights are you incorporating? Parameters, you are incorporating, only this part, right, only you, And what is your size? If the vector size is 512, this is 512 times 2, right? Instead of this, if we had a concatenation of tokens, it would be bigger. Right, the parameter size can also be decreased. Now this error, which is the task-specific error, will get back-propagated end-to-end. The mask, the error related to this mask token, and the error. Which is there on top of this CLS token, those two errors will be backpropagated into it.\n\nAnd all the encoder blocks will be updated. Now this is an encoder; you can use this encoder block for word embeddings as well. Like Elmo, you can use Bert for contextual word embeddings. Okay? All right, so this is the graphical depiction to look at here. E0, E1, E2, E3, E4 are basically E1 to E4 from this to this. This is one sequence; from this to this is another sequence, and this to this is another sequence. My dog is cute; he likes playing. Right? You have a position encoding, right? So here, they introduce another encoding called segment encoding.\n\nSegment encoding is just indicating whether this token is. It comes from the first sentence or the second sentence, right? So segment A is this and segment B is that. So, position encoding, segment encoding, and from that, you also have this normal token encoding. This can come from ELMO, from word2vec, and so on and so forth. And then you have the CLS prepended, the SEP separator, and so on and so forth. Now this is my input sequence, you mask some of them and you feed. So these are the training details.\n\nThey used a 2.5 billion Wikipedia corpus and another book corpus consisting of 800 million words. They tested two different variations of BERT. Bert base consists of only 12 layers of encoders. And BERT large consists of 24 layers of encoders with different hidden state sizes. and different multi-headed attention. 12 heads, 16 heads, right and so on and so forth. There are other epochs, and so on. Other details are also mentioned here. Okay. Now, if I ask you, can we use BERT for downstream tasks?\n\nAnd let's say the downstream task is question-answering. Okay. What is the question-answering task? You are given a question queue. Okay. And let us say who played the role of Vikram Rathore in \"Jawan.\" Okay, this is the question, and this is the passage. You have a passage where all these things are written. Somewhere in this passage, it is written that the role of Vikram Rathore. It was played by Shah Rukh Khan. This is the passage. And your task would be to essentially retrieve this information from the passage.\n\nNow tell me, how do we use BERT for this kind of task? Okay, this is a typical closed-book kind of question-answering task. Where you are given, there are so many data sets; SQuAD is one data set, right? There are other data sets like Quark, aren't there? Hotpot, QA, there are so many datasets available. Some data sets look like this; some data sets are basically conversational data sets. Where you have a conversation, you ask the question and retrieve an answer from the conversation. How do we use the BERT model to solve this kind of task?\n\nWhat do you think? The BERT model is already pre-trained using a mass language model. Now, when you fine-tune the model for this task, you already have some training data. where the question and the corresponding passage are the input and the output is the specific token. So, what do you do here in the BERT model? This is BERT, you add up to this model for specific task, This is one such task, okay? So during training, you feed the question queue the right separator. And the passage P separator, okay, and you have CLS, okay.\n\nSo, you have different tokens here. You have tokens, let's say token T1, token T2, token T3, and so on, right? So T60, and so on and so forth. Then what you do is add a classification layer on top of every token. In fact, you add two classification layers on top of every token, right? One classification layer will be responsible for. I mean, you can think of this specific phrase as a span. Right? So this is the start of a span; this is the end of a span. So here in this passage, there is a word called Shah Rukh Khan, right?\n\nAnd the corresponding embedding of Shah, and the corresponding embedding of Rukh, corresponding embedding of Khan will also be present here. Right, you maintain two different classification heads. One classification head will basically keep track of the beginning of the span. Another will keep track of the end of the span. Let's say you have C1; it's a CB, CE. So, CB for all these tokens will be 0, 0, 0, 0, 0, 0. During training, right and for the word \"Sa,\" right. Cb is going to be 1, and Cu will be 0.\n\nThen the other, the Rukh, will be 0, 0. Khan will be 0 and 1, and the remaining will be 0, 0, 0. This is my ground-truth data. I want the model to predict these labels correctly. Now you feed the input to the model, and the model will produce the embeddings, right? And those embeddings will be fed to the classification layer. Classification layers will essentially produce probabilities. There are two probabilities. One is the probability that the current token acts as a beginning. and the current token acts as an end.\n\nSo my hope will be that the token corresponds to SA. The PB for the token SA is going to be 10. To 1 that tends to 1 and the PE corresponds to Khan tends to 1. If it doesn't happen, then there's a loss, and that loss gets back-promoted. Right, task-specific layers meaning these two layers, right? These two layers will mostly be updated. And the other pretrained layers can also be updated, okay. So now this, the model is now added to specific task. When a test sentence comes in, it includes a test sentence, the test question, and the passage.\n\nWhat do you use this pretrained model for, pretrained followed by fine-tuned? Whatever the model is that is there, right? You passed the sequence, right? It will produce P, B and PE for all these tokens. And our task would be to maximize what? The task would be to figure out two such positions. such that pb times pe will be maximized. Now think about it: for every token, you have two probabilities, pb and pe, right? All there are so many possible combinations are there, right? pb times pe. So pb1 times pe2, right? pb3 times pb, pe1, and so on and so forth.\n\nAll these combinations are possible. Which combination will I take? I will take that combination where, number one, the int token, I mean. Let's say you have chosen i and j as two different positions, right? And PB comes from I, and PE comes from E; PE comes from J. j should always be greater than i. Of course, the end token should come after the beginning token, right? This is the first condition, or whatever gets them equal to. Because only one token can also be possible, okay. And then, you know, considering this condition, I want to maximize this probability.\n\nPb times P. Now you may ask if there are multiple appearances of the word in a passage. Shah Rukh Khan, so which one should I consider? So generally, you consider the beginning, the first appearance of the words that have come. So in this way, you can essentially use a pre-trained BERT model for a downstream task. Like the question-answering task, which apparently looks very different from the pre-training objective. Now, this is all about BERT. It was tested across multiple tasks, wasn't it?\n\nThis question-answering task is a natural language inference task. Remember, for each of these tasks, you need to adapt your model accordingly, right? This is the cola benchmark, which is a corpus of linguistic acceptability. It basically indicates whether a sentence is grammatically correct or not, right? STS benchmark, SPET benchmark, and so on. And it showed that what is large significantly improves the tasks, right? Compared to the other baselines, which were shorter at that time. It also beat the GPT-1 model, okay?\n\nBERT had been one of the state-of-the-art models until 2020. And following BERT, there were approaches like D-BERT, RobERT, and TinyBERT. where people started squeezing the model, making it tiny. Then this RoBERTa paper says that the BERT model is undertrained. You need to train them more and more. and so on and so forth. There are papers in which people essentially feed a lot of training data across multiple tasks. People also showed that when you keep adding new tasks, Whether the model will be generalized or not, and so on and so forth.\n\nSo this is one direction of research. I will not go into the details of the follow-up on BERT. But I will strongly suggest that you guys read those papers. At least use those models in your projects. With this, I stop here and In the next lecture, we will try to address the other types of pre-trainings. like GPD and T5. Thank you." + }, + { + "name": "Pre trained models", + "description": "### Summary of Video Content on Pre-training Strategies in Language Models\n\n---\n\n#### - [00:00:14 \u2192 00:04:12] Introduction to Pre-training Strategies: Encoder-Decoder and Decoder-Only Models\n\n- The lecture begins by recapping **encoder-only models** (covered previously), specifically the **BERT model**, which uses a **masked language model (MLM)** pre-training strategy.\n- **BERT** is a **bidirectional encoder-only** model where some tokens in the input are masked, and the model predicts these masked tokens using **self-attention** over all tokens (non-autoregressive).\n- The focus shifts to two other strategies:\n - **Encoder-decoder models** (e.g., **T5**, **BART**), which have both encoder and decoder components.\n - **Decoder-only models** (e.g., **GPT series**, **LLaMA**), which are **autoregressive** and only use the decoder stack.\n- Decoder-only models are popular for **generative tasks** (like text generation) because autoregressive modeling fits naturally with next-token prediction, requiring fewer parameters than encoder-decoder models.\n- Encoder-decoder models use **bidirectional encoding** but autoregressive decoding.\n- Overview of transformer architecture:\n - Encoder: multiple blocks with multi-head attention (unmasked) and feedforward layers.\n - Decoder: multiple blocks with **masked multi-head attention**, a **cross-attention** layer attending to encoder outputs, and feedforward layers.\n\n---\n\n#### - [00:04:12 \u2192 00:09:13] Encoder-Decoder Pre-training and Architecture Details\n\n- Encoder-decoder models work by feeding input sequences (possibly with instructions/prefixes) to the encoder and output sequences (e.g., summaries) to the decoder.\n- The decoder performs **next-token prediction** based on previous outputs and attends to encoder hidden states via cross-attention.\n- This setup is similar to the **vanilla transformer model** originally proposed for machine translation.\n- Pre-training objectives can be **next-word prediction** or **sentence classification**, depending on task framing.\n- A challenge is to use **unlabeled data** in encoder-decoder pre-training, unlike supervised tasks requiring labeled pairs.\n- The architecture facilitates tasks like summarization or sentiment analysis by including task instructions as part of encoder input.\n\n---\n\n#### - [00:09:13 \u2192 00:17:06] BART Model: Bidirectional and Autoregressive Transformer\n\n- **BART (Bidirectional and AutoRegressive Transformer)** combines BERT\u2019s bidirectional encoder with autoregressive decoder training.\n- Pre-training involves **corrupting input sequences** with noise and training the model to reconstruct the original sequence.\n- Corruption/noise strategies include:\n - **Masking tokens** (like BERT).\n - **Permuting tokens**.\n - **Rotating sequences**.\n - **Token deletion** (input with some tokens deleted, decoder predicts the full original).\n - **Masking consecutive spans of tokens** (variable-length masked spans sampled from a distribution).\n- BART\u2019s approach is **self-supervised**, requiring no labeled data.\n- The model is fine-tuned on downstream tasks like summarization, improving performance with less labeled data compared to older sequence-to-sequence models.\n- The training objective is to predict the **entire original sequence** given the corrupted input, differing from BERT\u2019s masked token prediction.\n\n---\n\n#### - [00:17:06 \u2192 00:27:34] T5 Model: Text-to-Text Transfer Transformer\n\n- **T5 (Text-to-Text Transfer Transformer)** is an ambitious encoder-decoder model by Google that frames **all NLP tasks as text-to-text problems**.\n- Every task (translation, classification, summarization, etc.) is converted into a text input and text output, with explicit **task instructions/prefixes**.\n- Examples of T5 tasks:\n - Translation: Input includes prompt \"translate English to German\" + English sentence; output is German translation.\n - Linguistic acceptability (CoLA dataset): Input sentence + instruction; output is \"acceptable\" or \"not acceptable\".\n - Sentence similarity (STS-B dataset): Input is two sentences + task prompt; output is a similarity score as text (e.g., \"3.8\").\n - Summarization: Input document + instruction; output is a summary.\n- T5 pre-training uses **span-masking** with **sentinel tokens**, different from BERT\u2019s single mask token:\n - Masked spans are replaced with unique sentinel tokens.\n - The decoder predicts only the masked spans (not the entire sequence).\n- T5 training data is the **C4 corpus**, a cleaned and filtered version of Common Crawl data (~745 GB), with extensive noise removal (non-English texts, HTML, duplicates).\n- The model uses a **BERT-sized encoder-decoder transformer** (e.g., 6 encoder + 6 decoder layers) for comparison purposes.\n- Pre-training is **supervised** on many tasks, unlike BART\u2019s unsupervised denoising.\n- T5 is fine-tuned on a variety of benchmarks (GLUE, SuperGLUE, CNN/DailyMail summarization, SQuAD QA, and machine translation datasets).\n\n---\n\n#### - [00:27:34 \u2192 00:36:39] T5 Fine-tuning and Benchmark Performance\n\n- The GLUE and SuperGLUE benchmarks contain multiple tasks such as:\n - COLA (linguistic acceptability)\n - SST-2 (sentiment analysis)\n - MRPC (paraphrase detection)\n - STSB (sentence similarity)\n - QQP (question paraphrase)\n - MNLI (natural language inference)\n - QNLI (question answering)\n - RTE (recognizing textual entailment)\n - WNLI (pronoun disambiguation)\n- T5 was fine-tuned and evaluated on these tasks, showing competitive or better performance compared to BERT.\n- Experiments revealed:\n - Pre-training significantly improves downstream performance compared to training from scratch.\n - Large machine translation datasets (e.g., English-French) sometimes do not require pre-training due to dataset size.\n - Cleaning the pre-training corpus (C4) improves accuracy by 2-3%.\n- Different architectural variants compared:\n - Encoder-decoder with separate parameters (P for encoder, P for decoder).\n - Encoder-decoder with shared parameters (halving total parameters).\n - Decoder-only autoregressive model.\n - Prefix language model (combining masked and autoregressive attention in one stack).\n- Results indicated:\n - Larger models with more layers perform better.\n - Encoder-decoder models outperform others.\n - Shared-parameter encoder-decoder models have performance close to prefix-LM.\n- Various pre-training objectives tested:\n - Span replacement (denoising).\n - Token dropping (deletion).\n - Token replacement.\n- Span replacement and token dropping showed the best results, with token dropping speeding up training due to shorter output sequences.\n\n---\n\n#### - [00:36:39 \u2192 00:44:34] Prefix Language Model and Pre-training Objectives\n\n- **Prefix language model** is a hybrid:\n - Part of the input tokens have unmasked attention (like BERT).\n - The rest are autoregressively masked (like GPT).\n - Unlike encoder-decoder, prefix-LM uses a single stack.\n- Pre-training strategies examined include:\n - BERT-style masked language modeling.\n - Span corruption and replacement.\n - Token dropping.\n - Reshuffling.\n- No clear winner strategy, but span replacement and token dropping are more favorable.\n- Token dropping leads to shorter target sequences, reducing computational cost.\n\n---\n\n#### - [00:44:34 \u2192 00:53:15] Decoder-Only Models: GPT Series and LLaMA\n\n- Decoder-only models use **autoregressive next-token prediction**.\n- Architecture:\n - Input tokens up to position t-1 used to predict token at position t.\n - Output is a probability distribution over vocabulary tokens via linear + softmax layers.\n- GPT Evolution:\n | Model | Year | Parameters | Layers | Hidden Dim | Dataset Size/Type | Key Innovations |\n |--------|-------|------------|--------|------------|-------------------------------------|---------------------------------|\n | GPT-1 | 2018 | 117M | 12 | 768 | BookCorpus (~7k books) | Transformer decoder-only model |\n | GPT-2 | 2019 | 1.5B | *Not specified* | *Not specified* | Common Crawl + WebText | Larger context, zero-shot learning |\n | GPT-3 | 2020 | 175B | 96 | 12,000 | Massive web-scale | Few-shot/in-context learning |\n- GPT-3 introduced **few-shot learning**: no fine-tuning needed; tasks are specified via examples in input.\n- GPT-4 (2023) remains a **black box**; exact architecture unknown.\n - Training cost and token processing cost 15-30x higher than GPT-3.\n - Includes **instruction fine-tuning** and **Reinforcement Learning with Human Feedback (RLHF)** to improve alignment and instruction following.\n- **LLaMA models** (open source) follow similar decoder-only architectures:\n - Multiple versions (LLaMA 1, 2, 3) with increasing size.\n - Use byte-pair encoding and rotary positional encoding.\n - Training data includes Common Crawl, GitHub, Wikipedia, books.\n- Decoder-only models are efficient for generative tasks, with simpler architectures and autoregressive training objectives.\n\n---\n\n#### - [00:53:15 \u2192 End] Future Directions: Instruction Fine-Tuning and RLHF\n\n- The next lecture will focus on **instruction fine-tuning**, a critical step after pre-training.\n- Instruction fine-tuning helps models learn to follow human instructions more effectively.\n- **RLHF (Reinforcement Learning from Human Feedback)** is used for aligning model outputs with human preferences.\n- These steps improve model usability and safety.\n\n---\n\n### Key Insights and Conclusions\n\n- **Pre-training strategies differ mainly in architecture (encoder-only, encoder-decoder, decoder-only) and training objectives (masked token prediction, denoising, autoregressive).**\n- **Encoder-decoder models like BART and T5 leverage self-supervised denoising or supervised text-to-text frameworks for flexible NLP tasks.**\n- **BART uses noise injection (masking, permutation, deletion) and reconstructs full sentences; T5 frames all tasks as text-to-text with span masking and sentinel tokens.**\n- **Pre-training on large, clean, diverse datasets (e.g., C4) significantly boosts downstream performance.**\n- **Decoder-only models like GPT are simpler, focus on autoregressive next-token prediction, and scale massively (GPT-3 with 175B parameters).**\n- **Instruction fine-tuning and RLHF are crucial post-pre-training steps to align models with human expectations.**\n- **Prefix language models offer hybrid attention mechanisms combining bidirectional and autoregressive components in one stack.**\n- **Pre-training objectives like span replacement and token dropping improve efficiency and effectiveness.**\n- **Large-scale experiments require significant computational resources, typically only accessible to large organizations like Google and OpenAI.**\n\n---\n\n### Glossary\n\n| Term | Definition |\n|---------------------------|----------------------------------------------------------------------------------------------|\n| Masked Language Model (MLM) | A training objective where some tokens in the input are masked, and the model predicts them. |\n| Autoregressive Model | A model that predicts the next token based only on previous tokens, not future ones. |\n| Encoder-Decoder Model | Transformer architecture with separate encoder and decoder stacks, used for sequence tasks. |\n| Self-Attention | Mechanism allowing models to weigh importance of different tokens in input sequence. |\n| Cross-Attention | Decoder attention mechanism attending to encoder outputs. |\n| Span Masking | Masking consecutive sequences of tokens instead of individual tokens. |\n| Sentinel Tokens | Unique tokens used in T5 to mark masked spans during training. |\n| Denoising Objective | Training objective where corrupted inputs are used to reconstruct original sequences. |\n| Prefix Language Model | A model with mixed masked and autoregressive attention in a single stack. |\n| Reinforcement Learning from Human Feedback (RLHF) | Technique for aligning model outputs with human preferences using reinforcement learning. |\n\n---\n\n### Timeline Table (Chronological Milestones)\n\n| Year | Model/Concept | Description |\n|-------|-----------------------|---------------------------------------------------------------------------------------------|\n| 2018 | GPT-1 | First decoder-only large transformer model; 117M parameters, trained on BookCorpus. |\n| 2019 | GPT-2 | Larger model (1.5B params), longer context, zero-shot multitask learning, Common Crawl data.|\n| 2020 | GPT-3 | Massive scale (175B params), introduces few-shot, in-context learning. |\n| 2020 | T5 | Encoder-decoder model framing all NLP as text-to-text tasks, trained on cleaned C4 corpus. |\n| 2020 | BART | Encoder-decoder denoising autoencoder with multiple corruption strategies for unsupervised pre-training.|\n| 2023 | GPT-4 | Black box decoder-only model, instruction fine-tuning, RLHF, significantly higher cost. |\n| *Not specified* | LLaMA | Open-source decoder-only models with various sizes and data sources. |\n\n---\n\n### Final Notes\n\n- The video provides a comprehensive overview of **modern pre-training strategies**, explicitly comparing different architectures and training objectives.\n- It emphasizes the evolution from **encoder-only masked language models (like BERT)** to **encoder-decoder denoising models (BART, T5)** to **decoder-only autoregressive models (GPT series, LLaMA)**.\n- The importance of **large-scale, clean datasets** and the **design of training objectives** (masking, span corruption, token deletion) is stressed.\n- The lecture sets the stage for upcoming discussions on **instruction fine-tuning** and **alignment techniques** essential for deploying practical, instruction-following language models.\n\n", + "links": "https://example.com/resource/2", + "module": "Pre trained models", + "module_id": 1, + "submodule_id": 2, + "index": 2, + "transcript": "Hello, everyone. Welcome back. So we were discussing different pre-training strategies, and in the last lecture, we discussed. You know, pre-training and encoder-only models, specifically, we discussed the BERT model, right? So, in today's class, we will focus on two other pre-training strategies. One is encoder-decoder-only models, right? Models like T5 and BART. So B-A-R-T is another model, right? This is not B-E-R-T; this is B-A-R-T, BART. And at the same time, we will also discuss decoder-only models, right?\n\nwhich includes ChatGPT, all GPT series models, LLaMA models, and Decoder-only models are essentially very popular. So, the encoder-only model, as I discussed, was discussed in the last class. We saw that a new pre-training strategy called masked language model was introduced. This was used in BERT, where you essentially mask some of the tokens in the input. And you ask the model to predict those masked tokens, right? This is a self-supervised approach. You don't need any labeled data for training, right?\n\nAnd being an encoder model, it essentially means, The BERT model can essentially look at all the tokens present in the input. It is not an autoregressive model, is it? So the entire input is exposed to the model. And you basically perform self-attention. Through self-attention, you look at all the tokens present in the input. And based on that, you predict the mass of the tokens. On the other hand, the encoder-decoder model that we discussed here, As we discussed in today's class, we have this encoder component and the decoder component.\n\nAnd we will see how you can best use both parts of the encoder. And the decoder part during pre-training. And then we will also discuss the decoder-only model. Now, in the decoder-only model, as the name suggests, the decoder part, If you remember in the transformer class we discussed, it's an autoregressive model. autoregressive component in the sense that when you predict a word at the t-th location, You only have access to tokens until the t minus 1th location, right? You don't have access to tokens after the location, right?\n\nWe will see how you understand this autoregressive style of pre-training essentially. helps the decoder-only model learn things correctly This set of models is very popular these days, as people realize. Over the times that an encoder-only model is not the right solution for. For a generative task, right? Because a generative task requires an autoregressive setup, right? Whereas in an encoder-decoder model, right, as you require. Both the encoder part and the decoder part require a lot of memory.\n\nIt requires a lot of parameters, right? You can do all these generative tasks through the encoder-decoder model together, right? But the decoder model makes more sense both in terms of parameters. As well as the setup, right? The autoregressive model setup itself is suitable for, you know, next token generation. And, parameter-wise, you need half of the parameters. than the encoder decoder model. So we will focus on this and discuss it. Mask language model as I mentioned BART model. a bidirectional model, meaning that you have access to tokens in the right direction\n\nAs well as in the left direction, right? But what is required for an encoder-decoder model to predict a sequence, right? Encoder-decoder architecture; you should remember that this is a vanilla transformer model, right? You have a quick recap of input positional encoding right here again. You have position encoding, then you add positional encoding and the input right input embedding. And then in the encoder part, you have multiple blocks in a number of blocks. Let's say six blocks or twelve blocks; every block has a multi-head attention.\n\nand a feedforward layer, right? This is the FFN feedforward layer, right? And this is unmasked, right? Multi-head attention. And in between feed forward and multi-head attention, you have an add and norm layer. and that is your connection. In the decoder part, you again have multiple decoder blocks. Each block consists of a masked multi-head attention, masked in the sense that, As I mentioned earlier, when you process the Tth token, the tokens Which, after the Tth token, are all masked, right? And then usually, you know, the usual feedforward network in between feedforward.\n\nAnd mask multi-head attention, you have a cross-attention layer. So this is cross multi-head attention, and through this cross attention, You essentially know the decoder component, the decoder block. Attends to the outputs of the encoder block, right? So this is the usual setup, right? And then you repeat this block a number of times. So, Now we see that in this kind of encoder-decoder setup, How do we pre-train this model? So, the objective is still language modeling. Next word prediction can be an objective.\n\nThe objective can also be, let's say, sentence classification, for example. So let's see. So this is my encoder block. Okay. These are the inputs. There are T number of inputs, and these inputs are called prefixes, right? Now this prefix can be an instruction. Let's say the instruction can be, \"Summarize this sentence,\" right? Or the instruction can be to find the sentiment of the sentence, right? So it can be an instruction followed by the inputs. Let's say, in the case of summarization, you know,\n\nThe instruction is to summarize the following document: this is the document. That you want to summarize, right? The entire thing will be fed to the encoder model, right? And after the encoder block, you will get hidden representations h1, h2, dot, dot, dot, ht. In the decoder block, when you pretrain your model, Let's say, for task-specific pre-training, you also have the summary. So, you give the summary here. So y1, y2, y3, ..., yt plus m, this indicates the summary. And your objective is next-word prediction, right?\n\nSo at this stage, you predict what is going to happen at the second stage. At this stage, you predict what is going to come in the third stage. and so on and so forth. So in your decoder block, the input is y1, y2, y3, and so on. This is my input. And my input is the hidden state that you obtained from the encoder block. And when you get to the end of the decoder block, you get the hidden state representation here. You pass it through a softmax. So, this is my hidden state representation of the decoder block.\n\nYou pass it through a softmax. And the softmax is going to give you the probability distribution for the token. And that's going to be your output. So this is pretty much the same as the Vanilla transformer model. The Vanilla Transformer model was proposed for machine translation. where the input was, let's say, an English sentence and the output was a Hindi sentence Our output was a Spanish sentence, right? And this was the usual way of, you know, pretending with the transformer model.\n\nSo now, we will see what additional components we can change in the pre-training architecture. And we will specifically discuss two models. One is the T5 model, and the other is the BART model, B-A-R-T, BART model, right? Now, whatever we have discussed so far here in this slide, I said that there is a task. The task is, let's say, summarization, correct? Or let's say the task is machine translation. Right? So for these tasks, you need a lot of human data, human-level data, don't you?\n\nBut now the question is, have we seen in the case of the BERT model? We did not require labeled data. It was a self-supervised approach; you basically had a running test. And you mask some of the tokens in the running text, and you want the model. To predict those running those masked tokens, right? So the question is, in the encoder-decoder model, how can we use unlabeled data? Because labeled data is very difficult to collect, right? And then how do we force the model to attend to all the inputs, right?\n\nBecause your output is why your input is X, right? Your input is fed. Your input is going to be fed to the encoder model. And your decoder model should be able to attend to all your encoder inputs, right? So, let's see. So in the case of BART, B-A-R-T, the full form is Bidirectional and AutoRegressive Transformer. As the name suggests, it essentially leverages the bidirectional part. which was there in BERT, and the autoregressive part, We will discuss what is going to be there in GPT today.\n\nRight? So it leverages both schools. Okay. So remember, we should be able to use unlabeled data. Okay. So, what is the idea here? Given an input sentence, you corrupt that input. You inject noise into that input and ask the model. To essentially predict the correct sentence, the right sentence, right. So the question is, how do you corrupt it? There are multiple corruption approaches that they have suggested. These are called noisy instances. For example, let's say your input is A, B, C, D, and E, right?\n\nWhat you do is mask some of the tokens similar to BERT, right? And you ask the model to predict those mask centers, right? So in your encoder, your input is the masked token. tokens, the mask set of tokens, and your decoder, The output is going to be the actual set of tokens, right? So, this is one strategy. The second strategy can be you; you can, basically, permute, right? So your sequence is ABCDE; you shuffle the tokens; it can be DE ABC, for example. Right? And you ask the model to predict the actual sequences.\n\nThe third one can be a rotation, right? So A, B, C, D, E; so C, D, E, A, B. This is one rotation you can rotate again, right? You keep rotating to obtain different inputs and your output will be the original one, right? And these are very similar because these strategies consider the entire sequence. And then sometimes mask it, sometimes rotate it, sometimes permute it, right? But the length of the input remains the same. Okay. They also tried out token deletion, where you delete some of the tokens.\n\nYou see here, you delete B, D, and F, right? And you ask the model to predict those tokens. Okay. But remember, since this is an autoregressive model, You ask the model to basically predict all the tokens. Not the masked tokens or the tokens that you have deleted. So in your encoder, your input is going to be ACE for this strategy. and your decoder, the output is going to be A, B, C, D, E. It is not that your output will be only B and D. Now this is called token deletion. They also tried an extreme approach where they essentially, you know,\n\nMask, not a single token, but a sequence of tokens\u2014a consecutive sequence of tokens. For example, as you see here, ABCDEF; ABCDE was the original sequence. You mask BC together, right? So sometimes you mask two tokens. Sometimes you mask one token, and so on and so forth. And what they said is that there is a distribution. From the distribution, they basically sampled the size of this mask, right? Sometimes, if the size is one, you just sample one token; you just mask one token. If the size is three, you mask three consecutive tokens, and so on and so forth.\n\nSo, they randomly chose some of the positions, right? And they sample a number from the distribution. That sample is going to tell you what is going to be. The size of the token that you are masking, right? So, nothing is fancy. This is just a way of doing pre-training as opposed to, you know, A sort of supervised pre-training was used in the transformer model. Where you need it, you basically need the labeled data. So here you don't need any labeled data as such. Okay. Okay. So this is my encoder.\n\nOkay. This is my input. Let's say that here, I mean, you can use permute and mask, right? This should be mask. Right or delete right, and you ask the model to predict the entire sequence, okay? So this is BART. Let's look at the difference between BERT and BART. So in case you know the BERT model, which is an You know the encoded only bi-directional model you asked your model. To predict the mask tokens, right? In the case of BART, you ask your model to predict all the tokens, right? It uses an encoder; it uses an encoder-decoder, so double the parameters, right?\n\nAnd so on and so forth. So that's it. Okay. They pre-train the model, pre-train the encoder decoder model in this way, injecting noise and asking the decoder model to basically predict the clean version. They fine-tune the model on downstream tasks, right? So once you pre-train, you can now think of a pre-tuned model. which knows the language in general. Then you fine-tune it; they fine-tuned it. By the way, you can also pre-tune; you can also use a pre-tuned model. for your downstream task without fine-tuning it, right?\n\nAnd we'll see the GPT-3 paper, which came, In 2020, they showed that you don't need to fine-tune your model. For downstream tasks. Nevertheless, on the summarization dataset, they fine-tuned the model. In a news article, a news corpus, and it turned out to be really effective. It also showed that you don't need a large data set. You need only a few summaries for fine-tuning compared to normal sequence-to-sequence models. Let's say, you know, LSTM or a normal sequence, I mean. LSTM GRO models require a lot of data for fine-tuning here.\n\nSince your model is already pre-trained, you don't need to fine-tune, you know, that much. So here is the output. of the BART model. This is the document that was given for summarization. And this is the summary that was obtained from the model. Okay. Around the same time that the BART model was proposed, right, Google also came up with a method called T5. At the same time, I think within a gap of one week or two weeks, right? And T5 is also an encoder-decoder model. It is called the text-to-text transfer transformer, right?\n\nAnd this is actually a, you know, this is very, very, I would say, Ambitious model, right? What they said is that we map all the NLP tasks, correct? Any tasks that you can think of, not necessarily NLP, are there? Any task that you can think of, you map. You can realize any task as a text-to-text transfer problem. It is very difficult to digest, isn't it? But bear with me. We will essentially discuss the techniques. There is no fancy technique behind this. The same encoder-decoder model and the same corruption technique that were proposed in BERT.\n\nBut remember, BERT was proposed by Facebook. T5 was proposed by Google around the same time. It's kind of a similar approach. But they said that, first of all, you don't need any fine-tuning as such. You pretrain on millions of tasks. And these tasks are all supervised tasks. So the T5 dataset is not a T5 training approach; it is not an unsupervised approach. As such. But the crux of the method is that every problem can be mapped. To a text-to-text problem, right? For example, let's say you want to translate an English sentence to German.\n\nSo what you do in your encoder, you write this prompt That translates English to German, right? It's your prefix, right? And then this is the sentence, and your output will be this one. The German version of it, right? So your input will be this sentence right into the encoder. and your output will be this sentence in the decoder. But you see, here there is a task instruction and a task prefix. which was not there in the case of Bart right. Bart's pretraining method was very different. Now, this is the second task.\n\nCOLA sentence. What is COLA? COLA is a task corpus for linguistic acceptability. So it basically checks whether an input sentence is linguistically acceptable or not. For example, the course is going well. Is it an acceptable sentence? No. So your input is the task instruction and the sentence. And your output is either acceptable or not acceptable, right? Your input is, so the next task is to check whether two sentences are similar. Or not, right? STSB task. Sentence one is: The rhino grazed on the grass.\n\nSentence two: A rhino is grazing in the field. Now, whether they are similar or not, you give a score. Right. It is not yes or no; it is a score. Now, think about it. Your output here 3.8 is essentially a string. So you are forcing your model to produce 3.8. Right? You are not creating any numbers. You are basically printing a string and calling Ruffle, the first order of this paper. So he said in one of his talks that, you know, believe, believe him, believe, believe on this, is this framework.\n\nIt actually works. Okay. So the fourth task can be summarizing. You write a summary of the entire sentence, right? The state authorities dispatched emergency crews on Tuesday. to survey the damage after an onslaught of several weather events in Mississippi and dot, dot, dot. There are other tokens as well. And your model should be able to summarize this. So you can still digest these three tasks, but they are difficult to digest. How a text-to-text model can produce a number. Nevertheless, they said that if you give instructions or write instructions,\n\nAnd then the task itself forces you to make the model produce the output. In your pre-training time, your model should be able to do that. So, you do this pre-training. Now, for your downstream task, if you have data, you want to fine-tune it. Right? And your model should be able to produce. normal encoder decoder architecture, right? Encoder blocks, decoder blocks, right? Nothing fancy here. No change in the framework. The vanilla transformer model was used here. So for the pre-training, right?\n\nSo when you pre-train, this is your, you know, I mean, There are different tasks, and in your pre-training, what you do. This is very similar to the BERT kind of pretraining. where you basically give a sentence as input, And then you mask some of the sentences, right? With different lengths. So you replace different-length spans, right? With some unique placeholders, the placeholders can be the mask tokens, right? And these tokens are called sentinel tokens. These tokens are part of your vocabulary; remember this, right?\n\nSo in the case of BERT, you only use the single token called \"mask.\" as a Sentinel token. But here you are using different tokens for different masking, aren't you? When you mask, let's say for an invitation, you have a token. When you mask last, you have another token. So, and the number of times you mask an input, You will have that many Sentinel tokens, right? So you are basically increasing the vocabulary, but that's fine. Okay. And as opposed to BERT, where we were to produce\n\nthe entire input in the decoder output, right? And not the mask inputs, but the mask tokens here. You, that your task is to only produce the mask spans, right? If your input is this, thank you for inviting me to your party next week. you will produce this kind of outputs X, For invitation, Y, You know, last and then, you know, Z can be your end of the sentence. Sentinel or some other sentinel tokens, right? So you are only producing the token or the span that was masked in your input. You are not producing anything like this.\n\nThank you, X, for inviting me to your Y last week. You do not need to produce or predict the entire sequence. That is the difference. So for training, they collected a lot of corpora, a lot of documents from the web. Right? They use this common crawl web scraper, don't they? It is a nonprofit organization. They periodically scrape the Internet, right? And as you may imagine, internet data has a lot of garbage. A lot of noise. So they basically did a series of preprocessing to remove noise from the input.\n\nPre-processing includes removing lines that do not end with the terminal punctuation. They only retained the English text. They removed all non-English text. They also removed those tokens that look like placeholders. They removed the HTML codes and JavaScript codes from the documents. When you scrape a website, there are many HTML tags and JavaScript tags. Which they essentially removed. And they removed many duplicate texts. They call the clean corpus the C4 corpus, and this is publicly available, right?\n\nA colossal cleaned version of the Common Crawl, Common Crawl Wave, and Crawl Corpus, right? There are four Cs. So the C4 corpus was collected for training, right? So this is their pre-training setup. They use, you know, a BERT-sized encoder-decoder transformer, right? It can be a six-layer encoder and a six-layer decoder, right? They basically wanted to compare with BERT. Remember when BERT was proposed, right? Bert was proposed one year before T5, and at that time. Everyone was essentially behind BERT, right?\n\nThey were trying to defeat and outperform BERT. So, in order to make a comparable model, they took a BERT-sized encoder-decoder model. Right? And they added different denoising objectives, didn't they? Spanning different areas, so masking different spans. You can also think of rotation, permutations, and so on. They use C4 data. And they pretend this; they pretend on this, and then they fine-tune. On a series of tasks, right? They fine-tune it on the glue task, okay. Now glue is a benchmark.\n\nGlue is a very standard NLP benchmark that contains many tasks, right? For example, one task is called COLA. As I mentioned, COLA is a corpus of linguistic acceptability. where given an input, your output will be whether this is acceptable or not. The second task is SST-2. This is a sentiment analysis task: movie sentiment, positive, negative, neutral. MRPC, this is basically a paraphrase of whether B is a paraphrasing of A or not. HTSB, there are two sentences, A and B, whether they are similar or not.\n\nAnd you produce a number. QQP, this is again about whether two questions are similar or not. MLNI, MM, this is essentially an intelligence, whether A intels B or A contradicts B, or so on. QNLI is a typical question-answering benchmark. RTE, whether again intelment, whether A intels B or not. and WNLI, which is sentence B, replaces sentence A. Is this the correct noun or not with the ambiguous pronoun? These are all typical NLP tasks that were present when the GLUE benchmark was released. And GLUE is actually a very standard benchmark even to date.\n\nSo, they tried with glue, right? They also took the CNN Daily Mail corpus. This is a corpus for summarization, right? Mostly abstractive summarization, isn't it? They took the squad's data. This is a question-answering dataset released by Stanford. They also use superglue. So glue and super glue are the standard benchmarks. There are many other benchmarks these days; for example, Big Bench is another benchmark. Now super glue is even tougher than regular glue, right? So, super glue has this many tasks, and these tasks are really tough, right?\n\nEven for human beings. For example, recognizing textual entailment and multi-sentence reading comprehension, Boolean question answering, reading comprehension tests, and a lot of other difficult questions are difficult tasks. So they took Glue, CNN, Daily Mail, Squared, and superglue. And three different machine translation data sets, right? WNT 14 and 15. So this is English-to-Dutch, English-to-German machine translation. This is English-to-French machine translation. This is an English to Romanian machine translation.\n\nAnd these data sets were also very popular at the time. This is also popular these days. This is the machine translation workshop. This workshop is conducted every year, and they really keep releasing. these kind of data sets. Okay, after fine-tuning, they reported the accuracy at every checkpoint, right? So at a certain point, let's say a checkpoint after this many steps, After this many steps, these are different checkpoints; now check at that checkpoint. What is the accuracy? This is basically model selection, you know, but this is not a very good approach.\n\nBut it is sometimes useful to understand, you know, whether over the iterations. Whether the model is getting improved or not is unclear. Let's look at a series of results, okay? Here, I will only show you. Some of the important results, but if you look at the T5 paper. I think this is a 50-page very long paper; there are a series of results, right? The paper reported very extensive empirical observations. A lot of abolition studies and things like that, but you know. You can easily imagine the amount of resources that Google has.\n\nAnd only companies like Google can do this kind of research. You know having their GPU access and TPU access because it really requires. a lot of compute, a really lot of compute Nevertheless, the baseline average is their model. If you fine-tune on glue and taste on glue, right? This is the correct accuracy. This is CNN Daily Mail squared, right? Superglue. By the way, in Glue, there are multiple tasks, right? I think that they took only one task for this. Super glue also took one task, or I don't exactly remember.\n\nMaybe they took one task for reporting the number, or I think they... I took an average, then reported the average; I don't exactly remember. And then, you know, these machine translation datasets. Baseline results: whenever you see the bold numbers, these numbers indicate. That the accuracy is within the standard deviation. They also reported the standard deviation here, didn't they? If something is bold, it means the number, since they have done it multiple times, right? So the reported number falls within the standard deviation.\n\nMean \u00b1 standard deviation range. And they tested their model with no pre-training, correct? You just do fine-tuning on a specific task. You don't need to do this mask pre-training phase. You don't need to do the mask pretraining, right? What you do is take a glue benchmark, let's say, You train on that task and test it on the task, right? Pure supervised machine learning method, right? And this is the number that they reported. What are the major observations? The first observation is that, as I mentioned, BERT was the best baseline at that time.\n\nRight? And they showed that the glue and the squared data sets. Their results are quite comparable to BERT, okay? They also showed that if you don't do pre-training, your numbers are pretty low. Pretty lesser, pretty lower than your pre-training followed by fine-tuning, right? So pre-training is actually necessary. Now, without pre-training, if you train and then make predictions, right? For all the tasks, your model significantly outperforms the baseline, right? You see this except for this English to French machine translation task.\n\nAnd you see, the numbers are quite close. And they argued by saying that the English-to-French machine translation data set was the largest one among other machine translation datasets. It was large enough that you didn't really need any pre-training as such. If you do normal training on the dataset, that will give you enough signal. about this task because of the size of the data set. If you don't have that much data, you need pre-training. The C4 data size is 745 GB. The filtered data, I mentioned that a series of filtration techniques were applied.\n\nIf you do not filter it and just take the crawled C4 data. which is 6 terabytes in size, you see that with clean data versus unclean data, The accuracy is not at all comparable. Sometimes comparable; sometimes not comparable. As you can see here, accuracy improves by 2%, 3% if you clean this. So cleaning is important; compact data is important. Pre-training on the in-domain data is always helpful. So they checked with three different architects. They compared their methods with the standard encoder-decoder model.\n\nthat they also use an autoregressive model, right? Next to our prediction model language modeling task, you see the attention head here. Now this guy is attending to all the previous guys. Look at this one. This guy is attending to right only the previous token. So this is a typical autoregressive setup, a GPT kind of setup. And this is a new model. It's called a prefix language model, right? In a prefix language model, what you do is this, right? One part of your attention is unmasked. Other part of your attention is masked, right?\n\nYour attention metrics may look like this, right? Now this is unmasked and this is masked. this part is masked. Okay. So this will act as BERT and this will act as GPT, but the difference here is that there is no encoder decoder. There's only one stack, right? There is only one layer stack. It's not like that. In one stack, one part is acting as an encoder model. And the other part is acting as an encoder decoder model. Okay. This is called prefix \"lm.\" Now this is the figure that I mentioned.\n\nIn the case of an encoder-only model, the entire token sequence is visible to you. In the case of a causal model or autoregressive model, you have access to one part. You don't have access to the other part. In the case of the causal prefix model, the prefix model that I mentioned, One part is completely exposed; the other part is basically autoregressive, okay. Okay, so now let's look at the experiments, right? With an encoder-decoder model, a denoising objective, and 2p number of parameters,\n\np for encoder, p for decoder, right? Now this cost is the number of flops, floating point operations per second. This is the result, right? Now, let's compare. So, okay? So, if you... Think of another model where it is also an encoder-decoder. But all the parameters are shared, right? Self-attention is also shared. The feedforward is also shared. Everything is shared, right? But you have two stacks. So you need half of the parameters, P number of parameters, right? You see, let's first understand the experimental setup, okay?\n\nThen we'll analyze the results. The third one is an encoder-decoder with six layers. This is again denoising objective, but here there are six layers in encoder, six layers in decoder. I think in this setup there are 12 layers. The fourth one is language modeling in an autoregressive setup, right? Decoder only. And the fifth one is prefix-LM, right? You have all these tasks, right? For the superglue, you only need to know that these numbers are reported for the classification task only, okay?\n\nOkay, so what are the objectives? What are the takeaways? The first takeaway is that six layers are versus twelve layers, right? This is a 12-layer, this is a 6-layer, okay? The performance drops. So, if you make the number of parameters half, it hurts your performance. If you compare the shared encoder-decoder and prefix LM, Performance on the encoder-decoder with shared parameters is almost on par with the prefix. You see, the numbers are quite close; sometimes worse, sometimes better, but on par.\n\nBut of course, the encoder-decoder model is always better, right? Always better than other models, okay? For pre-training, as I mentioned, they also did many experiments, right? Now for the pre-training strategy, they use different types of strategies. One is a board-style denoising, where you denoise, Where you basically, you know, add some of the, you replace tokens with masks, right? For example, your input is, \"Thank you for inviting me to your party.\" And your output would be, \"Thank you for inviting me to your party.\"\n\nNow, the interesting point is that you sometimes also corrupt. You replace a token with another token. For example, it's Apple week, and your task is to predict this as you did last week, right? You should do the prefix language modeling objective. For example, your input is \"Thank you for inviting.\" Your output will be for your party last week. You essentially predict the next set of sequences. Reshuffling, as I mentioned in the case of BERT, your input is Let's say your output should be, \"Thank you for inviting me to your party last week.\"\n\nYour input can be part of me for your last two fun activities and blah, blah, blah. Replace spans; this is span corruption. You just, you know, corrupt an entire span. The span is for inviting, for example, and you replace it with a sentinel token. You dropped some of the tokens. This is, again, another object, another, you know, another objective that was used in BERT. You drop some of the tokens and ask the model to predict those tokens, okay? All right, so... You look at the experiment, prefix language model; this is kind of like this one.\n\nWhere this is exposed to you, and this is something that you want to predict reshuffling. BERT-style mask language modeling replaces the corrupted span. And then drop the corrupted token, and you see here. In most cases, if you look at Glue, dropping an entire token helps. Here, this is also better. This is better. In the case of Super Glue, I think replacing the corrupted span actually works better. Right, and you know these numbers are quite comparable as this is better, right? So in most cases, as you can see, this is sometimes better.\n\nSo there is no clear-cut answer as to which strategy is better, but they, You know, they did many experiments, as I mentioned, and they concluded. That replaces the corrupted span, right? This one, and drop the corrupted token, right? These are more appealing than other tokens. Now, the other strategies; the reason is that the other reason is that. When you drop tokens, your length will decrease, right? You need to produce a smaller number of tokens. Isn't it? Therefore, this will also help you speed up the training process.\n\nOkay. So now we are discussing decoder-only models. Decoder-only models, as you can imagine, right? The strategy is not very new here, is it? You don't need any input corruption, right? You are given a sequence of tokens and you predict what's going to be the next token. Very simple, isn't it? Is it a generative model that calculates the probability of the language? And this is the auto-aggressive language modeling objective that I mentioned. I think a thousand times in this course.\n\nThis is the input. You predict the next token. You essentially produce a distribution, right, over the tokens? And that's going to be your output. Then you sample a token from the distribution. You can think of it as a classification problem because now, given mod V, Where V is the size of the vocabulary, you have mod V. number of tokens in your vocabulary. Essentially, your task would be to classify. You can basically choose one of the labels. You can think of tokens as labels. You choose one of the labels from the mod V number of labels.\n\nOkay. So this is your input. Right, it is a decoder-only block, right? You produce the hidden state; your hidden state will be fed to a linear layer, right? which will produce a softmax distribution of the course linear layer followed by soft max it will produce a distribution from the distribution you sample okay. So nothing fancy here; a quick comparison in the case of BERT. which was an encoder only model, your input was X1, X2, dot, dot, dot, X i minus 1, X i plus 1, and this was something that you masked, right?\n\nand you ask the model to predict that masked again. In the case of a GPT or decoder-only model, right? Your input is X1, X2, ..., Xi minus 1, and you have to predict Xi. Okay, so the architecture is simple, right? And GPT-1, The first GPT paper was published in 2018, right? They use a normal transformer decoder, 12 layers, and 117 million parameters. Remember, in 2018, we are talking about parameters in terms of millions, right? A 760-dimensional hidden state, the dimension of each hidden state is 768.\n\n3072-dimensional feedforward hidden state, hidden layers. Byte pair encoding was used. We discussed byte pair encoding before with these linear mergers. And they used this book corpus, which had around 7,000 unit books. This is GPT-1. GPT-2 was proposed the following year, 2019. What's the difference? The difference is that they use layer normalization between different sub-blocks. We discussed layer normalization earlier. Their vocabulary is now expanded to 50,000. Context size has now increased from 512 to 1024.\n\nRight? Now your data is a new data, which is a common crawl data, That same data that was used in T5, eight minimal documents, right? And, of course, minus Wikipedia. So this is GPT-2. And they showed that if you do unsupervised multitask learning, it essentially helps. So in GPT-2, they focused more on the generation task, didn't they? And and as opposed to GPT-1. And they showed that GPT-2 starts producing more human-like sentences, didn't they? If you write these as your input and ask the model\n\nTo essentially keep generating tokens to fill out the remaining part. So this is your input, and these are going to be your tokens. that you will essentially generate. Cost-wise, it's a quick comparison. I'm not very sure about that. I took this thing from this link, didn't I? But the base cost was 500 USD; I think that with a single pre-training run, it's right? The large required $7,000, whereas GPT-2 basically needed approximately $24,000. Then came GPT-3 in 2020, right? And if you look at the training petaflop per day, it's significantly high.\n\nGPT-3 has small, medium, large, XL, and many versions, including GPT-3 2.7 billion, right? And this is T5 statistics; this is BERT statistics. So, GPT-3, the major difference is the parameters. So we were talking about GPT-2 with only 1.5 billion parameters. Now this is 175 billion, 100 times larger than GPT-2, correct? 96 layers, 96 heads in the self-attention component, 12,000-dimensional vectors, right? And you know, this is the rough estimate of the cost that was calculated. So three major GPT models: GPT-1, GPT-2, and GPT-3.\n\nThe parameter, as you see, increased drastically; the number of players increased drastically. The data set also increased drastically; the size increased drastically. In GPT-1, they started focusing on, they said. that this is the first large-scale decoder-only model GPT-2 came up with this idea called zero-shot learning with larger training. And in GPT-3, they observed this in-context learning. You wrote examples. We'll talk about in-context learning later. You don't need to fine-tune the model.\n\nYou write examples; the model will be able to understand the task. And they said this is the emerging property. At that time, they were not very sure why this was happening. It's very difficult to understand and dissect. Then came GPT-4 in 2023, I suppose. This is a mystery because it is a black box model, right? After GPT-3, they stopped releasing the architecture, didn't they? OpenAI was no longer open at that time, was it? And the exact size is not very clear, but if you look at the cost,\n\nThe part token cost increased quite significantly, both in terms of inputs and outputs. 15 to 30 times more costly than GPT-3. This itself gives an indication of how big the model is, right? Or what's the amount of training and the cost that was incurred to train this model? So now GPT-4 is not only about pretraining, right? There are many additional things, right? For example, we'll discuss instruction fine-tuning. later instruction fine tuning. So in three instruction fine-tuning, you teach your model how to follow instructions.\n\nAnd then they use something called RLHF, reinforcement learning from human feedback. This is again something that we'll discuss later. Okay. So today's GPT, nobody knows what's there, right? but we can only assume that, you know, pre-training followed by a series of fine-tuning, Instruction fine-tuning, right, different types of alignments, and so on and so forth. Llama, the same thing: Llama is open source. You can still look at models and parameters. There is LlAMA 1, LlAMA 2, and LlAMA 3 still available, right? Different parameters.\n\nDimensions, increasing dimensions, number of heads, increasing number of heads. And they use byte-pair encoding. This is also something that we discussed earlier. And they use rotary position encoding. This is also discussed in the data set that uses this common crawl. GitHub data, Wikipedia data, book data, and so on. So this is about the GPT decoder-only model; what is coming next? So next week, we'll talk about instruction fine-tuning, right? After pre-training, this is the step that we do, isn't it?\n\nNow we are discussing how you can also design your own. You know, Your own language model, right? You do pre-training, then you do instruction fine-tuning. And then you align your model with human feedback through RLHF. You know, and then your model should be ready to understand the instructions, okay? All right, with this I will stop, and next week we will discuss the last part. Thank you." + }, + { + "name": "Tutorial: Introduction to huggingface", + "description": "### Summary of Hugging Face Library Tutorial \n**Duration:** 00:00:14 \u2013 00:48:00 (approximate) \n**Topic:** Introduction to Hugging Face Transformers library, tokenization, model loading, fine-tuning, inference, and pipeline usage for NLP tasks.\n\n---\n\n#### [00:00:14 \u2192 00:05:02] Introduction to Hugging Face and Transformer Workflow \n- **Hugging Face** is a popular library for working with transformer-based models and open-source datasets available on the **Hugging Face Hub**. \n- Key packages: \n - `Transformers`: for transformer models \n - `Datasets`: for dataset management \n- **Basic Input Processing Pipeline:** \n - Raw text (e.g., \"This course is amazing\") \u2192 Tokenizer breaks text into tokens \n - Tokens are mapped to numeric IDs (e.g., via Byte Pair Encoding or SentencePiece) \n - Token IDs passed to model \u2192 Model looks up embeddings from embedding matrix \n - Embeddings are processed with positional encodings (like sinusoidal or RoPE depending on model) \n - Transformer layers process embeddings \u2192 Output logits over vocabulary tokens \n - Softmax applied to logits \u2192 Probability distribution over tokens \n- Transformer architecture varies by model: \n - GPT-2 (decoder-only, no RoPE) \n - LLaMA (uses RoPE) \n - Mistral (uses sliding window attention) \n - Variations include different attention mechanisms like group query attention \n- Example task introduced: **Sentiment classification** using an encoder-only model like **RoBERTa** (a BERT variant trained with masked language modeling). RoBERTa outputs sentence representations used for classification.\n\n---\n\n#### [00:05:02 \u2192 00:12:17] Loading Tokenizers and Models with Hugging Face \n- Use **AutoTokenizer** and **AutoModel** classes for easy loading based on model name or path from Hugging Face Hub. \n- AutoTokenizer detects the correct tokenizer type automatically (e.g., RoBERTa tokenizer). \n- AutoModel loads the corresponding pre-trained transformer model. \n- Task-specific model variants exist: \n - `AutoModelForSequenceClassification` \n - `AutoModelForCausalLM` (causal language modeling) \n- Hugging Face Hub provides a searchable interface to find models by task and language (e.g., RoBERTa fine-tuned for sentiment analysis). \n- Tokenization example: \n - Input sentence: \"I am excited to learn about transformers.\" \n - Tokenizer returns token IDs and attention mask (indicates which tokens to attend to) \n - Output logits converted to probabilities via softmax for classification (e.g., positive vs. negative sentiment).\n\n---\n\n#### [00:12:17 \u2192 00:22:47] Tokenizer Details and Batch Processing \n- Tokenizers produce dictionaries with keys like `input_ids` and `attention_mask`. \n- Special tokens explained: \n - `[CLS]`: Classification token at sentence start, represents whole sentence \n - `[SEP]`: Separates sentences or marks sentence end \n - `[PAD]`: Padding token for batch uniformity \n - `[UNK]`: Unknown token for out-of-vocabulary words \n- Tokenizer can return PyTorch tensors directly with `return_tensors='pt'`. Without this, output is a list. \n- Batch tokenization handles multiple sentences at once with padding to equalize lengths and truncation to limit max length. \n- Attention masks zero out padded tokens so they don\u2019t contribute to attention calculations. \n- Batch decoding can skip special tokens to obtain clean text output. \n- Vocabulary size example: DistilBERT tokenizer has about **50,265 tokens**.\n\n---\n\n#### [00:22:47 \u2192 00:27:07] Model Architecture and Loading \n- Models typically have: \n - Embedding layer \n - Stacked transformer encoder layers (e.g., 6 layers for DistilBERT) \n - Classification head (feedforward or linear layer mapping hidden states to output classes) \n- Loading models: \n - Use `AutoModelForSequenceClassification.from_pretrained(model_path)` \n - Input to model: tokenized input IDs and attention masks \n - Output: logits corresponding to class probabilities \n- Model classes inherit from `torch.nn.Module`, enabling seamless integration with PyTorch training loops.\n\n---\n\n#### [00:27:07 \u2192 00:31:50] Training Loop and Model Internals \n- Training steps: \n 1. Forward pass input batch through model \u2192 get logits \n 2. Compute loss (e.g., cross-entropy between logits and true labels) \n 3. Zero gradients (`optimizer.zero_grad()`) \n 4. Backpropagation (`loss.backward()`) to calculate gradients \n 5. Optimizer step (`optimizer.step()`) to update weights \n- Hugging Face models automatically compute loss if labels are provided during the forward pass. \n- Setting `model.eval()` disables gradient calculations (inference mode). \n- Internal states accessible: \n - Hidden states per transformer layer \n - Attention weights per attention head \n- Model configuration (`model.config`) provides info like number of layers and attention heads (e.g., DistilBERT has 6 layers, 12 heads each).\n\n---\n\n#### [00:31:50 \u2192 00:38:20] Fine-Tuning on Custom Dataset \n- Dataset example: **Financial Phrase Bank** (available on Hugging Face datasets hub) \n- Dataset structure: dictionary with `train` split containing sentences and labels (neutral=1, positive=2, negative=0) \n- Data preparation: \n - Tokenize dataset with padding and truncation (max length 50 tokens) \n - Batch size example: 16 \n - Remove unused raw sentence column after tokenization \n - Format dataset to PyTorch tensors for input IDs, attention masks, and labels \n- Training: standard PyTorch loop with optimizer, loss, and scheduler \n- Hugging Face Trainer API simplifies fine-tuning by abstracting training loop details.\n\n---\n\n#### [00:38:20 \u2192 00:42:39] Using Hugging Face Trainer Class \n- Define training arguments: \n - Batch size per device (e.g., per GPU) \n - Number of epochs \n - Evaluation strategy (e.g., evaluate after each epoch) \n - Learning rate, weight decay, etc. \n- Initialize Trainer with model, tokenizer, dataset, training args, and compute metrics function \n- Supports callbacks like early stopping with patience and threshold to stop training if validation accuracy degrades \n- Trainer handles training, evaluation (`trainer.evaluate()`), and prediction (`trainer.predict()`) easily \n- Checkpoints saved at intervals during training, allowing reload and inference later (`from_pretrained(local_checkpoint_path)`) \n\n---\n\n#### [00:42:39 \u2192 00:46:53] Decoder-Only Models and Text Generation \n- Loading decoder-only models for **causal language modeling** (next token prediction): \n - Use `AutoModelForCausalLM.from_pretrained()` \n - Example models: GPT-2, DistilGPT-2 \n- Text generation via `model.generate()` function: \n - Input prompt tokenized and passed to generate \n - Parameters include `max_length`, sampling options (`do_sample=True`), `top_p` (nucleus sampling), `temperature`, `top_k` \n - Sampling methods control randomness and diversity of generated text \n- Example: Prompt \"Once upon a time\" \u2192 model generates continuations based on learned probabilities \n\n---\n\n#### [00:46:53 \u2192 00:48:00] Pipelines and Masked Language Modeling \n- **Pipeline API:** \n - Simplifies common NLP tasks (e.g., sentiment analysis, text generation) \n - Instantiate pipeline with task name and model (e.g., `pipeline(\"sentiment-analysis\", model=roberta_model)`) \n - Pass text input directly to pipeline object for immediate prediction \n - Useful for rapid prototyping or testing without detailed coding \n- Masked Language Modeling (MLM): \n - Load `AutoModelForMaskedLM` and corresponding tokenizer (e.g., BERT) \n - MLM task: predict token replacements for `[MASK]` tokens in input text \n - Example: \"I am asked to learn about [MASK]\" \u2192 model outputs top candidate tokens with probabilities \n - Can use pipeline `\"fill-mask\"` or do manual softmax over model logits \n\n---\n\n### Key Insights \n- **Hugging Face provides modular, standardized APIs** (AutoTokenizer, AutoModel, Trainer) to facilitate transformer-based NLP workflows. \n- Tokenization is critical: special tokens, padding, truncation, and attention masks help models process varied input lengths and batch data efficiently. \n- Hugging Face models are tightly integrated with PyTorch, allowing seamless training and fine-tuning with familiar paradigms (loss, optimizer, backpropagation). \n- Trainer API abstracts away boilerplate training code and supports callbacks, metrics, checkpointing, and multi-GPU setups. \n- Pipeline API offers fast, high-level access to pre-built models and tasks, ideal for quick experimentation or deployment. \n- Hugging Face Hub centralizes access to thousands of models and datasets, enabling easy loading from pre-trained checkpoints. \n- Transformer internals like attention weights and hidden states are accessible for research or interpretability. \n- Different transformer variants (encoder-only, decoder-only, encoder-decoder) serve different tasks like classification, generation, and MLM.\n\n---\n\n### Timeline Table of Major Topics\n\n| Timestamp | Topic | Key Points |\n|-----------------|------------------------------------------------------|----------------------------------------------------------------------------------------------|\n| 00:00:14\u201300:05:02 | Introduction to Hugging Face and transformer workflow | Tokenization, embeddings, positional encoding, logits, softmax, sentiment classification |\n| 00:05:02\u201300:12:17 | Loading tokenizers and models | AutoTokenizer, AutoModel, Hugging Face Hub, tokenization examples, softmax probabilities |\n| 00:12:17\u201300:22:47 | Tokenizer details and batch processing | Special tokens, attention masks, batch padding/truncation, vocabulary size |\n| 00:22:47\u201300:27:07 | Model architecture and loading | Transformer layers, classification head, PyTorch integration |\n| 00:27:07\u201300:31:50 | Training loop and model internals | Loss computation, backpropagation, optimizer step, model.eval(), hidden states & attention |\n| 00:31:50\u201300:38:20 | Fine-tuning on custom dataset | Financial Phrase Bank dataset, tokenization, batching, PyTorch DataLoader |\n| 00:38:20\u201300:42:39 | Hugging Face Trainer class | Training arguments, callbacks, checkpointing, evaluation, prediction |\n| 00:42:39\u201300:46:53 | Decoder-only models and text generation | Causal LM, GPT-2, model.generate(), sampling methods (nucleus, top-k, temperature) |\n| 00:46:53\u201300:48:00 | Pipelines and masked language modeling | Pipeline API for sentiment analysis, fill-mask task, MLM token prediction |\n\n---\n\n### Glossary of Important Terms\n\n| Term | Definition |\n|--------------------------|---------------------------------------------------------------------------------------------|\n| Tokenizer | Converts raw text into tokens and token IDs, adds special tokens, handles padding/truncation|\n| Input IDs | Numeric representation of tokens passed to models |\n| Attention Mask | Binary mask to indicate which tokens the model should pay attention to |\n| Embedding Matrix | Lookup table mapping token IDs to dense vector representations |\n| Positional Encoding | Added to embeddings to provide token order information |\n| Logits | Model raw outputs before softmax, representing unnormalized scores over vocabulary |\n| Softmax | Converts logits to probabilities |\n| CLS Token | Special token representing the whole sentence for classification tasks |\n| PAD Token | Special token used to pad sentences in batches to equal length |\n| AutoTokenizer/AutoModel | Classes to automatically load appropriate tokenizer/model given model name or path |\n| Trainer | High-level Hugging Face API to manage training, evaluation, checkpointing |\n| Causal Language Modeling | Task of predicting next token in a sequence (decoder-only models) |\n| Masked Language Modeling | Task of predicting masked tokens in input (encoder-only models like BERT) |\n| Pipeline | API to quickly run common NLP tasks without detailed coding |\n\n---\n\n### Summary of Quantitative Data\n\n| Model/Tokenizer | Number of Layers | Number of Attention Heads | Vocabulary Size |\n|-------------------|------------------|---------------------------|-----------------|\n| DistilBERT | 6 | 12 | 50,265 |\n| RoBERTa | *Not specified* | *Not specified* | *Not specified* |\n| GPT-2 | *Not specified* | *Not specified* | *Not specified* |\n\n---\n\n### Final Recommendations \n- Explore Hugging Face documentation to deepen understanding of classes, methods, and training arguments. \n- Experiment with pipelines for quick prototyping of common NLP tasks. \n- Utilize Trainer API for efficient fine-tuning on custom datasets with advanced features like early stopping and multi-GPU support. \n- Leverage Hugging Face Hub to access a wide variety of pre-trained models and datasets for diverse NLP applications. \n\n---\n\nThis summary captures the core content and practical insights from the tutorial, strictly adhering to the transcript details without speculation.", + "links": "https://example.com/resource/3", + "module": "Tutorial: Introduction to huggingface", + "module_id": 1, + "submodule_id": 3, + "index": 3, + "transcript": "\nHi, everyone. In this tutorial, we'll be discussing a kind of introduction to the HuggingFace library. So Hugging Face is a very useful library when we work with basically transformer-based models. Mostly the transformer-based model. All these models, open-source models, and data sets are available on something called the Hugging Face Hub. So we will see how we can use this Hugging Face library to load the models. To fine-tune them using some toy examples, how do I load the data sets? And how to use it for inference.\n\nSo let's get started. So here... That package in Hugging Face, which deals with transformer-based models, is called Transformers. And there's a package called Datasets that deals with all the open-source datasets. So we need to first install them. Let's first look at the whole pipeline: the whole flow of how we process an input. So, first, we have this raw text. This course is amazing. And then we have a tokenization algorithm that breaks the text into tokens. And map it to some numbers correctly.\n\nSo these numbers represent a token, right? So this code is amazing; suppose we apply some tokenization algorithm, say like byte pair encoding. Or something, or a sentence piece tokenization, something like that. And it maps it to a sequence of tokens, and tokens are represented as numbers, right? So these token numbers are basically a kind of dictionary mapping. Please provide the sentence that needs correction. So 101, say, this tells us this is a token, and its number is 101. Something like that.\n\nSo we then have a list of input IDs. And then these are passed on to the model. So when the model receives the input IDs, which are a list of token numbers, What it does is go to its embedding matrix and do a lookup. So the embedding dimension of the token is already stored. In the embedding matrix of the pre-trained model. If it is not pre-trained and we are looking to train it from scratch, Then initialization is either some random initialization or some informed initialization like Xavier initialization or something like that.\n\nAnd then, when you train the model, these embeddings are also updated. Embeddings of the tokens are also updated. So for now, let us assume that the model has been pre-trained. So once we pass the input IDs, the model maps the token IDs to their token embeddings. And then the token embeddings are passed on to the model's position encoding. is added in the case of transformers, regardless of the model architecture present Whether it's an encoder-decoder model or a decoder-only model. Whether it's an encoder-only model, it is processed accordingly.\n\nWe have already seen in our lecture, I guess, one or two weeks ago. That's how you implement transformers from scratch using PyTorch, right? There we saw how a transformer layer is implemented within the model. We saw every component of it: multi-reduction positional encoding and layer normalization. Encoder block, decoder block; we saw all that. So now we are putting all of this together into a single model. Right, and there are simple variations within the model. Like, say, GPT-2 doesn't use rotational positional encoding or ROPE.\n\nWhereas LLaMA uses RoPE, Mistral uses sliding window attention. So there are all these slight variations in the architecture and the types of attention. They used llamas like group query attention does. So all these variations are present. But let us assume that, within the model, we have a kind of function. A module that takes the input IDs maps them to embeddings. Add any equations and encodings that are defined for that model. processes it, and the final output is logits. Logits over the vocabulary tokens.\n\nSo suppose there are 32,000 tokens in the model's vocabulary. So, the output will be a set\u2014a series of logits over these 32,000 tokens. And then it is passed through the softmax function. The softmax maps these 32,000 logits into a probability distribution over the 32,000 tokens. So it will say, \"Okay, so token I has a probability of 0.3.\" Token, this has a probability of 0.2, and just like that, there is a probability distribution. Over the entire vocabulary set, you can then do some post-processing.\n\nSo if you are ready to start, we will take the example. Sentiment classification tasks. Try to perform sentiment classification using an encoder-only model like BERT. We'll use Robota here. So, Robota, just think of it as a variation of BERT. The architecture is the same as that of BERT. We have already discussed that BERT is trained using masked language modeling. And so, what BERT does is give you a representation of an input sentence. And then you can train some linear layers on top of BERT.\n\nTo perform any classification task or anything you want with the sentence representation, This is the flow, so once you have the sentence representation, You can use it to make some predictions or something like that, right? So let us now load the model and tokenizer correctly. So, there is something in the Transformer library called AutoTokenizer. So what the auto tokenizer does is, when you give the model the path, right? We'll see where the model's path leads. You give the model the path; it automatically has a defined dictionary.\n\nSo it automatically looks into what kind of tokenizer is trained for that model, right? So during pre-training, there is a particular tokenizer organization algorithm. So when you're doing inference, you need to use the same organizational algorithm. So, the auto tokenizer handles this. You can also explicitly say that, for now, we're using, say, Robata. So you can explicitly define which tokenizer you need to use. But the Auto Tokenizer makes our jobs easy. And we have the Auto Model, which again takes in the path.\n\nand loads the corresponding model in that path. So the auto model, again, does our job, at least for now. You could have explicitly defined it. And here you write what the tasks are. So the format is that you write the auto model. And then you write for the task you are trying to complete. It can be sequence classification. It can be a mathematical language model. It can be causal language modeling. So it can be an automobile model for causal elements. It can be an automotive model for a math scale.\n\nIt can be an automobile model for sequence classification. So it's the auto model for the task you have, right? And you initialize the tokenizer as the auto tokenizer from the pretrained model. So \"from_pretrained\" is a method defined in the AutoTokenizer class. So do it from pre-trained models. And then you pass the path to the model. So the question is, where do you get the path to this model? So normally, on the web, if you search for Hugging Face hubs, You will have this Hugging Face hub.\n\nSo there you can find a list of all the models. So, if you go to the Models, you can see the task. You can filter by task; you can view the datasets. You can see the models and other things like that. Suppose you do want to perform a natural language processing task: text classification. Then, if you go to the models, you will have all these models. And we will be using a model that is trained on Robata. So you can search here. You can search for Roberta's sentiment. Roberta speaks English fluently.\n\nSo we'll be using this model. So this model has been trained on sentiment analysis tasks. Roberta is basically the same architecture as BERT, and it is trained in English. So, how can you use this model? Just go use this model feature in the transformer libraries. And there you can see that, okay? So this is what you need to do: you're going to tokenize it. So, this is what you need to do: it says \"Hugging Face.\" So just copy that and paste it. This is what we have done here, right?\n\nSo that's how you can search for a model on Hub and use it in your code. So running it will just load some binary files that correspond to the models. So, it takes some time. So let it load all the files, and let us proceed. So, how does it, as I said, what are the steps? Let us think step by step now, shall we? So, the first thing to do is to tokenize. So suppose you have an input sentence. I am excited to learn about transformers. So what you do is pass the input to the tokenizer, right? And this returned tensor is equal to PT.\n\nWhat does it do? It says that, okay, return the output in the form of PyTorch tensors. If you don't write it, it doesn't return as tensors; it returns as a normal list. And then output the results you pass to the model. The unpacked list of tokenized input: you'll see this in more detail. And then the prediction is basically based on whatever logic you use. You take the maximum of that and map it to the labels \"negative\" or \"positive,\" right? So, what is this? Okay, what do you think?\n\nNo, the dot is not defined. Maybe I'm not wrong about this. Now it should be fine. So dot was not important. So now it should be fine. So now, if we do a softmax on the outputs, we get this logic. So it says, \"OK, I'm excited to learn about transformers.\" If you look at its sentiment, there is a 0.0012% probability. That is negative with a 0.9988% probability that it is positive. So that's what is done. So let us look at it step by step. So this is the input. The input was, \"I am excited to learn about transformers.\"\n\nSo, when I pass it to the tokenizer, the tokenized output is like this. 0, 100, 437. So, this is a list of token numbers, as I mentioned. And the attention mask, if you remember, We discussed in our \"Transformers from Scratch\" tutorial that the attention mask There are two kinds of things that it handles. So when it's on the encoder side, it handles whether you have a padding token or not. If you have a padding token, the attention mask will be zero in that position. If you have a future, such as in causal language modeling,\n\nWhen you are using the decoder on the decoder side, the attention mask will inform you. whether to mask the future tokens or not right. So, for the future tokens, all future tokens for a current token will be zeroed out. So when you apply softmax, it is subtracted first in the implementation. We mix zero to negative infinity in those attention scores. And then applying softmax makes it zero attention in the attention matrix, basically, right? So this is the tokenized outputs. And then our model outputs are produced when you pass them to the models.\n\nSo, what is the output? The logit over whatever distribution of apps. So, this is a model for sequence classification. So we already have a two-dimensional output linear layer. So it outputs a two-dimensional vector of two-dimensional logits, right? So it outputs a two-dimensional tensor because it's a sentiment robot, right? And then the prediction is the arc max of that; the logic is correct. So now we will basically divide this lecture or tutorial into two or three parts. In the first part, we will look at tokenizers, correct?\n\nSo, tokenizer, like I said, you can either import AutoTokenizer or play with it. Or you can explicitly say, \"Okay, this model I know uses the DistilBERT tokenizer.\" So here we are using the DistilBERT model. So, DistilBERT is basically the same architecture as BERT. It's trained with knowledge distillation from a larger BERT model, isn't it? So, think of it as a simple BERT model. So B-E-R-T BERT. So it's an encoder-only model, right? Masked language modeling pre-training objective.\n\nSo connect with whatever you learned in the previous weeks. So this part is encoded only by the model. So you can either use the DistilBERT tokenizer, or you can just use the tokens. I auto-tokenize and just pass it, right? So you can see that there are some tokens. Special tokens like pad token, unk token, and unk tokens handle unknown words in the vocabulary. So, if there is a token that is unknown, then that is how it is handled. We'll see the classifier. During the BERT lecture, you probably saw what the classification token does.\n\nThe classification token is a token added in front of the input sentences. So it captures the representation of the whole sentence. So we can use the representation of this classification token. Only to classify the sentence or perform any kind of classification task on the sentence. Separated tokens separate into sentences. So it's generally at the end of the sentences. And the mask is basically masking the forward tokens if you perform a generation task. Things like that. So now suppose we have the input string, and PTL is great, right?\n\nSo I pass the input token and the raw input sentence to the tokenizer. So during tokenization, if we say the return tensor is equal to pt, What it does is look; now it makes sense if we remove it. Right, if we remove this. It was just a list. And if we add return tensors, what it does is convert them. into PyTorch tensors. So, that's the difference. So the attention mask tells you, okay, you need to attend to all tokens. There are no special tokens. So, whether you use tokenized inputs or input IDs,\n\nOr these tokenized inputs are basically a dictionary, aren't they? So tokenized, when you pass the input string to the tokenizer, It runs a dictionary. One key of the dictionary is the input IDs, and another key is the attention masks. So you can access the input IDs just like you access any value in the dictionary. And then you can define what the classification token ID is. The classification token ID is used for the classification token, as I mentioned. It is used to perform any kind of classification tasks.\n\nSo, the classification token contains the whole representation of this sentence. And a separate token separates two sentences, right? So first, the tokenizer does is tokenize this into a list of numbers. Token numbers. And then we have the input IDs, right? So we are seeing what the step is. And then what the tokenizer does is that it has NPTEL, which is great. It first tokenizes it into \"NPTEL is great\"; that kind of tokenization is breaking. Then what it does is add, in the case of the BERT tokenizer,\n\nWhat it does is add a CLS token to the front. And a separate token at the end, right? So we can see this. So this is a step. So the first, this is a start sentence, NPTEL is great. When you tokenize it, it tokenizes into N-P-T-E-L, which is great, isn't it? So these are some special tokens that are being handled. And then it is converted into these tokens that are going to be identified. So, N is 487. So you can easily check, can't you? You can easily check that tokenizer.decode, and you can pass the input ID, right?\n\nYou can pass on the input; I'd say 487, right? This is a 487. So it should give n, right? 487 corresponds to n. Similarly, if you pass on, say, 328, right? So what should 328 give? It is the exclamation mark! So you can just see it; look, it's an exclamation mark! So just like that, in fact, you can check what the size of the is. vocabulary of a tokenizer right. You can just get the vocabulary and take the length of the program. Then you can see, okay, there are 50,265 tokens in the DistilBERT tokenizer, right?\n\nSo the DistilBERT tokenizer can handle 50,265 tokens. So that's how you can see what the number of tokens is in a particular tokenizer. How many known tokens are there? So, these are the steps for the tokenizer. So once you have converted the tokens to IDs, What the tokenizer does is add a zero. This zero corresponds to, again, you can see that over here, as I said, tokenizer.decode. What is zero? Zero is basically our classification token. So the classification token or the start of centers; you can also see it.\n\nAt the start of the centers. This acts as our classification token. And this... 2 is our separated token. So that's the kind of thing that ends sentences. So that these two are added to the beginning of the classifier's tokens, towards the end, that is the separated token. And that is the final tokenized sentence. So if we have already discussed that, you should return tensor 0 to Pytorch. The input IDs and the attention mask, instead of a list, are returned as a. Torch tensor is correct.\n\nIn fact, you can pass a batch of inputs to the tokenizer. So instead of passing a single sentence, you can pass three sentences. So that's how training works. There are batches of sentences passed together. So that is how we exploit the parallel processing power of GPUs. We pass inputs in batches. So you can pass three sentences. NPTEL is great. Learning transformers is fun. We will learn about RLHF next week. So there are three sentences, and we set the padding to true. But what padding truly does is what we already discussed in the transformer tutorial.\n\nUpon implementing it using Python. So padding, what it does, is determine the number of the three sentences. Suppose that, in this case, there are three sentences. So one sentence is shorter than the other one. So the shorter sentence will be padded. PadTokenIDs, which we will set here to 0, are defined. So the PadTokenID is 0, for example. So then it will be padded with 0s at the end to ensure That all sentences in the batch have the same length is incorrect. And what we do with truncation is define a maximum length.\n\nAny sentence after tokenization is longer than the maximum length. The number of tokens is greater than the maximum length. It truncates to the maximum length. So that's what padding and tokenization do. So just ensure that the pad token ID is set to 0. And if we run this, we see that the pad token is a pad. And the part token ID is set to one over here, okay? So here the pad token is set to one; it's not zero; it's one. That's so fine; it can be anything: it can be zero or one.\n\nIt can be two, it can be three thousand\u2014anything. But it is a tokenizer-dependent kind of thing, isn't it? So after padding, what happens is that, you see, this was our sentence. NPTEL is great, isn't it? There is this initial CLS, these two separators, and this one is padded. So, the longest sentence is the last one. We will learn about RLHF next week. So there is no padding here. There are no single ID token. And just look at the attention mask now! So, in the first sentence, there are three \"ones\" at the end.\n\nAnd what the attention mask does is place zeros in place of the paired tokens. So it tells the model, \"Okay, these are the spatial tokens.\" Unnecessary. You don't need to pay attention to this while processing. So this reduces noise during training and unnecessary computations as well. for using these tokens. So these are after tokenization. This is the tokenization of the batch. And you can batch-decode. So you can just pass on whatever output the tokenizer gives as a matrix. Whether the rows correspond to each input example is unclear.\n\nAnd when you batch-decode, this is what you get. So CLS. So, if you batch decode, okay, this is what you get. This is the start of the sentence. Okay, this is basically C.L.S. NPTEL is great. End of the sentence: basically, a separator. And then three pads. Three pad tokens, right? Similarly, learning transformations are fun. Is there a CLS? There is a separator. There are four pad tokens. And similarly, there is no padding in the last sentence. Because it is the maximum sentence length. So when you do simple batch decoding, you have all of these special tokens.\n\nshown in your output. But if you just pass on an argument, skip special tokens equal to true. During the decoding and then during output, it skips all those padding tokens. CLS tokens, but it skips and just gives the raw text, right? So, this is tokenization. So, this is the first part, right? So if we go back to our image, we have already dealt now that How do you tokenize the raw text into input IDs? The second part is passing it to the model. Now we'll tackle the models. So, as I said, it's generally automatic.\n\nWhat is the name of the module that handles it? It is an automobile model. What is the name of the class? You can say \"automobile model\" as the task name. So, the auto model is for sequence classification. Auto model for causal elements. Like that. Or you can explicitly say that, instead of the auto model, we are using DistilBERT. So you are saying, okay, it is DistilBERT for sequence classification? It's a DistilBERT for master's. We can say it like that. So, this diagram illustrates what the model architecture looks like.\n\nYou have a model's input. You have the embedding layer here. And then you have all these layers. So, in the case of an encoder-only model, there are n stacked encoder layers. And then you have the hidden states. Which comes up to the layer, and there is a head. So, a head is basically a kind of feedforward layer or a linear layer. Feedforward network MLP or a single linear layer that simply maps. From the output vocabulary dimension to the end. Say you are doing classification with two classes.\n\nSo it maps from, say, 1,024 to 2. Output the dimension to the number of classes. And then there\u2019s the final model output. So that's how this whole model architecture looks. And, as I said, it is generally a star. Star can be an automobile model. So, star for the task name, star for the master lab, and star for sequence classification. In the case of a star, you can just keep the auto model. Or you can keep the explicit names of the models, like DistilBERT and GPT-2, correct? Like T5 and other similar items.\n\nSo, yeah. So, how do you load the model? I already said you just need to define that class, didn't I? You will create an instance of this class like an auto model for signal classification. And then there is a method called dot from pre-trend, and you pass it along the path. How do you get to the path? If you have stored it in your local checkpoint, you have already saved it. In your local environment, you just provide the local path. If you don't have it stored, you can get it from the Hugging Face Hub, right?\n\nAnd when you are doing sequence classification for these models, You also need to tell what the dimensions of the head are. How many levels are we expecting to output? So, this is how you load the model. And then, what are the inputs to the model? The input is after tokenization. That is the input. What is the output of the model? You just passed. So, what should you pass to the model? You should pass the input IDs to the model that were obtained after tokenization. And the attention mask indicates which tokens to ignore during attention computations.\n\nYou can either tell it explicitly that the input ID can model the inputs. Or input the attention mass of this, or you can just do A. Simple star model inputs. So what the star model does is model the inputs in the dictionary. So, star star basically unpacks the dictionary, and the corresponding arguments are passed on. are mapped accordingly right. So you can just print the model's inputs and the model's outputs. And just to check, this is the distribution of the labels. Okay. So this hugging phase transformer model works very well; this is basically in the backend.\n\nThis is PyTorch, right? Everything is written in PyTorch. So this works very well with PyTorch modules. These are basically just PyTorch modules renamed in some classes. So they have inherited the nn.Module class and written their own classes. Just like we did for transformers when we implemented them in Scratch. In a previous tutorial. So Hugging Face has also done that. So this works very easily with PyTorch training loops. So we already know how to train a model using PyTorch. So, you have an actual target level defined.\n\nYou have a defined loss, correct? So here, suppose we use the cross-entropy loss. So, the cross-entropy loss is between the model output logits and the labels, right? And then you get the loss, and you do the backward pass. So, what does it do? So, how do you train a model? So, you have a model defined in Python. What do you do if you can have a defined model? You define a loss function; okay, I want to use cross-interval losses. Between the model outputs and logits, you define the optimizer, right?\n\nOptimizer setup, and you pass it on. Specify the parameters: What is the learning rate of the optimizer's steps? If you want to have weight decay or not. And you pass the model parameters to the optimizer. Then what does the training loop do? You first initiate the model, correct? You take an input batch and pass it to the model. The model gives some output, doesn't it? You calculate the loss between the model's output and the gold output. which you already have as levels. Then you do optimizer.0grad, so it ensures\n\nThat the gradient in all tensors is set to zero. Then you do optimizer.step. So what does optimizer.step() do? It performs a single step of updating. So, before the optimizer.step(), you need to calculate the gradient. So for that, you do loss.backwards. So first you have the optimizer with zero gradients, then we do loss.dot().backward, right? So loss, the optimizer of zero grad, sets the gradient of each tensor to zero. Then, when you do loss dot backward, what it does is take the loss. And perform a single backpropagation.\n\nSo the backpropagation now populates the gradient field, right? So every tensor has this gradient field, right? It is defined as a gradient function, and a gradient is stored in each tensor. The tensor is basically an object. So this gradient for each tensor is stored after backpropagation. And then, when you call optimizer.step(), it updates the trainable weights as Whatever the equation of the optimizer is. It can be simple gradient descent or stochastic gradient descent. So it can simply be the parameter minus eta; eta is the learning rate.\n\ninto gradient of that parameter. It can be as simple as that, or it can be more complicated in terms of momentum. Weight decay and similar concepts are important. So this is how it works. So you can just do loss.backward() to perform a single step of backpropagation. And you can also print the loss using this cross-entropy. In fact, when you model a single forward pass, The model-hugging phase automatically computes the loss. So, if you pass this model input to the model, it outputs what you see.\n\nI have already computed this loss; you can see it, right? So it actually makes our job during the hugging phase much easier. By implementing a class for these PyTorch modules, which already provide the loss, we can streamline the process. as a part of the model output object right. Now, one thing that is very good about this transformer module from Hugging Face is is that you can access like every inner component of the model. So you have this model. So setting this model.eval sets it into a non-training mode.\n\nSo it sets the request grad equal to false, right? So now there is no gradient computation. So that's what toss.nograd does as well. So when you pass in the model and push the model, you can access the hidden state. So if you print the model's output, right? This is the model output; print it correctly. What you see is that I can maybe show you a little. This is a big model output, and if I go on top of it, I am correct. Let's look at this. The hidden states per layer are 1, 8, 7, 6, and 8.\n\nAttention, is the head size right? And just look at this. It's the output of the base model. What do you have? You have all the hidden states that are computed for each layer. And also, we have the attention computed for each head, correct? So, you already have that in the model output. Right, so these are the tokens you already said yes to. So, in fact, you can see only the number of layers. And the number of heads in the model is. So an easier way to do that is to go to model.config. You can say that the DistilBERT model has six layers.\n\nSo, six encoder blocks, right? And if you use N heads. It says twelve. So each layer has 12 attention heads, and in fact, you can access them. These heads in each layer print the attention matrices. This is computed by each head when you perform a forward pass. So maybe you can look at the code; it's very simple. We just take the Attention matrix, which is computed by each head, is important. And just plot it correctly using a heat map. The number of this will give you the number of layers in the X.\n\nand the number of heads at the number of layers on the Y-axis. The number of heads on the x-axis is fine. So now, we will just look at a simple task of fine-tuning. Until now, we have seen how to load a model and how to use tokenizers. And how do models process the inputs, and how can you access the internals? Like the attention and hidden states in a model. Now we'll see how you can fine-tune a model on a particular dataset. For this purpose, we are using a dataset called the Financial Phrase Bank, right?\n\nSo you can also find this dataset on the Hugging Face Hub, correct? If you go to the Hugging Face Hub, you will find datasets. And just search for the financial term \"Bank,\" right? Financial phrase: Bank. So this data set that we are using, right? So we are using this dataset. So, the way to load the dataset is just to use the load_dataset function. After loading, if we print what a dataset is, So, the dataset is basically an instance of the class DatasetDict. And it says that it is a train split that you are using.\n\nEach example in the dataset has two features: a sentence and a label. There are basically two, six, and four examples in the dataset. We define a truncation function that essentially restricts the size of each sentence. To a maximum of 50 tokens. We take 128 random examples from 2,264 and use them as trendsets. And 32 examples are used for the validation split. So we split the dataset into two parts: training and validation. So this is a very standard practice in ML, isn't it? And like this, we can see what the first ten sentences look like.\n\nSo if we run all of this, and then that. Okay, so these are the first ten sentences. So an example sentence is supposed to be the combined capital of these funds. It is expected to be between 100 million and 150 million euros. So this is a kind of financial stress. And this one level indicates that it's neutral; two levels indicate that it's positive. And zero indicates that it's negative, right? So, this is how the dataset looks now. Sentences are followed by their levels, aren't they?\n\nThen what you do for each example in the dataset is to tokenize it, right? So we define that there is something called a map. So, inside the map, you can define a lambda function. You must be aware of lambda functions in Python. And what the Lambda function does is. So the argument to the lambda function is each example, right? So we pass the sentence, a component of this example, through the tokenizer. With padding and truncation, we use a batch size of 16. So when we map the dataset using this defined lambda function,\n\nIt is first tokenized and then batched into 16 sentences. Or are 16 tokenized sentences put into each batch, right? Then we remove some columns and format the dataset. so that now the dataset after removing the, so what do we do? We removed the sentence column because we didn't need the raw sentences. Now that we have the tokenized input IDs, don't we? And we rename some columns, and the format is set to touch. So all are like down by touch tensors, right? So now, how does the data set look?\n\nSo now the dataset looks like we have the labels in this form. PyTorch tensors. We have the input IDs. So, there are 16 input IDs in each batch. In this 16-item list of input IDs in the batch. So, if we look at the first two examples, this is one sentence of input IDs. zeros are the padded tokens, and another, and this attention marks tells you. So this is how we have the dataset now. And there is something called a data loader in Torch that you already know about. Which basically fits these batches of data into the model.\n\nwhile training right. Now, how do you train it? We already discussed this in the Python tutorial. In this tutorial, I will tell you what to do. We define an optimizer; you define a learning scheduler. And for each epoch, what you do is train the model. that is now you set the request grad equal to true for all parameters And for the batches, you first pass the batch to the model. Initialize the gradients of the tensors to zero and then perform backpropagation, right? Optimize the step that updates the weights using the gradient computed by backpropagation.\n\nAnd then learning about a scheduler if there is a word decal, and stuffing things like that. And then you can evaluate it. So this is how a standard PyTorch training loop works, right? You can pause and maybe see this code again. But this is a standard PyTorch training loop. You must be very familiar with that by now, right? Okay. So that's how you train the model using PyTorch. But then Hugging Face provides something more powerful. It provides something called a trainer class, doesn't it?\n\nSo, the trainer class will be enough to handle most of this fine-tuning training work, right? So, for the trainer class, what you need is to load the dataset again. And in this small dataset, you just have that trend test split. and similarly we define this lambda function where this tokenized right. So, after tokenization, you need to specify your training arguments, right? So for using the trainer class of a hundred, So, what do you need to specify for the training arguments? You need to specify the batch size.\n\nSo, per device, we are using one GPU. There can be eight GPUs, can't there? So that's what this per-device feature does. And then you don\u2019t need to specify the number of training epochs. The evaluation strategy basically calculates the validation score for each epoch, doesn't it? And then how do you say, \"What is the learning rate?\" and things like that? So in the trainer class, you pass the model and all of these training arguments. You have defined right, and then. You basically pass the tokenizer and the compute matrix that you want to use.\n\nSo I won't run this because it takes a little bit of time, correct? So I won't run this here. You can just pause the code and the video. Type the code and then run it yourself, right? So, this is what the trainer module does. Now you can also specify some callbacks, such as checks on the validation accuracy. And maybe stop your training early\u2014things like that. You can add that using trainer.addCallback, like the early stopping callback. Patience is equal to one. Because you can wait for one epoch, you may see whether the validation accuracy is decreasing.\n\nor not And if it continues to decrease for two epochs, you may stop. and there is some threshold. So you can maybe read the ad callback documentation a little bit. if you want to use this early stopping functionalities And then, if you call trainer.train, I have already done that. In this way, you are informed of the validation loss and the corresponding accuracy. And the evaluation is also quite simple. You just do trainer.evaluate, and... It will automatically evaluate the validation data. If you want to make predictions on some data, you just use `trainer.predict()`.\n\nAnd pass the test. So, in the evaluation metric here in the trainer function, If you remember, in the trainer's argument and in the evaluation strategy, You have already specified the epoch, and here, compute the metrics. You already specified the metric by which you want to evaluate it. So the evaluation and prediction are also made quite simple by using the Trainer module. I strongly suggest that you go to the Hugging Face documentation for the Hugging Face trainer. It's very simple. You just searched for the Hugging Face Trainer, right?\n\nAnd you will be directed to this training module. So you go through this documentation once to familiarize yourself with all the parameters. It takes time, and how do you use that correctly? It will be really helpful. You can pause the video and run this yourself on your Colab notebook, right? So after the prediction, you get the results and such, right? Now, before we end, all the things we did until now, right? So, in this way, you can have a test example. You can simply load the fine-tuned model that you saved during training.\n\nSo, checkpointing is the thing. So here, if you see again, we wrote that it is a load-based model. at end is going to be true and save the strategy will be poke. So we are saving this. The checkpoints were saved. So, after eight epochs, we saved the checkpoint. So you can load it this way from pre-trend. Then specify the local path, as I mentioned. If you have a local path, you can specify it. Then pass the input string to the tokenizer, and you can predict and observe. So, they all use the encoder-only models.\n\nNow, how do you use a decoder-only model? Say, you just want to generate stuff, right? Generate text. So, there, it's pretty much the same. There, you also need to specify the automobile model. But now the task is just a causal LM, right? So, causal LM is basically the task of next-generation predictions. So you just load the auto model for the causal LM class. And there, from pretraining, you can specify any decoder-only model. Suppose we use GPD2 here, or say we distill GPD2, correct? And then how do you basically pass on the generated content?\n\nSo, there is a function called model.generate. So, within the model.generate, what do you pass? You pass the tokenized prompt, unpacked tokenized prompt. So first you suppose once upon a time is your prompt. So you tokenize the \"once upon a prompt\" phrase. So, this GPT tokenizer is basically... Auto-tokenizer from a pre-trained GPT-2 model. Like before, it is very similar to what we did for the decoder-only model. The only difference is that, during model loading, you have to specify the auto model for the causal element, right?\n\nSo you just do model.generate() and pass the tokenized input IDs. What is the maximum length, and how many tokens do you want to generate? So I just restrict it to 50. And this doSampleEqualToTrue basically does the sampling. So it doesn't, if you do doSampleEqualToFalse, it does grid decoding. So, we have already discussed grid decoding, haven't we? So here I do doSampleEqualToTrue, and top is equal to 0.9. So we have already discussed nuclear sampling and top sampling, haven't we?\n\nSo they are specified. You can also specify the temperature parameter instead of the top-p. You can specify a temperature of 0.3 instead of top-p. You can also use top-k sampling. Like top k is equal to something based on what you want it to be. So here I perform nucleus sampling, and there I generate 10 possible completions. Once upon a time, right? So if I like \"Once Upon a Time\" for its great uncertainty, the truth of this matter or something that GPT-2 is generating. So this is basically a sentence completion task where you give a prompt.\n\nAnd you expect the model to complete the prompt. Or generate something based on the prompt. Now, there is something called a pipeline in the function of the pipeline. The pipeline class automates your entire pipeline for a particular task. For some popular tasks. Suppose we are dealing with the task of sentiment analysis. So, Hugging Face already has a complete pipeline written for sentiment analysis. So, you just take the pipeline class; you call the pipeline class. You pass the argument, the task name, and the model, right?\n\nSo, suppose the task name here is sentiment analysis, and the model is. Say, sentiment robot, right? So, you pass that into the pipeline, and then you can simply proceed. To this object, sentiment analysis, which is an instance of the class Pipeline, Defined using the class name and the model, the sentence can simply be passed. and get its predicted output. So, there are many pipelines defined for several popular tasks. But generally, when we conduct research, we define our own tasks. And things like that are not always helpful.\n\nBut when you are doing some fast coding, you want to get things done. There are already some very well-defined tasks that you want to test. If you want to be really fast, then this pipeline function becomes very handy, doesn't it? And to conclude, I will also show you masked language modeling. So for causal language modeling tasks, we were loading the auto model for causality. For masked language modeling, what will it be? It's a very simple, very trivial auto model for a masked LM, right?\n\nSo they are also the tokenizers you call support; we're using BERT for support. So you call the BERT tokenizer and the auto model for masked LM from Hugging Face. You just give the BERT base case, whatever path you have. And what is the masked language modeling objective? The masked language modeling objective is within your input sentences and prompts. whatever you call it, there is a masked token. Now, the task of masked language modeling is to predict possible token. in place of the masked token.\n\nSo basically, where there is a masked token, The masked language model will give you a probability distribution. as output over the set of vocabulary tokens. So suppose the prompt is that I am asked to learn about Hugging Face. and the pipeline is fill mask. So fill mask is the task now, right? And you give the model the base case. And then, if you pass the prompt to the model, what does the model output? The model outputs a score that is the probability for each possible token. So, the top five tokens have been printed.\n\nSo this is the mass language modeling task. And you can also do it manually. Instead of using the pipeline, you can also do it manually. using the soft max over the output language. So, this was a brief tutorial on Hugging Face. To learn more, you should really go and check out their documentation. Read through the documentation and familiarize yourself with the different classes. and methods in HuggingFace. So, thank you, and I hope you're enjoying the course. So happy to learn!\n" + }, + { + "name": "Fine tuning LLM", + "description": "fine tuning, gpt model, downstream task, supervised learning, task adaptation", + "links": "https://example.com/resource/4", + "module": "Fine tuning LLM", + "module_id": 1, + "submodule_id": 4, + "index": 4 + }, + { + "name": "Instruction tuning", + "description": "### Summary of Video Content on Fine-Tuning Large Language Models\n\n- **[00:00:00 \u2192 00:01:39] Introduction to Model Fine-Tuning**\n - Fine-tuning is defined as **taking a pre-trained large language model and training at least one internal parameter** (weights or biases) to specialize it for a specific use case.\n - Example: GPT-3 is like a **raw diamond**; fine-tuning transforms it into a polished diamond, such as ChatGPT built on GPT-3.5 Turbo.\n - Base models like GPT-3 are trained for **next-word prediction** and act as general-purpose document completers.\n - Base models often generate generic or impractical completions for specific prompts; fine-tuned models generate **more aligned, practical, and task-specific responses**.\n\n- **[00:01:39 \u2192 00:04:09] Comparing Base Models and Fine-Tuned Models**\n - Base model example: GPT-3\u2019s response to \u201chow to fine-tune a model\u201d is a list of generic questions, not practical guidance.\n - Fine-tuned model example: Text-Davinci-003\u2019s response is a concise explanation with clear steps: select base model, adjust parameters, train the model.\n - **Fine-tuned models produce more desirable, aligned outputs** tailored to the task.\n - OpenAI demonstrated that a smaller fine-tuned model (InstructGPT 1.3 billion parameters) can outperform a much larger base model (GPT-3 with 100x more parameters), highlighting **fine-tuning\u2019s efficiency and performance advantages**.\n\n- **[00:04:09 \u2192 00:07:13] Three Ways to Fine-Tune Large Language Models**\n 1. **Self-Supervised Learning** \n - Same method used for base model training: predict next word from curated text corpus aligned with the target use case.\n - Example: Fine-tuning GPT-3 on someone's blog posts to mimic their writing style.\n \n 2. **Supervised Learning** \n - Train on input-output pairs (e.g., question-answer pairs).\n - Language models need input-output pairs reformatted as prompts using **prompt templates** to convert supervised data into a self-supervised training format.\n - Example: Question \u201cWho was the 35th President?\u201d paired with answer \u201cJohn F. Kennedy\u201d used for fine-tuning.\n \n 3. **Reinforcement Learning (RL)**\n - Used by OpenAI in InstructGPT.\n - Process:\n 1. Supervised fine-tuning.\n 2. Train a **reward model** to score completions based on human rankings.\n 3. Use RL algorithm (Proximal Policy Optimization, PPO) to update model parameters based on reward feedback.\n - This **iterative training with human feedback improves alignment and output quality**.\n\n- **[00:07:13 \u2192 00:09:46] Focus on Supervised Fine-Tuning Approach**\n - Five-step supervised fine-tuning workflow:\n 1. Choose fine-tuning task (e.g., text summarization, classification).\n 2. Prepare training dataset with input-output pairs.\n 3. Select base model (foundation or existing fine-tuned).\n 4. Fine-tune the model using supervised learning.\n 5. Evaluate model performance.\n - Three options for parameter updates during fine-tuning:\n - **Retrain all parameters:** Expensive for large models.\n - **Transfer learning:** Freeze most parameters, fine-tune only the head (last few layers); cheaper but still costly.\n - **Parameter-Efficient Fine-Tuning (PEFT):** Freeze all base model weights and **add a small set of trainable parameters** to adapt the model, significantly reducing training cost.\n\n- **[00:09:46 \u2192 00:15:38] Parameter-Efficient Fine-Tuning Using LoRA**\n - LoRA (Low-Rank Adaptation) is a popular PEFT method.\n - Concept:\n - Original model layer represented by matrix \\( W_0 \\) (size \\( d \\times k \\)), with all parameters trainable in full fine-tuning.\n - LoRA freezes \\( W_0 \\) and introduces two smaller matrices \\( A \\) (\\( r \\times k \\)) and \\( B \\) (\\( d \\times r \\)), where \\( r \\ll d, k \\).\n - The update is \\( \\Delta W = BA \\), a low-rank decomposition, added to \\( W_0 \\).\n - This reduces the number of trainable parameters drastically (e.g., from 1 million to 4,000 parameters when \\( r=2 \\), \\( d=k=1000 \\)).\n - LoRA enables **fine-tuning with far fewer parameters and computational cost** without modifying the original model weights.\n - References provided for further reading: original LoRA paper and Towards Data Science blog.\n\n- **[00:15:38 \u2192 00:18:13] Example: Fine-Tuning DistilBERT for Sentiment Analysis**\n - Used Hugging Face ecosystem: libraries include Datasets, Transformers, PEFT, Evaluate, PyTorch, NumPy.\n - Base model: DistilBERT uncased, with 67 million parameters, chosen for its smaller size enabling local training.\n - Task: binary sentiment classification (labels 0 = negative, 1 = positive).\n - Dataset: IMDb truncated dataset with 1,000 training and 1,000 validation examples.\n - Fine-tuning requires **far fewer examples than training from scratch**, highlighting efficiency.\n\n- **[00:18:13 \u2192 00:21:09] Data Preparation and Tokenization**\n - Tokenization converts text input into numerical tokens required by the model.\n - Used Hugging Face AutoTokenizer for DistilBERT.\n - Created a tokenization function:\n - Extracts text from examples.\n - Applies truncation and padding to fixed max length (512 tokens).\n - Adds a pad token if missing to handle variable-length inputs.\n - Applies tokenization function to entire dataset using map method.\n - Data structure post-tokenization includes:\n - `input_ids` (token indices)\n - `attention_mask` (indicating valid tokens vs padding)\n - `labels` (target outputs for classification).\n\n- **[00:21:09 \u2192 00:22:42] Data Collation and Evaluation Metrics**\n - Created a **data collator** for dynamic padding within batches to improve computational efficiency by padding only to the longest sequence in each batch.\n - Defined evaluation metric using the `evaluate` library, focusing on **accuracy**.\n - Computed predictions by comparing model logits for negative vs positive classes.\n - Evaluation function converts logits to predicted labels by argmax and compares to ground truth.\n\n- **[00:22:42 \u2192 00:24:02] Base Model Evaluation Before Fine-Tuning**\n - Tested base model on five sample movie review texts.\n - Results:\n - Only 2 out of 5 predictions correct.\n - Performance is roughly chance-level (50% accuracy).\n - Demonstrates need for fine-tuning to improve task-specific performance.\n\n- **[00:24:02 \u2192 00:25:27] Applying LoRA for Fine-Tuning**\n - Defined LoRA configuration parameters:\n - Task type: sequence classification.\n - Intrinsic rank \\( r \\): controls size of trainable matrices.\n - LoRA alpha: scaling factor (akin to learning rate).\n - Dropout rate for LoRA layers.\n - Modules to apply LoRA to (e.g., query layers).\n - Applied LoRA via `get_peft_model` to augment base model.\n - Result: about **1 million trainable parameters out of 67 million total**, less than 2% trainable parameters.\n - This reflects approximately **50x fewer trainable parameters** than full fine-tuning.\n\n- **[00:25:27 \u2192 00:26:25] Training Setup and Execution**\n - Defined training hyperparameters:\n - Learning rate: 0.001\n - Batch size: 4\n - Number of epochs: 10\n - Set model checkpoint paths, evaluation strategy (every epoch), and saving strategy.\n - Used Hugging Face `Trainer` class to handle training loop, evaluation, and model saving.\n - Training progress showed:\n - Decreasing training loss.\n - Increasing accuracy.\n - Increasing validation loss (sign of overfitting).\n\n- **[00:26:25 \u2192 00:27:21] Fine-Tuned Model Evaluation**\n - Re-evaluated same five examples.\n - Fine-tuned model correctly classified:\n - \u201cIt was good\u201d \u2192 positive\n - \u201cNot a fan don\u2019t recommend\u201d \u2192 negative\n - \u201cBetter than the first one\u201d \u2192 positive\n - \u201cThis is not worth watching even once\u201d \u2192 negative\n - \u201cThis one is a pass\u201d \u2192 positive (ambiguous but classified positive)\n - Performance improved notably over base model.\n\n- **[00:27:21 \u2192 00:28:00] Closing Remarks**\n - Example is instructional rather than practical; real-world fine-tuning typically involves more data and careful tuning.\n - Suggested workflow: try transfer learning first, then LoRA for further fine-tuning.\n - Encouragement to experiment with fine-tuning and explore advanced methods.\n - Call to action: like, subscribe, comment questions or suggestions.\n\n---\n\n### Key Insights\n\n- **Fine-tuning transforms general-purpose base models into task-aligned specialized models.**\n- **Smaller fine-tuned models can outperform much larger base models on specific tasks.**\n- **Three main fine-tuning methods:** self-supervised, supervised, and reinforcement learning.\n- **Parameter-Efficient Fine-Tuning (PEFT) methods like LoRA drastically reduce computational costs.**\n- **LoRA uses low-rank matrix decomposition to add trainable parameters without changing original weights.**\n- **Hugging Face ecosystem provides practical tools for fine-tuning and evaluation.**\n- **Even a small dataset (~1,000 examples) can improve model performance significantly with fine-tuning.**\n- **Dynamic padding improves training efficiency by minimizing unnecessary computation.**\n- **Monitoring metrics like accuracy and loss during training helps detect issues like overfitting.**\n\n---\n\n### Timeline Table\n\n| Timestamp | Topic/Concept | Key Points |\n|------------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------|\n| 00:00:00 - 00:01:39 | Introduction to fine-tuning | Definition, analogy (raw diamond), difference from base models |\n| 00:01:39 - 00:04:09 | Base vs fine-tuned models | More aligned responses, smaller fine-tuned model outperforms larger base model |\n| 00:04:09 - 00:07:13 | Three fine-tuning methods | Self-supervised, supervised, reinforcement learning (InstructGPT example) |\n| 00:07:13 - 00:09:46 | Supervised fine-tuning workflow | Task selection, dataset prep, model choice, fine-tuning, evaluation |\n| 00:09:46 - 00:15:38 | Parameter-efficient fine-tuning and LoRA | Concept, math explanation, parameter reduction example |\n| 00:15:38 - 00:18:13 | Example: DistilBERT + IMDb dataset | Dataset description, task setup (sentiment classification) |\n| 00:18:13 - 00:21:09 | Data tokenization and processing | Tokenizer, padding, truncation, dataset mapping |\n| 00:21:09 - 00:22:42 | Data collation and evaluation metric | Dynamic padding, accuracy metric definition |\n| 00:22:42 - 00:24:02 | Base model evaluation | Poor accuracy (~50%) on sample inputs |\n| 00:24:02 - 00:25:27 | Applying LoRA for fine-tuning | LoRA config, parameter count reduction (~2% trainable params) |\n| 00:25:27 - 00:26:25 | Training setup and execution | Hyperparameters, Trainer class, training progress |\n| 00:26:25 - 00:27:21 | Fine-tuned model evaluation | Improved classification results |\n| 00:27:21 - 00:28:00 | Conclusion and next steps | Overfitting note, transfer learning recommendation, community engagement |\n\n---\n\n### Glossary of Key Terms\n\n| Term | Definition |\n|---------------------------|---------------------------------------------------------------------------------------------------|\n| Fine-tuning | Training a pre-trained language model on a specific dataset or task by adjusting internal parameters. |\n| Base model | A general-purpose large language model trained on broad corpora, e.g., GPT-3. |\n| Parameter-Efficient Fine-Tuning (PEFT) | Fine-tuning approach that freezes most model parameters and trains a small number of additional parameters. |\n| LoRA (Low-Rank Adaptation) | PEFT method adding low-rank matrices to frozen model weights to enable efficient fine-tuning. |\n| Transfer learning | Freezing most layers of a model and fine-tuning only the final layers. |\n| Reward model | A model trained to score outputs of language models based on human preferences for RL fine-tuning. |\n| Proximal Policy Optimization (PPO) | A reinforcement learning algorithm used to optimize language models based on reward feedback. |\n| Tokenizer | A tool that converts input text into numerical tokens for language model processing. |\n| Dynamic padding | Padding sequences in a batch only up to the length of the longest sequence in that batch. |\n| Evaluation metric | A quantitative measure (e.g., accuracy) used to assess model performance. |\n\n---\n\n### References & Further Resources Mentioned\n\n- OpenAI InstructGPT papers and blog posts on fine-tuning and RLHF (Reinforcement Learning with Human Feedback).\n- LoRA original paper (linked in video description).\n- Towards Data Science blog on LoRA and fine-tuning.\n- Hugging Face documentation for Transformers, Datasets, PEFT, and Evaluate libraries.\n\n---\n\nThis summary provides a comprehensive, structured, and detailed overview of the video content grounded strictly in the provided transcript, fulfilling the requested formatting and tone.", + "links": "https://example.com/resource/5", + "module": "Instruction tuning", + "module_id": 1, + "submodule_id": 5, + "index": 5, + "transcript": "\nhey everyone I'm Shaw and this is the fifth video in the larger series on how to use large language models in practice in the previous video we talked about prompt engineering which is concerned with using large language models out of the box while prompt engineering is a very powerful approach and can handle a lot of llm use cases in practice for some applications prompt engineering just doesn't cut it and for those cases we can go one step further and fine-tune a existing large language model for a\n\nspecific use case so Navy's question is what is model fine tuning the way I like to Define it is taking a pre-trained model and training at least one internal model parameter and here I mean the internal weights or biases inside the neural network what this typically looks like is taking a pre-trained existing model like gpt3 and fine-tuning it for a particular use case for example chatgypt to use an analogy here gpt3 is like a raw diamond right out of the earth it's a diamond but it's a bit rough around\n\nthe edges fine tuning is taking this raw diamond and transforming it into something a bit more practical something that you can put on a diamond ring for example so the process of taking the raw base model of gpt3 and transforming it into the fine-tuned model of gbt 3.5 turbo for example is what gives us applications like chat GPT or any of the other incredible applications of large language models we're seeing these days to get a more concrete sense of the difference between a base model link\n\ngpt3 and a fine-tuned model let's look at this particular example we have to keep in mind that these Foundation large language models like gpg3 llama 2 or whatever your favorite large language model is these models are strictly trained to do word prediction given a sequence of words predicting the next word so when you train one of these launch language models on huge Corpus of text and documents and web pages what it essentially becomes is a document completer what that translates to in practice is if you plug into a lot of\n\nthese base models like gpt3 the prompt tell me how to find tune a model a typical completion might look something like this where it's just listing out questions like you might see in a Google search or maybe like a homework assignment or something here when I prompted gpt3 to tell me how to fine-tune a model the completion was as follows how can I control the complexity of a model how do I know when my model is done how do I test a model well this might be reasonable for gpt3 to do based on the data that it was trained on this\n\nisn't something that's very practical now let's look at the fine-tuned model completion so now we have text DaVinci zero zero three which is just one of the many fine-tuned models based on gpt3 coming from open AI we give it the same prompt tell me how to fine tune a model and this is the completion fine-tuning a model involves a adjusting the parameters of a pre-trained model in order to make it better suited for a given task there are generally three steps involved to fine-tuning a model\n\nselect a base model adjust parameters train the model while this completion may not be perfect it's much more aligned what we were hoping to get out of the language model compared to the base model's completion so if you want to learn more about model fine tuning and how open AI did their fine tuning their alignment tuning and instruction tuning check out the references in the description and comment section below so as we saw when comparing a base model to a fine-tuned model we see that the\n\nfine-tune model can generate completions that are much more aligned and desirable for our particular use case Beyond this performance there's actually a deeper reason why you might want to fine tune and that is the observation that a smaller fine-tuned model can often outperform a larger base model this was demonstrated by open AI in their instruct GPT model where they're small 1.3 billion parameter fine tuned instruct GPT model generated to completions that were preferred to gpt3 completions even though gpt3 had about\n\n100 times as many internal parameters this is one of the biggest upsides of fine tuning you don't have to rely on some massive general purpose large language model to have good performance in a particular use case or application now that we have a better understanding of what fine-tuning is and why it's so great let's look at three possible ways one can fine tune an existing large language model the first is via self-supervised learning this is the same way these base models and Foundation large language models are\n\ntrained so in other words you get your training Corpus of text and you train the model in a self-supervised way in other words you take a sequence of text like listen to your you feed it into the model and you have it predict a completion if we feed in listen to your it might spit out hard what would differentiate fine tuning with self-supervised learning versus just training a base model through self-supervised learning is that you can curate your training Corpus to align with whatever application that you're\n\ngoing to use the fine-tuned model for for example if I wanted to fine-tune gpt3 to write text in the likeness of Me Maybe I would feed it a bunch of my torch data science blogs and then that resulting fine-tuned model might be able to generate completions that are more like my style the second way we can fine tune a model is via supervised learning this is where we have a training data set consisting of inputs and Associated outputs or targets for example if we have a set of question answer pairs such\n\nas who was the 35th President of the United States and then the answer is John F Kennedy we can use this question answer pair to fine-tune an existing model to learn how to better answer questions so the reason this might be helpful as we saw before if we were to just feed in who was the 35th President of the United States into a base model the completion that it might generate is who was the 36th president of the United States who was the 40th President of the United States who is the speaker of the\n\nhouse so on and so forth but through having these question answered pairs we can fine tune the model to essentially learn how to answer questions but there's a little trick here these language models are again document completers so we actually have to massage these input output pairs a bit before we can feed it into our large language model for training one simple way we can do this is via prompt templates for example we could generate a template please answer the following question where we input the question\n\nhere so our input would go here and then we input the target here so the answer would go here and then through this process we can translate our training data set to a set of prompts and generate a training Corpus and then go back to the self-supervised approach and the final way one one can fine tune an existing model is via reinforcement learning while there are many ways one could do this I'm going to just focus on the approach outlined by open Ai and generating their instruct GPT models\n\nwhich consisted of three steps the first was supervised fine tuning so essentially what we were talking about in this second way to fine-tune a model this consists of two steps one curating your training data set and then two fine-tuning the model The Next Step was to train a reward model and all this is is essentially a model that can generate a score for a language model's completion so if it generates a good completion it'll have a high score it generates a bad completion it'll generate a low score so what this looked\n\nlike for the instruct GPT case was as follows you start with a prompt and you pass it in to your supervised fine-tuned model from here but you don't just do it once you actually do it many times so you generate multiple completions for the same prompt then you get human labelers to rank the responses from worst to best and then you can use that ranking to train the reward model which is indicated by this Square here and then the final step is to do reinforcement learning with your favorite reinforcement learning\n\nalgorithm in the case of instruct GPT they used proximal policy optimization or PPO for short what this looks like is you take the prompt you pass it in to your supervised fine-tuned model and then you pass that completion to the reward model and then the reward model will essentially give feedback to the fine-tuned model and this is how you can update the model parameters and eventually end up with a model that's fine-tuned even further I know this was a ton of information but if you want to\n\ndive deeper into any one of these approaches check out the blog in towards data science where I go into a bit more detail on each of these approaches okay so to keep things relatively simple for the remainder of the video we'll be focused just on the supervised learning approach to model fine tuning here I break that process down into five steps first choose your fine tuning task so this could be text summarization it could be text generation it could be binary classification text classification whatever it is you want\n\nto do next you prepare your training data set if you're trying to do text summarization for example you would want to have input output pairs of text and the desired summarization and then you take those input Alpha Pairs and generate a training Corpus using prompt templates for example next you want to choose your base model there are many Foundation large language models out there or there are many existing fine-tuned large language models out there and you can choose either of these as your starting place next we can fine\n\ntune the model via supervised learning and then finally we evaluate model performance there's certainly a lot of details into each of these steps but here I'm just going to focus on step number four the fine tuning the model with supervise learning and here I want to talk about three different options we have when it comes to updating the model parameters the first option is to retrain all the parameters given our neural network given our language model we go in and we tweak all the parameters\n\nbut perhaps obviously this comes with the downside when you're talking about billions tens of billions hundreds of billions of internal model parameters the computational cost for training explodes even if you're doing the most efficient tricks to speed up the training process retraining a billion parameters is going to be expensive another option we can do is transfer learning and this is essentially where we take our language model and instead of retraining all the parameters we freeze most of the parameters and only\n\nfine tune the head namely we fine tune the last few layers of the model where the model embeddings or internal representations are translated into the Target or the output layer and while transfer learning is a lot cheaper than retraining all parameters there is still another approach that we can do which is the so-called parameter efficient fine tuning this is where we take our language model and instead of just phrasing a subset of the weights we freeze all of the weights we don't change any internal model parameters\n\ninstead what we do is we augment the model with additional parameters which are trainable and the reason why this is advantageous is that it turns out that we can fine tune a model with a relatively small set of new parameters as can be seen by this beautiful picture here one of the most popular ways to do this is the so-called low rank adaptation approach or low raw for short like I mentioned in the previous slide this fine tunes a model by adding new trainable parameters here we have a cartoon of a neural network but let's\n\njust consider one layer the mapping from these inputs to this hidden layer here we can call our inputs X and then we can call the hidden layer essentially some function of X and to make this a bit more concrete we can write this as an equation h of X is just equal to X we can think of it as a vector to keep things simple and some white Matrix which is just some two-dimensional Matrix to see this a bit more visually we have our weight Matrix which is some d by K Matrix we have X which will just take to be a vector in this case the\n\nmultiplication of these two things will generate our hidden layer and for the mathesonatos here the spaces that these objects live in and so this is what the situation looks like without Lora where if we're going to do the full parameter fine-tuning what this will look like is all the parameters in this weight Matrix are trainable so here W naught is a d by K Matrix and let's just say d is a thousand K is a thousand this would translate to one million trainable parameters which may not be a big number\n\nbut when you have a lot of layers this number of triangle parameters can really explode now let's see how low raw can help us reduce the number of trainable parameters again we're just going to look at one of the layers but now we're going to add some additional parameters to the model what that looks like mathematically is we have the W naught times x is equal to H of X like we saw in the previous slide but now we're adding this additional term here which is Delta W Times X this is going to be\n\nanother weight Matrix the same shape as W naught and looking at this you might think sha how does this help us we just doubled the number of parameters yeah sure if we keep W naught Frozen we still have Delta W with the same number of parameters to deal with but let's say that we Define delta W to be the multiplication of two matrices b and a in this case our hidden layer becomes W naught times X Plus ba times x looking at this more visually we have W naught which is the same same weight Matrix we\n\nsaw in the previous slide but now we have b and a which have far fewer terms than W naught does and then what we can do is through matrix multiplication generate a matrix of the proper size namely Delta W add it to W naught multiply all that by X and generate our h of X looking at the dimensionality of these things W naught and Delta W live in the same space they're matrices of d by K B is going to be a matrix of d by R A is going to be a matrix of R by K and then h of X is going to be D by 1. the\n\nkey thing here is this R number what the authors of this method called the intrinsic rank of the model the reason that this works and we get the efficiency gains is that this R is a lot smaller than D and K to see how this plays out unlike before where W naught was trainable now these parameters are going to be Frozen and B and a are trainable and maybe as you can just tell visually from the area of this rectangle versus the areas of these two rectangles b and a contain far fewer terms than W naught to make this a bit more concrete\n\nlet's say d is equal to a thousand K is equal to a thousand and our intrinsic rank is equal to two what this translates to is 4 000 trainable parameters as opposed to the million trainable parameters we saw in the previous slide this is the power of low raw it allows you to fine tune a model with far fewer trainable parameters if you want to learn more about Laura check out the paper Linked In the description below or if you want something that's a bit more accessible check out the blog\n\nand towards data science where I talk about this a bit more let's dive into some example code and how we can use low raw to fine tune a large language model here I'm going to use the hugging face ecosystem namely pulling from libraries like data sets Transformers p e f t and evaluate which are all hugging face python libraries also importing in pi torch and numpy for some extra things with our Imports the next step is to choose our base model here I use distill burnt uncased which is a base model\n\navailable on hugging faces model repository this is what the model card looks like we can see that it only has 67 million parameters in it and then there's a lot more information about it on the model card here we're gonna take distilbert uncased and we're gonna fine tune it to do sentiment analysis we're going to have it take in some text and generate a label of either positive or negative based on the sentiment of the input text so to do that we need to Define some label Maps so here we're\n\njust defining that 0 is going to be negative and one is going to mean positive and vice versa that negative means zero and positive means one now we can take these label maps and we can take our model checkpoint and we can plug it into this Nifty Auto model for sequence classification and class available from the Transformers library and very easily we import this base model specifically ready to do binary classification the way this works is that hugging face has all these base models and has many versions of them\n\nwhere they replace the head of the model for many different tasks and we can get a better sense of this from the Transformers documentation as shown here you can see that this Auto model for sequence classification has a lot of Base models that it can build on top of here we're using distilber which is a smaller version of bird here but there are several models you can choose from the reason I went with distillbird is because it only has 67 million parameters and it can actually run on my machine the next step is to load the\n\ndata set so here I've actually just made the data set available on the hugging face data set repository so you should be able to load it pretty easily it's called IMDb truncated it's a data set of IMDb movie reviews with an Associated positive or negative label if we print the data set it looks something like this there are two parts to it there's this train part and then there's this validation part and then you can see that both the training and validation data sets have 1000 rows in them this is\n\nanother great thing about model fine tuning is that while training a large language model from scratch may require trillions of tokens or a trillion words in your training Corpus fine-tuning a model requires far fewer examples here we're only going to be using a thousand examples for model fine tuning the next step is to pre-process the data here the most important thing is we need to create a tokenizer if you've been keeping up with this series you know that tokenization is a critical step\n\nwhen working with large language models because learner networks do not understand text they understand numbers and so we need to convert the text that we pass into the large language model into a numerical form so that it can actually understand it so here we can use the auto tokenizer class from Transformers to grab the tokenizer for the particular base model that we're working with next we can create a tokenization function this is a function that defines how we will take each example from our training data set and\n\ntranslate it from text to numbers this will take in examples which is coming from our training data set and you see we're extracting the text so going back to the previous slide you can see that our training data set has two features it has a label and a piece of text so you can imagine each row of this training data set has text and it has a label associated with that text so when we go over here the examples is just like a row from this data set and we're grabbing the text from that example and\n\nthen what we do is we Define the side that we want to truncate truncation is important because the examples that we pass into the model for training need to be the same length we can either achieve this by truncating long sequences or padding short sequences to like a predetermined fixed length or a combination of the two so we're just choosing the current truncation side to be left and here we're tokenizing the text here's our tokenizer that we defined up here passing in the text we're returning numpy tensors we're\n\ndoing the truncation and we defined how to do that here and then we're defining our max length and this will return our tokenized inputs since the tokenizer does not have a pad token this is a special token that you can add to a sequence which will essentially be ignored by the large language model here we're adding a pad token and then we're updating the model to handle this additional token that we just created finally we've applied this tokenize function to all the data in our data set\n\nusing this map method here we have our data set and we plan this map method we pass in our tokenized function and it'll output a tokenized version of our data set to see what the output looks like we have another data set dictionary we have the training and validation data sets but now you see we have have these additional features we don't only have the text in the label but we also have input IDs and we also have this attention mask one other thing we can do at this point is to create a data\n\ncollator this is essentially something that will dynamically pad examples in a given batch to be as long as the longest sequence in that batch for example if we have four examples in our batch the longest sequence has 500 but the others have shorter ones it'll dynamically pad the shorter sequences to match the longer one and the reason why this is helpful is because if you pad your sequences dynamically like this with a collater it's a lot more computationally efficient than padding all your examples\n\nacross all 1000 training examples because you might just have one very long sequence at 512 that is creating unnecessary data that you have to process next we want to define a valuation metrics so this is how we will monitor the performance of the model during training so here I just did something simple I'm going to import the accurac see from the evaluate python Library so we can package our evaluation strategy into a function that here I'm going to call compute metrics and so here we're not restricted to just using\n\none evaluation metric or even just using accuracy as an evaluation metric but just to keep things simple here I just stick with accuracy here we take a model output and we unpack it into a prediction and label the predictions here are the logits and so it's going to have two elements one associated with the negative class and one associated with the Positive class and all this is doing is evaluating which element is larger and whichever one is larger is going to become the label so if the zeroth element is larger the ARG Max\n\nwill return zero and that'll become the model prediction and vice versa if the first element is the largest this will return a one and then that will become the model prediction and then here we just compute accuracy by comparing the model prediction to the ground truth label so before training our find tuned model we can evaluate the performance of the base model out of the box so let's see what that looks like here we're going to generate a list of examples such as it was good not a fan don't\n\nrecommend the better than the first one this is not worth watching even once and then this one is a pass then what we do is for each piece of text in this list we're gonna tokenize it compute the logits so basically we're going to pass it into the model and take the logits out then we're going to convert the logits to a label either a zero or one and so the output looks like this we have the untrained model predictions it was good the model says this has a negative sentiment not a fan don't\n\nrecommend the model says this as a negative sentiment so that's correct better than the first one the model says this has a negative sentiment even though that's probably positive this is not worth watching even once model says it's a negative sentiment which is correct and then this one is a pass and the model signs a negative sentiment to them as you can see it got two out of five correctly essentially this model is as good as chance as flipping a coin it's right about half the time which is\n\nwhat we would expect from this unfine-tuned based model so now let's see how we can use Lora to fine-tune this model and hopefully get some better performance the first thing we need to do is Define our low configuration parameters first is the task type we're saying we're going to be doing sequence classification next we Define the intrinsic rank of the trainable weight matrices so that was that smaller number that allowed b and a to have far fewer parameters than just W naught next we\n\nDefine the lower Alpha value which is essentially a parameter that's like the learning rate when using the atom Optimizer then we Define the low raw Dropout which is just the probability of Dropout and that's where we randomly zero internal parameters during training finally we Define which modules we want to apply low raw to and so here we're only going to apply it to the query layers and then we can use these configuration settings and update our model to get another model but one that\n\nis ready to be fine-tuned using low raw and so that's pretty easy we just use this get p e f t model by passing in our original model and then our config from above then we can easily print the number of trainable parameters in our model and we can see it's about a million out of this 67 million that are in the base model so you can see that we're going to be fine-tuning less than two percent of the model parameters so it's just a huge cost savings like 50 times fewer model parameters than if we\n\nwere to do the full parameter fine tuning next we're going to Define our hyper parameters and training arguments so here we put the learning rate as .001 we put the batch size as four and the number of epochs as 10. next we say where we want the model to be saved here I dynamically create a name so it'll be the model checkpoint Dash low raw text classification learning rates what we defined before batch size is what we put before find weight Decay as 0.01 then we set the evaluation strategy as Epoch so\n\nevery Epoch is going to compute those evaluation metrics the safe strategy is every Epoch is going to save the model parameters and then load best model at the end so at the end of training it's going to return us the best version of the model then we just plug everything to this trainer class trainer takes in the model and takes in the learning arguments it takes in our training and validation data sets it takes in our tokenizer it takes in our data collator and it takes in our evaluation metrics\n\nput that all into this trainer class and then we train the model using this dot train method so during training these metrics will be generated so we can see the epochs the training loss the validation loss and the accuracy so as you can see the training loss is decreasing which is good and the accuracy is increasing which is good but you can see that the validation loss is increasing so this is a sign of overfitting which I'll comment on in a bit here now that we have our fine-tuned model in hand we can evaluate its\n\nperformance on those same five examples that we evaluated before 5 fine tuning basically same code copy pasted but here's the different output the text it was good is now correctly being classified as positive not a fan don't recommend is correctly classified as negative better than the first one correctly classified as positive and then this is not worth watching even one correctly classified as negative and then this one this one is a pass it's classified as positive but this one's a\n\nlittle tricky even though we don't get perfect performance on these five like baby examples we do see that the model is performing a little bit better and so returning back to the overfitting problem this example is meant to be more instructive than practical in practice before jumping to low raw one thing we might have tried is to Simply do transfer learning to see how close we can get to something that does sentiment analysis well after doing the transfer learning then maybe we would use low Rod\n\nto fine tune the model even further either way I hope this example was instructed and gave you an idea of how you can start fine-tuning your very own large language models if you enjoyed this content please consider liking subscribing and sharing it with others if you have any questions or suggestions for future content please feel free to drop those in the comment section below and as always thank you so much for your time and thanks for watching\n" + }, + { + "name": "Prompt based learning", + "description": "### Summary of Video Content on Instruction Fine-Tuning\n\n- **[00:00:13 \u2192 00:03:24] Introduction to Instruction Fine-Tuning and Base Models**\n\n - **Instruction fine-tuning (instruction tuning)** is a recent advancement in language modeling, enabling models to better follow instructions and have natural conversations.\n - Traditional **decoder-based language models** (also called **base models**) are trained on massive text corpora via **next word prediction**.\n - Such base models encode vast world knowledge and language understanding but have **limitations in following instructions**.\n - Example: When asked \"What is the national flower of India?\", a base model might continue with related questions rather than providing the expected answer (\"The lotus\").\n - This happens because base models **do not inherently understand or follow instructions**; they merely predict probable continuations.\n - **Key takeaway:** Next-word prediction alone does not guarantee instruction adherence, motivating the need for instruction tuning.\n\n- **[00:03:24 \u2192 00:07:35] Multitask Learning and Instruction Descriptions**\n\n - **Multitask learning** combines training on multiple tasks (e.g., sentiment analysis, machine translation) to improve overall performance.\n - During multitask training, models receive input-output examples **without explicit task labels or instructions**.\n - At test time, encountering **new tasks unseen during training** (e.g., Corpus of Linguistic Acceptability - COLA) causes models to struggle.\n - The model cannot recognize or properly respond to these new tasks due to lack of task description.\n - Solution: **Describe tasks using natural language instructions** (e.g., \"Predict the sentiment of the sentence,\" or \"Translate to Hindi\").\n - By supplying instructions along with input-output examples during training, the model learns to **interpret instructions**.\n - At inference, the model can handle new tasks by understanding instructions describing them.\n - **Instruction tuning enables generalization to unseen tasks described by natural language instructions.**\n\n- **[00:07:35 \u2192 00:11:07] What is Instruction Tuning?**\n\n - Instruction tuning is a **fine-tuning process** where a large language model is trained on datasets containing:\n - A natural language **instruction** describing the task.\n - An **input** (optional, depending on task).\n - The corresponding **target output**.\n - This improves the model\u2019s ability to **understand, reason, and follow instructions**, including for unseen tasks.\n - Example from \"Fine-tuned Language Models as Zero-Shot Learners\" paper:\n - Training tasks include common sense reasoning, translation, etc.\n - Each training example contains instruction, input sentence, and output (e.g., Spanish translation).\n - At test time, the model is given a new task (natural language inference) with an instruction and input, and it provides an appropriate answer.\n - Instruction tuning contrasts with prompting/in-context learning because:\n - **Instruction tuning updates model parameters via fine-tuning.**\n - Prompting uses the base model without parameter updates.\n\n- **[00:11:07 \u2192 00:15:04] Training Mechanics of Instruction Fine-Tuning**\n\n - During training:\n - The model receives an **instruction**, optional input, and the target output.\n - The model predicts one word at a time, conditioned on the instruction and previous generated tokens.\n - The **cross-entropy loss** between predicted and target tokens is used to update model weights.\n - Architecturally:\n - **Decoder-only models:** Instruction/input and outputs are fed into the decoder with **teacher forcing** (feeding ground-truth outputs during training).\n - **Encoder-decoder models:** Instruction/input go to the encoder; outputs are generated by the decoder, also with teacher forcing.\n - The training objective is to maximize the **log-likelihood** of the target output tokens given the instruction and input.\n\n- **[00:15:04 \u2192 00:17:17] Instruction Diversity and Templates**\n\n - For the **same task**, instructions can be phrased in multiple ways.\n - Example: Natural language inference task can have various instruction templates, such as:\n - \"Based on the paragraph above, can we conclude that this is the hypothesis?\"\n - \"Can we infer the following from the premise?\"\n - \"Read the following and determine if the hypothesis can be inferred.\"\n - Training on diverse instruction templates helps the model **generalize better**.\n - This diversity in phrasing is deliberately included in instruction tuning datasets.\n\n- **[00:17:17 \u2192 00:19:08] Large-Scale Instruction Tuning Datasets**\n\n - The **Scaling Instruction Fine-Tuned Language Models** paper created a dataset with **1,836 diverse tasks**.\n - Tasks are often generated by transforming existing datasets into multiple task formats.\n - Example: From the SQuAD extractive question-answering dataset, new tasks like:\n - Query generation (generating questions from context)\n - Context generation (generating context given a question)\n - Training tasks and held-out (test) tasks are **disjoint to test generalization**.\n\n- **[00:19:08 \u2192 00:20:25] Instruction Tuning Data Formats and Prompting Variants**\n\n - Different data formats affect instruction tuning:\n - **Zero-shot instruction tuning:** Only instruction and question, no examples.\n - **Few-shot instruction tuning:** Instruction plus example(s) before test question.\n - **Chain-of-thought prompting:** Encourages step-by-step reasoning.\n - Variants with or without examples.\n - These formats impact performance and model generalization.\n\n- **[00:20:25 \u2192 00:22:06] Performance Trends with Instruction Tuning**\n\n - Performance improves with:\n - **Increasing model size (number of parameters).**\n - **Increasing number of fine-tuning tasks.**\n - Graphs show consistent upward trends in accuracy and other metrics as both scale up.\n - Larger models fine-tuned on more tasks perform significantly better on unseen tasks.\n\n- **[00:22:06 \u2192 00:23:25] Computational Cost of Instruction Tuning**\n\n - Fine-tuning requires **much less computation** than pre-training:\n - Fine-tuning compute is about **0.2% to 1.6%** of pre-training compute.\n - Instruction tuning is computationally efficient compared to training language models from scratch.\n - Experiments span different model architectures and pre-training objectives (e.g., T5\u2019s span corruption, decoder-only causal language modeling).\n - Instruction tuning **improves performance across diverse model types**.\n\n- **[00:23:25 \u2192 00:24:00] Challenges of Human-Written Instruction Datasets**\n\n - Relying on **human-written instructions** is tedious and not scalable.\n - Creating vast datasets of tasks and instructions manually is time-consuming and expensive.\n - This motivates **automatic generation of instruction tuning datasets**.\n\n- **[00:24:00 \u2192 00:31:11] Self-Instruct: Automated Instruction Dataset Generation**\n\n - The **Self-Instruct** approach automates instruction dataset creation:\n - Start with a **small seed set** of 175 human-written tasks with one instruction and one instance each.\n - Use a large language model (e.g., GPT-3) to:\n - **Generate new instructions** based on a sample of seed tasks.\n - **Generate input-output pairs** (instances) for these instructions.\n - **Filter** generated data for quality and diversity.\n - Filtering includes:\n - Checking task type (classification vs. non-classification).\n - Ensuring balanced representation of class labels for classification tasks by generating inputs conditioned on output labels.\n - Removing duplicates and contradictory pairs.\n - Length constraints and excluding unsupported keywords (e.g., image, graph).\n - Using Rouge-L similarity to ensure new instructions are sufficiently different from existing ones.\n - This pipeline enables **scaling instruction tuning datasets without human labor**.\n - Example generated instruction: \"Write a story with three characters: a person, an animal, and an object,\" with corresponding inputs and outputs created automatically.\n\n- **[00:31:11 \u2192 00:32:30] Closing Remarks and Future Directions**\n\n - Instruction tuning is a foundational technique for teaching models to follow instructions.\n - Related techniques like **Reinforcement Learning from Human Feedback (RLHF)** build upon instruction tuning to better align models with human preferences.\n - The lecture foreshadows discussion on **parameter-efficient fine-tuning methods**, which update fewer parameters and reduce computational costs.\n - Encouragement to read survey papers on instruction tuning for in-depth understanding.\n\n---\n\n### Key Insights and Definitions\n\n| Term | Definition / Explanation |\n|-------------------------------|------------------------------------------------------------------------------------------------------------|\n| Base Model | Pre-trained language model trained via next-word prediction on large text corpora. |\n| Instruction Fine-Tuning | Fine-tuning language models on datasets containing natural language instructions, inputs, and outputs. |\n| Multitask Learning | Training on multiple tasks simultaneously without explicit task labels or instructions. |\n| Teacher Forcing | Training technique where ground-truth tokens are fed as input during training instead of model-generated tokens. |\n| Self-Instruct | Method for automatic generation of instruction tuning datasets using a language model to generate tasks and instances. |\n| Classification Task | Tasks with finite set of possible outputs (labels). |\n| Non-Classification Task | Open-ended tasks with potentially infinite or unbounded outputs. |\n| Rouge-L Similarity Score | Metric to compare similarity between two pieces of text, used here to filter similar instructions. |\n| Chain-of-Thought Prompting | Prompting technique encouraging step-by-step reasoning in model responses. |\n| Reinforcement Learning from Human Feedback (RLHF) | Technique to further improve models by aligning outputs with human values and preferences using feedback. |\n\n---\n\n### Timeline and Process Flow Table\n\n| Timestamp | Event/Concept |\n|---------------------|-----------------------------------------------------------------------------------------------------------------------------------|\n| 00:00:13 - 00:03:24 | Introduction to base models and their limitations in following instructions. |\n| 00:03:24 - 00:07:35 | Explanation of multitask learning and the importance of describing tasks via natural language instructions. |\n| 00:07:35 - 00:11:07 | Definition and examples of instruction tuning; difference from prompting/in-context learning. |\n| 00:11:07 - 00:15:04 | Training mechanics of instruction fine-tuning including architectures and teacher forcing. |\n| 00:15:04 - 00:17:17 | Importance of diverse instruction phrasing for the same task to improve generalization. |\n| 00:17:17 - 00:19:08 | Large-scale instruction tuning datasets and task generation from existing datasets (e.g., SQuAD). |\n| 00:19:08 - 00:20:25 | Different data formats for instruction tuning (zero-shot, few-shot, chain-of-thought). |\n| 00:20:25 - 00:22:06 | Empirical results showing performance increases with model size and number of fine-tuning tasks. |\n| 00:22:06 - 00:23:25 | Computational efficiency of instruction tuning compared to pre-training; generalization across architectures. |\n| 00:23:25 - 00:24:00 | Challenges of human-written instruction datasets and need for automation. |\n| 00:24:00 - 00:31:11 | Self-Instruct approach for automated instruction dataset generation: seed tasks, instruction generation, input-output generation, filtering. |\n| 00:31:11 - 00:32:30 | Summary, mention of RLHF and parameter-efficient fine-tuning to be covered in future lectures. |\n\n---\n\n### Frequently Asked Questions (FAQ)\n\n**Q1: Why can't base language models reliably follow instructions?** \nA1: Base models are trained on next-word prediction, which does not explicitly teach them to follow instructions; they learn to predict plausible continuations rather than understand or execute tasks.\n\n**Q2: How does instruction tuning improve a language model?** \nA2: By fine-tuning on datasets where each example includes a natural language instruction describing the task, the model learns to interpret and execute instructions, enabling it to generalize better to new, unseen tasks.\n\n**Q3: What is the difference between instruction tuning and prompting?** \nA3: Instruction tuning involves fine-tuning model parameters with instruction-labeled data, while prompting uses the base model with no parameter updates, relying on cleverly designed inputs to guide behavior.\n\n**Q4: What are the challenges in creating instruction tuning datasets?** \nA4: Human-written instructions are labor-intensive and hard to scale, motivating automated methods like Self-Instruct which use language models to generate instructions and task instances.\n\n**Q5: How does Self-Instruct ensure quality and diversity in generated instruction data?** \nA5: It uses filtering techniques such as classification/non-classification task identification, balancing class labels, removing duplicates, and checking instruction similarity using Rouge-L scores.\n\n---\n\n### Conclusion\n\nThis lecture extensively covers **instruction fine-tuning**, a transformative approach enabling language models to better understand and follow human instructions by training on diverse natural language task descriptions. It highlights the limits of base models, introduces multitask learning and instruction-based task descriptions, explains training methodologies and architectures, and discusses the importance of diverse instruction phrasing and large-scale datasets. The lecture also presents innovative solutions like **Self-Instruct** for automated dataset creation, improving scalability and efficiency. Finally, it contrasts instruction tuning with prompting, notes computational considerations, and points to ongoing research directions like RLHF and parameter-efficient fine-tuning.\n\n**Instruction tuning is thus a cornerstone technique advancing language models toward more adaptable, instruction-following AI systems.**", + "links": "https://example.com/resource/6", + "module": "Prompt based learning", + "module_id": 1, + "submodule_id": 6, + "index": 6, + "transcript": "\nHello everyone, today we will discuss instruction fine-tuning or instruction tuning. which is one of the key advancements in recent language modeling research. which enables us to have a conversation with language models easily Or, simply put, we can chat with language models. So, first, a quick review. So in previous weeks, we have learned about decoder-based language models. So such models are trained on vast amounts of text from the internet. Using the next word prediction task. As a result of this, these models learn to encode a great deal of information.\n\nAbout the world. They also have the ability to understand language to some extent. So these models are very powerful. They're pretty amazing, but we'll see in the upcoming slides. That they have some major limitations. One note: these pre-trained language models are also known as base models. So I'll be using the term \"base models\" throughout the lecture. So whenever I mention base models, it simply refers to pre-trained language models. For example, let's say we have been given the following prompt.\n\nWhat is the national flower of India? We have prompted the language model with this question. Now, what can happen is that the language model can generate the following response: What is the national animal of India? What is the national bird of India? So this response is nothing but the continuation of the prompt. And this is the result of the next word prediction that is happening. after the prompt. So here we see that the response contains questions that are quite common. which we can come across such questions on the web, where we see\n\nThere is a web page on general knowledge questions about India. Such questions are very common. However, this is not the desired response. Because when we asked this question of the language model, we were expecting an answer. That is, the national flower of India is the lotus. This was the desired outcome. However, since the language model is just predicting the next word, So the response to a question could be that it may. or may not follow the question. May or may not. Might follow the instructions, might not follow the instructions.\n\nBecause, as I said, it's simply just doing next-word prediction at this point. So the key takeaway from this slide is that next-word prediction, which is what is governing this response generation, That does not necessarily ensure that the model understands or follows instructions. So the reason we need instruction tuning is that. We want to teach the language models how to follow and understand instructions. So multitask learning is another very important paradigm in the natural language processing literature.\n\nSo in classical multitask learning, what we do is combine multiple tasks. We train the model, the language model, on multiple tasks with the intention. that these models will have a positive influence on one another And thereby, the final outcome will be improved across all the tasks. So here, if we take a look at this example during training, There are two tasks: sentiment analysis and machine translation. These are two tasks with which we are training the model. So when I say I'm training the model, what I'm doing is.\n\nI'm simply passing the input-output examples corresponding to these tasks to the language model. There's no explicit notion or information that I'm supplying to the language model. Saying that, \"Oh, this example corresponds to central analysis.\" This example corresponds to machine translation. No such thing is there. We are just passing the input-output examples, and that is the way. We are training the language model using multitask learning. Now, suddenly during test time, the language model encounters a new task.\n\nwhich was not there during training; for example, Here it is: COLA, which is the Corpus of Linguistic Acceptability. So what it does is that it's actually a task from the GLUE benchmark. So mainly, the task is to classify whether the sentence that I'm supplying is. Whether it's a grammatical sentence or not. So, to the language model, this is just a random example. And it has no idea what it is supposed to do with this example. So, as a result, the language model will have no clue. and will not generate a legitimate output in this case.\n\nHere, a major obstruction is that when I'm training the model, I'm simply passing the example. I'm not giving the language model any information about the task that I'm solving. Hence, when it encounters a new task during test time, it is clueless. Again, the takeaway from this slide is that using this approach. The model can only generalize within the training tasks. When it encounters a new task during test time or during inference, It struggles with it as it was not encountered during training.\n\nWhat could be a possible solution? Can we describe these tasks with instructions? Again, in this example, we can see that. Various NLP tasks can be expressed through natural language instructions. So for sentiment analysis, I can pass the instruction to predict the sentiment of the sentence. This is the instruction that defines the task, describing the task. instructing the model that what it has to do with the sentence. Action-packed thriller, and it predicts the sentiment is positive. Again, for machine translation, I'm explicitly mentioning it to the language model.\n\nThat translates the sentence to Hindi. It has been raining continuously for the past few days. So, what would the Hindi translation be? So, like this, now when I'm training the language model, I'm not only supplying the input and the output, I'm also supplying and providing the instruction along with the input. And the idea is that we want to teach the language model. How to understand instructions, because if it can understand instructions, It can generalize across some arbitrary tasks that it has to solve during test time.\n\nAnd since we'll be describing the task, for example, during inference time, when we are now taking the COLA task, we are now describing the new task by an instruction. We are asking, is this sentence grammatical? We are asking the language model, and the language model is able to respond. That it is not grammatical. So here the change is happening that now when I'm giving a new task During the test time, I'm describing the task through a natural language instruction. And the idea is that during training, since we had supplied the language model\n\nwith similar examples, which demonstrated how it is supposed to be to respond to such instructions, Now it won't fumble when it suddenly sees a new task during test time. And it would be able to solve it. So this is one of the goals of instruction tuning, where the idea is. That it should be able to generalize to new tasks seen during inference time. And these tasks will be described, and all the tasks will be described. by a natural language instructions. So with that, we can discuss: what is instruction tuning?\n\nSo to consolidate what we have spoken about until now, so instruction tuning is a fine tuning technique. We fine-tune the large language model on instruction datasets. Instruction data sets have tasks and examples; this type of natural language instructions. So these data sets contain examples that demonstrate to the language model. that how it should respond to an instruction. So this boosts their ability to understand, reason, and follow instructions. And when it comes across a new, unseen task, it becomes better at handling it.\n\nSo, in this slide, we have another example. So this is from the paper, \"Fine-tuned Language Models as Zero-Shot Learners.\" Here we can see in the example that during training, We are instruction-tuning the language model using a variety of tasks. common sense reasoning, translation, etc. And for each of these tasks, you see, If we take a look at the translation example, we are first giving the instruction. Translate the sentence into Spanish. Then we have the corresponding sentence, which would originally be the input to the language model.\n\nThe sentence we want to translate. The new office building was built in less than three months. And then the target is the translated Spanish sentence. So in instruction tuning, each example first has the instruction and the input. and output, these three components, Input is not always mandatory; sometimes, some tasks have input. Some do not, but the instructions will be there and the target output will be there. One thing is that the tasks are present here during training and inference. At this joint, there's no intersection between them.\n\nSo during inference time, we see how it is performing on a new, unseen task. which, in this case, is natural language inference. So we have the premise that says that at my age You will probably have learned one lesson, and the hypothesis is. It's not certain how many lessons you'll learn by your 30s. And the instructions, so this is originally the input, the premise, and the hypothesis. Now the instruction to the language model is: Does the premise entail the hypothesis? In this instruction, we are describing the task we want the language model to perform.\n\nAnd from this, since the language model now has some understanding. of what it is supposed to do and how it is supposed to respond to instructions, It responds by saying that it is not possible to tell. So we are not sure whether it's an entailment or not. So again, the key idea here is through instruction tuning. We want to teach the language model. We're training the language model to execute tasks based on natural language instructions. such that it can adapt efficiently to new, previously unseen tasks.\n\nOne more point that I would like to mention here is that this week, Prompting will also be covered. There's a distinction between prompting, context learning, and instruction tuning. In instruction tuning, we fine-tune the language model. The parameters of the weights of the language model are getting updated. whereas in prompting or in-context learning, no such thing happens. We are not updating any parameters of the language model. So let's discuss training laws in the case of instruction fine-tuning.\n\nSo say you're visiting Mumbai and you're curious to know. What places can you visit over here so you prompt the language model? Can you recommend some places to visit in Mumbai? And the desired target response for the language model is So here are some great places you can explore in Mumbai, along with the list of places. So basically, what would happen during training is that at each point, The prediction happens one word at a time based on the input. The instruction is fed to the language model.\n\nAlong with it whatever output has been generated by the language model till this point. For example, initially, this is the input that we passed to the language model. which is an instruction. For certain tasks, input will also be present. Like the example we saw in the previous slide. The input would also be fed to the language model. Initially, the first step, we are feeding the entire instruction and input to the language model. And what the language model would do is conditioned on this input and instruction.\n\nIt needs to predict the next word. So it would be predicting the probability distribution of the next word. for all the words in the vocabulary. And we know that this is our target word at this point. So here we can use the cross-entropy loss across each of the steps. And via that, we can train the language model. So this is how it gets updated. Now, in the next step, we'll have the instruction input. Along with that, we'll also have this word that was generated by the language model. And with that, the process goes on until the end of the sentence token.\n\nWould be encountered. And with that, the generation is complete. Next is a little bit more explanation about chaining loss for decoder-only models. and encoder decoder models. So in decoder-only models, what happens is we'll be feeding so i1 to i5 is the instruction or the input That we are feeding to the language model. And o1 to o4 are the outputs that a language model is generating. So here are these o1, o2, o3, and o4, which are colored in red. So these are the actual target outputs that we desire.\n\nSo this is marked in red because we are using teacher forcing here. So we are passing the actual output to the language model. And not the one that was generated by the language model. This is a difference. So for decoder models, we're feeding the entire thing into the decoder. For encoder-decoder models, we'll be feeding the instruction and the input to the encoder. And the rest of the things, and again due to the teacher forcing, will be feeding the... actual output to the decoder and the output would also be generated\n\nfrom the decoder end. So this is just that there's a bit of a difference in architecture. between encoder-decoder and decoder models where there is only decoder component you'll be feeding everything into the decoder component When there is an encoder and a decoder component. You feed the input and the instruction to the encoder. and you get the output from the decoder. So this is just following the previous lecture. So again, if we just quickly take a look at the formula, too, I just described the idea on the previous slide.\n\nSo here you can see the formulation. So here is the log likelihood. So this is the conditional probability of generating this token, Given all the previously generated tokens and the input that We have given it to the model. And you ideally take a sum of it since this is log likelihood. you will be maximizing this. A similar formulation holds for the encoder-decoder modules as well. So another interesting thing in instruction tuning is that. There's a lot of diversity in how we can phrase the instruction.\n\nGiven a single task. For example, here we have the task of natural language inference. For this task, there are several different ways in which we can describe it. in which we can provide the instruction to the language model. All these different ways are helpful for instruction tuning. So, for this task, there are four different templates or ten different templates. That I can generate. For all of those templates, if I train the language model on them By rephrasing the instructions, we'll see that the model can learn and generalize more effectively.\n\nSo if I discuss the example in more detail, here we have the premise. We have a hypothesis. The target is whether it is an entailment or not. So the options would be \"yes\" or \"no.\" So in this template, we have a placeholder for the premise, hypothesis, and options. Other than that, the text in red is the instruction that describes the task. in various ways. So one way to talk about it is based on the paragraph above. Which is the premise, can we conclude that this is the hypothesis?\n\nSo this is one way to describe the task. Another way is to give the premise and ask, can we infer the following? And then we provide the hypothesis. Another way is to read the following and determine if the hypothesis can be inferred. from the premise. This is very explicit; we are giving and explicitly providing the premise. and hypotheses along with the options. So there are many ways to describe the same task. And we train the model when we generate the instruction in the. fine-tuning data set.\n\nWe use all these ideas and train the model on various versions of the instructions. for the same task. So here, if I talk about the data set that is present in this slide, It is from the Scaling Instruction Fine-Tuned Language Models paper. This is a very interesting paper. If anyone is interested, please feel free to read the paper. Here, what they have done is create this dataset consisting of 1,836 tasks, Which is a huge number of tasks, and they have further fine-tuned a language model. on these tasks.\n\nSo here's one interesting idea that they have used: So from a single data set, there are different varieties of tasks. that can be generated. For example, if we take the SQuAD dataset, SQuAD is originally It's an extractive question-answering dataset. We have a passage, a paragraph, and a question. And there's a specific span in the paragraph that is the answer to that question. So it's an extract of a question-answering data set. So now other tasks can also be generated from this.\n\nFor example, query generation is important. We already have the paragraph. Maybe we can generate a query from that or generate context. That we have the question: can we generate the context for that? So just from this, just from the squad dataset, We can have multiple different tasks that can be created from it. Using this idea, they were able to generate a huge number of tasks. Again, like we saw in the previous slides, there's a Distinction between which tasks appear in the training dataset.\n\nversus the ones in the held-out task. So they are disjoint. There are no common tasks that appear in both the training and held-out sets. So the authors of this paper also tried various data formats when fine-tuning. So one way is your instruction tuning, So one way is when you're instruction tuning, you provide no examples. Just ask if you have a question. Can you write a whole haiku in a single tweet? And you simply ask the question, \"Answer the following yes or no question.\" So that's it.\n\nAll you can do is give examples; if you have another question, ask. That you provided as an example to the language model. And then you ask the intended question. Another thing that you can try is using a chain of thought prompting. So you can ask the language model to reason step by step. And here there are also two variations. You can give examples. You can simply ask the question without any examples. So there are four different variations in the data format. So this can also be useful. And this technique was also discussed in the paper.\n\nIf you look at the results, first, let's start from the topmost results. Here we can see that if we increase the model's size and as well as if we increase the number of fine-tuning tasks, We see that the performance of the language model increases. So if we look at the first graph, here each of the lines Indicate a fixed number of tasks. And we see as the number of tasks is increasing, The lines are also ascending on the graph. So here, this is also indicative that with a higher number of tasks,\n\nThe performance increases. Also, now along the x-axis, we see the number of parameters of the model. So here also, if we increase the parameters of the model, We also see that there's an increasing trend. So the performance is increasing with a higher number of model parameters. Higher model size, better performance. In the second graph, again, we see along the x-axis. Now we have the number of fine-tuning tasks. And here we have three models. We have an 8 billion model; we have a 62 billion model.\n\nAnd we have 540 billion-parameter models. And we see that if we increase the model size again, We see that the graphs are above the earlier graphs. So there's an increase in performance. As we're increasing the number of tasks, the performance is also increasing. So, overall, the key takeaway is length. When you increase the model size and the number of fine-tuning parameters, The performance increases. So far, we are only focusing on the improvement in performance. Does this come at a cost?\n\nDo we have to bear a lot of computing or not? So here we compare the amount of computation that was required for pre-training. versus what was required for fine-tuning, we see that It's simply like 0.2%, 0.6%, 1.6%, which is very, very little compared to. So the compute for fine-tuning is very, very low compared to What was required for the pre-training? And so here, the authors have also tried to experiment with different varieties of tasks. So tasks with different architectures, such as the encoder-decoder model,\n\ndecoder-only model, and also with different pre-training objectives. So we have span corruption, which is the pre-training objective for T5 model. For PARM models, which are decoder-only models, Their causal language modeling is a pre-training objective. So, across different varieties of language models, the authors have tried to see. If the results are generalizable. So, if you look at this big table over here, and now if you look at the results, The difference in results between the base model and the fine-tuned version of the model.\n\nWe see that there is an increment in performance, which is written in these blue numbers. We can see. So all these blue numbers indicate that after fine-tuning the language model, We observe that the performance of the language model has improved. So the previous approach that we discussed, The instruction tuning dataset was mainly generated by. mainly consists of human-written instructions. So that becomes very tedious when there is a human in the loop. Like creating and writing instructions, it becomes very tedious.\n\nAnd it becomes difficult to scale beyond a certain point. So for that, there has been research that has explored automated ways of generating. instruction tuning data sets. And one important work in this area is that of self-instruction. So, in this slide, we have an overview of the technique that they follow. So, they start with a number of C tasks. So there are 175 C tasks. They have one instruction and one instance for each task. This is human-written. Initiate. Then there's a task pool where they sample a number of instructions.\n\nAnd they pass it to their language model, which in this case is GPT-3. And from that, they generate instructions. And there are a few details that we'll cover in the next few slides. But they clear the instruction. That is one important step. Once you have the instruction, So the LLM creates the instruction, and once you have the instruction, you make the LLM itself generate instances like input output pairs, And then there's a filtering step involved, and if it's valid, Reasonable instruction input-output example that was generated.\n\nThen it is added to the task pool, and like that, the cycle goes on. So one of the steps that we saw was generating instructions. So for instruction generation, what is done is... What is done is from the initial pool, so we have 175 seed tasks. From the seed tasks, eight tasks were sampled. And in this age task, six tasks are human-written. And two are generated by the language model. And like that, we pass instructions for those existing tasks. So these will serve as examples. Based on this, we want the language model to generate new instructions.\n\nSo these are the eight tasks that were randomly sampled. From the pool of seed tasks. And with this, new instructions will be generated. Now, once an instruction is generated, you want to identify next. whether the task corresponds to the instruction, Is that a classification task or a non-classification task? I'll mention the reason for this distinction in some time. So, how do we do that? We prompt the language model again. So here we are doing few-shot prompting. We prompt the language model with examples that given this is the task instruction,\n\nWe need to know, is this a classification task? Yes or no. So, for example, you can describe a task with the instruction: Given my personality and the job, tell me if I would be suitable. So the answer to this is yes, you can be suitable, you cannot be suitable. So there's a finite output space, and this is indeed a classification task. Another example is to give me an example of a time. When you had to use your sense of humor. So, this is not a classification task. This is quite an open-ended task.\n\nSo this is not a classification task. So we are giving the language model examples that illustrate that, given an instruction, The language model needs to predict whether the task is a classification task or not. And then finally, given all these examples, we pass to the language model. the instruction for the target task. It predicts whether it is or it is not. Until now, in step one, we have created the instructions. Step two, we found out whether the task was a classification task or not. Now, the next step for classification tasks is a separate way in which\n\nWe'll be generating the input and output. And for non-classification tasks, we'll do it in a separate way. This is because the authors of the paper saw that for classification tasks, Since there is a finite output space, the language model gives importance. to the more popular output labels. As a result, in the final dataset, the representation of examples across all the labels is balanced. Is not even. So we want more or less an equal number of examples across all the Class labels that are present in the output space for the classification task.\n\nAnd for that, what the authors do is propose this approach. Called output-first approach for instance generation in the case of a classification task. Here they provide the classification task definition and the class labels. Based on that, they ask the language model to generate an input. That corresponds to the class level. For example, they\u2019re giving the task label, which is the output label. And then they're asking the language model to generate the input or the sentence. Like that, finally, for the target task, the language model needs to perform.\n\nthis similar response, the language model needs to generate. These are all examples of few-shot prompting where we show the language model. This is the behavior I want the language model to follow. We're first showing the language model through the examples. Again, now if you talk about non-classification tasks, They're following the much more intuitive way that you give the task. Then you ask the language model to first generate the input and then the output. So this is simple. And then there's also a filtering mechanism where we see that\n\nthe new instruction that is generated should be different from the instructions that are already present in the task. So they have used the Rouge-L similarity score, and they've seen that the Rouge-L similarity score between the new instruction and the existing instruction should be less than 0.7, only then the instruction will be included in the task. Second are keywords such as image, picture, graph. Instructions that contain such keywords will be excluded. Because the language model does not have the capacity to handle such inputs.\n\nAnother is, now we have generated an instruction at the time of instance generation, Say, we come across duplicate instances. We removed them. Or, for example, we come across instances where the input is the same. And the output is different. These are also removed. And finally, there's a heuristic-based filtering where, say, So there's a constraint on the length of the instruction. So the instruction should not be too long or too short. Based on this, filtering also happens. So these are some examples of data that were generated using this mechanism.\n\nSo you can look at the last example. So here is the instruction that was generated by the language model. Write a story with three characters: a person, an animal, and an object. Then again, the input was also generated by the language model. So here we have a person named John who's a programmer. An animal, a dog; an object, a laptop. Based on that, a story was generated again by the language model. So this is also quite a reasonably valid data instance. That was generated by the language model in an automatic manner.\n\nwithout any humans in the loop So there are other examples. You can pause the video and take a look at it. Nice survey paper, \"Instruction Tuning for Large Language Models.\" I would encourage the students to read this paper if they\u2019re interested. And here in this slide are all the lists of various instruction tuning data sets. are present. So in today's lecture, we discussed instruction tuning. which is a technique by which we teach the language models to follow instructions. There are other techniques, such as reinforcement learning from human feedback.\n\nwhich is our nature which further improves upon instruction tuning And it helps the language model better align with human objectives. and preferences which is So this topic will also be taken up in the upcoming weeks. Along with that, another point is when we spoke about instruction fine-tuning. We were mainly talking about how we are fully fine-tuning the language model. So all the entire model is getting updated. So this is compute intensive and takes up a lot of memory. So there are many more efficient strategies present that are the best parameter-efficient fine-tuning.\n\nSo those strategies will also be covered in the upcoming lectures. So that's it from my end for today. Thank you for listening, and happy learning." + }, + { + "name": "Parameter efficient fine tuning", + "description": "### Summary of Video Lecture on Prompting in Large Language Models (LLMs)\n\n- **[00:00:14 \u2192 00:04:31] Introduction to Prompting and Model Scaling**\n - The lecture begins with an overview of **prompting techniques** for large language models such as ChatGPT.\n - Focus areas include:\n - How prompts influence model accuracy.\n - The effect of model size scaling on prompt effectiveness.\n - **Prompt sensitivity**, i.e., how small changes to prompts can drastically affect model output.\n - Recap of foundational model types:\n - **Encoder-only models** (e.g., BERT) trained via masked language modeling (MLM).\n - **Decoder-only models** (e.g., GPT series) trained via autoregressive or causal language modeling.\n - **Encoder-decoder models** (e.g., T5, BART) that combine both strategies.\n - Historical timeline:\n - GPT-1 (2018), GPT-2 (2019), GPT-3 (2020).\n - GPT-3 demonstrated that fine-tuning is not always necessary; instead, carefully designed prompts can achieve strong performance.\n - A recommended survey paper by Graham Neubig et al. (CMU) is highlighted for detailed reading on prompting strategies.\n - The **\"scaling war\"** between major industry players (OpenAI, Meta, Google) is noted, where models have grown enormously in parameters without significant architecture changes, resulting in **emergent properties** related to prompting.\n\n- **[00:04:31 \u2192 00:09:02] Model Size and Dataset Scaling**\n - Parameter growth over time:\n | Model | Parameters (approx.) | Layers | Key Notes |\n |-------------|---------------------|--------|--------------------------------|\n | ELMO | 93 million | *Not specified* | Early model |\n | BERT Base | 110 million | 12 | Encoder-only |\n | BERT Large | 340 million | 24 | Larger encoder-only |\n | GPT-3 Small | 125 million | 12 | Decoder-only |\n | GPT-3 | 175 billion | 96 | Massive decoder-only |\n | Megatron Turing | 530 billion | *Not specified* | Larger than GPT-3 |\n - Larger models require massive datasets for training:\n - ELMO: ~1 billion tokens.\n - RoBERTa: ~30 billion tokens.\n - GPT-3: ~300 billion tokens sourced from Common Crawl, WebText, BookCorpus, Wikipedia, among others.\n - A metaphor comparing GPT-3 (planet size) and GPT-4 (sun size) illustrates the scale difference\u2014exact GPT-4 parameters are unknown (*Not specified*).\n\n- **[00:09:02 \u2192 00:15:23] Paradigm Shift: From Pre-train & Fine-tune to Pre-train & Prompt**\n - Traditional ML pipeline:\n - Pre-train on raw data.\n - Fine-tune on task-specific data.\n - Predict/infer on new inputs.\n - GPT-3 introduced a new paradigm: **no fine-tuning needed for downstream tasks**; instead, **prompt engineering** instructs the model to perform tasks.\n - This is possible due to the model\u2019s exposure to vast data during pre-training.\n - **Few-shot learning** is an emergent property where the model can perform tasks with minimal examples in the prompt.\n - The lecture raises critical questions:\n - How does prompt quality impact accuracy?\n - Does this capability depend on model size?\n - Can smaller models replicate performance with the same prompt?\n - Fine-tuning updates model parameters via gradients, but prompting keeps model parameters fixed; only the input prompt changes.\n\n- **[00:15:23 \u2192 00:24:05] Types of Prompting: Zero-shot, One-shot, Few-shot**\n - **Zero-shot:** Model gets only the task description, no examples.\n - Example: \"Translate English to French: cheese \u2192 ?\"\n - Prompt design details (symbols, wording) can influence performance, but optimal formats are unknown.\n - **One-shot:** Task description plus one example.\n - **Few-shot:** Task description plus several examples (e.g., 2, 3, or more).\n - Prompt length is limited by model context window (e.g., 4K or 10K tokens), so large numbers of examples are impractical.\n - For large datasets, fine-tuning or parameter-efficient fine-tuning (e.g., adapter layers) may be preferable.\n - The underlying mechanism of how prompting works (\"in-context learning\") remains poorly understood.\n\n- **[00:24:05 \u2192 00:29:53] Performance Comparisons on Trivia QA (Open-domain Question Answering)**\n - Using the **Trivia QA** dataset:\n - Fine-tuned state-of-the-art (SOTA) models compared against non-fine-tuned GPT models using zero-shot, one-shot, and few-shot prompting.\n - As model size grows from 0.1B to 175B parameters:\n - Few-shot GPT-3 (175B) surpasses fine-tuned SOTA models using only 64 examples.\n - Even smaller models (~13B) with few-shot prompting approach fine-tuned model accuracy (~60-69%).\n - Possible reason: The training data likely contained the factual content underlying the Trivia QA questions, though not necessarily in question format.\n - Models are effectively retrieving or recombining knowledge learned during pre-training.\n - Limitations:\n - Exact dataset composition and model training data are often unknown (*Not specified*).\n - Out-of-distribution questions (completely novel) remain challenging.\n\n- **[00:29:53 \u2192 00:35:34] Machine Translation and Comprehension Tasks**\n - **Machine Translation:**\n - Model trained mostly on English (93%) and 7% other languages, including Indian languages.\n - Comparison between supervised fine-tuned models (SOTA) and pre-trained models with zero/one/few-shot prompting.\n - Scores measured by **BLEU** metric.\n - Observations:\n - Few-shot prompting models are competitive but generally do not surpass supervised models.\n - Performance improves monotonically with model size.\n - **Comprehension QA (e.g., CoQA, SQuAD):**\n - These tasks are harder than Trivia QA.\n - GPT models with prompting perform worse than fine-tuned models.\n - Fine-tuning still preferred for complex tasks with sufficient data.\n\n- **[00:35:34 \u2192 00:38:11] Model Size vs. Accuracy: Insights from GLUE Benchmark**\n - The GLUE benchmark compares model accuracy (score range ~65-95) against parameter counts.\n - Parameter size grows exponentially (blue line), but accuracy improves roughly linearly.\n - Large jumps in parameter size (e.g., GPT-2 to Megatron) yield marginal accuracy gains.\n - Conclusion: **Simply increasing model size does not guarantee proportional accuracy improvements.**\n\n- **[00:38:11 \u2192 00:40:23] Practical Challenges and Prompt Prefixes**\n - Large models are difficult to deploy and share due to size.\n - Proposed solution: Use **task-specific prompt prefixes** shared alongside the pre-trained model.\n - These prefixes act as task descriptors.\n - Different tasks (A, B, C) get different prompt prefixes.\n - Alternatively, users access models via APIs with built-in prompting.\n - Prompting remains the main approach to leverage large pre-trained models without fine-tuning.\n\n- **[00:40:23 \u2192 00:46:55] Formalizing Prompts and Prompt Components**\n - Definitions:\n - Input: \\(X\\) (e.g., sentence).\n - Output: \\(Y\\) (e.g., sentiment label).\n - Prompt function/template: maps \\(X\\) to a formatted input prompt for the model.\n - Example: Sentiment analysis prompt template with placeholders for input and output.\n - Prompt components:\n 1. **Prompt addition:** The input plus task instructions.\n 2. **Answer prediction:** Model\u2019s generated output.\n 3. **Answer-label mapping:** Converting free-text model output to discrete labels.\n - Earlier models (2020-21) often required all three components; modern models are more sophisticated and sometimes need fewer.\n\n- **[00:46:55 \u2192 00:49:03] Types of Prompts: Prefix vs Cloze**\n - **Prefix prompt:** Placeholder for output at the end of sentence; suited for autoregressive models (e.g., GPT).\n - **Cloze prompt:** Placeholder can be anywhere; suited for masked language models (e.g., BERT).\n - Most prompts are **hard prompts** \u2014 natural language, human-readable instructions.\n - Problem: **Hard prompts are sensitive to wording and phrasing**, causing large accuracy fluctuations.\n\n- **[00:49:03 \u2192 00:50:34] Hard Prompt Sensitivity and Soft Prompts**\n - Example of accuracy variation with 4 phrasing variants of the same prompt:\n | Prompt Variation | Accuracy (%) |\n |---------------------------------------------|--------------|\n | \"X is located in Y.\" | 31 |\n | \"In which city, country, or state is X?\" | 19 |\n | \"In which country is X located?\" | 51 |\n | *Other variation* | *Not specified* |\n - Due to this, there is a trend toward **soft prompts**, which are learned continuous vectors not interpretable by humans.\n - Soft prompts can be optimized by gradient methods; humans need not design them manually.\n\n- **[00:50:34 \u2192 00:55:08] Prompt Tuning and Prefix Tuning**\n - **Prompt tuning:** Prepend learnable embeddings (parameters) before the task description.\n - Only these embeddings are updated during fine-tuning; the rest of the model is frozen.\n - Embeddings are randomly initialized or initialized from vocabulary embeddings.\n - Small datasets suffice for tuning these prompt embeddings.\n - **Prefix tuning:** Similar, but prepends learnable parameters to all layers, not only input.\n - Both methods belong to **parameter-efficient fine-tuning (PFT)**.\n - Benefits:\n - Small size of tunable parameters.\n - Easy to share and deploy.\n - Task-specific prompt vectors can be reused across models.\n\n- **[00:55:08 \u2192 00:58:54] Empirical Results on Prompt Tuning**\n - On SuperGLUE benchmark:\n - Manual prompt design (blue line) yields lowest accuracy.\n - Full model fine-tuning (orange line) is best.\n - Prompt tuning (green line) is intermediate but approaches full fine-tuning with larger models.\n - Accuracy variance exists in mid-sized models depending on prompt quality.\n - Varying prompt length (number of embeddings prepended): \n - Increasing from 1 to 5 embeddings improves performance.\n - Beyond ~20 embeddings, gains plateau; 100 or 150 embeddings do not meaningfully improve results.\n\n- **[00:58:54 \u2192 01:00:12] Initialization of Prompt Embeddings**\n - Small models benefit from initializing prompt embeddings with sampled vocabulary or class labels.\n - Larger models perform similarly regardless of random or vocabulary-based initialization.\n - Soft prompts are **not interpretable**, making debugging or understanding difficult.\n - Therefore, hard prompts remain popular despite limitations.\n\n- **[01:00:12 \u2192 End] Closing Remarks**\n - Upcoming topics in the following modules will include:\n - Advanced prompting techniques.\n - Methods to quantify prompt sensitivity.\n - Emphasis on the evolving landscape of prompting and fine-tuning in language models.\n\n---\n\n### Key Insights and Conclusions\n\n- **Prompting is a critical interface for large language models, enabling task performance without expensive fine-tuning.**\n- **Model size growth leads to emergent prompting capabilities but does not guarantee proportional accuracy improvements.**\n- **Few-shot prompting can rival or surpass fine-tuned models on certain tasks, especially when training data is scarce.**\n- **Prompt sensitivity is a major challenge; small changes in wording can cause large accuracy fluctuations.**\n- **Soft prompts (continuous embeddings) offer a promising direction for robust, parameter-efficient fine-tuning but lack interpretability.**\n- **Prompt tuning and prefix tuning enable task adaptation by tuning small sets of parameters while freezing the main model, facilitating easier sharing and deployment.**\n- **Hard prompts remain widely used due to their interpretability and ease of human design, though they are suboptimal.**\n- **Practical deployment of large models necessitates sharing prompt parameters or using APIs rather than distributing entire models.**\n- **Benchmarking across tasks (QA, machine translation, comprehension) shows mixed results: prompting shines in some areas but fine-tuning still excels in complex or low-resource tasks.**\n\n---\n\n### Terminology Table\n\n| Term | Definition |\n|-------------------------|--------------------------------------------------------------------------------------------|\n| Prompt | Instruction given to a model to elicit a specific task output. |\n| Zero-shot prompt | Prompt with only task description, no examples. |\n| One-shot prompt | Prompt with task description and one example. |\n| Few-shot prompt | Prompt with task description and multiple examples. |\n| Hard prompt | Human-readable natural language prompt. |\n| Soft prompt | Continuous vector prompt learned through optimization, not human-readable. |\n| Prompt tuning | Fine-tuning learnable embeddings prepended to input; model parameters frozen. |\n| Prefix tuning | Fine-tuning learnable embeddings prepended across all model layers; model frozen. |\n| Fine-tuning | Updating model parameters on task-specific data. |\n| Parameter-efficient fine-tuning (PFT) | Fine-tuning only a small subset of parameters or added parameters to adapt models.|\n| BLEU score | Metric to evaluate machine translation accuracy. |\n| GLUE/SuperGLUE | Benchmark datasets for evaluating natural language understanding tasks. |\n\n---\n\n### Timeline of Major Milestones in Model and Prompting Development\n\n| Year | Milestone | Description |\n|-------|-------------------------------------------|-----------------------------------------------------------------|\n| 2018 | GPT-1 paper | First autoregressive large language model introduced. |\n| 2019 | GPT-2 release | Larger decoder-only model demonstrating powerful text generation.|\n| 2020 | GPT-3 paper | Introduced few-shot prompting paradigm; avoided fine-tuning for many tasks.|\n| 2020-21| Manual prompt engineering surge | Exploration of hard prompts, mining, paraphrasing, and gradient-based search.|\n| 2021 | Prompt tuning and prefix tuning proposed | Parameter-efficient fine-tuning methods introduced. |\n| 2022+ | Emergence of extremely large models (Megatron, GPT-4 *estimates*) | Scaling continues, prompt engineering becomes critical. |\n\n---\n\n### Frequently Asked Questions (FAQ)\n\n**Q: Why does prompt wording affect model performance so much?** \nA: Models are sensitive to syntactic and lexical variations due to training biases and tokenization; no standardized optimal prompt exists yet.\n\n**Q: Can small models perform as well as large models with prompting?** \nA: Smaller models can perform reasonably but generally do not achieve the same level of accuracy or emergent properties as very large models.\n\n**Q: What is the difference between prompt tuning and full fine-tuning?** \nA: Prompt tuning updates only small additional vectors prepended to inputs; full fine-tuning updates all or most model parameters.\n\n**Q: Are soft prompts interpretable by humans?** \nA: No, soft prompts are continuous embeddings without direct human-readable meaning.\n\n**Q: When should one prefer prompting over fine-tuning?** \nA: Prompting is preferable when data for fine-tuning is scarce or when model size makes fine-tuning computationally expensive.\n\n---\n\nThis summary captures all key points presented in the lecture, grounded strictly on the transcript content without extrapolation beyond the source.", + "links": "https://example.com/resource/7", + "module": "Parameter efficient fine tuning", + "module_id": 1, + "submodule_id": 7, + "index": 7, + "transcript": "\nWelcome back. So in today's lecture, we are going to talk about prompts, right? And you know when it comes to chat GPT kind of models, Large language models, the first question that comes to our mind is how to write a prompt, right. What is going to be the optimal prompt for a specific model, right? So in this lecture, we will discuss different types of prompting techniques, right? how prompts affect the accuracy of the models, right? And how, with the scaling and the increasing size of the models,\n\nhow it basically affects the accuracy and how a specific prompt will be responsible for it Producing accuracy across different types of models. We'll also discuss prompt sensitivity, right? We'll see that most of these models are highly sensitive to simple perturbations of prompts. Right? And that would affect the accuracy; that would affect other aspects of the model. And how can we quantify them, right? So far, we have discussed different pretraining strategies. We have seen encoder-only models, haven't we?\n\nWe have seen models like BERT, which is a pure encoder-only model, right? And these models are pre-trained with an objective called masked language modeling, right, MLM. We have seen models like GPT, which is a purely decoder-only model. And these models are trained using an autoregressive setup, right, or causal language modeling. This is called causal language modeling or an autoregressive setup. There are other models like T5 and BART, and they are encoder-decoder models. Okay, which are also trained with a kind of autoregressive setup.\n\nWhere your input will be fed to the encoder. and decoder will predict the next word Our decoder will predict if your input is part of, and if your input has noise. the decoder model will be able to essentially denoise your input, right? And you know the board model came out around 2018. The first GPT paper was written in 2018, GPT-1, and then GPT-2 came in 2019. GPT-2 was released in 2019 and then GPT-3 in 2020. Right, and the GPT-3 paper showed that the model doesn't need any fine-tuning. We just need to write prompts, and the model will be able to.\n\nunderstand your prompt, okay. We will discuss all these topics in this lecture. Okay, so I strongly suggest that you guys read this wonderful survey paper. This is a survey paper written by Graham Neubig and his team from CMU. And this is, you know, this rightly and nicely reflects the, you know, Different prompting strategies and the kind of evolution of the overall. You know, prompt, you know, prompt as a kind of aspect overall, how it evolves, right? How do we quantify different components of a prompt, and so on and so forth?\n\nIt's a very nice survey paper that I strongly recommend you read. Okay, so you know there have been, kind of, we have seen. We have witnessed that there has been a kind of war, right? Among all these giant industries, such as Meta, OpenAI, and Google, They have been building larger and larger models with more and more parameters. and so on and so forth. So this is essentially a scaling war. You just increase the size of the models without changing the framework much. And you start seeing different, emerging properties.\n\nSo one such emerging property is this prompt. Now, this particular slide gives you an overview of. how this parameter size increases over time. So earlier, we had the ELMO model, which had approximately 93 million parameters. Then BERT came with 110 million parameters and a 12-layer transformer. BERT Base, BERT Large, 24 layers, 340 million parameters, and so on. The GPT series started coming in; you know, if you look at GPT-3. Different variations of GPT-3: small, medium, large, and XL, right?\n\n125 million parameters, 12 layers, right? You have 12 attention heads, right? Dimensions of the attention head and the hidden state, right? And then, different bath sizes. And as you see, as the model size increases, the batch size also increases, doesn't it? And then your learning rate decreases. So essentially, you are training larger and larger models with more iterations. More times, right? Whereas, you know, we have been talking about models with millions of parameters. Then, you know, a couple of billion, 1.7 billion, 2.7 billion, and suddenly GPT-3.\n\nwhich came with a gigantic size of 175 billion parameters, right? This is kind of 100 times larger than GPT-3, isn't it? And in terms of layers, now you see there are 96 layers, right? 96 decoder layers right and look at the you know dimension of your hidden state. This is the number of heads: 96. And right then, this is the dimension of every head. Bash size also increased extremely significantly. So earlier it was 1 m; now it is 3.2 m. The learning rate also decreased significantly, didn't it?\n\nSo you are essentially training the model more and more. With more and more iterations, the model will basically understand the data set. At the same time, if you look at the data, So, such a gigantic model automatically needs a large dataset for training. ELMO required approximately a 1-billion-token size training set. About 3.3 billion tokens are in the training data. Robata has approximately 30 billion tokens. And then the GPT model came with 300 billion tokens in its training set. And all these data cells contributed to these 300 billion tokens: Common Crawl.\n\nWebTest, Book Corpus, and Wikipedia Corpus show the percentages of these data sets. So, both in terms of the number of parameters and the number of tokens. Present in the training set to pre-train these gigantic models. Now this kind of gives you an idea of the model's size, you know. across different years, from 2018 to 2022, You'll see models like ELMO, BERT, GPT, and other types of linear scales. Right? It increases quite linearly with time, doesn't it? So we have this Megatron Turing model and energy model.\n\n530 billion parameter models, which are even bigger than the GPT-3 model. Okay. So, and GPT-4, you know, God knows how many parameters there are. But you know, it is huge, right? If it's GPT-3, then I got this figure from Twitter, right? If GPT-3 is this much, GPT-4 will be this much, right? So, when we see a galaxy, right? We see our planet, Earth, right? And then the sun, right? It looks like this. So if you look at it, if you compare GPT-3 and GPT-4, You know, basically, you are comparing a planet.\n\nLike an arc right or whatever, and then you have the sun. Okay, but we don't know the exact number of parameters, by the way. This is, you know, a rough estimate that somebody made on Twitter. I thought this figure was a nice depiction of a comparison. between GPT-3 and GPT-4. Okay, so what does the scaling law mean? What does scaling mean? Okay, so in the GPT-3 paper, before GPT-3, If you look at the paradigm that we have discussed so far, we have had pre-training. Right? So, pre-train, fine-tune, and predict, right?\n\nSo you have a model that is pre-trained on raw documents and a raw corpus. This can be encoder-based pre-training or decoder-based pre-training. And then you fine-tune this model further for a task-specific dataset. So we discussed, for example, in the case of T5, that you pre-trained T5. on this corrupted tokens, right? And so, basically, you pass a corrupted sequence and ask the model. To predict the actual sequence. And this is basically about trying to learn the overall syntax and semantics of the language.\n\nAnd then you have many tasks. The task can be, let's say, sentiment analysis or common sense reasoning. Summarization, chat, and so forth. There are a number of datasets that we use. For example, you can use this Flan dataset. Although Flan is specifically designed for instruction fine-tuning, you can still use it. You know the data sets from Flan to fine-tune the model, right? And then you make a prediction. Okay. So, this is an advanced version of the old-school machine-learning paradigm.\n\nwhere you had, where you did not have the concept of pre-training, You had only the concept of training, and then, you know, training and predicting, right? You train the model, and then you predict, right? But here, you now you are, you are incorporating a new step This is called pre-training. Then you fine-tune it. The fine-tuning is similar to old-school training, right? You just updated some parameters for your task. And then you predict, okay, predict, or infer. So this has been the paradigm since 2019.\n\nNow this GPT-3 paper came out in 2020, and they said, you know, This model is so big that you do not need to fine-tune it. for your downstream task, right? Your model has been pre-trained, hasn't it? And your model knows that it has been pre-trained on a gigantic data set. So your model has come to know how to solve different tasks. Right now, you have a downstream task that you want to. Which do you want to perform and which do you want to solve, right? You do not need the model to fine-tune to be fine-tuned.\n\nFor this specific task, right? What you need to do is just write a suitable prompt. Okay. The prompt is nothing but an instruction. You write simple English instructions, don't you? Assuming that this is a monolingual model, right? And the model will be able to respond to the instruction. Okay. Remember this specific instruction or this specific task, which is there in your hand has the, this task may not have, They may not have been exposed to the model during the pre-training period. Right.\n\nAnd they observed this, and as the name suggests, language models are few-shot learners. And what is few-shot learning? I will discuss it later. Right? So, they observed this in their papers. And they observed this wonderful phenomenon. And they call this phenomenon an emerging property of the model. And they were also very confused. They were surprised. They were confused. Why did this suddenly happen, right? What is it about the model that makes it so powerful? Any kind of task that you want to solve, the model solves it.\n\nRight? Now, of course, the accuracy of these models may not be very high. As compared to a fine-tuned model, right? You have a fine-tuned model for your task, whereas you have a gigantic pre-trained model. which has not been fine-tuned, but you write prompts. and this model essentially follows your prompt properly and produces the output versus that output, the fine-tuned model's output. Of course, you might not be able to compare them. And this is not a fair way to compare these two items.\n\nBecause they are trained in a very different way. But still, without fine-tuning, you may not be able to achieve it. Let's say 70% to 80% of the accuracy of the fine-tuned model, okay? And that is huge because, let's say, when it comes to a task, it matters. for which you do not have enough training data. How do you fine-tune the model? You don't have enough training data to fine-tune the model. but you now write prompts and your model will be able to solve this very efficiently And that may serve your purpose.\n\nBut the question is, do you know how good the accuracy of these models is? with respect to different prompts, the first question. The second question is whether the size of the model increases, correct? Is this the case that you see this emerging phenomenon? This emerging property occurs only after a certain threshold of model size is reached. Or, this is a property of a smaller-sized model, isn't it? Can we get a similar prompt? Can we get a similar output with the same prompt? For, let's say, a 7-billion model versus a 175-billion model, right?\n\nSo these are the questions that we are going to answer in today's lecture, aren't they? Okay, so now, as I mentioned, a traditional framework, right? In a traditional machine learning framework, you fine-tune your model with different examples. So this is example one, example two, and example three. Now you give these examples one by one. And your gradient, the parameters of the model, get updated one by one. So you feed one example; of course, you don't feed examples one at a time.\n\nYou feed a batch of examples at a time. And you update the gradient; then again, you pass in a batch of examples. The gradient will be updated, and so on. So in a normal fine-tuning setup, right, your gradient is. The parameters of some of the models may be. Maybe not all the parameters; maybe some of the parameters will be updated. during fine tuning right. But in case of prompt, your model, your parameters are not updated at all. What do you need to do? You need to write prompts appropriately.\n\nAnd there is no way to understand what the optimal prompt is. for a specific model. You just have to try out multiple prompts and see which one works best. You have to quantify what you mean by \"better.\" Of course, there are some ways to automatically figure out suboptimal prompts. But still, it is human driven, right? Human intervention is needed. Therefore, you know, these days prompt engineering has become. One of the important fields for recruitment for jobs, right? Okay, so now we are talking about introducing a new term.\n\nshot learning, zero-shot learning, one-shot learning Zero-shot prompts, one-shot prompts, few-shot prompts, and so on. What is a zero-shot prompt? In zero shot prompt, let's say you want to Your task is to translate an English sentence into a French one, right? So in your prompt, you just write \"translate from English to French,\" right? And the word that you want to translate is \"cheese,\" for example, right? And then you use some symbols. So, in this case, let's say you use this symbol.\n\nNobody knows whether this symbol is suitable. Either this symbol is suitable, or a dash is suitable. Nobody knows, do they? You write this task. So, this is called a task description. You write the task description and then the task you want to perform. and you ask the model to perform it. So there is no fine-tuning. This is just a prompt. In the case of normal fine-tuning, You fine-tune the model, but here you are not fine-tuning it. Here, there are multiple questions that may arise. The first question is how to write this task's description.\n\nSo if somebody writes, let's say, \"Please translate the following English word to French,\" That can also be a task description. So, nobody knows how to write an appropriate task description. and then how to also represent your task. Whether I should use this, you know, equal sign. and then followed by, you know, this greater-than sign Or some other symbol that we should try out? Okay, nobody knows. Okay, so later on we will see if we can. So later on, we will see how to quantify the sensitivity of a prompt.\n\nWith respect to a model, right? Let's say I change this colon, right, to an arrow, for example. For this symbol followed by a colon, right, what would be the impact of this? To the accuracy? And ideally, your model should be robust enough to handle small variations. And there in the syntactic structure. of the prompt, your model should not produce diverse accuracy. It should be consistent, but nobody knows how to quantify this sensitivity. So we will discuss how to quantify this later. So this is zero shot.\n\nZero-shot means you are not giving any examples to the model. Now, in one shot, give an example. So your task description is to translate from English to French, right? And this is an example. This is your English word, and this is your French word. And then you ask your model to translate \"cheese,\" right? So when this prompt is processed, right? And remember, all these things are happening, you know, in a context set up, right? In the context setup, you were writing the task description and some delimiters.\n\nYou know the example delimiter, and you know your task, right? This is one shot because you are now giving an example to the model. Right? You can give more examples: two-shot, three-shot, and few-shot. Let's say, in this case, it's the same task description. Now you are giving three examples to the models. Right? The more examples you give to the model, the better the model should be able to perform. Understand things properly. But again, there is no guarantee of what is going to happen.\n\nThe optimal number of examples that one should provide is unclear. It should not be, let's say, 1,000 examples. Right, in your prompt, right? And you also won't be able to write 1,000 examples in your prompt. Because the prompt length is also limited, Because let's say you know an LLM can only access. A prompt length of, let's say, 4K or 10K, is that right? 10k tokens. So you are not allowed to give so many examples in your prompt. Now remember, if you have many examples, You should not use a direct prompting approach.\n\nYou can fine-tune the model, and let's say, because now. You have 1,000 examples or 2,000 examples; you can think about fine-tuning. A few layers, right? Later, we'll talk about parameter-efficient fine-tuning. There we will see that we can fine-tune something called the adapter layer. A new layer that you can add to the model is available. And you can only fine-tune those adapter layers. If you have many examples, If you don't have many examples, you can use zero-shot or few-shot prompting.\n\nSo, as I mentioned, many such examples will be fed to the model. So, this is your entire prompt, your task description, and some delimiters, right? Your first example: some delimiter; second example: some delimiter; and so on and so forth. And then the task that you want to perform is. And you ask the model to essentially keep generating tokens. Okay. It may look like this, if you guys are not aware of it. It may look like a sci-fi movie because, you know, God knows. how this actually happens because you know understanding how prompts work,\n\nRight, it is still not fully understood. People have been studying things in our lab; we have also been studying them. We have been trying to understand how you know that different prompting strategies work. In-context learning works, but it isn't very clear. The science behind this prompting technique is not very clear. So now let's look at this new paradigm: pre-training and fine-tuning, which we have seen, right? And how does the new paradigm, which involves pre-training and prompting?\n\nSo a new paradigm has emerged called pre-training, prompting, and prediction, okay? As opposed to so fine-tuning, is not needed anymore. It is repeated by prompting. Okay. Okay. So, how do we check this? You know, zero-shot prompt, one-shot prompt, two-shot prompts are out, right? What is the accuracy of the different types of prompting techniques? Let's look at this. And for this, let us use the dataset called the Trivia QA dataset. It is, you know, an open-book question-answering data. where your model is asked about some of these questions, such as\n\nWho was the most famous pop deck of poppies, and so on and so forth? So these are all standard questions that you ask. And trivia QA is a standard dataset. And on this dataset, you now have a model. You have two models and one pretrained model. One pre-trained model has been fine-tuned on this specific dataset. And then you basically use this for inference. So this trivia QA data has been split into two parts. One part is used for fine-tuning, and the other part is used for prediction, isn't it?\n\nSo, this is your fine-tuned SOTA model, a state-of-the-art model. Your other model, let's say GPT, is not fine-tuned. But in one case, you use zero shot. In one case, you use one shot, and in the other case, you use a few shots. 64 examples are given correctly. So, on the x-axis, you are increasing the number of parameters in the model. from 0.1 billion to 175 billion. On the y-axis, you see the accuracy, correct? So this is the accuracy of your fine-tuned model, and you are trying to compare it.\n\nWith your non-fine-tuned, prom-based models. As you can see, at the higher range, the 175 billion range, The few-shot model surpasses the fine-tuning models. Fine-tuning requires a lot of data, remember. whereas for few-shot, you need only 64 In this case, you need only 64 examples to write the prompts. But that model surpasses and outperforms a fine-tuned SOTA, which is quite amazing. In fact, if you look at a $13 billion model, which is much smaller than a 75 billion model and a 13 billion model.\n\nA few-shot performance is also significantly comparable to that of the fine-tuned model. Approximately 60% and approximately 68% to 69% accuracy are noted. In fact, if you look at the... If you compare the green line and the yellow line, right? Whatever. Yellow, orange, okay. You see, the difference is not that great, right? Not that great, okay? So now let us understand what this means. What do you know about the accuracy of few-shot and one-shot models? you know surpassing a fine tune model mean right.\n\nNow, if you think about it carefully, you know this particular trivia QA dataset. Right? Now the model has been trained on a gigantic web dataset. So all the data that is on the internet has been used. To pre-train these models, right? And these questions are not very familiar questions, right? These questions may have appeared in different locations in this dataset, right? Now, when the model was trained on the data, The model must have been exposed to these sentences, not necessarily in this question format.\n\nBut they must have been exposed to these sentences during pre-training. Right now, this is essentially when this question arises. During prompting, the model needs to locate the right specific parameter. and trigger that parameter which corresponds to this question Although you know I can say this easily, right? But this is not that easy; remember this ultimately at the end of the day. You have 175 billion-parameter models, right? But what I'm trying to hint at is that this data has already been exposed.\n\nThe questions that you were asking were actually from this data. So it is not extremely surprising if the model produces the correct answers, right? What might have been surprising to us was the question. Which is completely out of the book and completely out of the data set has been asked. And the model is able to accurately answer that question, isn't it? But forming such a question, as you understand, is extremely difficult\u2014enormously difficult, isn't it? It's not possible at all.\n\nI mean, given the fact that you do not know it. Which data set has been used to train this model? Because all the recent models are black boxes. We do not know which data set has been used, do we? Which model parameters, how many model parameters, and so on. Okay. Now, if you look at the training data, 7% of the training data is included. Languages other than English are available. Only 7% of the data are in other languages, and the remaining 93% of the data is in English, right? When you want to do a non-English task, let's say machine translation,\n\nEnglish to French, right? French to English, and English to German. German to English, English to Romanian, Romanian to English, right? Let's see what happens. So this is my SOTA model, which is a supervised model, correct? and these models are, now this is supervised machine translation model. This model has been trained, not pre-trained, on this task. Whereas models like Mbert and XLM are pre-trained, these models are different. And it was fine-tuned on these tasks, right? So this is supervised, and this is pre-trained and fine-tuned.\n\nAnd these models are only pre-trained. 0 shot, 1 shot, few shot. So if you look at the result, of course you wouldn't expect it. That a pre-trained model with few shots will beat a supervised model. This is not possible. You should also not expect that a few-shot model will outperform. a fine tuned model. This is not happening either. If you look at the results very carefully, from English to French, 37% accuracy, 32% accuracy here, 34% accuracy, 39% accuracy here, right? And, by the way, the scores reported here are not accurate.\n\nThese scores are essentially the BLEU scores, right? There's something called the BLEU score in machine translation. that was used to report the accuracy. Okay. What numbers are important here? 29.8, 29.7. Here, a few-shot model essentially outperforms a fine-tuned model, right? 40.2, 40.6, 38.5; this is not going well, right? 39.9, 39.5, 38.6; very competitive, okay. What I'm trying to say here is that although only 7% of the data is not in English, And there are so many languages; Indian languages are also among them.\n\nNot in a great percentage, but still, they are present in the data. And there are many languages around the world. But still, the model performs, you know, like the GPT model without fine-tuning. Performs quite competitively, right, with a SOTA model? So SOTA is a supervised model and a fine-tuned model. Okay. Now look at the accuracy, BLEU score, versus the model parameters. right, on different machine translation task. As you increase the size of the models from 0.1 billion to 175 billion, is that correct?\n\nLet's say tasks like French to English, this blue line, right? You see the increase, right? So all the dotted lines correspond to translations from English into other languages. Whereas solid lines correspond to translation from another language into English, okay? So the interesting part to note here is that there is no plateau. The improvement is somewhat monotonic, right? You still see, you know, a kind of increasing trend in accuracy. Okay. So this is interesting. Let's see some other tasks.\n\nWe have seen the question-answering trivia QA tasks. We have seen machine translations. Now, let's see the comprehension QA task. And this is a more difficult task. This is harder than the previous QA task. And here, you have datasets like COQA, right? Squad data, you know, is very popular, isn't it? And you compare a fine-tuned SOTA model with all the GPT models. Right? Here you see that the accuracy is not very comparable, is it? 90.7 and 85 are okay, but 89.1, 36, 74, 44, 93, and 69 are not very comparable, are they?\n\nSo it seems that if the task is harder, All these models, GPT models with prompting techniques, essentially struggle. Therefore, fine-tuning is still a better strategy. If you have a significant amount of data, set it. So this is another plot where they reported the glue score. This is, you know, the GLUE benchmark we talked about, right? Glue the super glue benchmark and the number of parameters on this axis. And this is the glue score, and the y and x axes indicate the time, right? So you see, you have different models like ELMo, GPT-3, and BERT.\n\nGPT-1, GPT-2, ExcelNet, and Megatron LM of different sizes. So, the size increases quite exponentially. And with the increasing size, you also see that it. So this blue line corresponds to the parameters, and the number of parameters shows exponential growth. Whereas accuracy is, as you can see, quite linear, right? Accuracy is very linear. Glue scores are linear. It's ranging from 65 to 95 degrees. So from here, you may say that there is no guarantee. That with the exponential increase in the number of parameters.\n\nThe accuracy will also increase exponentially. If you compare, let's say, Megatron LM to GPT-2, the accuracy is quite similar. This is approximately 85%, whereas if you look at the model size. This is GPT-2, and Megatron is here. So this is not Megatron; I think this is ExcelNet. Megatron is here, I suppose. But it's still correct if you look at this point and this point. Accuracy-wise, it's not a great jump from 85 to 90. Whereas the parameter size is a huge jump, okay. So it is not that easy, right? I mean, if you just increase the number of parameters, it won't be.\n\nAssuming that you know it will automatically produce good results. This may not be the case. Okay, so what are the practical challenges? Let's see. Okay. So, such large models cannot be moved very easily, right? It's not like I will upload it to the cloud. And you will download it easily, won't you? And you will run it on your machine. It's not possible. So, is it possible that we have a pre-trained model, correct? And for different tasks, I have specific prompts designed beforehand, right?\n\nAnd these prompts are essentially indicative of the tasks, Not the entire prompt, but can we think of a prefix, a prompt prefix, right? Which will essentially act as a descriptor of the model for the task, right? And can you, basically, you know, train this separately for the task, right? So for task one, this is what task A is: you have a prompt. You have some shot prompts, right? For Task B, you have some prompts. Task C, you have some prompts, and so on, right? Let's see. Or you can think of what else you can do.\n\nYou can think of three or four different models with three different APIs. People now just call these APIs and get the output. Language model prompting is the solution, as I mentioned earlier. You can use zero-shot and few-shot learning. Now let's try to formally discuss what a prompt means. A prompt is essentially an instruction to the model for performing a specific task. by the model. Now, the task can be as simple as what a bird can do. The bird can fly, right? You just wrote, \"Birds can dash.\"\n\nThe model will say that the bird can fly. Or let's say some sort of QA-type setup where you ask a JDK is developed by Dash, and the model returns to Oracle. Or it can be a summary. So you wrote this is a super long text. TL;DR. So you ask the model to summarize this line. and the model will read this and summarizes the text right. So on and so forth, and you see that the prompts are very diverse, okay. So let's look at the terminology that we generally use. And these are very formal terms.\n\nI don't think this is very important to remember. But just for the sake of completeness, let us see. Your input is X, denoted by X. Your output is Y. Let's say your input is a sentence that you want. The model to find the translation for, sorry, the sentiment for, is that correct? Your input is a sentence; let's say, \"I love the movie.\" And your output is positive sentiment or negative sentiment, right? You have a prompt function. A prompt function is also called a prompt template.\n\nOkay, it looks like this. So, this x is the input, right? Overall, it was a Z movie. So, Z is another placeholder, and x is a placeholder. Z is a placeholder, x is for the input, and y is for the output, right? So let's say you have many examples, right? And you write this way. So I hate this movie. This is the input. I hate this movie. This was a bad movie. I love this movie overall. It was a good movie, wasn't it? And so on and so forth. So you have the template, the inputs, and the output.\n\nYou just, you know, replace the placeholder with the input. And the placeholder Z is by the output, right? So your prompt looks like this. Okay. This is a field prompt. I love the movie. Overall, it is a bad movie right now, isn't it? This here Z will be filled with the word bad. Okay. It can be good. It can be bad. And you, right? So you feed this as your prompt to the model, right? And you ask the model to essentially fill in this placeholder. and your model will fill this by the word good,\n\nWhat is the answer to the prompt, okay? And what are the answers? Answers can be good, fantastic, or boring, can't they? You can also define these answers in your prompts. Now, let's look at some of the flows, shall we? Prompt addition, answer prediction, and answer-label mapping. What does it mean? So, let's say your task is to predict sentiment. So you say that or find the sentiment of the following, correct? Then, colon, and you have the template right. Overall, it was\u2014dash\u2014right.\n\nAnd you write in this way: the movie was horrible; overall, it was a Z-movie. Okay, and you give it to the model; God knows. What the model predicts, right? What does the model produce? Let us say the model produces that, yes, it was indeed a horrible movie, okay. Okay, now this is your prompt addition. This is your answer, isn't it? But remember, you have only three levels: good, fantastic, and boring. So your task is to map this answer to one of these labels, right? Now, in this case, it will be boring.\n\nThis will be boring, right? So how do we map this? This is also something that we need to figure out. And this is called answer-label mapping. Okay. By the way, today's models are very sophisticated. You don't need all three of these components. Now I'm talking about models that were present in 2020 and 2021. They were not that great in terms of different prompts. But you know, that time you had to have three different components, right? Okay, so for prompt addition, you can figure out, I mean,\n\nYou can think of templates. Right? The one that I mentioned is input; this is a template. And these are your prompts, right? For answer prediction, you know, this is the answer. That you obtained from the model, correct? And how you map fantastic to positive is something that you want to determine. So you can have another head here, which will, you know, Take this as your input and predict, let's say, two outputs. One of the two outputs, positive and negative, and so on, is correct, right? There are two types of prompts.\n\nOne is called a field prompt. So, one is called a prefix prompt. Another one is called a cloze prompt. In the prefix prompt, you add this output placeholder at the end of your sentences. Assuming that your model is an autoregressive model, is that correct? Your model will keep on generating berts, Whereas close form your placeholder can come anywhere in the sentence, right? So the prefix prompt will be useful for GPT models. And this kind of model prompt will be useful for BERT, right? Most of the prompts are hard prompts, natural language instructions, and human-readable instructions.\n\nThe problem with the hard prompt is that we do not know it. What's going to be the optimal prompt? So, people move from a hard prompt to more of a soft prompt, right? And why is a hard prompt problematic? Let's look at this example. So, this is an accuracy table where you write a prompt in four different ways. Right? So X is located in Y. Again, X and Y are placeholders. If you write in this using this template, you will get 31% accuracy. Accuracy is precision, isn't it? If you write the same thing in a different way,\n\nIn which city, country, or state is X located? Yes, you get 19% accuracy, right? If you write it this way, in which country is X located? Question mark in Y, right? You achieved 51% accuracy. Nobody knows why this is happening. So the models are sensitive to different types of hard prompts. That's why people thought we should try to shift toward a soft prompt. where you have prompts that a human can't read Because these are vectors. Prompts are going to be vectors, and humans won't be able to read them.\n\nBut after all, what matters is how the model performs. It doesn't matter what humans need or not need, But what matters is whether the model produces the right answers. Okay. Okay. So we are now moving from discrete prompt to the continuous prompt. During 2020 and 2021, there were enormous efforts in designing manual prompts. mining prompts, paraphrasing prompts, gradient-based search for hard prompts. People have tried out whether you freeze most of the models. Only keep some of the model's parameters for your downstream tasks.\n\nGiven a set of words, you ask the model to choose one. Take one of these words one by one and see how the gradient flow happens. And how the accuracy is affected, and so on. And then you decide which word is suitable for your prompt. You know, auto-prompting, and so on and so forth. But you know, these things are not very suitable or useful nowadays. So we are now moving toward a continuous or soft prompt. right, where we will essentially produce embeddings as prompts. Okay. So, we will discuss two ideas.\n\nOne is called prompt tuning, and the other is called prefix tuning. Okay. In prompt tuning, what you do? When you write the task description, write it in English, right? You prepend a set of parameters, such as a parameter vector. Right, the size of the vector is again a hyperparameter. But you prepend a new parameter to the new set of parameters. Before the task description. So, translate from English to Hindi. But before that, you prepend it, correct? Translate English to Hindi, and then blah blah blah, okay.\n\nBut you have some parameters here. Now these parameters are randomly initialized right. You can think of better installation techniques, but I will show them to you later. that random installation is good enough right. And let's say you have a T5 model, right? Okay. This is encoder, this is decoder. and your output is a translation, Hindi translation, right. You give it to the model, okay? All the parameters of the model are frozen. Only this set of parameters are unfrozen and when you, So let's say you have a tiny dataset for finding the optimal parameters right here.\n\nAnd this tiny data set is being used for fine-tuning, right? And here, when I said fine-tuning, don't mix it up with actual fine-tuning. Here, fine-tuning means that you are only fine-tuning the prepended embedding, right? So you compute a loss here; this loss gets backpropagated. End-to-end till the input. None of the parameters will be updated. Only these parameters will be updated. Okay. So this is called prompt tuning. And why this is important is that it is important. now let's say for translating English to Hindi, you have one specific prompts, shot prompts.\n\nThese are called shot prompts, right? Let's say, for summarization, you have another set of short prompts. which you basically fine-tune. For QA, you have another set of shot prompts, and so on. Now, you can actually share these shot prompts, can't you? From one computer to another very easily because this is a small vector. Right? And in this way, you can see the entire model. you just replicate the model, you share the prompt parameters, and this prompt parameters act as task specific parameters.\n\nAnd then, you know, I mean, as if these parameters correspond to that parameter, The set of parameters corresponds to the task you want to perform, right? Now, so this is prompt tuning 2021. Again, around the same time in 2021, something called prefix tuning was proposed. The same idea; the only difference is that, in the case of prefix tuning, You add or prepend these parameters not only to the input, But also with all the other layers. So every layer has an additional parameter that is prepended, right?\n\nSo you are increasing the number of parameters, but that's okay. Because this parameter size is not huge, when you fine-tune, You only fine-tune these parameters. These prompt tuning and prefix tunings also come under the Overall, the big umbrella of parameter-efficient fine-tuning is important. We will discuss it later, right? Parameter-efficient fine-tuning (PFT) is okay. And we will see later models like adapters; know that adapters are of different types. Let us say LoRa; some of you have heard of this term, right?\n\nAnd LoRa, AdaloRa, and so on, we will discuss later. What we have learned is that we want to share our model with others, right? What can we do? You can share the prompts that we learned during the fine-tuning process. and these prompts are going to essentially describe the task that you want to perform. Of course, you need to write suitable descriptions. But these prompts will be suitable for the models to understand the task, okay? Let's look at some of the results. X-axis: number of parameters; Y-axis: accuracy score.\n\nAnd here they use this superglue benchmark, right? We discussed it earlier. The blue line is a prompt for manual design; this is manual work. The orange line represents model tuning. You fine-tuned the model, right? Remember, when I say fine-tuning, this is full fine-tuning. The green line represents prompt tuning. It can be a prefix, right? Prompt tuning\u2014I mean, prefix tuning\u2014prompt tuning that we discussed, right? This is the kind of model. So, manual prompts and manual prompting are not great ideas in terms of accuracy.\n\nAs you can see here. Of course, full fine-tuning is the best, but the green line, which represents prompt tuning, It falls in between full fine-tuning and manual tuning, right? With the increasing size of the model parameters, Prompt tuning matches the output of full fine-tuning. This is very important, right? Remember, for full fine-tuning, you need to fine-tune many parameters. Where in prompt tuning do you only need to fine-tune a few parameters, correct? So, with the increasing size of the models, you may be able to achieve the same accuracy.\n\nas that of a fully fine-tuned model, okay? Okay, and you see here kind of a variance, right? It basically indicates that this accuracy is not stable, correct? So in the middle zone, right in this area. Accuracy can vary if you change the prompts. I mean, if you change the, you know, the data, right? The task's accuracy may vary. Okay, let us look at the lengths. So I said that you needed to prepend something, didn't I? Let's say you prepend only one embedding versus five embeddings.\n\n20 embeddings, 100 embeddings, and 150 embeddings, okay? With the increasing size of the models, let's see what happens, shall we? One embedding is not a very good idea, is it? Five embeddings are better than one embedding, aren't they? But between choosing 20, 100, and 150. You see, these lines are almost identical. So there is no point in choosing; there is no point in adding 150 parameters. 150 tokens, right? As a prompt, you can simply add 20 or 30 additional tokens. That is good enough for your model.\n\nSo, I talked about initializing these parameters, right? So this experiment stated that if you randomly initialize these prompt parameters. Versus using some sample vocabulary, let's say you choose a few words. which you feel are suitable for this prompt and you take the corresponding embeddings From, let's say, \"word\" to \"vehicle\" glove, and you initialize this. What happens if you are even, you know? More advanced, you want to take more classes if you want to continue.\n\nRight, the labels that you want to predict as your prompts, right? What will happen? So small models, of course, sampled vocabulary or class labels are much better than others. Random initialization. But for larger models, it doesn't matter. You can start with random initializations. It will produce the same output as the sample vocabulary. and class labels that you can use. The problem with soft prompting is that it is not at all user-friendly. You will not get to know what has happened within these parameters.\n\nThese are not interpretable. Therefore, hard prompts, although they are not a very good solution, are still used in certain contexts. But hard prompts are still chosen as the default for writing. Okay, in the next module, submodule, I would say, I will discuss a few advanced prompting techniques. and then we will discuss the way to you know measure the sensitivity of a prompt. Okay, thank you.\n" + }, + { + "name": "Incontext Learning", + "description": "### Summary of Video Content on Parameter-Efficient Fine-Tuning (PEFT) in Large Language Models (LLMs)\n\n- **[00:00:14 \u2192 00:04:04] Introduction and Background on Transfer Learning and LLMs** \nDinesh Raghu, a senior researcher at IBM Research, introduces the topic of **parameter-efficient fine-tuning (PEFT)** in large language models. He begins by reviewing the traditional **transfer learning** paradigm prior to LLMs, which involved two main phases: \n- **Pre-training:** Using large unlabeled corpora to learn world knowledge via word or sentence representations. \n- **Fine-tuning:** Transferring this learned knowledge to task-specific datasets by fully fine-tuning the model or augmenting it with additional layers. \n\nWith the advent of LLMs, the landscape changed: \n- Models became more aware and better at modeling world knowledge. \n- Additional phases like **instruction fine-tuning** and **alignment** were introduced to incorporate labeled data but for general-purpose learning rather than task-specific learning. \n- A new method called **in-context learning** emerged, where the model uses prompts to understand and perform tasks without modifying the model weights.\n\n**Advantages of in-context learning:** \n- Allows downstream users to leverage hosted LLMs via APIs without the need to host or fine-tune large models themselves. \n- Enables solving many NLP tasks by simply providing task instructions and examples as input prompts.\n\n- **[00:04:04 \u2192 00:09:41] Limitations of In-Context Learning** \nDespite its utility, in-context learning has significant drawbacks: \n- **Lower accuracy on critical tasks:** Prompting often underperforms compared to fully fine-tuned smaller models. \n- **Prompt sensitivity:** Performance is sensitive to prompt wording, order of examples, and nuances of prompt engineering. This sensitivity increases with longer inputs or outputs. \n- **Model assumptions and interpretability:** The internal assumptions made by the LLM are opaque, making it unclear what the model infers from prompts and why certain weird or spurious examples sometimes work. This unpredictability reduces user trust. \n- **Computational inefficiency:** Detailed task instructions and multiple examples increase prompt length, causing higher latency, resource consumption, and energy use. This is especially problematic for tasks with long inputs/outputs like summarization or reading comprehension.\n\n- **[00:09:41 \u2192 00:13:47] Challenges of Full Fine-Tuning** \nGiven the limitations of prompting, full fine-tuning appears as an alternative but faces practical challenges: \n- **Memory requirements:** Fine-tuning LLMs like GPT-3 (175B parameters) requires enormous GPU memory (12\u201320x trainable parameters due to optimizer states, gradients, etc.). \n- **Infrastructure barriers:** Not all organizations have the hardware to fine-tune large models, limiting accessibility and research progress. \n- **Storage overhead:** Each fine-tuned model checkpoint can be hundreds of gigabytes, making it impractical to save multiple task-specific models. \n- **Data requirements:** Large models require massive task-specific datasets to avoid overfitting; small datasets cause memorization and loss of transfer learning benefits. \n- **Serving inefficiency:** Hosting many fully fine-tuned models wastes computation and energy, especially for organizations with multiple applications.\n\n- **[00:13:47 \u2192 00:18:47] Introduction to Parameter-Efficient Fine-Tuning (PEFT)** \nPEFT aims to strike a balance between in-context learning and full fine-tuning by: \n- **Freezing most model parameters** and training only a small subset of parameters specific to the new task. \n- The incremental parameters required for tasks like QA, summarization, and classification are **orders of magnitude smaller** than full model parameters. \n\n**Advantages of PEFT:** \n- **Lower memory usage:** Reduces optimizer and gradient memory, enabling training on fewer or older GPUs (e.g., Tesla V100 instead of A100/H100). \n- **Faster convergence:** With most of the model frozen, the model leverages frozen world knowledge and maps it efficiently to the task, speeding up training. \n- **Reduced overfitting risk:** Smaller trainable parameter space lowers memorization likelihood. \n- **Mitigates catastrophic forgetting:** Unlike full fine-tuning, PEFT preserves prior knowledge, enabling better generalization across domains. \n- **Lower storage footprint:** Only small incremental weights need to be stored per task, simplifying versioning and deployment.\n\n- **[00:18:47 \u2192 00:26:57] PEFT Technique 1: Prompt Tuning (Soft Prompting)** \nPrompt tuning modifies the input prompt embeddings rather than the model weights: \n- Instead of manually engineering prompts (\"hard prompting\"), **soft prompts** are learned vectors appended to the input embeddings. \n- Only these soft prompt vectors are trainable; the rest of the model is frozen. \n- This approach is **highly parameter-efficient**, as soft prompts involve very few tokens compared to the entire model. \n- Enables **multi-task serving** by training different soft prompts per task and running the same base LLM, allowing dynamic batching and efficient scaling. \n- Widely supported by LLM hosting providers due to low hosting cost and easy API usage.\n\n**Performance comparison:** \n| Fine-tuning Method | Accuracy Trend with Model Size | Notes |\n|---------------------|-------------------------------|-----------------------------------------|\n| Full Fine-Tuning | Performs better on smaller models | Best for small models (700M \u2013 1B params) |\n| Prompt Tuning | Catches up as model size increases | Comparable at large scale (10B+ params) |\n| Hard Prompting | Consistently lower performance | Sensitive and difficult to optimize |\n\n- Increasing soft prompt length improves performance up to ~20 tokens, after which gains saturate. \n- Initializing soft prompts using embeddings of words related to the task improves convergence and stability versus random initialization. \n- Prompt tuning generalizes better on **out-of-domain datasets** compared to full fine-tuning, e.g., reading comprehension trained on Wikipedia but tested on books.\n\n- **[00:26:57 \u2192 00:35:38] PEFT Technique 2: Prefix Tuning** \nPrefix tuning extends prompt tuning by adding trainable parameters not only at the input layer but **at every transformer layer**: \n- Trainable prefixes (embeddings) are concatenated to the keys and values inside each self-attention layer throughout the network. \n- This influences every word representation at every layer, enhancing expressivity compared to prompt tuning. \n- Requires careful architectural design to avoid **unstable training**, including: \n - Parameterizing prefixes with a two-layer MLP rather than a simple embedding lookup to maintain stable gradient flow. \n - Applying layer normalization and residual connections to prevent gradient explosion or vanishing. \n- Evaluated on tasks like **table-to-text generation** and summarization with decoder-only models (e.g., GPT-2). \n- Achieves comparable performance to full fine-tuning with only **0.1% of parameters** trained. \n- Like prompt tuning, prefix tuning generalizes well to unseen domains.\n\n- **[00:35:38 \u2192 00:40:34] PEFT Technique 3: Adapters** \nAdapters were one of the earliest PEFT methods, introduced in 2019: \n- Add **small bottleneck feed-forward layers** inside each transformer block. \n- Architecture: \n - Down-project hidden states to a smaller dimension (bottleneck), apply nonlinearity, then up-project back to original size. \n - Include residual connections both in the transformer and inside the adapter to maintain stable training and preserve original model behavior initially. \n- Allows training a small number of parameters per layer while freezing the main model. \n- Hyperparameter \\( m \\) (bottleneck size) controls the trade-off between capacity and overfitting. \n- Achieves performance close to full fine-tuning with only ~3.6% of parameters trained. \n- **Disadvantages:** \n - Adds **inference latency** and computational overhead due to architectural changes. \n - Harder to swap adapters dynamically during serving compared to prompt tuning or prefix tuning.\n\n- **[00:40:34 \u2192 01:02:01] PEFT Technique 4: Low-Rank Adaptation (LoRa)** \nLoRa is a recent and influential PEFT technique, notable for combining theory and practical utility: \n\n**Theoretical foundation:** \n- Inspired by intrinsic dimensionality and rank decomposition from deep learning literature. \n- Fine-tuning can be viewed as adding an update matrix \\(\\Delta W\\) to pre-trained weights \\(W\\). \n- LoRa constrains \\(\\Delta W\\) to be **low-rank**, factorizing it as \\( BA \\) where: \n - \\(A\\) is a down-projection matrix (reducing dimension). \n - \\(B\\) is an up-projection matrix (reconstructing dimension). \n- No nonlinearity between \\(A\\) and \\(B\\) to allow easy fusion back to \\(W\\). \n- This significantly reduces trainable parameters while preserving expressivity. \n\n**Structural insights:** \n- Only modifies specific weight matrices in the transformer: **query, key, value, and output projection matrices** of attention layers. \n- Layer-wise factorization and scaling improve stability and memory efficiency. \n- Initialization strategy: \n - \\(B\\) initialized to zero to preserve original model behavior at start of training. \n - \\(A\\) initialized with small Gaussian noise to allow smooth updates. \n\n**Empirical results:** \n- Outperforms or matches full fine-tuning on complex tasks like natural language inference, SQL query generation, and summarization with a tiny fraction of parameters. \n- Allows control of model capacity and generalization via rank \\(r\\), analogous to intrinsic dimensionality. \n- QLoRa variant further reduces memory footprint during training. \n- Extensions include Long LoRa for long context inputs, LoRa Plus (differentiated learning rates for \\(A\\) and \\(B\\)), and Dylora (adaptive rank selection). \n\n**Serving considerations:** \n- LoRa weights can be fused into base model weights for efficient inference but lose easy swapping flexibility. \n- Hot swapping of LoRa parameters at batch level is possible with some latency overhead. \n\n- **[01:02:01 \u2192 01:02:31] Conclusion** \nThe lecture summarized the motivations behind PEFT and detailed four key techniques: \n- **Prompt Tuning** \n- **Prefix Tuning** \n- **Adapters** \n- **Low-Rank Adaptation (LoRa)** \n\nEach offers different trade-offs in trainable parameters, computational efficiency, and generalization. PEFT methods enable efficient fine-tuning of LLMs, making task adaptation accessible to a broader community with limited hardware and data resources, while maintaining or improving model performance.\n\n---\n\n### Key Insights and Concepts\n\n| Term/Concept | Definition/Explanation |\n|-----------------------------|-----------------------------------------------------------------------------------------------------|\n| Transfer Learning | Two-phase training with pre-training on unlabeled data and fine-tuning on task-specific data. |\n| In-Context Learning | Providing task instructions/examples as prompts to LLMs without changing model weights. |\n| Full Fine-Tuning | Updating all model parameters on task-specific data, resource-intensive and prone to overfitting. |\n| Parameter-Efficient Fine-Tuning (PEFT) | Training only a small subset of parameters while freezing most of the model. |\n| Prompt Tuning (Soft Prompting) | Learning low-dimensional trainable prompt embeddings prepended to input. |\n| Prefix Tuning | Learning trainable prefixes inserted at every transformer layer, affecting all intermediate states. |\n| Adapters | Small bottleneck feed-forward layers inserted inside transformer blocks, adding trainable parameters.|\n| Low-Rank Adaptation (LoRa) | Factorizing weight updates into low-rank matrices to reduce trainable parameters and memory. |\n| Intrinsic Dimensionality | Minimum parameter dimension needed to achieve near full fine-tuning accuracy for a task. |\n| Catastrophic Forgetting | The loss of previously learned knowledge when fine-tuning on new tasks. |\n\n---\n\n### Timeline Table of PEFT Technique Development and Concepts\n\n| Timestamp | Event/Topic |\n|----------------|------------------------------------------------------------------------------------------------|\n| 00:00:14 | Introduction to conversational AI and fine-tuning in LLM era. |\n| 00:00:54 | Traditional transfer learning phases before LLMs. |\n| 00:02:11 | LLM era changes: instruction tuning and alignment phases. |\n| 00:02:48 | Introduction of in-context learning and its benefits. |\n| 00:04:04 | Limitations of in-context learning discussed. |\n| 00:09:41 | Challenges and impracticality of full fine-tuning LLMs. |\n| 00:13:47 | Introduction and motivation for PEFT approaches. |\n| 00:18:47 | Detailed explanation of prompt tuning (soft prompting). |\n| 00:26:57 | Explanation of prefix tuning and architectural considerations. |\n| 00:35:38 | Description of adapters and their bottleneck architecture. |\n| 00:40:34 | Introduction to LoRa and theoretical background on intrinsic dimensionality. |\n| 00:53:04 | LoRa method: low-rank factorization of weight updates and key matrices to fine-tune. |\n| 01:00:02 | Training initialization and stability considerations for LoRa. |\n| 01:02:31 | Summary and conclusion on PEFT techniques and their impact. |\n\n---\n\n### FAQ\n\n**Q: Why is in-context learning insufficient for critical tasks?** \nA: It generally yields lower accuracy than full fine-tuning, is sensitive to prompt formulation, has unpredictable model assumptions, and is computationally inefficient for long inputs/outputs.\n\n**Q: What makes full fine-tuning of LLMs challenging?** \nA: It requires enormous memory, storage, data to avoid overfitting, and infrastructure, making it inaccessible for many organizations.\n\n**Q: How does PEFT help overcome these challenges?** \nA: PEFT trains only a small subset of parameters, reducing memory, data, training time, storage, and preserving the base model\u2019s knowledge for better generalization.\n\n**Q: What is the main difference between prompt tuning and prefix tuning?** \nA: Prompt tuning trains soft prompts only at the input embedding layer, while prefix tuning trains additional parameters at every transformer layer for greater impact.\n\n**Q: What are adapters?** \nA: Adapters are small bottleneck feed-forward layers inserted inside transformer blocks, enabling efficient fine-tuning but with some inference overhead.\n\n**Q: What is LoRa and why is it significant?** \nA: LoRa factorizes weight updates into low-rank matrices, drastically reducing trainable parameters while maintaining or improving performance; it balances theory and practice and is widely used.\n\n---\n\nThis professional summary captures the core content, technical details, and key takeaways from the lecture on parameter-efficient fine-tuning in large language models.", + "links": "https://example.com/resource/8", + "module": "Incontext Learning", + "module_id": 1, + "submodule_id": 8, + "index": 8, + "transcript": "# Summary: Parameter-Efficient Fine-Tuning (PEFT) for Large Language Models\n\nThis lecture by Dinesh Raghu from IBM Research covers efficient methods for fine-tuning Large Language Models (LLMs) without updating all parameters.\n\n## Key Concepts\n\n**Why PEFT is Needed:**\n- Full fine-tuning requires 12-20\u00d7 model size in memory for optimizer states, gradients, and activations\n- Storage overhead: each task requires saving a full model checkpoint (e.g., 350GB)\n- In-context learning (prompting) has limitations: lower accuracy than fine-tuning, sensitivity to prompt wording, and high inference costs\n\n**Main PEFT Techniques:**\n\n1. **Prompt Tuning (Soft Prompting)**\n - Reserves special trainable tokens in the input while freezing all model weights\n - Extremely parameter-efficient (~0.1% of model parameters)\n - Enables multi-task serving: different soft prompts can be swapped for different tasks on the same base model\n - Performance approaches full fine-tuning for large models (11B+ parameters)\n\n2. **Prefix Tuning**\n - Adds trainable parameters at every transformer layer, not just the input\n - Uses a bottleneck MLP architecture to prevent training instability\n - Achieves comparable performance to full fine-tuning with only 0.1% trainable parameters\n\n3. **Adapters**\n - Inserts new trainable layers (bottleneck architecture) within each transformer block\n - Down-projects hidden dimensions, applies nonlinearity, then up-projects\n - Achieves good performance with ~3.6% of parameters\n - Drawback: inference latency overhead due to added layers\n\n4. **LoRA (Low-Rank Adaptation)**\n - Most popular PEFT method based on intrinsic dimensionality theory\n - Decomposes weight updates into low-rank matrices: \u0394W = BA\n - Only modifies query, key, value, and output projection matrices\n - Advantages: no inference latency, can be merged back into base weights\n - Variants: QLoRA (memory-efficient), DyLoRA (dynamic rank selection), LoRA+\n\n**Key Benefits of PEFT:**\n- Reduced memory and compute requirements (can use older GPUs)\n- Faster convergence due to smaller parameter space\n- Less overfitting and catastrophic forgetting\n- Better out-of-domain generalization\n- Minimal storage per task\n\nThe lecture emphasizes that PEFT bridges the gap between inefficient in-context learning and computationally prohibitive full fine-tuning, making LLM adaptation accessible and practical.\n\n[1](https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/attachments/52028987/a8512587-2c43-4219-84a6-bc60a1065297/paste.txt)" + }, + { + "name": "Prompting methods", + "description": "chain of thought, self consistency, role prompting, instruction prompting, zero shot prompting, few shot prompting", + "links": "https://example.com/resource/9", + "module": "Prompting methods", + "module_id": 1, + "submodule_id": 9, + "index": 9 + }, + { + "name": "Retrieval Methods", + "description": "- [00:00:05 \u2192 00:01:11] \n**Introduction and Context of In-Context Learning** \n- This screencast is the first in a series focused on **in-context learning**, designed to complement a previous series on **information retrieval**. Together, these series support tasks such as **homework 2** and **bake off 2**, which focus on **few-shot open-domain question answering** using **frozen retrievers** and **frozen large language models**. \n- The presenter reflects on the **origins of in-context learning**, framing it as a key milestone in **Natural Language Processing (NLP)** and its societal implications. \n- An early humorous reference is made to the **Chomsky bot**, a simple pattern-based language model from the 1990s that generates prose mimicking Noam Chomsky\u2019s style. This illustrates that **even simple models can produce stylistically coherent text**, hinting at underlying mechanisms relevant to modern large language models (LLMs). \n- The presenter emphasizes that the current success of LLMs is part of a longer history, highlighting **engram-based sparse language models** from the pre-deep learning era. For example, **Brants et al. (2007)** trained a **300 billion parameter language model on 2 trillion tokens** for machine translation, demonstrating the long-standing pursuit of large-scale language models\u2014albeit with different architectures than today\u2019s dense models.\n\n- [00:01:11 \u2192 00:02:18] \n**Early Research Foundations of In-Context Learning** \n- The earliest formal research explicitly related to **in-context learning** is traced to the **DECA NLP paper (McCann et al., 2018)**. This work introduced **multi-task training with natural language task instructions**, using freeform text to guide models to perform multiple tasks without task-specific retraining. \n- The **GPT paper (Radford et al., 2018)** hinted at prompt-based experiments but did not fully develop the idea. \n- The real conceptual breakthrough is credited to the **GPT-2 paper (Radford et al., 2019)**, which demonstrated that **language models can perform downstream tasks in a zero-shot manner without any parameter or architecture modification**, solely by conditioning on appropriate prompts.\n\n- [00:02:18 \u2192 00:04:56] \n**Key GPT-2 Experiments Demonstrating In-Context Learning** \n- GPT-2\u2019s authors provided compelling examples of prompting to induce specific behaviors: \n - **Summarization**: Adding the token \u201cTL;DR\u201d after articles to induce summaries by generating 100 tokens. \n - **Translation**: Conditioning the model on example English-French sentence pairs, then prompting it to generate a French translation of a new English sentence using greedy decoding. \n - **Question Answering (QA)**: Providing example question-answer pairs in the prompt to guide the model toward generating short answers in the appropriate style. \n- These experiments highlight the concept of **demonstrations** (i.e., example pairs or instructions) embedded in the prompt as a way to **coax the model into the desired task**, effectively serving as implicit task instructions without changing the model\u2019s weights. \n- Additionally, GPT-2\u2019s work explored other tasks such as **text completion, Winograd schema challenge**, and **reading comprehension**, offering a thorough and open assessment of the strengths and limitations of prompt-based learning. \n- The GPT-2 paper is described as a **creative and comprehensive exploration that laid the groundwork** for the subsequent development of in-context learning techniques.\n\n- [00:04:56 \u2192 00:08:00] \n**The GPT-3 Breakthrough and Scaling Effects** \n- The presenter identifies the **GPT-3 paper (Brown et al., 2020)** as the cultural and scientific moment when in-context learning gained widespread recognition. \n- Key points from the GPT-3 paper\u2019s abstract are highlighted: \n - **Scaling up model size dramatically improves task-agnostic few-shot performance**, sometimes rivaling fine-tuned models on certain benchmarks. \n - GPT-3 is an **autoregressive language model with 175 billion parameters**, roughly **10 times larger than any prior non-sparse language model** at the time. \n - The paper emphasizes that GPT-3 performs all tasks **without any gradient updates or fine-tuning**, relying solely on **few-shot demonstrations provided as text prompts**. \n- The speaker appreciates this explicit emphasis on the model being entirely **frozen**, underscoring how unfamiliar and counterintuitive this was at the time. \n- GPT-3 demonstrates **strong performance on a variety of NLP tasks**, including: \n - Translation \n - Question answering \n - Closed-domain tasks \n - Tasks requiring **on-the-fly reasoning or domain adaptation**, such as unscrambling words, using novel words in sentences, and performing three-digit arithmetic. \n- The paper also transparently discusses limitations: \n - Some datasets and tasks remain challenging for GPT-3\u2019s few-shot learning. \n - Methodological issues related to **training on large web corpora** (e.g., potential data leakage or overlaps with test data) were encountered and openly acknowledged. \n- This openness about both achievements and limitations is praised as characteristic of the best scientific work, providing a balanced view of what in-context learning could and could not achieve at scale.\n\n### Timeline of Key Developments in In-Context Learning\n\n| Year | Paper/Model | Contribution/Insight |\n|-------|------------------------|----------------------------------------------------------------------------------------------------|\n| 1990s | Chomsky Bot | Simple pattern-based language model illustrating early style mimicry. |\n| 2007 | Brants et al. | Massive sparse engram-based LM (300B parameters, 2T tokens) used for machine translation. |\n| 2018 | McCann et al. (DECA NLP)| Multitask training with natural language instructions; early concept of task instruction via text. |\n| 2018 | Radford et al. (GPT) | Initial prompt-based experiments hinted at zero-shot learning but not fully developed. |\n| 2019 | Radford et al. (GPT-2) | Demonstrated zero-shot and few-shot tasks via prompt conditioning without parameter updates. |\n| 2020 | Brown et al. (GPT-3) | Massive scaling (175B parameters); strong few-shot performance on diverse tasks without fine-tuning.|\n\n### Core Concepts and Definitions\n\n| Term | Definition |\n|--------------------------|---------------------------------------------------------------------------------------------------|\n| In-Context Learning | The ability of a frozen language model to perform new tasks by conditioning on textual prompts without parameter updates. |\n| Few-Shot Learning | Providing a small number of example demonstrations in the prompt to guide the model\u2019s behavior on a task. |\n| Frozen Model | A model whose parameters are fixed and not updated during task adaptation or inference. |\n| Demonstrations (in prompt)| Example input-output pairs included in the prompt to indicate the task to the model. |\n| Zero-Shot Learning | Performing a task without any examples or task-specific fine-tuning, relying solely on instructions or prompt cues. |\n\n### Key Insights and Conclusions\n\n- **In-context learning represents a paradigm shift in NLP**, where large, frozen language models can be prompted with task instructions and demonstrations to perform diverse tasks without any gradient updates or dedicated fine-tuning. \n- The **GPT-2 paper was the first to explicitly demonstrate and articulate this capability** in detail, showing impressive zero-shot and few-shot task generalization. \n- The **GPT-3 paper massively scaled this idea**, showing that increasing model size significantly improves few-shot performance, enabling the model to handle a broad variety of tasks, including reasoning and domain adaptation, purely from textual prompts. \n- Despite the impressive capabilities, the **limitations and challenges remain**, including struggles with certain datasets and methodological issues regarding training data contamination, which the GPT-3 authors openly discuss. \n- The history of large language models includes prior work on **enormous sparse models**, but modern dense transformer-based LLMs have unlocked new practical capabilities through in-context learning. \n- The **concept of demonstrations in prompts** is central for guiding model behavior, effectively serving as implicit task instructions without changing the model\u2019s parameters. \n- This mode of learning is **still evolving**, with ongoing research into understanding its mechanisms, limitations, and potential applications.\n\n### Summary \nThis screencast provides a foundational overview of **in-context learning** in NLP, tracing its lineage from early large-scale language models through the seminal GPT-2 and GPT-3 papers. It highlights how **frozen large language models can be prompted with natural language instructions and few-shot examples to perform a wide variety of tasks without parameter updates**. The presenter underscores the importance of **scaling model size** (GPT-3\u2019s 175 billion parameters) in enhancing few-shot capabilities and openly acknowledges the limitations and open questions still faced by the community. Overall, the segment situates in-context learning as a transformative development in NLP, rooted in a rich research history and promising broad applications.", + "links": "https://example.com/resource/10", + "module": "Retrieval Methods", + "module_id": 1, + "submodule_id": 10, + "index": 10, + "transcript": "\nwelcome everyone this is the first screencast in our series on in context learning this series is a kind of companion to the one that we did on information retrieval the two series come together to help you with homework 2 and bake off two which is focused on few shot open domain question answering with frozen retrievers and Frozen large language models to start this series I thought we would just reflect a bit on the origins of the idea of in context learning which is really a story of how NLP got to this\n\nstrange and exciting and chaotic moment for the field and maybe also for the society more broadly all credit to the Chomsky bot for bringing us to this moment I'm only joking the the Chomsky bot is a very simple pattern-based language model it's been around since the 90s I believe and with very simple mechanisms it produces Pros that is roughly in the style of the political philosopher and sometimes linguist Noam Chomsky it produces prose that Delights and maybe informs us and the underlying mechanisms are very\n\nsimple and I think that's a nice reminder about what all of these large language models might be doing even in the present day but I'm only joking although it's only partly a joke I think when we think about precedence for in context learning it is worth mentioning that in the pre-deep learning era engram based language models very sparse large language models were often truly massive for example brands at all 2007 use a 300 billion parameter language model trained on 2 trillion tokens of text to help\n\nwith machine translation that is a very large and very powerful mechanism with a different character from the large language models of today but it is nonetheless worth noting that they played an important role in a lot of different fields way back when I think for in context learning as we know it now the earliest paper as far as I know is the DECA NLP paper this is McCann adult 2018. they do multitask training with task instructions that are natural language questions and that does seem that like the origin of the idea\n\nthat with freeform natural language instructions we could essentially end up with artifacts that could do multiple things guided solely by text and then it's worth noting also that in the GPT paper Radford at all 2018 you can find buried in there some tentative proposals to do prompt-based uh experiments with that model but the real origins of the ideas again as far as I know are Radford at all 2019 this is the gpt2 paper and let me just show you some Snippets from this paper it's really inspiring\n\nhow much they did they say at the start we demonstrate language models can perform Downstream tasks in a zero shot setting without any parameter or architecture modification so there you see this idea of using Frozen models prompting them and seeing if they will produce interesting behaviors they looked at a bunch of different tasks for summarization they say to induce summarization Behavior we add the text tldr after the article and generate 100 tokens this is mind-blowing I remember when I first heard about this\n\nidea I had such a cognitive bias against in context learning of this sort being successful that I assumed what they were trying to say to us is that they had trained that token uh in a task specific way to do summarization and then just kind of given it a colorful name but no they really meant it they simply prompt the model with this token and look at what comes out for translation they say we test whether gpt2 has begun to learn how to translate from one language to another in order to help it infer that this is the desired\n\ntask we condition the language model on a context of example pairs of the format English sentence equals French sentence and then after a final prompt of English sentence equals we sample from the model with greedy decoding and use the first generated sentence as the translation incredible and what you see emerging there is this idea of demonstrations including in the prompt some examples of the behavior that you want as a way of coaxing the model to do what you would like it to do here's a similar example\n\nthey say similar to translation the context of the language model is seated with example question answer pairs which helps the model infer the short answer style of the data set so that's for QA and again they started to see that demonstrations could help the model see what the implicit task instruction was and they also in the paper evaluate a bunch of other things text completion Winograd schemas and reading comprehension and maybe others it's a very impressive and thorough exploration\n\nvery open about the benefits and limitations of the methods a very impressive and creative paper that was the beginning of the idea in terms of research the cultural moment certainly arrives with the gpt3 paper Brown at all 2020 which is also impressive in its own ways and here I'm just going to quote from the abstract and we can linger a bit over what it says they start we show that scaling up language models greatly improves task agnostic fuchsia performance sometimes even reaching competitiveness with prior\n\nstate-of-the-art fine-tuning approaches we could quibble with whether or not they actually saw competitiveness in that sense but it is absolutely true that they got very impressive behaviors out of their model in this task agnostic few shot setting specifically we train gpt3 an auto-regressive language model with 175 billion parameters 10x more than any previous non-sparce language model and test its performance in the few shot settings there are two things I really like about this part first 175 billion\n\nparameters is indeed incredibly ambitious and impressive even today to say nothing of back in 2020 and I also really love that they mentioned non-sparce language model a nod to those engram based models that I mentioned before which were often truly massive for all tasks gpt3 is applied without any gradient updates or fine-tuning with tasks and few shot demonstrations specified purely via text interaction with the model that's nice you might think in retrospect that they're kind of repeating themselves here they've\n\nalready established that these are going to be frozen models but I think it's necessary for them to do that because this was such an unfamiliar idea and I can imagine again being a reader of this paper and assuming that they can't really mean they're just using Frozen models for all these tasks surely there is some fine tuning somewhere and so they're emphasizing that in fact the model is entirely Frozen gpt3 achieves strong performance on many NLP data sets including translation\n\nquestion answering and closed tasks as well as several tasks that require on-the-fly reasoning or domain adaptations such as unscrambling words using a novel word in a sentence or performing three-digit arithmetic I love this a real diversity of tasks and what I think you can see them doing is really trying to push the limits of what would be possible in this mode at the same time we also Identify some data sets where gpt3's fuse shock learning still struggles as well as some data sets where gpt3 faces\n\nmethodological issues related to training on large web corpora I also love this sentence it's again very open about what they achieved and where the limitations are they're acknowledging that they found some tasks that are still hard for the model and they also acknowledge in the paper that they had some sort of minor slip UPS where they intended to make sure they hadn't trained on data that was relevant for the test task that they were performing and in fact they had not quite gotten\n\nthat right and so they're being very open about that and kind of exploring how hard it is to get that right at the scale that they're operating at so just like the gpt2 paper a wonderfully open and thorough exploration of the ideas" + }, + { + "name": "Retrieval Augmented Generation", + "description": "### Summary \nThis screencast introduces the concept of in-context learning, tracing its origins and development within natural language processing (NLP). The video contextualizes in-context learning as a technique where large language models (LLMs), often frozen (i.e., without parameter updates), are prompted with natural language instructions or examples, enabling them to perform a variety of downstream tasks without explicit fine-tuning. The discussion begins by reflecting on early language models, such as pattern-based models like the Chomsky bot and massive sparse models like those from the pre-deep learning era, noting their influence on the field. The earliest formalization of in-context learning is attributed to the DECA NLP paper (McCann et al., 2018), which introduced multitask training with natural language instructions.\n\nThe video then highlights the seminal GPT series papers by Radford et al., especially GPT-2 (2019) and GPT-3 (2020), which demonstrated zero-shot and few-shot learning capabilities via prompting frozen language models. The GPT-2 paper showed how simple prompts (e.g., \"TL;DR\" for summarization) or demonstrations (example input-output pairs) could coax the model into performing tasks such as summarization, translation, and question answering. This was a paradigm shift away from traditional fine-tuning toward prompt-based learning.\n\nThe GPT-3 paper represented a cultural and technical milestone, scaling up to 175 billion parameters and achieving strong task-agnostic few-shot performance on a wide range of NLP tasks, including reasoning and domain adaptation. Importantly, GPT-3 was used entirely as a frozen model, interacting solely through text prompts, underscoring the power and novelty of in-context learning. The paper also openly discussed the model\u2019s limitations, dataset biases, and methodological challenges, demonstrating a transparent approach to research at scale. Overall, the screencast situates in-context learning as a pivotal and evolving technique that brings together advances in model scale, prompting methods, and task generalization.\n\n### Highlights \n- [00:00:05] Introduction to in-context learning as a companion to information retrieval for few-shot open domain question answering. \n- [00:01:11] Early language models like the Chomsky bot illustrate simple pattern-based generation, hinting at foundational ideas behind LLMs. \n- [00:02:18] DECA NLP paper (2018) pioneers multitask training with natural language instructions, a precursor to in-context learning. \n- [00:02:54] GPT-2 paper (2019) introduces zero-shot and few-shot prompting using simple cues like \"TL;DR\" for summarization, translation, and QA. \n- [00:05:31] GPT-3 (2020) scales to 175 billion parameters, showing task-agnostic few-shot learning without any model fine-tuning. \n- [00:07:04] GPT-3 demonstrates diverse abilities including translation, reasoning, arithmetic, and domain adaptation. \n- [00:07:35] Open acknowledgment of GPT-3\u2019s limitations, data leakage issues, and ongoing challenges in large-scale training. \n\n### Key Insights \n- [00:01:11] **Simplicity in early language models foreshadows modern LLM mechanisms:** The Chomsky bot, a simple pattern-based model producing prose in Chomsky\u2019s style, serves as a reminder that foundational language generation can emerge from straightforward mechanisms. This underscores that even today\u2019s complex LLMs may be sophisticated extensions of fundamentally simple principles of pattern matching and prediction. \n- [00:01:44] **Massive sparse models of the pre-deep learning era set important historical precedents:** Models like the 300 billion parameter engram-based language model from 2007 demonstrate that the idea of large-scale language modeling is not entirely new. However, these earlier models had a different character and training regimes, which contrasts with the dense, transformer-based architectures dominating today. Understanding this lineage helps appreciate the evolution and novelty of modern in-context learning approaches. \n- [00:02:18] **DECA NLP\u2019s multitask training with natural language instructions laid groundwork for prompt-based models:** By framing multiple tasks via freeform text instructions, DECA NLP introduced the idea that models could be \u201cguided\u201d purely through language rather than explicit reconfiguration or fine-tuning. This insight is critical because it anticipates the prompt engineering approaches that are central to GPT-style in-context learning. \n- [00:02:54] **GPT-2\u2019s zero-shot and few-shot prompting marked a paradigm shift:** The GPT-2 paper\u2019s demonstration that models can perform summarization, translation, and question answering by simply conditioning on textual prompts (e.g., \u201cTL;DR,\u201d example pairs) without retraining was revolutionary. This approach moved away from task-specific fine-tuning toward a more flexible, generalist model interface. It also introduced the idea that demonstrations embedded in prompts can convey implicit task instructions. \n- [00:05:31] **Scaling up model size (GPT-3) dramatically improves few-shot learning capabilities:** GPT-3\u2019s 175 billion parameter size was a bold leap that enabled strong performance across diverse NLP tasks with no gradient updates. This scale effect emphasizes that model capacity is a key factor enabling in-context learning, suggesting that larger models internalize more generalizable knowledge and reasoning ability. \n- [00:06:38] **Frozen models prompt interaction is a novel and sometimes unintuitive concept:** Emphasizing that GPT-3 requires no fine-tuning and is used purely via textual interaction highlights a conceptual shift. This challenges traditional ML paradigms where task adaptation typically involves retraining. It also raises questions about the nature of knowledge and task instruction stored implicitly in model parameters versus delivered explicitly in prompts. \n- [00:07:35] **Transparency about limitations and data challenges is crucial in large-scale modeling:** The GPT-3 paper\u2019s open discussion of datasets where the model struggles and issues such as potential data contamination exemplifies responsible scientific communication. This transparency is important for setting realistic expectations and guiding future research to address current shortcomings, especially when deploying models at scale in real-world applications. \n\nOverall, this screencast provides a thorough, historically grounded overview of in-context learning\u2019s emergence, highlighting key papers and ideas that shaped the field. It underscores the interplay between scale, prompt engineering, and model architecture that defines current approaches to few-shot and zero-shot NLP tasks.", + "links": "https://example.com/resource/11", + "module": "Retrieval Augmented Generation", + "module_id": 1, + "submodule_id": 11, + "index": 11, + "transcript": "\nwelcome everyone this is the first screencast in our series on in context learning this series is a kind of companion to the one that we did on information retrieval the two series come together to help you with homework 2 and bake off two which is focused on few shot open domain question answering with frozen retrievers and Frozen large language models to start this series I thought we would just reflect a bit on the origins of the idea of in context learning which is really a story of how NLP got to this\n\nstrange and exciting and chaotic moment for the field and maybe also for the society more broadly all credit to the Chomsky bot for bringing us to this moment I'm only joking the the Chomsky bot is a very simple pattern-based language model it's been around since the 90s I believe and with very simple mechanisms it produces Pros that is roughly in the style of the political philosopher and sometimes linguist Noam Chomsky it produces prose that Delights and maybe informs us and the underlying mechanisms are very\n\nsimple and I think that's a nice reminder about what all of these large language models might be doing even in the present day but I'm only joking although it's only partly a joke I think when we think about precedence for in context learning it is worth mentioning that in the pre-deep learning era engram based language models very sparse large language models were often truly massive for example brands at all 2007 use a 300 billion parameter language model trained on 2 trillion tokens of text to help\n\nwith machine translation that is a very large and very powerful mechanism with a different character from the large language models of today but it is nonetheless worth noting that they played an important role in a lot of different fields way back when I think for in context learning as we know it now the earliest paper as far as I know is the DECA NLP paper this is McCann adult 2018. they do multitask training with task instructions that are natural language questions and that does seem that like the origin of the idea\n\nthat with freeform natural language instructions we could essentially end up with artifacts that could do multiple things guided solely by text and then it's worth noting also that in the GPT paper Radford at all 2018 you can find buried in there some tentative proposals to do prompt-based uh experiments with that model but the real origins of the ideas again as far as I know are Radford at all 2019 this is the gpt2 paper and let me just show you some Snippets from this paper it's really inspiring\n\nhow much they did they say at the start we demonstrate language models can perform Downstream tasks in a zero shot setting without any parameter or architecture modification so there you see this idea of using Frozen models prompting them and seeing if they will produce interesting behaviors they looked at a bunch of different tasks for summarization they say to induce summarization Behavior we add the text tldr after the article and generate 100 tokens this is mind-blowing I remember when I first heard about this\n\nidea I had such a cognitive bias against in context learning of this sort being successful that I assumed what they were trying to say to us is that they had trained that token uh in a task specific way to do summarization and then just kind of given it a colorful name but no they really meant it they simply prompt the model with this token and look at what comes out for translation they say we test whether gpt2 has begun to learn how to translate from one language to another in order to help it infer that this is the desired\n\ntask we condition the language model on a context of example pairs of the format English sentence equals French sentence and then after a final prompt of English sentence equals we sample from the model with greedy decoding and use the first generated sentence as the translation incredible and what you see emerging there is this idea of demonstrations including in the prompt some examples of the behavior that you want as a way of coaxing the model to do what you would like it to do here's a similar example\n\nthey say similar to translation the context of the language model is seated with example question answer pairs which helps the model infer the short answer style of the data set so that's for QA and again they started to see that demonstrations could help the model see what the implicit task instruction was and they also in the paper evaluate a bunch of other things text completion Winograd schemas and reading comprehension and maybe others it's a very impressive and thorough exploration\n\nvery open about the benefits and limitations of the methods a very impressive and creative paper that was the beginning of the idea in terms of research the cultural moment certainly arrives with the gpt3 paper Brown at all 2020 which is also impressive in its own ways and here I'm just going to quote from the abstract and we can linger a bit over what it says they start we show that scaling up language models greatly improves task agnostic fuchsia performance sometimes even reaching competitiveness with prior\n\nstate-of-the-art fine-tuning approaches we could quibble with whether or not they actually saw competitiveness in that sense but it is absolutely true that they got very impressive behaviors out of their model in this task agnostic few shot setting specifically we train gpt3 an auto-regressive language model with 175 billion parameters 10x more than any previous non-sparce language model and test its performance in the few shot settings there are two things I really like about this part first 175 billion\n\nparameters is indeed incredibly ambitious and impressive even today to say nothing of back in 2020 and I also really love that they mentioned non-sparce language model a nod to those engram based models that I mentioned before which were often truly massive for all tasks gpt3 is applied without any gradient updates or fine-tuning with tasks and few shot demonstrations specified purely via text interaction with the model that's nice you might think in retrospect that they're kind of repeating themselves here they've\n\nalready established that these are going to be frozen models but I think it's necessary for them to do that because this was such an unfamiliar idea and I can imagine again being a reader of this paper and assuming that they can't really mean they're just using Frozen models for all these tasks surely there is some fine tuning somewhere and so they're emphasizing that in fact the model is entirely Frozen gpt3 achieves strong performance on many NLP data sets including translation\n\nquestion answering and closed tasks as well as several tasks that require on-the-fly reasoning or domain adaptations such as unscrambling words using a novel word in a sentence or performing three-digit arithmetic I love this a real diversity of tasks and what I think you can see them doing is really trying to push the limits of what would be possible in this mode at the same time we also Identify some data sets where gpt3's fuse shock learning still struggles as well as some data sets where gpt3 faces\n\nmethodological issues related to training on large web corpora I also love this sentence it's again very open about what they achieved and where the limitations are they're acknowledging that they found some tasks that are still hard for the model and they also acknowledge in the paper that they had some sort of minor slip UPS where they intended to make sure they hadn't trained on data that was relevant for the test task that they were performing and in fact they had not quite gotten\n\nthat right and so they're being very open about that and kind of exploring how hard it is to get that right at the scale that they're operating at so just like the gpt2 paper a wonderfully open and thorough exploration of the ideas\n" + }, + { + "name": "Quantization", + "description": "model compression, low precision, int8, int4, reduced memory, faster inference", + "links": "https://example.com/resource/12", + "module": "Quantization", + "module_id": 1, + "submodule_id": 12, + "index": 12 + }, + { + "name": "Mixture of Experts Model", + "description": "mixture of experts, expert routing, sparse activation, gating network, scalable models, conditional computation", + "links": "https://example.com/resource/13", + "module": "Mixture of Experts Model", + "module_id": 1, + "submodule_id": 13, + "index": 13 + }, + { + "name": "Agentic AI", + "description": "### Summary of Video Content on Advanced RAG Techniques for Conversational AI \n*Featuring Lena, founder of Pars Labs and expert in building Retrieval-Augmented Generation (RAG) systems and chatbots*\n\n---\n\n#### [00:00:03 \u2192 00:01:38] Introduction and Speaker Background \n- The video is part of a Comet YouTube channel series focusing on building and scaling GenAI systems. \n- Lena introduces herself as a founder of Pars Labs, with 8 years of experience in conversational AI and chatbots. \n- Her background includes theoretical linguistics, NLP research engineering, full stack development, and running a small AI agency. \n- She also conducts public speaking and teaches AI automation courses.\n\n---\n\n#### [00:01:38 \u2192 00:04:28] Overview of Advanced Techniques to Improve RAG Systems \n- The session dives into **advanced techniques for optimizing retrieval quality** in RAG systems. \n- Lena revisits **intent detection**, a technique from earlier chatbot development, which is underutilized by newer teams: \n - Instead of always generating responses with LLMs, detect user intent (e.g., \"What's the salary?\") using intent models or function calls. \n - If intent matches, return a static or pre-written answer from a database. \n - If intent is unrecognized, fallback to vector search and RAG typical retrieval. \n- Introduction of **function calling as an intent detection alternative**: \n - Using LLM-based function or tool calls (e.g., OpenAI\u2019s function calling API) to trigger specific operations like scheduling interviews. \n - This can integrate external API calls or data retrieval to enhance response relevance. \n - Function calls can also be used to **store user data during a conversation** for personalization, e.g., scheduling preferences.\n\n---\n\n#### [00:04:28 \u2192 00:09:32] Optimizing Data Storage for Better Retrieval \n- Discussed how data is stored in vector databases affects retrieval quality: \n - Traditional method: store **raw data chunks** (e.g., \"The salary for developers is 80k\"). \n - Improved method: store **frequently asked questions (FAQs) paired with answers** as separate entries, e.g., \"What is the salary for developers?\" + answer metadata. \n- This approach reduces vector distance between user queries and stored FAQs, **improving retrieval accuracy**. \n- Workflow comparison:\n\n| Step | Raw Data Storage | FAQ-based Storage |\n|---------------------|---------------------------------|------------------------------------------------------|\n| Stored Vector Data | Raw factual statements | Questions paired with answers |\n| Query Matching | User query matched to raw data | User query matched to similar FAQ question |\n| Result Quality | Lower accuracy due to vector distance | Higher accuracy, more precise matching |\n\n- Lena suggests **automatically generating FAQs** with LLMs from raw document chunks to scale this approach and avoid manual effort. \n- On **chunking strategy**: no universal best practice; chunks should ideally represent a coherent topic, but it depends on data type and use case.\n\n---\n\n#### [00:09:32 \u2192 00:14:31] Further Retrieval Quality Improvements \n- **Data hierarchy for retrieval:** \n - Prioritize FAQ search first; if no relevant FAQ is found, fallback to searching raw data chunks. \n- **Query rephrasing based on chat context:** \n - Use chat history to clarify vague questions. For example, if the user asks \"What is the salary?\" and previously stated their position is \"engineer,\" rewrite query to \"What is the salary for engineers?\" \n - This contextual query rewriting narrows down retrieval scope, improving answer relevance. \n- **Multi-query expansion:** \n - Generate multiple paraphrased queries from the original user question (e.g., \"How much do they pay?\", \"What is the salary?\"). \n - This increases chances of matching relevant information but adds cost and latency due to multiple retrievals. \n - Can be used as a fallback when initial retrieval fails.\n\n---\n\n#### [00:14:31 \u2192 00:18:52] Incorporating Chat History and Personalization \n- Three main techniques for incorporating chat history into RAG systems: \n 1. **Pasting full or recent chat history** directly into the LLM prompt along with retrieved data. \n 2. **Summarizing chat history with an LLM** to create a concise, relevant summary for the prompt, reducing token usage and focusing on key info. \n 3. **Retrieval-augmented search over past conversations:** \n - Perform vector search not only on documents but also on stored past user conversations or summaries, pulling relevant context dynamically. \n- Lena highlights the benefit of **extracting and storing stable user properties** (e.g., English proficiency level, job position) from conversation history to personalize responses consistently. \n- Example: storing user\u2019s English level as a persistent attribute and including it in subsequent prompts to tailor language complexity.\n\n---\n\n#### [00:18:52 \u2192 00:24:03] Stable vs. Fluctuating User Properties for Dynamic Interaction \n- Lena introduces a **two-tier personalization model**: \n - **Stable properties:** Long-lasting user traits (e.g., technical knowledge, user\u2019s preferred terminology). Injected at the beginning of conversations to guide tone and style. \n - **Fluctuating properties:** Temporary states (e.g., current mood, frustration level). These are detected dynamically from recent messages and used only for the current response. \n- Fluctuating properties can be managed by querying a metadata store or applying rules (e.g., if user is angry, respond differently). \n- This model aligns well with customer support and sales frameworks, where recognizing and responding to emotional or situational cues is critical. \n- Lena suggests leveraging **AI to analyze past customer interactions** to extract guidelines and best practices for handling different user states.\n\n---\n\n#### [00:24:03 \u2192 00:26:40] Using AI for Prompt Engineering and Clarifying Questions \n- Lena and Claire discuss using AI to generate system prompts and coaching content, with LLM-generated prompts outperforming manually written ones in some cases. \n- Last advanced technique: **clarifying questions to improve retrieval precision**. \n - When user query is ambiguous, the system uses an LLM to analyze if the available context suffices to answer confidently. \n - If not, the system generates a clarifying question (e.g., \"Are you an engineer or a doctor?\") before attempting retrieval again. \n- This approach was illustrated with a telecom use case, where domain-specific guidelines are numerous but user queries are often vague. \n- The mechanism relies on LLM prompts that compare user questions with retrieved data chunks to assess specificity.\n\n---\n\n#### [00:26:40 \u2192 00:30:25] Closing Recommendations and Best Practices \n- Lena\u2019s **key advice for building RAG systems**: \n - **Focus on data quality** above all. Clean, up-to-date, and well-organized data is critical (\"garbage in, garbage out\"). \n - Use techniques like generating FAQs at scale and clarifying questions to improve retrieval quality. \n- Encourages community sharing of projects and learnings to accelerate collective progress. \n- Claire shares enthusiasm for implementing chat history summaries to build user profiles in her own projects. \n- Lena encourages continuous experimentation and open sharing of work, including partial or in-progress results.\n\n---\n\n### Key Insights and Recommendations\n\n- **Intent detection (via traditional NLU or function/tool calling) remains a powerful technique to optimize retrieval and reduce unnecessary LLM calls.** \n- **Storing FAQs paired with answers, rather than raw data chunks, improves vector search accuracy and retrieval relevance.** \n- **Context-aware query rewriting using chat history significantly sharpens retrieval focus.** \n- **Multi-query expansion trades higher retrieval cost for improved recall and can be used as a fallback method.** \n- **Incorporating chat history via full text, summarization, or retrieval over past conversations enhances personalization and response accuracy.** \n- **Extracting stable and fluctuating user properties enables dynamic, personalized interactions tailored to user traits and current state.** \n- **Clarifying questions triggered by LLM analysis of query specificity improve user experience by ensuring more precise answers.** \n- **Data quality and organization are paramount\u2014no retrieval technique can compensate for poor data.** \n- **Experimentation, iteration, and community sharing accelerate innovation in RAG system development.**\n\n---\n\n### Timeline Table of Major Topics\n\n| Timestamp | Topic |\n|-----------------|--------------------------------------------------------------|\n| 00:00:03-00:01:38 | Introduction, Speaker background |\n| 00:01:38-00:04:28 | Intent detection and function calling for retrieval |\n| 00:04:28-00:09:32 | Optimizing data storage: FAQs vs raw data |\n| 00:09:32-00:14:31 | Retrieval improvements: data hierarchy, query rewriting, multi-query expansion |\n| 00:14:31-00:18:52 | Chat history incorporation: full, summary, retrieval-based |\n| 00:18:52-00:24:03 | Personalization via stable vs fluctuating user properties |\n| 00:24:03-00:26:40 | AI-generated prompts and clarifying questions |\n| 00:26:40-00:30:25 | Closing advice, best practices, and community encouragement |\n\n---\n\n### Glossary of Key Terms\n\n| Term | Definition |\n|------------------------|----------------------------------------------------------------------------------------------|\n| RAG (Retrieval-Augmented Generation) | A method combining retrieval of relevant documents with LLM generation to produce responses. |\n| Intent Detection | Classification of user queries into predefined categories to guide response strategies. |\n| Function/Tool Calling | Using LLMs to predict and invoke external functions or APIs to fulfill user intents. |\n| Vector Database | A database storing vector embeddings of data, enabling similarity search for retrieval. |\n| FAQ Storage | Indexing frequently asked questions paired with answers to improve retrieval accuracy. |\n| Query Rewriting | Adjusting user queries based on context to improve search relevance. |\n| Multi-Query Expansion | Generating multiple query paraphrases to increase retrieval recall. |\n| Chat History Summary | Condensed representation of past conversation to reduce prompt size while preserving context.|\n| Stable User Properties | Persistent user attributes extracted and used for consistent personalization. |\n| Fluctuating User Properties | Temporary user states (e.g., mood), influencing immediate responses dynamically. |\n| Clarifying Questions | Follow-up questions generated to resolve ambiguity in user queries before retrieval. |\n\n---\n\n### Final Remarks \nThis video provides a comprehensive, expert-level exploration of **advanced retrieval optimization techniques in RAG systems**, especially for chatbots. Lena\u2019s insights emphasize **strategic data handling, contextual awareness, and personalization frameworks** that can dramatically improve the accuracy, relevance, and user experience of conversational AI. Implementing these techniques requires thoughtful engineering and experimentation but offers clear pathways to more effective, scalable, and user-centric AI assistants.", + "links": "https://example.com/resource/14", + "module": "Agentic AI", + "module_id": 1, + "submodule_id": 14, + "index": 14, + "transcript": "\nAll right, welcome everyone to the Comet YouTube channel. We are doing a series of guest speakers where we dive into some very fun technical topics related to building and scaling Genai systems. And today I have with us Lena. She is a absolute expert in building rags. I've had some good conversations with her as we prepared for this session about her approaches. And I'm really excited to just go through really some of the best practices around like optimizing retrieval for LMS. We're going to deep\n\ndive into some rag techniques, some advanced stuff here. Um, before we dive in, I want to pass it over to Lena to introduce herself. Thank you Claire for the introduction and uh, hi everyone who is watching the recording. I'm so happy to be here. Uh, my name is Lennena. I'm a founder of pars labs and chatbot and I've been working in this space of chatbot development conversational AI agents since um I think eight years now. Uh my career started in linguistics. I studied theoretical linguistics then AI. I\n\nworked as an NLP research engineer and full stack developer and uh yeah now running a small agency with a team of five now and doing also a lot of um public speaking and sharing my knowledge online with people um running courses on AI automation and things like that. Uh so hopefully I will have something interesting to share uh with you today. >> All right well let's dig into some deep technical topics. Um, I wanted to dive into the advanced techniques for improving rags. I think there's a few\n\nideas that you had here. Um, and if you want to share screen, uh, feel free. We can, uh, pull up some slides here. But let's dive into some of these more advanced techniques in regards to just improving your RAG systems. >> Uh, yes. Um, what should we start with? >> Let's see. I'm thinking about optimizing the retrieval quality. Um, >> okay. Let me share my screen and then we'll just see where it leads us. So I prepared a lot of different diagrams uh with different techniques\n\n>> um in a random order. So you are welcome. >> Thank you. I love this. This is a gold mine of information. >> Uh yeah. So let's start with um maybe uh this um I classified it as improving a retrieval quality trick. So uh in the past we used intent detection when we were building chatbots. So instead of generating anything for anything we had to make data sets for okay this type of question is about salary this type of question is I want to talk to customer support. We had all\n\nthose categories and um I don't see that often being used yet with newer teams who just joined this whole chatbot development. So I want to bring it back. Uh so instead of just generating the response using LLM, you get the user question. So what's the salary? Then you predict an intent and you get an an answer that your SMMES have written. So the answer that's in your database and it says the salary for this position is salary. And then you paste this information uh to the prompt and then\n\nyou generate an answer or just like you used uh it before we just show a static response uh skipping the LLM part and rag part completely and then as a fallback if no intent is predicted so if it's not one of your frequently asked questions then you can go and search answer in the vector database find relevant documents and give a response just as you typically do with rag and otherwise say that you don't the answer. So this is um a basic technique that we can use. Uh let's see what else\n\nI do I have. Um then I have an idea of using function calling as intense. So what I was thinking about here is you can either use the old NLU models. They were work still perfectly fine for intent detection or you can use LLM for intent detection. But you can also use function calling as an alternative for intent detection. So it would work in a very similar way as what I just described. If function is not predicted then you do whatever you are doing before. And if function is predicted then here we can do multiple things. For\n\nexample, let's say the user said I want to schedule an interview. I think most of my use cases would be in this HR domain just uh for the context. Um so the user said I want to schedule an interview and we predicted a function which is called schedule interview. And what we can do here is maybe we call an API to schedule an interview and then uh we bring information from the API and then we say okay it's scheduled. We can also fetch the data from the external source and this is why I put this in the\n\ncategory of improving retrieval. So you trigger another um data retrieval mechanism on function calling and then you get a better quality response more relevant one. Um and this is not so relevant for rag but uh you can like another fun thing that you can do is on function call you maybe push the user data. So let's say uh here you know that the user wants to schedule interview on Monday and they can also be there on Tuesday and Wednesday. So you store this data for the user so that later when you are talking with the same\n\nuser within this conversation you can pull it back and this is to tap on another thing that we wanted to discuss today which is personalization. So the more data you store about the user the better you can personalize things. >> Okay this is fantastic. Um a few things stood out to me that I want to dig into for um the function predicted square here. Um how are you actually implementing that? Is that like a predictive model there? >> Um no it's just part of standard um LLM libraries like OpenAI has this function\n\ncalling CL has something called tool calling. So that's what I mean by that. >> Oh fantastic. So it's a tool calling. So you essentially have like an agent architecture here where rag is really just a piece across different kind of routes that it could take including like tool calling. >> Yeah. So you >> right >> like yeah kind of like that. So in in this diagram this is sort of rag. >> Yeah. >> Where you load data about available interviews and paste it into the next\n\nprompt. And this can be implemented as rag or as an API call or as a SQL query or anything that basically gets the relevant data for the user parameters. >> I see. I see. And then how do you think about really refining the retrieval dynamically based on the user's intent? It seems like you're able to get the user's intent here, like schedule an interview or ask about a salary. But in terms of like refining and optimizing the retrieval for that, do you have approaches? Um, yeah. So,\n\nlet's see. Um, I had this idea with how we store the data for the retrieval to improve the retrieval quality. And I have some visualization here. So how we usually store data in the vector database is something like that. Let's say we have a lot of row data and one piece of data says that the salary for developers is 80k and the salary for doctors is 90k and then in the inference time if the user comes and they ask what's the salary for developers then we map it into the same vector space and\n\nit's kind of similar and close by and further away from the question um from the data source which says our company is located in Amsterdam. So that's how it works right. Um if however instead of storing just row data like the salary for developers is ATK we would have been storing questions like what is the salary for developers and what's the salary for doctors and then for each of those questions we would have stored an answer as the metadata. Then if the user comes and they ask what is the salary for\n\ndevelopers we would match it with what is the salary for developers because it's actually the same. It's very similar. So as you can see the distance between this uh is bigger then the distance between this. So you have higher chances of getting the answer correct if you store it as questions. So that's a little tip for um for storing the data. And the way it then works is uh literally like this. You get a user question then you find um h this is the row data sorry. Uh so this is like the standard uh process where a\n\nuser asks a question you find the data and then you put that in the prompt and an alternative that I suggest is that the user asks a question then we do the vector search and we map on FAQs on uh questions and then for the question so what is the salary for developers we get the associated answer from our data. So the salary for developers is 80k and this is what we paste to the prompt. >> Yeah this is fantastic. This is such a pro tip for anyone building a rag today because it looks like you're essentially\n\nlike indexing the question and answer data by the question and then you can look at a similar question to what the user has asked and that's going to grow your search down to the right answers. >> Yeah, exactly. And I have few tips here as well like one thing you can do is manually write all those FAQs. That can be a lot of work and then it can become too many FAQs. uh an alternative is to generate those questions uh on the fly when you train your model. So you still have the row documents with all the\n\ninformation about vacancies in this case and then you get the row document you split it into chunks as you uh usually do. So we see those chunks as answers and then for each of this chunk you generate using LLM a question that this chunk answers and then you store that and then you use that in your uh system. >> Oh it's a really interesting approach. Any tips for us on chunking? >> Um not really. It's something I've experimented with a bit. I've always wondered like is there a best practice\n\nfor this just like the right size of the chunk and things like that. >> I don't know if there is the correct size of chunk. I think for me it's more about the data formatting than chunks. >> Okay. >> And chunks should preferably be about the same topic that would help as well. >> Okay. >> I think it's really personal. >> Yeah, that makes sense. And maybe really more driven by the texture of the data itself. Yeah, I do have more techniques on um how to improve the retrieval.\n\n>> Fantastic. Yeah, talk me through those. >> Okay, let's see what else did I prepare. Um oh, I actually had a followup on uh on this FAQ idea. Um how you can use it. So you can use the fallback mechanism still and this is what I wanted to mention that you can decide that your FAQs are more important than uh your row documents. So here we are coming back to the data hierarchy and you first try to find the FAQ and if you don't find it then you go to your row documents and then you try to find\n\nan answer there. So this is just to complete that idea. Does this so I definitely see how this is going to help us get to the right information better. Does it also optimize cost in any way or or retrieval time? [Music] Well, not sure actually. Maybe no. >> It's adding >> mostly like a technique to improve the retrieval quality. >> Yeah. Yeah. So, it's improving the quality. So, this is really focused on improving the quality here. >> Yeah. >> Yeah. Makes sense. I like it a lot. Not\n\nsomething I'd thought of. So, thank you for sharing that. >> This is like my my secret thing. Um I have um another one on query rephrasing which I think also is quite interesting and it's also something that improves the quality of the um response. So the way it works is the user asks a question what is the salary and this is a very abstract question like salary for whom? But then from the chat history we know that uh the chatbot asked what's your position and the user said they an\n\nengineer. So they are probably asking about the salary for engineers. So then we put that to the LLM and we ask to generate uh a chat history aware user query which is now what is the salary for engineers. So instead of asking what is the salary and then you get from your rack system salary for doctors is 90k salary for engineers is 80k blah blah blah you now only get one response which is salary for engineers is 80k and then this is what you paste to the lm and you get the answer. So this way you narrow\n\ndown the choices. Oh, so you're really using like the context of the chat history here to reframe the question more precisely in a way that the retrieval is going to be >> yeah accurate. >> Yeah. And then there is another technique which goes um in another direction um which is multi-query um expans multiquery retrieval or query expansion which works like that. So you take the user question and then instead of narrowing it down, you generate multiple questions for what the user asked. So\n\nhow much do they pay? What is the salary they pay? How much will I earn? And then this way you have higher chances of finding the information which you might not have found if you would have only sent uh one query. And you might ask about the cost. So this is not not helping with the cost because you now have this like extra LLM layer, but it does help to find information that you wouldn't have found otherwise. >> Gotcha. So it's really um I mean it's going to be a trade-off between like\n\ncost and accuracy and finding the the right information is what it looks like here. >> And maybe you can use it as a fallback as well. So let's say you first always try to generate it immediately and then only if you didn't find any chunks you are using this technique. >> Okay. Okay. I'm very curious about chat history because I'm building a rag right now and it's a chatbot and there's like old chat sessions that I want to be considered context. If the user is like\n\nasking a question related to something that they've chatted about a while ago, I want that conversation to be part of the context as the uh chatbot is responding. I'm curious how you incorporate things like chat history. Like you touched on it a little bit with enhancing the context of the question, but in general, is that the right approach to really consider chat history when building an LM is to put a rag in place? And how do you think about that? Um well actually I also have a slide for\n\nthat. >> Fantastic. >> Um no that so I have uh three techniques and one bonus one which I hope I will not forget. So let me write it down. >> Okay. >> Um yeah. Okay. So uh well the basic one is I think uh what you already know is you just paste the whole chat history or if it's not realistic then the last 10 turns the last five turns. So user asks a question you get information from your rag and you paste to your prompt both this uh rag uh information plus the chat history and you just say please use this\n\nchat history to give an answer. So this is the simplest technique. Okay. uh an alternative is to do the chat history summary. So here we have this extra LLM block which analyzes full chest history writes a summary and then you paste summary into the prompt and the benefit of this is that then your prompt is smaller. So instead of pasting the whole chat history you have a very concise summary of what's important and you can really tune this LLM to extract what is important for your use case to remember.\n\nUm maybe on this topic a while I didn't forget the idea is while you are chatting with the user you can also have the data extraction mechanism >> uh for things that are important for you. I think this is similar to what chat GPT is doing right now but the idea is not very new. Basically, um, if you are building a language learning chatbot, which is one of the projects that we did for a client while back, um, I might be interested in their level of English. So, if they ever mention that\n\ntheir level of English is B2, I want to make sure that I store this in my database and that I paste it into my prompt every time I'm talking with the user. So, that becomes a a stable property of that user. So you can extract that from the chat history. And then there is this last third technique where you use rag on top of your past conversation history. So what you do is you get user question. What's this? what's the salary for this position? And then you do the regular search as we\n\nusually do uh in the vector database. And next to it, you also search among past conversations that the user had or past summaries or um this um information about the user that we are storing in in a more structured format in a SQL uh or JSON and then you get that as well and you pass it to the LLM. This is great. This is what I have today because I wanted to I wanted my chatbot to look at relevant past conversations if the conversation is about a topic that was discussed in the past. So, I'm storing\n\nall my chat history and then I'm doing retrieval and injecting it into the prompt. Um, here's what I'm not doing though. The summary thing. The summary is really interesting. If you go back to the previous slide, the thing that stands out to me about this is how customizable it is. Because the summary, when you're asking for the summary, it's probably a prompt there. And based on the use case, you can really direct what kind of information you're pulling out of this chat history. So if it's\n\nrelevant to like build a user profile or something like that, you could essentially do that with this mechanism. >> Yeah, I love this. Or you can even do this query retrieval thing, but then also for the conversation history. So maybe you rewrite the query um with which you search for the summary. >> So like searching for uh if they ever mentioned uh their job position and now I'm searching for something else. >> Yeah, I like it because it's very customizable. So any use case you're\n\nbuilding for whether it's like an HR chatbot or something else, it's just so I don't know. I love working with like free text data because the way that you retrieve information from from it, it's just going to be related to whatever use case, whatever kind of information you need need to retrieve and then summarizing that here is really kind of like focusing the system on that information directly. Yeah. What I also really liked and it's not about chat history but it's about summarization\n\nthat when you are uploading your documents to your vector database you can also be doing an extra layer of AI on top of it and generate summaries for the whole document or generate metadata for your whole document and then you store it this way and then you would be searching when you are doing your rag uh through the document title document description that you generated with AI or different um AI generated metadata filters. >> I like it. I like it. I feel like that kind of personalization is where these\n\nthings can become quite powerful. >> I have a really um cool use case for personalization actually which I which I had a look which I found at um at one of the conferences. This was a talk by Garrett Sebeck and Danielle Fitzpatrick. This is the YouTube link. And I really liked um the approach to personalization. This was a BMW use case and they were building if I'm not mistaken this chatbot that's supposed to help you find your perfect BMW car or assemble it. And they come up with this\n\nidea um or they shared this idea of stable properties of the user which I really loved. The idea was that uh the user can have stable properties like um in their case if I'm very techsavvy and I know all those BMW use cases and technical words. So this is going to stay the same throughout the conversation. And this is where we can use this llm on the conversation history to extract this table property like okay it looks like this person is like that and then you can paste it into the prompt in the beginning of the\n\nconversation and it will always stay like that and the LLM might be instructed to then also use this technical language because maybe I would love it. Uh however there are also those fluctuating properties that depend on where we are in the conversation. So for example right now when uh the chatbot told me something I got very angry and in this current message you can see like oh my god can't you just do what I asked that in this moment I'm very angry. So you then have this LLM predicting this\n\nfluctuate actually I don't know if it's fluctuating or floating. Well, one of those two >> similar semantically >> like a fluctuation property uh when generating the next response only. So the instruction and here you can also do maybe even rag on those instructions. So you can have a database where you say um it doesn't have to be rag it can be just a simple a SQL query or like a metadata search that when the user is angry then this is the instruction uh that you need to execute. So you paste that into the\n\nllm only for the current response and then the next time you forget that and you only keep the stable properties until you discover new important properties that are relevant for here and now. I really love this. >> Wow. Okay. You promised advanced rag techniques in this uh talk and you're definitely delivering. This is really interesting. So you're talking about you have essentially stable static properties and that's maybe like I don't know trying to put it into context. I\n\nthink of like um this example this example is great. um the person's mood is not going to um be stable necessarily. Um whereas maybe the task that they're trying to accomplish is and then you have these fluctuating properties and that's like something that is also injected into the prompt and helping the LM respond at that time. So it's almost like adjusting dynamically to the user and their behavior as they're interacting with it. >> Yeah, exactly. And here you can also put\n\nthe whole science behind customer support and sales in because there are multiple frameworks on how you actually work with those fluctuating properties in customer support and sales settings. Like if you notice the person is in a hurry then do this. If you notice that they are unsure do this. So this is a framework you can use to implement this this um rules guidelines. >> Yeah. Yeah. Absolutely. drawing from those subject matter experts like some people are just amazing at customer support and like you're saying there's\n\nall these kind of like methods to these different scenarios and we can encode all of that dynamically here. >> Yeah. Or use AI to analyze the past customer support conversations and then extract those guidelines from them as well. >> Yes. I actually have good luck with uh using AI in in situations like that like as a subject matter expert sometimes. >> What did you try? >> Yeah. So, I was doing prompt engineering. Um, I'm building a poker coaching app. Um, that's the rack system\n\nthat I'm building right now. Um, that's top of mind as you walk me through all this. And I needed a prompt that would just define, it was like a system prompt defining the role of the poker coach and all its knowledge that it has and expertise. And I was writing it myself. And then I asked Chad GBT to write a prompt and described what I was trying to do. And the best performing prompt was actually the one that came from CH GBT. So that was kind of an interesting experiment. >> Yeah, there is even those um like I\n\nthink Claude has this prompt generator. I think that maybe they are good at this retrospective and better if you speak it language. >> Yeah, >> very interesting. >> I have one last technique which I think would be really cool and I think that this is all for for the scary diagrams that I prepared. Do you want to see? >> Amazing. Yeah, let's wrap up with that one. >> Okay. Uh, this one I uh looked up at um at the YouTube channel of voice flow and this one is about asking clarifying\n\nquestions to also help guide the rag and uh the way it works is the user asks um how much for example and then we are using this query rewriting technique. So we say probably the user meant what's the salary? it's a better uh query and then we take chunks from our vector database as we usually do and here where it becomes interesting we have another LLM prompt which goes through this information and it compares it with this and it asks do we need to ask a clarifying question based on what the\n\nuser asked and the information that I have can I already confidently answer the user in this case we can see that we don't have enough information from the user we don't know if they are an engineer or a doctor. So in this case LLM prompt would say yes we need a clarifying question then we would generate this clarifying question which would be are you an engineer or a doctor and then uh we'll do the interaction again and in case we don't need a clarifying question then we just give\n\nresponse. So in this situation how is a system identifying that it needs to ask that clarifying question that there isn't enough uh information to answer. So you typically write an LLM prompt where you input the chunks. So the context retrieved by your rag system and you compare it with the user question. So basically the question that this LLM prompt is trying to answer is is the question specific enough given the data that I have and they had a really cool telecom example. I really recommend this\n\nYouTube video by the way. It's probably one of the most shared YouTube videos at my consultation. So when I am giving consultations I always share it. Um yeah and for this telecom they have a lot of um guidelines and documentation of how the uh phone system work, what you pay for um how things depend on the country and the data is very specific but questions aren't always and by comparing those two you can know if you need to ask a clarifying question. >> Okay. Super helpful. We will link these\n\nYouTube videos that you mentioned down below so that our audience can find their way to those as well. >> Cool. Um, yeah, I think that from the diagrams, that's it. So, I'll stop sharing my screen. >> Okay. Thank you for walking us through that. Very relevant because I'm in the middle of building a rag and now I'm like inspired with so many ideas about how to improve this thing. Um, really appreciate you sharing your insights. It was a lot of stuff that I think a lot of\n\nfolks building rags today are not thinking about yet. So, really enlightening. Um, I guess to wrap up with one last question, I'd love to just hear from you any tips or advice you have for people who are building rag systems today. What's any pitfalls they should avoid based on your experience or any best practices they should make sure that they don't forget? Well, if I only had to choose one advice, it would be about the data quality. So, garbage in means garbage out. So think about how to organize\n\nthose data checking contradiction checking systems to make sure that your data is up to date and is clean. This is probably going to impact your data quality the most. And then use some of the techniques that I mentioned uh my favorites would and the easy to set up would be this FAQ idea. >> Uh generating those FAQs at scale and this clarifying questions. I also think it's quite helpful. >> Very cool. Thank you for sharing. Um, my favorite takeaway, like everything was really, really helpful, but the summary\n\nthing really stood out to me because I feel like that would be such a useful way to like build a user profile based on conversation history. So, I got my homework. That's going to be my next step in my project. So, thank you for sharing. >> I'm curious to see what you're going to build with this poker trainer. >> Yeah, it's a poker coaching app. So, it your play style and then can give you like very personalized advice. So, I'm really leaning on the rag to offer that\n\nlike personalization. Yeah. >> Oh, wow. I have never seen anyone who's building anything like this. >> I see a lot of like apps that are automating like playing poker but not coaching humans and that's where I'm trying to >> Oh, they're taking away the most fun part. >> Exactly. I don't want to automate that. Amazing. Well, this was so helpful. Um, any last words um or anything you wanted to share with our audience? Um maybe keep experimenting, iterating\n\nand sharing your results online so that we can all learn from each other and build uh cool amazing things together. >> Absolutely. I love it when people share their work. I I try to share my work when I'm working on projects. I'll just post even if it's not done, I'll just post blogs about it or share on LinkedIn. So if anybody's working on a rag, drop some comments down below. We'd love to hear what you're building and share share your best practices with us. What worked, what didn't worked. Um\n\nalways love those conversations. >> Yes, me too. I would gladly read those comments and also have discussions on that topic. >> Amazing. All right. Well, hope to see you all in the comments. Thank you all. Bye. >> Bye." + }, + { + "name": "Multimodal LLMs", + "description": "### Summary of Video Content on Retrieval Augmentation in Language Models\n\n---\n\n- **[00:00:06 \u2192 00:01:18] Introduction and Speaker Background** \nThe lecture features Adella, CEO of Contextual AI and adjunct professor at Stanford, with a strong research background in NLP, machine learning, and symbolic systems. He introduces the lecture\u2019s focus on **retrieval augmentation** in language models, chosen as it hadn\u2019t been covered yet in the course.\n\n---\n\n- **[00:01:18 \u2192 00:04:24] Historical Context and User Interface for Language Models** \n- **Language models (LMs)** are not a recent invention by OpenAI but date back decades (e.g., first neural LM in 1991). \n- The core idea: factorize sequence probability by predicting the next token. \n- Early LMs had a **poor user interface** requiring awkward prompts to guide generation. \n- ChatGPT fixed this by enabling **natural language instructions** (\"generate pirate style rap lyrics\"), which is rare in raw web data but crucial for usability. \n- This led to instruction tuning and alignment via reinforcement learning with human feedback (RLHF). \n- **Key insight:** The breakthrough of modern LMs lies in fixing the user interface, not just the model itself.\n\n---\n\n- **[00:04:24 \u2192 00:06:47] Limitations of Current Language Models and Introduction to Retrieval Augmentation (RAG)** \n- Despite progress, LMs still face serious challenges: hallucination (confidently making up facts), **lack of attribution**, being **outdated**, and difficulties in **customization** for specific enterprise needs. \n- **Customization requires external memory** to handle domain-specific data and updates efficiently. \n- Retrieval Augmentation (RAG) architecture couples a **retriever** (which fetches relevant documents) with a **generator** (the LM). \n- This approach allows an **\"open-book\" model** (access to external knowledge) instead of memorizing everything in parameters (\"closed-book\"). \n- RAG is a **semi-parametric model**: parametric LM + non-parametric retrieval component. \n- Benefits include: \n - Easy updates to knowledge base (index). \n - Grounding LM output in retrieved evidence reduces hallucination and improves attribution.\n\n---\n\n- **[00:06:47 \u2192 00:11:15] Architecture, Training, and Frozen RAG Baseline** \n- The RAG system has components: input prompt, retriever, retrieved documents, and language generator. \n- Important design questions include: \n - How to encode queries and documents? \n - What granularity of documents to retrieve (sentence, paragraph, full doc)? \n - How to train the retriever and generator jointly or separately? \n- **Frozen RAG** is a baseline approach where the retriever and generator are pre-trained separately and combined only at test time without further training. \n- Retriever uses a vector database built from document embeddings; retrieval is done by similarity search. \n- Frozen RAG relies heavily on **in-context learning** by the LM but is considered inelegant and suboptimal.\n\n---\n\n- **[00:11:15 \u2192 00:17:44] Sparse and Dense Retrieval Techniques** \n- **Sparse retrieval (TF-IDF, BM25):** \n - Uses sparse vectors counting term frequency and inverse document frequency. \n - Efficient on CPUs and interpretable but limited in semantic understanding. \n- **Dense retrieval:** \n - Uses learned dense vector embeddings (e.g., BERT-based), capturing semantic similarity and synonyms. \n - Enables better retrieval for semantically related queries. \n - Dense embeddings are smaller in dimension but require GPU acceleration for speed. \n- **Maximum Inner Product Search (MIPS)** underlies dense retrieval efficiency. \n- FAISS (Facebook AI Similarity Search) library is a key tool for approximate nearest neighbor (ANN) search in dense retrieval. \n- Hybrid approaches combine sparse and dense retrieval to get best performance. \n- Examples: \n - **ColBERT:** late interaction model improving scoring granularity beyond simple dot product. \n - **SPADE:** sparsifies dense embeddings for efficiency. \n - **Dragon:** state-of-the-art dense retriever trained with progressive data augmentation.\n\n---\n\n- **[00:17:44 \u2192 00:22:10] Hallucination Reduction and Retrieval Benchmarking** \n- Retrieval augmentation reduces hallucination by grounding answers in retrieved documents. \n- Benchmarking studies confirm RAG systems have fewer hallucinations than closed-book LMs. \n- Exact word overlap retrieval is important in cases like brand names (e.g., \u201cApple\u201d the company vs. the fruit). \n- Hybrid search can handle both precise and semantic matching. \n- Sparse and dense retrieval are often frozen; contextualization and training of retriever to generator interaction is an active area of research.\n\n---\n\n- **[00:22:10 \u2192 00:28:33] Improving Retriever-Generator Interaction: Replug and Reranking** \n- **Replug:** \n - Uses likelihood (perplexity) of LM output on retrieved documents to inform retriever training via KL divergence loss. \n - Retriever is optimized to retrieve documents minimizing LM perplexity on correct answers. \n - Works with any blackbox LM with likelihood output. \n- **Reranking on top of BM25:** \n - BM25 produces candidate documents; a neural ranker reranks them using gradients passed back to retriever but LM remains fixed. \n- These approaches improve retrieval quality without retraining the entire system. \n- The goal is to optimize retriever and generator jointly or at least in a coordinated manner.\n\n---\n\n- **[00:28:33 \u2192 00:37:15] Joint Training of Retriever and Generator: RAG 2.0 and Retro** \n- Original RAG paper introduced joint training of retriever and generator, updating query encoder and parts of generator with backpropagation. \n- Key questions: \n - When to retrieve during generation? (Every token, once per sequence, or learned retrieval schedule) \n- Frozen RAG called \u201cFrankenstein\u2019s monster\u201d due to cobbled-together components without end-to-end optimization. \n- **Retro (DeepMind):** \n - Retrieval-augmented LM trained from scratch, 25x smaller but outperforming larger LMs by offloading knowledge to retrieval. \n - Uses late fusion to combine retrieved info and LM scores. \n - No open-source version; reproducibility is challenging. \n- Nvidia\u2019s Retro+ hybridizes Retro and RAG for improved results. \n- Open questions remain about how to pre-train and optimize these architectures efficiently.\n\n---\n\n- **[00:37:15 \u2192 00:44:07] Updating Document Encoder and Scaling Challenges** \n- Updating document encoder is expensive because each update requires re-encoding the entire corpus (potentially trillions of tokens). \n- Realm paper proposes asynchronous sharded updates to handle this. \n- Often only query encoder is updated for efficiency; document encoder remains frozen. \n- High-quality document embeddings are prerequisite for query-side fine-tuning to work well. \n- Atlas paper extensively benchmarks different retriever training losses: \n - Replug style loss and leave-one-out loss outperform frozen or no joint training baselines. \n- Training the retriever with tasks aligned to the LM\u2019s training (e.g., T5-style losses) improves performance.\n\n---\n\n- **[00:44:07 \u2192 00:52:15] Training Data, Contextualization, and Generator Architectures** \n- Document training corpora can differ from test-time indices; more data generally improves retrieval accuracy. \n- Mistral 7B model uses sliding window attention to handle longer sequences efficiently, which may inspire architectural adaptations for RAG generators. \n- Retro uses chunk cross-attention to integrate retrieved chunks within LM architecture. \n- Retrieval can happen at different granularities and frequencies (per token, per chunk, once per sequence). \n- Efficient training techniques include reinforcement learning and approximate gradient methods to handle blackbox LMs. \n- Query-side updates suffice in many cases, but document-side updates may be needed for general language modeling tasks.\n\n---\n\n- **[00:52:15 \u2192 01:01:00] Retrieval Strategies, Cascading, Long Context vs RAG** \n- Retrieval can be hierarchical: first find relevant documents, then find relevant chunks within them. \n- Context expansion around retrieved chunks can improve LM input. \n- Long context windows in LMs (e.g., entire books) are inefficient since most content is irrelevant to a query. \n- Long context attention mechanisms often use sparsity, effectively becoming retrieval-like mechanisms. \n- RAG-style systems are more efficient by retrieving only relevant passages. \n- Learning when and how often to retrieve is an open problem; active retrieval systems like FLARE let the LM decide retrieval timing. \n- Cascaded retrieval and reranking pipelines are common.\n\n---\n\n- **[01:01:00 \u2192 01:09:01] Advanced RAG, Tool Use, Instruction Tuning, and Open Problems** \n- Language models can be seen as **tool users**, with retrieval as one tool among others including web search, rankers, and reasoning modules. \n- **Self-RAG** architecture involves active retrieval, critique, and natural language inference with a single LM. \n- Instruction tuning mostly applies to LMs, not retrievers; extending instruction tuning to entire retrieval-augmented systems is an emerging research area. \n- Developer communities have created frameworks (LangChain, LlamaIndex) and open source vector databases (Grom, WV8) mostly based on frozen RAG. \n- Hybrid search, recursive retrieval, zero-shot LM rankers, and hallucination reduction via hypothetical document generation are active techniques. \n- Open questions remain: \n - How to pre-train retrieval-augmented architectures effectively? \n - Can we decouple memorization to retrieval and keep LMs smaller and reasoning-focused? \n - Can vector databases be replaced or merged with traditional sparse DBs? \n - How to generate better synthetic data for training? \n - How to reliably measure retrieval accuracy and hallucination in RAG systems? \n- Multimodality is a hot topic: augmenting LMs with vision and other modalities via retrieval mechanisms. \n- Future systems will optimize retrieval and generation jointly from a **systems perspective** rather than isolated components.\n\n---\n\n- **[01:09:01 \u2192 01:19:00] Final Reflections on Hallucination, Grounding, and Scaling** \n- Hallucination is often misunderstood: it specifically means LM output contradicts **retrieved ground truth**, not just making errors. \n- Systems can tune how much to hallucinate (useful for creative tasks vs factual tasks). \n- Control knobs beyond sampling temperature are needed for nuanced hallucination control. \n- Ground truth can be defined by varying indices (e.g., peer-reviewed papers vs preprints). \n- Efficient retrieval is key to scaling: embeddings allow fast dot product search, cheaper than full LM self-attention over large context. \n- Dedicated retrieval hardware is emerging from major chip manufacturers to accelerate dense retrieval. \n- End-to-end training of retriever-generator systems is critical to reduce hallucination and improve grounding. \n- The field is rapidly evolving with many open research opportunities.\n\n---\n\n### Key Insights\n\n- **Retrieval Augmentation (RAG)** is fundamental for overcoming closed-book LM limitations such as hallucination, outdated knowledge, and lack of attribution. \n- RAG is a **semi-parametric approach** combining parametric LMs with non-parametric retrieval. \n- Effective RAG systems need **joint training or tight integration** of retriever and generator. \n- Sparse and dense retrieval methods complement each other; **hybrid models are state-of-the-art**. \n- Training objectives that optimize retriever performance relative to LM output likelihood (e.g., Replug loss) yield better retrieval alignment. \n- Scaling retrieval with document encoder updates is computationally expensive and requires asynchronous or approximate methods. \n- Emerging architectures like **Retro** suggest retrieval-augmented LMs can be smaller, more efficient, and outperform larger closed-book LMs. \n- Multimodal retrieval augmentation is an important future direction. \n- Measuring retrieval quality and hallucination remains challenging and is an open research problem. \n- Instruction tuning and data generation for entire RAG systems (beyond LMs) are key frontiers. \n- Retrieval hardware accelerators and database integration will be critical for deployment.\n\n---\n\n### Timeline Table of Key Concepts and Advances\n\n| Timestamp | Topic/Concept | Key Points |\n|------------------|-----------------------------------------------|------------------------------------------------------------------------------------------------|\n| 00:00 - 00:04 | Intro & LM history | LMs date back decades; ChatGPT fixed usability via instructions |\n| 00:04 - 00:07 | Limitations of LMs, intro to RAG | Hallucination, stale knowledge; RAG = retrieval + generation |\n| 00:07 - 00:11 | RAG architecture & frozen RAG baseline | Separate retriever/generator, vector DB, in-context learning |\n| 00:11 - 00:18 | Sparse vs Dense retrieval | TF-IDF, BM25 (sparse); BERT embeddings (dense); FAISS for efficient search |\n| 00:18 - 00:22 | Hybrid retrieval & hallucination reduction | Combining sparse+dense; RAG reduces hallucination |\n| 00:22 - 00:28 | Retriever optimization: Replug, reranking | Losses based on LM perplexity; reranking on BM25 candidates |\n| 00:28 - 00:37 | Joint training & Retro | End-to-end RAG training; Retro smaller but powerful retrieval-augmented LM |\n| 00:37 - 00:44 | Document encoder updates & scaling challenges | Expensive to update docs; Realm async updates; query-side training usually sufficient |\n| 00:44 - 00:52 | Training data & generator architectures | Sliding window attention (Mistral 7B); chunk cross-attention (Retro); retrieval frequency |\n| 00:52 - 01:01 | Retrieval strategies & long context comparison | Cascading retrieval; long context inefficient; active retrieval (FLARE) |\n| 01:01 - 01:09 | Advanced RAG, tool use, instruction tuning | Tool augmentation beyond retrieval; instruction tuning retriever; open source frameworks |\n| 01:09 - 01:19 | Hallucination definitions & future directions | Hallucination vs error; control over hallucination; retrieval hardware; end-to-end training |\n\n---\n\n### Glossary / Definitions\n\n| Term | Definition |\n|-------------------------|----------------------------------------------------------------------------------------------|\n| Language Model (LM) | A neural network predicting next tokens in text sequences |\n| Large Language Model (LLM)| LMs with billions of parameters trained on massive text corpora |\n| Retrieval Augmentation (RAG) | Combining a retriever (fetching documents) with a language model generator |\n| Sparse Retrieval | Retrieval based on sparse vectors (e.g., TF-IDF, BM25) counting term frequency |\n| Dense Retrieval | Retrieval based on dense learned embeddings for semantic similarity |\n| In-Context Learning | Using the LM\u2019s ability to learn from prompt text without parameter updates |\n| Frozen RAG | RAG system with fixed retriever and generator, no joint training |\n| Replug | Training method optimizing retriever to reduce LM perplexity on retrieved documents |\n| Retro | Retrieval-augmented LM trained from scratch, smaller than traditional LMs |\n| FAISS | Facebook AI Similarity Search, an ANN search library for fast vector retrieval |\n| Maximum Inner Product Search (MIPS)| Efficient search for vectors maximizing dot product similarity |\n| Instruction Tuning | Fine-tuning LMs on instructions to improve prompt following |\n| Hallucination (in LMs) | LM output that contradicts a known ground truth document |\n| Hybrid Search | Combining sparse and dense retrieval results for better accuracy |\n| Cross Attention | Attention mechanism allowing LM to attend to retrieved documents |\n| FLARE | Active retrieval method where LM decides when to retrieve |\n\n---\n\n### Frequently Asked Questions (FAQ)\n\n**Q: Why use retrieval augmentation instead of just bigger LMs?** \nA: RAG allows models to access up-to-date and vast external knowledge without memorizing it, reducing hallucination and enabling customization.\n\n**Q: What is the difference between sparse and dense retrieval?** \nA: Sparse retrieval relies on exact word matching (TF-IDF/BM25), while dense retrieval uses semantic vector embeddings to capture meaning and synonyms.\n\n**Q: How do you train retrievers to work well with LMs?** \nA: By optimizing retrieval based on LM output likelihoods (e.g., Replug loss) or learning to rank retrieved documents to minimize LM perplexity.\n\n**Q: What are the challenges with updating the document encoder?** \nA: It requires re-encoding the entire corpus after each update, which is computationally expensive at large scale.\n\n**Q: Can retrieval augmentation reduce hallucination completely?** \nA: It reduces hallucination by grounding LM output in evidence but cannot eliminate it unless the entire system is jointly trained and carefully controlled.\n\n**Q: How does long context attention compare with retrieval?** \nA: Long context attention is computationally expensive and inefficient for large documents; retrieval augmentation is a more scalable alternative.\n\n**Q: Are multimodal retrieval-augmented models feasible?** \nA: Yes, recent work (e.g., GPD-4V) shows integrating vision and other modalities with retrieval is a growing area.\n\n---\n\nThis summary encapsulates the core content of the lecture strictly based on the transcript provided, emphasizing key concepts, open problems, and research directions in retrieval-augmented language modeling.", + "links": "https://example.com/resource/15", + "module": "Multimodal LLMs", + "module_id": 1, + "submodule_id": 15, + "index": 15, + "transcript": "\n### Lecture Summary: Retrieval-Augmented Generation (RAG) and Contextualization of Language Models\n\nWelcome everyone. Today we\u2019ll discuss **retrieval augmentation** \u2014 one of the most active areas in modern NLP. Our speaker, Adella, is CEO of Contextual AI, an enterprise LLM company, an adjunct professor at Stanford, and former head of research at Hugging Face and Facebook AI. His research focuses on machine learning and NLP, especially on language understanding, generation, and evaluation.\n\n---\n\n### 1. The Age of Language Models\n\nWe live in the era of large language models (LLMs). However, **language models** are not a recent invention\u2014neural language modeling dates back to the 1990s. The idea is simple: given a sequence of tokens, predict the next token. Early versions (e.g., Bengio et al., 2003) already had embeddings and similar formulations to today\u2019s models. What changed is scale.\n\nThe main breakthrough of **ChatGPT** wasn\u2019t the model architecture\u2014it was the **user interface**. Previously, using a language model required strange prompt engineering. ChatGPT solved this through *instruction tuning* and *reinforcement learning from human feedback (RLHF)*, allowing people to simply \u201cask\u201d the model naturally.\n\n---\n\n### 2. Problems with Pure Language Models\n\nEven with good interfaces, LLMs have serious issues:\n\n* **Hallucination:** Models generate incorrect facts with high confidence.\n* **Attribution:** We don\u2019t know *why* a model produced an answer.\n* **Staleness:** Models become outdated quickly.\n* **Editing:** We can\u2019t easily revise or delete knowledge (e.g., for GDPR compliance).\n* **Customization:** Hard to adapt models to specific domains or private data.\n\nThese limitations make LLMs unreliable for enterprise or high-accuracy applications.\n\n---\n\n### 3. Enter Retrieval-Augmented Generation (RAG)\n\nThe solution many have turned to is **RAG** \u2014 connecting a generator (LLM) with an **external retriever** or **memory**. Instead of relying solely on parameters, the model can look up information dynamically.\n\nThink of **closed-book vs open-book exams**:\n\n* Closed-book \u2192 memorize everything (parametric LMs).\n* Open-book \u2192 look up relevant facts when needed (RAG).\n\nThis architecture has two main parts:\n\n1. **Retriever:** Fetches relevant documents or passages from an external database.\n2. **Generator:** Takes the query and retrieved context to produce an answer.\n\nThis approach gives **updatable, grounded, and customizable** models that hallucinate less and can cite their sources.\n\n---\n\n### 4. Retrieval Foundations\n\nEarly retrieval methods used **sparse retrieval** (e.g., TF-IDF, BM25).\nBM25 scores documents by term frequency and inverse document frequency, emphasizing distinctive words. It\u2019s fast and efficient on CPUs but fails with synonyms or paraphrases.\n\n**Dense retrieval** (e.g., DPR, ORQA) replaces sparse counts with **embedding-based similarity** using models like BERT. Each document and query is encoded into a dense vector; retrieval is done via dot product similarity. This enables *semantic* retrieval\u2014capturing meaning rather than exact words.\n\nEfficient dense retrieval relies on **approximate nearest neighbor (ANN)** search libraries like **FAISS** (Facebook AI Similarity Search). FAISS and similar systems power modern vector databases such as Pinecone, Weaviate, or Milvus.\n\nHybrid approaches like **Spade** (sparse + dense) and **Dragon** (progressive data augmentation) combine both worlds, often giving the best results.\n\n---\n\n### 5. Retrieval-Augmentation Benefits\n\nWhy does retrieval help?\n\n* **Customization:** Swap or update indices easily.\n* **Grounding:** Model statements can be traced to retrieved evidence.\n* **Reduced Hallucination:** Model \u201creads\u201d rather than imagines.\n* **Freshness:** The retriever can be updated continuously.\n\nEmpirical work (e.g., \u201cRetrieval Augmentation Reduces Hallucination,\u201d 2021) confirms significant factual accuracy improvements over closed-book models.\n\n---\n\n### 6. Improving the Retriever\n\nMost early RAG systems were **frozen**\u2014retriever and generator trained separately. That\u2019s easy but suboptimal.\nNewer work optimizes retrieval jointly or iteratively with generation.\n\n**Examples:**\n\n* **RePlug (2023):** Adjusts the retriever to prefer documents that minimize perplexity of the correct answer for the language model (using KL divergence).\n* **In-Context RAG:** Adds a learned ranker on top of BM25 results; backpropagates only through the ranker while keeping the LLM frozen.\n* **Joint Optimization:** Later works train both retriever and generator together end-to-end.\n\n---\n\n### 7. Fully Joint Training: RAG and Beyond\n\nThe original **RAG model (Lewis et al., 2020)** combined dense retrieval with sequence-to-sequence generation (e.g., BART). It allowed partial gradient flow into the retriever and generator, updating both.\n\nChallenges arose about **how often to retrieve**:\n\n* Retrieve once per document?\n* Retrieve per token?\n* Retrieve dynamically as needed?\n\n**Fusion-in-Decoder (FiD)** later addressed scalability\u2014allowing hundreds of retrieved passages by letting the decoder attend over multiple contexts efficiently.\n\n**KNN-LM** and **Retro (DeepMind)** extended this by integrating retrieval directly into pretraining.\nRetro showed that a *25\u00d7 smaller model with retrieval* could match or beat a much larger standalone LM, demonstrating that external memory can substitute for parameters\u2014though replication has proven difficult.\n\n---\n\n### 8. Training Retrieval-Augmented Systems\n\nJoint training is expensive, especially if the **document encoder** must be updated (re-encoding billions of documents after every gradient step is infeasible).\nGoogle\u2019s **REALM** introduced asynchronous updates\u2014re-encoding the corpus periodically instead of continuously. Later works like **Atlas (Meta)** extended this for generative T5 models, testing various training losses:\n\n* **RePlug loss:** Match documents that lower perplexity.\n* **Leave-One-Out loss:** Measure impact when removing a document.\n* **FID-style distillation:** Use teacher models for supervision.\n\nFindings:\n\n* Updating **query encoders** alone often suffices if document embeddings are strong.\n* Retrieval-augmented models outperform closed-book equivalents significantly.\n\n---\n\n### 9. Efficient Retrieval and Scaling\n\nUpdating retrievers at scale remains costly. Some methods cluster similar queries to reuse representations or perform **in-batch negatives** for contrastive learning.\n\nLegal and ethical issues also arise around data. **SILO** (2023) proposed training on safe public data (like Wikipedia) while retrieving from larger, potentially proprietary indices during inference\u2014balancing compliance and performance.\n\n---\n\n### 10. Known Limitations\n\nEven in RAG, ordering matters. Research shows LLMs attend mostly to the **first and last** retrieved chunks (\u201clost-in-the-middle\u201d effect). This brittleness means that small retrieval ranking errors can greatly change results.\n\nMoreover, because many RAG systems still use **frozen retrievers**, models can ignore retrieved facts, especially if the base LM is prone to hallucination.\n\n---\n\n### 11. When and How to Retrieve\n\nInstead of always retrieving, models can learn **when** retrieval is useful.\nThe **FLARE** model introduces *active retrieval*, where the LM decides whether to search and what to query. This connects to **agentic LMs**\u2014models that use tools dynamically.\n\n---\n\n### 12. Toward RAG 2.0: Systems, Not Models\n\nThe field is evolving toward **end-to-end optimization**, integrating all components\u2014retrieval, ranking, generation, even chunking\u2014into one trainable system. This mirrors the evolution of earlier deep learning systems (e.g., moving from handcrafted parsers to end-to-end NLP).\n\nFrameworks like **LangChain** and **LlamaIndex** have popularized *frozen RAG pipelines*, combining vector and sparse search, recursive retrieval, and hallucination reduction tricks like **Hypothetical Document Embeddings (HyDE)**\u2014where the LM \u201cimagines\u201d possible answers, retrieves evidence, then rewrites the final grounded response.\n\nThese frameworks are effective but still non-trainable. The next leap is **RAG 2.0** \u2014 contextualized, trainable, efficient, and adaptive systems.\n\n---\n\n### 13. Open Research Directions\n\n1. **Pretraining RAG models** from scratch (as in Retro or Atlas) to replace static LMs.\n2. **Scaling laws:** Understanding trade-offs between LM size and retrieval corpus size.\n3. **Beyond bi-encoders:** Moving past static vector databases toward re-rankers or hybrid methods.\n4. **Synthetic data generation** for instruction-tuning retrieval systems.\n5. **Evaluation:** New metrics for retrieval accuracy, grounding, and hallucination, beyond task accuracy.\n6. **Multimodal retrieval:** Combining text, images, and other data types (e.g., the *Lens* model and Gated Flamingo-like architectures).\n\nUltimately, the goal is **small, efficient, grounded language models** that reason with external knowledge rather than memorizing it.\n\n---\n\n### 14. Hallucination, Grounding, and Control\n\nHallucination isn\u2019t simply \u201cwrong answers.\u201d It\u2019s when model output contradicts available evidence.\nRetrieval grounding can eliminate most hallucinations if the LM truly relies on its sources. Still, hallucination can be *useful* in creative tasks\u2014so ideally, we\u2019d have a **\u201challucination knob\u201d** controlling factuality vs. creativity beyond simple temperature scaling.\n\n---\n\n### 15. Hardware and Infrastructure Outlook\n\nEfficient retrieval has become a major hardware focus. Large chipmakers are developing **dedicated retrieval accelerators** for dense vector search, complementing GPU computation. Meanwhile, vector databases are converging with traditional systems (e.g., PostgreSQL + vector extensions), blurring the line between ML infrastructure and databases.\n\n---\n\n### 16. Conclusion\n\nRAG represents a shift from **models to systems**\u2014from static LLMs to dynamic, grounded architectures.\nFrozen RAG (RAG 1.0) is just the beginning; **RAG 2.0** aims for fully integrated, end-to-end contextual systems that:\n\n* Learn when and what to retrieve.\n* Update knowledge dynamically.\n* Minimize hallucination while remaining efficient.\n\nIn the long run, this approach promises **smaller, cheaper, and more reliable** AI systems that reason with external knowledge instead of memorizing it.\n\nIf you\u2019re interested in this line of work, there are ongoing research and practical projects at Stanford and Contextual AI exploring these directions. Thank you.\n\n" + }, + { + "name": "Vision Language Models", + "description": "vision language models, image text alignment, multimodal transformers, cross modal attention, visual grounding, image captioning", + "links": "https://example.com/resource/16", + "module": "Vision Language Models", + "module_id": 1, + "submodule_id": 16, + "index": 16 + }, + { + "name": "Policy learning using DQN", + "description": "deep q network, reinforcement learning, value based learning, q learning, epsilon greedy, reward optimization", + "links": "https://example.com/resource/17", + "module": "Policy learning using DQN", + "module_id": 1, + "submodule_id": 17, + "index": 17 + }, + { + "name": "RLHF", + "description": "- [00:00:14 \u2192 00:04:05] \n### Introduction: Motivation Behind Quantization, Pruning, and Distillation \n- **Context:** The discussion centers on **quantization, pruning, and distillation** as methods to address challenges arising from the exponential growth in large language model (LLM) sizes and their inference costs. \n- **Model Growth & Performance:** \n - LLMs are increasing exponentially in size and performance (measured by reductions in test loss across language tasks). \n - Larger models require increasingly powerful hardware, raising deployment costs. \n- **Key Deployment Challenges:** \n - **Hardware demands:** Larger GPU requirements limit who can run LLM inference. \n - **Latency:** Large models take longer to respond, which is problematic for real-time applications like chatbots. \n - **Inference cost:** The cost to serve users must be less than the revenue generated, making high-cost LLMs commercially challenging. \n - **Sustainability:** Running large models consumes significant energy and increases carbon emissions. \n- **Example:** \n - OpenAI's **GPT-4o Mini** trades a slight accuracy drop for a dramatic reduction in inference cost compared to GPT-4o and GPT-3.5 Turbo, demonstrating the commercial viability of smaller, cheaper models. \n- **Goal:** \n - Explore techniques to deploy LLMs cost-effectively while maintaining reasonable performance. The techniques fall into two categories: \n 1. **Model compression** (reduce model size with minimal accuracy loss). \n 2. **Efficient engineering** (optimize software/hardware to reduce inference costs without changing the model). \n- **Focus of Lecture:** Model compression, specifically quantization, pruning, and distillation.\n\n---\n\n- [00:06:16 \u2192 00:10:45] \n### Overview of Model Compression Techniques \n- **Quantization:** Reduce the number of bits used to represent model parameters and activations, retaining the same model architecture. \n- **Pruning:** Remove parts of the network (weights or structures) to reduce size while trying to maintain performance. \n- **Distillation:** Use a large, well-trained teacher model to train a smaller student model by mimicking the teacher\u2019s outputs. \n- **Trade-offs:** \n - Distillation is **least lossy** but **most computationally expensive** due to the need for retraining. \n - Quantization and pruning are **simpler and cheaper** but somewhat lossy.\n\n---\n\n- [00:10:45 \u2192 00:16:11] \n### Deep Dive into Quantization \n- **Neural Network Inference Recap:** \n - Inputs pass through weights (parameters) to produce activations. Both weights and activations are memory-expensive. \n- **Goal:** Efficiently store and compute parameters/activations to save memory, speed, and cost. \n- **Floating Point Representation:** \n | Format | Bits | Sign | Exponent Bits | Mantissa Bits | Notes |\n |--------|------|------|---------------|---------------|-------|\n | FP32 | 32 | 1 | 8 | 23 | High precision, standard in older ML |\n | FP16 | 16 | 1 | 5 | 10 | Reduced precision, widely used in DL now |\n | INT8 | 8 | 1 | n/a | 7 (int bits) | Integer representation, much smaller |\n- **Quantization Process (FP32 to INT8):** \n - Identify maximum absolute value (alpha) in a vector. \n - Map range [-alpha, alpha] to [-127, 127] (symmetric quantization). \n - Compute scale factor = 127 / alpha. \n - Quantized value = round(original \u00d7 scale factor). \n- **De-quantization:** Inverse operation dividing by scale factor to approximate original FP32 value. \n- **Quantization Error:** The difference between original and de-quantized values; inherent lossy compression. \n\n---\n\n- [00:16:11 \u2192 00:21:49] \n### Quantization Usage in Machine Learning \n- **Two approaches:** \n 1. **Post-Training Quantization (PTQ):** Apply quantization after full precision training; no retraining needed, low cost. \n 2. **Quantization-Aware Training (QAT):** Model is trained with quantization effects simulated, producing better accuracy but more expensive (e.g., QLoRA). \n- **Scale factors for weights are static** (weights are fixed after training). For activations/inputs, scale factors vary dynamically, so calibration datasets help estimate ranges during PTQ. \n- **Typical PTQ inference flow:** \n - Quantize weights and activations \u2192 perform matrix multiplications in INT8 \u2192 de-quantize results \u2192 pass to next layer. \n- **Hardware advantage:** \n - INT8 operations can be executed much faster and with less energy than FP16 or FP32. \n - Example: NVIDIA H100 GPU provides higher throughput (teraflops) for INT8 tensor operations compared to FP16. \n\n---\n\n- [00:22:37 \u2192 00:26:54] \n### Challenges and Solutions for INT8 Quantization in Large Models \n- **Problem:** After models reach about 2.7 billion parameters, INT8 quantization causes accuracy degradation. \n- **Cause:** Presence of **outliers** in weight/activation vectors\u2014extremely large values skew scale factor computation, causing most values to collapse into narrow quantized bins, losing information. \n- **Solution: LLM.int8() technique:** \n - Identify outliers exceeding a threshold and exclude them from scale factor computation. \n - Quantize the non-outlier values normally. \n - Keep outliers in full precision and add them back after quantized matrix multiplication. \n - Result: Restores accuracy to near FP16 levels while retaining INT8 efficiency.\n\n---\n\n- [00:27:38 \u2192 00:40:02] \n### Quantization-Aware Training: QLoRA \n- **Motivation:** Fine-tuning huge models (e.g., 65B parameters) requires huge memory (up to 780GB), which is prohibitive. \n- **QLoRA:** A PEFT (Parameter-Efficient Fine-Tuning) technique combining: \n - LoRA (low-rank adaptation) for efficient fine-tuning. \n - Normal Float 4-bit (NF4) quantization of weights assuming normal distribution instead of uniform distribution. \n - Double quantization: further quantizes quantization constants to reduce memory. \n - Hardware-aware optimizations (e.g., paging between GPU and CPU memory). \n- **Normal Float 4 (NF4) Quantization:** \n - Unlike uniform bucket spacing, assumes weights follow a Gaussian distribution, improving quantization bucket allocation and accuracy. \n- **Double Quantization:** \n - Quantizes the scale/offset constants themselves to 4-bits, saving memory without significant accuracy loss. \n- **Paging:** \n - Uses CPU memory to supplement limited GPU memory during fine-tuning, enabling training on GPUs with less VRAM than model size. \n- **Effectiveness:** \n - QLoRA allows fine-tuning a 65B parameter model in 48GB memory with minimal performance degradation (similar to FP16). \n\n---\n\n- [00:40:02 \u2192 00:43:58] \n### Pruning Techniques \n- **Definition:** Removing parts of the network to reduce size and inference cost. \n- **Types:** \n - **Unstructured pruning:** Remove individual weights based on magnitude (lowest magnitude weights are discarded). \n - Can prune 40% weights with minimal performance loss. \n - More aggressive pruning (>40%) degrades performance but can be recovered partially by fine-tuning. \n - Problem: sparse weights may not translate to actual speedup unless hardware supports sparse operations. \n - **Structured pruning:** Remove whole structural components (e.g., layers, attention heads, hidden units). \n - Hardware (e.g., NVIDIA A100) supports specific sparsity patterns like 2-to-4 sparsity for speedup. \n - Pruning algorithms can be designed aware of these hardware patterns to maximize efficiency. \n- **Recent advances:** Pruning of various network components (layers, heads, feed-forward submodules) to achieve performance gains and speedups.\n\n---\n\n- [00:44:31 \u2192 00:51:10] \n### Knowledge Distillation \n- **Concept:** Train a smaller \"student\" model to imitate a larger \"teacher\" model\u2019s behavior. \n- **Challenges:** \n - Requires access to representative data similar to training data of the teacher, which is often unavailable. \n - Distillation is data-dependent; synthetic or proxy data may be needed, especially for task-specific distillation. \n- **Process:** \n - Feed input to teacher \u2192 get output distribution (soft labels). \n - Feed same input to student \u2192 train student to match teacher\u2019s output distributions via loss functions (e.g., cross-entropy, KL-divergence). \n- **Loss types:** \n - Hard target distillation: use argmax label (one-hot). \n - Soft target distillation: use full softmax distribution, which retains more information and yields better performance. \n- **Sequence distillation:** \n - Word-level: matches output distributions token-by-token. \n - Sequence-level: tries to match the entire output sequence distribution but is computationally expensive. \n - Approximate sequence-level by using beam search output as target. \n- **Advantages:** \n - Allows designing smaller models with reduced latency tailored to application needs. \n- **Disadvantages:** \n - Very expensive in compute due to repeated teacher inference and retraining. \n\n---\n\n- [00:51:35 \u2192 00:56:01] \n### Connection Between Large Models and Distillation: Synthetic Data Generation \n- **Why extremely large models (e.g., 405B parameters)?** \n - They capture nuanced data distributions and generate high-quality outputs. \n - These models serve as \"teachers\" to generate synthetic training data to fine-tune smaller models. \n- **Self-Instruct Method:** \n - Start with a small set of manually created \"seed\" instruction tasks (~175 tasks). \n - Use the large LLM to generate new instructions and associated inputs/outputs based on seed tasks. \n - Filter and validate generated tasks, expanding the training set. \n - Use this synthetic dataset to fine-tune smaller models to follow instructions effectively. \n- **Advantages:** \n - Overcomes lack of real data by leveraging powerful LLMs\u2019 generative capabilities. \n - Enables creation of specialized, instruction-following smaller models with less manual data curation. \n- **Industrial Application:** \n - Use synthetic data from LLMs to fine-tune smaller models for domain-specific tasks (e.g., natural language interfaces for software). \n - Resulting smaller models are more cost-effective and responsive.\n\n---\n\n- [00:56:01 \u2192 00:56:40] \n### Summary and Conclusions \n- **Main focus:** Reduce inference cost while maintaining model performance through model compression techniques. \n- **Techniques discussed:** \n - **Quantization:** Including post-training INT8 quantization with LLM.int8() to handle outliers and QLoRA for quantization-aware fine-tuning. \n - **Pruning:** Both unstructured and structured pruning, with emphasis on hardware support for sparsity. \n - **Distillation:** Training smaller student models from large teachers using soft targets and synthetic data generation paradigms. \n- **Importance:** These techniques enable practical, sustainable, and cost-effective deployment of LLMs, balancing accuracy, latency, and resource usage.\n\n---\n\n### Key Terminology \n| Term | Definition |\n|-----------------------|----------------------------------------------------------------------------------------------|\n| Quantization | Reducing the number of bits to represent model parameters/activations to save memory and compute. |\n| Pruning | Removing weights or structural components from a network to reduce its size and complexity. |\n| Distillation | Training a smaller model (student) to mimic the outputs of a larger model (teacher). |\n| Post-Training Quant. | Applying quantization after full precision training without retraining. |\n| Quantization-Aware Training (QAT) | Training the model with quantization effects included to improve accuracy post-quantization. |\n| LLM.int8() | A method for INT8 post-training quantization that excludes outliers from scale factor computation. |\n| QLoRA | A quantization-aware fine-tuning approach combining NF4 quantization, double quantization, and LoRA. |\n| NF4 Quantization | Normal Float 4-bit quantization assuming Gaussian distribution for better bucket allocation. |\n| Double Quantization | Quantizing quantization constants themselves to reduce memory footprint. |\n| LoRA | Low-Rank Adaptation method for parameter-efficient fine-tuning. |\n| Self-Instruct | A method of generating synthetic instruction-following data from a large LLM to fine-tune smaller models. |\n\n---\n\n### Quantitative Comparison Example: Inference Cost (GPT-4o vs GPT-4o Mini) \n| Model | MMLU Performance Drop | NGSM Performance Drop | Relative Inference Cost (USD) | Notes |\n|--------------|-----------------------|----------------------|------------------------------|---------------------------------------|\n| GPT-4o | Baseline | Baseline | $80 | High accuracy, high cost |\n| GPT-4o Mini | ~6 points lower | ~3 points lower | $3 | Slight accuracy drop with >25x cost reduction |\n\n---\n\n### Summary Timeline Table of Key Topics Covered \n| Timestamp | Topic Covered |\n|--------------------|-------------------------------------------------------------------------------------------------|\n| 00:00:14 - 00:04:05 | Introduction: Model size growth, inference challenges, GPT-4o Mini example |\n| 00:06:16 - 00:10:45 | Model compression overview: Quantization, pruning, distillation |\n| 00:10:45 - 00:16:11 | Quantization fundamentals: floating-point formats, quantization and de-quantization process |\n| 00:16:11 - 00:21:49 | Quantization in inference: PTQ, QAT, calibration, hardware advantages |\n| 00:22:37 - 00:26:54 | INT8 quantization challenges and LLM.int8() solution for outliers |\n| 00:27:38 - 00:40:02 | Quantization-aware fine-tuning: QLoRA, NF4 quantization, double quantization, paging |\n| 00:40:02 - 00:43:58 | Pruning techniques: unstructured vs structured, hardware support |\n| 00:44:31 - 00:51:10 | Knowledge distillation: concept, loss functions, sequence distillation |\n| 00:51:35 - 00:56:01 | Large model use in synthetic data generation and self-instruct method |\n| 00:56:01 - 00:56:40 | Summary and conclusions |\n\n---\n\nThis summary provides a comprehensive, structured, and detailed overview of the video transcript\u2019s key points about quantization, pruning, and distillation in large language model deployment and training.", + "links": "https://example.com/resource/18", + "module": "RLHF", + "module_id": 1, + "submodule_id": 18, + "index": 18, + "transcript": "\nAlright, hello everyone. So, today we will be talking about quantization, pruning, and distillation. So, these all fall under the same umbrella, and maybe once I go into the introduction, It will be clear why we are discussing these three topics together. So, like we discussed last time, the reason why we wanted to do theft was Because of how the model sizes have been increasing over time. And this is, again, a recap of that. Over time, the size of the model is increasing exponentially. It is not just the size but also the performance of these models.\n\nare also increasing over time. So, here we see that these are the test laws for various language modeling tasks. on different datasets and you see that they are decreasing considerably Based on the number of parameters that are in the large language model. So now, what is the flip side of having such large models during inference? So, the first thing, as we discussed last time, is that the bigger the model is, You will have to buy new hardware to support them. In a sort of way, it is not very friendly to keep buying hardware.\n\nAnd when the model keeps growing over time. So, this also puts a cap on how many organizations can actually run these LLM inferences. So, the GPU requirement is going to get larger and One of the biggest problems in deployment is latency. So, the larger the model is, the more time it will take to come back. with a completion for a given prompt. Now, let's say that you have a chatbot that has been deployed. whose backbone is an LLM, then having to wait for 30 or 40 seconds, It seems really difficult at this point in time when we are so used to.\n\nGetting replies very quickly. And the way the LLM field is progressing now, People are not just using a single LLM call per sort of response. For example, there is now a new paradigm called agentic behavior. where once a language model responded, You ask the same language model or a different language model to reflect on the output. or even sometimes execute the output, for example, if the model generates a code, you execute the code, Get the outputs and then make the model reflect on the output.\n\nAnd then decide whether there is an execution error; should it redo things. And finally, you give back the answer. So now, if you have to make multiple LLM inferences, to just get back with one output. Then that's going to increase latency even more. So, latency is one of the biggest concerns. Third is the inference cost. If you have an application deployed that uses LLMs, Of course, you'll be worried about how much money. You're going to spend to serve a single user. And the money that a single user is going to pay you.\n\nshould be more than what you invest for that user. So, if LLM is for slightly more improvement in accuracy, If you have to spend a lot more, then it does not seem commercially viable. So, of course, the inference cost is going to be one of the biggest dimensions. And finally, sustainability and environmental concerns. So, you need a lot of energy to run these LLMs. You need to support data centers that require cooling water. And so, if your energy is not coming from a sustainable source, Then there are a lot of carbon emissions that sort of affect the overall environment.\n\nSo, again, LLM inference has a huge impact on sustainability. So here, before going into how we can do this, Let's look at what just happened very recently. In OpenAI, after they released GPT-4, which is the pink model, They released something called GPT-4o Mini. Now, GPT-4o Mini is sort of like, let's say, six points lower on MMLU. On NGSM, it is three points lower. The GPT-40 mini is also almost always better than the previous version. I mean the one before GPT-4, GPT-3.5. But if you see the inference cost that you will have to pay,\n\nYou will end up paying a lot more for GPT-3.5 Turbo than GPT-4 Mini. Even now, this is a website called GPT for Work. where, if you give them the number of tokens in the input, The number of tokens that you expect the model to have in its output. and how many times you'll end up calling this over and over again, It'll give you an estimate of how much money you will end up spending. So, here, if you see for the same amount, for the same setting, For GPT-4o, you end up spending $80, but for GPT-4o Mini, you only spend $3.\n\nYou guys just saw the performance drop you get for GPT-4 mini. It's not that bad. So, if you are able to get this much reduction in cost for a slight drop. In accuracy, I think a lot of people would go for it, right? Because it makes so much commercial sense to do things like this. rather than going for high accuracy Now, why is GPT-40 Mini so cheap compared to the non-mini version? The bigger question here is how we can deploy LLMs. In a cost-effective manner while maintaining the performance of\n\nthe bigger model. So, this is what we will be answering in this talk. We are going to cover the different ways in which we can achieve this. So, there are two main classes of techniques that you could use. One is model compression. Which is like if you have a 65-billion-parameter model, You can compress it to, let us say, an 8 billion parameter model with very minimal loss. Like, not so much. But there is also another class of algorithms where you would do engineering. efficient engineering to sort of achieve the same thing that you achieve now.\n\nBut it sort of helps you reduce the inference costs. So, I can give you some examples of efficient engineering. For one of the most common operations that you do in transformer inference, It is the scale dot product. You have the key vector; you have the value vector. You will have to compute a dot product; then you will have to scale it up. Or scale it down, and then you will have to do a softmax. There are software kernels that have been developed. which fuses all these subtasks together into a single kernel.\n\nAnd that gives you tremendous improvements in efficiency. So, here it is not lossy; you do get the exact same operation. You do get the exact same output; it is just that you do some engineering efficiency. To reduce the costs. Now, there is also hardware that specifically supports certain operations out of the box. and people who do this efficient engineering when they fuse kernels, They also effectively make use of the information provided in the instruction manual. In the data sheet about how to leverage those operations.\n\nAnd then make it more efficient. So the efficient engineering part, Yatin will be covering it. Some parts of it Yatin will cover in the next lecture. For this lecture, we will focus mainly on model compression techniques. And specifically, we will be talking about quantization, pruning, and distillation. So let me give you a brief overview of what these are. Quantization, as the name suggests, is what we want to do. If we want to keep the exact same model, but just use a reduced number of bits to represent parameters, activations, and whatnot.\n\nAnd pruning is, as the name suggests, again. You just prune certain parts of the network. And then, while trying to maintain similar performance, of course, it's going to be lossy. And finally, it is distillation in which you have a big model that is trained well. And now you train a smaller model by using the bigger model. As a teacher. Do not use the real output; instead, use what the model predicts. to guide the smaller models. Now, out of these techniques distillation helps you is the least lossy.\n\nBut it's the most expensive because now you have to train the whole system. First of all, you have to make inferences over the teacher network. and then get the outputs that are expensive on a really large data set. And then you have to use those outputs and backdrop on the new model. which is much smaller than the original architecture to achieve model compression. So distillation is the costliest, but it gives you a lot. It is sort of the best way to maintain accuracy. whereas quantization and pruning are somewhat slightly lossy,\n\nbut they require very few changes. But quantization has other advantages as well, which I will be covering. When I talk about. So, let us first talk about quantization. So, before jumping into the quantization technique, Let us just do a recap of how LLMs do inference and talk about it. how floating point numbers are represented in computers And then finally, talk about quantization. So, this is a usual neural network where there is input. And then there are weights, which are here, and you also have activations here.\n\nSo LLMs have a lot of parameters that are in the weights. They are really expensive to store. And during inference, you end up creating activations. And these are the products of the input and the weights. They are also required to be stored. And the goal of quantization is how efficiently you can store these values. So that you end up using less memory, you compute faster and save costs. So, it is more about how you represent them. So, let us talk about how numbers are represented. So, typically, full precision is what is referred to as the FP32 notation.\n\nwhere you represent the floating point with a single sign bit for positive and negative And there is an exponent that is represented using 8 bits. And the fraction, or the mantissa, is represented using 23 bits. So, the difference between the two smallest numbers that you can represent is. The difference between the two smallest numbers is very low. And it is sort of captured in the 23 bits. So, the precision is really high. This is typically called full precision, like in the past a lot of. Machine learning modules used FP32 to represent data.\n\nSo, this provides enough precision for many scientific applications. And after a while, during the deep learning era, people moved to FP16. Because GPUs out of the box started supporting FP16. with improvements in time compared to FP32. And so here the difference is that, as you see, It is the number of bits that you use for the exponent. And the number of bits that you use for the fraction. So, the range has reduced because the exponent has decreased. And even the precision has reduced considerably.\n\nBut for machine learning, specifically deep learning applications, this still does the trick. You almost do not see any drops in performance. But your computations improve significantly if you move from FP32 to FP16. Now you can reduce it even further. So, there are certain applications where people simply use the int8 representation. where you use 1 bit to represent the sign and 7 bits to represent the rest. So, let us talk about how you would quantize a FP32 into int8. For FP16, it also sort of remains the same.\n\nSo, this is a very simple way of quantization, which is the simplest of all. So, what we do here is the first task. So let us say this is the FP32 vector that you have. So, the first task is to identify the value in this vector. that has the highest magnitude. So, for a given vector, you identify the maximum absolute number. which is 10.8 in this particular case. and then what you do is that you set the maximum. Set it symmetrical and at the minimum. The range is always centered at 0, and you set the maximum absolute value.\n\nand the minimum absolute value as a two extremes and then the rest is simple. You sort of map this maximum alpha to 127 and minimum alpha to -127. And how do we compute this number, the int8? So, basically, the first thing you do is compute a scale factor. So, here we are using 8 bits. So, this number would be 127, and alpha is the highest absolute value. So, this is going to be your scale factor. And to compute the, let us say, the quantized value for 5.47, All you have to do is multiply the value 5.47 by the scale factor.\n\nAnd then round it up. So, that is how you quantize a value. 32-bit floating point into an intake. And, of course, for different vectors, your alpha is going to change. And so the scale factor is going to change. So, if you have, let us say, a two-dimensional matrix that has 20,000 rows, Then you will have 20,000 different scale factors to sort of quantize. So, this is how quantization works. So this is how quantization works, and de-quantization is quite straightforward. All you have to do is check if you have the quantized vector.\n\nAnd you divide it by the scale factor. You end up getting the dequantized version, which is the FP32 version. So here you have the FP32 matrix. And then let us say you want to quantize it into int8 format. You will end up getting the alphas. The scaling factor for each vector is separated individually. And you will quantize them, and then when you de-quantize, of course, You are not going to get the original back. So, the difference between the original and de-quantized versions is this quantization error.\n\nSo, it is going to be lossy. And you will end up getting a quantization error. So, let us see how this quantization and dequantization are typically used in Machine learning inference. So, there are two types of ways in which you can use this quantization technique. in your LLM inference. One is during training; do not worry about quantization. You train the model in half precision, which is FP16 or mixed precision. FP32 and BF16. Whichever format that the model supports and you want to train on, do that.\n\nAnd during inference, you first perform post-training quantization. and figure out your scale factors, other things, and other constants And then use them for inference. So, that is one way you can do it. The second way is quantization-aware training. So, maybe, for example, we discussed LoRa in the last class. There is a variant of LoRa called quantized LoRa. So, that fine-tuning would be a quantized version of LoRa. And it is quantization-aware during training. So, you can leverage quantization even during your fine-tuning process.\n\nSo, now let us discuss post-training quantization. So, the good part is that you do not have to worry. About figuring out what your compressed version architecture should be. It is exactly the same. So, there is no difficult decision to make there. And the good part is you also do not need retraining. So, it is very cheap. But again, it is at the cost of how much quantization loss you incur. And how much lossy compression occurs. So, the easiest thing here is that the weights and biases are fixed after training.\n\nAnd when you look at the weight matrix, You look at it one vector at a time. So whatever quantization I discussed just before is called symmetric quantization. Because 0 is represented using 0 in the quantized format. and you have both positive and negative going through the same value. But there are other asymmetric formats where 0 is not mapped to 0. and the negative minimum and maximum values are not, The absolute values are not the same. So, there, in addition to scale factors, you will have other constants as well.\n\nSo since weights and biases don't change over time, It is easy to compute all these scale factors and other constants. And then keep them fixed. But this is not going to be the same for inputs and activations. You're going to receive inputs. Like during, you're going to get inputs during inference. Sometimes the input matrices might have a smaller maximum value. Sometimes it may have a really big maximum value. which will make it like a clip that will end up clipping If you choose the scale factors as in a not-so-\n\nGood way. So what people typically do is similar to what we do for validation. That you take some sort of a very small calibration data set, okay? pass them through your network. So if you just have the data set, you will only know the inputs. But if you pass them through the network, you will understand. how these activation values change with respect to the weights. So once you do it for a bunch of inputs called the calibration data set. You will be able to get some sense of. What is the range of values you would see?\n\nAnd what vectors would look like the maximum value you would hit? And what range would you get? So you use all this data and you again compute the scale factors. So now, for a given network, you would know all the scale factors. That is needed to quantize both weights and model parameters. and the inputs and activations right. So, what do we do then? So, typically what you do during inference is have multiple layers. And within layers, you will have multiple operations, one after the other. that leads you to complete that layer.\n\nSo, the typical way people use post-training quantization is to take one operation at a time. You have full precision or half precision, whichever precision the model is trained on as input. Quantize the weights and the activations, and then de-quantize them after that operation. And then you keep repeating this. So, why is this efficient? Because you are doing a matrix multiplication using FP16. There is only so much you can do. But if you convert this into a much smaller, quantized version, Then the number of operations you can do like blows up.\n\nSo, that blow-up is sort of not so high that even the Quantization and dequantization are sort of very small in that fraction. So, you end up getting a lot of speed. So, here if you see, you have the input and the weight matrix. And then first you quantize them because here you know your scaling factor. The scaling factor is 127 divided by alpha. And you have the input; you quantize them from FP16 to INT8. You do the same for your weights and then you do your matrix multiplication. In the quantized format, and then you end up de-quantizing them.\n\nSo why is this really helpful, like I said, like here? We saw that we are converting from FP16 to INT8, right? So this is a data sheet of the H100 UH GPUs. So here, if you see for FP16, you get. Let's say for NVL you get, or as XM you end up getting thousand nines. 79 teraflops, but upon intake, you end up getting a lot more tensor operations. So, for the same amount of energy, you end up doing a lot more operations. than what you would do on FP16. So, this is the advantage of doing this type of quantization.\n\nand de quantization within every step in your computations. So, people started using this, and this seems to work a lot better. But then, in 2022, when the size of the model started to increase, People found that after a certain value, the 8-bit baseline was not giving accurate results. as much accuracy as the 16-bit one, which is the green line. So, here, if you see the point at which this property emerges. It is around the 2.7 billion model. Until then, there was not much difference in performance here.\n\nBut then, after that, there was a steady decline. And it sort of does not make sense to quantize anymore. So then, these people figured out that the reason behind this emerging pattern They also proposed a way in which you can fix this. And their technique is called llm.initate. Now we will discuss a bit about what this does. So before going into what this does, the reason is The reason this dip is happening is because of outliers in your vectors. So we will see what they say about the percentage of outliers.\n\nIn your vectors, the sort of negligible effects are not noticeable until you reach a 2.7 billion parameter model. But once you cross that, you start from 6 billion and above. The number of outliers becomes very high. That you end up not getting the same performance as what you would expect. So, let us see the effects of outliers. So, let us say that you have your vector. Most of the numbers in your vector are sort of between, let us say, 10. minus 10 and 10 Or something like that. Most of them are in that range, and a lot of them are focused around 0.\n\nBut there is one value in the vector that is really high. So, what happens as a result of this? The alpha is decided by this particular group. absolute number and so you set both your extremes to represent this particular number. So, what happens is that every other number in the vector. sort of ends up getting mapped to the same point. Which means you end up losing everything just because of that one number. And if most of your vectors are going to be like this, Then quantization is not going to help.\n\nSo, this was the reason why you sort of saw a dip after the 2.7-billion model. Because most of them seem to have these outliers. So how do we fix it? Their proposal was quite simple. The main contribution of this paper is that it found the issue. So that was the biggest contribution. The approach is very simple and elegant. So this is what we used to do before LLM intake. If you have any matrices X and W, you would just quantize them. And then do the matrix multiplication and de-quantize them. So, what these guys said is that you have a certain threshold.\n\nAny value that crosses that threshold is sort of the outlier. So, do not use them to compute your scale factors or other constants. keep them away, just identify them as outliers and keep them out. Do all your quantization constants, and then during input, when you see these outliers, then just mask them out, take them out. So, if you see here, the yellow ones are the outliers. So, they have removed the outlier columns here. The outlier rows here have been kept separate. And now, for the non-outlier ones, you do quantization.\n\nAnd for the outlier ones, you do not perform quantization. We represent them as is and then whatever precision you used During your training and what the full model uses, Use the same method to do inference here as well, and then you add them up. So, the simplicity of this sort is really good. And it helps solve this problem, as we saw in the first graph. Once you do this small hack, everything gets fixed. So, this is INT8, LLM INT8 post-training quantization. So, the most popular technique for quantization-aware training is QLORA.\n\nbecause this is sort of one of the most popularly used PEF techniques mainly for two reasons. One is that its memory footprint is quite low, and it almost achieves similar performance as FP16. Now, on average, if you're going to fine-tune a 65 billion parameter model, Again, this is for the full fine-tuning that I mentioned. You would end up requiring 12 to 20 times the size of the parameters, right? So you will end up taking 780 gigabytes of memory. which seems almost impossible to get that much GPU memory, right?\n\nSo, the good part about Qlora is that it reduces the memory footprint by a big margin. And you can fine-tune a 65-billion-parameter model in just 48 gigabytes of memory. And the best part is that it almost doesn't degrade your performance. For a lot of tasks. So QLoRa is a combination of these four items. So here, LoRa is somewhat borrowed from the previous work. But what these guys suggested is that, on top of LoRa, We would use this specific format for the quantization of weights. which is called the 4-bit normal float.\n\nThey would use something called double quantization. I will go into the details. And they also make use of some of this hardware specification. to sort of further reduce the memory footprint. And this sort of is, they looked at the fact sheet of NVIDIA GPUs. And then they came up with these page optimizers. which would further reduce the amount of memory that you need during a PEFT. So, let us talk about what normal float 4 quantization is. Since it is 4, it is clear that it is a 4-bit quantization.\n\nAnd float again, it would represent floating numbers, and then. n here represents a normal distribution. So, typically, when you do the regular quantization, You would define the max and the min, and then you divide the buckets. into equally spaced ones. Because now you know the range, you can bucket them. And because of this, what will happen? The assumption here is that your array is uniformly distributed. So if that is the case, you'll end up getting sort of the same number of candidates.\n\nIn each of the buckets, you would do the computations. But if you make another assumption that most of your weights are are going to come from a normal distribution, Then this is not an effective way of leveraging quantization. So, for this, rather than using equally spaced, Now that you know the data distribution and it is not uniform, So you can get an equally sized distribution by assuming that. This is a Gaussian normal distribution. So, this is what NF4 quantization is in simple terms. And so now let us look at what double quantization is.\n\nSo, when you quantize, You typically store the quantization constants in FP32, full precision. Because the more precise your constants are, The lower your quantization error is going to be. So, what people typically do is represent these in FP32. But now imagine if you had so many vectors in your parameter space. Because there are going to be so many matrices, There are going to be so many feedforward networks. So many weight matrices, attention weights, and so on. And you will end up getting so many constants.\n\nSo, storing these constants will end up taking a lot of memory. And what they said is that once I have all these constants, I will divide those constants into blocks and further quantize them. These quantization constants. So, you sort of do double quantization. So, they say that by doing double quantization, You do not lose so much on your performance. But you gain a lot of storage space. in saving storage space. So that is the advantage of double quantization. It's not going to give you a performance boost.\n\nIt's just going to save a lot of space. And thirdly, like I said, page optimizers. So let's say that you want to fine-tune a 1.3 billion-parameter model. And it needs about 21 gigabytes of memory. So if you look at the GPU in isolation, you don't have 21 gigabytes of memory. But if you look at the memory that the chip has. Both GPU and CPU are good enough. So, how do you do paging for CPU and main memory? Some of this hardware does the same for GPU and CPU memory. And what Qlora does is make use of this particular feature.\n\nin the hardware. So you would be able to fine-tune a V1 model with this particular GPU. Even though it has only 16 gigabytes of GPU memory. So, this is one thing. And finally, of course, they are going to use LoRa as a basis for fine-tuning. So, here's a small recap of what we did in the last class. In LoRa, the weight matrix is basically divided into W and del W. And this part is del W, and this part is W. and any input is first multiplied here with this and then here with this And this sort of leads to plus del W a times, right?\n\nSo, this is what you end up getting, and this is factored in a way. where the rank here R is much less than t, right? So just the same sort of equation is rewritten slightly. Where you have the input, you have the weights. This is the scale factor again, and this is a, b, l1, l2. And the input is the same. So this is the usual LoRa equation for obtaining the output given the input. The PEFT weights are L1 and L2. So, now let us do a little bit of a recap of different styles. Different data formats again because we will use.\n\nThis helps in understanding the whole Q LoRa setup better. So far, we discussed FP32, FP16, and INT8. And in FP16, they reduce the exponents. and they also reduce the fractions in a sort of similar proportion. But what people at Google Brain did was that They came up with this new format called BF16. so that its brain floats because it is from Google Brain And what they did was say, \"Let us not change the exponent.\" Let us just reduce the precision. So, why is this helpful? If you have to convert an FP32 to BF16, it is very easy.\n\nAll you need to do is truncate it. You don't need to scale or do anything. So, this simplicity actually speeds up many things. This conversion is so convenient, and there are also certain operations. where this amount of precision does not lead to a bigger gain in performance. It increases the computations, but it does not lead to a bigger performance jump. So, there are certain training paradigms called mixed-precision training. where not all parameters are represented in the same representation.\n\nFor certain formats, you use certain representations; for certain operations, you use certain representations. For certain, you do not, and this paradigm, or fold paradigm, is called mixed precision training. So, here we also sort of use some mixed precision. Let us look at how it works. So, let us look at this format; this is rewritten. We just rewrote the first equation here by representing what goes on within Q LoRa. Most of your operations are going to be in BF16. So if you see, this part is in BF16.\n\nThis part is also going to be in BF16. L1 and L2 are a negligible number of parameters. So it's okay; let them be in BF16, and here the input is also in BF16. So far, everything here is in BF16, and here is the part. Where we are going to do double quantization and NF4 quantization. So, let us look at this in more detail. So, this double quantization can be first; let us look at only this bit. This is the first time we are de-quantizing the quantization factors. The scale factors and the quantization constants.\n\nSo, for that quantization, the second-level quantization constants are in FP32. The first level is now represented in 4-bit. But now, once you do the dequantization of this, You will end up getting the 32-bit precision dequantized version of the new scaling factors. And now again you de-quantize your weights, which are in the normal float. And then you get back the double quantization. The double quantized value, and finally, you would use that. To get the weight matrix in BF16. Which again goes back here; everything is in BF 16.\n\nAnd you get Y in BF16. So, this is the whole crux of QLoRa. and this gives an incredible amount of memory efficiency. Again, here I haven't actually talked about the paging. But what typically happens is that you won't be able to save, let's say, If a certain 65 billion model uses 48 gigabytes of memory. And you only have 32 GB of memory on your GPU. Then there will be a time when you will hit. And certain matrices will not be available. and all the hardware would do is that go replace something that hasn't been used for a long time,\n\nGet the right matrix that needs to be. Used in the memory, and you will not run out of memory. you will keep running. So, that is not covered in these equations. But that is also a very essential part of why QLoRa should work. So, all three of these graphs are 4-bit quantizations. The only difference here is that this is simple FP4, something like FP4. The orange line is the normal float. So, there is a good improvement in accuracy from FP4 to Normal 4. But like I said earlier, double quantization does not give you as much improvement in accuracy.\n\nBut it gives you a lot of space. So, you reduce storage. And so, if you use only 4-bit quantization, like, say, float 4, Then there is always a drop in comparison to doing the usual one. which is using BF16. So, 38 becomes 37; there is a drop here. But when you use normal float 4 plus, Again, DQ is not going to help with accuracy. But again, it will help in storage, but by adding both, you end up getting almost. Actually, it's slightly meaner than even the VF 16. But that's only a little bit; for all practical reasons, you can assume.\n\nthat it's able to maintain the half precision performance. So in pruning, as the name suggests, it is very simple. All you need to do is remove certain things from the network. So here are the weights. So unstructured pruning is like randomly keeping removing weights from the network. With the hope that you don't affect performance. And structure pruning is more like what you take into account. The exact structure of the network that you are going to prune. and then prune certain structural components from the network.\n\nSo, I will cover both of them. So, the simplest form of unstructured pruning is called magnitude pruning. Just sort the weights; whichever weights are low, just ditch them. It is as simple as that. So, what they found is that if you remove 40 percent of the weight, You do not actually see that bigger change in performance. But if you start removing a lot more, then you see degradation. But what they say is that after you prune, if you fine-tune, continue pre-training. Or continue fine-tuning; then you would end up bridging that gap.\n\nUntil 80% of the cases. And some people figured out that everybody only does weight pruning. You only take into account the weight. Of the parameters, only the parameters are important, but activations are also important. So, if you take into account activations, then the product of weights is and activations will show you a different story. So, rather than pruning at the weight level, prune at the activations. So, this is one idea that was very recently proposed. And so the issue with unstructured pruning is that, let's say you prune,\n\nYou make a lot of values in your matrix. But still, if hardware is not going to utilize that information, It is as much about using that same FP16 to store 0 as well. It is not going to give you a speed up in numbers. It is not going to save compute; it is not going to save energy. Nothing is saved. So there is no point in actually doing unstructured pruning. If your hardware does not support it, then it is a very important part. So, people started to move slightly away from unstructured pruning.\n\nAnd start to focus on structured pruning. For example, there are these A100 NVIDIA GPUs which say that If you take up any block of four, you compress it. into blocks of 2 by ignoring 2. This is called a 2-to-4 sparsity pattern. If you do that and represent your values with half the number of weights. and you use 2-bit indices to store the indices that are non-zero, It will give you a performance speedup. So, now your tuning algorithm that you are going to design is ready. Should be aware of this particular hardware choice as a sort of knowledge test.\n\nSo, if you see here, if you do it for any different type of precision, The dense operation, and there is a big difference between. How much you can do with the dense operation and the sparse operations. So, because the hardware supports this, Now you can think of pruning your weights in this particular format without loss. While minimizing the loss of performance. Another recent paper that came out was that. Let's say that you have a fine-tuned BERT; there are a lot of multi-head attentions.\n\nFeed-forward networks and within multi-head attention, you have multiple heads. Within a feed-forward network, you would have certain hidden dimensions and so on. So what they say is that you can prune almost any component, right? You can remove a layer; for example, they remove this feedforward layer. If they remove this multi-head attention, you can remove certain heads. Within the multi-head, you can remove certain hidden dimensions here and there. And they say that you should take into account the structure of the network.\n\nAnd remove structural components; you end up getting a performance speed-up. So, this is about pruning. So I am just covering the overview to give a brief picture of what pruning is. I am not going into details or other techniques that do pruning more efficiently. Finally, I will talk about distillation. So, distillation again is a process of training a much smaller model. to imitate a bigger model. The bigger model is the teacher, and then the smaller model is the student. So the smaller model should end up mimicking what the larger model does.\n\nSo it sort of works like this: One of the biggest issues with distillation is that you need this data. So, think about post-training quantization. If you have the LAMA 70 billion model and you want to quantize it, You don't need the data that LAMA was trained on. You don't have to find a proxy for the data for which Lama was trained. You can just do your quantization. But here, you need the data. If you don't have the real data, you should sort it out. end up getting some representative data.\n\nThat sort of looks like what this model would have trained on. And that is a big bottleneck in this era. Because people are willing to share and open-source their models. But nobody is willing to open-source the data because that's the goal. So, nobody would do that. So this distillation is typically difficult from that standpoint. And so, people do task-specific distillation. You have task-specific data; let me distill the model conditioned on the task. So, in general, distillation is still a useful technique to learn.\n\nBut thinking that I can fine-tune. Or I can distill any big model into a smaller model that is not going to be. that effective unless you have the right data. So, the way it works is that you only feed the input. to the train fully trained bigger model, you get its predictions. So, let us call it \u0177. And then you give the same data to the student network. Let's call it y dash, right? And now you want y dash to be as close as y hat. Both are not good. But you just want y dash to be as close to y hat as possible.\n\nThat's all the objective function is. So, there are different flavors of distillation. Some of them are specifically about how you compute this loss. There are different ways to compute this loss. And some are like you don't just do distribution matching. Only on the output through distribution matching, even in the intermediate layers. Based on certain structures. There are many variants of distillation. So, let's take an example here. So here are five outputs. Let's assume that they're all going to give us probabilities.\n\nAnd so let's say that if the goal label is something like this. Then your typical loss would have been that you increased. The log likelihood of that particular value, which is one, is correct. That's going to be your usual loss. So now, for the knowledge distillation, your theta is here. That is your theta; these network parameters are theta t. So now, in addition to theta, you also have theta d, but theta d is frozen. So what you do is end up getting the value. Of every output with Q, and you serve that as a reference distribution.\n\nAnd compute the cross interval. So, there are several advantages to doing Q rather than Again, taking only the max value and doing it like that. You can find more details in this first paper that came out for distillation. Now, like I mentioned before, the good part of distillation is that Your student network can be much smaller. You can have a very small number of layers. so that you can reduce the latency by quite a bit. Whatever your latency is, you can define your network structure to satisfy that latency.\n\nYou have that flexibility, and then you can try to fine-tune. And then figure out what the performance gap is that you get. Now, if you have to gain a lot of speed, This is the way to go, right? Pruning and quantization are not going to get you there. Now again, like I said, the con is that you need the training data. And it's extremely expensive to perform this type of model compression. Mainly because you have to do the first inference with a lot of data. You have to do inference; inference is a lot more expensive.\n\nBecause it has to do with one. Generate one token at a time; it's extremely expensive. And then again, after you complete that expensive task of generating outputs of every data point in your input, You will again have to train the student model from scratch. There are techniques that say things like If you initialize your student network this way by pruning the teacher, And then using a very aggressively pruned version of the teacher as a student, Your starting point is much better; you can reduce the time.\n\nBut still, you will have to end up fine-tuning. Now, like I said, there are different ways in which Hinton proposed them, Hinton et al. proposed distillation. One is called the hard target, where, let us say, this was the value. That got the highest probability. You can forget about the likelihood; just take the argmax and then set it to 1. and use it, or the other way is to end up using the entire software distribution. This is specifically found to be a lot better than simply using hard targets.\n\nBut sometimes when you don't have access to the probabilities. For example, if you are using an open API and they are not going to give you, They do give you some cases, but maybe you are doing some complex things. and you are not able to access those probabilities, You can still do some sort of hard target distillation. Although it is illegal, it still happens. So, what I talked about before is more like a single-step quantization, sorry, distillation. Now, what if you want to distill an entire sequence?\n\nwhich is done in an autoregressive manner or something. So, this is our usual knowledge distillation equation. where Qs are the teacher likelihoods, and then you want to match them. Students' ones are exactly the same as the teachers'. Now in word-level distillation, what we say is that We add this particular summation over all the J words. And it's simply the same as before. You do it one word at a time by fixing the other ones. And in the case of sequence-level distribution, we ideally would want to do it.\n\nby taking the entire space of all possible sequences. But that's going to be like all possible sequences being insanely expensive. The space is so high that it's not even possible to do this. So, what they typically do is very badly approximate this into Just what the model gives you is the highest sequence for a beam search. or something like that. And then set that as \u0177 and simply increase the log likelihood of that. the highest sequence that you get. So these are some simple ways of knowledge distillation.\n\nThere are also other techniques where the loss function is not cross-entropy. They use Kullback-Leibler divergence. They use the inverse KL divergence. So there are many different flavors of loss functions. And a lot of different flavors of how you approximate the knowledge. So one way of approximating the knowledge from the teacher to the student. Is to mimic the whole distribution, right? But you're only mimicking the distribution in the output space, But there are multiple other places where you can sort of.\n\nFix your network parameters in such a way. that you expect the middle layer in this to be replicated by this layer here. Something of that type, and you can do a lot more. And then try to mimic the teacher as much as possible. So, I just want to make a small connection between distillation and sort of. network-related literature and a certain other distillation that people are now doing with large language models. Which is that, let us say, if you take the LAMA 405 billion model. So, do you guys know why someone would use a 405 billion model?\n\nIt is going to take a few minutes to come back with a response. But why would anybody want to use a 405-billion model? It has much better output quality than any of the models. but then its latency and cost are so high. Whatever task you use it for, you're not going to get the money back. That you put in. But why would people still use it? It will be used as a proxy for human beings. So that is sort of heading toward the right answer. So basically what they say is that an extremely over-parameterized model.\n\nIt can capture all the nuances in the training data, and it can model it better. So once it models better, you can always distill it. So, the whole point of why you want such a big model is that. It will generate output that is so good. That you can use that output to distill a smaller model. Which would be much better than just training that smaller model from scratch. So, one such way is the first paper that came out in this paradigm. is called self-instruct. What they say is that, let us say, seed tasks are something like this.\n\nFind out if the given text is in favor of or against. This is one task, and the other task is to give me a quote. From a famous person on this topic, and for each task, They give an instruction along with an example. And they only take 175 seed tasks, not more. These are handcrafted with one instruction and one instance; that is about it. Now what they say is that they ask the LLM to generate new instruction. Like you have 175, you do a few short examples. You give a few short examples and then you say, \"Generate one more.\"\n\nIt's going to give you one task that is different from the 175 tasks. And once you have one task, you again check whether it's good or not. If it is good, then you ask it to generate the class label. and the input given the instruction. This is output first, input next. And in another case, you input first and output second. So there are some advantages to doing these. And what they say is that by doing this, you end up generating a lot of data. So, this is a few-shot examples. So, a base LLM can do this.\n\nA base LLM that is not instruction fine-tuned can still do this work. Because they cannot follow zero-shot instructions, but they can do few-shot very well. So, just with a one-sided seed example, you end up getting a lot of information. from the model itself on how to fine-tune it. So, what you end up getting is an instruct fine-tuned data set. Now, you can use this instruct fine-tuned dataset to actually. Instruct to fine-tune the model itself. So, here distillation comes from gathering as much knowledge from the model as possible.\n\nIn another way, how did you gather knowledge? You gave the input, and you saw how the model reacted to it. So now these are generative models. If they are discriminative models, all you can do is see how it reacts. You cannot ask it to generate new things. But if it's a generative model, then you can ask it to actually. Generate new stuff. And this is a new paradigm, and this is sort of a widely used paradigm. Now for any industrial fine-tuning applications. For example, if you were to create, let's say,\n\na new natural language interface for one of your software applications or something like that. So you have your own action space regarding what all it can do. Maybe some descriptions of what they achieved. You can actually synthesize a lot of examples of how a person. I would ask in natural language to do this execution. And if you have an example for a few, you can end up. Generating a lot of data from a bigger model. That can do this, and then you can use that to fine-tune a smaller model. and now you have a very easy to use software right.\n\nSo there are a lot of good applications for synthetic data generation. And then using it to fine-tune a smaller model is sort of The big thing is right now. So, distillation is sort of indirectly what led to this whole space being developed. And this distillation is possible only if you have an awesome teacher, right? And that's why people want to build really, really big models. Because you end up getting really good synthetic data, you also end up saving costs. So, to summarize, we looked at effective ways to reduce inference costs.\n\nAnd specifically, we looked at model compression techniques. In them, we discussed in detail quantization. We discussed the LLM intake for post-training quantization. We discussed QLORA for the quantization of training. We discussed some pruning techniques and some distillation techniques. and where the field is moving more rapidly. Alright, thank you." + } +] \ No newline at end of file diff --git a/backend/nlp_api.py b/backend/nlp_api.py new file mode 100644 index 0000000000000000000000000000000000000000..26ee7eb426727e618c1354181a3b91ac610785af --- /dev/null +++ b/backend/nlp_api.py @@ -0,0 +1,1178 @@ +""" +NLP Learning Grid API +Provides endpoints for the frontend grid-based NLP learning system. +""" + +import os +import json +import pandas as pd +from flask import jsonify, request +import numpy as np +from datetime import datetime +import nltk + +# Ensure requisite NLTK data is available +try: + nltk.download('stopwords', quiet=True) + nltk.download('wordnet', quiet=True) + from nltk.corpus import stopwords + STOPWORDS = set(stopwords.words('english')) +except Exception as e: + print(f"Warning: Could not load NLTK stopwords: {e}") + STOPWORDS = set() + +# Import backend modules (support both script and package execution) +try: + from .init import app + from .database import get_session, update_session, save_summary, save_polyline, get_polylines as get_db_polylines, get_notes, add_note, get_lectures, reset_db + from .request_logger import log_request + from .utils import utils_preprocess_text, get_cos_sim + from . import navigator +except ImportError: + from init import app + from database import get_session, update_session, save_summary, save_polyline, get_polylines as get_db_polylines, get_notes, add_note, get_lectures, reset_db + from request_logger import log_request + from utils import utils_preprocess_text, get_cos_sim + import navigator + +# Define stopwords +stop_words = set(stopwords.words('english')) + +# Polyline logging +POLYLINE_LOG_FILE = os.path.join(os.path.dirname(__file__), 'polyline_generation.log') + +def log_polyline_step(step, details): + """Log detailed steps of polyline generation""" + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + with open(POLYLINE_LOG_FILE, 'a', encoding='utf-8') as f: + f.write(f"[{timestamp}] [{step}]\n{details}\n{'-'*50}\n") + +_bert_model = None + +def get_bert_model(): + global _bert_model + if _bert_model is None: + try: + from sentence_transformers import SentenceTransformer + print("Loading BERT model (lazy)...") + _bert_model = SentenceTransformer('all-MiniLM-L6-v2') + print("BERT model loaded successfully") + except Exception as e: + print(f"Error loading BERT model: {e}") + _bert_model = None + return _bert_model + +# Load NLP data from JSON (Excel was rejected by HF) +nlp_json_path = os.path.join(os.path.dirname(__file__), 'nlp', 'nlp_resources.json') + +def load_nlp_resources(): + """Load NLP resources from JSON file""" + try: + # Check if the JSON file exists + if not os.path.exists(nlp_json_path): + print(f"File not found: {nlp_json_path}") + return [] + + with open(nlp_json_path, 'r', encoding='utf-8') as f: + data = json.load(f) + + if not isinstance(data, list): + print(f"Unexpected data format in {nlp_json_path}") + return [] + + # First-quadrant arc projection (bottom-left origin) + # Resources fan from 5ยฐ to 85ยฐ like Q1 of a polar chart + # Origin: bottom-LEFT of 20ร—20 grid + cx, cy = 0.0, 19.5 + + # Sequential split: 7 / 5 / 4 / 2 = 18 + ordered_data = data[:18] if len(data) >= 18 else data + + tier_configs = [ + {'label': 'Fundamentals', 'count': 4, 'radius': 3, 'difficulty': 2}, + {'label': 'Intermediate', 'count': 5, 'radius': 7, 'difficulty': 4}, + {'label': 'Advance', 'count': 5, 'radius': 11, 'difficulty': 6}, + {'label': 'Mastery', 'count': 4, 'radius': 15, 'difficulty': 8}, + ] + + used_positions = set() + resources = [] + resource_idx = 0 + + # Fan: 8ยฐ to 82ยฐ (keeps resources comfortably inside axes) + angle_start_deg = 8.0 + angle_end_deg = 82.0 + + for t_idx, tier in enumerate(tier_configs): + count = tier['count'] + r_val = tier['radius'] + tier_data = ordered_data[resource_idx : resource_idx + count] + resource_idx += count + + n = len(tier_data) + if n == 0: + continue + + angle_step = (angle_end_deg - angle_start_deg) / (n - 1) if n > 1 else 0.0 + + for i, row in enumerate(tier_data): + angle_deg = angle_start_deg + i * angle_step + angle_rad = np.radians(angle_deg) + + x_raw = cx + r_val * np.cos(angle_rad) + y_raw = cy - r_val * np.sin(angle_rad) + + x = int(round(np.clip(x_raw, 0, 18))) + y = int(round(np.clip(y_raw, 0, 18))) + + # Resolve collisions by nudging along the arc (y direction) + attempts = 0 + while (x, y) in used_positions and attempts < 20: + y = max(0, min(18, y - 1 if attempts % 2 == 0 else y + 1)) + attempts += 1 + + used_positions.add((x, y)) + + # Tier-based points: Fundamentals=50, Intermediate=100, Advance=150, Mastery=200 + tier_points = {2: 50, 4: 100, 6: 150, 8: 200} + base_pts = tier_points.get(tier['difficulty'], 50) + + # Per-resource high_line: seeded random 0.70-0.85 + import random as _rnd + _rnd.seed(len(resources) + 42) # deterministic per resource index + high_line = round(_rnd.uniform(0.70, 0.85), 2) + + resources.append({ + 'id': str(len(resources) + 1), + 'position': {'x': int(x), 'y': int(y)}, + 'type': 'video' if 'youtube' in str(row.get('links', '')).lower() else 'book', + 'title': str(row.get('name', f'Resource {len(resources) + 1}')).strip(), + 'visited': False, + 'difficulty': tier['difficulty'], + 'reward': base_pts, + 'base_points': base_pts, + 'high_line': high_line, + 'url': str(row.get('links', '')).strip(), + 'description':str(row.get('description', '')).strip(), + 'module': str(row.get('module', 'NLP Concept')).strip() + }) + + print(f"Successfully projected {len(resources)} resources into 4-tier Radar arcs") + return resources + except Exception as e: + print(f"Error loading NLP resources: {e}") + return [] + +# Cache resources +nlp_resources = load_nlp_resources() + +# Load YouTube links mapping +_youtube_links_path = os.path.join(os.path.dirname(__file__), 'data', 'youtube_links.json') +try: + if os.path.exists(_youtube_links_path): + with open(_youtube_links_path, 'r', encoding='utf-8') as f: + raw_links = json.load(f) + + # Create a normalized mapping for easier lookup + _youtube_links = {str(k).strip().lower(): v for k, v in raw_links.items()} + print(f"Loaded {len(_youtube_links)} YouTube links from mapping file") + + # Inject youtube_url into each resource + for r in nlp_resources: + module_lower = r['module'].lower() + title_lower = r['title'].lower() + + # 1. Exact module match + url = _youtube_links.get(module_lower, '') + + # 2. Fuzzy match on title or module + if not url: + for key, val in _youtube_links.items(): + if key in title_lower or key in module_lower or title_lower in key or module_lower in key: + url = val + break + + r['youtube_url'] = url + + yt_count = sum(1 for r in nlp_resources if r.get('youtube_url')) + print(f"Matched YouTube URLs for {yt_count}/{len(nlp_resources)} resources") + else: + print(f"YouTube links file not found: {_youtube_links_path}") + for r in nlp_resources: r['youtube_url'] = '' +except Exception as e: + print(f"Could not load YouTube links: {e}") + for r in nlp_resources: r['youtube_url'] = '' + +# Pre-compute module embeddings +module_embeddings = {} + +def compute_module_embeddings(): + bert_model = get_bert_model() + if not bert_model: + return + + print("Computing module embeddings...") + # Group resources by module to form a "document" for each module + module_docs = {} + for r in nlp_resources: + m = r['module'] + # Combine title and description for a rich representation + text = f"{r['title']} {r.get('description', '')}" + if m in module_docs: + module_docs[m] += " " + text + else: + module_docs[m] = text + + # Compute embeddings + for m, doc in module_docs.items(): + # Apply preprocessing + clean_doc = utils_preprocess_text(doc, flg_stemm=False, flg_lemm=True, lst_stopwords=stop_words) + module_embeddings[m] = bert_model.encode(clean_doc) + print(f"Computed embeddings for {len(module_embeddings)} modules") + +# Compute embeddings on startup (REMOVED: Too slow on startup, will compute lazily or skip if needed) +# compute_module_embeddings() + +# ============================================= +# RESOURCES ENDPOINTS +# ============================================= + +@app.before_request +def before_request_logging(): + if request.path.startswith('/api'): + log_request() + +@app.route('/api/reset', methods=['POST']) +def reset_database(): + """Wipes the database memory completely""" + try: + reset_db() + return jsonify({'status': 'success', 'message': 'Database memory wiped completely'}) + except Exception as e: + return jsonify({'status': 'error', 'message': str(e)}), 500 + +@app.route('/api/resources', methods=['GET']) +def get_resources(): + """Get all NLP learning resources with their grid positions and correct visited state""" + session_id = request.args.get('session_id', 'default') + from database import get_session + session = get_session(session_id) + visited_ids = set(str(v).strip() for v in session.get('visitedResources', [])) + + # Return a copy of resources with updated visited flags + updated_resources = [] + for r in nlp_resources: + r_copy = r.copy() + r_copy['visited'] = str(r['id']).strip() in visited_ids + updated_resources.append(r_copy) + + return jsonify(updated_resources) + + +@app.route('/api/resources/', methods=['GET']) +def get_resource(resource_id): + """Get a single resource by ID""" + resource = next((r for r in nlp_resources if r['id'] == resource_id), None) + if not resource: + return jsonify({'error': 'Resource not found'}), 404 + return jsonify(resource) + + +# ============================================= +# AGENT STATE ENDPOINTS +# ============================================= + +@app.route('/api/agent', methods=['GET']) +def get_agent_state(): + """Get current agent state (position, level, reward)""" + session_id = request.args.get('session_id', 'default') + return jsonify(get_session(session_id)) + + +@app.route('/api/agent/move', methods=['POST']) +def move_agent(): + """Move agent to a new position""" + data = request.get_json() + session_id = data.get('session_id', 'default') + position = data.get('position', {}) + + session = get_session(session_id) + session['position'] = position + update_session(session_id, session) + + return jsonify(session) + + +# ============================================= +# NOTIFICATION ENDPOINTS +# ============================================= + +@app.route('/api/notifications', methods=['GET']) +def get_notifications(): + """Get all notifications for a session""" + session_id = request.args.get('session_id', 'default') + from database import get_session + session = get_session(session_id) + return jsonify(session.get('notifications', [])) + + +@app.route('/api/notifications/add', methods=['POST']) +def add_notification(): + """Add a new notification to the database""" + data = request.get_json() + session_id = data.get('session_id', 'default') + message = data.get('message') + notif_type = data.get('type', 'info') + + if not message: + return jsonify({'error': 'Message required'}), 400 + + from database import get_session, update_session + session = get_session(session_id) + if 'notifications' not in session: + session['notifications'] = [] + + new_notif = { + 'id': f"notif_{int(datetime.now().timestamp())}", + 'type': notif_type, + 'message': message, + 'timestamp': int(datetime.now().timestamp() * 1000), + 'read': False + } + + session['notifications'].insert(0, new_notif) + update_session(session_id, session) + return jsonify(new_notif) + + +@app.route('/api/notifications/read', methods=['POST']) +def mark_notifications_read(): + """Mark all notifications as read in the database""" + data = request.get_json() + session_id = data.get('session_id', 'default') + + from database import get_session, update_session + session = get_session(session_id) + if 'notifications' in session: + for n in session['notifications']: + n['read'] = True + update_session(session_id, session) + + return jsonify({'status': 'success'}) + + +def sync_agent_progression(session): + """Utility to ensure level and totalReward are consistent""" + # Level = totalReward // 100 (Stage 1 starts at 0 pts, Stage 2 at 100 pts, etc.) + # Floor level is 1 + session['level'] = max(1, (session.get('totalReward', 0) // 100) + 1) + return session + +# ============================================= +# RESOURCE INTERACTION ENDPOINTS +# ============================================= + +@app.route('/api/resource/visit', methods=['POST']) +def visit_resource(): + """Mark a resource as visited and update agent""" + data = request.get_json() + session_id = data.get('session_id', 'default') + resource_id = data.get('resource_id') + + session = get_session(session_id) + + # Find resource + resource = next((r for r in nlp_resources if r['id'] == resource_id), None) + if not resource: + return jsonify({'error': 'Resource not found'}), 404 + + # Update session + if resource_id not in session['visitedResources']: + session['visitedResources'].append(resource_id) + # Add reward + session['totalReward'] = session.get('totalReward', 0) + resource.get('reward', 0) + + # Sync progression + session = sync_agent_progression(session) + + update_session(session_id, session) + return jsonify(session) + + +# ============================================= +# LEARNING SUMMARY ENDPOINTS +# ============================================= + +@app.route('/api/summary/create', methods=['POST']) +def create_learning_summary(): + """ + Create a learning summary from visited resources + """ + data = request.get_json() + session_id = data.get('session_id', 'default') + session = get_session(session_id) + title = data.get('title', '') + summary = data.get('summary', '') + visited_ids = data.get('visited_resources', []) + + if not title or not summary: + return jsonify({'error': 'Title and summary required'}), 400 + + # Get visited resources using robust ID matching + visited_set = set(str(v).strip() for v in visited_ids) + visited_resources = [r for r in nlp_resources if str(r['id']).strip() in visited_set] + + print(f"[DEBUG] create_learning_summary: incoming visited_ids={visited_ids}, matched count={len(visited_resources)}") + + # Calculate learning metrics + total_difficulty = sum(r['difficulty'] for r in visited_resources) + total_reward = sum(r['reward'] for r in visited_resources) + avg_difficulty = total_difficulty / len(visited_resources) if visited_resources else 0 + + # Extract unique modules from resources (preserving order) + seen_modules = set() + ordered_modules = [] + for r in nlp_resources: + m = r['module'] + if m not in seen_modules: + ordered_modules.append(m) + seen_modules.add(m) + + # Module Aliases for better keyword matching + module_aliases = { + 'Pre training objectives': ['pre-training', 'pre training', 'objectives'], + 'Pre trained models': ['pre-trained', 'pre trained'], + 'Tutorial: Introduction to huggingface': ['huggingface', 'hugging face'], + 'Fine tuning LLM': ['fine-tuning', 'fine tuning', 'ft'], + 'Instruction tuning': ['instruction tuning', 'instruction-tuning'], + 'Prompt based learning': ['prompt based', 'prompt-based'], + 'Parameter efficient fine tuning': ['peft', 'parameter efficient'], + 'Incontext Learning': ['in-context', 'incontext', 'icl'], + 'Prompting methods': ['prompting'], + 'Retrieval Methods': ['retrieval'], + 'Retrieval Augmented Generation': ['rag', 'retrieval augmented'], + 'Quantization': ['quantization', 'quantized'], + 'Mixture of Experts Model': ['moe', 'mixture of experts'], + 'Agentic AI': ['agentic', 'agents'], + 'Multimodal LLMs': ['multimodal', 'multi-modal'], + 'Vision Language Models': ['vlm', 'vision-language', 'vision language'], + 'Policy learning using DQN': ['dqn', 'deep q', 'policy gradient'], + 'RLHF': ['rlhf', 'reinforcement learning from human feedback'] + } + + # Check for keywords in summary text + summary_lower = summary.lower() + keywords_found = [] + + for module in ordered_modules: + if module.lower() in summary_lower: + keywords_found.append(module) + continue + aliases = module_aliases.get(module, []) + for alias in aliases: + if alias.lower() in summary_lower: + keywords_found.append(module) + break + for r in visited_resources: + if r['title'].lower() in summary_lower and r['title'] not in keywords_found: + keywords_found.append(r['title']) + + # Calculate module scores for polyline + module_scores = [] + log_polyline_step("START_GENERATION", f"Generating polyline for summary: '{summary[:100]}...'") + + bert_model = get_bert_model() + if bert_model: + if not module_embeddings: + compute_module_embeddings() + + try: + clean_summary = utils_preprocess_text(summary, flg_stemm=False, flg_lemm=True, lst_stopwords=stop_words) + summary_embedding = bert_model.encode(clean_summary) + for module in ordered_modules: + score = 0.0 + if module in module_embeddings: + sim = get_cos_sim(summary_embedding, module_embeddings[module]) + score = max(0.0, sim) + if module in keywords_found: score += 0.3 + module_visited_count = sum(1 for r in visited_resources if r['module'] == module) + if module_visited_count > 0: score += 0.1 * module_visited_count + module_scores.append(float(max(0.0, min(1.0, score)))) + except Exception as e: + print(f"Error computing BERT scores: {e}") + for module in ordered_modules: + score = 0.5 + (np.random.random() - 0.5) * 0.1 + if module in keywords_found: score += 0.2 + module_visited_count = sum(1 for r in visited_resources if r['module'] == module) + if module_visited_count > 0: score += 0.1 * module_visited_count + module_scores.append(float(max(0.0, min(1.0, score)))) + else: + for module in ordered_modules: + score = 0.5 + (np.random.random() - 0.5) * 0.1 + if module in keywords_found: score += 0.2 + module_visited_count = sum(1 for r in visited_resources if r['module'] == module) + if module_visited_count > 0: score += 0.1 * module_visited_count + module_scores.append(float(max(0.0, min(1.0, score)))) + + # โ”€โ”€ DQN Recommendation โ”€โ”€ + rec_result = navigator.recommend_next(visited_ids, module_scores, nlp_resources) + next_recommendation_obj = rec_result.get('resource') + + recommendations = [] + if next_recommendation_obj: + recommendations.append(next_recommendation_obj['title']) + + unvisited_remaining = [r for r in nlp_resources if r['id'] not in visited_ids and r['title'] not in recommendations] + unvisited_remaining.sort(key=lambda r: (-r.get('reward', 0), r.get('difficulty', 0))) + for r in unvisited_remaining: + if len(recommendations) < 3: recommendations.append(r['title']) + else: break + + strengths = keywords_found if keywords_found else [r['title'] for r in visited_resources if r.get('difficulty', 0) <= 2] + + # Analysis results + polylines = get_db_polylines() + from collections import Counter + all_keywords = [] + for p in polylines.values(): + if 'keywords_found' in p: all_keywords.extend(p['keywords_found']) + all_keywords.extend(keywords_found) + keyword_counts = Counter(all_keywords) + most_common_keywords = [k for k, v in keyword_counts.most_common(3)] + dominant_topics = most_common_keywords + + # Define scored_modules for recommendation logic + scored_modules = list(zip(ordered_modules, module_scores)) + + # Calculate XP based on high lines + current_polyline_sum = sum(module_scores) + total_earned_base_pts = 0 + high_line_sum = 0 + for module, score in scored_modules: + resource = next((r for r in nlp_resources if r['module'] == module), None) + if resource: + hl = float(resource.get('high_line', 0.8)) + high_line_sum += hl + if score >= hl: + total_earned_base_pts += resource.get('base_points', 50) + + high_line_sum = max(0.1, high_line_sum) + xp_earned = int(total_earned_base_pts * (current_polyline_sum / high_line_sum)) + + # Update session with new XP + session['totalReward'] = session.get('totalReward', 0) + xp_earned + session = sync_agent_progression(session) + update_session(session_id, session) + + # Generate generic AI analysis + ai_analysis = f"Learning profile enriched by modules like {', '.join(keywords_found[:3]) if keywords_found else 'Basics'}. Stage {session['level']} achieved with {session['totalReward']} points." + + # Recommendations: Unvisited modules with high rewards or logical next steps + visited_module_names = set(r['module'] for r in visited_resources) + all_module_names = set(r['module'] for r in nlp_resources) + unvisited_modules = list(all_module_names - visited_module_names) + + # Sort unvisited modules by order in ORDERED_MODULES + unvisited_modules.sort(key=lambda m: ordered_modules.index(m) if m in ordered_modules else 99) + + # Combine BERT scores with sequential progression for recommendations + recommendations = unvisited_modules[:3] if unvisited_modules else [m for m, s in scored_modules if s <= 0.3][:3] + + timestamp_id = datetime.now().strftime("%Y%m%d_%H%M%S") + summary_result = { + 'id': f"summary_{session_id}_{timestamp_id}", + 'title': title, 'summary': summary, 'keywords_found': keywords_found, + 'totalResources': len(nlp_resources), 'visitedResources': len(visited_resources), + 'currentLevel': session['level'], + 'strengths': strengths, 'recommendations': recommendations, + 'ai_analysis': ai_analysis, + 'avgDifficulty': round(avg_difficulty, 2), 'totalReward': session['totalReward'], + 'xp_earned': xp_earned + } + save_summary(summary_result) + + # Final result construction + agent_pos = session.get('position', {'x': 10, 'y': 10}) + assimilation_position = {'x': agent_pos.get('x', 10), 'y': agent_pos.get('y', 10)} + + polyline_id = f"polyline_{timestamp_id}" + new_polyline = { + 'id': polyline_id, 'name': title, 'path': [r['position'] for r in visited_resources], + 'color': f'rgba({np.random.randint(100,200)}, {np.random.randint(100,200)}, 255, 0.4)', + 'isActive': True, 'summary': summary, 'keywords_found': keywords_found, + 'module_scores': module_scores, 'strengths': strengths, 'dominant_topics': dominant_topics, + 'ai_analysis': ai_analysis, 'assimilation_position': assimilation_position, + 'next_recommendation': { + 'id': next_recommendation_obj['id'], 'title': next_recommendation_obj['title'], + 'position': next_recommendation_obj['position'], 'module': rec_result['module'], 'reason': rec_result['reason'] + } if next_recommendation_obj else None + } + save_polyline(polyline_id, new_polyline) + + return jsonify({ + 'summary': summary_result, + 'polyline': new_polyline, + 'assimilation_position': assimilation_position, + 'next_recommendation': new_polyline['next_recommendation'] + }) + + +# ============================================= +# POLYLINE ENDPOINTS +# ============================================= + +@app.route('/api/polylines', methods=['GET']) +@app.route('/api/polylines', methods=['GET']) +def get_polylines_route(): + """Get all polylines including dynamically generated High Line and Current Average polylines""" + polylines = get_db_polylines() + + # Generate ordered_modules to ensure consistent mapping + seen_modules = set() + ordered_modules = [] + for r in nlp_resources: + m = r['module'] + if m not in seen_modules: + ordered_modules.append(m) + seen_modules.add(m) + + # Compute average module scores across all historical polylines + import math + history_scores = [p.get('module_scores', []) for p in polylines.values() if p.get('module_scores')] + num_histories = len(history_scores) + + avg_module_scores = [0.0] * len(ordered_modules) + if num_histories > 0: + for scores in history_scores: + for i, s in enumerate(scores): + if i < len(avg_module_scores): + avg_module_scores[i] += s + avg_module_scores = [s / num_histories for s in avg_module_scores] + + # Sort resources by angle (origin is 0, 19) + def compute_angle(r): + return math.atan2(19 - r['position']['y'], r['position']['x']) + + resources_sorted = sorted(nlp_resources, key=compute_angle) + + high_line_path = [] + current_path = [] + + for r in resources_sorted: + dx = r['position']['x'] + dy = 19 - r['position']['y'] + radius = math.hypot(dx, dy) + theta = math.atan2(dy, dx) + + # High Line + hl = float(r.get('high_line', 0.8)) + hl_rad = radius * hl + hl_x = hl_rad * math.cos(theta) + hl_y = 19 - hl_rad * math.sin(theta) + high_line_path.append({'x': hl_x, 'y': hl_y}) + + # Current Average + try: + m_idx = ordered_modules.index(r['module']) + avg_s = avg_module_scores[m_idx] if num_histories > 0 else 0.0 + except ValueError: + avg_s = 0.0 + + cur_rad = radius * avg_s + cur_x = cur_rad * math.cos(theta) + cur_y = 19 - cur_rad * math.sin(theta) + current_path.append({'x': cur_x, 'y': cur_y}) + + # Create the virtual polylines. Close the loops by adding the first point to the end. + if high_line_path: + high_line_path.append(high_line_path[0]) + if current_path: + current_path.append(current_path[0]) + + hl_polyline = { + 'id': 'high_line', + 'name': 'High Line Target', + 'path': high_line_path, + 'color': 'rgba(239, 68, 68, 0.8)', # Red + 'isActive': True, + 'confidence': 1.0, + 'summary': 'Target threshold for each module' + } + + cur_polyline = { + 'id': 'current_average', + 'name': 'Current Knowledge Base', + 'path': current_path, + 'color': 'rgba(59, 130, 246, 0.8)', # Blue + 'isActive': True, + 'confidence': 1.0, + 'summary': 'Your overall average knowledge across all summaries' + } + + # Format result: Return ONLY the virtual polylines, or include histories? + # User said "everywhere it should be shown like a high polyline... and current should be average of all histories" + # We will return the historical ones but set them to inactive, and these two to strictly active. + + result = list(polylines.values()) + for p in result: + p['isActive'] = False # Disable historical polylines by default + + result.append(hl_polyline) + result.append(cur_polyline) + + return jsonify(result) + + +@app.route('/api/polylines/', methods=['GET']) +def get_polyline(polyline_id): + """Get a specific polyline""" + polylines = get_db_polylines() + polyline = polylines.get(polyline_id) + if not polyline: + return jsonify({'error': 'Polyline not found'}), 404 + return jsonify(polyline) + + +@app.route('/api/polylines//toggle', methods=['POST']) +def toggle_polyline(polyline_id): + """Toggle polyline visibility""" + data = request.get_json() + is_active = data.get('isActive', False) + + polylines = get_db_polylines() + polyline = polylines.get(polyline_id) + if not polyline: + return jsonify({'error': 'Polyline not found'}), 404 + + polyline['isActive'] = is_active + save_polyline(polyline_id, polyline) + return jsonify(polyline) + + + +# ============================================= +# DQN PATH ENDPOINTS +# ============================================= + +@app.route('/api/dqn-path', methods=['POST']) +def generate_dqn_path(): + """ + Generate DQN optimal path using the Navigator module. + + Request JSON: + { + "session_id": "str", + "agent_position": {"x": int, "y": int}, + "visited_resource_ids": ["id1", "id2", ...] + } + """ + data = request.get_json() + agent_pos = data.get('agent_position', {'x': 10, 'y': 10}) + visited_ids = list(data.get('visited_resource_ids', [])) + + # Get latest module scores from most recent polyline (if any) + polylines = get_db_polylines() + latest_scores = [] + if polylines: + last_polyline = list(polylines.values())[-1] + latest_scores = last_polyline.get('module_scores', []) + + # Use DQN navigator to get top recommendation + rec = navigator.recommend_next( + visited_ids=visited_ids, + module_scores=latest_scores, + nlp_resources=nlp_resources + ) + + # Build a path: agent โ†’ recommended resource, plus up to 4 more close unvisited + path = [agent_pos] + visited_set = set(str(v).strip() for v in visited_ids) + + if rec['resource']: + path.append(rec['resource']['position']) + # Add up to 4 more nearest unvisited resources + remaining = [r for r in nlp_resources + if str(r['id']).strip() not in visited_set and r['id'] != rec['resource']['id']] + remaining.sort(key=lambda r: ( + (r['position']['x'] - rec['resource']['position']['x'])**2 + + (r['position']['y'] - rec['resource']['position']['y'])**2 + )) + for r in remaining[:4]: + path.append(r['position']) + + final_resource = rec['resource'] + total_reward = sum(r['reward'] for r in nlp_resources + if r['position'] in path[1:]) if path else 0 + + return jsonify({ + 'path': path, + 'finalResource': final_resource, + 'totalReward': total_reward, + 'pathLength': len(path), + 'navigatorReason': rec['reason'] + }) + + +@app.route('/api/next-recommendation', methods=['GET']) +def get_next_recommendation(): + """ + Get the DQN navigator's next resource recommendation for a session. + Returns: { resource, module, reason, q_values } + """ + session_id = request.args.get('session_id', 'default') + session = get_session(session_id) + visited_ids = [str(v).strip() for v in session.get('visitedResources', [])] + + # Get latest module scores from most recent polyline + polylines = get_db_polylines() + latest_scores = [] + if polylines: + last_polyline = list(polylines.values())[-1] + latest_scores = last_polyline.get('module_scores', []) + + rec = navigator.recommend_next( + visited_ids=visited_ids, + module_scores=latest_scores, + nlp_resources=nlp_resources + ) + + return jsonify(rec) + + +# ============================================= +# LEARNING DATA ENDPOINTS +# ============================================= + +@app.route('/api/learning-data', methods=['GET']) +def get_learning_data(): + """Get comprehensive learning data based on session history and latest summary""" + session_id = request.args.get('session_id', 'default') + session = get_session(session_id) + + visited_ids = set(str(v).strip() for v in session.get('visitedResources', [])) + visited_resources = [r for r in nlp_resources if str(r['id']).strip() in visited_ids] + + # Defaults + strengths = [r['title'] for r in visited_resources if r.get('difficulty', 0) <= 2] + # Recommendations using rewarding modules that are unvisited + unvisited = [r for r in nlp_resources if str(r['id']).strip() not in visited_ids] + unvisited.sort(key=lambda r: (-r.get('reward', 0), r.get('difficulty', 0))) + recommendations = [r['title'] for r in unvisited[:3]] + + # Try to augment with results from the latest summary analysis + ai_analysis = "" + xp_earned = 0 + try: + from database import load_db + db = load_db() + # Find latest summary for this session (they contain session_id in their ID or we match title) + matching_summaries = [s for s in db.get('summaries', []) if f"summary_{session_id}" in s.get('id', '')] + if matching_summaries: + latest = matching_summaries[-1] + if latest.get('strengths'): + strengths = latest['strengths'] + if latest.get('recommendations'): + recommendations = latest['recommendations'] + if latest.get('ai_analysis'): + ai_analysis = latest['ai_analysis'] + xp_earned = latest.get('xp_earned', 0) + except Exception as e: + print(f"Error augmenting learning data from summaries: {e}") + + # Calculate activity log and heatmap from all summaries for this session + activity_heatmap = {} + activity_log = [] + try: + from database import load_db + db = load_db() + all_summaries = db.get('summaries', []) + + # 1. Add Summary Activity + for s in all_summaries: + s_id = s.get('id', '') + # Robust parsing for summary_{session_id}_{YYYYMMDD}_{HHMMSS} + # We look for the YYYYMMDD part after the first two underscores + if f"summary_" in s_id: + parts = s_id.split('_') + # For summary_default_20260403_032235, parts are: ['summary', 'default', '20260403', '032235'] + # Search for the part that is exactly 8 digits (YYYYMMDD) and >= 2024 + date_str = None + for p in parts[2:]: + if len(p) == 8 and p.isdigit() and p.startswith('20'): + date_str = p + break + + if date_str: + formatted_date = f"{date_str[:4]}-{date_str[4:6]}-{date_str[6:]}" + # Weighted count: Summaries (deep learning) count as 2, visits/notifs as 1 + activity_heatmap[formatted_date] = activity_heatmap.get(formatted_date, 0) + 2 + + # Add to activity log (list of recent events) + activity_log.append({ + 'id': s_id, + 'type': 'summary', + 'title': s.get('title', 'Summary Written'), + 'timestamp': s.get('timestamp', int(datetime.now().timestamp() * 1000)) + }) + + # 2. Add Notification/Visit Activity + notifs = session.get('notifications', []) + for n in notifs: + ts = n.get('timestamp') + if ts: + # Convert ms timestamp to YYYY-MM-DD + dt = datetime.fromtimestamp(ts / 1000.0) + formatted_date = dt.strftime('%Y-%m-%d') + activity_heatmap[formatted_date] = activity_heatmap.get(formatted_date, 0) + 1 + + # Sort log by timestamp descending to show most recent at the top + activity_log.sort(key=lambda x: str(x.get('timestamp', '')), reverse=True) + activity_log = activity_log[:50] # Limit window + + except Exception as e: + print(f"Error calculating activity log: {e}") + + # Find most visited module + from collections import Counter + module_counts = Counter(r['module'] for r in visited_resources) + most_visited_module = module_counts.most_common(1)[0][0] if module_counts else "None" + + return jsonify({ + 'totalResources': len(nlp_resources), + 'visitedResources': len(visited_resources), + 'currentLevel': session.get('level', 1), + 'strengths': strengths[:3], + 'recommendations': recommendations[:3], + 'ai_analysis': ai_analysis, + 'activityHeatmap': activity_heatmap, + 'activityLog': activity_log, + 'nextOptimalResource': unvisited[0]['position'] if unvisited else None, + 'totalReward': session.get('totalReward', 0), + 'mostVisitedModule': most_visited_module, + 'xp_earned': xp_earned + }) + + +# ============================================= +# BOOKMARK ENDPOINTS +# ============================================= + +@app.route('/api/bookmarks', methods=['GET']) +def get_bookmarks(): + """Get all bookmarked resources for a session""" + session_id = request.args.get('session_id', 'default') + from database import get_bookmarks as get_db_bookmarks + return jsonify(get_db_bookmarks(session_id)) + + +@app.route('/api/bookmarks/add', methods=['POST']) +def add_bookmark(): + """Add a resource to bookmarks""" + data = request.get_json() + session_id = data.get('session_id', 'default') + resource_id = data.get('resource_id') + + if not resource_id: + return jsonify({'error': 'Resource ID required'}), 400 + + from database import add_bookmark as add_db_bookmark + add_db_bookmark(session_id, resource_id) + return jsonify({'status': 'success', 'resource_id': resource_id}) + + +@app.route('/api/bookmarks/remove', methods=['POST']) +def remove_bookmark(): + """Remove a resource from bookmarks""" + data = request.get_json() + session_id = data.get('session_id', 'default') + resource_id = data.get('resource_id') + + if not resource_id: + return jsonify({'error': 'Resource ID required'}), 400 + + from database import remove_bookmark as remove_db_bookmark + remove_db_bookmark(session_id, resource_id) + return jsonify({'status': 'success', 'resource_id': resource_id}) + + +# ============================================= +# NOTES ENDPOINTS +# ============================================= + +@app.route('/api/notes', methods=['GET']) +def get_notes_route(): + """Get all notes for a session""" + session_id = request.args.get('session_id', 'default') + return jsonify(get_notes(session_id)) + + +@app.route('/api/notes', methods=['POST']) +def add_note_route(): + """Add a new note""" + data = request.get_json() + session_id = data.get('session_id', 'default') + note_data = data.get('note') + + if not note_data: + return jsonify({'error': 'Note data required'}), 400 + + new_note = add_note(session_id, note_data) + return jsonify(new_note) + + +# ============================================= +# LECTURES ENDPOINTS +# ============================================= + +@app.route('/api/lectures', methods=['GET']) +def get_lectures_route(): + """Get all available lectures""" + return jsonify(get_lectures()) + + + +# ============================================= +# AI SIDER CHAT ENDPOINT +# ============================================= + +# Load YouTube transcripts +_transcripts_path = os.path.join(os.path.dirname(__file__), 'data', 'youtube_transcripts.json') +try: + if os.path.exists(_transcripts_path): + with open(_transcripts_path, 'r', encoding='utf-8') as f: + raw_transcripts = json.load(f) + # Normalize keys to lowercase for robust matching + _youtube_transcripts = {str(k).strip().lower(): v for k, v in raw_transcripts.items()} + print(f"Loaded and normalized transcripts for {len(_youtube_transcripts)} modules") + else: + print(f"Transcripts file not found: {_transcripts_path}") + _youtube_transcripts = {} +except Exception as e: + print(f"Could not load transcripts: {e}") + _youtube_transcripts = {} + + +from openai import OpenAI + +# AI Client configuration +# Using Groq (OpenAI-compatible) for free high-quality inference +_ai_client = None +try: + _api_key = os.getenv("GROQ_API_KEY") or os.getenv("OPENAI_API_KEY") or "FIXME_YOUR_API_KEY" + _base_url = "https://api.groq.com/openai/v1" if "GROQ" in _api_key or _api_key == "FIXME_YOUR_API_KEY" else None + _ai_client = OpenAI(api_key=_api_key, base_url=_base_url) +except Exception as e: + print(f"AI Client initialization warning: {e}") + +@app.route('/api/chat', methods=['POST']) +def chat_with_ai(): + """ + AI Sider chat endpoint - upgraded to use the openai package. + Uses YouTube transcript context and a premium model for better answers. + """ + data = request.get_json() + module = data.get('module', '') + question = data.get('question', '') + history = data.get('history', []) + + if not question.strip(): + return jsonify({'answer': 'Please ask a question about this lesson.', 'source': 'none'}) + + # 1. Find transcript/context with better matching + # Normalize input module for lookup + module_norm = str(module).strip().lower() + transcript = _youtube_transcripts.get(module_norm, '') + + if not transcript: + # Try finding the resource first to get its formal title + resource_match = None + for r in nlp_resources: + if r['id'] == module or r['title'].lower() == module_norm or r.get('module', '').lower() == module_norm: + resource_match = r + break + + target_name = resource_match['title'] if resource_match else module_norm + target_name_lower = target_name.lower() + + # Fuzzy match on transcripts keys + for key, val in _youtube_transcripts.items(): + if key in target_name_lower or target_name_lower in key: + transcript = val + break + + resource_desc = '' + for r in nlp_resources: + if r.get('module', '').lower() == module_norm or r.get('title', '').lower() == module_norm: + resource_desc = r.get('description', '')[:1000] + break + + context = transcript[:4500] if transcript else resource_desc[:1500] + + # 2. Try Premium Inference via OpenAI Package + # Check for actual keys, not just the placeholder + _key = os.getenv("GROQ_API_KEY") or os.getenv("OPENAI_API_KEY") + if _ai_client and _key and _key != "FIXME_YOUR_API_KEY": + try: + # Determine model based on provider + if "groq" in (_ai_client.base_url or "").lower(): + model = "llama-3.3-70b-versatile" + else: + model = "gpt-3.5-turbo" + + system_prompt = f"""You are 'Sider AI', a premium learning assistant for an Advanced NLP course. +Your goal is to help students understand the current lesson module: '{module}'. + +Use the following context from the lesson's YouTube transcript/description to answer the student's question accurately: +--- +{context} +--- + +INSTRUCTIONS: +- Be concise, professional, and encouraging. +- If the answer is in the context, prioritize that information. +- If the answer isn't in the context, use your general LLM knowledge to explain the concept. +- Format your response using clean Markdown.""" + + messages = [{"role": "system", "content": system_prompt}] + # Add limited history for continuity + for msg in history[-4:]: + role = "user" if msg.get("role") == "user" else "assistant" + messages.append({"role": role, "content": msg.get("content", "")}) + + messages.append({"role": "user", "content": question}) + + completion = _ai_client.chat.completions.create( + model=model, + messages=messages, + temperature=0.7, + max_tokens=800 + ) + answer = completion.choices[0].message.content + return jsonify({'answer': answer, 'source': f'openai-{model}'}) + + except Exception as e: + print(f"[CHAT] Premium AI error: {e}") + # Fall through to lookup if premium fails + + # 3. Fallback to Search/Lookup (Avoiding T5 to prevent worker timeouts on HF) + relevant_context = "" + if context: + sentences = context.split('.') + # Find sentences containing keywords from the question + keywords = [w.lower() for w in question.split() if len(w) > 3] + matching = [] + for s in sentences: + if any(k in s.lower() for k in keywords): + matching.append(s.strip()) + relevant_context = ". ".join(matching[:3]) + + if relevant_context: + answer = f"I found some relevant information in the lesson material: {relevant_context}. For a deeper explanation, please ensure an API key is configured in the environment." + else: + answer = f"I'm here to help with the lesson on '{module}'. I couldn't find a specific answer in the local material, but you should review the module description for more details. (Tip: Configure an AI API key for better responses)." + + return jsonify({'answer': answer, 'source': 'transcript-lookup'}) + + +if __name__ == '__main__': + print(f"Loaded {len(nlp_resources)} NLP resources") diff --git a/backend/request_logger.py b/backend/request_logger.py new file mode 100644 index 0000000000000000000000000000000000000000..f9f958cb52ae8c7318b31b95d1d87a02d1a643d0 --- /dev/null +++ b/backend/request_logger.py @@ -0,0 +1,28 @@ +import os +from datetime import datetime +from flask import request + +LOG_FILE = os.path.join(os.path.dirname(__file__), 'backend_logs.txt') + +def log_request(info=None): + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + method = request.method + url = request.url + + log_entry = f"[{timestamp}] {method} {url}\n" + + if info: + log_entry += f"Info: {info}\n" + + if method in ['POST', 'PUT']: + try: + json_data = request.get_json(silent=True) + if json_data: + log_entry += f"Payload: {json_data}\n" + except Exception: + pass + + log_entry += "-" * 50 + "\n" + + with open(LOG_FILE, 'a', encoding='utf-8') as f: + f.write(log_entry) diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..44add1cca8440a8a4746fe19bb9f17e8bcfb7d85 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,19 @@ +beautifulsoup4==4.12.3 +bs4==0.0.2 +Flask==3.0.3 +Flask-Cors==4.0.1 +gunicorn +Jinja2==3.1.4 +keybert==0.8.5 +nltk==3.8.1 +numpy +pandas +pdfplumber==0.11.5 +requests==2.32.3 +scikit-learn +sentence-transformers==3.0.1 +torch +Werkzeug==3.0.3 +youtube-transcript-api +lxml +openai diff --git a/backend/utils.py b/backend/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..ded21bd5a0364c0ca18addb4519ac416aff675eb --- /dev/null +++ b/backend/utils.py @@ -0,0 +1,266 @@ +""" +========================================================= + Polyline & Vector Utility Functions +========================================================= + +This file contains utility (i.e. globally used) functions in accordance with DRY principle : +- Handling polylines (multi-dimensional paths) used in courses/resources. +- Computing geometric and vector operations (centroids, distances, cosine similarity). +- Finding nearest resources for a learner based on polyline similarity. +- Converting NumPy arrays to standard Python lists for JSON serialization. +(more to be added with time...) +========================================================= +""" + +import numpy as np +import math +import heapq +import re +from bs4 import BeautifulSoup +import nltk +from nltk.stem import WordNetLemmatizer, PorterStemmer +from nltk.corpus import stopwords + +# Ensure NLTK data is downloaded +try: + nltk.data.find('corpora/stopwords') +except LookupError: + nltk.download('stopwords') + +try: + nltk.data.find('corpora/wordnet') +except LookupError: + nltk.download('wordnet') + +# =========================== +# utils_preprocess_text +# =========================== +def utils_preprocess_text(text: str, flg_stemm: bool = False, flg_lemm: bool = True, lst_stopwords: list = None) -> str: + """ + Preprocess text by removing HTML tags, punctuations, numbers, stopwords, and applying stemming/lemmatization. + + Parameters: + text (str): The text to preprocess. + flg_stemm (bool): Flag to apply stemming. Default is False. + flg_lemm (bool): Flag to apply lemmatization. Default is True. + lst_stopwords (list): List of stopwords to remove. Default is None. + + Returns: + str: The preprocessed text. + """ + if not text: + return "" + + # Remove HTML + soup = BeautifulSoup(text, 'lxml') + text = soup.get_text() + + # Remove punctuations and numbers + text = re.sub('[^a-zA-Z]', ' ', text) + + # Single character removal + text = re.sub(r"\s+[a-zA-Z]\s+", ' ', text) + + # Remove multiple spaces + text = re.sub(r'\s+', ' ', text) + + # Tokenize text + lst_text = text.split() + + # Remove stopwords + if lst_stopwords is not None: + lst_text = [word for word in lst_text if word not in lst_stopwords] + + # Apply stemming + if flg_stemm: + ps = PorterStemmer() + lst_text = [ps.stem(word) for word in lst_text] + + # Apply lemmatization + if flg_lemm: + lem = WordNetLemmatizer() + lst_text = [lem.lemmatize(word) for word in lst_text] + + text = " ".join(lst_text) + return text + +# =========================== +# convert_to_lists +# =========================== +def convert_to_lists(data): + """ + Recursively convert NumPy arrays to standard Python lists. + + Parameters: + data (np.ndarray | list | dict | other): Input data structure. + + Returns: + list | dict | original type: Data converted to lists recursively. + """ + if isinstance(data, np.ndarray): + return data.tolist() + elif isinstance(data, list): + return [convert_to_lists(item) for item in data] + elif isinstance(data, dict): + return {key: convert_to_lists(value) for key, value in data.items()} + else: + return data + + +# =========================== +# get_lowline_of_polylines +# =========================== +def get_lowline_of_polylines(polylines): + """ + Get the minimum value along each dimension across all polylines. + + Parameters: + polylines (list of lists): Each inner list is a polyline vector. + + Returns: + list: Minimum values for each dimension (lowline). + """ + if not polylines: + return [0] * 12 # Default 12-dimension zero vector + + lowline = [min([polylines[i][j] for i in range(len(polylines))]) + for j in range(len(polylines[0]))] + return lowline + + +# =========================== +# get_highline_of_polylines +# =========================== +def get_highline_of_polylines(polylines): + """ + Get the maximum value along each dimension across all polylines. + + Parameters: + polylines (list of lists): Each inner list is a polyline vector. + + Returns: + list: Maximum values for each dimension (highline). + """ + return [max([polylines[i][j] for i in range(len(polylines))]) + for j in range(len(polylines[0]))] + + +# =========================== +# get_cos_sim +# =========================== +def get_cos_sim(a: np.ndarray, b: np.ndarray) -> float: + """ + Calculate the cosine similarity between two vectors. + + Cosine similarity measures how similar two vectors are in direction + regardless of their magnitude. + + Parameters: + a (np.ndarray): First vector. + b (np.ndarray): Second vector. + + Returns: + float: Cosine similarity (1 = identical direction, -1 = opposite). + """ + dot_product = np.dot(a, b) + norm_a = np.linalg.norm(a) + norm_b = np.linalg.norm(b) + return dot_product / (norm_a * norm_b) + + +# =========================== +# calculate_centroid +# =========================== +def calculate_centroid(polylines): + """ + Calculate the centroid (mean point) of a list of polylines. + + Parameters: + polylines (list of lists): List of N-dimension polyline vectors. + + Returns: + list: Centroid coordinates (mean along each dimension). + """ + polyline_array = np.array(polylines) + centroid = np.mean(polyline_array, axis=0) + return centroid.tolist() + + +# =========================== +# two_polyline_distance +# =========================== +def two_polyline_distance(point1, point2): + """ + Calculate Euclidean distance between two polylines (points in N-dimensional space). + + Parameters: + point1 (list | np.ndarray): First polyline coordinates. + point2 (list | np.ndarray): Second polyline coordinates. + + Returns: + float: Euclidean distance between the two polylines. + """ + if len(point1) != len(point2): + raise ValueError("Points must have the same dimensions") + + return math.sqrt(sum((p2 - p1) ** 2 for p1, p2 in zip(point1, point2))) + + +# =========================== +# nearest_seven +# =========================== +def nearest_seven(learner_polyline, resources_id_polylines): + """ + Find the 7 nearest resources to the learner based on polyline distance. + + Parameters: + learner_polyline (list): Learner's current polyline coordinates. + resources_id_polylines (list of tuples): Each tuple is (resource_id, polyline). + + Returns: + list: IDs of the 7 nearest resources. + """ + top7 = [] + for id_polyline in resources_id_polylines: + distance = two_polyline_distance(learner_polyline, id_polyline[1]) + heapq.heappush(top7, (-distance, id_polyline[0])) # Use negative distance for max-heap + if len(top7) > 7: + heapq.heappop(top7) + return [id[1] for id in top7] + + +# =========================== +# calculate_distance +# =========================== +def calculate_distance(pos1, pos2): + """ + Compute Euclidean distance between two 2D points. + + Parameters: + pos1 (list | tuple): [x, y] of first point. + pos2 (list | tuple): [x, y] of second point. + + Returns: + float: Euclidean distance between pos1 and pos2. + """ + return np.sqrt((pos1[0] - pos2[0]) ** 2 + (pos1[1] - pos2[1]) ** 2) + + +# =========================== +# is_valid_id +# =========================== +def is_valid_id(id): + """ + Check if a given ID is valid (convertible to integer). + + Parameters: + id (any): ID to validate. + + Returns: + bool: True if valid integer, False otherwise. + """ + try: + _ = int(id) + return True + except: + return False \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..56f06857e202dd1c078d8a9469b0dbce3ab1705e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +version: '3.8' + +services: + nlp-learning-grid: + build: . + image: nlp-learning-grid:latest + container_name: nlp-learning-grid + ports: + - "5000:5000" + environment: + - PORT=5000 + - GROQ_API_KEY=${GROQ_API_KEY:-} + - OPENAI_API_KEY=${OPENAI_API_KEY:-} + volumes: + - ./backend/data:/app/backend/data + restart: unless-stopped diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000000000000000000000000000000000000..82c2e20ccc2bae01b6589f5f391cb20f34db41fd --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + } +); diff --git a/index.html b/index.html new file mode 100644 index 0000000000000000000000000000000000000000..d008c698a3516421ffed2aae931a9844127a25b8 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + DQN Agent Learning Environment + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..e994b70cbd3744f6fea9e36624f0ae135553910b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4937 @@ +{ + "name": "vite-react-typescript-starter", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vite-react-typescript-starter", + "version": "0.0.0", + "dependencies": { + "@react-three/drei": "^10.7.7", + "@react-three/fiber": "^9.5.0", + "@supabase/supabase-js": "^2.57.4", + "@types/canvas-confetti": "^1.9.0", + "canvas-confetti": "^1.9.4", + "framer-motion": "^12.38.0", + "lucide-react": "^0.344.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^7.13.2", + "three": "^0.183.2", + "zustand": "^5.0.12" + }, + "devDependencies": { + "@eslint/js": "^9.9.1", + "@types/react": "^18.3.5", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.18", + "eslint": "^9.9.1", + "eslint-plugin-react-hooks": "^5.1.0-rc.0", + "eslint-plugin-react-refresh": "^0.4.11", + "globals": "^15.9.0", + "postcss": "^8.4.35", + "tailwindcss": "^3.4.1", + "typescript": "^5.5.3", + "typescript-eslint": "^8.3.0", + "vite": "^5.4.2" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", + "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.25.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.7.tgz", + "integrity": "sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.7.tgz", + "integrity": "sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helpers": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/template": "^7.25.7", + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", + "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.7", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", + "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", + "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", + "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-simple-access": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", + "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", + "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", + "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", + "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", + "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", + "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.7.tgz", + "integrity": "sha512-JD9MUnLbPL0WdVK8AWC7F7tTG2OS6u/AKKnsK+NdRhUiVdnzyR1S3kKQCaRLOiaULvUiqK6Z4JQE635VgtCFeg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.7.tgz", + "integrity": "sha512-S/JXG/KrbIY06iyJPKfxr0qRxnhNOdkNXYBl/rmwgDd72cQLH9tEGkDm/yJPGvcSIUoikzfjMios9i+xT/uv9w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", + "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", + "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", + "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@dimforge/rapier3d-compat": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", + "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", + "license": "Apache-2.0" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz", + "integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz", + "integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz", + "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==", + "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", + "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz", + "integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.0", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mediapipe/tasks-vision": { + "version": "0.10.17", + "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.17.tgz", + "integrity": "sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==", + "license": "Apache-2.0" + }, + "node_modules/@monogrid/gainmap-js": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@monogrid/gainmap-js/-/gainmap-js-3.4.0.tgz", + "integrity": "sha512-2Z0FATFHaoYJ8b+Y4y4Hgfn3FRFwuU5zRrk+9dFWp4uGAdHGqVEdP7HP+gLA3X469KXHmfupJaUbKo1b/aDKIg==", + "license": "MIT", + "dependencies": { + "promise-worker-transferable": "^1.0.4" + }, + "peerDependencies": { + "three": ">= 0.159.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@react-three/drei": { + "version": "10.7.7", + "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.7.7.tgz", + "integrity": "sha512-ff+J5iloR0k4tC++QtD/j9u3w5fzfgFAWDtAGQah9pF2B1YgOq/5JxqY0/aVoQG5r3xSZz0cv5tk2YuBob4xEQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mediapipe/tasks-vision": "0.10.17", + "@monogrid/gainmap-js": "^3.0.6", + "@use-gesture/react": "^10.3.1", + "camera-controls": "^3.1.0", + "cross-env": "^7.0.3", + "detect-gpu": "^5.0.56", + "glsl-noise": "^0.0.0", + "hls.js": "^1.5.17", + "maath": "^0.10.8", + "meshline": "^3.3.1", + "stats-gl": "^2.2.8", + "stats.js": "^0.17.0", + "suspend-react": "^0.1.3", + "three-mesh-bvh": "^0.8.3", + "three-stdlib": "^2.35.6", + "troika-three-text": "^0.52.4", + "tunnel-rat": "^0.1.2", + "use-sync-external-store": "^1.4.0", + "utility-types": "^3.11.0", + "zustand": "^5.0.1" + }, + "peerDependencies": { + "@react-three/fiber": "^9.0.0", + "react": "^19", + "react-dom": "^19", + "three": ">=0.159" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/@react-three/fiber": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.5.0.tgz", + "integrity": "sha512-FiUzfYW4wB1+PpmsE47UM+mCads7j2+giRBltfwH7SNhah95rqJs3ltEs9V3pP8rYdS0QlNne+9Aj8dS/SiaIA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@types/webxr": "*", + "base64-js": "^1.5.1", + "buffer": "^6.0.3", + "its-fine": "^2.0.0", + "react-use-measure": "^2.1.7", + "scheduler": "^0.27.0", + "suspend-react": "^0.1.3", + "use-sync-external-store": "^1.4.0", + "zustand": "^5.0.3" + }, + "peerDependencies": { + "expo": ">=43.0", + "expo-asset": ">=8.4", + "expo-file-system": ">=11.0", + "expo-gl": ">=11.0", + "react": ">=19 <19.3", + "react-dom": ">=19 <19.3", + "react-native": ">=0.78", + "three": ">=0.156" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + }, + "expo-asset": { + "optional": true + }, + "expo-file-system": { + "optional": true + }, + "expo-gl": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/@react-three/fiber/node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@supabase/auth-js": { + "version": "2.71.1", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.71.1.tgz", + "integrity": "sha512-mMIQHBRc+SKpZFRB2qtupuzulaUhFYupNyxqDj5Jp/LyPvcWvjaJzZzObv6URtL/O6lPxkanASnotGtNpS3H2Q==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.6.tgz", + "integrity": "sha512-bhjZ7rmxAibjgmzTmQBxJU6ZIBCCJTc3Uwgvdi4FewueUTAGO5hxZT1Sj6tiD+0dSXf9XI87BDdJrg12z8Uaew==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "1.21.4", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.21.4.tgz", + "integrity": "sha512-TxZCIjxk6/dP9abAi89VQbWWMBbybpGWyvmIzTd79OeravM13OjR/YEYeyUOPcM1C3QyvXkvPZhUfItvmhY1IQ==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.15.5", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.15.5.tgz", + "integrity": "sha512-/Rs5Vqu9jejRD8ZeuaWXebdkH+J7V6VySbCZ/zQM93Ta5y3mAmocjioa/nzlB6qvFmyylUgKVS1KpE212t30OA==", + "dependencies": { + "@supabase/node-fetch": "^2.6.13", + "@types/phoenix": "^1.6.6", + "@types/ws": "^8.18.1", + "ws": "^8.18.2" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.12.1.tgz", + "integrity": "sha512-QWg3HV6Db2J81VQx0PqLq0JDBn4Q8B1FYn1kYcbla8+d5WDmTdwwMr+EJAxNOSs9W4mhKMv+EYCpCrTFlTj4VQ==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.57.4", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.57.4.tgz", + "integrity": "sha512-LcbTzFhHYdwfQ7TRPfol0z04rLEyHabpGYANME6wkQ/kLtKNmI+Vy+WEM8HxeOZAtByUFxoUTTLwhXmrh+CcVw==", + "dependencies": { + "@supabase/auth-js": "2.71.1", + "@supabase/functions-js": "2.4.6", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.21.4", + "@supabase/realtime-js": "2.15.5", + "@supabase/storage-js": "2.12.1" + } + }, + "node_modules/@tweenjs/tween.js": { + "version": "23.1.3", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/canvas-confetti": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@types/canvas-confetti/-/canvas-confetti-1.9.0.tgz", + "integrity": "sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg==", + "license": "MIT" + }, + "node_modules/@types/draco3d": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz", + "integrity": "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "24.3.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz", + "integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==", + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/@types/offscreencanvas": { + "version": "2019.7.3", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", + "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==", + "license": "MIT" + }, + "node_modules/@types/phoenix": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", + "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.3.11", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", + "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-reconciler": { + "version": "0.28.9", + "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz", + "integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/stats.js": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", + "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", + "license": "MIT" + }, + "node_modules/@types/three": { + "version": "0.183.1", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.183.1.tgz", + "integrity": "sha512-f2Pu5Hrepfgavttdye3PsH5RWyY/AvdZQwIVhrc4uNtvF7nOWJacQKcoVJn0S4f0yYbmAE6AR+ve7xDcuYtMGw==", + "license": "MIT", + "dependencies": { + "@dimforge/rapier3d-compat": "~0.12.0", + "@tweenjs/tween.js": "~23.1.3", + "@types/stats.js": "*", + "@types/webxr": ">=0.5.17", + "@webgpu/types": "*", + "fflate": "~0.8.2", + "meshoptimizer": "~1.0.1" + } + }, + "node_modules/@types/webxr": { + "version": "0.5.24", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", + "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz", + "integrity": "sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.8.1", + "@typescript-eslint/type-utils": "8.8.1", + "@typescript-eslint/utils": "8.8.1", + "@typescript-eslint/visitor-keys": "8.8.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.1.tgz", + "integrity": "sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.8.1", + "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/typescript-estree": "8.8.1", + "@typescript-eslint/visitor-keys": "8.8.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz", + "integrity": "sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/visitor-keys": "8.8.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.1.tgz", + "integrity": "sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.8.1", + "@typescript-eslint/utils": "8.8.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.1.tgz", + "integrity": "sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz", + "integrity": "sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/visitor-keys": "8.8.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.1.tgz", + "integrity": "sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.8.1", + "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/typescript-estree": "8.8.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz", + "integrity": "sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.8.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@use-gesture/core": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz", + "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", + "license": "MIT" + }, + "node_modules/@use-gesture/react": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz", + "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", + "license": "MIT", + "dependencies": { + "@use-gesture/core": "10.3.1" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.2.tgz", + "integrity": "sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/plugin-transform-react-jsx-self": "^7.24.7", + "@babel/plugin-transform-react-jsx-source": "^7.24.7", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, + "node_modules/@webgpu/types": { + "version": "0.1.69", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz", + "integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/camera-controls": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-3.1.2.tgz", + "integrity": "sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==", + "license": "MIT", + "engines": { + "node": ">=22.0.0", + "npm": ">=10.5.1" + }, + "peerDependencies": { + "three": ">=0.126.1" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001769", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", + "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/canvas-confetti": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.9.4.tgz", + "integrity": "sha512-yxQbJkAVrFXWNbTUjPqjF7G+g6pDotOUHGbkZq2NELZUMDpiJ85rIEazVb8GTaAptNW2miJAXbs1BtioA251Pw==", + "license": "ISC", + "funding": { + "type": "donate", + "url": "https://www.paypal.me/kirilvatev" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/detect-gpu": { + "version": "5.0.70", + "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.70.tgz", + "integrity": "sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==", + "license": "MIT", + "dependencies": { + "webgl-constants": "^1.1.1" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/draco3d": { + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz", + "integrity": "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==", + "license": "Apache-2.0" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.33", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.33.tgz", + "integrity": "sha512-+cYTcFB1QqD4j4LegwLfpCNxifb6dDFUAwk6RsLusCwIaZI6or2f+q8rs5tTB2YC53HhOlIbEaqHMAAC8IOIwA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.12.0.tgz", + "integrity": "sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.6.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.12.0", + "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.5", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.1.0", + "eslint-visitor-keys": "^4.1.0", + "espree": "^10.2.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.1.0-rc-fb9a90fa48-20240614", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0-rc-fb9a90fa48-20240614.tgz", + "integrity": "sha512-xsiRwaDNF5wWNC4ZHLut+x/YcAxksUd9Rizt7LaEn3bV8VyYRpXnRJQlLOfYaVy9esk4DFP4zPPnoNVjq5Gc0w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.12.tgz", + "integrity": "sha512-9neVjoGv20FwYtCP6CB1dzR1vr57ZDNOXst21wd2xJ/cTlM2xLq0GWVlSNTdMn/4BtP6cHYBMCSp1wFBJ9jBsg==", + "dev": true, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/eslint-scope": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", + "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", + "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", + "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", + "dev": true, + "dependencies": { + "acorn": "^8.12.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/framer-motion": { + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz", + "integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.38.0", + "motion-utils": "^12.36.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "15.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.11.0.tgz", + "integrity": "sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glsl-noise": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz", + "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==", + "license": "MIT" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hls.js": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.15.tgz", + "integrity": "sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==", + "license": "Apache-2.0" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/its-fine": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz", + "integrity": "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==", + "license": "MIT", + "dependencies": { + "@types/react-reconciler": "^0.28.9" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.344.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.344.0.tgz", + "integrity": "sha512-6YyBnn91GB45VuVT96bYCOKElbJzUHqp65vX8cDcu55MQL9T969v4dhGClpljamuI/+KMO9P6w9Acq1CVQGvIQ==", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/maath": { + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/maath/-/maath-0.10.8.tgz", + "integrity": "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==", + "license": "MIT", + "peerDependencies": { + "@types/three": ">=0.134.0", + "three": ">=0.134.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/meshline": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.3.1.tgz", + "integrity": "sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==", + "license": "MIT", + "peerDependencies": { + "three": ">=0.137" + } + }, + "node_modules/meshoptimizer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.0.1.tgz", + "integrity": "sha512-Vix+QlA1YYT3FwmBBZ+49cE5y/b+pRrcXKqGpS5ouh33d3lSp2PoTpCw19E0cKDFWalembrHnIaZetf27a+W2g==", + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/motion-dom": { + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz", + "integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.36.0" + } + }, + "node_modules/motion-utils": { + "version": "12.36.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.36.0.tgz", + "integrity": "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/potpack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==", + "license": "ISC" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/promise-worker-transferable": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/promise-worker-transferable/-/promise-worker-transferable-1.0.4.tgz", + "integrity": "sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw==", + "license": "Apache-2.0", + "dependencies": { + "is-promise": "^2.1.0", + "lie": "^3.0.2" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.2.tgz", + "integrity": "sha512-tX1Aee+ArlKQP+NIUd7SE6Li+CiGKwQtbS+FfRxPX6Pe4vHOo6nr9d++u5cwg+Z8K/x8tP+7qLmujDtfrAoUJA==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.2.tgz", + "integrity": "sha512-aR7SUORwTqAW0JDeiWF07e9SBE9qGpByR9I8kJT5h/FrBKxPMS6TiC7rmVO+gC0q52Bx7JnjWe8Z1sR9faN4YA==", + "license": "MIT", + "dependencies": { + "react-router": "7.13.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-use-measure": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz", + "integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.13", + "react-dom": ">=16.13" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.24.0", + "@rollup/rollup-android-arm64": "4.24.0", + "@rollup/rollup-darwin-arm64": "4.24.0", + "@rollup/rollup-darwin-x64": "4.24.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", + "@rollup/rollup-linux-arm-musleabihf": "4.24.0", + "@rollup/rollup-linux-arm64-gnu": "4.24.0", + "@rollup/rollup-linux-arm64-musl": "4.24.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", + "@rollup/rollup-linux-riscv64-gnu": "4.24.0", + "@rollup/rollup-linux-s390x-gnu": "4.24.0", + "@rollup/rollup-linux-x64-gnu": "4.24.0", + "@rollup/rollup-linux-x64-musl": "4.24.0", + "@rollup/rollup-win32-arm64-msvc": "4.24.0", + "@rollup/rollup-win32-ia32-msvc": "4.24.0", + "@rollup/rollup-win32-x64-msvc": "4.24.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stats-gl": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/stats-gl/-/stats-gl-2.4.2.tgz", + "integrity": "sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ==", + "license": "MIT", + "dependencies": { + "@types/three": "*", + "three": "^0.170.0" + }, + "peerDependencies": { + "@types/three": "*", + "three": "*" + } + }, + "node_modules/stats-gl/node_modules/three": { + "version": "0.170.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.170.0.tgz", + "integrity": "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ==", + "license": "MIT" + }, + "node_modules/stats.js": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz", + "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/suspend-react": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz", + "integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=17.0" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/three": { + "version": "0.183.2", + "resolved": "https://registry.npmjs.org/three/-/three-0.183.2.tgz", + "integrity": "sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==", + "license": "MIT" + }, + "node_modules/three-mesh-bvh": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.8.3.tgz", + "integrity": "sha512-4G5lBaF+g2auKX3P0yqx+MJC6oVt6sB5k+CchS6Ob0qvH0YIhuUk1eYr7ktsIpY+albCqE80/FVQGV190PmiAg==", + "license": "MIT", + "peerDependencies": { + "three": ">= 0.159.0" + } + }, + "node_modules/three-stdlib": { + "version": "2.36.1", + "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.36.1.tgz", + "integrity": "sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg==", + "license": "MIT", + "dependencies": { + "@types/draco3d": "^1.4.0", + "@types/offscreencanvas": "^2019.6.4", + "@types/webxr": "^0.5.2", + "draco3d": "^1.4.1", + "fflate": "^0.6.9", + "potpack": "^1.0.1" + }, + "peerDependencies": { + "three": ">=0.128.0" + } + }, + "node_modules/three-stdlib/node_modules/fflate": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz", + "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==", + "license": "MIT" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/troika-three-text": { + "version": "0.52.4", + "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.52.4.tgz", + "integrity": "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==", + "license": "MIT", + "dependencies": { + "bidi-js": "^1.0.2", + "troika-three-utils": "^0.52.4", + "troika-worker-utils": "^0.52.0", + "webgl-sdf-generator": "1.1.1" + }, + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/troika-three-utils": { + "version": "0.52.4", + "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.52.4.tgz", + "integrity": "sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==", + "license": "MIT", + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/troika-worker-utils": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.52.0.tgz", + "integrity": "sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==", + "license": "MIT" + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tunnel-rat": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tunnel-rat/-/tunnel-rat-0.1.2.tgz", + "integrity": "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==", + "license": "MIT", + "dependencies": { + "zustand": "^4.3.2" + } + }, + "node_modules/tunnel-rat/node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.8.1.tgz", + "integrity": "sha512-R0dsXFt6t4SAFjUSKFjMh4pXDtq04SsFKCVGDP3ZOzNP7itF0jBcZYU4fMsZr4y7O7V7Nc751dDeESbe4PbQMQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.8.1", + "@typescript-eslint/parser": "8.8.1", + "@typescript-eslint/utils": "8.8.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/vite": { + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", + "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/webgl-constants": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz", + "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==" + }, + "node_modules/webgl-sdf-generator": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz", + "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==", + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zustand": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz", + "integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000000000000000000000000000000000000..b998ec2f07b88192e8e4dc5122606937e7bf91a1 --- /dev/null +++ b/package.json @@ -0,0 +1,42 @@ +{ + "name": "vite-react-typescript-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@react-three/drei": "^10.7.7", + "@react-three/fiber": "^9.5.0", + "@supabase/supabase-js": "^2.57.4", + "@types/canvas-confetti": "^1.9.0", + "canvas-confetti": "^1.9.4", + "framer-motion": "^12.38.0", + "lucide-react": "^0.344.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^7.13.2", + "three": "^0.183.2", + "zustand": "^5.0.12" + }, + "devDependencies": { + "@eslint/js": "^9.9.1", + "@types/react": "^18.3.5", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.18", + "eslint": "^9.9.1", + "eslint-plugin-react-hooks": "^5.1.0-rc.0", + "eslint-plugin-react-refresh": "^0.4.11", + "globals": "^15.9.0", + "postcss": "^8.4.35", + "tailwindcss": "^3.4.1", + "typescript": "^5.5.3", + "typescript-eslint": "^8.3.0", + "vite": "^5.4.2" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000000000000000000000000000000000000..2aa7205d4b402a1bdfbe07110c61df920b370066 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/scripts/deploy_hf.ps1 b/scripts/deploy_hf.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..5511ddb429ca2378c3bf59f10bcd0bfc922e588b --- /dev/null +++ b/scripts/deploy_hf.ps1 @@ -0,0 +1,28 @@ +# Hugging Face Deployment Script for NL Main + +$HF_TOKEN = $env:HF_TOKEN # Set this in your local environment +if (-not $HF_TOKEN) { $HF_TOKEN = "YOUR_HF_TOKEN_HERE" } +$HF_USERNAME = "minowau" # Guessed from GitHub remote +$SPACE_NAME = "NavigatedLearning" + +Write-Host "--- 1. Building Frontend ---" -ForegroundColor Cyan +npm run build + +Write-Host "--- 2. Setting up Hugging Face Remote ---" -ForegroundColor Cyan +$HF_REMOTE_URL = "https://user:$($HF_TOKEN)@huggingface.co/spaces/$($HF_USERNAME)/$($SPACE_NAME)" + +# Check if HF remote already exists +$existingRemote = git remote | Select-String "^hf$" +if ($existingRemote) { + git remote set-url hf $HF_REMOTE_URL +} else { + git remote add hf $HF_REMOTE_URL +} + +Write-Host "--- 3. Pushing to Hugging Face ---" -ForegroundColor Cyan +# Ensure we are on a branch +$currentBranch = git rev-parse --abbrev-ref HEAD +git push hf "$($currentBranch):main" --force + +Write-Host "--- Deployment Request Sent! ---" -ForegroundColor Green +Write-Host "Check your Space at: https://huggingface.co/spaces/$($HF_USERNAME)/$($SPACE_NAME)" -ForegroundColor Yellow diff --git a/scripts/deploy_minro.ps1 b/scripts/deploy_minro.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..5547a3afbc681de6a57cc36db7cd19e952f523ea --- /dev/null +++ b/scripts/deploy_minro.ps1 @@ -0,0 +1,45 @@ +# deploy_minro.ps1 +# PowerShell script to bundle and deploy the application to MINRO server + +$SERVER_IP = "113.30.156.94" +$SERVER_PORT = "33010" +$SERVER_USER = "ubuntu" +$REMOTE_PATH = "/home/ubuntu/nl_main" +$ZIP_FILE = "nl_main_deployment.zip" + +Write-Host "--- 1. Cleaning up previous bundles ---" -ForegroundColor Cyan +if (Test-Path $ZIP_FILE) { Remove-Item $ZIP_FILE } + +Write-Host "--- 2. Building Frontend ---" -ForegroundColor Cyan +npm run build + +Write-Host "--- 3. Bundling Source Code (Excluding large/sensitive files) ---" -ForegroundColor Cyan +# Create a temporary list for inclusion +$include = @( + "backend/*", + "dist/*", + "Dockerfile", + "docker-compose.yml", + "package.json", + "render.yaml", + "scripts/*" +) + +# Use Compress-Archive to create the bundle +Compress-Archive -Path $include -DestinationPath $ZIP_FILE -Force + +Write-Host "--- 4. Transferring to Server ($SERVER_IP) ---" -ForegroundColor Cyan +scp -P $SERVER_PORT $ZIP_FILE "$($SERVER_USER)@$($SERVER_IP):$REMOTE_PATH/" + +Write-Host "--- 5. Remote Execution: Unzip & Restart ---" -ForegroundColor Cyan +$remoteCmd = @" +cd $REMOTE_PATH +unzip -o $ZIP_FILE +docker-compose down +docker-compose up --build -d +"@ + +ssh -p $SERVER_PORT "$($SERVER_USER)@$($SERVER_IP)" $remoteCmd + +Write-Host "--- Deployment Complete! ---" -ForegroundColor Green +Write-Host "App should be running on: http://113.30.156.101:5000" -ForegroundColor Yellow diff --git a/scripts/deploy_remote.ps1 b/scripts/deploy_remote.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..4cf91320a0a78bb92022c4886a9a26de809b3def --- /dev/null +++ b/scripts/deploy_remote.ps1 @@ -0,0 +1,34 @@ + +$SERVER_IP = "172.16.193.4" +$SERVER_PORT = "22" +$SERVER_USER = "suhan" +$SERVER_PASS = "Test@123" +$REMOTE_PATH = "/home/suhan/nl_main" +$ZIP_FILE = "nl_main_deployment.tar.gz" +$PASS = $SERVER_PASS + +Write-Host "--- 1. Cleaning up previous bundles ---" -ForegroundColor Cyan +if (Test-Path $ZIP_FILE) { Remove-Item $ZIP_FILE } + +Write-Host "--- 2. Building Frontend ---" -ForegroundColor Cyan +npm run build + +Write-Host "--- 3. Bundling Source Code with Tar ---" -ForegroundColor Cyan +tar -czf $ZIP_FILE backend dist Dockerfile docker-compose.yml package.json scripts + +Write-Host "--- 4. Transferring to Server ($SERVER_IP) ---" -ForegroundColor Cyan +scp -P $SERVER_PORT $ZIP_FILE "$($SERVER_USER)@$($SERVER_IP):$REMOTE_PATH/" + +Write-Host "--- 5. Remote Execution: Untar & Restart ---" -ForegroundColor Cyan +$remoteCmd = @" +echo $PASS | sudo -S mkdir -p $REMOTE_PATH +cd $REMOTE_PATH +echo $PASS | sudo -S rm -rf * +echo $PASS | sudo -S tar -xzf $ZIP_FILE +echo $PASS | sudo -S docker-compose down +echo $PASS | sudo -S docker-compose up --build -d +"@ + +ssh -p $SERVER_PORT "$($SERVER_USER)@$($SERVER_IP)" $remoteCmd + +Write-Host "--- Deployment Complete! ---" -ForegroundColor Green diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000000000000000000000000000000000000..9fd00225d4fef8dff00309827217637fa96ff313 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,35 @@ +import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; +import { LoginPage } from './pages/auth/LoginPage'; +import { SignupPage } from './pages/auth/SignupPage'; +import { ForgotPasswordPage } from './pages/auth/ForgotPasswordPage'; +import { ResourcesDashboard } from './pages/dashboard/ResourcesDashboard'; +import NavigatorDashboard from './pages/dashboard/NavigatorDashboard'; +import GridMatrixPage from './pages/dashboard/GridMatrixPage'; +import KnowledgeHubPage from './pages/dashboard/KnowledgeHubPage'; +import { AppProvider } from './context/AppContext'; +import { CustomCursor } from './components/ui/CustomCursor'; + +function App() { + return ( + + + + + } /> + } /> + } /> + } /> + } /> + } /> + + {/* Redirects */} + } /> + } /> + } /> + + + + ); +} + +export default App; \ No newline at end of file diff --git a/src/components/ControlPanel.tsx b/src/components/ControlPanel.tsx new file mode 100644 index 0000000000000000000000000000000000000000..699af1ae207455c63089da598414a8cbf44cabb8 --- /dev/null +++ b/src/components/ControlPanel.tsx @@ -0,0 +1,788 @@ +import React, { useState } from 'react'; +import { motion } from 'framer-motion'; +import { LearningSummary, Polyline, Resource } from '../types'; +import { X, BookOpen, Activity, Map, PlayCircle, HelpCircle, Sparkles, CheckCircle, TrendingUp, Search, Award, Bookmark } from 'lucide-react'; + +interface ControlPanelProps { + onSummarizeLearning: (title: string, summary: string) => void; + onShowPolyline: (polylineId: string) => void; + onToggleSimulation: () => void; + onPlayPath: () => void; + learningData: LearningSummary; + polylines: Polyline[]; + isSimulationRunning: boolean; + isLoading: boolean; + learningPath: string[]; + bookmarks: string[]; + toggleBookmark: (id: string) => void; + resources: Resource[]; + onResourceClick: (resource: Resource) => void; + onStartTutorial: () => void; + agent: any; +} + +export const ControlPanel: React.FC = ({ + onSummarizeLearning, + onShowPolyline, + onToggleSimulation, + onPlayPath, + learningData, + polylines, + isSimulationRunning, + isLoading, + learningPath, + bookmarks, + toggleBookmark, + resources, + onResourceClick, + onStartTutorial, + agent +}) => { + const [showSummaryModal, setShowSummaryModal] = useState(false); + const [showPolylineModal, setShowPolylineModal] = useState(false); + const [showPolylineListModal, setShowPolylineListModal] = useState(false); + const [title, setTitle] = useState(''); + const [summary, setSummary] = useState(''); + const [selectedPolyline, setSelectedPolyline] = useState(null); + + const handleSummarySubmit = () => { + if (title.trim() && summary.trim()) { + onSummarizeLearning(title, summary); + setTitle(''); + setSummary(''); + setShowSummaryModal(false); + } + }; + + const handleShowPolyline = () => { + const activePolyline = polylines.find(p => p.isActive); + if (activePolyline) { + setSelectedPolyline(activePolyline); + setShowPolylineModal(true); + } + }; + + // Generate chart data for polyline visualization + const generateChartData = (polyline: Polyline) => { + if (polyline.module_scores && polyline.module_scores.length > 0) { + return polyline.module_scores.map((score, index) => ({ + x: index + 1, + y: score + })); + } + + const data = []; + for (let i = 1; i <= 18; i++) { + data.push({ + x: i, + y: 0.5 + (Math.random() - 0.5) * 0.1 + Math.sin(i * 0.5) * 0.05 + }); + } + return data; + }; + + const topicLegendItems = [ + "Pre training objectives", "Pre trained models", "Tutorial: Introduction to huggingface", + "Fine tuning LLM", "Instruction tuning", "Prompt based learning", + "Parameter efficient fine tuning", "Incontext Learning", "Prompting methods", + "Retrieval Methods", "Retrieval Augmented Generation", "Quantization", + "Mixture of Experts Model", "Agentic AI", "Multimodal LLMs", + "Vision Language Models", "Policy learning using DQN", "RLHF" + ]; + + const activePolyline = polylines.find(p => p.isActive); + + const generateHighLineData = () => { + return topicLegendItems.map((topic, i) => { + const res = resources.find(r => r.module === topic); + return { x: i + 1, y: res?.high_line || 0.8 }; + }); + }; + const highLineChartData = generateHighLineData(); + + + return ( +
+ {/* Header */} +
+
+

Control Center

+ +
+

Manage your neural sync and analysis

+
+ + {/* Profile Section */} +
+
+ + + + +
+ Agent Avatar +
+
+
+

STUDENT

+
+ + LVL {agent.level} + +
+
+ {agent.totalReward} pts + {(learningData.xp_earned ?? 0) > 0 && ( + + +{learningData.xp_earned} + + )} +
+ STUDY POINTS +
+
+
+
+ {/* Scrollable Content Area */} +
+ {/* Control Actions */} +
+ + + +
+ + + + + +
+ + +
+ + {/* Learning Stats / Insights */} + {(learningData.strengths.length > 0 || learningData.recommendations.length > 0 || learningData.ai_analysis) && ( +
+

Insights

+ + {learningData.ai_analysis && ( +
+
+ + AI feedback +
+

+ "{learningData.ai_analysis}" +

+
+ )} + +
+ {learningData.strengths.length > 0 && ( +
+ Strengths +
+ {learningData.strengths.slice(0, 2).map((strength, i) => ( + + {strength} + + ))} +
+
+ )} +
+
+ )} + + {/* Bookmarks Section */} +
+
+

+ + Bookmarks +

+ {bookmarks.length} +
+ +
+ {bookmarks.length > 0 ? ( + bookmarks.map(id => { + const resource = resources.find(r => r.id === id); + if (!resource) return null; + return ( +
+
onResourceClick(resource)} + > +

+ {resource.title} +

+

+ {resource.module || 'Resource'} +

+
+ +
+ ); + }) + ) : ( +

No bookmarks saved yet.

+ )} +
+
+ + {/* Learning Timeline */} +
+
+

Activity Log

+ {learningPath.length} items +
+ +
+
+ {learningData.activityLog && learningData.activityLog.length > 0 ? ( + learningData.activityLog.slice(0, 15).map((log) => ( +
+
+ {log.type === 'visit' && } + {log.type === 'summary' && } + {log.type === 'optimal' && } + {log.type === 'search' && } + {log.type === 'start' && } +
+
+
+ {log.timestamp} + {log.type === 'optimal' && AI Optimized} +
+ + {log.title} + +
+
+ )) + ) : ( +
+
+
+ Environment Ready + Initialized learning grid +
+
+ )} +
+
+
+
+ + {/* Summary Modal */} + {showSummaryModal && ( +
+
+
+

Summarize Learning

+ +
+ +
+
+ + setTitle(e.target.value)} + placeholder="e.g., Introduction to Transformers" + className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 outline-none transition-all placeholder:text-gray-400" + /> +
+ +
+ +