去年 12 月,DeepSeek 横空出世。官方宣传时不断拿自己和其他国际一流大模型对比,声称性能更强、效果更优,一度被包装成“国产大模型之光”。朋友圈、媒体、技术社区都在热议,仿佛一夜之间 AI 的未来已经被点亮。
然而,实际体验下来,DeepSeek 的表现并没有达到宣传时的高度。与其说它“超越了最好的大模型”,不如说它在对比中显得严重名不符实。
没想到,时隔10个月,DeepSeek 团队低调发布了一个小工具 ——DeepSeek-OCR。没有大张旗鼓的营销,却在真实使用中给了我极大的惊喜。
DeepSeek-OCR 是什么?
简单来说,它是一款多模态 OCR 模型,能把图片、PDF、扫描件里的文字、表格、公式完整识别出来,并且保留排版结构。
和传统 OCR 工具相比,它有几个显著优势:
复杂排版支持:图文混排、表格、数学公式都能准确还原。
多语言识别:不仅支持中文,还能处理英文、日文等多种语言。
结构化输出:可以直接生成 Markdown、JSON 等格式,方便后续处理。
开源可用:在 Hugging Face 上就能下载,部署简单。
我的实测体验
我分别用两类文档测试了 DeepSeek-OCR:
1. 普通图片文字:识别率接近 100%,几乎没有错字,如下图侧为手机拍照的图片,右侧为程序运行后OCR识别的结果对比:
2. PDF 文档(数学试卷,图文混排,含公式和表格):这是最让我惊讶的,段落、标题、表格都能保留,结构清晰。公式识别准确率几乎接近 100%,LaTeX 表达式输出非常稳定,如下为一份初中一年级的数学试卷效果:
相比之下,传统 OCR 工具在数学公式和复杂排版上往往会“翻车”,而 DeepSeek-OCR 的表现堪称“无痛还原”。输出的文本几乎和原始试卷一模一样,公式、表格、段落都被完整保留。
测试使用的代码
"""DeepSeek-OCR测试脚本"""fromtransformersimportAutoModel,AutoTokenizerimporttorchimportosimporttimeimportpsutilimportGPUtilfrompathlibimportPathfromPILimportImageimportfitz#PyMuPDF#============配置区============#1.模型路径model_name='deepseek-ai/DeepSeek-OCR'#从HuggingFace下载#2.设置图片/PDF路径IMAGE_PATH='images/2024数学练习.pdf'#3.设置输出目录OUTPUT_DIR='./output'#4.设置任务类型:'markdown'或'ocr'TASK='markdown'#5.GPU设备os.environ["CUDA_VISIBLE_DEVICES"]='0'#=================================defmain():print("="*70)print("加载模型...")#记录初始状态process=psutil.Process()initial_memory=process.memory_info().rss/1024/1024#MB#加载模型load_start=time.time()tokenizer=AutoTokenizer.from_pretrained(model_name,trust_remote_code=True,local_files_only=True#只使用本地缓存,不联网检查更新)model=AutoModel.from_pretrained(model_name,trust_remote_code=True,local_files_only=True#只使用本地缓存,不联网检查更新)model=model.eval().cuda().to(torch.bfloat16)load_time=time.time()-load_start#记录加载后状态after_load_memory=process.memory_info().rss/1024/1024#MBgpu=GPUtil.getGPUs()[0]ifGPUtil.getGPUs()elseNoneprint(f"✓模型加载完成(耗时:{load_time:.2f}秒)")print(f"内存占用:{after_load_memory-initial_memory:.2f}MB")ifgpu:print(f"显存占用:{gpu.memoryUsed:.2f}MB/{gpu.memoryTotal:.2f}MB")print("="*70)#设置提示词ifTASK=='markdown':prompt="<image>\n<|grounding|>Convertthedocumenttomarkdown."else:prompt="<image>\n<|grounding|>FreeOCR."#检查文件类型并转换PDFfile_path=Path(IMAGE_PATH)iffile_path.suffix.lower()=='.pdf':print(f"\n检测到PDF文件:{IMAGE_PATH}")print("正在转换PDF为图片...")#打开PDFpdf_doc=fitz.open(IMAGE_PATH)total_pages=len(pdf_doc)print(f"
DF共{total_pages}页")os.makedirs(OUTPUT_DIR,exist_ok=True)all_results=[]total_infer_time=0#逐页处理forpage_numinrange(total_pages):print(f"\n{'='*70}")print(f"处理第{page_num+1}/{total_pages}页")print("-"*70)#转换当前页为图片page=pdf_doc[page_num]pix=page.get_pixmap(matrix=fitz.Matrix(2,2))#保存临时图片temp_image_path=f'{OUTPUT_DIR}/temp_page_{page_num+1}.png'pix.save(temp_image_path)#记录推理前状态infer_start=time.time()cpu_percent_start=psutil.cpu_percent(interval=0.1)gpu_util_start=gpu.load*100ifgpuelse0#执行OCRpage_output_dir=f'{OUTPUT_DIR}/page_{page_num+1}'result=model.infer(tokenizer,prompt=prompt,image_file=temp_image_path,output_path=page_output_dir,base_size=1024,image_size=640,crop_mode=True,save_results=True)#记录推理后状态infer_time=time.time()-infer_starttotal_infer_time+=infer_time#读取保存的结果文件result_file=f'{page_output_dir}/result.mmd'ifos.path.exists(result_file):withopen(result_file,'r',encoding='utf-8')asf:page_result=f.read()#复制该页的images目录到output/images下page_images_dir=f'{page_output_dir}/images'ifos.path.exists(page_images_dir)
utput_images_dir=f'{OUTPUT_DIR}/images'os.makedirs(output_images_dir,exist_ok=True)#复制图片并重命名(添加页码前缀)importshutilforimg_fileinos.listdir(page_images_dir):src=os.path.join(page_images_dir,img_file)dst=os.path.join(output_images_dir,f'page{page_num+1}_{img_file}')shutil.copy2(src,dst)#更新结果中的图片路径page_result=page_result.replace('](images/',f'](images/page{page_num+1}_')all_results.append(f"\n\n#第{page_num+1}页\n\n{page_result}")print(f"✓第{page_num+1}页识别完成(耗时:{infer_time:.2f}秒)")else:print(f"✗第{page_num+1}页识别失败")pdf_doc.close()#合并所有页结果result="\n".join(all_results)#保存完整结果withopen(f'{OUTPUT_DIR}/full_result.md','w',encoding='utf-8')asf:f.write(result)print(f"\n{'='*70}")print("
DF处理完成")print("="*70)print(f"总页数:{total_pages}")print(f"总耗时:{total_infer_time:.2f}秒")print(f"平均每页:{total_infer_time/total_pages:.2f}秒")#获取最终状态final_memory=process.memory_info().rss/1024/1024cpu_percent_end=psutil.cpu_percent(interval=0.1)ifgpu:gpu=GPUtil.getGPUs()[0]gpu_util_end=gpu.load*100else:gpu_util_end=0print(f"内存使用:{final_memory:.2f}MB")ifgpu:print(f"显存占用:{gpu.memoryUsed:.2f}MB/{gpu.memoryTotal:.2f}MB")print("="*70)else:#处理单个图片process_path=IMAGE_PATHprint(f"\n处理文件:{IMAGE_PATH}")print("-"*70)#记录推理前状态infer_start=time.time()cpu_percent_start=psutil.cpu_percent(interval=0.1)gpu_util_start=gpu.load*100ifgpuelse0result=model.infer(tokenizer,prompt=prompt,image_file=process_path,output_path=OUTPUT_DIR,base_size=1024,image_size=640,crop_mode=True,save_results=True)#记录推理后状态infer_time=time.time()-infer_startcpu_percent_end=psutil.cpu_percent(interval=0.1)final_memory=process.memory_info().rss/1024/1024ifgpu:gpu=GPUtil.getGPUs()[0]gpu_util_end=gpu.load*100else:gpu_util_end=0#输出性能统计print("-"*70)print("\n性能统计:")print("="*70)print(f"推理耗时:{infer_time:.2f}秒")print(f"CPU使用率:{cpu_percent_end:.1f}%")print(f"内存使用:{final_memory:.2f}MB(推理增加:{final_memory-after_load_memory:.2f}MB)")ifgpu:print(f"GPU使用率:{gpu_util_end:.1f}%")print(f"显存占用:{gpu.memoryUsed:.2f}MB/{gpu.memoryTotal:.2f}MB({gpu.memoryUsed/gpu.memoryTotal*100:.1f}%)")print("="*70)#显示结果预览print(f"\n识别结果预览:")print("-"*70)ifresult:preview=result[:300]iflen(result)>300elseresultprint(preview)iflen(result)>300:print(f"\n...(共{len(result)}字符)")else:print("未获取到结果")print("-"*70)print(f"\n✓完整结果已保存到:{OUTPUT_DIR}/full_result.md"iffile_path.suffix.lower()=='.pdf'elsef"\n✓完整结果已保存到:{OUTPUT_DIR}")if__name__=='__main__':main()🎯 总结与思考
做产品和模型,有一个朴素的道理:
真正的突破,往往不在于“喊得多响”、“营销多好”,而在于“做得多实”。
希望越做越好!
| 欢迎光临 链载Ai (http://www.lianzai.com/) | Powered by Discuz! X3.5 |