英語を中間言語として使用した多言語→日本語翻訳手法

# オフラインでも使える安全な翻訳システム
## 英語を中間言語として使用した多言語→日本語翻訳手法
<a href="https://huggingface.co/">HuggingFace</a>の事前学習済みモデルを使用して、多言語から日本語への機械翻訳プログラムを作ってみた。（英語を中間言語として使用）

## はじめに
オープンソースの機械翻訳モデルはいくつか出ているが（<a href="https://ai.meta.com/blog/nllb-200-high-quality-machine-translation/ja/">NLLB-200</a>、<a href="https://ai.meta.com/blog/nllb-200-high-quality-machine-translation/ja/">M2M100</a> etc.）、いずれも、
・でかい
・おそい
・コンシューマで買えそうなビデオカードのメモリに乗りそうなモデルだと、精度もいまいち

本来は、使用用途にあわせてFine Tuningせよ！ということですが、なるべく既存の公開モデルを使用して、いい感じにいろんな言語から日本語訳出来ないかな～と思い、英語を中間言語として使用する翻訳プログラムを作ってみました。
Pythonのライブラリや翻訳モデルをいったんダウンロードした後は、オフライン環境でも使えます。

## 概要
・使用言語：Python3
・ライブラリ：<a href"https://github.com/UKPLab/EasyNMT">EasyNMT</a>（裏でtransformersが動いています）

原文（英語以外の中国語,ベトナム語 etc.）から英語への翻訳は、<a href="https://github.com/Helsinki-NLP/Opus-MT">Opus-MT</a>を使用し、英語から日本語への翻訳には<a href="https://staka.jp/wordpress/?p=413#comment-10339">Fugu-MT</a>を使用します。

原文→(Opus-MT)→英語→(Fugu-MT)→日本語

### 注意事項
・EasyNMTは依存ライブラリであるfasttextが、Windows + Python3.10以降ではコンパイルできません。(pip install easynmtでエラーとなる)
　fasttextは言語自動判定に使われているだけなので（今回は不使用）、githubから直接ダウンロードするか、Python3.9を使いましょう。
・Fugu-MTは、transformers4.32以降は対応していないようです。(<a href="https://twitter.com/voleneko/status/1694306912710565917">@veleneko</a>)
・Opus-MTの英語→日本語翻訳のモデル(opus-mt-en-jap)は、聖書の学習しかしていないようで、実用できません。（名称からして・・・）

### 中国語→英語→日本語翻訳の実装

入力文(中国語) ["路上小心。最近怎么样？", "这里在下雪"]

```python:translate.py
from easynmt import EasyNMT,models
import torch
import time

#ISO639-1で、原文の言語を指定
source_lang = 'zh'
#HuggingFaceから該当モデルを自動ダウンロード
model_path = f'Helsinki-NLP/opus-mt-{source_lang}-en'
#model_path = 'facebook/m2m100_1.2B'
#GPU利用可否の判定
device = 'cuda' if torch.cuda.is_available() else 'cpu'
#英訳に対応したMarianMTモデルをロード(モデルのロードには時間がかかるため、適宜使いまわそう)
model_zh2en = EasyNMT(translator=models.AutoModel(model_path), device=device)
#英日翻訳はFugu-MT固定
model_en2ja = EasyNMT(translator=models.AutoModel('staka/fugumt-en-ja'), device=device)

#原文を配列で指定（EasyNMT内部で文章分割している。日中韓の場合、句読点などで分割）
sentences = [
    "路上小心。最近怎么样？",
    "这里在下雪"
]
# WarmUp
model_zh2en.translate("你好",source_lang=source_lang,target_lang='en',max_new_tokens=512)
model_en2ja.translate("Hello",source_lang='en',target_lang='ja',max_new_tokens=512)

#中国語→英語翻訳
time_start = time.perf_counter()
translated_temp = model_zh2en.translate(
    sentences,
    source_lang=source_lang,
    target_lang='en',#MarianMTモデルの場合、本来は指定不要
    max_new_tokens=1024,#最大生成トークン
    document_language_detection=False,#言語の自動判別
    show_progress_bar=False,#進行状況の表示
    batch_size=8,#同時実行数
    beam_size=3,#翻訳処理時の候補数
    repetition_penalty=1.1#繰り返し分に対するペナルティ
)
#英訳文
print(translated_temp)
#英語→日本語翻訳
translated_sentences = model_en2ja.translate(
    translated_temp,
    source_lang='en',
    target_lang='ja',
    max_new_tokens=1024,
    document_language_detection=False,
    show_progress_bar=False,
    batch_size=8,
    beam_size=3,
    repetition_penalty=1.1
)
#和訳文
print(translated_sentences)
time_end = time.perf_counter()
print(time_end- time_start)
```
#### 出力
["Be careful on the road.How's it going?", "It's snowing here."]
['道路に気をつけなさい。調子はどうですか。', 'ここで雪が降っている。']
0.2983019999999996

#### M2M100による翻訳
1.2B
['道路に気をつけろ。最近どう?', 'ここで雪が降る。']
0.4319429000000028

418M
['途中で気をつけましょう。最近どうですか?', 'ここは雪。']
0.2546081000000022

### GPUメモリ使用量
|    モデル        |GPUメモリ|
|:----------------|--------:|
|Opus-MT + Fugu-MT| 約1.5GB |
|    M2M100_1.2B  | 約5.6GB |
|    M2M100_418M  | 約2.8GB |


### 結果
・パラメータ数にもよるが、M2M100やNLLB-200で原文→日本語訳を行うより、原文→英語→日本語で、２回推論を行うほうが大抵早い。（1.5倍くらい）
・直訳に近くなる。（BLEU等は計測していないため、あくまで体感）
・メモリ使用量は約1.5GB、2GBメモリ搭載のビデオカードで動作可能

・大量の外国語文書を、とりあえず内容を把握して必要な情報をピックアップするのには使えそう。

### ToDo
CTranslate2を利用した量子化モデルによる爆速翻訳環境の構築