前回の記事では、多言語・多様式ドキュメントの一括処理(テキスト抽出+翻訳+Markdown形式で保存)の全体の流れと、AI-OCR各モデルの比較、翻訳処理の仕方やMarkdown変換方法等についてご紹介させてもらいました。今回は更なる精度向上を目的に、AI-OCRの上位モデルの評価と、リリースされたばかりの翻訳モデルであるGoogleのtranslategemmaを試してみたので、続編としてまとめてみました。
今回の結論は、AI-OCRの上位モデルとしてはdatalabのchandraがお勧めで、翻訳用にはGoogle翻訳のような文字数やファイルサイズ上限の無いtranslategemmaは十分使えるレベルであることが分かりました。
1. AI-OCR(上位モデル)
最も処理結果の精度に影響が大きいOCR機能について、前回よりサイズの大きい下記のモデルを試してみました。
| Repo id | 特 徴 及 び 概 要 | ライセンス | |
|---|---|---|---|
| datalab-to/chandra | 9B | OCR精度が高く日本語も正確でかなり実用的だが、処理は少し重くなる。Ollamaあり、MLXあり。 | Open Rail-M |
| allenai/olmOCR-2-7B-1025 | 7B | 英語以外の抽出精度がイマイチで、MarkDown出力も苦手。Ollamaあり、MLXあり。 | Apache2.0 |
| deepseek-ai/DeepSeek-OCR | 3B | 処理は早く全体を抽出できており、日本語もほぼ正確。Ollamaあり、MLXあり。 | MIT |
| ChatDOC/OCRFlux-3B | 3B | 英語以外の抽出精度がイマイチ。Ollamaあり、MLXなし。 | ----- |
上記各モデルを各々のデモサイト及びローカル環境でMLX版を実際に動作させた結果(OCRFlux-3Bはデモサイトでの検証のみ)、 OCR精度が最も良かったのはchandraでした。サイズが大きく処理は一番長くかかりますが、量子化4Bitで動かせば許容範囲内かと思います。但しライセンスはOpen Rail-Mなので商用利用時は注意が必要です。二番目のお勧めは定番のDeepSeek-OCRですが、少し気になる所があったので試しに天*門事件のWikiのスクリーショットを読ませてみましたが、問題なく抽出出来て異常な送信パケットの増加等もありませんでした(大まかな検証なので保証はできませんが)。残りのモデルはソースが英語の場合のみ検討した方が良さそうです。
また、どのモデルもLightOn-OCRと同様にtemperatureやmax_token数等のパラメータで精度が結構変わってしまうので、以下に推奨パラメータを記載しておきます(あくまでも一例ですので参考程度です)。
# chandra:
generation_args = {
"max_tokens": 12384,
"temperature": 0.0,
}
# olmoOCR-2:
generation_args = {
temperature=0.1,
max_new_tokens=50,
num_return_sequences=1,
do_sample=True,
}
# DeepSeek-OCR:
generation_args = {
temperature=0.0,
max_tokens=8192,
# ngram logit processor args
extra_args=dict(
ngram_size=30,
window_size=90,
whitelist_token_ids={128821, 128822}, # whitelist: <td>, </td>
),
skip_special_tokens=False,
}
前回のコードのOCRモデル名と各パラメータを置き換えて試してみて下さい。なお、chandraを利用する場合は下記のサイトに最適化されたサンプルスクリプトがありますので、そちらを使った方が精度が上がるかもしれません。
2. TranslateGemma
TranslateGemmaはGoogleが最近リリースした翻訳専用のLLMモデルで、MLX版とOllama版も既にHaggingFaceとOllamaモデルサイトにあります。パラメータサイズは4B/12B/27Bの3つで、ライセンスは”Gemma Terms of Use”で他のGemmaシリーズと同じようです。ベースモデルはGemma3ですが翻訳に特化しているため、chat templatesに下記のように3つのパラメータと、テキスト又は画像を読み込ませて翻訳します。
messages = [
{
"role": "user",
"content": [
{
"type": "text", # または"image"
"source_lang_code": "en", # ISO 639-1 Alpha-2 language code
"target_lang_code": "ja-JP", # ISO 639-1 Alpha-2 language code
"text": "This is an apple.", # "type":"image"の場合は"url":"https://xxx"
}
],
}
]
また推奨プロンプトは下記の通りです。
You are a professional {SOURCE_LANG} ({SOURCE_CODE}) to {TARGET_LANG} ({TARGET_CODE}) translator. Your goal is to accurately convey the meaning and nuances of the original {SOURCE_LANG} text while adhering to {TARGET_LANG} grammar, vocabulary, and cultural sensitivities.
Produce only the {TARGET_LANG} translation, without any additional explanations or commentary. Please translate the following {SOURCE_LANG} text into {TARGET_LANG}:
{TEXT}
上記から分かるようにソース言語の自動判定機能は無いため、前回記事で紹介したような”lingua”モジュールなどで判別すると良いですね。
検証した結果では、翻訳精度は4Bモデルでも十分使える(私の主観です)レベルで処理もそれほど重くはありませんが、OCRモデルとの併用で結構ストレージ容量は多く使います。前回紹介したgoogle翻訳の非公式APIモジュールが結構動作不安定なので(非公式版なので仕方ありませんが...)置き換え手段としては有りかなと思いました。
参考に前回記載した翻訳部分のPython関数をtranslategennma用に作り替えたものを載せておきます。
from mlx_lm import load as t_load #OCRモデルと区別するためt_loadで読み込み
from mlx_lm import generate as t_generate #OCRモデルと区別するためt_generateで推論
from lingua import Language, LanguageDetectorBuilder
tmodel_path = "mlx-community/translategemma-4b-it-4bit"
t_model, t_tokenizer = t_load(tmodel_path)
def translategemma(text):
print(" 文字数=", len(text))
languages = [Language.ENGLISH, Language.CHINESE, Language.JAPANESE, Language.RUSSIAN, Language.KOREAN]
detector = LanguageDetectorBuilder.from_languages(*languages).build()
language = detector.detect_language_of(str(text))
print(" Language: ",language)
if language == Language.ENGLISH:
source_lang = "en"
elif language == Language.CHINESE:
source_lang = "zh-CH"
elif language == Language.RUSSIAN:
source_lang = "ru-RU"
elif language == Language.KOREAN:
source_lang = "ko-KR"
elif language == Language.JAPANESE:
source_lang = "ja-JP"
SOURCE_LANG = str(language).split(".")[-1].capitalize()
print("SOURCE_LANG: ",SOURCE_LANG)
SOURCE_CODE = source_lang
TARGET_LANG = "Japanese"
TARGET_CODE = "ja-JP"
TEXT = text
prompt = f"""
You are a professional {SOURCE_LANG} ({SOURCE_CODE}) to {TARGET_LANG} ({TARGET_CODE}) translator. Your goal is to accurately convey the meaning and nuances of the original {SOURCE_LANG} text while adhering to {TARGET_LANG} grammar, vocabulary, and cultural sensitivities.
Produce only the {TARGET_LANG} translation, without any additional explanations or commentary. Please translate the following {SOURCE_LANG} text into {TARGET_LANG}:
{TEXT}
"""
if t_tokenizer.chat_template is not None:
messages = [
{
"role": "user",
"content": [
{
"type": "text",
"source_lang_code": source_lang,
"target_lang_code": "ja-JP",
"text": prompt,
}
],
}
]
t_prompt = t_tokenizer.apply_chat_template(
messages, add_generation_prompt=True, return_dict=False
)
translated_output = t_generate(t_model, t_tokenizer, max_tokens=8192, prompt=t_prompt, verbose=False)
return translated_output
ここで注意が必要なのは、apply_chat_templateでreturn_dict=TrueだとエラーになったのでFalseに変更しましたが、transformersやmlx-lmのバージョン次第で挙動が変わる可能性のあることです。
3. アップグレード版のまとめ
今回は肝となるOCRモデルの上位モデルと、translategemmaの翻訳機能を試してみました。その結果chandraとdeepseek-ocrが多言語用にはお勧めで、translategemmaが4Bレベルでも使えそうなことが分かりました。全てをローカルで処理できるので、セキュリティ面やコスト面でも良い使い方が出来そうなので、是非試してみて下さい。