概要
- 「float64よりfloat32を使った方が高速らしいよ」
( ・ω・) (^ワ^ ) - 「でも、大事なデータが破損してしまうのでは....?」
( ´・ω・`) (^ワ^;) (え?) - 「よし、データサイズを小さくしたらどのくらい精度が落ちるのか、確かめることにしよう!」
(`・ω・´)
精度測定実験
1. 大きな整数
jupyter-notebook
bigint = np.array([123456789012345678901234567])
bigint
-> array([123456789012345678901234567], dtype=object)
# このままだとちょっとしか表示されないが...
bigint.astype('float64')
-> array([1.23456789e+26])
# ndarray内の要素を一つ指定すると、保持されている桁が最後まで見える
# (konandoiruasaさんの指摘により)
bigint.astype('float64')[0]
-> 1.2345678901234568e+26 # 16桁目までは元の値を保持
bigint.astype('float32')[0]
-> 1.2345679e+26 # 7桁目までは元の値を保持
bigint.astype('float16')[0]
-> inf # 草w
2. 小さな小数
jupyter-notebook
small_float = np.array([-0.00001234567890123456789])
small_float[0]
-> -1.2345678901234568e-05 # 生成してすぐに17桁目以降が破損...
small_float.astype('float64')[0]
-> -1.2345678901234568e-05 # 変化なし
small_float.astype('float32')[0]
-> -1.2345679e-05 # 7桁目までは元の値を保持
small_float.astype('float16')[0]
-> -1.234e-05 # 4桁目までは元の値を保持
3. 間を取ってみる
jupyter-notebook
halfway_number = np.array([1234.567890123456789012345])
halfway_number[0]
-> 1234.567890123457 # 生成してすぐに16桁目以降が破損...
halfway_number.astype('float64')[0]
-> 1234.567890123457 # 変化なし
halfway_number.astype('float32')[0]
-> 1234.5679 # 7桁目までは元の値を保持
halfway_number.astype('float16')[0]
-> 1235.0 # 3桁目までしか元の値が保持されない...
わかったこと(精度)(^ワ^*)
-
float16
数値が4桁3桁に収まるならOK -
float32
数値が8桁7桁に収まるならOK?
ただし、konandoiruasaさんの指摘にあるように、7桁であっても8388608までしか保持されないと考えると、8388608以上の数値は壊れてしまう可能性がある
というわけで、6桁で済む情報を格納する場合に限定した方がよさそう
※ただし、float32に関する 不具合事例(20191022追記)も参照 -
float64
扱える数値は12桁16桁までっぽい?
ただし、同じ理由で4503599627370496以上の値は壊れてしまう可能性がある
そうすると、15桁までしか保持されないと考えるのがよさそう
こんな感じか
numpyの型 | 桁数 |
---|---|
float16 | 3 |
float32 | 6 |
float64 | 15 |
不具合事例(20191022追記)
float32を使うと、
- たとえ6桁に収まる小数であっても最下位の桁が想定外の値になる
- roundメソッドによる丸め処理の結果がおかしい
事例がありました。
sample
df = pd.DataFrame({'i': [132.2, 332.11, 5.555555555]})
df.astype('float32')
i
0 132.199997 <= 元々4桁にもかかわらず、最下位がずれる
1 332.109985 <= 元々5桁にもかかわらず、最下位がずれる
2 5.555555 <= まあ予想通り
更に、float32だとroundメソッドによる丸めすら行えない?!
# float32 の dataframe を round(3) しても何も起こらない...
df.astype('float32').round(3)
i
0 132.199997 <= 3桁目以下も残っている....
1 332.109985
2 5.556000
# float64 なら round で下位の桁を排除できる
df.astype('float64').round(3)
i
0 132.200
1 332.110
2 5.556
ただし、生の値を実際に取り出せば、たとえfloat32であっても期待した値を取得できる
df.astype('float32').round(3).values
array([[132.2 ],
[332.11 ],
[ 5.556]], dtype=float32)
いずれも、3桁目以下が丸められて消えており、期待した値になっている
ここからわかること
- dtype=float32 の dataframe は信用できない
(小数点がない場合は調べてないので不明)
float32だと、dataframeから値を取り出してみるまで、実際にどんな値が入っているのかわからないことがある.....(-□-;)
こうなると、float16やfloat32を使うべきタイミングは、相当限られてきそう
float32でも精度が十分足りる場合
# この程度の桁数なら、十分正しく動作する
df = pd.DataFrame({'消費税': [0.100000, 0.080000, 0.050000]})
df.astype('float32').round(3)
消費税
0 0.10
1 0.08
2 0.05
速度測定実験
1. ひたすらndarrayを連結し続ける
測定対象のメソッドはこちら
jupyter-notebook
def generate_ndarray(num, np_type):
number = np.array([1234.56789123456789]).astype(np_type)
np_ar = np.array([]).astype(np_type)
count_ar = np.arange(num)
for i in count_ar:
np_ar = np.concatenate([np_ar, number])
return np_ar
結果はこちら
jupyter-notebook
%timeit generate_ndarray(num=1000, np_type=np.float64)
-> 5.88 ms ± 1.12 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit generate_ndarray(num=1000, np_type=np.float32)
-> 5.96 ms ± 328 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit generate_ndarray(num=1000, np_type=np.float16)
-> 5.77 ms ± 512 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
何も変わらないじゃないか!!!ヾ(`д´)ノ
気を取り直して....
2. 巨大行列同士の掛け算
jupyter-notebook
a = np.zeros((1000, 1000), dtype=np.float64)
b = np.zeros((1000, 1000), dtype=np.float32)
c = np.zeros((1000, 1000), dtype=np.float16)
jupyter-notebook
%timeit a * a
-> 12.2 ms ± 1.13 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit b * b
-> 6.44 ms ± 461 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit c * c
-> 31.5 ms ± 910 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
# 遅くなった!!!!
おや....これはどういうことだ.....( ´・ω・`) (・ω・`;)
わかったこと?(速度)(。ŏ﹏ŏ)
- float64 -> float32
処理によっては時間が半分で済むが、あまり時間が変わらない場合もある
値を計算に使用したとき、初めて差が出てくるのかも?
もう少し調べてみたい - float64 -> float16
むしろ遅くなる!!!
float16についてはもうちょっと継続調査しないとだめかも...?
参考サイト
より詳細が知りたい方はこちらへ....