論文 PDF から(概ね) 自動でNotion翻訳メモを作成する

本記事はAcompany5周年アドベントカレンダーの2日目の記事となります。

[Acompany5周年アドベントカレンダー](https://witty-hide-004.notion.site/b6d945cfebca4be0876128af68dd5d8b?v=a7e2f459e57b45a482c5b45a4ef03fa6)

### 背景
皆さんは論文をどのように理解してメモを残しているでしょうか？
自分はプライバシーテックのR&Dチームのマネージャーをしていて、コードの実装はヘルプに入る時くらいしかないのですが、論文はめちゃくちゃ読みます。自分の仕事は、ビジネスや法律のチームと連携を取って、ビジネス的な価値や法的壁、技術的制約や乗り越えられるポイントはどこかなど検討して、方向性が見えたら解くべき問題を定義してどのような解く手段があるかをある程度メンバーに提示して解いてもらう、といったことをすることが多いので、理論を根本からちゃんと理解しないといけないことや範囲が広く、永遠と論文ばっか読んでんなっていう日々か、永遠とMTGばっかしてんなっていう日々を過ごしてます。

弊社では、Notion という万能オンラインドキュメントを使用していて、R&Dチームでは、Notion を使って分野ごとに分かりやすく理論やアルゴリズムを書いたり、検証結果を残していったり、ブログを書いたりして、辿っていけばやってきたことが誰でも分かるようにしています。

それはさておき、論文管理も Notion のデータベースでやっていて、他のメンバーが論文を翻訳したメモや、理解の跡のメモを Notion にいけば見れるようにしています。 この辺の運用は、Google Drive のフォルダに論文の PDF ぶっ込んだら、GAS から API 叩いて論文のメタ情報等を引っこ抜いてきて、GAS から NotionAPI 叩いて、メタ情報と Google Drive のリンクを Notion データベースに登録するといった運用になる予定です。(今回のアドカレで書いてくれるはず)

Notion のデータベースだと、色々とカテゴリとか設定して絞れたり、viewを変えたりできて探しやすいし、メモもすぐに作れるし便利なんですよね。他の記事やブログも Notion で書くので流用しやすいですし。社内向けだと、github の Readme からも Notion に飛ばしたりします。

その環境で生まれる発想が、論文のTeX抽出と翻訳メモ自動で作れねえかなあ、というものです。自分は恥ずかしながら英語がそこまで得意でないので、英語のまま読むと時間がかかったり、間違えた解釈をする可能性があって、元の論文と機械翻訳を照らし合わせながら理解していくことが多いです。また、翻訳メモを残しておけば、他のメンバーも論文を読みたい時に理解を助けられます。

そこで、できるだけ自動で論文 PDF から markdown を 経由して DeepLAPIに突っ込んで翻訳して Notion メモとして取り込む方法を(かなり雑ですが)作りました。


## Quick Start
1. [mathpixのsnip](https://mathpix.com/snip)に登録します。
1. 残念ながら、Freeアカウントだと、1アカウントあたり PDF:10page/month, 10snips/monthしかできません。Pro Annualプランだと、$49.90で6000page/year, 60000snips/year できます。snips というのはデスクトップアプリで、スクショ的に撮った数式をLaTeXの数式に自動で変換してくれるものです。こっちも結構便利です。
1. snip web から 論文の PDF を import します。この辺は拡張機能でもできます。
1. アップロードが終わったら、markdownでダウンロードします。
1. [DeepL API](https://www.deepl.com/ja/pro-api/)に登録します。Freeプランでも、50万文字/month までできます。Freeでもクレカの登録が認証のために求められます。
1. markdown を DeepLAPIにぶん投げます。
    1. https://github.com/rtafds/MarkdownDeepL をクローンします。
        1. ```git clone https://github.com/rtafds/MarkdownDeepL```
    1. ターミナルからsrcに移動します。
        1. ```cd MarkdownDeepL/src```
    1. MarkdownDeepL/src/config.json の "auth_key" にDeepLAPIを記載します。
    1. 必要な python ライブラリをインストールします。python は大体動くと思いますが、3.9以降が安心です。
        1. ```pip install deepl```
    1. pythonで実行ファイルを叩きます
```
python markdown_deepl_strait.py <path/to/inputfile> <path/to/outputfile>
```
7 . ぶん投げて返ってきたmarkdownをNotionでimportします。 右上のimportからText & Markdownを選択して、そこから読み込みます。
8. 残念ながらNotionが自動で数式として読み込んでくれる設定はないので、直します。半角スペースの後に、 &dollar;&dollar;s_a&dollar; となっているところで、最後の&dollar;を打つと、インラインの数式にしてくれます。あと一個になってるはずなので、最後の$を手でポチポチ打っていきます。
9. ブロックの数式も、自動で変換してくれることはないので、/math コマンドからブロック数式を作り、そこにコピペします。
10. あとは、全体的に&dollar;の影響でイタリックになっているので、一回全体イタリックにして戻したりすると良いかもしれません。
11. 適切なところに move to から移動して終わりです。権限がないページには move to で動かせないので注意です。

残念ながら、現在の mathpix の仕様では、文章中にある程度複雑な数式が混じると、読み取ってくれないようです。そこは snips を使って論文からTeXコマンドを読み取ったりすると良いかもしれません。ちゃんと範囲を指定すれば相当精度が高いです。また、ブロック数式は精度高く読み込んでくれます。

## Demo

デモ動画を作りました。

<iframe width="560" height="315" src="https://www.youtube.com/embed/ym778G1-oBE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>


## 解説
### mathpix
PDF から markdown 等にできれば良いんですが、、、と思っていましたが、こんな素晴らしいサービスがありました。https://mathpix.com/
とりあえず、メールくらいしか求められないので、[mathpixのsnip](https://mathpix.com/snip) に登録します。
残念ながら、Freeアカウントだと、1アカウントあたり PDF:10page/month, 10snips/monthしかできません。Pro Annualプランだと、$49.90で6000page/year, 60000snips/month できます。snips というのはデスクトップアプリで、スクショ的に撮った数式をLaTeXの数式に自動で変換してくれるものです。こっちも結構便利です。

まあ、アカウントたくさん作れば個人でも問題なく使えます。面倒な人はそんなに高くはないので、Pro Annualに申し込みます。

こいつで、PDF -> markdown は完了です。AIで読み取ってるらしいですが、精度高いな〜と感じます。文章中のインライン数式は、簡単な記号しか読み取ってくれなかったりしますが、それでも記号挟んだ文章をちゃんと作ってくれるので、それだけでも重宝します。ブロックになっている数式はかなり高い精度で読み取ってくれます。

また、拡張機能も提供しており、これを入れると PDF 登録はWebから行えます。便利ですね。

ページ数カウントなので、節約のため、無駄なページは事前に省くか指定できるので省くと良いと思います。

突っ込んだら markdown で export するだけです。その他にも、DOCX(Word)、LaTeX、HTML、Overleaf 形式での export をサポートしています。

### DeepLに突っ込む
とりあえず、Markdownを pythonから DeepLAPIにぶっ込みます。とりあえず、必要な python ライブラリをインストールします。python は大体動くと思いますが、3.9以降が安心です。

```pip install deepl```

誰かそれっぽいの作ってないかな〜と思ってたら、https://qiita.com/koshian2/items/0c695274ef82e170adc5 の記事と、https://github.com/koshian2/MarkdownDeepL というリポジトリを見つけました。
残念ながら、 Mac だったので、まあ python で直で実行するかと思い、python で処理をしている主要部分だけ抜き出しました。

とりあえず、こいつを使ってDeepLAPIにぶん投げると、数式が翻訳でぐちゃぐちゃになって返ってきてしまったので、何かしらの対策を打つ必要がありました。


### 前処理
なんかないかな〜と API 仕様書を眺めてたら、[XML Handling](https://www.deepl.com/ja/docs-api/xml/markup/) のところに、Ignored Tags というのを見つけ、tag_handling=xml, ignore_tags=keep としたら、```<keep>r_a</keep>``` で囲まれた部分は翻訳がされないようだとわかリました。mathpix で export した場合は、```$```で囲まれたインライン数式と、```$$``` で囲まれたブロック数式を何らかの xml tag で挟めば良いことがわかりました。

特定の文字で挟まれた場所を抜き出してきて、数式か文章か判定するとか、色々考えましたが、~~面倒だったので~~ mathpix を信用して、前から順番に交互に ```$```を ```<keep>```,```</keep>```で置き換える処理を適当な数繰り返せば良いや〜という非常に雑な実装をしました。ついでに、```$$```は、```<keep></keep>```になるので、そいつをもう一回```<keep2>```と```</keep2>```に交互に置き換えるという雑っぷりです。[この辺ですね。](https://github.com/rtafds/MarkdownDeepL/blob/0d0dbbd92e3f29a6b5772d7a996255803eb53357/src/markdown_deepl_strait.py#L88)

~~面倒だったので~~ 時間がなかったので、とりあえずハードコーディングです。適当にいじってください。
とりあえずこれで数式が変換されなくなったので、python のライブラリを使って、DeepLにぶん投げます。

```
translator = deepl.Translator(auth_key=master_data["auth_key"])
result = translator.translate_text(
                    [sources], 
                    source_lang=master_data["source_lang"],
                    target_lang=master_data["target_lang"],
                    tag_handling="xml",
                    ignore_tags=["keep","keep2"])
```
投げる部分はこんな感じです。

元のコードで良い感じに文章を分割して投げてくれるっぽいのですが、多すぎるとなんかエラーになります。対応できていないので、ファイルが大きすぎるみたいなエラーが出る時はとりあえずは元のファイルを分割して一個一個実行してください。

### 後処理
投げたら返ってくるので、Notionが読み込んだ時に良い感じに後処理をします。
とりあえず、```<keep>``` -> ```$$``` にして、```</keep>``` -> ```$```にしたら、Notionに読み込んだ時に、最後に```$```を打てば数式化するように変換します。 最初の半角スペースがないと数式化してくれません。あと、読み込みの関係でエスケープとかが要ります。

ブロック数式の方は、とりあえず aligh* 環境に変えました。完全に自分用に作って、引数にすらしていないハードコーディングっぷりなので、適当に変えていじってください。しかし、勝手にmathpixが aligned 環境を作ってくれることもあってバッティングしたりします。 あと、```$$```で囲まないと今後は Notion が数式として読み取ってくれず、この部分が無視されてしまいます。
Notion さん、import 関係もうちょっと何とかしてください、、、それかもっと良いやり方知ってたら誰か教えてください。

```
def to_notion_tex_postprocess(result_str):
    result_str = re.sub(r"<keep>",r" \$\$", result_str)
    result_str = re.sub(r"</keep>", r"\$ ", result_str)
    result_str = re.sub(r"<keep2>", r"$$\n\\begin{align*}", result_str)
    result_str = re.sub(r"</keep2>", r"\\end{align*}\n$$", result_str)
    return result_str
```

### Notionへ読み込む
返ってきた markdown を Notion に import　します。右上のメニューから import するだけです。あとは、数式を直しましょう。

(2024年3月21日追記)
~~mathpix がまだまだなのか~~インラインの数式は読み込みがよくないですね。それでも、翻訳が簡単にできるのと、　ブロック数式が簡単にできるので、大助かりです。

mathpixは完璧なのですが、Notionに読み込まれるときに、Markdownがめっちゃ崩れるのと、インラインの数式は一部分が消しとばされます。
コピペして、テキストに変換するのも手ですし、Notion API 経由で、MarkdownをNotionにパースするツールがいくつか開発されているようなので、調べてみてください。



ちょうど [mathpix のデスクトップアプリ](https://mathpix.com/snipping-tool)で、スクショ的に数式を読み込んで LaTeX に変換してくれるものもあるので使うと良いかもしれません。精度はいいです。回数制限があるので、アカウント増やすか Pro で何とかしましょう。

使ってみると、以前のようにはもう戻れないです。

それでは良い R&D ライフを。