{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"},{"sourceId":14453188,"sourceType":"datasetVersion","datasetId":9231578},{"sourceId":14454181,"sourceType":"datasetVersion","datasetId":9231883},{"sourceId":14456231,"sourceType":"datasetVersion","datasetId":9233530},{"sourceId":608797,"sourceType":"modelInstanceVersion","modelInstanceId":457027,"modelId":456721}],"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install --no-index --find-links /kaggle/input/wheels/kaggle/working/wheels --no-deps transformers==4.57.3 peft==0.18.1 accelerate==1.12.0 qwen-vl-utils tokenizers safetensors huggingface_hub numpy packaging regex pyyaml requests tqdm filelock fsspec typing_extensions hf_xet charset_normalizer idna urllib3 certifi psutil pillow av\n!pip install --no-index --find-links /kaggle/input/wheels/kaggle/working/wheels --upgrade --force-reinstall --no-deps tokenizers==0.22.2\n!pip install --no-index --find-links /kaggle/input/wheels/kaggle/working/wheels --upgrade --force-reinstall --no-deps \"protobuf==4.25.3\"\n\n# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport torch\nfrom transformers import AutoProcessor, AutoModelForVision2Seq\nfrom peft import PeftModel\nfrom PIL import Image\nfrom tqdm import tqdm\nimport sys,transformers\n\nprint(\"python:\", sys.version)\nprint(\"transformers:\", transformers.__version__)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\ndef predict():\n    # Configuration\n    if torch.cuda.is_available():\n        print(f\"CUDA is available. Using GPU: {torch.cuda.get_device_name(0)}\")\n\n    base_model_id = \"/kaggle/input/qwen-3-vl/transformers/4b-instruct/1\" # Match the training model\n    adapter_path = \"/kaggle/input/finetune/qwen-vl-biomass-finetuned\"  # Path where train.py saved the model\n    test_csv = \"/kaggle/input/csiro-biomass\" # Submission csv usually contains IDs we need to predict\n    # Note: sample_submission might not have image paths or other metadata. \n    # We might need to join with test.csv or a similar file if available.\n    # The user provided structure shows csiro-biomass/test.csv and csiro-biomass/test/ folder.\n    # Let's check csiro-biomass/test.csv content first to be sure how to map sample_id to image.\n    \n    # Load test.csv which contains sample_id, image_path, target_name\n    test_params_df = pd.read_csv(\"/kaggle/input/csiro-biomass/test.csv\")\n    \n    # Load Model and Adapter\n    try:\n        model = AutoModelForVision2Seq.from_pretrained(\n            base_model_id,\n            device_map=\"auto\",\n            torch_dtype=torch.float16,\n            trust_remote_code=True\n        )\n    except Exception as e:\n        print(f\"Error loading model: {e}\")\n        raise e\n\n    # Load LoRA adapters\n    model = PeftModel.from_pretrained(model, adapter_path)\n    model.eval()\n    \n    processor = AutoProcessor.from_pretrained(adapter_path, trust_remote_code=True)\n    \n    all_target_names_ref = ['Dry_Clover_g', 'Dry_Dead_g', 'Dry_Green_g', 'Dry_Total_g', 'GDM_g']\n    \n    # Group test items by image to optimize inference (process each image once)\n    # test.csv has: sample_id, image_path, target_name\n    grouped_images = test_params_df.groupby('image_path')\n    \n    results_map = {} # sample_id -> prediction\n    \n    for image_rel_path, group_df in tqdm(grouped_images, desc=\"Inferencing\"):\n        # Image path handling\n        # Assuming image_rel_path is like \"test/IDxxxx.jpg\"\n        image_path = os.path.join(\"/kaggle/input/csiro-biomass\", image_rel_path)\n        \n        # Collecting requested targets for this image\n        current_targets_list = group_df['target_name'].unique().tolist()\n        \n        # Sort targets to prefer the training order\n        sorted_targets = [t for t in all_target_names_ref if t in current_targets_list]\n        sorted_targets += [t for t in current_targets_list if t not in all_target_names_ref] # Append leftovers\n        \n        targets_str = \", \".join(sorted_targets)\n        \n        # Try to load image\n        try:\n            if not os.path.exists(image_path):\n                 print(f\"Warning: Image not found {image_path}\")\n                 for _, row in group_df.iterrows():\n                     results_map[row['sample_id']] = 0.0\n                 continue\n                 \n            image = Image.open(image_path).convert(\"RGB\")\n        except:\n             print(f\"Warning: Failed to open image {image_path}\")\n             for _, row in group_df.iterrows():\n                 results_map[row['sample_id']] = 0.0\n             continue\n\n        # Prepare Prompt\n        system_prompt = (\n            \"You are an expert agronomist specialized in remote sensing and biomass estimation. \"\n            f\"Analyze the pasture image features (color, texture, density) to predict the following biomass metrics: {targets_str}. \"\n            \"Provide the output as a list of 'Metric: Value' pairs.\"\n        )\n\n        messages = [\n            {\"role\": \"system\", \"content\": [{\"type\": \"text\", \"text\": system_prompt}]},\n            {\n                \"role\": \"user\",\n                \"content\": [\n                    {\"type\": \"image\", \"image\": image},\n                    {\"type\": \"text\", \"text\": f\"Please predict the values for {targets_str} based on this image.\"},\n                ],\n            }\n        ]\n        \n        text = processor.apply_chat_template(\n            messages, tokenize=False, add_generation_prompt=True\n        )\n        \n        inputs = processor(\n            text=[text],\n            images=[image],\n            padding=True,\n            return_tensors=\"pt\",\n        )\n        inputs = {k: v.to(model.device) for k, v in inputs.items()}\n\n        # Generate (Max tokens slightly increased to accommodate list of 5 values)\n        with torch.no_grad():\n            generated_ids = model.generate(**inputs, max_new_tokens=128)\n\n            \n        generated_ids_trimmed = [\n            out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs[\"input_ids\"], generated_ids)\n        ]\n        output_text = processor.batch_decode(\n            generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False\n        )[0]\n        \n        # Parse output: Expecting multiple lines \"Metric: Value\"\n        img_preds = {}\n        for line in output_text.split('\\n'):\n            line = line.strip()\n            if ':' in line:\n                parts = line.split(':', 1)\n                k = parts[0].strip()\n                try:\n                    v = float(parts[1].strip())\n                    img_preds[k] = v\n                except ValueError:\n                    continue\n        \n        # Fill results for this group\n        for _, row in group_df.iterrows():\n            tgt_name = row['target_name']\n            s_id = row['sample_id']\n            # Get prediction or default to 0.0\n            val = img_preds.get(tgt_name, 0.0)\n            results_map[s_id] = val\n\n    # Create submission file from results\n    # We reconstruct a DataFrame to save as CSV\n    submission_data = [{'sample_id': k, 'target': v} for k, v in results_map.items()]\n    submission_df = pd.DataFrame(submission_data)\n    \n    # Optional: reorder if we want to match test.csv order\n    # test_params_df order might be important\n    # Let's map values back to test_params_df directly to keep order\n    test_params_df['target'] = test_params_df['sample_id'].map(results_map)\n    final_submission = test_params_df[['sample_id', 'target']]\n\n    final_submission.to_csv(\"submission.csv\", index=False)\n    print(\"Submission saved to submission.csv\")\n\nif __name__ == \"__main__\":\n    predict()\n\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-10T16:52:31.910551Z","iopub.execute_input":"2026-01-10T16:52:31.910822Z","iopub.status.idle":"2026-01-10T16:54:41.357881Z","shell.execute_reply.started":"2026-01-10T16:52:31.910800Z","shell.execute_reply":"2026-01-10T16:54:41.357111Z"}},"outputs":[],"execution_count":null}]}