File size: 4,280 Bytes
7592c61
 
 
 
 
43197d9
7592c61
43197d9
 
 
 
 
 
 
 
 
0c9965a
43197d9
 
0c9965a
43197d9
 
 
 
 
 
0c9965a
43197d9
 
0c9965a
9dbd859
 
 
 
 
 
 
71bcad8
7592c61
 
9dbd859
71bcad8
43197d9
 
0c9965a
43197d9
0c9965a
 
 
 
 
 
 
 
 
 
43197d9
0c9965a
 
 
 
 
 
 
 
 
 
 
 
 
71bcad8
43197d9
0c9965a
43197d9
0c9965a
 
 
 
 
 
 
9dbd859
0c9965a
7592c61
0c9965a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43197d9
 
7592c61
43197d9
9dbd859
0c9965a
43197d9
 
0c9965a
43197d9
 
 
 
 
 
9dbd859
0c9965a
9dbd859
 
43197d9
 
 
 
 
 
 
0c9965a
43197d9
 
 
 
 
9dbd859
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import gradio as gr
from transformers import AutoModelForSequenceClassification, AutoTokenizer
import torch
import numpy as np

# -------------------------------
# Load model and tokenizer
# -------------------------------
MODEL_NAME = "JacobLinCool/IELTS_essay_scoring_safetensors"

model = AutoModelForSequenceClassification.from_pretrained(MODEL_NAME)
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)

model.eval()

# -------------------------------
# Scoring + Feedback function
# -------------------------------
def score_essay(essay: str):
    if not essay.strip():
        return {
            "Task Achievement": 0.0,
            "Coherence & Cohesion": 0.0,
            "Vocabulary": 0.0,
            "Grammar": 0.0,
            "Overall": 0.0,
            "Feedback": "Please write an essay to get a score."
        }

    # Tokenize input
    inputs = tokenizer(
        essay,
        return_tensors="pt",
        truncation=True,
        max_length=512
    )

    # Model inference
    with torch.no_grad():
        outputs = model(**inputs)

    preds = outputs.logits.squeeze().cpu().numpy()

    # -------------------------------
    # Refined piecewise scaling
    # -------------------------------
    scores = preds.copy()
    for i in range(len(scores)):
        if scores[i] < 4.0:       # very weak essays
            scores[i] = scores[i]*1.25 + 0.3
        elif scores[i] < 6.0:     # medium essays
            scores[i] = scores[i]*1.1 + 0.2
        elif scores[i] < 8.0:     # good essays
            scores[i] = scores[i]*1.05 + 0.1
        else:                      # very strong essays
            scores[i] = scores[i]  # keep unchanged

    # -------------------------------
    # Word count bonus
    # -------------------------------
    wc = len(essay.split())
    if wc >= 250:
        scores += 0.3
    elif wc >= 200:
        scores += 0.15

    # -------------------------------
    # Clamp and round to 0.5
    # -------------------------------
    scores = np.clip(scores, 0, 9)
    rounded = np.round(scores * 2) / 2

    ta, cc, vocab, gram, overall_dummy = rounded  # we will compute weighted overall

    # -------------------------------
    # Weighted overall band
    # -------------------------------
    overall = 0.3*ta + 0.25*cc + 0.2*vocab + 0.25*gram
    overall = np.clip(overall, 0, 9)
    overall = np.round(overall * 2) / 2
    rounded[-1] = overall  # replace last element

    labels = ["Task Achievement", "Coherence & Cohesion", "Vocabulary", "Grammar", "Overall"]

    # -------------------------------
    # Dynamic mini feedback
    # -------------------------------
    feedback_lines = []

    if ta < 5:
        feedback_lines.append("Task: Ideas are underdeveloped; give examples and elaboration.")
    if cc < 5:
        feedback_lines.append("Coherence: Improve transitions; use connectors like 'however', 'moreover'.")
    if vocab < 5:
        feedback_lines.append("Vocabulary: Use a wider range of words and avoid repetition.")
    if gram < 5:
        feedback_lines.append("Grammar: Check articles, plurals, tenses, and sentence structures.")
    if overall >= 5:
        feedback_lines.append("Overall: Good attempt! Refine grammar and vocabulary to increase your band.")

    feedback = "\n".join(feedback_lines[:4])  # max 4 lines

    result = {label: float(score) for label, score in zip(labels, rounded)}
    result["Feedback"] = feedback
    return result

# -------------------------------
# Gradio UI
# -------------------------------
with gr.Blocks() as demo:
    gr.Markdown("## 📝 Refined Automated IELTS Writing Scorer")
    gr.Markdown(
        "Paste your IELTS Task 2 essay below. "
        "The system estimates band scores with mini feedback."
    )

    essay_input = gr.Textbox(
        lines=12,
        placeholder="Paste your IELTS essay here..."
    )

    score_output = gr.JSON(label="Estimated IELTS Band Scores + Feedback")

    submit_btn = gr.Button("Score Essay")
    submit_btn.click(
        fn=score_essay,
        inputs=essay_input,
        outputs=score_output
    )

    gr.Markdown(
        "⚠️ Note: AI-based estimator; not an official IELTS score."
    )

# -------------------------------
# Launch app
# -------------------------------
demo.launch()