Add DeepSeek OCR notebook - #576
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
One or more issues must be addressed before approval.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds an isolated DeepSeek OCR notebook workflow for OpenVINO conversion, inference, quantization, and an interactive Gradio demo.
Changes:
- Adds model selection, download, conversion, and inference notebook cells.
- Implements OpenVINO model conversion and runtime helpers.
- Adds encoder implementations and isolated Python environment configuration.
File summaries
| File | Description |
|---|---|
| openvino_notebooks/deepseek-ocr/README.md | Updated as part of this pull request. |
| openvino_notebooks/deepseek-ocr/pyproject.toml | Updated as part of this pull request. |
| openvino_notebooks/deepseek-ocr/ov_deepseek_ocr_helper.py | Updated as part of this pull request. |
| openvino_notebooks/deepseek-ocr/gradio_helper.py | Updated as part of this pull request. |
| openvino_notebooks/deepseek-ocr/deepseek-ocr.ipynb | Updated as part of this pull request. |
| openvino_notebooks/deepseek-ocr/deepencoderv2.py | Updated as part of this pull request. |
| openvino_notebooks/deepseek-ocr/deepencoder.py | Updated as part of this pull request. |
| openvino_notebooks/deepseek-ocr/.python-version | Updated as part of this pull request. |
| openvino_notebooks/deepseek-ocr/.gitignore | Updated as part of this pull request. |
Review details
Suppressed comments (11)
openvino_notebooks/deepseek-ocr/deepseek-ocr.ipynb:431
- The notebook text misspells the OpenVINO API as
core.complie_model; readers following this description cannot find that method. Correct it tocore.compile_model.
"DeepSeek-OCR is PyTorch model. OpenVINO supports PyTorch models via conversion to OpenVINO Intermediate Representation (IR). [OpenVINO model conversion API](https://docs.openvino.ai/2024/openvino-workflow/model-preparation.html#convert-a-model-with-python-convert-model) should be used for these purposes. `ov.convert_model` function accepts original PyTorch model instance and example input for tracing and returns `ov.Model` representing this model in OpenVINO framework. Converted model can be used for saving on disk using `ov.save_model` function or directly loading on device using `core.complie_model`. \n",
openvino_notebooks/deepseek-ocr/gradio_helper.py:240
- For an empty PDF,
len(doc) - 1is-1, so this clamp still selects page0andload_page(0)raises. Handle zero-page PDFs before clamping/loading and avoid exposing a 1-based selector for a document with no pages.
page_idx = max(0, min(int(page_num) - 1, len(doc) - 1))
page = doc.load_page(page_idx)
openvino_notebooks/deepseek-ocr/gradio_helper.py:78
- Model-generated
<|det|>contents are parsed without validation; a malformed coordinate payload makesast.literal_evalraise and aborts the whole Gradio request. Parse each reference defensively and skip invalid boxes, as the notebook helper does.
coords = ast.literal_eval(ref[2])
openvino_notebooks/deepseek-ocr/gradio_helper.py:24
- These presets are not all valid for
OVDeepseekOCR2ForCausalLM: itsextract_image_featuresaccepts only 144 or 256 queries, while 512/640/1280 inputs produce 64/100/400 queries and raiseValueError. Since this demo is used with either model variant, make the dropdown model-specific or restrict it to dimensions supported by the loaded model.
"Gundam": {"base_size": 1024, "image_size": 640, "crop_mode": True},
"Tiny": {"base_size": 512, "image_size": 512, "crop_mode": False},
"Small": {"base_size": 640, "image_size": 640, "crop_mode": False},
"Base": {"base_size": 1024, "image_size": 1024, "crop_mode": False},
"Large": {"base_size": 1280, "image_size": 1280, "crop_mode": False},
openvino_notebooks/deepseek-ocr/ov_deepseek_ocr2_helper.py:1460
dynamic_preprocessis called withoutimage_size, so it always creates 768×768 local tiles, while the token sequence below uses the caller'simage_size(the default is 640). For a high-resolution image this produces 144 local feature vectors but only 100 image-token slots, causingmasked_scatter_to fail. Keep preprocessing and token counting tied to the same supported tile size and validate that size.
images_crop_raw, crop_ratio = dynamic_preprocess(image)
openvino_notebooks/deepseek-ocr/ov_deepseek_ocr2_helper.py:335
- The no-normalization branch calls
nn.Identity(), but this module never importsnn; constructingBasicImageTransform(..., normalize=False)therefore raisesNameError. UseNonefor the no-op branch (the following condition already omits it) or importtorch.nn as nn.
normalize = normalize_transform(mean, std) if normalize else nn.Identity()
openvino_notebooks/deepseek-ocr/ov_deepseek_ocr2_helper.py:1377
inferdefaultsoutput_pathto an empty string and then callsos.makedirs(output_path, exist_ok=True), which raisesFileNotFoundErrorwhen callers rely on the default. Useoutput_path="."as the default or guard the directory creation.
output_path="",
openvino_notebooks/deepseek-ocr/ov_deepseek_ocr2_helper.py:1430
- The
elif promptbranch creates a conversation without images, soload_pil_imagesreturns an empty list; the nextimages[0]access then always raisesIndexErrorfor the text-only call path. Reject missingimage_filebefore this branch or implement a real text-only path.
image_draw = images[0].copy()
openvino_notebooks/deepseek-ocr/ov_deepseek_ocr_helper.py:735
nncf.compress_weightsreturns the compressedov.Model, but its return value is discarded here before saving. As a result, the requested vision INT8 compression is not included in the saved IR; the language-model path below correctly assigns the return value.
nncf.compress_weights(ov_model, **quantization_config["vision"])
openvino_notebooks/deepseek-ocr/ov_deepseek_ocr_helper.py:1269
inferdefaultsoutput_pathto an empty string and then callsos.makedirs(output_path, exist_ok=True), which raisesFileNotFoundErrorwhen callers rely on the default. Useoutput_path="."as the default or guard the directory creation.
output_path="",
openvino_notebooks/deepseek-ocr/ov_deepseek_ocr_helper.py:1322
- The
elif promptbranch creates a conversation without images, soload_pil_imagesreturns an empty list; the nextimages[0]access then always raisesIndexErrorfor the text-only call path. Reject missingimage_filebefore this branch or implement a real text-only path.
image_draw = images[0].copy()
- Files reviewed: 10/11 changed files
- Comments generated: 7
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| with requests.get(url, stream=True, timeout=30) as response: | ||
| response.raise_for_status() |
| stdout = sys.stdout | ||
| sys.stdout = StringIO() |
|
|
||
| transform_pipelines = [transforms.ToTensor()] | ||
|
|
||
| normalize = normalize_transform(mean, std) if normalize else nn.Identity() |
| self.token_emb_request = None | ||
| self._device = device.upper() | ||
| self.device = torch.device("cpu") | ||
| self.ov_config = {"KV_CACHE_PRECISION": "f32", "DYNAMIC_QUANTIZATION_GROUP_SIZE": "0"} |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: morteza89 <51894034+morteza89@users.noreply.github.com>
Co-authored-by: morteza89 <51894034+morteza89@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
No description provided.