
В выходные я просматривал Твиттер, чтобы узнать, что происходит в сфере ИИ. И снова DeepSeek привлек внимание всего мира.
Это не просто очередной инструмент для распознавания текста, а новая технология контекстного оптического сжатия, которая использует визуальные методы для решения проблемы обработки длинных текстов, предлагая новый подход к работе с огромными массивами информации.
Любой, кто пользовался большой языковой моделью (LLM), сталкивался с такой проблемой:
Когда вы просите модель обобщить десятки тысяч слов из конспектов конференций или научных статей, она начинает терять память.
Это происходит потому, что квадратичная сложность длины последовательности по своей сути ограничивает GPT, Gemini и Claude - чем длиннее входные данные, тем больше вычислительной мощности требуется.
Люди устроены иначе. Мы можем взглянуть на заметку или диаграмму и мгновенно вспомнить целый отрывок.
Традиционно, чтобы ИИ понял длинные документы, весь документ должен быть преобразован в цифровой текст. Этот процесс потребляет большое количество токенов (единицы, используемые ИИ для обработки информации), что приводит к низкой вычислительной эффективности.
DeepSeek-OCR использует другой подход. Сначала он преобразует текст в изображения, а затем использует визуальные токены для сжатия этой информации. Представьте, что у ��ас есть статья на 10 000 слов - вместо того чтобы заставлять ИИ читать ее слово за словом, он может просто взглянуть на изображение, чтобы понять и восстановить исходный текст.
Прорыв заключается в его способности представлять богатую информацию в одном изображении, содержащем текст документа, используя гораздо меньше токенов, чем эквивалентный текст. Это означает, что оптическое сжатие с визуальными токенами может достичь более высоких коэффициентов сжатия, позволяя нам делать больше с меньшими ресурсами.
Итак, позвольте мне быстро продемонстрировать живого чат-бота, чтобы показать, что я имею в виду.
Я задам чат-боту вопрос: «Каковы основные выводы?» Если вы посмотрите, как чат-бот генерирует ответ, вы увидите, что агент извлекает текст с каждой страницы. Но если страница содержит менее 50 символов или не имеет встроенного текста, он преобразует эту страницу в изображение с высоким разрешением и отправляет его в DeepSeek-OCR на Replicate. Там используется подход «Контекстного оптического сжатия», при котором документ преобразуется в визуальные токены, а информация сжимается - по сути, позволяя ИИ взглянуть на изображение, а не читать слово за словом, что может превратить статью из 10 000 слов в гораздо более эффективный сжатый формат.
Как только весь текст извлечен, система разбивает его на фрагменты по 500 символов с перекрытием в 50 символов для сохранения контекста, преобразует каждый фрагмент в математические векторы с помощью эмбеддингов OpenAI и сохраняет их в векторной базе данных Chroma, которая сохраняется на диске для будущего использования.
Когда вы задаете вопрос, агент ищет в этих векторах 5 наиболее семантически похожих фрагментов документа, собирает их в контекстный промпт вместе с вашим вопросом и инструкциями по цитированию номеров страниц, а затем отправляет все это модели Llama 3.1 405B, работающей через потоковый API Replicate. Модель обрабатывает промпт и генерирует интеллектуальный ответ фрагмент за фрагментом в реальном времени.
Затем генерируется ответ и цитаты из исходного документа, показывающие, с каких страниц была взята информация, создавая полноценного RAG-агента, способного понять любой PDF.
Делегируйте часть рутинных задач вместе с BotHub! Для доступа к сервису не требуется VPN и можно использовать российскую карту. По ссылке вы можете получить 100 000 бесплатных токенов для первых задач и приступить к работе с нейросетями прямо сейчас!
Что делает DeepSeek-OCR уникальным?
DeepSeek-OCR — это сквозная модель OCR и парсинга документов, разработанная для достижения оптического контекстного сжатия.

Эта модель состоит из двух основных компонентов: DeepEncoder, который сжимает входное изображение высокого разрешения в небольшое количество визуальных токенов, и декодера DeepSeek-3B-MoE (языковая модель Mixture-of-Experts), который восстанавливает исходный текст из последовательности визуальных токенов.
DeepEncoder (примерно 380 миллионов параметров) включает механизм оконного внимания на основе SAM для извлечения локальных признаков изображения. Вставив между ними двухслойную CNN с 16-кратным сжатием, он значительно сжимает изображение размером 1024x1024 пикселей с 4096 патчей до примерно 256 токенов.
Сторона декодера, которая получает эти визуальные токены, имеет в общей сложности 3 миллиарда параметров (примерно 570 миллионов эффективны во время инференса) и имеет структуру MoE, которая динамически использует 6 экспертов на шаг из 64 экспертов, обеспечивая легкую, но эффективную реконструкцию текста.
Благодаря такой архитектуре DeepSeek-OCR использует нетрадиционный подход, преобразуя содержимое текстового документа в изображение и затем считывая его.
PaddleOCR-VL против DeepSeek-OCR:
Посмотрите видео PaddleOCR-VL:
Когда я тестировал обе модели OCR, я обнаружил кое-что интересное - PaddleOCR-VL, которая на самом деле меньше (0,9 млрд параметров), превосходила гораздо более крупные модели на 3 млрд параметров в реальных тестах.
Я давал ей сложные задачи: чтение вертикального текста в правильном направлении, понимание сложных математических формул и обработка документов с несколькими колонками - и PaddleOCR-VL справилась со всем этим блестяще, в то время как DeepSeek-OCR делала ошибки в порядке чтения и формулах, несмотря на свои крутые функции сжатия.
Затем я обнаружил кое-что забавное в исследовательской статье DeepSeek-OCR - они на самом деле поблагодарили PaddleOCR и признали, что использовали ее для разметки своих обучающих данных. Это заставило меня понять, почему такие компании, как Baidu, DeepSeek и Shanghai AI Lab, выпускают модели OCR: они не делают инструменты OCR своей основной работой, они создают их для очистки огромных объемов данных для обучения своих моделей ИИ, а мы получаем эти мощные инструменты OCR в качестве бесплатных бонусов.
Протестировав все, я пришел к выводу, что если вы создаете что-то для реальной работы и вам нужно читать печатный текст, формы, таблицы или документы на разных языках, PaddleOCR-VL - это то, что нужно. В то время как DeepSeek-OCR лучше подойдет, если вы исследователь, пытающийся сжать данные, чтобы сэкономить деньги на затратах на ИИ.
Текстовые токены против визуальных токенов
В традиционных LLM текст разбивается на дискретные текстовые токены (обычно слова или части слов). Каждому токену присваивается фиксированный ID в словаре, и он отображается в вектор через большую «таблицу поиска» (слой эмбеддинга). Хотя этот процесс эффективен, его сила ограничена словарем.
Визуальные токены - это совершенно другое. Вместо того чтобы браться из фиксированной таблицы поиска, они представляют собой непрерывные векторы, генерируемые непосредственно из пикселей изображения нейронной сетью (визуальным энкодером). Это означает:
Более высокая плотность информации: Визуальные токены существуют в непрерывном векторном пространстве и могут кодировать более богатую и детализированную информацию, чем дискретные текстовые токены. Визуальный токен может представлять цвет, форму, текстуру и пространственные отношения внутри области, а не просто слово или часть слова.
Глобальное восприятие паттернов: Визуальный энкодер может захватывать глобальную информацию, такую как общий макет, верстка и стиль шрифта текста, которая теряется в последовательности простого текста.
Большее пространство выражения: Теоретически словарь визуальных токенов бесконечен, потому что они являются непрерывными векторами, генерируемыми непосредственно из пикселей, а не выбираемыми из фиксированного словаря.
Давайте начнем кодить
Прежде чем мы погрузимся в наше приложение, мы создадим идеальную среду для работы кода. Для этого нам нужно установить необходимые библиотеки Python.
pip install requirements
Следующий шаг - мы импортируем соответствующие библиотеки, значение которых станет очевидным по ходу дела, и выполним базовую конфигурацию.
import os
import replicate
from langchain_openai import OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_chroma import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.documents import Document
from langchain_core.language_models.llms import LLM
from typing import List, Optional, Any
import fitz
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()
Я разработал этот пользовательский класс Llama, унаследовав его от базового класса LLM LangChain и настроив его с идентификатором модели Llama 3.1 405B, лимитами токенов и настройками температуры.
Я реализовал обязательное свойство llmtype для возврата идентификатора, затем создал основной метод _call, который принимает промпт, упаковывает его с конфигурацией в словарь, отправляет в потоковый API Replicate и проходит через фрагменты ответа, чтобы объединить их в полный ответ.
class Llama(LLM):
model: str = "meta/meta-llama-3.1-405b-instruct"
max_tokens: int = 1024
temperature: float = 0.7
@property
def _llm_type(self) -> str:
return "replicate_llama"
def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
input_data = {
"prompt": prompt,
"max_tokens": self.max_tokens,
"temperature": self.temperature
}
output = ""
for event in replicate.stream(self.model, input=input_data):
output += str(event)
return output
Я создал этот класс OCRPDFLoader для извлечения текста из PDF-файлов, сначала пытаясь извлечь текст, а при необходимости переключаясь на OCR. Я инициализировал его с путем к файлу, необязательным флагом OCR и порогом текста (по умолчанию 50 символов), чтобы определить достаточно ли текста на странице.
В методе load я открыл PDF с помощью PyMuPDF, прошел через каждую страницу для извлечения текста, затем проверил, было ли включено OCR или извлеченный текст был ниже порога - если да,
Я вызвал свой метод ocrpage, который я создал для преобразования страницы в PNG-изображение высокого разрешения, отправки его в API DeepSeek-OCR на Replicate, получения текста OCR обратно, очистки временного изображения и возврата извлеченного текста.
Наконец, я упаковал текст каждой страницы в объекты Document LangChain с метаданными (исходный файл, номер страницы, имя файла) и вернул их в виде списка, получив умный загрузчик, который автоматически обрабатывает как цифровые, так и отсканированные PDF-файлы.
class OCRPDFLoader:
def __init__(self, file_path: str, use_ocr: bool = False, text_threshold: int = 50):
self.file_path = file_path
self.use_ocr = use_ocr
self.text_threshold = text_threshold
def load(self) -> List[Document]:
doc = fitz.open(self.file_path)
documents = []
for page_num in range(len(doc)):
page = doc[page_num]
text = page.get_text()
if self.use_ocr or len(text.strip()) < self.text_threshold:
print(f"OCR: page {page_num + 1}")
text = self._ocr_page(page, page_num)
if text.strip():
documents.append(Document(
page_content=text.strip(),
metadata={
'source': self.file_path,
'page': page_num + 1,
'filename': Path(self.file_path).name
}
))
doc.close()
return documents
def _ocr_page(self, page, page_num, temp_dir='./temp_ocr'):
os.makedirs(temp_dir, exist_ok=True)
pix = page.get_pixmap(matrix=fitz.Matrix(2, 2))
img_path = f"{temp_dir}/page_{page_num}.png"
pix.save(img_path)
with open(img_path, "rb") as image_file:
input_data = {
"image": image_file,
"task_type": "Free OCR"
}
output = replicate.run(
"lucataco/deepseek-ocr:cb3b474fbfc56b1664c8c7841550bccecbe7b74c30e45ce938ffca1180b4dff5",
input=input_data
)
os.remove(img_path)
return output
Далее я создал класс LangChainPDFRAG - это главный оркестратор, который связывает все вместе в полноценную систему RAG. Я инициализировал его, настроив свою пользовательскую модель Llama для генерации ответов, эмбеддинги OpenAI для преобразования текста в векторы, разделитель текста, который разбивает документы на фрагменты по 500 символов с перекрытием в 50 символов для сохранения контекста между фрагментами, и векторную базу данных Chroma, которую я настроил на сохранение на диске, чтобы она могла перезагружать существующие данные между сеансами.
Я создал метод add_pdf, который использует мой загрузчик OCR для извлечения текста из PDF-файлов, разбивает этот текст на управляемые фрагменты, затем либо создает новое векторное хранилище, либо добавляет к существующему, преобразуя каждый фрагмент в эмбеддинги и сохраняя их для семантического поиска.
Наконец, я реализовал метод query, где я настроил ретривер для поиска 5 наиболее релевантных фрагментов документа, создал цепочку LangChain, которая принимает вопрос пользователя, извлекает релевантный контекст, форматирует его в шаблон промпта с просьбой к LLM цитировать номера страниц, передает все моей модели Llama для генерации и возвращает как сгенерированный ответ, так и исходные документы с их номерами страниц - по сути, создавая полноценную систему вопросов и ответов, которая может интеллектуально искать по PDF-файлам и предоставлять точные ответы со ссылками.
class LangChainPDFRAG:
def __init__(self,
llm_model='meta/meta-llama-3.1-405b-instruct',
embedding_model='text-embedding-3-small',
persist_directory='./chroma_db'):
self.llm = Llama(model=llm_model)
self.embeddings = OpenAIEmbeddings(model=embedding_model)
self.persist_directory = persist_directory
self.vectorstore = None
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", ". ", " ", ""]
)
if os.path.exists(persist_directory):
self.vectorstore = Chroma(
persist_directory=persist_directory,
embedding_function=self.embeddings
)
def add_pdf(self, pdf_path: str, use_ocr: bool = False):
loader = OCRPDFLoader(pdf_path, use_ocr=use_ocr)
documents = loader.load()
splits = self.text_splitter.split_documents(documents)
if self.vectorstore is None:
self.vectorstore = Chroma.from_documents(
documents=splits,
embedding=self.embeddings,
persist_directory=self.persist_directory
)
else:
self.vectorstore.add_documents(splits)
print(f"Added {len(splits)} chunks from {Path(pdf_path).name}")
return len(splits)
def query(self, question: str):
if self.vectorstore is None:
raise ValueError("No documents.")
retriever = self.vectorstore.as_retriever(search_kwargs={"k": 5})
def format_docs(docs):
return "\n\n".join([doc.page_content for doc in docs])
prompt = ChatPromptTemplate.from_template(
"You are a helpful assistant. Answer based on the context provided. Cite page numbers when relevant.\n\n"
"Context:\n{context}\n\n"
"Question: {question}\n\n"
"Answer:"
)
chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| self.llm
| StrOutputParser()
)
docs = retriever.invoke(question)
answer = chain.invoke(question)
return {
'answer': answer,
'sources': [
{
'filename': doc.metadata.get('filename'),
'page': doc.metadata.get('page'),
'content': doc.page_content[:200]
}
for doc in docs
]
}
Я создал экземпляр системы RAG с Llama 3.1 405B, загрузил PDF в векторную базу данных и запросил его с вопросом. Агент извлек релевантные фрагменты документа, сгенерировал ответ и вернул как ответ, так и ссылки на источники.
if __name__ == "__main__":
# Using Llama 3.1 405B from Replicate
rag = LangChainPDFRAG(llm_model='meta/meta-llama-3.1-405b-instruct')
rag.add_pdf('TSLA-Q2-2025-Update.pdf', use_ocr=False)
result = rag.query('What are the main findings?')
print("=== Answer ===")
print(result['answer'])
print("\n=== Sources ===")
for source in result['sources']:
print(f"- {source['filename']}, Page {source['page']}")
Заключение:
DeepSeek-OCR - это не просто очередной апгрейд для распознавания текста. Это настоящий прорыв, открывающий новую главу в истории ИИ. Предложенная идея визуально-текстового сжатия - смелое решение одной из главных проблем современных нейросетей: как переварить огромные объемы информации и не подавиться.
А теперь ваша очередь
Как вы думаете, что это означает для будущего?
Настоящий прорыв, который поможет нам создавать ИИ, способных мгновенно понимать любые книги, архивы и научные работы?
Или это просто очередной умный трюк, который останется нишевым инструментом для исследователей, сжимающих данные?
Делитесь своим мнением в комментариях!
Bertram11
Кроме слова [революция] в этом посте больше нет ничего.