こしあん
2021-01-23

Byte列を通じてNumPy配列からTensorFlowのテンソルへ変換する

Pocket
LINEで送る
Delicious にシェア

155{icon} {views}



Byte列を通じてNumPy配列からTensorFlowのテンソルへ変換する方法です。TFRecordで記録したデータを扱う際に役に立つと思われるやり方です。

NumPy配列をByte列に変換する

こんなことやらなくても、TF2のデフォルトとなったEagerテンソルだと特に問題ないのです。しかし、TF1系列のときに作られたと思われる機能で、Eagerテンソルになっていない(グラフモードのテンソル)場合であると便利です。例えば、TFRecordで記録したものをtf.data.Datasetで回すケースです。これ以外はほとんど使わないと思います。

ニッチすぎるかもしれません。NumPy配列を最も容量的に無駄のないやり方で、ファイルに書き出せるとしたら、Byte列に変換するという方法です。.tobytes()という関数で一発です。

import numpy as np

def tobytes():
    x = np.arange(8, dtype=np.int32)
    x_bytes = x.tobytes()
    print(x)
    # [0 1 2 3 4 5 6 7]
    print(x_bytes)
    # b'\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00'

if __name__ == "__main__":
    tobytes()

NumPy配列の型を変えるとByte列の長さが変わります。データ型によって、配列の要素にどのぐらいのメモリを割り当てるかが変わるので当たり前ですね。

def tobytes():
    x = np.arange(8, dtype=np.uint8)
    x_bytes = x.tobytes()
    print(x)
    print(x_bytes)
    # [0 1 2 3 4 5 6 7]
    # b'\x00\x01\x02\x03\x04\x05\x06\x07'

Byte列に変換するとshapeは消える

Byte列に変換すると元の配列のshapeの情報は消えます。以下のコードはNumPy配列としては異なりますが、Byte列に変換すると全て同じ値を示します。shapeの情報が落ちているからです。

def tobytes():
    x = np.arange(8, dtype=np.int32).reshape(4, 2).tobytes()
    print(x)
    y = np.arange(8, dtype=np.int32).tobytes()
    print(y)
    z = np.arange(8, dtype=np.int32).reshape(2, 2, 2).tobytes()
    print(z)
# b'\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00'
# b'\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00'
# b'\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00'

それっぽい痕跡はありますが、元のデータの型の情報は落ちていると捉えるべきです。データセットのスキーマーを記録しておくとあとで役立つでしょう。

Byte列からNumPy配列を復元するとはnp.frombuffer

Byte列からNumPy配列を復元するときは、np.frombufferという関数を使います。.tobytes()がシリアライズにあたるなら、np.frombufferがデシリアライズですね。

def frombytes():
    x_bytes = np.arange(8, dtype=np.int32).tobytes()
    x_recon = np.frombuffer(x_bytes, dtype=np.int32)
    print(x_recon)
    # [0 1 2 3 4 5 6 7]

shapeの調整はfrombufferでNumPy配列に戻したあとでやります。

def frombytes():
    x_bytes = np.arange(8, dtype=np.int32).reshape(4, 2).tobytes()
    x_recon = np.frombuffer(x_bytes, dtype=np.int32).reshape(4, 2)
    print(x_recon)

データ型をミスマッチがあると

つまり、元のshapeとデータ型の情報を持っておく必要があります。もし変換の前後でデータ型がミスマッチするような意地悪なケースを想定すると、配列の要素数や値が変わってしまいます。

def frombytes():
    x_bytes = np.arange(8, dtype=np.int32).tobytes()
    x_recon = np.frombuffer(x_bytes, dtype=np.uint8)
    print(x_recon)
    # [0 0 0 0 1 0 0 0 2 0 0 0 3 0 0 0 4 0 0 0 5 0 0 0 6 0 0 0 7 0 0 0]
def frombytes():
    x_bytes = np.arange(8, dtype=np.float32).tobytes()
    x_recon = np.frombuffer(x_bytes, dtype=np.int32)
    print(x_recon)
    # [         0 1065353216 1073741824 1077936128 1082130432 1084227584
    # 1086324736 1088421888]

Byte列の変換前後で、intとfloatを混同すると、とんでもない値になってしまうのでここは注意が必要です。

TensorFlowでByte列をテンソルに変換する

ここからが本題ですが、NumPyでtobytes()に変換したあと、NumPy配列ではなくTensorFlowのテンソルに変換したいことがあります。TFRecordのことを考えたときです。tf.io.decode_rawという関数でできます。

def bytes_to_tf():
    x_bytes = np.arange(8, dtype=np.int32).tobytes()
    x_tf = tf.io.decode_raw(x_bytes, tf.int32)
    print(x_tf)
    # tf.Tensor([0 1 2 3 4 5 6 7], shape=(8,), dtype=int32)

「エンディアンとか大丈夫なのかな?」と思ったのですが、そもそもNumPy配列とTensorFlowのテンソルが相当互換性があるので、こういうことやっても大丈夫なようです。一応主要な型で試してみます。

def bytes_to_tf():
    x_bytes = np.arange(8, dtype=np.uint8).tobytes()
    x_tf = tf.io.decode_raw(x_bytes, tf.uint8)
    print(x_tf)
    # tf.Tensor([0 1 2 3 4 5 6 7], shape=(8,), dtype=uint8)
def bytes_to_tf():
    x_bytes = (np.arange(8, dtype=np.float32)-3).tobytes()
    x_tf = tf.io.decode_raw(x_bytes, tf.float32)
    print(x_tf)
    # tf.Tensor([-3. -2. -1.  0.  1.  2.  3.  4.], shape=(8,), dtype=float32)
def bytes_to_tf():
    x = np.zeros(8, dtype=np.complex64)
    x.real = np.arange(8)
    x.imag = -np.arange(8)
    x_bytes = x.tobytes()
    x_tf = tf.io.decode_raw(x_bytes, tf.complex64)
    print(x_tf)
    # tf.Tensor([0.+0.j 1.-1.j 2.-2.j 3.-3.j 4.-4.j 5.-5.j 6.-6.j 7.-7.j], shape=(8,), dtype=complex64)

特に問題なさそうです

画像をByte列してTensorFlowのテンソルにする

より実践的に、NumPy配列の画像をByte列を経由してTensorFlowのテンソルにする例を見てみます。MNISTでやってみましょう。

import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt

def mnist():
    (img, _), (_, _) = tf.keras.datasets.mnist.load_data()
    img_bytes = img.tobytes()
    img_tf = tf.reshape(tf.io.decode_raw(img_bytes, tf.uint8), (60000, 28, 28))
    for i in range(9):
        ax = plt.subplot(3, 3, i + 1)
        ax.imshow(img_tf[i])
    plt.show()

うまくいきました。TFRecordではそのままByte列で記録すればよさそうですね。



Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

【新刊】インフィニティNumPy――配列の初期化から、ゲームの戦闘、静止画や動画作成までの221問

「本当の実装力を身につける」ための221本ノック――
機械学習(ML)で避けて通れない数値計算ライブラリ・NumPyを、自在に活用できるようになろう。「できる」ための体系的な理解を目指します。基礎から丁寧に解説し、ディープラーニング(DL)の難しいモデルで遭遇する、NumPyの黒魔術もカバー。初心者から経験者・上級者まで楽しめる一冊です。問題を解き終わったとき、MLやDLなどの発展分野にスムーズに入っていけるでしょう。

本書の大きな特徴として、Pythonの本でありがちな「NumPyとML・DLの結合を外した」点があります。NumPyを理解するのに、MLまで理解するのは負担が大きいです。本書ではあえてこれらの内容を書いていません。行列やテンソルの理解に役立つ「従来の画像処理」をNumPyベースで深く解説・実装していきます。

しかし、問題の多くは、DLの実装で頻出の関数・処理を重点的に取り上げています。経験者なら思わず「あー」となるでしょう。関数丸暗記では自分で実装できません。「覚える関数は最小限、できる内容は無限大」の世界をぜひ体験してみてください。画像編集ソフトの処理をNumPyベースで実装する楽しさがわかるでしょう。※紙の本は電子版の特典つき

モザイク除去から学ぶ 最先端のディープラーニング

「誰もが夢見るモザイク除去」を起点として、機械学習・ディープラーニングの基本をはじめ、GAN(敵対的生成ネットワーク)の基本や発展型、ICCV, CVPR, ECCVといった国際学会の最新論文をカバーしていく本です。
ディープラーニングの研究は発展が目覚ましく、特にGANの発展型は市販の本でほとんどカバーされていない内容です。英語の原著論文を著者がコードに落とし込み、実装を踏まえながら丁寧に解説していきます。
また、本コードは全てTensorFlow2.0(Keras)に対応し、Googleの開発した新しい機械学習向け計算デバイス・TPU(Tensor Processing Unit)をフル活用しています。Google Colaboratoryを用いた環境構築不要の演習問題もあるため、読者自ら手を動かしながら理解を深めていくことができます。

AI、機械学習、ディープラーニングの最新事情、奥深いGANの世界を知りたい方にとってぜひ手にとっていただきたい一冊となっています。持ち運びに便利な電子書籍のDLコードが付属しています。

「おもしろ同人誌バザールオンライン」で紹介されました!(14:03~) https://youtu.be/gaXkTj7T79Y?t=843

まとめURL:https://github.com/koshian2/MosaicDeeplearningBook
A4 全195ページ、カラー12ページ / 2020年3月発行

Shikoan's ML Blog -Vol.1/2-

累計100万PV超の人気ブログが待望の電子化! このブログが電子書籍になって読みやすくなりました!

・1章完結のオムニバス形式
・機械学習の基本からマニアックなネタまで
・どこから読んでもOK
・何巻から読んでもOK

・短いものは2ページ、長いものは20ページ超のものも…
・通勤・通学の短い時間でもすぐ読める!
・読むのに便利な「しおり」機能つき

・全巻はA5サイズでたっぷりの「200ページオーバー」
・1冊にたっぷり30本収録。1本あたり18.3円の圧倒的コストパフォーマンス!
・文庫本感覚でお楽しみください

Vol.1 電子550円
Vol.2 電子550円

北海道の駅巡りコーナー

日高本線 車なし全駅巡り

ローカル線や秘境駅、マニアックな駅に興味のある方におすすめ! 2021年に大半区間が廃線になる、北海道の日高本線の全区間・全29駅(苫小牧~様似)を記録した本です。マイカーを使わずに、公共交通機関(バス)と徒歩のみで全駅訪問を行いました。日高本線が延伸する計画のあった、襟裳岬まで様似から足を伸ばしています。代行バスと路線バスの織り成す極限の時刻表ゲームと、絶海の太平洋と馬に囲まれた日高路、日高の隠れたグルメを是非たっぷり堪能してください。A4・フルカラー・192ページのたっぷりのボリュームで、あなたも旅行気分を漫喫できること待ったなし!

見どころ:日高本線被災区間(大狩部、慶能舞川橋梁、清畠~豊郷) / 牧場に囲まれた絵笛駅 / 窓口のあっただるま駅・荻伏駅 / 汐見の戦争遺跡のトーチカ / 新冠温泉、三石温泉 / 襟裳岬

A4 全192ページフルカラー / 2020年11月発行


Pocket
LINEで送る
Delicious にシェア

One Comment

Add a Comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です