Google Colabで英語の論文を無料で翻訳する方法

![translated_atten_pdf.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958603/37b9abf1-5c93-b096-29e6-4d9be3f89740.png)


こんにちは！逆瀬川 ( https://twitter.com/gyakuse ) です。
今日は英語論文をサクッと翻訳する方法を共有します。
素晴らしい事前学習済みモデルの恩恵で素人でも1時間程度で実装できてしまいます。
なお、実装めちゃ汚いのですが、そのあたりはご容赦ください。

論文以外の文字埋め込みのないpdfを翻訳したい場合はこちらを参考にしてください:

https://qiita.com/sakasegawa/items/78c66fc83eda03b784da

論文全体の自動要約についてはこちら:

https://qiita.com/sakasegawa/items/1d906d66330a09b6939f

## 概要

翻訳モデル、レイアウト検知ライブラリとpdfを操作するライブラリを用いて外国語で書かれたpdfファイルを翻訳します。
翻訳には[フリーのニューラル機械翻訳モデルFuguMT](https://staka.jp/wordpress/?p=413)を使用します。

## この手法の嬉しさ

- DeepLおよびDeepL APIではpdf翻訳がサポートされていますが、行の切り替わりで別の文章と認識されることが多く、途中までの文章で翻訳されるため精度が落ちてしまいます
- この手法では段落単位で文章が一つにまとめられるため、より正確な翻訳が機能します。
- また、DeepL Proアカウントでは1ヶ月に翻訳できる文書ファイル数が5つに限定されています。

## Google Colab

https://colab.research.google.com/drive/1wz5BfvyJFLI_80rTVPkXM6zrjvUTRaIH?usp=sharing

### 使い方

- 上記colabを開き、ランタイム > すべてのセルを実行 をクリックします
    - ![pdf_translate_fugumt_ipynb_-_Colaboratory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958603/36742881-d014-0e67-f088-5e719f1da0c3.png)
- pip install をしている部分で、PILのインストール時に以下のようなRUNTIME ERRORが出るのでクリックしてランタイムを再起動します(バージョン違いのため)
    - ![pdf_translate_fugumt_ipynb_-_Colaboratory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958603/a7224dca-e982-89e2-9b67-ccfaecf4e338.png)
- 再度「すべてのセルを実行」をクリックします
    - Restart runtimeとか出てきますが今度は無視でいいです
- pip install が終わったら、ファイルをアップロードするUIが出てくるので、ファイルをアップロードします
    - ![Cursor_と_pdf_translate_fugumt_ipynb_-_Colaboratory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958603/ec151542-d9ac-fc3c-316a-516079c6df00.png)
- 処理を待ちます
- 処理が終わったらtranslated_{file_name}.pdfで保存されます
- 2ページ表示にするといい感じに幸福を味わえます

## 使用しているライブラリ等について

- layoutparser: レイアウトをパースするためにDetectron2のwrapperとして使っています。このライブラリではpdfを画像として渡して内部で`mask_rcnn_X_101_32x8d_FPN_3x`という事前学習モデルを呼び出しています
- reportlab: pdfの文章読み込み用に使用しています
- pypdf: pdfの文章書き込み用に使用しています
- FuguMT: huggingfaceのtransformersを使ってFuguMTを呼び出しています

## 処理の流れ

翻訳の処理の流れは以下のようになります。

- pdfをlayoutparserでレイアウト分けする
- レイアウトで検出された部分ごとに以下の処理を行う
    - レイアウトに対応するpypdfで抽出された文章をピックする
    - 文章をFuguMTで翻訳する
    - レイアウトの対象部分を白い長方形で覆う
    - その上に文章を詰め込み、reportlabで書き込む

このようにシンプルな処理で英語のpdfだけではなくどんな言語の資料でも、文字埋め込みがあれば翻訳することができます。

## 実装

### pdfをパラグラフ単位で取得する

レイアウトモデルで検出させます。レイアウトは`draw_box`関数でどのように検出されたかを確認することができます。
そのままだと画像やセクション名なども含まれてしまうので、`type=='Text'`とすることでテキスト(パラグラフ)のみを抽出することができます(薄いオレンジで囲まれた部分)。
![pdf_summarize_test_ipynb_-_Colaboratory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958603/30f13fcf-2f8b-d094-d68c-49bd44d84e0c.png)

```py
pdf_layout = model.detect(pdf_image)
paragraph_blocks = lp.Layout([b for b in pdf_layout if b.type=='Text'])
```

なお、添字のように、検出されたパラグラフは人間の認識する順序と同一になっていません。

### 特定のブロックに含まれた文章を取得する

layoutparserで取得できたパラグラフブロックの中に入っている文章を抽出します。
layoutparserで文章を扱うにはOCRをしなければなりませんが(たぶん)、コストが高いので、pypdfで文章を取ってきて、layoutparserが検出した領域内に含まれているかを判定してあげます。
また、layoutparserの検出が甘いことも考えられるので、許容誤差も設定します。

```py
inner_text_blocks = list(filter(lambda x: is_inside(paragraph_block, x), text_blocks))
```

```py
# 特定のtext_blockがparagraph_blockに含まれているかチェック
def is_inside(paragraph_block, text_block):
    paragraph_width = paragraph_block.block.x_2 - paragraph_block.block.x_1
    paragraph_height = paragraph_block.block.y_2 - paragraph_block.block.y_1
    if paragraph_width > 300:
        allowable_error_pixel = 10
        return (text_block.block.x_1 >= paragraph_block.block.x_1 - allowable_error_pixel and text_block.block.y_1 >= paragraph_block.block.y_1 and
                text_block.block.x_2 <= paragraph_block.block.x_2 + allowable_error_pixel and text_block.block.y_2 <= paragraph_block.block.y_2 + allowable_error_pixel)
    else:
        allowable_error_pixel = 3
        return (text_block.block.x_1 >= paragraph_block.block.x_1 - allowable_error_pixel and text_block.block.y_1 >= paragraph_block.block.y_1 and
                text_block.block.x_2 <= paragraph_block.block.x_2 + allowable_error_pixel and text_block.block.y_2 <= paragraph_block.block.y_2 + allowable_error_pixel)
```

### 文章を埋め込む

layoutparserのParagraphを使って文章を埋め込みます。改行もいい感じにやってくれるのですが、フォントサイズはうまく自動化できなかったために、`calc_fontsize` という関数で処理しています。

```py
frame = Frame(paragraph_x, height - paragraph_y, paragraph_width, paragraph_height,
                            showBoundary=0, leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0)
# テキスト実態の追加
fontsize = calc_fontsize(paragraph_width, paragraph_height, translated_text)
style = ParagraphStyle(name='Normal', fontName=font_name, fontSize=fontsize, leading=fontsize)
paragraph = Paragraph(translated_text, style)
story = [paragraph]
story_inframe = KeepInFrame(paragraph_width * 1.5, paragraph_height * 1.5, story)
frame.addFromList([story_inframe], text_canvas)
```


## 既知のバグや課題

- フォント系
    - フォントサイズがデカすぎたり小さすぎたりする (`calc_fontsize` をまともなものにする)
    - フォントサイズがでかすぎて入らなかったりする
- 領域系
    - 元のテキストの上に白く塗っただけなので、コピペしづらい (英語のフォント情報を事前に削除する)
    - 領域判定がわりと泥臭い、白く塗りつぶしすぎてしまったり、足りなかったり
- 打ち漏らしがある (章タイトルとか余計な翻訳しないようにlayoutparserでtext判定されたやつだけを対象にしているのでtext判定されなかったやつは翻訳されない)
- 引用やリンクが元の英文の場所を参照している

## 後記

このように、資料翻訳を行うことは事前学習済みモデルのおかげで簡単にできるようになっています (上記は1時間程度で実装できました。なお、記事作成に2時間かかって俺は泣いてます)。
綺麗に実装したりフォントサイズを安定させたり数式を解析しようとすると (latex image to mathmlのような処理を入れる)、実装コストが高くなります。
ただ、こういう雑実装でも今後の実装のためになると思うので、公開しました。

## ところで

- 論文をいちいちcolabを動かして翻訳するのはつらいです
- https://readable.jp/ であれば、月額980円で無限に翻訳することができます (筆者もこれを作ったあとこのサービスに気づき、今では月に200本程度翻訳処理かけています。控えめに言って神です)
    - 拡張機能もあります
        - https://chrome.google.com/webstore/detail/readable/pmhcplemclcflofgnjfhoilpkclnjnfh

## 参考

- layoutparser
    - [https://blog.shinonome.io/layoutparser/](https://blog.shinonome.io/layoutparser/)
    - [https://tech-blog.optim.co.jp/entry/2021/08/05/100000](https://tech-blog.optim.co.jp/entry/2021/08/05/100000)
    - [https://towardsdatascience.com/analyzing-document-layout-with-layoutparser-ed24d85f1d44](https://towardsdatascience.com/analyzing-document-layout-with-layoutparser-ed24d85f1d44)
- ReportLab
    - [https://gammasoft.jp/blog/pdf-japanese-font-by-python/](https://gammasoft.jp/blog/pdf-japanese-font-by-python/)
    - [https://qiita.com/takahashi_you/items/8c5fb1f07db1825c67a5](https://qiita.com/takahashi_you/items/8c5fb1f07db1825c67a5)
    - [https://qiita.com/kokardy/items/92e8f3b65c965e20de34](https://qiita.com/kokardy/items/92e8f3b65c965e20de34)