🦀

Deepseek-r1:1.5bを日英翻訳付きで快適に使う方法

2025/01/25に公開

1. はじめに

1.1 目的

ローカルPC(GPUほぼなし)で、Deepseek-r1:1.5bと、七誌さんが公開されている「7shi/gemma-2-jpn-translate:2b-instruct-q8_0」の2つのモデルを使用して、日英翻訳付きの推論を試して見ました。

1.2. 読者対象と前提知識

  • Ollamaを利用されている方
  • LangChainの知識がある方

2. Deepseek-r1の概要と課題

2.1 Deepkseek-r1の概要

Deepseek-r1は、中国のDeepSeek社が開発したLLMです。
OpenAIのo1と同様に、単に言葉の確率を計算するだけのモデルではなく、
言葉の意味を理解して、推論を行って回答を作成します。

https://huggingface.co/deepseek-ai/DeepSeek-R1

2.2 Deepseek-r1の課題

Deepseek-r1を使ってみると、推論(Think)を中国語か、英語で実施しているようです。
そのため、日本語をそのままDeepseek-r1に投げると、
凄く不思議なThinkを行って正しい回答が得られませんでした。
特に、1.5bを含む小さなモデルが顕著で、日本語で質問するなら14b以上でないと、
上手く回答してくれないように感じます。

しかし、ローカルPC(GPUがなし)だと14bだと非常に遅く、時間を要します。

3. 解決策

3.1 翻訳モデルの追加

日本語が苦手ならば、質問を日本語から英語に翻訳した後、
Deepseekにインプットすれば良いだろうという単純な発想です。
翻訳に利用するモデルを何個か探しましたが・・

候補1. qwen2.5:0.5b,qwen2.5:1.5b,qwen2.5:3b
→ モデルサイズが小さくて良いのですが、上手く翻訳されない。
候補2. 7shi/gemma-2-jpn-translate:2b-instruct-q8_0
→ gemma-2-2b-jpn-it-translate をベースに、長文の翻訳に対応している

他のモデルでも可能かもしれませんが、翻訳モデルとしてファインチューニングされている
gemma-2-jpn-translate:2b-instruct-q8_0を利用します。
https://qiita.com/7shi/items/ae0da373184b82d53fcc
https://ollama.com/7shi/gemma-2-jpn-translate:2b-instruct-q8_0

3.2 処理方法

以下のようにDeepseekへのインプットを日英翻訳します。

上記の流れにすれば、Deepseekの推論(Think)は全て英語で実施されます。

3.3 ソースコード

以下はLangChainで作ったCUI上で動くチャットボットです。
以下の3点がPointです。

Point1: 会話履歴も全て英語で保存し、推論を英語のみにする
Point2: 回答は推論部分(<think></think>)を除去して日本語変換している。
Point3: 日・英翻訳する際のMessagesに意図的に翻訳タスクを実行する前提にしている

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_community.chat_models import ChatOllama
from langchain_core.output_parsers import StrOutputParser
from langchain.memory import ConversationBufferMemory
from typing import Iterator
import re

def create_translation_chain():
    # 翻訳用モデルの初期化
    translate_model = ChatOllama(
        model="7shi/gemma-2-jpn-translate:2b-instruct-q8_0",
        base_url="http://localhost:11434",
        temperature=0.0,
        streaming=True
    )
    return translate_model

def create_reasoning_chain():
    # 推論用モデルの初期化
    reasoning_model = ChatOllama(
        model="deepseek-r1:1.5b",
        base_url="http://localhost:11434",
        temperature=0.7,
        streaming=True
    )
    
    # 会話履歴用のメモリを初期化
    memory = ConversationBufferMemory(return_messages=True)
    
    # プロンプトテンプレートの作成(メモリを含む)
    prompt = ChatPromptTemplate.from_messages([
        ("system", "You are a helpful AI assistant. Please provide a detailed and thoughtful response."),
        MessagesPlaceholder(variable_name="history"),
        ("human", "{input}")
    ])
    
    # チェーンの作成
    chain = prompt | reasoning_model | StrOutputParser()
    
    return chain, memory

def translate_to_english(translate_model, text) -> Iterator[str]:
    # 日本語から英語への翻訳
    prompt = ChatPromptTemplate.from_messages([
        ("system", "Translate Japanese to English:"),
        ("assistant", "OK"),
        ("human", "{input}")
    ])
    chain = prompt | translate_model | StrOutputParser()
    return chain.stream({"input": text})

def translate_to_japanese(translate_model, text) -> Iterator[str]:
    # 英語から日本語への翻訳
    prompt = ChatPromptTemplate.from_messages([
        ("system", "Translate English to Japanese:"),
        ("assistant", "OK"),
        ("human", "{input}")
    ])
    chain = prompt | translate_model | StrOutputParser()
    return chain.stream({"input": text})

def get_reasoning_response(chain, memory, text) -> Iterator[str]:
    # 会話履歴を含めて推論による回答生成
    memory_variables = memory.load_memory_variables({})
    response_stream = chain.stream({
        "input": text,
        "history": memory_variables["history"]
    })
    
    # 完全な応答を構築
    full_response = ""
    for chunk in response_stream:
        full_response += chunk
        yield chunk
    
    # 会話履歴の更新
    memory.save_context({"input": text}, {"output": full_response})

def main():
    print("チャットを開始します。終了するには 'exit' と入力してください。")
    
    # 各モデルの初期化
    translate_model = create_translation_chain()
    reasoning_chain, memory = create_reasoning_chain()
    
    while True:
        # ユーザー入力の受け取り
        user_input = input("\nあなた: ")
        
        if user_input.lower() == 'exit':
            print("チャットを終了します。")
            break

        try:
            # Step 1: 日本語から英語への翻訳
            print("\nStep 1. 日本語から英語への翻訳:")
            english_query = ""
            for chunk in translate_to_english(translate_model, user_input):
                english_query += chunk
                print(chunk, end="", flush=True)
            print("\n" + "-" * 100)

            # Step 2: 英語での回答生成
            print("Step 2. 英語での回答生成:")
            english_response = ""
            for chunk in get_reasoning_response(reasoning_chain, memory, english_query):
                english_response += chunk
                print(chunk, end="", flush=True)
            print("\n" + "-" * 100)

            # Step 3: 英語から日本語への翻訳
            print("Step 3. 英語から日本語への翻訳:")
            # </think>以降のテキストを抽出
            last_think_match = re.search(r'</think>', english_response, re.DOTALL)
            if last_think_match:
                english_answer = english_response[last_think_match.end():]
            else:
                english_answer = english_response

            print("AI: ", end="", flush=True)
            for chunk in translate_to_japanese(translate_model, english_answer):
                print(chunk, end="", flush=True)
            print("\n" + "-" * 100)

        except Exception as e:
            print(f"エラーが発生しました: {str(e)}")

if __name__ == "__main__":
    main()

3.4 実行例

以下、三段論法が成立するか試してみると。

チャットを開始します。終了するには 'exit' と入力してください。

あなた: すべての鳥は飛べる。ペンギンは鳥だ。ペンギンは飛べますか?

Step 1. 日本語から英語への翻訳:
All birds can fly. Penguins are birds. Can penguins fly?
------------------
Step 2. 英語での回答生成:
<think>
Okay, so I'm trying to figure out if penguins can fly because the user asked
"Can penguins fly?" The initial thought was that all birds can fly, but
that's not accurate because not all birds are able to fly. Penguins are
birds, but they don't actually fly on their own; they rely on other sources
of air, like water and ice.

First, I need to understand the difference between birds that fly naturally
and those that require assistance from other creatures. Penguins are one
example where this happens. They can't fly by themselves, so we have to talk
about how they get oxygen when they're flying. Penguins are warm-blooded,
which means their body temperature is similar to their environment, and they
regulate their body temperature using sweat glands. When they need to fly,
these sweat glands work together with other systems like the respiratory
system to pump blood around them.

I should also consider the biology of penguins. They have a streamlined body
 shape called a bill, which helps them move efficiently through water. Their
wings are actually covered in feathers, and they use these feathers along
with their airways for circulation. Penguins don't fly the way we do;
instead, they hover by flapping their wings. Flapping their wings is a small
act of movement that allows them to ascend quickly.

Another point is the role of their environment. Penguins need oxygen from
ice cubes in the water and sunlight in the morning. This circulatory system
supports their flight because without oxygen, they can't move efficiently.
Penguins also have a specialized sense called the blunderboard, which helps
them locate areas with less oxygen to avoid getting stuck.

I should explain that while penguins aren't birds that fly on their own,
they do require external air sources and assistance from other creatures
like ice and sunlight to stay aloft. This makes them unique and why they
exist in certain regions where there's a lot of cold water and short days.
</think>

Penguins are not birds that can fly on their own; they require additional
sources of air and help from other creatures, such as ice and sunlight, to
maintain altitude during flight. Penguins are warm-blooded and use
techniques like flapping wings and circulatory systems to move efficiently
in water, relying on oxygen from ice and sunlight for energy. Their
streamlined bodies and specialized sense allow them to hover by flapping
wings, making penguins unique due to their reliance on external air support.
------------------
Step 3. 英語から日本語への翻訳:
AI: ペンギンは、自力で飛ぶ鳥ではありません。飛行には、氷や太陽光などの補助的なエネルギー源が
必要であり、他の生物の助けを借りて高度を維持します。ペンギンは温血で、翼を振ったり循環器系を利
用したりして水中で効率的に移動し、氷や太陽光の酸素を利用してエネルギーを得ています。流線型な体
と特殊な感覚器官により、翼を振って浮遊する事ができ、外からの空気の助けを借りることでペンギンは
ユニークな存在です。
------------------

推論部分をgpt-4oに日本語訳してもらうと以下になります。

<think>
オーケー、ペンギンが飛べるかどうかを調べているのは、ユーザーが「ペンギンは飛べるの?」と聞いて
きたからだね。最初は「すべての鳥は飛べる」と思ったけど、それは正確じゃない。すべての鳥が飛べる
わけではないからね。ペンギンは鳥だけど、実際には自分だけで飛ぶことはなく、水や氷といった他の要
素に頼っている。

まず、自然に飛べる鳥と、他のものの助けを必要とする鳥の違いを理解する必要がある。ペンギンはその
典型的な例だね。彼らは自分だけで飛ぶことができないので、飛ぶときに酸素をどう得ているかについて
考えないといけない。ペンギンは恒温動物だから、体温は環境に近い温度で一定に保たれている。汗腺を
使って体温を調節しているんだ。飛ぶ必要があるときは、この汗腺が呼吸器系と協力して血液を体内に循
環させる仕組みになっている。

ペンギンの生物学的特徴についても考えるべきだね。彼らは「ビル」と呼ばれる流線型の体型をしてい
て、水中を効率的に動くのに役立っている。翼には実は羽が生えていて、この羽を気道とともに使って循
環を助けている。ペンギンは私たちのように飛ぶわけではなく、翼をバタバタさせることでホバリングす
るんだ。翼をバタバタさせるのは小さな動きだけど、それが素早く上昇するのに役立っている。

次に、環境の役割についても重要だね。ペンギンは、水中の氷や朝の日光から酸素を必要としている。こ
の循環システムが飛行をサポートしていて、酸素がなければ効率的に動くことができないんだ。ペンギン
には「ブランダーボード」と呼ばれる特殊な感覚もあって、酸素が少ない場所を見つけることで、動けな
くなるのを防いでいる。

ペンギンは自分だけで飛ぶ鳥ではないけれど、外部の空気源や、氷や日光といった他の要素の助けが必要
なんだ。これがペンギンをユニークにしていて、寒冷な水が多い場所や、日が短い地域に生息する理由で
もあるんだね。
</think>

冒頭で自ら間違いに気づいて訂正しています、回答までの過程が見れるのが面白いですね。
1.5bという小さいモデルでここまで推論して回答生成できるのは凄いですね。

4.モデルを変えてみる

4.1 DeepSeek-R1-ReDistill-Qwen-1.5B-v1.0-GGUF:Q6_Kの場合

https://huggingface.co/bartowski/DeepSeek-R1-ReDistill-Qwen-1.5B-v1.0-GGUF

デフォルトの1.5Bより判りやすいですね。同じサイズならこちらのモデルを使った方が良さそうです。

AI: ペンギンは繁殖期に独特の飛行のような行動をしますが、実際には翼が泳ぎをするために進化した
ものであり、通常の鳥のように飛行する能力はありません。バランスと操縦性を維持するために泳ぐのが
彼らの主な手段です。翼構造が鳥類の一般的なものとは異なり、翼が上昇力を生み出すことができないた
め、特定の条件下での飛行は限られています。そのため、ペンギンは特定の行動に優れているものの、通
常の鳥のように飛ぶことはできません。

4.2 DeepSeek-R1-Distill-Qwen-7B-GGUF:Q4_K_Mの場合

https://huggingface.co/bartowski/DeepSeek-R1-Distill-Qwen-7B-GGUF

4.1の1.5Bよりも判りやすい日本語で回答生成できてますね。

AI: ペンギンは鳥であるにもかかわらず、水中で生活するために進化したため、飛べません。一般的に
「すべての鳥は飛べる」と述べるのは、空を飛ぶための翼構造を持つ鳥に当てはまりますが、ペンギンは
水中で生活するように進化しました。彼らの身体的特徴や行動は、飛ぶよりも泳ぐことや潜水をするのに
適しています。ペンギンの flippers は運動とつかみ、潜水時にはつかみやすい表面になっています。
そのため、ペンギンは翼を大きく振ることで空を飛ぶように動かすことができないので、ペンギンが異な
る生活様式に適した移動戦略を用いて飛べないことを示しています。

4.3 DeepSeek-R1-Distill-Qwen-14B-GGUF:Q6_Kの場合

https://huggingface.co/bartowski/DeepSeek-R1-Distill-Qwen-14B-GGUF

日本語も綺麗だし、推論の途中経過が良いですね。やはりモデルのサイズと回答の精度は比例するのがよく判りますね。

AI: 提示された問題は論理的推論の練習で、2つの前提に基づいています。「すべての鳥は飛べ」と「ペンギンは鳥である」です。この論理系から結論として、ペンギンも飛べると導かれますが、これは現実の世界での知識に反します。なぜならペンギンは飛べないからです。

**分析:**

1. **前提評価:**
   - 「すべての鳥は飛べ」という前提は単純化しています。
      ostrich や emu など、飛行能力がない鳥もいます。

2. **ペンギンの特徴:**
   - ペンギンは鳥類に属していますが、泳ぎが得意なため、飛ぶことはできません。
     翼は水中で効率的に泳ぐために進化しました。

3. **飛翔の定義:**
   - 飛翔とは空を移動することですが、ペンギンはそれをしません。
     代わりに、翼を使って潜水や泳ぎをするのです。

4. **結論:**
   - 論理的な構造から、ペンギンも飛べると導かれますが、現実の世界の生物学では、
     ペンギンの身体的特徴から飛行は不可能であることが示されています。

この例は、一般的な化合物が特定のケースに適用されると、誤った結論につながる可能性があることを示しています。ペンギンは、最初の前提で述べた規則に反する例

5.注意点

5.1 日本語の人名など固有名詞が弱い

日英翻訳する場合に、日本語の人名など複数の読み方や、書き方が存在する単語は、
誤訳する場合が多々あります。

5.2 </think>タグが抜ける場合がある

推論せずに直接回答を考えた際に、以下のように</think>タグが抜けた回答が生成される場合があります。

あなた: 100迄の素数を全て教えて下さい

Step 1. 日本語から英語への翻訳:
Tell me all the prime numbers up to 100.
---------------
Step 2. 英語での回答生成:
<think>

Sure! Let me list them out for you:

2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
31, 37, 41, 43, 47, 53, 59, 61, 67,
71, 73, 79, 83, 89, 97.

These are all the prime numbers from 2 to 100.
---------------
Step 3. 英語から日本語への翻訳:
AI: <think>
---------------

Discussion