CoolFace
Apppublic

serhany/question_generation

sourceHugging Facecc-by-nc-4.0updated 2y agoView on Hugging Face
0likes
app.py238 linesDownload Raw Back to root
1import cohere2import numpy as np3from sentence_transformers import SentenceTransformer4from transformers import pipeline5from typing import List, Tuple6import os7import logging8import json9import gradio as gr10import pandas as pd11from datasets import load_dataset12import random13 14# Set up logging15logging.basicConfig(level=logging.INFO)16logger = logging.getLogger(__name__)17 18# Initialize Cohere client, SentenceTransformer model, and QA pipeline19co = cohere.Client(api_key=os.environ.get("COHERE_API_KEY"))20sentence_model = SentenceTransformer('all-MiniLM-L6-v2')21qa_pipeline = pipeline("question-answering", model="distilbert-base-cased-distilled-squad")22 23# Load the dataset24dataset = load_dataset("serhany/scaling-qa")25 26# Define sample inputs27samples = [28    {29        "context": "Albert Einstein is an Austrian scientist, who has completed his higher education in ETH Zurich in Zurich, Switzerland. He was later a faculty at Princeton University.",30        "answer": "Switzerland"31    },32    {33        "context": "The Eiffel Tower, located in Paris, France, is one of the most famous landmarks in the world. It was constructed in 1889 as the entrance arch to the 1889 World's Fair. The tower is 324 meters (1,063 ft) tall and is the tallest structure in Paris.",34        "answer": "Paris"35    },36    {37        "context": "The Great Wall of China is a series of fortifications and walls built across the historical northern borders of ancient Chinese states and Imperial China to protect against nomadic invasions. It is the largest man-made structure in the world, with a total length of more than 13,000 miles (21,000 kilometers).",38        "answer": "China"39    }40]41 42def generate_questions(context: str, answer: str) -> List[str]:43    try:44        response = co.chat(45            model="command-r",46            message=f"Based on this context: '{context}' and answer: '{answer}', generate 5 diverse questions which when asked to the context returns the answer.",47            response_format={48                "type": "json_object",49                "schema": {50                    "type": "object",51                    "required": ["question1", "question2", "question3", "question4", "question5"],52                    "properties": {53                        "question1": {"type": "string"},54                        "question2": {"type": "string"},55                        "question3": {"type": "string"},56                        "question4": {"type": "string"},57                        "question5": {"type": "string"}58                    }59                }60            }61        )62        63        json_response = response.text64        logger.info(f"Raw JSON response: {json_response}")65        66        parsed_response = json.loads(json_response)67        questions = [parsed_response[f"question{i}"] for i in range(1, 6)]68        return questions69    except Exception as e:70        logger.error(f"Error in generate_questions: {e}")71        return [f"Failed to generate question {i}" for i in range(1, 6)]72 73def calculate_structural_diversity(questions: List[str]) -> List[float]:74    def get_question_type(q):75        q = q.lower()76        if q.startswith('what'): return 177        elif q.startswith('why'): return 278        elif q.startswith('how'): return 379        elif q.startswith('when'): return 480        elif q.startswith('where'): return 581        else: return 082 83    lengths = [len(q.split()) for q in questions]84    types = [get_question_type(q) for q in questions]85    86    length_scores = [1 - (abs(l - np.mean(lengths)) / np.max(lengths)) for l in lengths]87    type_scores = [len(set(types)) / len(types) for _ in types]88    89    return [(l + t) / 2 for l, t in zip(length_scores, type_scores)]90 91def calculate_semantic_relevance(context: str, answer: str, questions: List[str]) -> List[float]:92    context_embedding = sentence_model.encode(context + " " + answer)93    question_embeddings = sentence_model.encode(questions)94    95    similarities = [np.dot(context_embedding, q_emb) / (np.linalg.norm(context_embedding) * np.linalg.norm(q_emb)) 96                    for q_emb in question_embeddings]97    98    return [(sim + 1) / 2 for sim in similarities]  # Normalize to 0-1 range99 100def check_answer_precision(context: str, questions: List[str], original_answer: str) -> Tuple[List[float], List[str]]:101    precision_scores = []102    generated_answers = []103    for question in questions:104        result = qa_pipeline(question=question, context=context)105        generated_answer = result['answer']106        generated_answers.append(generated_answer)107        answer_embedding = sentence_model.encode(original_answer)108        generated_embedding = sentence_model.encode(generated_answer)109        similarity = np.dot(answer_embedding, generated_embedding) / (np.linalg.norm(answer_embedding) * np.linalg.norm(generated_embedding))110        precision_scores.append((similarity + 1) / 2)  # Normalize to 0-1 range111    return precision_scores, generated_answers112 113def calculate_composite_scores(sd_scores: List[float], sr_scores: List[float], ap_scores: List[float]) -> List[float]:114    # Normalize other scores based on answer precision115    max_other_score = max(max(sd_scores), max(sr_scores))116    normalized_sd_scores = [sd * (ap / max_other_score) for sd, ap in zip(sd_scores, ap_scores)]117    normalized_sr_scores = [sr * (ap / max_other_score) for sr, ap in zip(sr_scores, ap_scores)]118    119    # Calculate composite scores with higher weight for answer precision120    return [0.6 * ap + 0.2 * sd + 0.2 * sr for ap, sd, sr in zip(ap_scores, normalized_sd_scores, normalized_sr_scores)]121 122def rank_questions_with_details(context: str, answer: str) -> Tuple[pd.DataFrame, List[pd.DataFrame], str]:123    questions = generate_questions(context, answer)124    125    sd_scores = calculate_structural_diversity(questions)126    sr_scores = calculate_semantic_relevance(context, answer, questions)127    ap_scores, generated_answers = check_answer_precision(context, questions, answer)128    129    composite_scores = calculate_composite_scores(sd_scores, sr_scores, ap_scores)130    131    # Create detailed scores dataframe132    detailed_scores = pd.DataFrame({133        'Question': questions,134        'Answer Precision': ap_scores,135        'Composite Score': composite_scores,136        'Structural Diversity': sd_scores,137        'Semantic Relevance': sr_scores,138        'Generated Answer': generated_answers139    })140    detailed_scores = detailed_scores.sort_values('Answer Precision', ascending=False).reset_index(drop=True)141    142    # Create separate ranking dataframes for each metric143    metrics = ['Answer Precision', 'Composite Score', 'Structural Diversity', 'Semantic Relevance']144    rankings = []145    146    for metric in metrics:147        df = pd.DataFrame({148            'Rank': range(1, 6),149            'Question': [questions[i] for i in np.argsort(detailed_scores[metric])[::-1]],150            f'{metric}': sorted(detailed_scores[metric], reverse=True)151        })152        if metric == 'Answer Precision':153            df['Generated Answer'] = [generated_answers[i] for i in np.argsort(detailed_scores[metric])[::-1]]154        rankings.append(df)155    156    best_question = detailed_scores.iloc[0]['Question']157    158    return detailed_scores, rankings, best_question159 160def gradio_interface(context: str, answer: str) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame, pd.DataFrame, pd.DataFrame, str]:161    detailed_scores, rankings, best_question = rank_questions_with_details(context, answer)162    return (163        detailed_scores,164        rankings[0],  # Answer Precision Ranking165        rankings[1],  # Composite Score Ranking166        rankings[2],  # Structural Diversity Ranking167        rankings[3],  # Semantic Relevance Ranking168        f"Best Question: {best_question}"169    )170 171def use_sample(sample_index: int) -> Tuple[str, str]:172    return samples[sample_index]["context"], samples[sample_index]["answer"]173 174def get_random_entry():175    # Get a random entry from the dataset176    random_index = random.randint(0, len(dataset['train']) - 1)177    entry = dataset['train'][random_index]178    return entry['context'], entry['answer']179 180# Create Gradio interface with improved layout and sample buttons181with gr.Blocks(theme=gr.themes.Default()) as iface:182    gr.Markdown("# Question Generator and Ranker")183    gr.Markdown("Enter a context and an answer to generate and rank questions, use one of the sample inputs, or get a random entry from the dataset.")184    185    with gr.Row():186        with gr.Column(scale=1):187            context_input = gr.Textbox(lines=5, label="Context")188            answer_input = gr.Textbox(lines=2, label="Answer")189            submit_button = gr.Button("Generate Questions")190            191            with gr.Row():192                sample_buttons = [gr.Button(f"Sample {i+1}") for i in range(3)]193                random_button = gr.Button("Random Dataset Entry")194        195        with gr.Column(scale=2):196            best_question_output = gr.Textbox(label="Best Question")197            detailed_scores_output = gr.DataFrame(label="Detailed Scores")198    199    with gr.Row():200        with gr.Column():201            answer_precision_ranking_output = gr.DataFrame(label="Answer Precision Ranking")202        with gr.Column():203            composite_ranking_output = gr.DataFrame(label="Composite Score Ranking")204    205    with gr.Row():206        with gr.Column():207            structural_diversity_ranking_output = gr.DataFrame(label="Structural Diversity Ranking")208        with gr.Column():209            semantic_relevance_ranking_output = gr.DataFrame(label="Semantic Relevance Ranking")210 211    submit_button.click(212        fn=gradio_interface,213        inputs=[context_input, answer_input],214        outputs=[215            detailed_scores_output,216            answer_precision_ranking_output,217            composite_ranking_output,218            structural_diversity_ranking_output,219            semantic_relevance_ranking_output,220            best_question_output221        ]222    )223 224    # Set up sample button functionality225    for i, button in enumerate(sample_buttons):226        button.click(227            fn=lambda i=i: use_sample(i),228            outputs=[context_input, answer_input]229        )230 231    # Set up random button functionality232    random_button.click(233        fn=get_random_entry,234        outputs=[context_input, answer_input]235    )236 237# Launch the app238iface.launch()