File size: 10,173 Bytes
698e6dd
586650a
 
 
 
 
698e6dd
34dc7a3
 
 
 
698e6dd
34dc7a3
586650a
 
 
34dc7a3
 
586650a
34dc7a3
 
 
 
 
586650a
 
 
 
 
 
34dc7a3
 
 
 
 
586650a
 
 
 
34dc7a3
586650a
 
34dc7a3
 
 
 
 
 
 
 
 
 
 
586650a
 
 
34dc7a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
586650a
 
 
34dc7a3
586650a
34dc7a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
586650a
34dc7a3
 
 
586650a
34dc7a3
 
586650a
34dc7a3
 
 
 
 
 
 
586650a
 
 
 
34dc7a3
 
586650a
 
 
 
34dc7a3
586650a
 
 
 
34dc7a3
 
 
586650a
 
 
34dc7a3
 
586650a
 
 
34dc7a3
 
586650a
 
 
 
 
34dc7a3
 
 
 
 
 
586650a
34dc7a3
586650a
34dc7a3
586650a
 
 
 
 
34dc7a3
586650a
7dd5f47
586650a
34dc7a3
586650a
 
 
 
34dc7a3
586650a
34dc7a3
 
 
586650a
 
 
34dc7a3
 
586650a
 
 
 
 
 
 
 
34dc7a3
 
 
586650a
34dc7a3
 
586650a
34dc7a3
 
 
 
 
 
 
 
 
 
 
586650a
 
 
34dc7a3
 
586650a
34dc7a3
 
 
 
 
 
 
 
 
586650a
34dc7a3
 
586650a
 
34dc7a3
 
586650a
34dc7a3
586650a
 
 
34dc7a3
 
 
 
 
 
586650a
34dc7a3
 
 
 
 
 
586650a
34dc7a3
 
586650a
34dc7a3
 
586650a
34dc7a3
 
 
 
 
 
 
 
 
 
 
586650a
 
 
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
import gradio as gr
import pandas as pd
import tempfile
import ast
import sys
import os

sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from grewtse.pipeline import GrewTSEPipe
from grewtse.evaluators import GrewTSEvaluator
from grewtse.visualise import GrewTSEVisualiser

grewtse = GrewTSEPipe()

def parse_treebank(path: str, treebank_selection: str) -> pd.DataFrame:
    if treebank_selection == "None":
        parsed_treebank = grewtse.parse_treebank(path)
        # treebank_path = path
    else:
        treebank_selection = f"./datasets/{treebank_selection}"
        parsed_treebank = grewtse.parse_treebank(treebank_selection)
        # treebank_path = treebank_selection

    return grewtse.get_morphological_features().tail()


def to_masked_dataset(query, node) -> pd.DataFrame:
    df = grewtse.generate_masked_dataset(query, node)
    return df

def to_prompt_dataset(query, node) -> pd.DataFrame:
    df = grewtse.generate_prompt_dataset(query, node)
    return df


def safe_str_to_dict(s):
    try:
        return ast.literal_eval(s)
    except (ValueError, SyntaxError):
        return None


def truncate_text(text, max_len=50):
    """
    Truncate a string to max_len characters and append '...' if it was longer.
    """
    if not isinstance(text, str):
        return text  # Keep non-string values unchanged
    return text[:max_len] + "..." if len(text) > max_len else text

def generate_minimal_pairs(query: str, node: str, alt_features: str, task_type: str):
    if not grewtse.is_treebank_parsed():
        raise ValueError("Please parse a treebank first.")

    # determine whether an alternative LI should be found
    alt_features_as_dict = safe_str_to_dict(alt_features)
    if alt_features_as_dict is None:
        raise Exception("Invalid features provided.")

    has_leading_whitespace = False
    is_encoder = False
    masked_or_prompt_dataset = None
    if task_type.lower() == "masked":
        # mask the target word in the sentence
        masked_or_prompt_df = to_masked_dataset(query, node)
        has_leading_whitespace = False
        is_encoder = True
    elif task_type.lower() == "prompt":
        # create prompts from each sentence (i.e. cut them off right at the target word)
        masked_or_prompt_dataset = to_prompt_dataset(query, node)
        has_leading_whitespace = True
    else:
        raise Exception("Invalid task type.")

    full_dataset = grewtse.generate_minimal_pair_dataset(alt_features_as_dict, {},
                                                         ood_pairs=None, has_leading_whitespace=has_leading_whitespace)

    # save to a temporary CSV file
    temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".csv")
    full_dataset.to_csv(temp_file.name, index=False)

    if is_encoder:
        dataset_for_vis = full_dataset[["masked_text", "form_grammatical", "form_ungrammatical"]]
        dataset_for_vis["masked_text"] = dataset_for_vis["masked_text"].apply(truncate_text)
    else:
        dataset_for_vis = full_dataset[["prompt_text", "form_grammatical", "form_ungrammatical"]]
        dataset_for_vis["prompt_text"] = dataset_for_vis["prompt_text"].apply(truncate_text)


    num_exceptions = grewtse.get_num_exceptions()
    num_targets_parsed = len(masked_or_prompt_df)
    num_success = len(full_dataset)

    exceptions_info = f"{num_targets_parsed+num_exceptions} targets identified and turned into masks/prompts. {num_exceptions} of these could not be used due to treebank structure issues. After searching for minimal pairs, a total of <br>{num_success} minimal-pair syntactic tests</br> were successfully generated."
    gr.Info(exceptions_info, duration=60, title="Grew-TSE Results")

    return dataset_for_vis, temp_file.name


def evaluate_model(
    model_repo: str,
    task_type: str
):
    if not grewtse.are_minimal_pairs_generated():
        raise ValueError(
            "Please parse a treebank, mask a dataset and generate minimal pairs first."
        )

    g_eval = GrewTSEvaluator()
    g_vis = GrewTSEVisualiser()

    model_type = "encoder" if task_type.lower() == "masked" else "decoder"
    mp_with_eval_dataset = g_eval.evaluate_model(grewtse.get_minimal_pair_dataset(), model_repo, model_type)
    metrics = g_eval.get_all_metrics()
    metrics = pd.DataFrame(metrics.items(), columns=["Metric", "Value"])
    print("===METRICS===")
    print(metrics)
    print("----")

    # save to a temporary CSV file
    temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".csv")
    mp_with_eval_dataset.to_csv(temp_file.name, index=False)
    return metrics, temp_file.name


def show_df():
    return gr.update(visible=True)


with gr.Blocks(theme=gr.themes.Ocean()) as demo:
    is_treebank_parse_success = False

    with gr.Row():
        gr.Markdown(
            "# GREW-TSE: A Pipeline for Query-based Targeted Syntactic Evaluation"
        )

    with gr.Row():
        with gr.Column():
            gr.Markdown(
                """
            #### Load a Treebank
            You can begin by loading up a particular treebank that you'd like to work with.<br>
            You can either select a treebank from the pre-loaded options below, or upload your own.<br>
            """
            )

        with gr.Column():
            with gr.Tabs():
                with gr.TabItem("Choose Treebank"):
                    treebank_selection = gr.Dropdown(
                        choices=[
                            "None",
                            "en/English-EWT-UD-Treebank.conllu",
                            "Polish-Test-Treebank.conllu",
                            "Spanish-Test-SM.conllu",
                        ],
                        label="Select a treebank",
                        value="en/English-EWT-UD-Treebank.conllu",
                    )

                with gr.TabItem("Upload Your Own"):
                    gr.Markdown("## Upload a .conllu File")
                    file_input = gr.File(
                        label="Upload .conllu file",
                        file_types=[".conllu"],
                        type="filepath",
                    )
            parse_file_button = gr.Button("Parse Treebank", size="sm", scale=0)

            morph_table = gr.Dataframe(interactive=False, visible=False)

    parse_file_button.click(
        fn=parse_treebank,
        inputs=[file_input, treebank_selection],
        outputs=[morph_table],
    )
    parse_file_button.click(fn=show_df, outputs=morph_table)

    gr.Markdown("## Isolate A Syntactic Phenomenon")

    with gr.Row():
        with gr.Column():
            gr.Markdown(
                """
                **GREW (Graph Rewriting for Universal Dependencies)** is a query and transformation language used to search within and manipulate dependency treebanks. A GREW query allows linguists and NLP researchers to find specific syntactic patterns in parsed linguistic data (such as Universal Dependencies treebanks).
                Queries are expressed as graph constraints using a concise pattern-matching syntax.

                #### Example
                The following short GREW query will find target any verbs. Try it with one of the sample treebanks above.
                Make sure to include the variable V as the target that we're trying to isolate.

                ```grew
                pattern {
                    V [upos=\"VERB\"];
                }
                ```
            """
            )
        with gr.Column():
            query_input = gr.Textbox(
                label="GREW Query",
                lines=5,
                placeholder="Enter your GREW query here...",
                value="pattern { V [upos=VERB, Number=Sing]; }"
            )
            node_input = gr.Textbox(
                label="Target",
                placeholder="The variable in your GREW query to isolate, e.g., N",
                value="V",
            )
            feature_input = gr.Textbox(
                label="Enter Alternative Feature Values for Minimal Pair as a Dictionary",
                placeholder='e.g. {"case": "Acc", "number": "Sing"}',
                value='{"number": "Plur"}',
                lines=3,
            )
            task_type = gr.Dropdown(
                choices=[
                    "Masked",
                    "Prompt",
                ],
                label="Select whether you want masked- or prompt-based tests.",
                value="Masked"
            )
            run_button = gr.Button("Run Query", size="sm", scale=0)

            output_table = gr.Dataframe(label="Output Table", visible=False)
            download_file = gr.File(label="Download CSV")
    run_button.click(
        fn=generate_minimal_pairs,
        inputs=[query_input, node_input, feature_input, task_type],
        outputs=[output_table, download_file],
    )
    run_button.click(fn=show_df, outputs=output_table)

    with gr.Row():
        with gr.Column():
            gr.Markdown(
                """
            ## Evaluate A Model (BETA Version)
            You can evaluate models trained either for MLM or NTP tasks that are available on the Hugging Face platform.
            """
            )
        with gr.Column():
            repository_input = gr.Textbox(
                label="Model Repository",
                lines=1,
                placeholder="Enter the model repository here...",
                value="google-bert/bert-base-multilingual-cased",
            )

            with gr.Column():
                evaluate_button = gr.Button("Evaluate Model", size="sm", scale=0)

            mp_with_eval_output_dataset = gr.Dataframe(label="Output Table", visible=False)
            mp_with_eval_output_download = gr.File(label="Download CSV")

            evaluate_button.click(
                fn=evaluate_model,
                inputs=[
                    repository_input,
                    task_type
                ],
                outputs=[
                    gr.DataFrame(),
                    mp_with_eval_output_download,
                ],
            )

if __name__ == "__main__":
    demo.launch(share=True)