Views
No views yet
1mkdir llm4chem
2cd llm4chem
3git clone https://github.com/deepseek-ai/DeepSeek-OCR-2.git
4git clone https://github.com/HaCTang/ChemSeek-OCR.git1conda create -n chemseek-ocr python=3.12.9 -y
2conda activate chemseek-ocr1cd DeepSeek-OCR-2
2pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu118
3pip install vllm-0.8.5+cu118-cp38-abi3-manylinux1_x86_64.whl
4pip install -r requirements.txt
5pip install flash-attn==2.7.3 --no-build-isolation
6pip install peft accelerate wandb
7pip install matplotlib albumentations opencv-python rdkit SmilesPE pandas1export HF_HOME=$PWD/hf_cache
2export TRANSFORMERS_CACHE=$PWD/hf_cache
3export HF_DATASETS_CACHE=$PWD/hf_cache
4export HF_HUB_DISABLE_XET=11from transformers import AutoModel, AutoTokenizer
2import torch
3import os
4os.environ["CUDA_VISIBLE_DEVICES"] = '1'
5model_name = 'deepseek-ai/DeepSeek-OCR-2'
6# model_name = 'model_file'
7
8tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
9model = AutoModel.from_pretrained(model_name, _attn_implementation='flash_attention_2', use_safetensors=True, trust_remote_code=True)
10model = model.eval().cuda().to(torch.bfloat16)
11
12# prompt = "<image>\nFree OCR. "
13# prompt = "<image>\n<|grounding|>Give me the smiles of the molecule. "
14prompt = "<image>\n Give me the smiles of the molecule. "
15image_file = 'test_img/penicillin.jpg'
16output_path = 'output'
17
18res = model.infer(tokenizer, prompt=prompt, image_file=image_file, output_path = output_path, base_size = 1024, image_size = 768, crop_mode=True, save_results = True)cd ../ChemSeek-OCR1python evaluation.py --config evaluation_config.yaml
2python gspo/evaluation_gspo.py --config gspo/evaluation_gspo_config.yaml1train_sets:
2 - train_csv: ./training_data/pubchem/train_200k.csv
3 data_mode: dynamic
4 pre_rendered_image_dir: null
5 realistic_image_root: null
6 instruction: "<image>\n Give me the SMILES of the molecule. "
7 style: molscribe_default # molscribe_default / chemdraw_like
8 mol_augment: true
9 include_condensed: False
10 max_samples: null
11 sample_num: 64000
12 - train_csv: ./training_data/pubchem/train_200k.csv
13 data_mode: dynamic
14 pre_rendered_image_dir: null
15 realistic_image_root: null
16 instruction: "<image>\n Give me the SMILES of the molecule. "
17 style: chemdraw_like # molscribe_default / chemdraw_like
18 mol_augment: false
19 include_condensed: False
20 max_samples: null
21 sample_num: 64000
22 - train_csv: ./training_data/uspto_mol/train_200k.csv
23 data_mode: realistic
24 realistic_image_root: ./training_data
25 instruction: "<image>\n Give me the SMILES of the molecule. "
26 sample_num: 640001...
2batch_size: 4 -> 32
3grad_accum: 8 -> 1
4...
5accelerate_num_processes: 4 -> 8
6accelerate_gpu_ids: "0,1,2,3" -> "0,1,2,3,4,5,6,7,8"
7...python lora_sft.py --config lora_sft_config.yaml1python merge_lora_weight.py \
2 --pretrained_weight_path ../DeepSeek-OCR-2 \
3 --checkpoint_path ./weight/checkpoint-1500 \
4 --merged_model_dir ./merged_models \
5 --full_or_lora lora1train_sets:
2 - train_csv: ./training_data/pubchem/train_1m.csv
3 data_mode: dynamic
4 pre_rendered_image_dir: null
5 realistic_image_root: null
6 instruction: "<image>\n Give me the SMILES of the molecule. "
7 style: molscribe_default # molscribe_default / chemdraw_like
8 mol_augment: true
9 include_condensed: False
10 max_samples: null
11 sample_num: null
12 - train_csv: ./training_data/pubchem/train_1m.csv
13 data_mode: dynamic
14 pre_rendered_image_dir: null
15 realistic_image_root: null
16 instruction: "<image>\n Give me the SMILES of the molecule. "
17 style: chemdraw_like # molscribe_default / chemdraw_like
18 mol_augment: false
19 include_condensed: False
20 max_samples: null
21 sample_num: null
22 - train_csv: ./training_data/uspto_mol/train_680k.csv
23 data_mode: realistic
24 realistic_image_root: ./training_data
25 instruction: "<image>\n Give me the SMILES of the molecule. "
26 sample_num: nullpython progressive_sft.py --config progressive_sft_config.yaml1conda create -n chemseek-ocr-verl python=3.12
2conda activate chemseek-ocr-verl
3unzip verl-0.6.1.zip
4cd verl-0.6.11# pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu118 #You can run this if error says libcudart.so.11.0
2pip install -e .[vllm]
3pip install flash-attn==2.7.3 --no-build-isolation
4pip install matplotlib albumentations rdkit SmilesPE pandas addictclip_ratio_low/clip_ratio_high) instead of KL regularization, providing more stable policy updates. The implementation is built on the verl framework and is now organized under the gspo/ directory:| File | Description |
|---|---|
| gspo/prepare_verl_data.py | Reads CSV datasets, resolves image paths, and writes train/val parquet splits in verl-compatible format. |
| gspo/gspo_rl_verl.py | Defines the custom reward function (compute_score), multimodal dataset (ChemSeekOCRDataset), and assembles verl launch command with GSPO-specific Hydra overrides. |
| gspo/gspo_rl_verl_config.yaml | All hyperparameters: model path, data sources, GSPO clipping, reward weights, GPU/FSDP/vLLM settings, and logging. |
| gspo/evaluation_gspo.py | Merges verl FSDP checkpoints and evaluates them on configured OCR benchmarks. |
| gspo/evaluation_gspo_config.yaml | Benchmark selection and checkpoint/evaluation settings for GSPO models. |
gspo: section):clip_ratio_low / clip_ratio_high: Tight symmetric clipping bounds (default 3e-4 / 4e-4) that replace KL penalty.clip_ratio_c: Upper clip bound for the importance-sampling ratio (default 10.0).loss_agg_mode: seq-mean-token-mean: Sequence-level then token-level mean aggregation.use_kl_loss: false / kl_loss_coef: 0.0: KL loss is disabled; tight clipping suffices.group_size: 8: Number of responses sampled per prompt for advantage estimation.use_dynamic_bsz: true: Dynamic batch sizing based on token budget.1# Step 1: Prepare parquet data
2python gspo/prepare_verl_data.py --config gspo/gspo_rl_verl_config.yaml --workers 8
3
4# Step 2: Launch GSPO training
5python gspo/gspo_rl_verl.py --config gspo/gspo_rl_verl_config.yaml
6
7# Step 3: Evaluate a GSPO checkpoint
8python gspo/evaluation_gspo.py --config gspo/evaluation_gspo_config.yamlreft/ directory and provides a two-phase pipeline: best-of-N generation with vLLM followed by SFT on the curated high-reward samples.| File | Description |
|---|---|
| reft/reft.py | Runs the generation, scoring, filtering, and iterative fine-tuning pipeline. |
| reft/reft_config.yaml | Controls model path, sampling, reward thresholds, vLLM settings, and SFT hyperparameters. |
1# Run the full ReFT pipeline
2python reft/reft.py --config reft/reft_config.yaml
3
4# Only generate best-of-N candidates
5python reft/reft.py --config reft/reft_config.yaml --phase generate
6
7# Only train a specific iteration
8python reft/reft.py --config reft/reft_config.yaml --phase train --iteration 0python transformer_infer_case.py --model-path ./weight_progressive_sft/checkpoint-2000 --image-file ./test_img/penicillin.jpg@misc{tang2026finetuningdeepseekocr2molecularstructure,
title={Fine-tuning DeepSeek-OCR-2 for Molecular Structure Recognition},
author={Haocheng Tang and Xingyu Dang and Junmei Wang},
year={2026},
eprint={2604.03476},
archivePrefix={arXiv},
primaryClass={cs.CV},
url={https://arxiv.org/abs/2604.03476},
}