Mind2Web Dataset and Framework

repository·main·Indexed 21 days ago

https://github.com/osu-nlp-group/mind2web

A dataset and framework for training and evaluating generalist web agents. Mind2Web provides real-world web tasks with complex action sequences, HTML snapshots, and traces. It includes tools for fine-tuning and evaluating candidate generation models (using DeBERTa) and action prediction models (using T5 or LLMs like GPT-3.5-turbo), along with utilities for DOM tree representation and multi-choice dataset formatting.

Tokens
16.9K
Snippets
39
Records
59
Agent score
76%

What's inside Mind2Web

  1. Implement a custom model for evaluation

    main
    You can implement and evaluate your own model by ensuring it exposes a generate function that accepts a prompt and returns generated outputs. The required interface can be found in action_prediction/metric.py at line 328.
  2. Understand the Mind2Web dataset structure

    main

    The dataset is organized into training and various test splits (Cross Task, Cross Website, and Cross Domain). After cloning the training data and unzipping the test data, the directory structure should follow this pattern:

    Mind2Web
    ├── data
    │   ├── train
    │   │   └── train_*.json
    │   ├── test_task
    │   │   └── test_task_*.json
    │   ├── test_website
    │   │   └── test_website_*.json
    │   └── test_domain
    │       └── test_domain_*.json
    └── ...
  3. Evaluate Action Prediction with LLMs

    main

    To evaluate using Large Language Models (LLMs), you need the dataset, candidate generation results, and an OPENAI_API_KEY. The evaluation uses a multi-choice QA formulation where the model selects the target element from a list of candidates.

    Setup

    1. Set your OpenAI API key in the environment: export OPENAI_API_KEY={YOUR_API_KEY}
    2. Use the 3-shot prompt located at src/action_prediction/llm_prompt.json.

    Execution

    Run the evaluation script using the following command:

    export OPENAI_API_KEY={YOUR_API_KEY}
    python action_prediction/evaluate_llm.py \
      +output_path={OUTPUT_DIR} \
      +llm_prompt=action_prediction/llm_prompt.json \
      +llm=gpt-3.5-turbo \
      +llm_rate_limit=60 \
      +top_k=50
  4. Evaluate Action Prediction models

    main

    To evaluate trained action prediction models (such as flan-t5-base, flan-t5-large, or flan-t5-xl), you must first configure the parameters in action_prediction/conf/config.yaml. The project uses Hydra for configuration management.

    Configuration Keys

    In action_prediction/conf/config.yaml, ensure the following are set:

    • data.data_path: The directory where you cloned the Mind2Web dataset.
    • data.score_file: The path to the downloaded pickle file containing candidate generation model outputs.
    • hydra.run.dir: The working directory for log files.

    Execution

    Run the evaluation script using the following command structure:

    python action_prediction/evaluate.py \
      +model_path={MODEL_PATH_OR_NAME} \
      model=flan-t5-large \
      +output_path={OUTPUT_PATH} \
      +top_k=50
  5. Access the Mind2Web dataset

    main

    Mind2Web provides a training set available directly on Huggingface and a protected test set to prevent data contamination.

    1. Training Set: Clone the repository from Huggingface using git.
    2. Test Set: Download the test split zip files from Huggingface. Unzip them into the same directory as your training data using the password mind2web.

    Important: Do not redistribute unzipped data files online. The dataset contains canary GUIDs to detect unauthorized use in training corpora.

    git clone git@hf.co:datasets/osunlp/Mind2Web
  6. Fine-tune Action Prediction models

    main

    Fine-tuning is performed using a seq2seq T5 model implementation based on Huggingface Transformers's Seq2SeqTrainer. Configuration is managed via action_prediction/conf/config.yaml. Checkpoints are saved in the workdir defined by Hydra.

    Use torchrun for distributed training. Example for 4 GPUs:

    torchrun --nproc-per-node 4 --master_port=8004\
        action_prediction/train.py\
        model=flan-t5-large\
        train.per_device_train_batch_size=8\
        train.gradient_accumulation_steps=1\
        train.fsdp=True\
        train.num_gpus=4\
        train.epoch=5\
        run_id="full"
  7. Access the Raw Dump with Full Traces and Snapshots

    main

    The raw dump contains high-fidelity data including Playwright trace files, network traffic (.har), video recordings, and various page snapshots (.mhtml).

    Due to its size, the raw dump is shared via Globus. You can access the collection via the following link: https://app.globus.org/file-manager?origin_id=32e6b738-a0b0-47f8-b475-26bf1c5ebf19

    Key files in the raw dump structure:

    • trace.zip: Playwright trace file (inspect via trace viewer).
    • session.har.zip: Network traffic for replaying.
    • videos/: WebM recordings of the annotation process.
    • dom_content.json: DOM snapshots extracted via DOMSnapshot.captureSnapshot.
    • screenshot.json: Page screenshots stored as base64 strings.
    • {action_id}_before/after.mhtml: Standalone MHTML snapshots of the page state.
  8. Normalize action nodes using fuzzy matching

    main

    Because different annotators might select slightly different nodes for the same logical action, the project provides heuristics to normalize the selection:

    1. move_up_annotation: Finds the closest clickable ancestor that contains the target interaction position.
    2. move_down_annotation: Finds all descendants within the target bounding box.
    3. set_alternative: Sets data_pw_testid_buckeye_alt on the normalized ancestor and data_pw_testid_buckeye_alt_fuzzy on the candidate descendants to allow for flexible matching.
  9. Reconstruct HTML from DOM Snapshot

    main

    The project uses a build_dom_tree function to reconstruct a full HTML tree from a DOMSnapshot.NodeTreeSnapshot object. This is necessary because the raw data is stored in a compressed/indexed format (nodes, layout, and strings) rather than raw HTML.

    Key steps in reconstruction:

    1. Map indices to actual string values using str_mapping.
    2. Iterate through nodes to create lxml.etree.Element objects.
    3. Apply attributes like bounding_box_rect, text_value, input_value, and is_clickable.
    4. Recursively handle iframes via contentDocumentIndex.
    5. Reconstruct the parent-child hierarchy using parentIndex.
  10. Identify and clean empty or invisible nodes

    main

    The processing pipeline includes logic to prune the DOM tree of nodes that do not contribute to user interaction or visibility.

    • is_visible: Checks if a node's bounding_box_rect is valid (non-negative coordinates and positive dimensions).
    • is_empty: Determines if a node has no text, no meaningful attributes (from salient_attributes), and no children.
    • Special Handling: Elements like a, button, select, option, and input are treated as visible if they contain text, even if their bounding box suggests otherwise.
    • clean_empty_node: Iterates through the tree in reverse to safely remove nodes identified as empty or invisible.
  11. Evaluate action prediction models via CLI

    main

    The src/action_prediction/evaluate.py script serves as a CLI entrypoint for evaluating action prediction models using the Hydra configuration framework. It supports both seq2seq and lm (causal language model) architectures and can perform evaluation in either multichoice or generation modes.

    To run the evaluation, you must provide a Hydra configuration file (located in conf/config.yaml by default) that defines the model path, data paths, and evaluation parameters. The script uses AutoModel from the Transformers library to load models and ActionEvaluator classes to compute metrics.

    Key configuration requirements:

    • model_path: Path to the model weights.
    • model.arch: Either seq2seq or lm.
    • model.mode: Either multichoice or generation mode.
    • data.data_path: Path to the Mind2Web dataset.
    • data.test_split_files: A dictionary mapping test keys to their respective split files.
    • lm_template: (Required for lm architecture) A JSON file containing the language model template.
    • top_k: The number of top candidates to consider during evaluation.
    python src/action_prediction/evaluate.py --config-name your_config_name
  12. Extract and prune candidate nodes for training data

    main

    To prepare high-quality training data, you can use get_candidates_for_actions to transform raw annotations into a structured format containing positive and negative candidates, along with their HTML representations.

    This process involves:

    1. Cleaning the tree via clean_tree(): Removes non-salient attributes (keeping only alt, aria_label, role, etc.) and cleans text/SVG icon classes.
    2. Pruning the tree via prune_tree(): For each candidate, it keeps only the candidate node, its ancestors, a limited number of descendants (controlled by max_depth and max_children), and immediate siblings. This reduces noise and token count.
    3. Generating representations: It creates ancestor_repr (the pruned tree up to the node) and subtree_repr (the node's own subtree) using get_tree_repr().

    Output Format: The function returns a list of dictionaries for each action, containing pos_candidates (those matching the action_uid via fuzzy matching) and neg_candidates (others).

    def get_candidates_for_actions(x):
        results = []
        action_seq = [a["action_repr"] for a in x[1]]
        for idx, a in enumerate(x[1]):
            target_uid = a["uid"]
            dom_tree = etree.fromstring(a["raw"])
            candidates = get_candidates(dom_tree, target_uid)
            all_candidate_ids =  [c["attributes"]["backend_node_id"] for c in candidates]
            dom_tree = clean_tree(dom_tree, all_candidate_ids)
            for c in candidates:
                node_tree = prune_tree(dom_tree, [c["attributes"]["backend_node_id"]])
                c_node = node_tree.xpath("//*[@backend_node_id]")[0]
                if c_node.getparent() is not None:
                    c_node.getparent().remove(c_node)
                    c["ancestor_repr"] = get_tree_repr(node_tree)
                else:
                    c["ancestor_repr"] = ""
                c["subtree_repr"] = get_tree_repr(c_node)
            pos_candidates = [c for c in candidates if c["attributes"].get("data_pw_testid_buckeye_alt_fuzzy", "") == target_uid]
            neg_candidates = [c for c in candidates if c["attributes"].get("data_pw_testid_buckeye_alt_fuzzy", "") != target_uid]
            results.append({
                "original_task": x[0]["task"],
                "confirmed_task": x[0]["confirmed_task"],
                "action_uid": a["uid"],
                "annotation_id": x[0]["annotation_id"],
                "annotator_id": x[0]["annotator_id"],
                "session_id": x[0]["session_id"],
                "website": x[0]["website"],
                "pos_candidates": pos_candidates,
                "neg_candidates": neg_candidates,
                "raw": a["raw"].decode("utf8"),
                "cleaned_html": etree.tostring(dom_tree).decode("utf8"),
                "previous_actions": action_seq[:idx],
                "current_action": action_seq[idx]
            })
        return (x[0], results)