【PyTorch】違うバージョン間での型の違う演算には気を付けようメモ

#TL;DR
torch=<1.2.0 と torch=>1.4.0において、float型とtorch.int64型の演算(torch.sub)結果が違った。

torch=<1.2.0では、(float型) と (torch.int64型)の演算 = (torch.int64) となる。
torch=>1.4.0では、(float型) と (torch.int64型)の演算 = (torch.float32) となる。

torch=<1.2.0では、演算結果に対して(float型)の小数点以下の情報が消えてしまう。
実行する環境は統一させる、または演算の際はキャストを考慮する事が大事である。

# はじめに
Qiitaに初投稿させていただきます。[Jun (twitter)](https://twitter.com/reito_pirahu)です。

普段はPyTorchを使用して、楽しくNNを構築してお勉強をしております。
そんな中で、異なるPyTorchのバージョン間での数値演算を行った際にこちらの問題にぶち当たったので、メモとして記しておきます。

もし間違いがあればコメント等で指摘していただけると幸いです。

※「そもそも違う型同士で演算するな！」という意見ございましたら、誠におっしゃる通りでございます。大変申し訳ございません。

# 背景
そもそも、なぜ私がPyTorchの違うバージョン間を行き来してるのかと言いますと、

コロナの影響で研究室に立ち入る頻度が減ったため、家のゲーミングPCで環境構築すれば研究が色々楽になるのではと思って以下のように環境を構築しました。

+ 研究室環境 
    + Python3.5 + CUDA9.0 + torch==1.1.0

+ 家環境
    + Python3.7 + CUDA9.2 + torch==1.5.0

そして、研究室で走らせていたコードを、家で走らせたら普通に動いたので「ええやんええやん」と思って悠々自適に研究ライフをエンジョイしていました。

※「環境くらい再現性保つために全部統一しろよ！」という意見ございましたら、誠におっしゃる通りでございます。大変申し訳ございません。(n回目)

# 問題
それからは、家でコーディング作業と実行出来るかどうかの確認をし、研究室でNNの学習と検証をしていたところ、２つの環境間で異なる結果が出力されているのが目に留まりました。

以下に問題の再現として、Pythonのコマンドラインを示します。

### 研究室環境
+ Python3.5
+ CUDA9.0
+ torch == 1.1.0

~~~
>>> import torch
>>> torch.__version__
'1.1.0'
>>> float = 3.14
>>> tensor_int = torch.tensor(3, dtype=torch.int64)
>>>
>>> type(float)
<class 'float'>
>>> tensor_int.dtype
torch.int64
>>>
>>> ans = torch.sub(float, tensor_int)
>>> ans
tensor(0)
>>>
>>> ans.dtype
torch.int64
>>>  
~~~
### 家環境
+ Python3.7
+ CUDA9.2
+ torch==1.5.0

~~~
>>> import torch
>>> torch.__version__
'1.5.0'
>>> float = 3.14
>>> tensor_int = torch.tensor(3, dtype=torch.int64)
>>>
>>> type(float)
<class 'float'>
>>> tensor_int.dtype
torch.int64
>>>
>>> ans = torch.sub(float, tensor_int)
>>> ans
tensor(0.1400)
>>>
>>> ans.dtype
torch.float32
>>>  
~~~

見てわかる通り、演算結果"ans"のデータの型が研究室環境では `torch.int64`、家環境では`torch.float32`となっています。つまり、torch == 1.1.0では"float"の小数点以下の情報が"ans"では消えてしまっていたのです。

こちらの問題はおそらくtorchのバージョン間の問題に依存していると思われます。
(torch==1.5.0では`torch.int64`になってしまう問題が解決されていると捉えてます。ありがとう PyTorch。)

# 検証
では、torchのバージョン間の問題に依存していると予想したことから、
torch==1.1.0 ～ 1.5.0のどこでこの仕様が変わったのか、検証してみました。

検証した環境は以下の通りです。

+ Python 3.7.7
+ torchは全てにおいてcpu版を使用

検証したバージョンは以下の通りです。

+ torch==1.1.0
+ torch==1.2.0
+ torch==1.4.0
+ torch==1.5.0

(1.3.0は[PyTorchアーカイブ](https://pytorch.org/get-started/previous-versions/)になかったので、検証してません。)

### torch==1.1.0 (再掲)

~~~
>>> import torch
>>> torch.__version__
'1.1.0'
>>> float = 3.14
>>> tensor_int = torch.tensor(3, dtype=torch.int64)
>>>
>>> ans = torch.sub(float, tensor_int)
>>> ans
tensor(0)
>>>
>>> ans.dtype
torch.int64
>>>  
~~~

### torch==1.2.0
~~~
>>> import torch
>>> torch.__version__
'1.2.0'
>>> float = 3.14
>>> tensor_int = torch.tensor(3, dtype=torch.int64)
>>>
>>> ans = torch.sub(float, tensor_int)
>>> ans
tensor(0)
>>>
>>> ans.dtype
torch.int64
>>>  
~~~

### torch==1.4.0
~~~
>>> import torch
>>> torch.__version__
'1.4.0'
>>> float = 3.14
>>> tensor_int = torch.tensor(3, dtype=torch.int64)
>>>
>>> ans = torch.sub(float, tensor_int)
>>> ans
tensor(0.1400)
>>>
>>> ans.dtype
torch.float32
>>>      
~~~

### torch==1.5.0 (再掲)
~~~
>>> import torch
>>> torch.__version__
'1.5.0'
>>> float = 3.14
>>> tensor_int = torch.tensor(3, dtype=torch.int64)
>>>
>>> ans = torch.sub(float, tensor_int)
>>> ans
tensor(0.1400)
>>>
>>> ans.dtype
torch.float32
>>>  
~~~

結果からtorch==1.4.0から仕様が変わったようですね。やはりtorchのバージョンに依存していました。

おそらく [公式ドキュメント](https://pytorch.org/blog/pytorch-1-dot-4-released-and-domain-libraries-updated/) や [pytorch 1.4 リリース情報](https://github.com/pytorch/pytorch/releases/tag/v1.4.0) を読めばわかることだと思います。
(私は見つけられませんでした。。。)

# おわりに
「違うバージョン間での型の違う演算」という題目のもと、torchの異なるバージョン間でのfloat型とtorch.int64型の演算に注目して、出力結果の違いを検証しました。

私から言えることは、

+ 実験を行う際は開発環境には注意しましょう
+ 演算を行う際はキャストには注意しましょう
+ そして、PyTorchの開発陣の方々本当にありがとう

です。

私自身、研究室環境でCUDA9.0を入れていたため、torch==1.1.0を使用していたという妥協のもと、このような結果を招いてしまいました。これを機に、研究室環境を家環境に揃えるためPython,CUDA,torchをアップグレードしました。

みなさんも今一度、開発環境について見つめ直してみてはいかがでしょうか！