PyTorchで記述したPythonスクリプトをPyInstallerでexe化

#環境
・Windows 10 pro 64bit
・Python 3.7.9
・Anaconda 4.8.5
・PyTorch 1.6.0+cpu
・Pyinstaller 4.0

#はじめに
Pythonスクリプトをexe化する場合に、よく使われるものとして、Pyinstallerというものがあります。ここでは、PyTorchで文書分類器を作ったスクリプトをexe化したときに、起こったエラーの対処方法を記載していきます。

#Anacondaを使っている人へ
ライブラリのインストールは、__pip__と__conda__が混在しないようにしてください。また、exe化する際は、exe化するスクリプトに必要なライブラリのみをインストールした仮想環境を作ることをおすすめします。仮想環境の作り方は以下の通りです。



以下のPyTorchのフォーラムに__pip__でインストールするようにと書かれていましたので、私は全て__pip__でインストールしました。

[Create exe file - PyTorch Forums]
(https://discuss.pytorch.org/t/create-exe-file/56626)

#Pyinstallerの使い方
インストール

```
$ pip install pyinstaller
```

通常はexe化したいスクリプトのあるディレクトリで、以下のコマンド1つでexeファイルにできます。(exeファイル1つにしたい場合は--onefileを後ろに付ける)

```
$ pyinstaller sample.py
```

今回は、specファイルを編集したのとonefileではなかなかうまく実行できなかったで、以下の2つの手順でexeファイルにしていきます。

まず、以下のコマンドでspecファイルを作ります。

```
$ pyi-makespec sample.py --onedir
```

次に以下のコマンドexeファイルを作ります。

```
$ pyinstaller sample.spec --clean
```
--cleanは、同じスクリプトに関してpyinstallerで作られたファイルを削除するオプションです。build内にデータがなければ付ける必要はありません。
このコマンドを実行したディレクトリに__build__と__dist__というディレクトリが作られているはずです。__dist__内にexeファイルが作られています。


#PyTorchに関するエラー

exeファイル実行時に以下のようなエラーが発生しました。

```
File "torch\__init__.py", line 189, in <module>
ImportError: numpy.core.multiarray failed to import
```

exe化したいpythonスクリプトに以下の文を追加し、multiarrayを明示的にインポートすることで解決しました。

```
import numpy.core.multiarray
```

***
dllファイルが見つからないエラーまたは警告

```
lib not found: 〇〇.dll
```

pyi-makespecまたはpyinstallerでexe化する際に、__〇〇.spec__のようなファイルが作られていると思います。そのファイルのa=Analysisのpathex部分を以下のように修正します。

```sample.spec
a = Analysis(['sample.py'],
             pathex=['exe化するファイルのパス', 'エラーが出ていたdllファイルがあるディレクトリのパス(追加)'],
             binaries=[],
             datas=[],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
```

私の場合、torch_python.dllでしたので、'エラーが出ていたdllファイルがあるディレクトリのパス(追加)'は以下のようなパスになります。(releaseという仮想環境で作業を行っている場合)
'C:\\ProgramData\\Anaconda3\\envs\\release\\Lib\\site-packages\\torch\\lib'

※specファイルを修正したので、exeファイルを作る際はpyinstaller sample.pyではなく以下のようにしてください。

```
$ pyinstaller sample.spec --clean
```

#Janomeに関するエラー
形態素解析器にJanomeを使っていると実行時に以下のようなエラーが出ると思います。

```
FileNotFoundError: [Errno 2] No such file or directory: 
'C:\\Users\\username\\AppData\\Local\\Temp\\_MEI172322\\janome\\sysdic\\entries_compact0.py'
```
ファイルのパスは人によって微妙に違います。onefileオプションを付けている人は、同じようなパスになっていると思います。

__Janomeで使っている辞書データをpyinstallerが認識してくれず組み込めない__ことが原因らしいです。exeがあるディレクトリに__janomeをフォルダごとコピー__しておきましょう。janomeの中にあるsysdic以外は消しても問題ありません。

私の場合は以下のようなパスにjanomeのフォルダがありました。
C:\ProgramData\Anaconda3\envs\release\Lib\site-packages\janome

以下の記事でエラーの原因が書かれていました。
[パワポエンジニアの憂鬱を軽減する誤字/表記揺れ検出ツールを作った物語](https://qiita.com/youwht/items/062c41c88829fcf25107)

#transformersに関するエラー
huggingfaceのtransformersのtokenizerを使っていた場合、実行時に以下のようなエラーが出ると思います。

```
FileNotFoundError: [Errno 2] No such file or directory: 
'C:\\Users\\username\\AppData\\Local\\Temp\\_MEI191522\\sacremoses\\data\\perluniprops\\IsN.txt'
```

こちらも、Janomeに関するエラーと同じで、__tokenizerで用いる辞書データをpyinstallerが認識してくれず組み込めない__ことが原因です。janomeと対処方法は同じでexeのあるディレクトリに__sacremosesをフォルダごとコピー__すれば解決です。sacremoses内のdata\perluniprops以外は消しても問題ありません。
私の場合のsacremosesのパスは以下の通りです。
C:\ProgramData\Anaconda3\envs\release\Lib\site-packages\sacremoses


#最後に
Pyinstallerを使っていると原因がよくわからないエラーが出たりします。原因が調べてもわからなかった場合は、警告文に対処していくのも1つの方法です。私もそのようにしていました:sweat:　調べても出てこなかったときは、諦めかけていたのですが、なんとかなるもんですね。Pyinstaller自体の開発も現在進行形で続いているので、将来のアップデートで解決されるかもしれませんね。

#参考
Create exe file - PyTorch Forums
https://discuss.pytorch.org/t/create-exe-file/56626

パワポエンジニアの憂鬱を軽減する誤字/表記揺れ検出ツールを作った物語
https://qiita.com/youwht/items/062c41c88829fcf25107