0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【翻訳モデル】循環翻訳で「ばらつき」 LP2_1_3

Last updated at Posted at 2025-07-21
QQQ :bow_tone1:3Q!
📚 【翻訳モデル】200の言語を翻訳できるNLLBモデル体験 LM2_1 :arrow_heading_up:

循環翻訳のばらつき

  • trans_drift.py
import ctranslate2
from transformers import AutoTokenizer
from language_map import NLLB_LANG_CODES
import sys
import os

# コンソールのエンコーディングをUTF-8に設定します
if sys.platform.startswith('win'):
    os.system('chcp 65001 > nul')
    sys.stdout.reconfigure(encoding='utf-8')

# モデルロード(グローバル)
model_path = "../models/nllb-ct2"
translator = ctranslate2.Translator(model_path, device="cuda")  # GPUを使用してメモリ問題を回避
tokenizer = AutoTokenizer.from_pretrained("facebook/nllb-200-distilled-600M")


def translate_text(text, src_lang, tgt_lang):
    """単回翻訳関数"""
    # トークン化 + ソース言語タグと終了タグを追加
    tokens = tokenizer.convert_ids_to_tokens(
        tokenizer.encode(text, add_special_tokens=False)
    )
    inputs = [f"{src_lang}"] + tokens + ["</s>"]

    # 翻訳
    translated = translator.translate_batch(
        [inputs], target_prefix=[[f"{tgt_lang}"]], beam_size=4
    )
    hypo_tokens = translated[0].hypotheses[0]

    # デコード
    output = tokenizer.decode(
        tokenizer.convert_tokens_to_ids(hypo_tokens), skip_special_tokens=False
    )
    # 言語タグと特殊トークンを削除
    output = output.replace(f"{tgt_lang}", "").replace("</s>", "").strip()
    return output


def circular_translation_test(original_text, languages, cycles=3):
    """循環翻訳テスト"""
    print(f"元のテキスト: {original_text}")
    print(f"言語シーケンス: {' -> '.join(languages)}")
    print(f"循環回数: {cycles}")
    print("=" * 60)

    current_text = original_text
    current_lang = languages[0]

    for cycle in range(cycles):
        print(f"\n{cycle + 1} ラウンド:")
        print("-" * 40)

        for i, next_lang in enumerate(
            languages[1:] + [languages[0]]
        ):  # 最初の言語に循環
            print(f"{current_lang} -> {next_lang}: {current_text}")

            # 翻訳
            translated_text = translate_text(current_text, current_lang, next_lang)
            print(f"翻訳結果: {translated_text}")
            print()

            # 現在の状態を更新
            current_text = translated_text
            current_lang = next_lang

    print("=" * 60)
    print(f"最終結果 ({current_lang}): {current_text}")

    # 類似度を計算(簡単な方法:文字長の変化)
    original_len = len(original_text)
    final_len = len(current_text)
    change_ratio = abs(final_len - original_len) / original_len * 100

    print(f"テキスト長の変化: {original_len} -> {final_len} (変化 {change_ratio:.1f}%)")

    return current_text


def multi_language_drift_test(original_text, languages_list, cycles=2):
    """多言語組み合わせのドリフトテスト"""
    print("多言語翻訳ドリフトテスト")
    print("=" * 60)

    for i, languages in enumerate(languages_list):
        print(f"\nテスト組み合わせ {i+1}: {' -> '.join(languages)}")
        print("-" * 40)
        circular_translation_test(original_text, languages, cycles)
        print()


if __name__ == "__main__":
    # テストテキスト
    test_texts = [
        "今日は天気が良くて、公園を散歩したいです。",
        "The quick brown fox jumps over the lazy dog.",
        "你好,今天过得怎么样?",
        "人工知能は私たちの世界を変えています。",
    ]

    # 言語組み合わせ
    language_combinations = [
        ["zho_Hans", "eng_Latn", "jpn_Jpan", "zho_Hans"],  # 中日英日
        ["eng_Latn", "fra_Latn", "vie_Latn", "eng_Latn"],  # 英仏越英
        ["jpn_Jpan", "kor_Hang", "zho_Hans", "jpn_Jpan"],  # 日韓中日
        ["zho_Hans", "eng_Latn", "fra_Latn", "vie_Latn", "zho_Hans"],  # 中日仏越中
    ]

    for test_text in test_texts:
        # テストを実行
        multi_language_drift_test(test_text, language_combinations, cycles=2)

    print("\n" + "=" * 60)
    print("翻訳ドリフト現象の説明:")
    print("1. 各翻訳で微細な意味の変化が導入される可能性があります")
    print("2. 異なる言語間の表現方法の違いにより情報が失われます")
    print("3. モデルの翻訳プロセスにおける不確実性が蓄積されます")
    print("4. 一部の概念は異なる言語で完全に対応する表現がない場合があります")
    print("5. 循環翻訳はこれらの誤差を増幅し、最終的に意味のドリフトを引き起こします")

  • ↓↓ 実行結果 ↓↓
PS I:\DocTranslate\scripts> python .\trans_drift.py
多言語翻訳ドリフトテスト
============================================================

テスト組み合わせ 1: zho_Hans -> eng_Latn -> jpn_Jpan -> zho_Hans
----------------------------------------
元のテキスト: 今日は天気が良くて、公園を散歩したいです。
言語シーケンス: zho_Hans -> eng_Latn -> jpn_Jpan -> zho_Hans
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: 今日は天気が良くて、公園を散歩したいです。
翻訳結果: I want the weather to be nice and I want to go for a walk in the park.

eng_Latn -> jpn_Jpan: I want the weather to be nice and I want to go for a walk in the park.
翻訳結果: 天気が良いと公園で散歩したい.

jpn_Jpan -> zho_Hans: 天気が良いと公園で散歩したい.
翻訳結果: 如果天气好,我想在公园散步.

zho_Hans -> zho_Hans: 如果天气好,我想在公园散步.
翻訳結果: 如果天气好,我想在公园散步.


第 2 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: 如果天气好,我想在公园散步.
翻訳結果: If the weather is good, I want to take a walk in the park.

eng_Latn -> jpn_Jpan: If the weather is good, I want to take a walk in the park.
翻訳結果: 天気が良いなら,公園で散歩したい.

jpn_Jpan -> zho_Hans: 天気が良いなら,公園で散歩したい.
翻訳結果: 如果天气好,我想在公园散步.

zho_Hans -> zho_Hans: 如果天气好,我想在公园散步.
翻訳結果: 如果天气好,我想在公园散步.

============================================================
最終結果 (zho_Hans): 如果天气好,我想在公园散步.
テキスト長の変化: 21 -> 14 (変化 33.3%)


テスト組み合わせ 2: eng_Latn -> fra_Latn -> vie_Latn -> eng_Latn
----------------------------------------
元のテキスト: 今日は天気が良くて、公園を散歩したいです。
言語シーケンス: eng_Latn -> fra_Latn -> vie_Latn -> eng_Latn
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
eng_Latn -> fra_Latn: 今日は天気が良くて、公園を散歩したいです。
翻訳結果: Aujourd'hui, la météo est bonne. Je veux faire une promenade dans le parc.

fra_Latn -> vie_Latn: Aujourd'hui, la météo est bonne. Je veux faire une promenade dans le parc.
翻訳結果: Hôm nay thời tiết rất tốt nên tôi muốn đi dạo trong công viên.

vie_Latn -> eng_Latn: Hôm nay thời tiết rất tốt nên tôi muốn đi dạo trong công viên.
翻訳結果: The weather is good today, so I wanted to take a walk in the park.

eng_Latn -> eng_Latn: The weather is good today, so I wanted to take a walk in the park.
翻訳結果: The weather is good today, so I wanted to take a walk in the park.


第 2 ラウンド:
----------------------------------------
eng_Latn -> fra_Latn: The weather is good today, so I wanted to take a walk in the park.
翻訳結果: Le temps est beau aujourd'hui, alors j'ai voulu me promener dans le parc.

fra_Latn -> vie_Latn: Le temps est beau aujourd'hui, alors j'ai voulu me promener dans le parc.
翻訳結果: Thời tiết hôm nay đẹp, nên tôi muốn đi dạo trong công viên.

vie_Latn -> eng_Latn: Thời tiết hôm nay đẹp, nên tôi muốn đi dạo trong công viên.
翻訳結果: The weather is nice today, so I wanted to take a walk in the park.

eng_Latn -> eng_Latn: The weather is nice today, so I wanted to take a walk in the park.
翻訳結果: The weather is nice today, so I wanted to take a walk in the park.

============================================================
最終結果 (eng_Latn): The weather is nice today, so I wanted to take a walk in the park.
テキスト長の変化: 21 -> 66 (変化 214.3%)


テスト組み合わせ 3: jpn_Jpan -> kor_Hang -> zho_Hans -> jpn_Jpan
----------------------------------------
元のテキスト: 今日は天気が良くて、公園を散歩したいです。
言語シーケンス: jpn_Jpan -> kor_Hang -> zho_Hans -> jpn_Jpan
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
jpn_Jpan -> kor_Hang: 今日は天気が良くて、公園を散歩したいです。
翻訳結果: 오늘 날씨가 좋아서 공원을 걷고 싶어요.

kor_Hang -> zho_Hans: 오늘 날씨가 좋아서 공원을 걷고 싶어요.
翻訳結果: 今天天气好,我想走在公园里.

zho_Hans -> jpn_Jpan: 今天天气好,我想走在公园里.
翻訳結果: 今日は天気が良いので,公園を歩いたかった.

jpn_Jpan -> jpn_Jpan: 今日は天気が良いので,公園を歩いたかった.
翻訳結果: 今日は天気が良いので,公園を歩いたかった.


第 2 ラウンド:
----------------------------------------
jpn_Jpan -> kor_Hang: 今日は天気が良いので,公園を歩いたかった.
翻訳結果: 오늘 날씨가 좋기 때문에 공원을 걷고 싶었습니다.

kor_Hang -> zho_Hans: 오늘 날씨가 좋기 때문에 공원을 걷고 싶었습니다.
翻訳結果: 今天天气很好,所以我想走在公园里.

zho_Hans -> jpn_Jpan: 今天天气很好,所以我想走在公园里.
翻訳結果: 今日は天気が良いので,公園を歩いたかった.

jpn_Jpan -> jpn_Jpan: 今日は天気が良いので,公園を歩いたかった.
翻訳結果: 今日は天気が良いので,公園を歩いたかった.

============================================================
最終結果 (jpn_Jpan): 今日は天気が良いので,公園を歩いたかった.
テキスト長の変化: 21 -> 21 (変化 0.0%)


テスト組み合わせ 4: zho_Hans -> eng_Latn -> fra_Latn -> vie_Latn -> zho_Hans
----------------------------------------
元のテキスト: 今日は天気が良くて、公園を散歩したいです。
言語シーケンス: zho_Hans -> eng_Latn -> fra_Latn -> vie_Latn -> zho_Hans
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: 今日は天気が良くて、公園を散歩したいです。
翻訳結果: I want the weather to be nice and I want to go for a walk in the park.

eng_Latn -> fra_Latn: I want the weather to be nice and I want to go for a walk in the park.
翻訳結果: Je veux que le temps soit agréable et je veux aller faire une promenade dans le parc.

fra_Latn -> vie_Latn: Je veux que le temps soit agréable et je veux aller faire une promenade dans le parc.
翻訳結果: Tôi muốn thời tiết tốt đẹp và tôi muốn đi dạo trong công viên.

vie_Latn -> zho_Hans: Tôi muốn thời tiết tốt đẹp và tôi muốn đi dạo trong công viên.
翻訳結果: 我希望天气好,我想散步公园.

zho_Hans -> zho_Hans: 我希望天气好,我想散步公园.
翻訳結果: 我希望天气好,我想散步公园.


第 2 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: 我希望天气好,我想散步公园.
翻訳結果: I want the weather to be good and I want to walk in the park.

eng_Latn -> fra_Latn: I want the weather to be good and I want to walk in the park.
翻訳結果: Je veux que le temps soit bon et je veux marcher dans le parc.

fra_Latn -> vie_Latn: Je veux que le temps soit bon et je veux marcher dans le parc.
翻訳結果: Tôi muốn thời tiết tốt đẹp và tôi muốn đi bộ trong công viên.

vie_Latn -> zho_Hans: Tôi muốn thời tiết tốt đẹp và tôi muốn đi bộ trong công viên.
翻訳結果: 我想要美好的天气,我想在公园里散步.

zho_Hans -> zho_Hans: 我想要美好的天气,我想在公园里散步.
翻訳結果: 我想要美好的天气,我想在公园里散步.

============================================================
最終結果 (zho_Hans): 我想要美好的天气,我想在公园里散步.
テキスト長の変化: 21 -> 18 (変化 14.3%)

多言語翻訳ドリフトテスト
============================================================

テスト組み合わせ 1: zho_Hans -> eng_Latn -> jpn_Jpan -> zho_Hans
----------------------------------------
元のテキスト: The quick brown fox jumps over the lazy dog.
言語シーケンス: zho_Hans -> eng_Latn -> jpn_Jpan -> zho_Hans
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: The quick brown fox jumps over the lazy dog.
翻訳結果: The quick brown fox jumps over the lazy dog.

eng_Latn -> jpn_Jpan: The quick brown fox jumps over the lazy dog.
翻訳結果: スピードブラウンフォックスは怠け犬を飛び越えます.

jpn_Jpan -> zho_Hans: スピードブラウンフォックスは怠け犬を飛び越えます.
翻訳結果: 快速棕<unk>狐<unk>可以跳过一个<unk>狗.

zho_Hans -> zho_Hans: 快速棕<unk>狐<unk>可以跳过一个<unk>狗.
翻訳結果: 一个快速棕<unk>狐<unk>可以跳过一只<unk>狗.


第 2 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: 一个快速棕<unk>狐<unk>可以跳过一只<unk>狗.
翻訳結果: A fast palm fox can jump over a rabbit dog.

eng_Latn -> jpn_Jpan: A fast palm fox can jump over a rabbit dog.
翻訳結果: 急速なパーム狐はウサギ犬を飛び越える.

jpn_Jpan -> zho_Hans: 急速なパーム狐はウサギ犬を飛び越える.
翻訳結果: 一个快速的棕<unk>狐<unk>跳过<unk>子.

zho_Hans -> zho_Hans: 一个快速的棕<unk>狐<unk>跳过<unk>子.
翻訳結果: 一个快速的棕<unk>狐<unk>跳过<unk>子.

============================================================
最終結果 (zho_Hans): 一个快速的棕<unk>狐<unk>跳过<unk>子.
テキスト長の変化: 44 -> 26 (変化 40.9%)


テスト組み合わせ 2: eng_Latn -> fra_Latn -> vie_Latn -> eng_Latn
----------------------------------------
元のテキスト: The quick brown fox jumps over the lazy dog.
言語シーケンス: eng_Latn -> fra_Latn -> vie_Latn -> eng_Latn
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
eng_Latn -> fra_Latn: The quick brown fox jumps over the lazy dog.
翻訳結果: Le renard brun rapide saute sur le chien paresseux.

fra_Latn -> vie_Latn: Le renard brun rapide saute sur le chien paresseux.
翻訳結果: Con cáo nâu nhanh chóng nhảy lên trên con chó lười biếng.

vie_Latn -> eng_Latn: Con cáo nâu nhanh chóng nhảy lên trên con chó lười biếng.
翻訳結果: The brown fox jumps quickly on a lazy dog.

eng_Latn -> eng_Latn: The brown fox jumps quickly on a lazy dog.
翻訳結果: The brown fox jumps quickly on a lazy dog.


第 2 ラウンド:
----------------------------------------
eng_Latn -> fra_Latn: The brown fox jumps quickly on a lazy dog.
翻訳結果: Le renard brun saute rapidement sur un chien paresseux.

fra_Latn -> vie_Latn: Le renard brun saute rapidement sur un chien paresseux.
翻訳結果: Con cáo màu nâu nhanh chóng nhảy lên một con chó lười biếng.

vie_Latn -> eng_Latn: Con cáo màu nâu nhanh chóng nhảy lên một con chó lười biếng.
翻訳結果: The brown fox quickly jumped on a lazy dog.

eng_Latn -> eng_Latn: The brown fox quickly jumped on a lazy dog.
翻訳結果: The brown fox quickly jumped on a lazy dog.

============================================================
最終結果 (eng_Latn): The brown fox quickly jumped on a lazy dog.
テキスト長の変化: 44 -> 43 (変化 2.3%)


テスト組み合わせ 3: jpn_Jpan -> kor_Hang -> zho_Hans -> jpn_Jpan
----------------------------------------
元のテキスト: The quick brown fox jumps over the lazy dog.
言語シーケンス: jpn_Jpan -> kor_Hang -> zho_Hans -> jpn_Jpan
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
jpn_Jpan -> kor_Hang: The quick brown fox jumps over the lazy dog.
翻訳結果: "이빨한 갈색 여우가 게으른 개를 뛰어넘는다.

kor_Hang -> zho_Hans: "이빨한 갈색 여우가 게으른 개를 뛰어넘는다.
翻訳結果: "一个棕色的<unk>跳过一个<unk>的狗.

zho_Hans -> jpn_Jpan: "一个棕色的<unk>跳过一个<unk>的狗.
翻訳結果: "茶色の<unk>が<unk>の犬を跳ねる.

jpn_Jpan -> jpn_Jpan: "茶色の<unk>が<unk>の犬を跳ねる.
翻訳結果: "茶色の<unk>は<unk>の犬を跳ねる.


第 2 ラウンド:
----------------------------------------
jpn_Jpan -> kor_Hang: "茶色の<unk>は<unk>の犬を跳ねる.
翻訳結果: "갈색 <unk>은 <unk>의 개를 뛰어넘는다.

kor_Hang -> zho_Hans: "갈색 <unk>은 <unk>의 개를 뛰어넘는다.
翻訳結果: "棕色<unk>比<unk>狗还要高.

zho_Hans -> jpn_Jpan: "棕色<unk>比<unk>狗还要高.
翻訳結果: 青い<unk>は<unk>犬よりも高い.

jpn_Jpan -> jpn_Jpan: 青い<unk>は<unk>犬よりも高い.
翻訳結果: 青い<unk>は<unk>犬よりも高い.

============================================================
最終結果 (jpn_Jpan): 青い<unk>は<unk>犬よりも高い.
テキスト長の変化: 44 -> 20 (変化 54.5%)


テスト組み合わせ 4: zho_Hans -> eng_Latn -> fra_Latn -> vie_Latn -> zho_Hans
----------------------------------------
元のテキスト: The quick brown fox jumps over the lazy dog.
言語シーケンス: zho_Hans -> eng_Latn -> fra_Latn -> vie_Latn -> zho_Hans
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: The quick brown fox jumps over the lazy dog.
翻訳結果: The quick brown fox jumps over the lazy dog.

eng_Latn -> fra_Latn: The quick brown fox jumps over the lazy dog.
翻訳結果: Le renard brun rapide saute sur le chien paresseux.

fra_Latn -> vie_Latn: Le renard brun rapide saute sur le chien paresseux.
翻訳結果: Con cáo nâu nhanh chóng nhảy lên trên con chó lười biếng.

vie_Latn -> zho_Hans: Con cáo nâu nhanh chóng nhảy lên trên con chó lười biếng.
翻訳結果: 棕<unk>狐<unk>快速跳上一个<unk>狗.

zho_Hans -> zho_Hans: 棕<unk>狐<unk>快速跳上一个<unk>狗.
翻訳結果: 棕<unk>狐<unk>快速跳上一只<unk>狗.


第 2 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: 棕<unk>狐<unk>快速跳上一只<unk>狗.
翻訳結果: Palm fox jumps quickly on a rabbit.

eng_Latn -> fra_Latn: Palm fox jumps quickly on a rabbit.
翻訳結果: Le renard de palme saute rapidement sur un lapin.

fra_Latn -> vie_Latn: Le renard de palme saute rapidement sur un lapin.
翻訳結果: Con cáo cánh cọ nhanh chóng nhảy lên một con thỏ.

vie_Latn -> zho_Hans: Con cáo cánh cọ nhanh chóng nhảy lên một con thỏ.
翻訳結果: 棕<unk>狐<unk>快速跳上一个<unk>子.

zho_Hans -> zho_Hans: 棕<unk>狐<unk>快速跳上一个<unk>子.
翻訳結果: 棕<unk>狐<unk>快速跳上一个<unk>子.

============================================================
最終結果 (zho_Hans): 棕<unk>狐<unk>快速跳上一个<unk>子.
テキスト長の変化: 44 -> 25 (変化 43.2%)

多言語翻訳ドリフトテスト
============================================================

テスト組み合わせ 1: zho_Hans -> eng_Latn -> jpn_Jpan -> zho_Hans
----------------------------------------
元のテキスト: こんにちは、お元気ですか?
言語シーケンス: zho_Hans -> eng_Latn -> jpn_Jpan -> zho_Hans
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: こんにちは、お元気ですか?
翻訳結果: Hello, how are you?

eng_Latn -> jpn_Jpan: Hello, how are you?
翻訳結果: こんにちは どうですか?

jpn_Jpan -> zho_Hans: こんにちは どうですか?
翻訳結果: 您好,您好吗?

zho_Hans -> zho_Hans: 您好,您好吗?
翻訳結果: 你好,你好吗?


第 2 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: 你好,你好吗?
翻訳結果: Hello, are you okay?

eng_Latn -> jpn_Jpan: Hello, are you okay?
翻訳結果: こんにちは,大丈夫ですか?

jpn_Jpan -> zho_Hans: こんにちは,大丈夫ですか?
翻訳結果: 你好吗?

zho_Hans -> zho_Hans: 你好吗?
翻訳結果: 你好吗?

============================================================
最終結果 (zho_Hans): 你好吗?
テキスト長の変化: 13 -> 4 (変化 69.2%)


テスト組み合わせ 2: eng_Latn -> fra_Latn -> vie_Latn -> eng_Latn
----------------------------------------
元のテキスト: こんにちは、お元気ですか?
言語シーケンス: eng_Latn -> fra_Latn -> vie_Latn -> eng_Latn
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
eng_Latn -> fra_Latn: こんにちは、お元気ですか?
翻訳結果: Bonjour, vous allez bien ?

fra_Latn -> vie_Latn: Bonjour, vous allez bien ?
翻訳結果: Chào anh, anh ổn chứ?

vie_Latn -> eng_Latn: Chào anh, anh ổn chứ?
翻訳結果: Hey, man, are you okay?

eng_Latn -> eng_Latn: Hey, man, are you okay?
翻訳結果: Hey, man, are you okay?


第 2 ラウンド:
----------------------------------------
eng_Latn -> fra_Latn: Hey, man, are you okay?
翻訳結果: Hé, mec, tu es bien ?

fra_Latn -> vie_Latn: Hé, mec, tu es bien ?
翻訳結果: Này, anh bạn, anh ổn chứ?

vie_Latn -> eng_Latn: Này, anh bạn, anh ổn chứ?
翻訳結果: Hey, buddy, are you okay?

eng_Latn -> eng_Latn: Hey, buddy, are you okay?
翻訳結果: Hey, buddy, are you okay?

============================================================
最終結果 (eng_Latn): Hey, buddy, are you okay?
テキスト長の変化: 13 -> 25 (変化 92.3%)


テスト組み合わせ 3: jpn_Jpan -> kor_Hang -> zho_Hans -> jpn_Jpan
----------------------------------------
元のテキスト: こんにちは、お元気ですか?
言語シーケンス: jpn_Jpan -> kor_Hang -> zho_Hans -> jpn_Jpan
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
jpn_Jpan -> kor_Hang: こんにちは、お元気ですか?
翻訳結果: 안녕하세요, 안녕하세요?

kor_Hang -> zho_Hans: 안녕하세요, 안녕하세요?
翻訳結果: 您好,您好?

zho_Hans -> jpn_Jpan: 您好,您好?
翻訳結果: こんにちは,こんにちは?

jpn_Jpan -> jpn_Jpan: こんにちは,こんにちは?
翻訳結果: こんにちは,こんにちは?


第 2 ラウンド:
----------------------------------------
jpn_Jpan -> kor_Hang: こんにちは,こんにちは?
翻訳結果: 안녕하세요, 안녕하세요?

kor_Hang -> zho_Hans: 안녕하세요, 안녕하세요?
翻訳結果: 您好,您好?

zho_Hans -> jpn_Jpan: 您好,您好?
翻訳結果: こんにちは,こんにちは?

jpn_Jpan -> jpn_Jpan: こんにちは,こんにちは?
翻訳結果: こんにちは,こんにちは?

============================================================
最終結果 (jpn_Jpan): こんにちは,こんにちは?
テキスト長の変化: 13 -> 12 (変化 7.7%)


テスト組み合わせ 4: zho_Hans -> eng_Latn -> fra_Latn -> vie_Latn -> zho_Hans
----------------------------------------
元のテキスト: こんにちは、お元気ですか?
言語シーケンス: zho_Hans -> eng_Latn -> fra_Latn -> vie_Latn -> zho_Hans
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: こんにちは、お元気ですか?
翻訳結果: Hello, how are you?

eng_Latn -> fra_Latn: Hello, how are you?
翻訳結果: Bonjour, comment allez-vous ?

fra_Latn -> vie_Latn: Bonjour, comment allez-vous ?
翻訳結果: Chào anh, anh khỏe không?

vie_Latn -> zho_Hans: Chào anh, anh khỏe không?
翻訳結果: 你好吗?

zho_Hans -> zho_Hans: 你好吗?
翻訳結果: 你好吗?


第 2 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: 你好吗?
翻訳結果: Are you all right?

eng_Latn -> fra_Latn: Are you all right?
翻訳結果: Vous allez bien ?

fra_Latn -> vie_Latn: Vous allez bien ?
翻訳結果: Anh ổn chứ?

vie_Latn -> zho_Hans: Anh ổn chứ?
翻訳結果: 你好吗?

zho_Hans -> zho_Hans: 你好吗?
翻訳結果: 你好吗?

============================================================
最終結果 (zho_Hans): 你好吗?
テキスト長の変化: 13 -> 4 (変化 69.2%)

多言語翻訳ドリフトテスト
============================================================

テスト組み合わせ 1: zho_Hans -> eng_Latn -> jpn_Jpan -> zho_Hans
----------------------------------------
元のテキスト: 人工知能は私たちの世界を変えています。
言語シーケンス: zho_Hans -> eng_Latn -> jpn_Jpan -> zho_Hans
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: 人工知能は私たちの世界を変えています。
翻訳結果: Artificial intelligence is changing our world.

eng_Latn -> jpn_Jpan: Artificial intelligence is changing our world.
翻訳結果: 人工知能は私たちの世界を変化させています

jpn_Jpan -> zho_Hans: 人工知能は私たちの世界を変化させています
翻訳結果: 人工智能正在改变我们的世界.

zho_Hans -> zho_Hans: 人工智能正在改变我们的世界.
翻訳結果: 人工智能正在改变我们的世界.


第 2 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: 人工智能正在改变我们的世界.
翻訳結果: Artificial intelligence is changing our world.

eng_Latn -> jpn_Jpan: Artificial intelligence is changing our world.
翻訳結果: 人工知能は私たちの世界を変化させています

jpn_Jpan -> zho_Hans: 人工知能は私たちの世界を変化させています
翻訳結果: 人工智能正在改变我们的世界.

zho_Hans -> zho_Hans: 人工智能正在改变我们的世界.
翻訳結果: 人工智能正在改变我们的世界.

============================================================
最終結果 (zho_Hans): 人工智能正在改变我们的世界.
テキスト長の変化: 19 -> 14 (変化 26.3%)


テスト組み合わせ 2: eng_Latn -> fra_Latn -> vie_Latn -> eng_Latn
----------------------------------------
元のテキスト: 人工知能は私たちの世界を変えています。
言語シーケンス: eng_Latn -> fra_Latn -> vie_Latn -> eng_Latn
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
eng_Latn -> fra_Latn: 人工知能は私たちの世界を変えています。
翻訳結果: L'intelligence artificielle change notre monde.

fra_Latn -> vie_Latn: L'intelligence artificielle change notre monde.
翻訳結果: Trí tuệ nhân tạo đang thay đổi thế giới của chúng ta.

vie_Latn -> eng_Latn: Trí tuệ nhân tạo đang thay đổi thế giới của chúng ta.
翻訳結果: AI is changing our world.

eng_Latn -> eng_Latn: AI is changing our world.
翻訳結果: AI is changing our world.


第 2 ラウンド:
----------------------------------------
eng_Latn -> fra_Latn: AI is changing our world.
翻訳結果: L'IA change notre monde.

fra_Latn -> vie_Latn: L'IA change notre monde.
翻訳結果: AI thay đổi thế giới của chúng ta.

vie_Latn -> eng_Latn: AI thay đổi thế giới của chúng ta.
翻訳結果: AI is changing our world.

eng_Latn -> eng_Latn: AI is changing our world.
翻訳結果: AI is changing our world.

============================================================
最終結果 (eng_Latn): AI is changing our world.
テキスト長の変化: 19 -> 25 (変化 31.6%)


テスト組み合わせ 3: jpn_Jpan -> kor_Hang -> zho_Hans -> jpn_Jpan
----------------------------------------
元のテキスト: 人工知能は私たちの世界を変えています。
言語シーケンス: jpn_Jpan -> kor_Hang -> zho_Hans -> jpn_Jpan
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
jpn_Jpan -> kor_Hang: 人工知能は私たちの世界を変えています。
翻訳結果: 인공지능은 우리의 세상을 변화시키고 있습니다.

kor_Hang -> zho_Hans: 인공지능은 우리의 세상을 변화시키고 있습니다.
翻訳結果: 人工智能正在改变我们的世界.

zho_Hans -> jpn_Jpan: 人工智能正在改变我们的世界.
翻訳結果: 人工知能は私たちの世界を変化させている.

jpn_Jpan -> jpn_Jpan: 人工知能は私たちの世界を変化させている.
翻訳結果: 人工知能は私たちの世界を変化させている.


第 2 ラウンド:
----------------------------------------
jpn_Jpan -> kor_Hang: 人工知能は私たちの世界を変化させている.
翻訳結果: 인공지능은 우리 세계를 변화시키고 있습니다.

kor_Hang -> zho_Hans: 인공지능은 우리 세계를 변화시키고 있습니다.
翻訳結果: 人工智能正在改变我们的世界.

zho_Hans -> jpn_Jpan: 人工智能正在改变我们的世界.
翻訳結果: 人工知能は私たちの世界を変化させている.

jpn_Jpan -> jpn_Jpan: 人工知能は私たちの世界を変化させている.
翻訳結果: 人工知能は私たちの世界を変化させている.

============================================================
最終結果 (jpn_Jpan): 人工知能は私たちの世界を変化させている.
テキスト長の変化: 19 -> 20 (変化 5.3%)


テスト組み合わせ 4: zho_Hans -> eng_Latn -> fra_Latn -> vie_Latn -> zho_Hans
----------------------------------------
元のテキスト: 人工知能は私たちの世界を変えています。
言語シーケンス: zho_Hans -> eng_Latn -> fra_Latn -> vie_Latn -> zho_Hans
循環回数: 2
============================================================

第 1 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: 人工知能は私たちの世界を変えています。
翻訳結果: Artificial intelligence is changing our world.

eng_Latn -> fra_Latn: Artificial intelligence is changing our world.
翻訳結果: L'intelligence artificielle change notre monde.

fra_Latn -> vie_Latn: L'intelligence artificielle change notre monde.
翻訳結果: Trí tuệ nhân tạo đang thay đổi thế giới của chúng ta.

vie_Latn -> zho_Hans: Trí tuệ nhân tạo đang thay đổi thế giới của chúng ta.
翻訳結果: 人工智能正在改变我们的世界.

zho_Hans -> zho_Hans: 人工智能正在改变我们的世界.
翻訳結果: 人工智能正在改变我们的世界.


第 2 ラウンド:
----------------------------------------
zho_Hans -> eng_Latn: 人工智能正在改变我们的世界.
翻訳結果: Artificial intelligence is changing our world.

eng_Latn -> fra_Latn: Artificial intelligence is changing our world.
翻訳結果: L'intelligence artificielle change notre monde.

fra_Latn -> vie_Latn: L'intelligence artificielle change notre monde.
翻訳結果: Trí tuệ nhân tạo đang thay đổi thế giới của chúng ta.

vie_Latn -> zho_Hans: Trí tuệ nhân tạo đang thay đổi thế giới của chúng ta.
翻訳結果: 人工智能正在改变我们的世界.

zho_Hans -> zho_Hans: 人工智能正在改变我们的世界.
翻訳結果: 人工智能正在改变我们的世界.

============================================================
最終結果 (zho_Hans): 人工智能正在改变我们的世界.
テキスト長の変化: 19 -> 14 (変化 26.3%)


============================================================
翻訳ドリフト現象の説明:
1. 各翻訳で微細な意味の変化が導入される可能性があります
2. 異なる言語間の表現方法の違いにより情報が失われます
3. モデルの翻訳プロセスにおける不確実性が蓄積されます
4. 一部の概念は異なる言語で完全に対応する表現がない場合があります
5. 循環翻訳はこれらの誤差を増幅し、最終的に意味のドリフトを引き起こします

ドキュメント翻訳

  • translate_docx.py
import sys
from translator import translate_text
from docx import Document
from language_map import NLLB_LANG_CODES

def do_translate(text: str, src_lang: str, target_lang: str) -> str:
    return translate_text(text, src_lang, target_lang)
def translate_docx(input_path: str, output_path: str, src_lang: str, target_lang: str):
    if src_lang not in NLLB_LANG_CODES or target_lang not in NLLB_LANG_CODES:
        raise ValueError(f"サポートされていない言語コード: {src_lang} または {target_lang}")

    doc = Document(input_path)
    for para in doc.paragraphs:
        if para.text.strip():
            para.text = do_translate(para.text, src_lang, target_lang)
    doc.save(output_path)
    print(f"翻訳完了、出力パス:{output_path}")

if __name__ == "__main__":
    if len(sys.argv) != 5:
        print("使用方法:python translate_docx.py 入力ファイル 出力ファイル ソース言語コード ターゲット言語コード")
        print("例:python translate_docx.py ../input_docs/at.docx ../output_docs/at_zh.docx ja zh")
        sys.exit(1)

    input_file = sys.argv[1]
    output_file = sys.argv[2]
    src_language = sys.argv[3]
    target_language = sys.argv[4]

    translate_docx(input_file, output_file, src_language, target_language)
  • translate.py
import sys
from docx import Document
import ctranslate2
from transformers import AutoTokenizer
from language_map import NLLB_LANG_CODES

# NLLBモデルパスとロード(グローバルで一度だけロードを維持)
model_path = "../models/nllb-ct2"
translator = ctranslate2.Translator(model_path, device="cuda")
tokenizer = AutoTokenizer.from_pretrained("facebook/nllb-200-distilled-600M")

def translate_text(text, src_lang, tgt_lang):
    # トークン化してソース言語タグと終了識別子を追加
    inputs = tokenizer.convert_ids_to_tokens(
        tokenizer.encode(text, add_special_tokens=False)
    )
    inputs = [f"{src_lang}"] + inputs + ["</s>"]

    # モデルを呼び出し、ターゲット言語タグをプレフィックスとして渡す
    results = translator.translate_batch(
        [inputs], target_prefix=[[f"{tgt_lang}"]], beam_size=4
    )

    print("inputs:", inputs)
    print("target_prefix:", [[f"{tgt_lang}"]])

    tokens = results[0].hypotheses[0]
    # 文字列にデコードし、特殊トークンをスキップ
    output = tokenizer.decode(
        tokenizer.convert_tokens_to_ids(tokens), skip_special_tokens=True
    )
    return output


def translate_docx(input_path, output_path, src_lang_code, tgt_lang_code):
    doc = Document(input_path)
    for para in doc.paragraphs:
        if para.text.strip():
            translated = translate_text(para.text, src_lang_code, tgt_lang_code)
            para.text = translated
    doc.save(output_path)
    print(f"翻訳完了、保存先: {output_path}")


if __name__ == "__main__":
    if len(sys.argv) != 5:
        print("使用方法: python translate_docx.py 入力ファイル 出力ファイル ソース言語 ターゲット言語")
        print("例: python translate_docx.py input.docx output.docx ja zh")
        sys.exit(1)

    input_file = sys.argv[1]
    output_file = sys.argv[2]
    src_lang = NLLB_LANG_CODES.get(sys.argv[3])
    tgt_lang = NLLB_LANG_CODES.get(sys.argv[4])

    if src_lang is None or tgt_lang is None:
        print("エラー:サポートされていない言語コード")
        sys.exit(1)

    translate_docx(input_file, output_file, src_lang, tgt_lang)
QQQ :bow_tone1:3Q!
📚 【翻訳モデル】200の言語を翻訳できるNLLBモデル体験 LM2_1 :arrow_heading_up:
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?